diff --git a/.editorconfig b/.editorconfig index 8fbad9dcd..1c53c29c2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -342,6 +342,6 @@ ij_json_wrap_long_lines = false indent_size = 2 ij_yaml_keep_indents_on_empty_lines = true ij_yaml_keep_line_breaks = true -ij_yaml_space_before_colon = true +ij_yaml_space_before_colon = false ij_yaml_spaces_within_braces = true ij_yaml_spaces_within_brackets = true diff --git a/README.md b/README.md index 45629f0eb..0b0eb8b93 100644 --- a/README.md +++ b/README.md @@ -7,24 +7,40 @@ to your specifications, with no knowledge of Java required. * 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) +* Forge **(ALPHA - NOT PRODUCTION-READY)**: [Modrinth](https://modrinth.com/mod/terra) / [CurseForge](https://www.curseforge.com/minecraft/mc-mods/terra-world-generator) -## Building and running Terra +## Building and Running Terra -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. +To build, simply run `./gradlew build` (`gradlew.bat build` on Windows). This will build all platforms, and +produce JARs in `platforms//build/libs` -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) -and install the server in the `target/server` directory, along with all the needed plugins. +### Production JARs: +* Bukkit: `Terra--shaded.jar` +* Fabric: `Terra--shaded-mapped.jar` +* Forge: `Terra--shaded.jar` -**Note: You will need to adjust the `NAME` variable `bukkit.yml` of the test server if you are not using the default Terra config.** +### Building a Specific Platform +To build a specific platform, run `gradlew :platforms::build`. +JARs are produced in `platforms//build/libs`. + +### Running Minecraft in the IDE +To run Minecraft with Terra in the IDE (for testing) use the following tasks: +* Bukkit + * `installPaper` - Install a [Paper](https://github.com/PaperMC/Paper) test server. (Only needs to be run once). + * `installPurpur` - Install a [Purpur](https://github.com/pl3xgaming/Purpur) test server. (Only needs to be run once). + * `runPaper` - Run the Paper test server with Terra (`installPaper` must have been run previously). + * `runPurpur` - Run the Purpur test server with Terra (`installPurpur` must have been run previously). +* Fabric + * `runClient` - Run a Minecraft Fabric client with Terra installed. + * `runServer` - Run a Minecraft Fabric server with Terra installed. +* Forge + * `runClient` - Run a Minecraft Forge client with Terra installed. + * `runServer` - Run a Minecraft Forge server with Terra installed. ## Contributing Contributions are welcome! If you want to see a feature in Terra, please, open an issue, or implement it yourself and submit a PR! Join the discord [here](https://discord.gg/PXUEbbF) if you would like to talk more about the project! ## Beta -Terra is still in beta! While it is stable, it is not feature-complete. There is a lot to be added! \ No newline at end of file +Terra is still in beta! While it is stable, it is not feature-complete. There is a lot to be added! diff --git a/build.gradle.kts b/build.gradle.kts index a50f799a3..60ac2571e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,10 +1,27 @@ import com.dfsek.terra.getGitHash -val versionObj = Version("5", "1", "0", true) +val versionObj = Version("5", "3", "3", true) allprojects { version = versionObj group = "com.dfsek.terra" + + tasks.withType().configureEach { + options.isFork = true + options.isIncremental = true + } + tasks.withType().configureEach { + useJUnitPlatform() + + maxHeapSize = "2G" + ignoreFailures = false + failFast = true + maxParallelForks = (Runtime.getRuntime().availableProcessors() - 1).takeIf { it > 0 } ?: 1 + + reports.html.isEnabled = false + reports.junitXml.isEnabled = false + } + } /** * Version class that does version stuff. @@ -18,4 +35,4 @@ class Version(val major: String, val minor: String, val revision: String, val pr else //Only use git hash if it's a prerelease. "$major.$minor.$revision-BETA+${getGitHash()}" } -} \ No newline at end of file +} diff --git a/buildSrc/src/main/kotlin/com/dfsek/terra/CommonConfig.kt b/buildSrc/src/main/kotlin/com/dfsek/terra/CommonConfig.kt index 2405568cd..3f10dc573 100644 --- a/buildSrc/src/main/kotlin/com/dfsek/terra/CommonConfig.kt +++ b/buildSrc/src/main/kotlin/com/dfsek/terra/CommonConfig.kt @@ -7,30 +7,15 @@ import org.gradle.kotlin.dsl.withType import java.io.ByteArrayOutputStream fun Project.configureCommon() { - apply(plugin = "java-library") - apply(plugin = "maven-publish") - apply(plugin = "idea") - configureDependencies() configureCompilation() configureDistribution() version = rootProject.version - - - - tasks.withType().configureEach { - useJUnitPlatform() - - maxHeapSize = "2G" - ignoreFailures = false - failFast = true - maxParallelForks = 12 - } } fun Project.getGitHash(): String { - val stdout = java.io.ByteArrayOutputStream() + val stdout = ByteArrayOutputStream() exec { commandLine = mutableListOf("git", "rev-parse", "--short", "HEAD") standardOutput = stdout diff --git a/buildSrc/src/main/kotlin/com/dfsek/terra/CompilationConfig.kt b/buildSrc/src/main/kotlin/com/dfsek/terra/CompilationConfig.kt index a6619cc1f..560c0c165 100644 --- a/buildSrc/src/main/kotlin/com/dfsek/terra/CompilationConfig.kt +++ b/buildSrc/src/main/kotlin/com/dfsek/terra/CompilationConfig.kt @@ -3,14 +3,16 @@ package com.dfsek.terra import org.gradle.api.JavaVersion import org.gradle.api.Project import org.gradle.api.plugins.JavaPluginConvention +import org.gradle.api.tasks.bundling.Jar 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.kotlin.dsl.* import org.gradle.language.jvm.tasks.ProcessResources fun Project.configureCompilation() { + apply(plugin = "maven-publish") + apply(plugin = "idea") + configure { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 @@ -19,7 +21,7 @@ fun Project.configureCompilation() { tasks.withType { options.encoding = "UTF-8" doFirst { - options.compilerArgs = mutableListOf("-Xlint:all") + options.compilerArgs.add("-Xlint:all") } } @@ -27,7 +29,12 @@ fun Project.configureCompilation() { include("**/*.*") filter( "tokens" to mapOf( - "VERSION" to project.version.toString() + "VERSION" to project.version.toString(), + "DESCRIPTION" to project.properties["terra.description"], + "WIKI" to project.properties["terra.wiki"], + "SOURCE" to project.properties["terra.source"], + "ISSUES" to project.properties["terra.issues"], + "LICENSE" to project.properties["terra.license"] ) ) } @@ -35,4 +42,19 @@ fun Project.configureCompilation() { tasks.withType { options.encoding = "UTF-8" } + + tasks.withType { + archiveBaseName.set("Terra-${archiveBaseName.get()}") + from("../LICENSE", "../../LICENSE") + } + + tasks.register("sourcesJar") { + archiveClassifier.set("sources") + } + + tasks.register("javadocJar") { + dependsOn("javadoc") + archiveClassifier.set("javadoc") + from(tasks.getByName("javadoc").destinationDir) + } } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/com/dfsek/terra/DependencyConfig.kt b/buildSrc/src/main/kotlin/com/dfsek/terra/DependencyConfig.kt index bdcada325..a73154b3f 100644 --- a/buildSrc/src/main/kotlin/com/dfsek/terra/DependencyConfig.kt +++ b/buildSrc/src/main/kotlin/com/dfsek/terra/DependencyConfig.kt @@ -1,13 +1,27 @@ package com.dfsek.terra import org.gradle.api.Project +import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.invoke import org.gradle.kotlin.dsl.repositories fun Project.configureDependencies() { + apply(plugin = "java") + apply(plugin = "java-library") + + configurations { + val shaded = create("shaded") + val shadedApi = create("shadedApi") + shaded.extendsFrom(shadedApi) + getByName("api").extendsFrom(shadedApi) + val shadedImplementation = create("shadedImplementation") + shaded.extendsFrom(shadedImplementation) + getByName("implementation").extendsFrom(shadedImplementation) + } repositories { - maven { url = uri("http://maven.enginehub.org/repo/") } + maven { url = uri("https://maven.enginehub.org/repo/") } maven { url = uri("https://repo.codemc.org/repository/maven-public") } maven { url = uri("https://papermc.io/repo/repository/maven-public/") } maven { url = uri("https://maven.fabricmc.net/") } @@ -18,9 +32,7 @@ fun Project.configureDependencies() { dependencies { "testImplementation"("org.junit.jupiter:junit-jupiter-api:5.7.0") - "testImplementation"("org.yaml:snakeyaml:1.27") - "testImplementation"("com.googlecode.json-simple:json-simple:1.1.1") - "testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.7.0") - "compileOnly"("org.jetbrains:annotations:20.1.0") + "testImplementation"("org.junit.jupiter:junit-jupiter-engine:5.7.0") + "api"("org.jetbrains:annotations:20.1.0") } } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/com/dfsek/terra/DistributionConfig.kt b/buildSrc/src/main/kotlin/com/dfsek/terra/DistributionConfig.kt index 855d50c95..ee81dc9b8 100644 --- a/buildSrc/src/main/kotlin/com/dfsek/terra/DistributionConfig.kt +++ b/buildSrc/src/main/kotlin/com/dfsek/terra/DistributionConfig.kt @@ -14,26 +14,8 @@ fun Project.configureDistribution() { apply(plugin = "java-library") apply(plugin = "com.github.johnrengelman.shadow") - -// configurations.create("shaded") - - configurations { - val shaded = create("shaded") - getByName("compile").extendsFrom(shaded) -// shaded.extendsFrom(getByName("compile")) - val shadedApi = create("shadedApi") - shaded.extendsFrom(shadedApi) - getByName("api").extendsFrom(shadedApi) - val shadedImplementation = create("shadedImplementation") - shaded.extendsFrom(shadedImplementation) - getByName("implementation").extendsFrom(shadedImplementation) - } - -// tasks.withType { -// classpath += -// } - val downloadDefaultPacks = tasks.create("downloadDefaultPacks") { + group = "terra" doFirst { file("${buildDir}/resources/main/packs/").deleteRecursively() @@ -45,21 +27,6 @@ fun Project.configureDistribution() { } tasks["processResources"].dependsOn(downloadDefaultPacks) - tasks.withType { - archiveBaseName.set("Terra-${archiveBaseName.get()}") - from("../LICENSE", "../../LICENSE") - } - - tasks.register("sourcesJar") { - archiveClassifier.set("sources") - } - - tasks.register("javadocJar") { - dependsOn("javadoc") - archiveClassifier.set("javadoc") - from(tasks.getByName("javadoc").destinationDir) - } - tasks.named("shadowJar") { // Tell shadow to download the packs dependsOn(downloadDefaultPacks) @@ -69,8 +36,14 @@ fun Project.configureDistribution() { archiveClassifier.set("shaded") setVersion(project.version) relocate("org.apache.commons", "com.dfsek.terra.lib.commons") - relocate("parsii", "com.dfsek.terra.lib.parsii") relocate("net.jafama", "com.dfsek.terra.lib.jafama") + relocate("org.objectweb.asm", "com.dfsek.terra.lib.asm") + relocate("com.google.errorprone", "com.dfsek.terra.lib.google.errorprone") + relocate("com.google.j2objc", "com.dfsek.terra.lib.google.j2objc") + relocate("org.checkerframework", "com.dfsek.terra.lib.checkerframework") + relocate("org.javax.annotation", "com.dfsek.terra.lib.javax.annotation") + relocate("org.json", "com.dfsek.terra.lib.json") + relocate("org.yaml", "com.dfsek.terra.lib.yaml") minimize() } convention.getPlugin().archivesBaseName = project.name diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 5938324de..311054dd7 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,11 +1,14 @@ -import com.dfsek.terra.configureCommon +import com.dfsek.terra.configureCompilation +import com.dfsek.terra.configureDependencies plugins { `java-library` `maven-publish` + idea } -configureCommon() +configureCompilation() +configureDependencies() group = "com.dfsek.terra.common" @@ -14,13 +17,14 @@ dependencies { "shadedApi"("commons-io:commons-io:2.4") "shadedApi"("com.dfsek:Paralithic:0.3.2") - "shadedApi"("com.dfsek:Tectonic:1.2.3") + "shadedApi"("com.dfsek:Tectonic:1.3.1") "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.googlecode.json-simple:json-simple:1.1.1") + "shadedApi"("org.yaml:snakeyaml:1.27") "compileOnly"("com.google.guava:guava:30.0-jre") diff --git a/common/src/main/java/com/dfsek/terra/api/TerraPlugin.java b/common/src/main/java/com/dfsek/terra/api/TerraPlugin.java index fb9f87613..2982da04f 100644 --- a/common/src/main/java/com/dfsek/terra/api/TerraPlugin.java +++ b/common/src/main/java/com/dfsek/terra/api/TerraPlugin.java @@ -7,14 +7,19 @@ 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.JarUtil; 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.profiler.Profiler; import com.dfsek.terra.world.TerraWorld; import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.jar.JarFile; /** * Represents a Terra mod/plugin instance. @@ -64,4 +69,10 @@ public interface TerraPlugin extends LoaderRegistrar { default void runPossiblyUnsafeTask(Runnable task) { task.run(); } + + Profiler getProfiler(); + + default JarFile getModJar() throws URISyntaxException, IOException { + return JarUtil.getJarFile(); + } } diff --git a/common/src/main/java/com/dfsek/terra/api/command/TerraCommandManager.java b/common/src/main/java/com/dfsek/terra/api/command/TerraCommandManager.java index d76c3a83f..e58dbbf30 100644 --- a/common/src/main/java/com/dfsek/terra/api/command/TerraCommandManager.java +++ b/common/src/main/java/com/dfsek/terra/api/command/TerraCommandManager.java @@ -65,7 +65,7 @@ public class TerraCommandManager implements CommandManager { return; } - if(commandClass.isAnnotationPresent(WorldCommand.class) && (!(sender instanceof Player) || !TerraWorld.isTerraWorld(((Player) sender).getWorld()))) { + if(commandClass.isAnnotationPresent(WorldCommand.class) && (!(sender instanceof Player) || !(((Player) sender).getWorld()).isTerraWorld())) { sender.sendMessage("Command must be executed in a Terra world."); return; } @@ -160,7 +160,6 @@ public class TerraCommandManager implements CommandManager { 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() + "\""); } diff --git a/common/src/main/java/com/dfsek/terra/api/event/events/AbstractCancellable.java b/common/src/main/java/com/dfsek/terra/api/event/events/AbstractCancellable.java new file mode 100644 index 000000000..e6fdd5319 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/event/events/AbstractCancellable.java @@ -0,0 +1,20 @@ +package com.dfsek.terra.api.event.events; + +import com.dfsek.terra.api.util.mutable.MutableBoolean; + +/** + * Abstract class containing basic {@link Cancellable} implementation. + */ +public abstract class AbstractCancellable implements Cancellable { + private final MutableBoolean cancelled = new MutableBoolean(false); + + @Override + public boolean isCancelled() { + return cancelled.get(); + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled.set(cancelled); + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/event/events/config/ConfigPackLoadEvent.java b/common/src/main/java/com/dfsek/terra/api/event/events/config/ConfigPackLoadEvent.java index 7aa7ab8a4..e995c8897 100644 --- a/common/src/main/java/com/dfsek/terra/api/event/events/config/ConfigPackLoadEvent.java +++ b/common/src/main/java/com/dfsek/terra/api/event/events/config/ConfigPackLoadEvent.java @@ -1,5 +1,7 @@ package com.dfsek.terra.api.event.events.config; +import com.dfsek.tectonic.config.ConfigTemplate; +import com.dfsek.tectonic.exception.ConfigException; import com.dfsek.terra.api.event.events.PackEvent; import com.dfsek.terra.config.pack.ConfigPack; @@ -8,13 +10,28 @@ import com.dfsek.terra.config.pack.ConfigPack; */ public abstract class ConfigPackLoadEvent implements PackEvent { private final ConfigPack pack; + private final ExceptionalConsumer configLoader; - public ConfigPackLoadEvent(ConfigPack pack) { + public ConfigPackLoadEvent(ConfigPack pack, ExceptionalConsumer configLoader) { this.pack = pack; + this.configLoader = configLoader; } @Override public ConfigPack getPack() { return pack; } + + /** + * Load a custom {@link ConfigTemplate} using the pack manifest. + * + * @param template Template to register. + */ + public void loadTemplate(ConfigTemplate template) throws ConfigException { + configLoader.accept(template); + } + + public interface ExceptionalConsumer { + void accept(T value) throws ConfigException; + } } diff --git a/common/src/main/java/com/dfsek/terra/api/event/events/config/ConfigPackPostLoadEvent.java b/common/src/main/java/com/dfsek/terra/api/event/events/config/ConfigPackPostLoadEvent.java index 138de647c..1398ec219 100644 --- a/common/src/main/java/com/dfsek/terra/api/event/events/config/ConfigPackPostLoadEvent.java +++ b/common/src/main/java/com/dfsek/terra/api/event/events/config/ConfigPackPostLoadEvent.java @@ -1,12 +1,13 @@ package com.dfsek.terra.api.event.events.config; +import com.dfsek.tectonic.config.ConfigTemplate; import com.dfsek.terra.config.pack.ConfigPack; /** * Called when a config pack has finished loading. */ public class ConfigPackPostLoadEvent extends ConfigPackLoadEvent { - public ConfigPackPostLoadEvent(ConfigPack pack) { - super(pack); + public ConfigPackPostLoadEvent(ConfigPack pack, ExceptionalConsumer loader) { + super(pack, loader); } } diff --git a/common/src/main/java/com/dfsek/terra/api/event/events/config/ConfigPackPreLoadEvent.java b/common/src/main/java/com/dfsek/terra/api/event/events/config/ConfigPackPreLoadEvent.java index 317603c9e..1ac2de54d 100644 --- a/common/src/main/java/com/dfsek/terra/api/event/events/config/ConfigPackPreLoadEvent.java +++ b/common/src/main/java/com/dfsek/terra/api/event/events/config/ConfigPackPreLoadEvent.java @@ -1,12 +1,14 @@ package com.dfsek.terra.api.event.events.config; +import com.dfsek.tectonic.config.ConfigTemplate; +import com.dfsek.tectonic.exception.ConfigException; import com.dfsek.terra.config.pack.ConfigPack; /** * Called before a config pack's registries are filled. At this point, the pack manifest has been loaded, and all registries are empty. */ public class ConfigPackPreLoadEvent extends ConfigPackLoadEvent { - public ConfigPackPreLoadEvent(ConfigPack pack) { - super(pack); + public ConfigPackPreLoadEvent(ConfigPack pack, ExceptionalConsumer configLoader) { + super(pack, configLoader); } } diff --git a/common/src/main/java/com/dfsek/terra/api/event/events/world/TerraWorldLoadEvent.java b/common/src/main/java/com/dfsek/terra/api/event/events/world/TerraWorldLoadEvent.java index 213786432..52e5bf956 100644 --- a/common/src/main/java/com/dfsek/terra/api/event/events/world/TerraWorldLoadEvent.java +++ b/common/src/main/java/com/dfsek/terra/api/event/events/world/TerraWorldLoadEvent.java @@ -1,6 +1,5 @@ package com.dfsek.terra.api.event.events.world; -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; @@ -9,18 +8,24 @@ import com.dfsek.terra.world.TerraWorld; /** * Called upon initialization of a TerraWorld. */ -public class TerraWorldLoadEvent implements Event { +public class TerraWorldLoadEvent implements PackEvent { private final TerraWorld world; + private final ConfigPack pack; - public TerraWorldLoadEvent(TerraWorld world) { + public TerraWorldLoadEvent(TerraWorld world, ConfigPack pack) { this.world = world; + this.pack = pack; } public TerraWorld getWorld() { return world; } - public WorldConfig getPack() { + public ConfigPack getPack() { + return pack; + } + + public WorldConfig getWorldConfig() { return world.getConfig(); } } diff --git a/common/src/main/java/com/dfsek/terra/api/event/events/world/generation/EntitySpawnEvent.java b/common/src/main/java/com/dfsek/terra/api/event/events/world/generation/EntitySpawnEvent.java new file mode 100644 index 000000000..2f4b25bbe --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/event/events/world/generation/EntitySpawnEvent.java @@ -0,0 +1,45 @@ +package com.dfsek.terra.api.event.events.world.generation; + +import com.dfsek.terra.api.event.events.PackEvent; +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.entity.Entity; +import com.dfsek.terra.api.structures.structure.buffer.items.BufferedEntity; +import com.dfsek.terra.config.pack.ConfigPack; + +/** + * Called when an entity is spawned via {@link BufferedEntity}. + */ +public class EntitySpawnEvent implements PackEvent { + private final ConfigPack pack; + private final Entity entity; + private final Location location; + + public EntitySpawnEvent(ConfigPack pack, Entity entity, Location location) { + this.pack = pack; + this.entity = entity; + this.location = location; + } + + @Override + public ConfigPack getPack() { + return pack; + } + + /** + * Get the entity that triggered the event. + * + * @return The entity. + */ + public Entity getEntity() { + return entity; + } + + /** + * Get the location of the entity. + * + * @return Location of the entity. + */ + public Location getLocation() { + return location; + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/event/events/world/generation/LootPopulateEvent.java b/common/src/main/java/com/dfsek/terra/api/event/events/world/generation/LootPopulateEvent.java new file mode 100644 index 000000000..cb3735017 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/event/events/world/generation/LootPopulateEvent.java @@ -0,0 +1,80 @@ +package com.dfsek.terra.api.event.events.world.generation; + +import com.dfsek.terra.api.event.events.AbstractCancellable; +import com.dfsek.terra.api.event.events.Cancellable; +import com.dfsek.terra.api.event.events.PackEvent; +import com.dfsek.terra.api.platform.block.Block; +import com.dfsek.terra.api.platform.block.state.Container; +import com.dfsek.terra.api.structures.loot.LootTable; +import com.dfsek.terra.api.structures.script.StructureScript; +import com.dfsek.terra.api.structures.structure.buffer.items.BufferedLootApplication; +import com.dfsek.terra.config.pack.ConfigPack; +import org.jetbrains.annotations.NotNull; + +/** + * Called when loot is populated via {@link BufferedLootApplication}. + */ +public class LootPopulateEvent extends AbstractCancellable implements PackEvent, Cancellable { + private final Block block; + private final Container container; + private LootTable table; + private final ConfigPack pack; + private final StructureScript script; + + public LootPopulateEvent(Block block, Container container, LootTable table, ConfigPack pack, StructureScript script) { + this.block = block; + this.container = container; + this.table = table; + this.pack = pack; + this.script = script; + } + + @Override + public ConfigPack getPack() { + return pack; + } + + /** + * Get the block containing the tile entity loot is applied to. + * + * @return Block at which loot is applied. + */ + public Block getBlock() { + return block; + } + + /** + * Get the {@link Container} representing the inventory. + * + * @return Inventory recieving loot. + */ + public Container getContainer() { + return container; + } + + /** + * Get the loot table to be populated. + * @return Loot table. + */ + public LootTable getTable() { + return table; + } + + /** + * Set the loot table to be populated. + * + * @param table New loot table. + */ + public void setTable(@NotNull LootTable table) { + this.table = table; + } + + /** + * Get the script used to generate the structure. + * + * @return Structure script. + */ + public StructureScript getStructureScript() { + return script; + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/platform/inventory/ItemStack.java b/common/src/main/java/com/dfsek/terra/api/platform/inventory/ItemStack.java index dfa8d9719..5b7ac6906 100644 --- a/common/src/main/java/com/dfsek/terra/api/platform/inventory/ItemStack.java +++ b/common/src/main/java/com/dfsek/terra/api/platform/inventory/ItemStack.java @@ -1,6 +1,7 @@ package com.dfsek.terra.api.platform.inventory; import com.dfsek.terra.api.platform.Handle; +import com.dfsek.terra.api.platform.inventory.item.Damageable; import com.dfsek.terra.api.platform.inventory.item.ItemMeta; public interface ItemStack extends Handle { @@ -13,4 +14,8 @@ public interface ItemStack extends Handle { ItemMeta getItemMeta(); void setItemMeta(ItemMeta meta); + + default boolean isDamageable() { + return getItemMeta() instanceof Damageable; + } } diff --git a/common/src/main/java/com/dfsek/terra/api/platform/world/World.java b/common/src/main/java/com/dfsek/terra/api/platform/world/World.java index 1dbcd85a6..45d13ab4d 100644 --- a/common/src/main/java/com/dfsek/terra/api/platform/world/World.java +++ b/common/src/main/java/com/dfsek/terra/api/platform/world/World.java @@ -6,6 +6,8 @@ import com.dfsek.terra.api.platform.block.Block; import com.dfsek.terra.api.platform.entity.Entity; import com.dfsek.terra.api.platform.entity.EntityType; import com.dfsek.terra.api.platform.world.generator.ChunkGenerator; +import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; +import com.dfsek.terra.api.world.generation.TerraChunkGenerator; import java.io.File; import java.util.UUID; @@ -17,20 +19,12 @@ public interface World extends Handle { ChunkGenerator getGenerator(); - String getName(); - - UUID getUID(); - - boolean isChunkGenerated(int x, int z); - 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); default Block getBlockAt(Location l) { @@ -40,4 +34,12 @@ public interface World extends Handle { Entity spawnEntity(Location location, EntityType entityType); int getMinHeight(); + + default boolean isTerraWorld() { + return getGenerator().getHandle() instanceof GeneratorWrapper; + } + + default TerraChunkGenerator getTerraGenerator() { + return ((GeneratorWrapper) getGenerator().getHandle()).getHandle(); + } } diff --git a/common/src/main/java/com/dfsek/terra/api/structures/loot/functions/DamageFunction.java b/common/src/main/java/com/dfsek/terra/api/structures/loot/functions/DamageFunction.java index 189a0c100..517777db0 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/loot/functions/DamageFunction.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/loot/functions/DamageFunction.java @@ -34,8 +34,8 @@ public class DamageFunction implements LootFunction { @Override public ItemStack apply(ItemStack original, Random r) { if(original == null) return null; + if(!original.isDamageable()) return original; ItemMeta meta = original.getItemMeta(); - if(!(meta instanceof Damageable)) return original; double itemDurability = (r.nextDouble() * (max - min)) + min; Damageable damage = (Damageable) meta; damage.setDamage((int) (original.getType().getMaxDurability() - (itemDurability / 100) * original.getType().getMaxDurability())); diff --git a/common/src/main/java/com/dfsek/terra/api/structures/script/StructureScript.java b/common/src/main/java/com/dfsek/terra/api/structures/script/StructureScript.java index 391713edb..673381d13 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/script/StructureScript.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/script/StructureScript.java @@ -29,6 +29,7 @@ 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.profiler.ProfileFrame; import com.dfsek.terra.registry.config.FunctionRegistry; import com.dfsek.terra.registry.config.LootRegistry; import com.dfsek.terra.registry.config.ScriptRegistry; @@ -39,6 +40,7 @@ import org.apache.commons.io.IOUtils; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; import java.util.Random; import java.util.concurrent.ExecutionException; @@ -52,7 +54,7 @@ public class StructureScript { public StructureScript(InputStream inputStream, TerraPlugin main, ScriptRegistry registry, LootRegistry lootRegistry, FunctionRegistry functionRegistry) throws ParseException { Parser parser; try { - parser = new Parser(IOUtils.toString(inputStream)); + parser = new Parser(IOUtils.toString(inputStream, Charset.defaultCharset())); } catch(IOException e) { throw new RuntimeException(e); } @@ -68,7 +70,7 @@ public class StructureScript { .registerFunction("setMark", new SetMarkFunctionBuilder()) .registerFunction("getMark", new GetMarkFunctionBuilder()) .registerFunction("pull", new PullFunctionBuilder(main)) - .registerFunction("loot", new LootFunctionBuilder(main, lootRegistry)) + .registerFunction("loot", new LootFunctionBuilder(main, lootRegistry, this)) .registerFunction("entity", new EntityFunctionBuilder(main)) .registerFunction("getBiome", new BiomeFunctionBuilder(main)) .registerFunction("getBlock", new CheckBlockFunctionBuilder()) @@ -87,6 +89,12 @@ public class StructureScript { .registerFunction("ceil", new UnaryNumberFunctionBuilder(number -> FastMath.ceil(number.doubleValue()))) .registerFunction("log", new UnaryNumberFunctionBuilder(number -> FastMath.log(number.doubleValue()))) .registerFunction("round", new UnaryNumberFunctionBuilder(number -> FastMath.round(number.doubleValue()))) + .registerFunction("sin", new UnaryNumberFunctionBuilder(number -> FastMath.sin(number.doubleValue()))) + .registerFunction("cos", new UnaryNumberFunctionBuilder(number -> FastMath.cos(number.doubleValue()))) + .registerFunction("tan", new UnaryNumberFunctionBuilder(number -> FastMath.tan(number.doubleValue()))) + .registerFunction("asin", new UnaryNumberFunctionBuilder(number -> FastMath.asin(number.doubleValue()))) + .registerFunction("acos", new UnaryNumberFunctionBuilder(number -> FastMath.acos(number.doubleValue()))) + .registerFunction("atan", new UnaryNumberFunctionBuilder(number -> FastMath.atan(number.doubleValue()))) .registerFunction("max", new BinaryNumberFunctionBuilder((number, number2) -> FastMath.max(number.doubleValue(), number2.doubleValue()))) .registerFunction("min", new BinaryNumberFunctionBuilder((number, number2) -> FastMath.min(number.doubleValue(), number2.doubleValue()))); @@ -104,22 +112,31 @@ public class StructureScript { * @param rotation Rotation of structure * @return Whether generation was successful */ + @SuppressWarnings("try") public boolean execute(Location location, Random random, Rotation rotation) { - StructureBuffer buffer = new StructureBuffer(location); - boolean level = applyBlock(new TerraImplementationArguments(buffer, rotation, random, 0)); - buffer.paste(); - return level; + try(ProfileFrame ignore = main.getProfiler().profile("terrascript:" + id)) { + StructureBuffer buffer = new StructureBuffer(location); + boolean level = applyBlock(new TerraImplementationArguments(buffer, rotation, random, 0)); + buffer.paste(); + return level; + } } + @SuppressWarnings("try") public boolean execute(Location location, Chunk chunk, Random random, Rotation rotation) { - StructureBuffer buffer = computeBuffer(location, random, rotation); - buffer.paste(chunk); - return buffer.succeeded(); + try(ProfileFrame ignore = main.getProfiler().profile("terrascript_chunk:" + id)) { + StructureBuffer buffer = computeBuffer(location, random, rotation); + buffer.paste(chunk); + return buffer.succeeded(); + } } + @SuppressWarnings("try") public boolean test(Location location, Random random, Rotation rotation) { - StructureBuffer buffer = computeBuffer(location, random, rotation); - return buffer.succeeded(); + try(ProfileFrame ignore = main.getProfiler().profile("terrascript_test:" + id)) { + StructureBuffer buffer = computeBuffer(location, random, rotation); + return buffer.succeeded(); + } } private StructureBuffer computeBuffer(Location location, Random random, Rotation rotation) { @@ -134,13 +151,19 @@ public class StructureScript { } } + @SuppressWarnings("try") public boolean executeInBuffer(Buffer buffer, Random random, Rotation rotation, int recursions) { - return applyBlock(new TerraImplementationArguments(buffer, rotation, random, recursions)); + try(ProfileFrame ignore = main.getProfiler().profile("terrascript_recursive:" + id)) { + return applyBlock(new TerraImplementationArguments(buffer, rotation, random, recursions)); + } } + @SuppressWarnings("try") public boolean executeDirect(Location location, Random random, Rotation rotation) { - DirectBuffer buffer = new DirectBuffer(location); - return applyBlock(new TerraImplementationArguments(buffer, rotation, random, 0)); + try(ProfileFrame ignore = main.getProfiler().profile("terrascript_direct:" + id)) { + DirectBuffer buffer = new DirectBuffer(location); + return applyBlock(new TerraImplementationArguments(buffer, rotation, random, 0)); + } } public String getId() { diff --git a/common/src/main/java/com/dfsek/terra/api/structures/script/builders/LootFunctionBuilder.java b/common/src/main/java/com/dfsek/terra/api/structures/script/builders/LootFunctionBuilder.java index fe4634848..d5e46d9d3 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/script/builders/LootFunctionBuilder.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/script/builders/LootFunctionBuilder.java @@ -3,6 +3,7 @@ package com.dfsek.terra.api.structures.script.builders; 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.StructureScript; import com.dfsek.terra.api.structures.script.functions.LootFunction; import com.dfsek.terra.api.structures.tokenizer.Position; import com.dfsek.terra.registry.config.LootRegistry; @@ -12,16 +13,18 @@ import java.util.List; public class LootFunctionBuilder implements FunctionBuilder { private final TerraPlugin main; private final LootRegistry registry; + private final StructureScript script; - public LootFunctionBuilder(TerraPlugin main, LootRegistry registry) { + public LootFunctionBuilder(TerraPlugin main, LootRegistry registry, StructureScript script) { this.main = main; this.registry = registry; + this.script = script; } @SuppressWarnings("unchecked") @Override public LootFunction build(List> argumentList, Position position) { - return new LootFunction(registry, (Returnable) argumentList.get(0), (Returnable) argumentList.get(1), (Returnable) argumentList.get(2), (Returnable) argumentList.get(3), main, position); + return new LootFunction(registry, (Returnable) argumentList.get(0), (Returnable) argumentList.get(1), (Returnable) argumentList.get(2), (Returnable) argumentList.get(3), main, position, script); } @Override diff --git a/common/src/main/java/com/dfsek/terra/api/structures/script/functions/EntityFunction.java b/common/src/main/java/com/dfsek/terra/api/structures/script/functions/EntityFunction.java index 547ae7a00..b8b9f8d1b 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/script/functions/EntityFunction.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/script/functions/EntityFunction.java @@ -21,9 +21,11 @@ public class EntityFunction implements Function { private final EntityType data; private final Returnable x, y, z; private final Position position; + private final TerraPlugin main; public EntityFunction(Returnable x, Returnable y, Returnable z, Returnable data, TerraPlugin main, Position position) throws ParseException { this.position = position; + this.main = main; if(!(data instanceof ConstantExpression)) throw new ParseException("Entity data must be constant", data.getPosition()); this.data = main.getWorldHandle().getEntity(((ConstantExpression) data).getConstant()); @@ -39,7 +41,7 @@ public class EntityFunction implements Function { RotationUtil.rotateVector(xz, arguments.getRotation()); - arguments.getBuffer().addItem(new BufferedEntity(data), new Vector3(xz.getX(), y.apply(implementationArguments, variableMap).doubleValue(), xz.getZ()).toLocation(arguments.getBuffer().getOrigin().getWorld())); + arguments.getBuffer().addItem(new BufferedEntity(data, main), new Vector3(xz.getX(), y.apply(implementationArguments, variableMap).doubleValue(), xz.getZ()).toLocation(arguments.getBuffer().getOrigin().getWorld())); return null; } diff --git a/common/src/main/java/com/dfsek/terra/api/structures/script/functions/LootFunction.java b/common/src/main/java/com/dfsek/terra/api/structures/script/functions/LootFunction.java index 353294c73..42add8070 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/script/functions/LootFunction.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/script/functions/LootFunction.java @@ -8,6 +8,7 @@ import com.dfsek.terra.api.structures.parser.lang.ImplementationArguments; import com.dfsek.terra.api.structures.parser.lang.Returnable; import com.dfsek.terra.api.structures.parser.lang.functions.Function; import com.dfsek.terra.api.structures.parser.lang.variables.Variable; +import com.dfsek.terra.api.structures.script.StructureScript; import com.dfsek.terra.api.structures.script.TerraImplementationArguments; import com.dfsek.terra.api.structures.structure.RotationUtil; import com.dfsek.terra.api.structures.structure.buffer.items.BufferedLootApplication; @@ -23,8 +24,9 @@ public class LootFunction implements Function { private final Returnable x, y, z; private final Position position; private final TerraPlugin main; + private final StructureScript script; - public LootFunction(LootRegistry registry, Returnable x, Returnable y, Returnable z, Returnable data, TerraPlugin main, Position position) { + public LootFunction(LootRegistry registry, Returnable x, Returnable y, Returnable z, Returnable data, TerraPlugin main, Position position, StructureScript script) { this.registry = registry; this.position = position; this.data = data; @@ -32,6 +34,7 @@ public class LootFunction implements Function { this.y = y; this.z = z; this.main = main; + this.script = script; } @Override @@ -49,7 +52,7 @@ public class LootFunction implements Function { return null; } - arguments.getBuffer().addItem(new BufferedLootApplication(table, main), new Vector3(FastMath.roundToInt(xz.getX()), y.apply(implementationArguments, variableMap).intValue(), FastMath.roundToInt(xz.getZ())).toLocation(arguments.getBuffer().getOrigin().getWorld())); + arguments.getBuffer().addItem(new BufferedLootApplication(table, main, script), new Vector3(FastMath.roundToInt(xz.getX()), y.apply(implementationArguments, variableMap).intValue(), FastMath.roundToInt(xz.getZ())).toLocation(arguments.getBuffer().getOrigin().getWorld())); return null; } diff --git a/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedEntity.java b/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedEntity.java index b0e7987ce..fc90123ce 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedEntity.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedEntity.java @@ -1,18 +1,24 @@ package com.dfsek.terra.api.structures.structure.buffer.items; +import com.dfsek.terra.api.TerraPlugin; +import com.dfsek.terra.api.event.events.world.generation.EntitySpawnEvent; import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.entity.Entity; import com.dfsek.terra.api.platform.entity.EntityType; public class BufferedEntity implements BufferedItem { private final EntityType type; + private final TerraPlugin main; - public BufferedEntity(EntityType type) { + public BufferedEntity(EntityType type, TerraPlugin main) { this.type = type; + this.main = main; } @Override public void paste(Location origin) { - origin.clone().add(0.5, 0, 0.5).getWorld().spawnEntity(origin, type); + Entity entity = origin.clone().add(0.5, 0, 0.5).getWorld().spawnEntity(origin, type); + main.getEventManager().callEvent(new EntitySpawnEvent(entity.getWorld().getTerraGenerator().getConfigPack(), entity, entity.getLocation())); } } diff --git a/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedLootApplication.java b/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedLootApplication.java index 5f69a6470..89e534449 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedLootApplication.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedLootApplication.java @@ -1,35 +1,46 @@ package com.dfsek.terra.api.structures.structure.buffer.items; import com.dfsek.terra.api.TerraPlugin; +import com.dfsek.terra.api.event.events.world.generation.LootPopulateEvent; import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.block.Block; import com.dfsek.terra.api.platform.block.state.BlockState; import com.dfsek.terra.api.platform.block.state.Container; import com.dfsek.terra.api.structures.loot.LootTable; +import com.dfsek.terra.api.structures.script.StructureScript; import com.dfsek.terra.api.util.FastRandom; public class BufferedLootApplication implements BufferedItem { private final LootTable table; private final TerraPlugin main; + private final StructureScript structure; - public BufferedLootApplication(LootTable table, TerraPlugin main) { + public BufferedLootApplication(LootTable table, TerraPlugin main, StructureScript structure) { this.table = table; this.main = main; + this.structure = structure; } @Override public void paste(Location origin) { try { - BlockState data = origin.getBlock().getState(); + Block block = origin.getBlock(); + BlockState data = block.getState(); if(!(data instanceof Container)) { main.logger().severe("Failed to place loot at " + origin + "; block " + data + " is not container."); return; } Container container = (Container) data; - table.fillInventory(container.getInventory(), new FastRandom(origin.hashCode())); + + LootPopulateEvent event = new LootPopulateEvent(block, container, table, block.getLocation().getWorld().getTerraGenerator().getConfigPack(), structure); + main.getEventManager().callEvent(event); + if(event.isCancelled()) return; + + event.getTable().fillInventory(container.getInventory(), new FastRandom(origin.hashCode())); data.update(false); } catch(Exception e) { main.logger().warning("Could not apply loot at " + origin + ": " + e.getMessage()); - main.getDebugLogger().stack(e); + e.printStackTrace(); } } } diff --git a/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedStateManipulator.java b/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedStateManipulator.java index 703ca2695..158f1a951 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedStateManipulator.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedStateManipulator.java @@ -21,7 +21,7 @@ public class BufferedStateManipulator implements BufferedItem { state.update(false); } catch(Exception e) { main.logger().warning("Could not apply BlockState at " + origin + ": " + e.getMessage()); - main.getDebugLogger().stack(e); + e.printStackTrace(); } } } diff --git a/common/src/main/java/com/dfsek/terra/api/transform/NotNullValidator.java b/common/src/main/java/com/dfsek/terra/api/transform/NotNullValidator.java deleted file mode 100644 index b94b7be39..000000000 --- a/common/src/main/java/com/dfsek/terra/api/transform/NotNullValidator.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.dfsek.terra.api.transform; - -public class NotNullValidator implements Validator { - @Override - public boolean validate(T value) { - return !(value == null); - } -} diff --git a/common/src/main/java/com/dfsek/terra/api/transform/Transformer.java b/common/src/main/java/com/dfsek/terra/api/transform/Transformer.java index 30ce77b6f..efd801dd8 100644 --- a/common/src/main/java/com/dfsek/terra/api/transform/Transformer.java +++ b/common/src/main/java/com/dfsek/terra/api/transform/Transformer.java @@ -53,6 +53,7 @@ public class Transformer { private final LinkedHashMap, List>> transforms = new LinkedHashMap<>(); @SafeVarargs + @SuppressWarnings("varargs") public final Builder addTransform(Transform transform, Validator... validators) { transforms.put(transform, Arrays.asList(validators)); return this; diff --git a/common/src/main/java/com/dfsek/terra/api/transform/Validator.java b/common/src/main/java/com/dfsek/terra/api/transform/Validator.java index c9ba5db9e..b22b7c08b 100644 --- a/common/src/main/java/com/dfsek/terra/api/transform/Validator.java +++ b/common/src/main/java/com/dfsek/terra/api/transform/Validator.java @@ -1,6 +1,12 @@ package com.dfsek.terra.api.transform; +import java.util.Objects; + public interface Validator { boolean validate(T value) throws TransformException; + + static Validator notNull() { + return Objects::nonNull; + } } diff --git a/common/src/main/java/com/dfsek/terra/api/util/JarUtil.java b/common/src/main/java/com/dfsek/terra/api/util/JarUtil.java index 20ad875cc..981dfdb7f 100644 --- a/common/src/main/java/com/dfsek/terra/api/util/JarUtil.java +++ b/common/src/main/java/com/dfsek/terra/api/util/JarUtil.java @@ -1,9 +1,13 @@ package com.dfsek.terra.api.util; +import com.dfsek.terra.api.TerraPlugin; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -32,4 +36,12 @@ public class JarUtil { } } } + + public static JarFile getJarFile() throws URISyntaxException, IOException { + return new JarFile(new File(getJarURL().toURI())); + } + + public static URL getJarURL() { + return TerraPlugin.class.getProtectionDomain().getCodeSource().getLocation(); + } } diff --git a/common/src/main/java/com/dfsek/terra/api/util/mutable/MutableBoolean.java b/common/src/main/java/com/dfsek/terra/api/util/mutable/MutableBoolean.java index 50abd8feb..a71fdb9be 100644 --- a/common/src/main/java/com/dfsek/terra/api/util/mutable/MutableBoolean.java +++ b/common/src/main/java/com/dfsek/terra/api/util/mutable/MutableBoolean.java @@ -5,6 +5,14 @@ import org.jetbrains.annotations.NotNull; public class MutableBoolean implements MutablePrimitive { private boolean value; + public MutableBoolean() { + this.value = false; + } + + public MutableBoolean(boolean value) { + this.value = value; + } + @Override public Boolean get() { return value; diff --git a/common/src/main/java/com/dfsek/terra/api/world/generation/Chunkified.java b/common/src/main/java/com/dfsek/terra/api/world/generation/Chunkified.java new file mode 100644 index 000000000..3b9925d46 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/world/generation/Chunkified.java @@ -0,0 +1,7 @@ +package com.dfsek.terra.api.world.generation; + +/** + * Marker interface that marks a feature as "chunkified" (only modifying one chunk at a time) + */ +public interface Chunkified { +} diff --git a/common/src/main/java/com/dfsek/terra/api/world/generation/GenerationPhase.java b/common/src/main/java/com/dfsek/terra/api/world/generation/GenerationPhase.java deleted file mode 100644 index 6dfee8985..000000000 --- a/common/src/main/java/com/dfsek/terra/api/world/generation/GenerationPhase.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.dfsek.terra.api.world.generation; - -/** - * The phase of terrain generation. Used for modifying values based on the phase of generation. - */ -public enum GenerationPhase { - BASE, POPULATE, GENERATION_POPULATE, PALETTE_APPLY, POST_GEN -} diff --git a/common/src/main/java/com/dfsek/terra/api/world/generation/TerraChunkGenerator.java b/common/src/main/java/com/dfsek/terra/api/world/generation/TerraChunkGenerator.java index 280ee8f96..345e32c61 100644 --- a/common/src/main/java/com/dfsek/terra/api/world/generation/TerraChunkGenerator.java +++ b/common/src/main/java/com/dfsek/terra/api/world/generation/TerraChunkGenerator.java @@ -6,10 +6,10 @@ import com.dfsek.terra.api.platform.world.World; import com.dfsek.terra.api.platform.world.generator.ChunkData; import com.dfsek.terra.api.world.biome.provider.BiomeProvider; import com.dfsek.terra.config.pack.ConfigPack; -import com.dfsek.terra.world.generation.math.SamplerCache; import com.dfsek.terra.world.generation.math.samplers.Sampler; import org.jetbrains.annotations.NotNull; +import java.util.List; import java.util.Random; public interface TerraChunkGenerator { @@ -32,4 +32,6 @@ public interface TerraChunkGenerator { TerraPlugin getMain(); Sampler createSampler(int chunkX, int chunkZ, BiomeProvider provider, World world, int elevationSmooth); + + List getPopulators(); } diff --git a/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileQueryCommand.java b/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileQueryCommand.java index 16f383b2d..eb54976e5 100644 --- a/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileQueryCommand.java +++ b/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileQueryCommand.java @@ -4,16 +4,10 @@ import com.dfsek.terra.api.TerraPlugin; import com.dfsek.terra.api.command.CommandTemplate; import com.dfsek.terra.api.command.annotation.Command; 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.injection.annotations.Inject; import com.dfsek.terra.api.platform.CommandSender; -import com.dfsek.terra.api.platform.entity.Player; -import com.dfsek.terra.world.TerraWorld; @Command -@WorldCommand -@PlayerCommand @DebugCommand public class ProfileQueryCommand implements CommandTemplate { @Inject @@ -21,8 +15,9 @@ public class ProfileQueryCommand implements CommandTemplate { @Override public void execute(CommandSender sender) { - Player player = (Player) sender; - TerraWorld world = main.getWorld(player.getWorld()); - player.sendMessage(world.getProfiler().getResultsFormatted()); + StringBuilder data = new StringBuilder("Terra Profiler data dump: \n"); + main.getProfiler().getTimings().forEach((id, timings) -> data.append(id).append(": ").append(timings.toString()).append('\n')); + main.logger().info(data.toString()); + sender.sendMessage("Profiler data dumped to console."); } } diff --git a/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileResetCommand.java b/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileResetCommand.java index 1a4c47e6e..2e5a97d6d 100644 --- a/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileResetCommand.java +++ b/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileResetCommand.java @@ -4,16 +4,10 @@ import com.dfsek.terra.api.TerraPlugin; import com.dfsek.terra.api.command.CommandTemplate; import com.dfsek.terra.api.command.annotation.Command; 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.injection.annotations.Inject; import com.dfsek.terra.api.platform.CommandSender; -import com.dfsek.terra.api.platform.entity.Player; -import com.dfsek.terra.world.TerraWorld; @Command -@WorldCommand -@PlayerCommand @DebugCommand public class ProfileResetCommand implements CommandTemplate { @Inject @@ -21,9 +15,7 @@ public class ProfileResetCommand implements CommandTemplate { @Override public void execute(CommandSender sender) { - Player player = (Player) sender; - TerraWorld world = main.getWorld(player.getWorld()); - world.getProfiler().reset(); - player.sendMessage("Profiler reset."); + main.getProfiler().reset(); + sender.sendMessage("Profiler reset."); } } diff --git a/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileStartCommand.java b/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileStartCommand.java index 36dcec85b..63c28c472 100644 --- a/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileStartCommand.java +++ b/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileStartCommand.java @@ -4,16 +4,10 @@ import com.dfsek.terra.api.TerraPlugin; import com.dfsek.terra.api.command.CommandTemplate; import com.dfsek.terra.api.command.annotation.Command; 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.injection.annotations.Inject; import com.dfsek.terra.api.platform.CommandSender; -import com.dfsek.terra.api.platform.entity.Player; -import com.dfsek.terra.world.TerraWorld; @Command -@WorldCommand -@PlayerCommand @DebugCommand public class ProfileStartCommand implements CommandTemplate { @Inject @@ -21,9 +15,7 @@ public class ProfileStartCommand implements CommandTemplate { @Override public void execute(CommandSender sender) { - Player player = (Player) sender; - TerraWorld world = main.getWorld(player.getWorld()); - world.getProfiler().setProfiling(true); - player.sendMessage("Profiling enabled."); + main.getProfiler().start(); + sender.sendMessage("Profiling enabled."); } } diff --git a/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileStopCommand.java b/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileStopCommand.java index 64e46fe39..baa5aeab3 100644 --- a/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileStopCommand.java +++ b/common/src/main/java/com/dfsek/terra/commands/profiler/ProfileStopCommand.java @@ -4,16 +4,10 @@ import com.dfsek.terra.api.TerraPlugin; import com.dfsek.terra.api.command.CommandTemplate; import com.dfsek.terra.api.command.annotation.Command; 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.injection.annotations.Inject; import com.dfsek.terra.api.platform.CommandSender; -import com.dfsek.terra.api.platform.entity.Player; -import com.dfsek.terra.world.TerraWorld; @Command -@WorldCommand -@PlayerCommand @DebugCommand public class ProfileStopCommand implements CommandTemplate { @Inject @@ -21,9 +15,7 @@ public class ProfileStopCommand implements CommandTemplate { @Override public void execute(CommandSender sender) { - Player player = (Player) sender; - TerraWorld world = main.getWorld(player.getWorld()); - world.getProfiler().setProfiling(false); - player.sendMessage("Profiling disabled."); + main.getProfiler().stop(); + sender.sendMessage("Profiling disabled."); } } diff --git a/common/src/main/java/com/dfsek/terra/commands/structure/StructureLoadCommand.java b/common/src/main/java/com/dfsek/terra/commands/structure/StructureLoadCommand.java index ae1327b11..dfd22addf 100644 --- a/common/src/main/java/com/dfsek/terra/commands/structure/StructureLoadCommand.java +++ b/common/src/main/java/com/dfsek/terra/commands/structure/StructureLoadCommand.java @@ -63,8 +63,6 @@ public class StructureLoadCommand implements CommandTemplate { @Override public void execute(CommandSender sender) { - System.out.println(rotation); - Player player = (Player) sender; long t = System.nanoTime(); diff --git a/common/src/main/java/com/dfsek/terra/config/PluginConfig.java b/common/src/main/java/com/dfsek/terra/config/PluginConfig.java index e1112dcf4..d391fed8e 100644 --- a/common/src/main/java/com/dfsek/terra/config/PluginConfig.java +++ b/common/src/main/java/com/dfsek/terra/config/PluginConfig.java @@ -69,12 +69,12 @@ public class PluginConfig implements ConfigTemplate { ConfigLoader loader = new ConfigLoader(); loader.load(this, file); if(dumpDefaultConfig) { // Don't dump default config if already loaded. - try(JarFile jar = new JarFile(new File(TerraPlugin.class.getProtectionDomain().getCodeSource().getLocation().toURI()))) { + try(JarFile jar = main.getModJar()) { JarUtil.copyResourcesToDirectory(jar, "packs", new File(main.getDataFolder(), "packs").toString()); } catch(IOException | URISyntaxException e) { main.getDebugLogger().error("Failed to dump default config files!"); e.printStackTrace(); - main.getDebugLogger().error("Report this to Terra!"); + main.getDebugLogger().error("Either you're on Forge, or this is a bug. If it's the latter, report this to Terra!"); } } } catch(ConfigException | IOException e) { diff --git a/common/src/main/java/com/dfsek/terra/config/builder/BiomeBuilder.java b/common/src/main/java/com/dfsek/terra/config/builder/BiomeBuilder.java index a454cb07c..f29a06c7c 100644 --- a/common/src/main/java/com/dfsek/terra/config/builder/BiomeBuilder.java +++ b/common/src/main/java/com/dfsek/terra/config/builder/BiomeBuilder.java @@ -4,7 +4,10 @@ import com.dfsek.terra.api.platform.world.Biome; import com.dfsek.terra.api.util.collections.ProbabilityCollection; import com.dfsek.terra.api.util.seeded.SeededBuilder; import com.dfsek.terra.api.world.biome.TerraBiome; +import com.dfsek.terra.config.templates.BiomeTemplate; public interface BiomeBuilder extends SeededBuilder { ProbabilityCollection getVanillaBiomes(); + + BiomeTemplate getTemplate(); } diff --git a/common/src/main/java/com/dfsek/terra/config/builder/GeneratorBuilder.java b/common/src/main/java/com/dfsek/terra/config/builder/GeneratorBuilder.java deleted file mode 100644 index 798e91245..000000000 --- a/common/src/main/java/com/dfsek/terra/config/builder/GeneratorBuilder.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.dfsek.terra.config.builder; - -import com.dfsek.paralithic.eval.parser.Scope; -import com.dfsek.paralithic.eval.tokenizer.ParseException; -import com.dfsek.terra.api.math.noise.NoiseSampler; -import com.dfsek.terra.api.math.noise.samplers.ExpressionSampler; -import com.dfsek.terra.api.math.noise.samplers.noise.ConstantSampler; -import com.dfsek.terra.api.util.seeded.NoiseSeeded; -import com.dfsek.terra.api.world.palette.holder.PaletteHolder; -import com.dfsek.terra.config.loaders.config.function.FunctionTemplate; -import com.dfsek.terra.world.generation.WorldGenerator; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public class GeneratorBuilder { - private final Map gens = Collections.synchronizedMap(new HashMap<>()); - - private String noiseEquation; - - private String elevationEquation; - - private String carvingEquation; - - private Scope varScope; - - private Map noiseBuilderMap; - - private Map functionTemplateMap; - - private PaletteHolder palettes; - - private PaletteHolder slantPalettes; - - private boolean preventInterpolation; - - private boolean interpolateElevation; - - private NoiseSeeded biomeNoise; - - private double elevationWeight; - - private int blendDistance; - - private int blendStep; - - private double blendWeight; - - public WorldGenerator build(long seed) { - synchronized(gens) { - return gens.computeIfAbsent(seed, k -> { - NoiseSampler noise; - NoiseSampler elevation; - NoiseSampler carving; - try { - noise = new ExpressionSampler(noiseEquation, varScope, seed, noiseBuilderMap, functionTemplateMap); - elevation = elevationEquation == null ? new ConstantSampler(0) : new ExpressionSampler(elevationEquation, varScope, seed, noiseBuilderMap, functionTemplateMap); - carving = new ExpressionSampler(carvingEquation, varScope, seed, noiseBuilderMap, functionTemplateMap); - } catch(ParseException e) { - throw new RuntimeException(e); - } - return new WorldGenerator(palettes, slantPalettes, noise, elevation, carving, biomeNoise.apply(seed), elevationWeight, blendDistance, blendStep, blendWeight); - }); - } - } - - public void setBlendWeight(double blendWeight) { - this.blendWeight = blendWeight; - } - - public void setFunctionTemplateMap(Map functionTemplateMap) { - this.functionTemplateMap = functionTemplateMap; - } - - public void setBlendStep(int blendStep) { - this.blendStep = blendStep; - } - - public void setBlendDistance(int blendDistance) { - this.blendDistance = blendDistance; - } - - public void setBiomeNoise(NoiseSeeded biomeNoise) { - this.biomeNoise = biomeNoise; - } - - public void setElevationWeight(double elevationWeight) { - this.elevationWeight = elevationWeight; - } - - public void setNoiseEquation(String noiseEquation) { - this.noiseEquation = noiseEquation; - } - - public void setElevationEquation(String elevationEquation) { - this.elevationEquation = elevationEquation; - } - - public void setCarvingEquation(String carvingEquation) { - this.carvingEquation = carvingEquation; - } - - public Scope getVarScope() { - return varScope; - } - - public void setVarScope(Scope varScope) { - this.varScope = varScope; - } - - public void setNoiseBuilderMap(Map noiseBuilderMap) { - this.noiseBuilderMap = noiseBuilderMap; - } - - public PaletteHolder getPalettes() { - return palettes; - } - - public void setPalettes(PaletteHolder palettes) { - this.palettes = palettes; - } - - public PaletteHolder getSlantPalettes() { - return slantPalettes; - } - - public void setSlantPalettes(PaletteHolder slantPalettes) { - this.slantPalettes = slantPalettes; - } - - public boolean isPreventInterpolation() { - return preventInterpolation; - } - - public void setPreventInterpolation(boolean preventInterpolation) { - this.preventInterpolation = preventInterpolation; - } - - public void setInterpolateElevation(boolean interpolateElevation) { - this.interpolateElevation = interpolateElevation; - } - - public boolean interpolateElevation() { - return interpolateElevation; - } -} diff --git a/common/src/main/java/com/dfsek/terra/config/builder/UserDefinedBiomeBuilder.java b/common/src/main/java/com/dfsek/terra/config/builder/UserDefinedBiomeBuilder.java index 904a541c9..a4a39e3e3 100644 --- a/common/src/main/java/com/dfsek/terra/config/builder/UserDefinedBiomeBuilder.java +++ b/common/src/main/java/com/dfsek/terra/config/builder/UserDefinedBiomeBuilder.java @@ -66,4 +66,9 @@ public class UserDefinedBiomeBuilder implements BiomeBuilder { public ProbabilityCollection getVanillaBiomes() { return template.getVanilla(); } + + @Override + public BiomeTemplate getTemplate() { + return template; + } } diff --git a/common/src/main/java/com/dfsek/terra/config/dummy/DummyWorld.java b/common/src/main/java/com/dfsek/terra/config/dummy/DummyWorld.java index 06cd712fc..26082be6a 100644 --- a/common/src/main/java/com/dfsek/terra/config/dummy/DummyWorld.java +++ b/common/src/main/java/com/dfsek/terra/config/dummy/DummyWorld.java @@ -9,9 +9,6 @@ import com.dfsek.terra.api.platform.world.World; import com.dfsek.terra.api.platform.world.generator.ChunkGenerator; import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; -import java.io.File; -import java.util.UUID; - public class DummyWorld implements World { @Override public Object getHandle() { @@ -33,31 +30,11 @@ public class DummyWorld implements World { return () -> (GeneratorWrapper) () -> null; } - @Override - public String getName() { - return "DUMMY"; - } - - @Override - public UUID getUID() { - return UUID.randomUUID(); - } - - @Override - public boolean isChunkGenerated(int x, int z) { - return false; - } - @Override public Chunk getChunkAt(int x, int z) { throw new UnsupportedOperationException("Cannot get chunk in DummyWorld"); } - @Override - public File getWorldFolder() { - throw new UnsupportedOperationException("Cannot get folder of DummyWorld"); - } - @Override public Block getBlockAt(int x, int y, int z) { throw new UnsupportedOperationException("Cannot get block in DummyWorld"); diff --git a/common/src/main/java/com/dfsek/terra/config/fileloaders/Loader.java b/common/src/main/java/com/dfsek/terra/config/fileloaders/Loader.java index b6d2a4b8a..c4b0bf6c7 100644 --- a/common/src/main/java/com/dfsek/terra/config/fileloaders/Loader.java +++ b/common/src/main/java/com/dfsek/terra/config/fileloaders/Loader.java @@ -1,5 +1,6 @@ package com.dfsek.terra.config.fileloaders; +import com.dfsek.tectonic.config.Configuration; import com.dfsek.tectonic.exception.ConfigException; import com.dfsek.terra.api.util.GlueList; @@ -18,8 +19,12 @@ public abstract class Loader { * * @param consumer Something to do with the streams. */ - public Loader then(ExceptionalConsumer> consumer) throws ConfigException { - consumer.accept(new GlueList<>(streams.values())); + public Loader then(ExceptionalConsumer> consumer) throws ConfigException { + List list = new GlueList<>(); + streams.forEach((id, stream) -> { + list.add(new Configuration(stream, id)); + }); + consumer.accept(list); return this; } diff --git a/common/src/main/java/com/dfsek/terra/config/lang/LangUtil.java b/common/src/main/java/com/dfsek/terra/config/lang/LangUtil.java index 347a1e577..87f24b13e 100644 --- a/common/src/main/java/com/dfsek/terra/config/lang/LangUtil.java +++ b/common/src/main/java/com/dfsek/terra/config/lang/LangUtil.java @@ -17,7 +17,7 @@ public final class LangUtil { public static void load(String langID, TerraPlugin main) { Logger logger = main.logger(); File file = new File(main.getDataFolder(), "lang"); - try(JarFile jar = new JarFile(new File(TerraPlugin.class.getProtectionDomain().getCodeSource().getLocation().toURI()))) { + try(JarFile jar = main.getModJar()) { copyResourcesToDirectory(jar, "lang", file.toString()); } catch(IOException | URISyntaxException e) { main.getDebugLogger().error("Failed to dump language files!"); diff --git a/common/src/main/java/com/dfsek/terra/config/loaders/config/OreHolderLoader.java b/common/src/main/java/com/dfsek/terra/config/loaders/config/OreHolderLoader.java index 71b021fa3..08f682171 100644 --- a/common/src/main/java/com/dfsek/terra/config/loaders/config/OreHolderLoader.java +++ b/common/src/main/java/com/dfsek/terra/config/loaders/config/OreHolderLoader.java @@ -18,7 +18,7 @@ public class OreHolderLoader implements TypeLoader { Map map = (Map) o; for(Map.Entry entry : map.entrySet()) { - holder.add(configLoader.loadClass(Ore.class, entry.getKey()), (OreConfig) configLoader.loadType(OreConfig.class, entry.getValue())); + holder.add(configLoader.loadClass(Ore.class, entry.getKey()), configLoader.loadClass(OreConfig.class, entry.getValue()), entry.getKey()); } return holder; diff --git a/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/ExpressionFunctionTemplate.java b/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/ExpressionFunctionTemplate.java index 79aa5a41a..90926aea6 100644 --- a/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/ExpressionFunctionTemplate.java +++ b/common/src/main/java/com/dfsek/terra/config/loaders/config/sampler/templates/noise/ExpressionFunctionTemplate.java @@ -65,7 +65,6 @@ public class ExpressionFunctionTemplate extends SamplerTemplate noiseFunctionMap = new HashMap<>(); for(Map.Entry entry : expressions.entrySet()) { - System.out.println(entry); noiseFunctionMap.put(entry.getKey(), UserDefinedFunction.newInstance(entry.getValue(), new Parser(), new Scope())); } diff --git a/common/src/main/java/com/dfsek/terra/config/pack/ConfigPack.java b/common/src/main/java/com/dfsek/terra/config/pack/ConfigPack.java index a0f2c8bfe..871b4ebe3 100644 --- a/common/src/main/java/com/dfsek/terra/config/pack/ConfigPack.java +++ b/common/src/main/java/com/dfsek/terra/config/pack/ConfigPack.java @@ -2,6 +2,7 @@ package com.dfsek.terra.config.pack; import com.dfsek.paralithic.eval.parser.Scope; import com.dfsek.tectonic.abstraction.AbstractConfigLoader; +import com.dfsek.tectonic.config.Configuration; import com.dfsek.tectonic.exception.ConfigException; import com.dfsek.tectonic.exception.LoadException; import com.dfsek.tectonic.loading.ConfigLoader; @@ -110,6 +111,8 @@ public class ConfigPack implements LoaderRegistrar { private final TerraPlugin main; private final Loader loader; + private final Configuration configuration; + private final BiomeProvider.BiomeProviderBuilder biomeProviderBuilder; @@ -130,15 +133,20 @@ public class ConfigPack implements LoaderRegistrar { File pack = new File(folder, "pack.yml"); try { - selfLoader.load(template, new FileInputStream(pack)); + configuration = new Configuration(new FileInputStream(pack)); + selfLoader.load(template, configuration); main.logger().info("Loading config pack \"" + template.getID() + "\""); + main.getEventManager().callEvent(new ConfigPackPreLoadEvent(this, template -> selfLoader.load(template, configuration))); + load(l, main); + ConfigPackPostTemplate packPostTemplate = new ConfigPackPostTemplate(); selfLoader.load(packPostTemplate, new FileInputStream(pack)); biomeProviderBuilder = packPostTemplate.getProviderBuilder(); biomeProviderBuilder.build(0); // Build dummy provider to catch errors at load time. + checkDeadEntries(main); } catch(FileNotFoundException e) { throw new LoadException("No pack.yml file found in " + folder.getAbsolutePath(), e); } @@ -173,9 +181,12 @@ public class ConfigPack implements LoaderRegistrar { if(pack == null) throw new LoadException("No pack.yml file found in " + file.getName()); - selfLoader.load(template, file.getInputStream(pack)); + configuration = new Configuration(file.getInputStream(pack)); + selfLoader.load(template, configuration); main.logger().info("Loading config pack \"" + template.getID() + "\""); + main.getEventManager().callEvent(new ConfigPackPreLoadEvent(this, template -> selfLoader.load(template, configuration))); + load(l, main); ConfigPackPostTemplate packPostTemplate = new ConfigPackPostTemplate(); @@ -183,6 +194,7 @@ public class ConfigPack implements LoaderRegistrar { selfLoader.load(packPostTemplate, file.getInputStream(pack)); biomeProviderBuilder = packPostTemplate.getProviderBuilder(); biomeProviderBuilder.build(0); // Build dummy provider to catch errors at load time. + checkDeadEntries(main); } catch(IOException e) { throw new LoadException("Unable to load pack.yml from ZIP file", e); } @@ -198,9 +210,17 @@ public class ConfigPack implements LoaderRegistrar { for(C template : configTemplates) registry.add(template.getID(), factory.build(template, main)); } - private void load(long start, TerraPlugin main) throws ConfigException { - main.getEventManager().callEvent(new ConfigPackPreLoadEvent(this)); + private void checkDeadEntries(TerraPlugin main) { + biomeRegistry.getDeadEntries().forEach((id, value) -> main.getDebugLogger().warn("Dead entry in biome registry: '" + id + "'")); + paletteRegistry.getDeadEntries().forEach((id, value) -> main.getDebugLogger().warn("Dead entry in palette registry: '" + id + "'")); + floraRegistry.getDeadEntries().forEach((id, value) -> main.getDebugLogger().warn("Dead entry in flora registry: '" + id + "'")); + carverRegistry.getDeadEntries().forEach((id, value) -> main.getDebugLogger().warn("Dead entry in carver registry: '" + id + "'")); + treeRegistry.getDeadEntries().forEach((id, value) -> main.getDebugLogger().warn("Dead entry in tree registry: '" + id + "'")); + oreRegistry.getDeadEntries().forEach((id, value) -> main.getDebugLogger().warn("Dead entry in ore registry: '" + id + "'")); + } + + private void load(long start, TerraPlugin main) throws ConfigException { for(Map.Entry var : template.getVariables().entrySet()) { varScope.create(var.getKey(), var.getValue()); } @@ -225,15 +245,15 @@ public class ConfigPack implements LoaderRegistrar { }).close(); loader - .open("carving", ".yml").then(streams -> buildAll(new CarverFactory(this), carverRegistry, abstractConfigLoader.load(streams, CarverTemplate::new), main)).close() - .open("palettes", ".yml").then(streams -> buildAll(new PaletteFactory(), paletteRegistry, abstractConfigLoader.load(streams, PaletteTemplate::new), main)).close() - .open("ores", ".yml").then(streams -> buildAll(new OreFactory(), oreRegistry, abstractConfigLoader.load(streams, OreTemplate::new), main)).close() - .open("structures/trees", ".yml").then(streams -> buildAll(new TreeFactory(), treeRegistry, abstractConfigLoader.load(streams, TreeTemplate::new), main)).close() - .open("structures/structures", ".yml").then(streams -> buildAll(new StructureFactory(), structureRegistry, abstractConfigLoader.load(streams, StructureTemplate::new), main)).close() - .open("flora", ".yml").then(streams -> buildAll(new FloraFactory(), floraRegistry, abstractConfigLoader.load(streams, FloraTemplate::new), main)).close() - .open("biomes", ".yml").then(streams -> buildAll(new BiomeFactory(this), biomeRegistry, abstractConfigLoader.load(streams, () -> new BiomeTemplate(this, main)), main)).close(); + .open("carving", ".yml").then(configs -> buildAll(new CarverFactory(this), carverRegistry, abstractConfigLoader.loadConfigs(configs, CarverTemplate::new), main)).close() + .open("palettes", ".yml").then(configs -> buildAll(new PaletteFactory(), paletteRegistry, abstractConfigLoader.loadConfigs(configs, PaletteTemplate::new), main)).close() + .open("ores", ".yml").then(configs -> buildAll(new OreFactory(), oreRegistry, abstractConfigLoader.loadConfigs(configs, OreTemplate::new), main)).close() + .open("structures/trees", ".yml").then(configs -> buildAll(new TreeFactory(), treeRegistry, abstractConfigLoader.loadConfigs(configs, TreeTemplate::new), main)).close() + .open("structures/structures", ".yml").then(configs -> buildAll(new StructureFactory(), structureRegistry, abstractConfigLoader.loadConfigs(configs, StructureTemplate::new), main)).close() + .open("flora", ".yml").then(configs -> buildAll(new FloraFactory(), floraRegistry, abstractConfigLoader.loadConfigs(configs, FloraTemplate::new), main)).close() + .open("biomes", ".yml").then(configs -> buildAll(new BiomeFactory(this), biomeRegistry, abstractConfigLoader.loadConfigs(configs, () -> new BiomeTemplate(this, main)), main)).close(); - main.getEventManager().callEvent(new ConfigPackPostLoadEvent(this)); + main.getEventManager().callEvent(new ConfigPackPostLoadEvent(this, template -> selfLoader.load(template, configuration))); main.logger().info("Loaded config pack \"" + template.getID() + "\" v" + template.getVersion() + " by " + template.getAuthor() + " in " + (System.nanoTime() - start) / 1000000D + "ms."); } diff --git a/common/src/main/java/com/dfsek/terra/config/pack/ConfigPackTemplate.java b/common/src/main/java/com/dfsek/terra/config/pack/ConfigPackTemplate.java index c49d2b667..bfebcb717 100644 --- a/common/src/main/java/com/dfsek/terra/config/pack/ConfigPackTemplate.java +++ b/common/src/main/java/com/dfsek/terra/config/pack/ConfigPackTemplate.java @@ -73,6 +73,46 @@ public class ConfigPackTemplate implements ConfigTemplate { @Default private String version = "0.1.0"; + @Value("disable.carvers") + @Default + private boolean disableCarvers = false; + + @Value("disable.structures") + @Default + private boolean disableStructures = false; + + @Value("disable.ores") + @Default + private boolean disableOres = false; + + @Value("disable.trees") + @Default + private boolean disableTrees = false; + + @Value("disable.flora") + @Default + private boolean disableFlora = false; + + public boolean disableCarvers() { + return disableCarvers; + } + + public boolean disableFlora() { + return disableFlora; + } + + public boolean disableOres() { + return disableOres; + } + + public boolean disableStructures() { + return disableStructures; + } + + public boolean disableTrees() { + return disableTrees; + } + public LinkedHashMap getFunctions() { return functions; } diff --git a/common/src/main/java/com/dfsek/terra/config/templates/BiomeTemplate.java b/common/src/main/java/com/dfsek/terra/config/templates/BiomeTemplate.java index b3a28548b..69ee69122 100644 --- a/common/src/main/java/com/dfsek/terra/config/templates/BiomeTemplate.java +++ b/common/src/main/java/com/dfsek/terra/config/templates/BiomeTemplate.java @@ -190,10 +190,19 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf @Default private Map carvers = new HashMap<>(); + @Value("colors") + @Abstractable + @Default + private Map colors = new HashMap<>(); // Plain ol' map, so platforms can decide what to do with colors (if anything). + public Set getTags() { return tags; } + public Map getColors() { + return colors; + } + public Map getCarvers() { return carvers; } diff --git a/common/src/main/java/com/dfsek/terra/profiler/DataHolder.java b/common/src/main/java/com/dfsek/terra/profiler/DataHolder.java deleted file mode 100644 index e90671e7f..000000000 --- a/common/src/main/java/com/dfsek/terra/profiler/DataHolder.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.dfsek.terra.profiler; - -/** - * Class to hold a profiler data value. Contains formatting method to highlight value based on desired range. - */ -public class DataHolder { - private final long desired; - private final DataType type; - private final double desiredRangePercent; - - /** - * Constructs a DataHolder with a DataType and a desired value, including a percentage around the desired value considered acceptable - * - * @param type The type of data held in this instance. - * @param desired The desired value. This should be the average value of whatever is being measured. - * @param desiredRangePercent The percentage around the desired value to be considered acceptable. - */ - public DataHolder(DataType type, long desired, double desiredRangePercent) { - this.desired = desired; - this.type = type; - this.desiredRangePercent = desiredRangePercent; - } - - /** - * Returns a String, formatted with Bungee ChatColors.
- * GREEN if the value is better than desired and outside of acceptable range.
- * YELLOW if the value is better or worse than desired, and within acceptable range.
- * RED if the value is worse than desired and outside of acceptable range.
- * - * @param data The data to format. - * @return String - The formatted data. - */ - public String getFormattedData(long data) { - return type.getFormatted(data); - } -} diff --git a/common/src/main/java/com/dfsek/terra/profiler/DataType.java b/common/src/main/java/com/dfsek/terra/profiler/DataType.java deleted file mode 100644 index eaaad91ea..000000000 --- a/common/src/main/java/com/dfsek/terra/profiler/DataType.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.dfsek.terra.profiler; - -import net.jafama.FastMath; - -public enum DataType { - PERIOD_MILLISECONDS(Desire.LOW, 1000000, "ms"), PERIOD_NANOSECONDS(Desire.LOW, 1, "ns"); - private final Desire desire; - private final long divisor; - private final String unit; - - DataType(Desire d, long divisor, String unit) { - this.desire = d; - this.divisor = divisor; - this.unit = unit; - } - - public String getFormatted(long value) { - return (double) FastMath.round(((double) value / divisor) * 100D) / 100D + unit; - } - - public Desire getDesire() { - return desire; - } -} diff --git a/common/src/main/java/com/dfsek/terra/profiler/Desire.java b/common/src/main/java/com/dfsek/terra/profiler/Desire.java deleted file mode 100644 index df1488138..000000000 --- a/common/src/main/java/com/dfsek/terra/profiler/Desire.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.dfsek.terra.profiler; - - -/** - * Enum to represent the "goal" of a value, whether it is desirable for the value to be high (e.g. Frequency), or low (e.g. Period) - */ -public enum Desire { - LOW, HIGH - -} diff --git a/common/src/main/java/com/dfsek/terra/profiler/Frame.java b/common/src/main/java/com/dfsek/terra/profiler/Frame.java new file mode 100644 index 000000000..7b7be66b5 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/profiler/Frame.java @@ -0,0 +1,24 @@ +package com.dfsek.terra.profiler; + +public class Frame { + private final String id; + private final long start; + + public Frame(String id) { + this.id = id; + this.start = System.nanoTime(); + } + + public String getId() { + return id; + } + + public long getStart() { + return start; + } + + @Override + public String toString() { + return id; + } +} diff --git a/common/src/main/java/com/dfsek/terra/profiler/Measurement.java b/common/src/main/java/com/dfsek/terra/profiler/Measurement.java deleted file mode 100644 index 3bae77f52..000000000 --- a/common/src/main/java/com/dfsek/terra/profiler/Measurement.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.dfsek.terra.profiler; - -import com.dfsek.terra.api.math.MathUtil; -import com.dfsek.terra.api.util.GlueList; -import net.jafama.FastMath; - -import java.math.BigInteger; -import java.util.LinkedList; -import java.util.List; - -/** - * Class to record and hold all data for a single type of measurement performed by the profiler. - */ -public class Measurement { - private final List measurements = new LinkedList<>(); - private final long desirable; - private final DataType type; - private long min = Long.MAX_VALUE; - private long max = Long.MIN_VALUE; - - /** - * Constructs a new Measurement with a desired value and DataType. - * - * @param desirable The desired value of the measurement. - * @param type The type of data the measurement is holding. - */ - public Measurement(long desirable, DataType type) { - this.desirable = desirable; - this.type = type; - } - - public void record(long value) { - max = FastMath.max(value, max); - min = FastMath.min(value, min); - measurements.add(value); - } - - public int size() { - return measurements.size(); - } - - public ProfileFuture beginMeasurement() { - ProfileFuture future = new ProfileFuture(); - long current = System.nanoTime(); - future.thenRun(() -> record(System.nanoTime() - current)); - return future; - } - - public void reset() { - min = Long.MAX_VALUE; - max = Long.MIN_VALUE; - measurements.clear(); - } - - public DataHolder getDataHolder() { - return new DataHolder(type, desirable, 0.25); - } - - public long getMin() { - if(min == Long.MAX_VALUE) return 0; - return min; - } - - public long getMax() { - if(max == Long.MIN_VALUE) return 0; - return max; - } - - public long average() { - BigInteger running = BigInteger.valueOf(0); - List mTemp = new GlueList<>(measurements); - for(Long l : mTemp) { - running = running.add(BigInteger.valueOf(l)); - } - if(measurements.size() == 0) return 0; - return running.divide(BigInteger.valueOf(measurements.size())).longValue(); - } - - public double getStdDev() { - return MathUtil.standardDeviation(new GlueList<>(measurements)); - } - - public int entries() { - return measurements.size(); - } - -} diff --git a/common/src/main/java/com/dfsek/terra/profiler/ProfileFrame.java b/common/src/main/java/com/dfsek/terra/profiler/ProfileFrame.java new file mode 100644 index 000000000..bf5c64dab --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/profiler/ProfileFrame.java @@ -0,0 +1,14 @@ +package com.dfsek.terra.profiler; + +public class ProfileFrame implements AutoCloseable { + private final Runnable action; + + public ProfileFrame(Runnable action) { + this.action = action; + } + + @Override + public void close() { + action.run(); + } +} diff --git a/common/src/main/java/com/dfsek/terra/profiler/ProfileFuture.java b/common/src/main/java/com/dfsek/terra/profiler/ProfileFuture.java deleted file mode 100644 index fdecac8f8..000000000 --- a/common/src/main/java/com/dfsek/terra/profiler/ProfileFuture.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.dfsek.terra.profiler; - -import java.util.concurrent.CompletableFuture; - -public class ProfileFuture extends CompletableFuture implements AutoCloseable { - public ProfileFuture() { - super(); - } - - public boolean complete() { - return super.complete(true); - } - - @Override - public void close() { - this.complete(); - } -} diff --git a/common/src/main/java/com/dfsek/terra/profiler/Profiler.java b/common/src/main/java/com/dfsek/terra/profiler/Profiler.java new file mode 100644 index 000000000..65560ad56 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/profiler/Profiler.java @@ -0,0 +1,56 @@ +package com.dfsek.terra.profiler; + +import java.util.Map; + +public interface Profiler { + /** + * Push a frame to this profiler. + * + * @param frame ID of frame. + */ + void push(String frame); + + /** + * Pop a frame from this profiler. + * + * @param frame ID of frame. Must match ID + * at the top of the profiler stack. + */ + void pop(String frame); + + /** + * Start profiling. + */ + void start(); + + /** + * Stop profiling. + */ + void stop(); + + /** + * Get the profiler data. + * + * @return Profiler data. + */ + Map getTimings(); + + /** + * Return a {@link AutoCloseable} implementation that + * may be used in a try-with-resources statement for + * more intuitive profiling, with auto-push/pop. + * + * @param frame ID of frame. + * @return {@link AutoCloseable} implementation for use + * in try-with-resources. + */ + default ProfileFrame profile(String frame) { + push(frame); + return new ProfileFrame(() -> pop(frame)); + } + + /** + * Clear the profiler data. + */ + void reset(); +} diff --git a/common/src/main/java/com/dfsek/terra/profiler/ProfilerImpl.java b/common/src/main/java/com/dfsek/terra/profiler/ProfilerImpl.java new file mode 100644 index 000000000..f91deecd1 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/profiler/ProfilerImpl.java @@ -0,0 +1,91 @@ +package com.dfsek.terra.profiler; + +import com.dfsek.terra.api.util.mutable.MutableInteger; +import com.dfsek.terra.profiler.exception.MalformedStackException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +public class ProfilerImpl implements Profiler { + private static final ThreadLocal> THREAD_STACK = ThreadLocal.withInitial(Stack::new); + private static final ThreadLocal>> TIMINGS = ThreadLocal.withInitial(HashMap::new); + private final List>> accessibleThreadMaps = new ArrayList<>(); + private volatile boolean running = false; + private static boolean instantiated = false; + + private static final ThreadLocal SAFE = ThreadLocal.withInitial(() -> false); + private static final ThreadLocal STACK_SIZE = ThreadLocal.withInitial(() -> new MutableInteger(0)); + + public ProfilerImpl() { + if(instantiated) throw new IllegalStateException("Only one instance of Profiler may exist!"); + instantiated = true; + } + + @Override + public void push(String frame) { + STACK_SIZE.get().increment(); + if(running && SAFE.get()) { + Stack stack = THREAD_STACK.get(); + stack.push(new Frame(stack.isEmpty() ? frame : stack.peek().getId() + "." + frame)); + } else SAFE.set(false); + } + + @Override + public void pop(String frame) { + MutableInteger size = STACK_SIZE.get(); + size.decrement(); + if(running && SAFE.get()) { + long time = System.nanoTime(); + Stack stack = THREAD_STACK.get(); + + Map> timingsMap = TIMINGS.get(); + + if(timingsMap.size() == 0) { + synchronized(accessibleThreadMaps) { + accessibleThreadMaps.add(timingsMap); + } + } + + Frame top = stack.pop(); + if((stack.size() != 0 && !top.getId().endsWith("." + frame)) || (stack.size() == 0 && !top.getId().equals(frame))) + throw new MalformedStackException("Expected " + frame + ", found " + top); + + List timings = timingsMap.computeIfAbsent(top.getId(), id -> new ArrayList<>()); + + timings.add(time - top.getStart()); + } + if(size.get() == 0) SAFE.set(true); + } + + @Override + public void start() { + running = true; + } + + @Override + public void stop() { + running = false; + } + + @Override + public Map getTimings() { + Map map = new HashMap<>(); + accessibleThreadMaps.forEach(smap -> smap.forEach((key, list) -> { + String[] keys = key.split("\\."); + Timings timings = map.computeIfAbsent(keys[0], id -> new Timings()); + for(int i = 1; i < keys.length; i++) { + timings = timings.getSubItem(keys[i]); + } + list.forEach(timings::addTime); + })); + return map; + } + + @Override + public void reset() { + accessibleThreadMaps.forEach(Map::clear); + } +} diff --git a/common/src/main/java/com/dfsek/terra/profiler/Timings.java b/common/src/main/java/com/dfsek/terra/profiler/Timings.java new file mode 100644 index 000000000..3875285b3 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/profiler/Timings.java @@ -0,0 +1,73 @@ +package com.dfsek.terra.profiler; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class Timings { + private final Map subItems = new HashMap<>(); + + private final List timings = new ArrayList<>(); + + public void addTime(long time) { + timings.add(time); + } + + public List getTimings() { + return timings; + } + + public double average() { + return (double) timings.stream().reduce(0L, Long::sum) / timings.size(); + } + + public long max() { + return timings.stream().mapToLong(Long::longValue).max().orElse(0L); + } + + public long min() { + return timings.stream().mapToLong(Long::longValue).min().orElse(0L); + } + + public double sum() { + return timings.stream().mapToDouble(Long::doubleValue).sum(); + } + + public Timings getSubItem(String id) { + return subItems.computeIfAbsent(id, s -> new Timings()); + } + + public String toString(int indent, Timings parent, Set branches) { + StringBuilder builder = new StringBuilder(); + + builder.append((double) min() / 1000000).append("ms min / ").append(average() / 1000000).append("ms avg / ") + .append((double) max() / 1000000).append("ms max (").append(timings.size()).append(" samples, ") + .append((sum() / parent.sum()) * 100).append("% of parent)"); + + List frames = new ArrayList<>(); + Set newBranches = new HashSet<>(branches); + newBranches.add(indent); + subItems.forEach((id, timings) -> frames.add(id + ": " + timings.toString(indent + 1, this, newBranches))); + + for(int i = 0; i < frames.size(); i++) { + builder.append('\n'); + for(int j = 0; j < indent; j++) { + if(branches.contains(j)) builder.append("│ "); + else builder.append(" "); + } + if(i == frames.size() - 1 && !frames.get(i).contains("\n")) builder.append("└───"); + else builder.append("├───"); + builder.append(frames.get(i)); + } + return builder.toString(); + } + + @Override + public String toString() { + return toString(1, this, Collections.emptySet()); + } +} diff --git a/common/src/main/java/com/dfsek/terra/profiler/WorldProfiler.java b/common/src/main/java/com/dfsek/terra/profiler/WorldProfiler.java deleted file mode 100644 index a1ea04908..000000000 --- a/common/src/main/java/com/dfsek/terra/profiler/WorldProfiler.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.dfsek.terra.profiler; - -import com.dfsek.terra.api.platform.world.World; -import com.dfsek.terra.world.TerraWorld; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import net.jafama.FastMath; - -import java.util.Map; - -public class WorldProfiler { - private final BiMap measures = HashBiMap.create(); - private final World world; - private boolean isProfiling; - - public WorldProfiler(World w) { - if(!TerraWorld.isTerraWorld(w)) - throw new IllegalArgumentException("Attempted to instantiate profiler on non-Terra managed world!"); - this.addMeasurement(new Measurement(2500000, DataType.PERIOD_MILLISECONDS), "TotalChunkGenTime") - .addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "FloraTime") - .addMeasurement(new Measurement(10000000, DataType.PERIOD_MILLISECONDS), "TreeTime") - .addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "OreTime") - .addMeasurement(new Measurement(5000000, DataType.PERIOD_MILLISECONDS), "CaveTime") - .addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "StructureTime"); - - isProfiling = false; - this.world = w; - } - - public String getResultsFormatted() { - if(! isProfiling) return "Profiler is not currently running."; - StringBuilder result = new StringBuilder("Gaea World Profiler Results (Min / Avg / Max / Std Dev): \n"); - for(Map.Entry e : measures.entrySet()) { - result - .append(e.getKey()) - .append(": ") - .append(e.getValue().getDataHolder().getFormattedData(e.getValue().getMin())) - .append(" / ") - .append(e.getValue().getDataHolder().getFormattedData(e.getValue().average())) - .append(" / ") - .append(e.getValue().getDataHolder().getFormattedData(e.getValue().getMax())) - .append(" / ") - .append((double) FastMath.round((e.getValue().getStdDev() / 1000000) * 100D) / 100D) - .append("ms") - .append(" (x").append(e.getValue().size()).append(")\n"); - } - return result.toString(); - } - - public void reset() { - for(Map.Entry e : measures.entrySet()) { - e.getValue().reset(); - } - } - - public com.dfsek.terra.profiler.WorldProfiler addMeasurement(Measurement m, String name) { - measures.put(name, m); - return this; - } - - public void setMeasurement(String id, long value) { - if(isProfiling) measures.get(id).record(value); - } - - public ProfileFuture measure(String id) { - if(isProfiling) return measures.get(id).beginMeasurement(); - else return null; - } - - public String getID(Measurement m) { - return measures.inverse().get(m); - } - - public boolean isProfiling() { - return isProfiling; - } - - public void setProfiling(boolean enabled) { - this.isProfiling = enabled; - } - - public World getWorld() { - return world; - } -} diff --git a/common/src/main/java/com/dfsek/terra/profiler/exception/MalformedStackException.java b/common/src/main/java/com/dfsek/terra/profiler/exception/MalformedStackException.java new file mode 100644 index 000000000..0ae345a0f --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/profiler/exception/MalformedStackException.java @@ -0,0 +1,17 @@ +package com.dfsek.terra.profiler.exception; + +public class MalformedStackException extends ProfilerException { + private static final long serialVersionUID = -3009539681021691054L; + + public MalformedStackException(String message) { + super(message); + } + + public MalformedStackException(String message, Throwable cause) { + super(message, cause); + } + + public MalformedStackException(Throwable cause) { + super(cause); + } +} diff --git a/common/src/main/java/com/dfsek/terra/profiler/exception/ProfilerException.java b/common/src/main/java/com/dfsek/terra/profiler/exception/ProfilerException.java new file mode 100644 index 000000000..27c6d734f --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/profiler/exception/ProfilerException.java @@ -0,0 +1,17 @@ +package com.dfsek.terra.profiler.exception; + +public class ProfilerException extends RuntimeException { + private static final long serialVersionUID = 8206737998791649002L; + + public ProfilerException(String message) { + super(message); + } + + public ProfilerException(String message, Throwable cause) { + super(message, cause); + } + + public ProfilerException(Throwable cause) { + super(cause); + } +} diff --git a/common/src/main/java/com/dfsek/terra/registry/OpenRegistry.java b/common/src/main/java/com/dfsek/terra/registry/OpenRegistry.java index 1cd0b81f2..6f4c179f0 100644 --- a/common/src/main/java/com/dfsek/terra/registry/OpenRegistry.java +++ b/common/src/main/java/com/dfsek/terra/registry/OpenRegistry.java @@ -7,18 +7,19 @@ import com.dfsek.terra.registry.exception.DuplicateEntryException; import java.lang.reflect.Type; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.stream.Collectors; /** * Registry implementation with read/write access. For internal use only. * @param */ public class OpenRegistry implements Registry { - private final Map objects = new HashMap<>(); + private final Map> objects = new HashMap<>(); @Override public T load(Type type, Object o, ConfigLoader configLoader) throws LoadException { @@ -35,6 +36,10 @@ public class OpenRegistry implements Registry { * @param value Value to add. */ public boolean add(String identifier, T value) { + return add(identifier, new Entry<>(value)); + } + + protected boolean add(String identifier, Entry value) { boolean exists = objects.containsKey(identifier); objects.put(identifier, value); return exists; @@ -60,22 +65,24 @@ public class OpenRegistry implements Registry { @Override public T get(String identifier) { - return objects.get(identifier); + Entry entry = objects.get(identifier); + if(entry == null) return null; + return entry.getValue(); } @Override public void forEach(Consumer consumer) { - objects.forEach((id, obj) -> consumer.accept(obj)); + objects.forEach((id, obj) -> consumer.accept(obj.getRaw())); } @Override public void forEach(BiConsumer consumer) { - objects.forEach(consumer); + objects.forEach((id, entry) -> consumer.accept(id, entry.getRaw())); } @Override public Set entries() { - return new HashSet<>(objects.values()); + return objects.values().stream().map(Entry::getRaw).collect(Collectors.toSet()); } @Override @@ -83,10 +90,41 @@ public class OpenRegistry implements Registry { return objects.keySet(); } + public Map getDeadEntries() { + Map dead = new HashMap<>(); + objects.forEach((id, entry) -> { + if(entry.dead()) dead.put(id, entry.value); // dont increment value here. + }); + return dead; + } + /** * Clears all entries from the registry. */ public void clear() { objects.clear(); } + + + protected static final class Entry { + private final T value; + private final AtomicInteger access = new AtomicInteger(0); + + public Entry(T value) { + this.value = value; + } + + public T getValue() { + access.incrementAndGet(); + return value; + } + + private T getRaw() { + return value; + } + + public boolean dead() { + return access.get() == 0; + } + } } diff --git a/common/src/main/java/com/dfsek/terra/registry/config/FloraRegistry.java b/common/src/main/java/com/dfsek/terra/registry/config/FloraRegistry.java index 71fd680cd..65858b8ad 100644 --- a/common/src/main/java/com/dfsek/terra/registry/config/FloraRegistry.java +++ b/common/src/main/java/com/dfsek/terra/registry/config/FloraRegistry.java @@ -57,7 +57,9 @@ public class FloraRegistry extends OpenRegistry { private void addItem(String id, Callable flora) { try { - add(id, flora.call()); + Entry entry = new Entry<>(flora.call()); + entry.getValue(); // Mark as not dead. + add(id, entry); } catch(Exception e) { main.logger().warning("Failed to load Flora item: " + id + ": " + e.getMessage()); } @@ -66,10 +68,4 @@ public class FloraRegistry extends OpenRegistry { private BlockData data(String s) { return main.getWorldHandle().createBlockData(s); } - - - @Override - public Flora get(String identifier) { - return super.get(identifier); - } } diff --git a/common/src/main/java/com/dfsek/terra/world/TerraWorld.java b/common/src/main/java/com/dfsek/terra/world/TerraWorld.java index d173d12f2..cc37116a2 100644 --- a/common/src/main/java/com/dfsek/terra/world/TerraWorld.java +++ b/common/src/main/java/com/dfsek/terra/world/TerraWorld.java @@ -13,7 +13,6 @@ import com.dfsek.terra.api.world.generation.TerraChunkGenerator; import com.dfsek.terra.api.world.palette.Palette; import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.WorldConfig; -import com.dfsek.terra.profiler.WorldProfiler; import com.dfsek.terra.world.generation.math.samplers.Sampler; import net.jafama.FastMath; @@ -21,33 +20,25 @@ public class TerraWorld { private final BiomeProvider provider; private final WorldConfig config; private final boolean safe; - private final WorldProfiler profiler; private final World world; private final BlockData air; public TerraWorld(World w, ConfigPack c, TerraPlugin main) { - if(!isTerraWorld(w)) throw new IllegalArgumentException("World " + w + " is not a Terra World!"); + if(!w.isTerraWorld()) throw new IllegalArgumentException("World " + w + " is not a Terra World!"); this.world = w; config = c.toWorldConfig(this); this.provider = config.getProvider(); - profiler = new WorldProfiler(w); air = main.getWorldHandle().createBlockData("minecraft:air"); - main.getEventManager().callEvent(new TerraWorldLoadEvent(this)); + main.getEventManager().callEvent(new TerraWorldLoadEvent(this, c)); safe = true; } - public static boolean isTerraWorld(World w) { - return w.getGenerator().getHandle() instanceof GeneratorWrapper; - } public World getWorld() { return world; } - public TerraChunkGenerator getGenerator() { - return ((GeneratorWrapper) world.getGenerator().getHandle()).getHandle(); - } public BiomeProvider getBiomeProvider() { return provider; @@ -61,9 +52,6 @@ public class TerraWorld { return safe; } - public WorldProfiler getProfiler() { - return profiler; - } /** * Get a block at an ungenerated location diff --git a/common/src/main/java/com/dfsek/terra/world/generation/generators/DefaultChunkGenerator2D.java b/common/src/main/java/com/dfsek/terra/world/generation/generators/DefaultChunkGenerator2D.java index ed93986bc..be9268205 100644 --- a/common/src/main/java/com/dfsek/terra/world/generation/generators/DefaultChunkGenerator2D.java +++ b/common/src/main/java/com/dfsek/terra/world/generation/generators/DefaultChunkGenerator2D.java @@ -10,20 +10,27 @@ import com.dfsek.terra.api.util.world.PaletteUtil; import com.dfsek.terra.api.world.biome.TerraBiome; import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.provider.BiomeProvider; +import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.api.world.generation.TerraChunkGenerator; import com.dfsek.terra.api.world.palette.Palette; import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.templates.BiomeTemplate; -import com.dfsek.terra.profiler.ProfileFuture; +import com.dfsek.terra.profiler.ProfileFrame; import com.dfsek.terra.world.Carver; import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.carving.NoiseCarver; import com.dfsek.terra.world.generation.math.SamplerCache; import com.dfsek.terra.world.generation.math.samplers.Sampler; import com.dfsek.terra.world.generation.math.samplers.Sampler2D; +import com.dfsek.terra.world.population.CavePopulator; +import com.dfsek.terra.world.population.OrePopulator; +import com.dfsek.terra.world.population.StructurePopulator; +import com.dfsek.terra.world.population.TreePopulator; import net.jafama.FastMath; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.List; import java.util.Random; public class DefaultChunkGenerator2D implements TerraChunkGenerator { @@ -31,12 +38,18 @@ public class DefaultChunkGenerator2D implements TerraChunkGenerator { private final TerraPlugin main; private final Carver carver; + private final List blockPopulators = new ArrayList<>(); private final SamplerCache cache; public DefaultChunkGenerator2D(ConfigPack c, TerraPlugin main, SamplerCache cache) { this.configPack = c; this.main = main; + blockPopulators.add(new CavePopulator(main)); + blockPopulators.add(new StructurePopulator(main)); + blockPopulators.add(new OrePopulator(main)); + blockPopulators.add(new TreePopulator(main)); + blockPopulators.add(new TreePopulator(main)); carver = new NoiseCarver(new Range(0, 255), main.getWorldHandle().createBlockData("minecraft:air"), main); this.cache = cache; } @@ -81,7 +94,7 @@ public class DefaultChunkGenerator2D implements TerraChunkGenerator { public ChunkData generateChunkData(@NotNull World world, Random random, int chunkX, int chunkZ, ChunkData chunk) { TerraWorld tw = main.getWorld(world); BiomeProvider grid = tw.getBiomeProvider(); - try(ProfileFuture ignore = tw.getProfiler().measure("TotalChunkGenTime")) { + try(ProfileFrame ignore = main.getProfiler().profile("chunk_base_2d")) { if(!tw.isSafe()) return chunk; int xOrig = (chunkX << 4); int zOrig = (chunkZ << 4); @@ -125,4 +138,9 @@ public class DefaultChunkGenerator2D implements TerraChunkGenerator { public Sampler createSampler(int chunkX, int chunkZ, BiomeProvider provider, World world, int elevationSmooth) { return new Sampler2D(chunkX, chunkZ, provider, world, elevationSmooth); } + + @Override + public List getPopulators() { + return blockPopulators; + } } diff --git a/common/src/main/java/com/dfsek/terra/world/generation/generators/DefaultChunkGenerator3D.java b/common/src/main/java/com/dfsek/terra/world/generation/generators/DefaultChunkGenerator3D.java index af656ac84..01ddbe380 100644 --- a/common/src/main/java/com/dfsek/terra/world/generation/generators/DefaultChunkGenerator3D.java +++ b/common/src/main/java/com/dfsek/terra/world/generation/generators/DefaultChunkGenerator3D.java @@ -17,19 +17,27 @@ import com.dfsek.terra.api.util.world.PaletteUtil; import com.dfsek.terra.api.world.biome.TerraBiome; import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.provider.BiomeProvider; +import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.api.world.generation.TerraChunkGenerator; import com.dfsek.terra.api.world.palette.Palette; import com.dfsek.terra.api.world.palette.SinglePalette; import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.templates.BiomeTemplate; -import com.dfsek.terra.profiler.ProfileFuture; +import com.dfsek.terra.profiler.ProfileFrame; import com.dfsek.terra.world.Carver; import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.carving.NoiseCarver; import com.dfsek.terra.world.generation.math.samplers.Sampler; import com.dfsek.terra.world.generation.math.samplers.Sampler3D; +import com.dfsek.terra.world.population.CavePopulator; +import com.dfsek.terra.world.population.FloraPopulator; +import com.dfsek.terra.world.population.OrePopulator; +import com.dfsek.terra.world.population.StructurePopulator; +import com.dfsek.terra.world.population.TreePopulator; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Random; @@ -38,14 +46,20 @@ public class DefaultChunkGenerator3D implements TerraChunkGenerator { private final TerraPlugin main; private final BlockType water; private final SinglePalette blank; + private final List blockPopulators = new ArrayList<>(); private final Carver carver; - - public DefaultChunkGenerator3D(ConfigPack c, TerraPlugin main) { this.configPack = c; this.main = main; + + blockPopulators.add(new CavePopulator(main)); + blockPopulators.add(new StructurePopulator(main)); + blockPopulators.add(new OrePopulator(main)); + blockPopulators.add(new TreePopulator(main)); + blockPopulators.add(new FloraPopulator(main)); + carver = new NoiseCarver(new Range(0, 255), main.getWorldHandle().createBlockData("minecraft:air"), main); water = main.getWorldHandle().createBlockData("minecraft:water").getBlockType(); blank = new SinglePalette<>(main.getWorldHandle().createBlockData("minecraft:air")); @@ -89,9 +103,10 @@ public class DefaultChunkGenerator3D implements TerraChunkGenerator { @Override @SuppressWarnings({"try"}) public ChunkData generateChunkData(@NotNull World world, Random random, int chunkX, int chunkZ, ChunkData chunk) { - TerraWorld tw = main.getWorld(world); - BiomeProvider grid = tw.getBiomeProvider(); - try(ProfileFuture ignore = tw.getProfiler().measure("TotalChunkGenTime")) { + try(ProfileFrame ignore = main.getProfiler().profile("chunk_base_3d")) { + TerraWorld tw = main.getWorld(world); + BiomeProvider grid = tw.getBiomeProvider(); + if(!tw.isSafe()) return chunk; int xOrig = (chunkX << 4); int zOrig = (chunkZ << 4); @@ -105,7 +120,7 @@ public class DefaultChunkGenerator3D implements TerraChunkGenerator { int cx = xOrig + x; int cz = zOrig + z; - TerraBiome b = grid.getBiome(xOrig + x, zOrig + z); + TerraBiome b = grid.getBiome(cx, cz); BiomeTemplate c = ((UserDefinedBiome) b).getConfig(); int sea = c.getSeaLevel(); @@ -212,17 +227,20 @@ public class DefaultChunkGenerator3D implements TerraChunkGenerator { return false; } + @SuppressWarnings({"try"}) static void biomes(@NotNull World world, int chunkX, int chunkZ, @NotNull BiomeGrid biome, TerraPlugin main) { - int xOrig = (chunkX << 4); - int zOrig = (chunkZ << 4); - BiomeProvider grid = main.getWorld(world).getBiomeProvider(); - for(int x = 0; x < 4; x++) { - for(int z = 0; z < 4; z++) { - int cx = xOrig + (x << 2); - int cz = zOrig + (z << 2); - TerraBiome b = grid.getBiome(cx, cz); + try(ProfileFrame ignore = main.getProfiler().profile("biomes")) { + int xOrig = (chunkX << 4); + int zOrig = (chunkZ << 4); + BiomeProvider grid = main.getWorld(world).getBiomeProvider(); + for(int x = 0; x < 4; x++) { + for(int z = 0; z < 4; z++) { + int cx = xOrig + (x << 2); + int cz = zOrig + (z << 2); + TerraBiome b = grid.getBiome(cx, cz); - biome.setBiome(cx, cz, b.getVanillaBiomes().get(b.getGenerator(world).getBiomeNoise(), cx, 0, cz)); + biome.setBiome(cx, cz, b.getVanillaBiomes().get(b.getGenerator(world).getBiomeNoise(), cx, 0, cz)); + } } } } @@ -236,4 +254,9 @@ public class DefaultChunkGenerator3D implements TerraChunkGenerator { public Sampler createSampler(int chunkX, int chunkZ, BiomeProvider provider, World world, int elevationSmooth) { return new Sampler3D(chunkX, chunkZ, provider, world, elevationSmooth); } + + @Override + public List getPopulators() { + return blockPopulators; + } } diff --git a/common/src/main/java/com/dfsek/terra/world/generation/math/SamplerCache.java b/common/src/main/java/com/dfsek/terra/world/generation/math/SamplerCache.java index 64d4577b1..e9965d49b 100644 --- a/common/src/main/java/com/dfsek/terra/world/generation/math/SamplerCache.java +++ b/common/src/main/java/com/dfsek/terra/world/generation/math/SamplerCache.java @@ -25,7 +25,7 @@ public class SamplerCache { public Sampler load(@NotNull Long key) { int cx = (int) (key >> 32); int cz = (int) key.longValue(); - return world.getGenerator().createSampler(cx, cz, world.getBiomeProvider(), world.getWorld(), world.getConfig().getTemplate().getElevationBlend()); + return world.getWorld().getTerraGenerator().createSampler(cx, cz, world.getBiomeProvider(), world.getWorld(), world.getConfig().getTemplate().getElevationBlend()); } }); } diff --git a/common/src/main/java/com/dfsek/terra/world/population/CavePopulator.java b/common/src/main/java/com/dfsek/terra/world/population/CavePopulator.java index 0dc4524fb..09ce5976a 100644 --- a/common/src/main/java/com/dfsek/terra/world/population/CavePopulator.java +++ b/common/src/main/java/com/dfsek/terra/world/population/CavePopulator.java @@ -9,11 +9,12 @@ import com.dfsek.terra.api.platform.handle.WorldHandle; import com.dfsek.terra.api.platform.world.Chunk; import com.dfsek.terra.api.platform.world.World; import com.dfsek.terra.api.util.world.PopulationUtil; +import com.dfsek.terra.api.world.generation.Chunkified; import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.carving.UserDefinedCarver; import com.dfsek.terra.config.pack.WorldConfig; import com.dfsek.terra.config.templates.CarverTemplate; -import com.dfsek.terra.profiler.ProfileFuture; +import com.dfsek.terra.profiler.ProfileFrame; import com.dfsek.terra.world.TerraWorld; import org.jetbrains.annotations.NotNull; @@ -23,7 +24,7 @@ import java.util.Map; import java.util.Random; import java.util.Set; -public class CavePopulator implements TerraBlockPopulator { +public class CavePopulator implements TerraBlockPopulator, Chunkified { private static final Map shiftStorage = new HashMap<>(); // Persist BlockData created for shifts, to avoid re-calculating each time. private final TerraPlugin main; @@ -37,48 +38,51 @@ public class CavePopulator implements TerraBlockPopulator { TerraWorld tw = main.getWorld(world); WorldHandle handle = main.getWorldHandle(); BlockData AIR = handle.createBlockData("minecraft:air"); - try(ProfileFuture ignored = tw.getProfiler().measure("CaveTime")) { + try(ProfileFrame ignore = main.getProfiler().profile("carving")) { Random random = PopulationUtil.getRandom(chunk); if(!tw.isSafe()) return; WorldConfig config = tw.getConfig(); + if(config.getTemplate().disableCarvers()) return; for(UserDefinedCarver c : config.getCarvers()) { CarverTemplate template = c.getConfig(); Map shiftCandidate = new HashMap<>(); Set updateNeeded = new HashSet<>(); c.carve(chunk.getX(), chunk.getZ(), world, (v, type) -> { - Block b = chunk.getBlock(v.getBlockX(), v.getBlockY(), v.getBlockZ()); - BlockData m = b.getBlockData(); - BlockType re = m.getBlockType(); - switch(type) { - case CENTER: - if(template.getInner().canReplace(re)) { - b.setBlockData(template.getInner().get(v.getBlockY()).get(random), false); - if(template.getUpdate().contains(re)) updateNeeded.add(b); - if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); - } - break; - case WALL: - if(template.getOuter().canReplace(re)) { - b.setBlockData(template.getOuter().get(v.getBlockY()).get(random), false); - if(template.getUpdate().contains(re)) updateNeeded.add(b); - if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); - } - break; - case TOP: - if(template.getTop().canReplace(re)) { - b.setBlockData(template.getTop().get(v.getBlockY()).get(random), false); - if(template.getUpdate().contains(re)) updateNeeded.add(b); - if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); - } - break; - case BOTTOM: - if(template.getBottom().canReplace(re)) { - b.setBlockData(template.getBottom().get(v.getBlockY()).get(random), false); - if(template.getUpdate().contains(re)) updateNeeded.add(b); - if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); - } - break; + try(ProfileFrame ignored = main.getProfiler().profile("carving:" + c.getConfig().getID())) { + Block b = chunk.getBlock(v.getBlockX(), v.getBlockY(), v.getBlockZ()); + BlockData m = b.getBlockData(); + BlockType re = m.getBlockType(); + switch(type) { + case CENTER: + if(template.getInner().canReplace(re)) { + b.setBlockData(template.getInner().get(v.getBlockY()).get(random), false); + if(template.getUpdate().contains(re)) updateNeeded.add(b); + if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); + } + break; + case WALL: + if(template.getOuter().canReplace(re)) { + b.setBlockData(template.getOuter().get(v.getBlockY()).get(random), false); + if(template.getUpdate().contains(re)) updateNeeded.add(b); + if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); + } + break; + case TOP: + if(template.getTop().canReplace(re)) { + b.setBlockData(template.getTop().get(v.getBlockY()).get(random), false); + if(template.getUpdate().contains(re)) updateNeeded.add(b); + if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); + } + break; + case BOTTOM: + if(template.getBottom().canReplace(re)) { + b.setBlockData(template.getBottom().get(v.getBlockY()).get(random), false); + if(template.getUpdate().contains(re)) updateNeeded.add(b); + if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); + } + break; + } } }); for(Map.Entry entry : shiftCandidate.entrySet()) { @@ -91,7 +95,7 @@ public class CavePopulator implements TerraBlockPopulator { if(template.getShift().get(entry.getValue().getBlockType()).contains(mut.getBlock().getBlockData().getBlockType())) { mut.getBlock().setBlockData(shiftStorage.computeIfAbsent(entry.getValue().getBlockType(), BlockType::getDefaultData), false); } - } catch(NullPointerException ignore) { + } catch(NullPointerException ignored) { } } for(Block b : updateNeeded) { diff --git a/common/src/main/java/com/dfsek/terra/world/population/FloraPopulator.java b/common/src/main/java/com/dfsek/terra/world/population/FloraPopulator.java index 281854cce..99c97f411 100644 --- a/common/src/main/java/com/dfsek/terra/world/population/FloraPopulator.java +++ b/common/src/main/java/com/dfsek/terra/world/population/FloraPopulator.java @@ -8,7 +8,7 @@ import com.dfsek.terra.api.util.world.PopulationUtil; import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.provider.BiomeProvider; import com.dfsek.terra.api.world.generation.TerraBlockPopulator; -import com.dfsek.terra.profiler.ProfileFuture; +import com.dfsek.terra.profiler.ProfileFrame; import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.population.items.flora.FloraLayer; import org.jetbrains.annotations.NotNull; @@ -32,7 +32,9 @@ public class FloraPopulator implements TerraBlockPopulator { @Override public void populate(@NotNull World world, @NotNull Chunk chunk) { TerraWorld tw = main.getWorld(world); - try(ProfileFuture ignored = tw.getProfiler().measure("FloraTime")) { + try(ProfileFrame ignore = main.getProfiler().profile("flora")) { + if(tw.getConfig().getTemplate().disableFlora()) return; + if(!tw.isSafe()) return; BiomeProvider provider = tw.getBiomeProvider(); Map> layers = new HashMap<>(); diff --git a/common/src/main/java/com/dfsek/terra/world/population/OrePopulator.java b/common/src/main/java/com/dfsek/terra/world/population/OrePopulator.java index a67944409..3d5c0966a 100644 --- a/common/src/main/java/com/dfsek/terra/world/population/OrePopulator.java +++ b/common/src/main/java/com/dfsek/terra/world/population/OrePopulator.java @@ -10,7 +10,7 @@ import com.dfsek.terra.api.world.biome.TerraBiome; import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.config.templates.BiomeTemplate; -import com.dfsek.terra.profiler.ProfileFuture; +import com.dfsek.terra.profiler.ProfileFrame; import com.dfsek.terra.world.TerraWorld; import org.jetbrains.annotations.NotNull; @@ -27,7 +27,9 @@ public class OrePopulator implements TerraBlockPopulator { @Override public void populate(@NotNull World world, @NotNull Chunk chunk) { TerraWorld tw = main.getWorld(world); - try(ProfileFuture ignored = tw.getProfiler().measure("OreTime")) { + try(ProfileFrame ignore = main.getProfiler().profile("ore")) { + if(tw.getConfig().getTemplate().disableOres()) return; + if(!tw.isSafe()) return; for(int cx = -1; cx <= 1; cx++) { for(int cz = -1; cz <= 1; cz++) { @@ -38,11 +40,13 @@ public class OrePopulator implements TerraBlockPopulator { BiomeTemplate config = ((UserDefinedBiome) b).getConfig(); int finalCx = cx; int finalCz = cz; - config.getOreHolder().forEach((ore, oreConfig) -> { - int amount = oreConfig.getAmount().get(random); - for(int i = 0; i < amount; i++) { - Vector3 location = new Vector3(random.nextInt(16) + 16 * finalCx, oreConfig.getHeight().get(random), random.nextInt(16) + 16 * finalCz); - ore.generate(location, chunk, random); + config.getOreHolder().forEach((id, orePair) -> { + try(ProfileFrame ignored = main.getProfiler().profile("ore:" + id)) { + int amount = orePair.getRight().getAmount().get(random); + for(int i = 0; i < amount; i++) { + Vector3 location = new Vector3(random.nextInt(16) + 16 * finalCx, orePair.getRight().getHeight().get(random), random.nextInt(16) + 16 * finalCz); + orePair.getLeft().generate(location, chunk, random); + } } }); } diff --git a/common/src/main/java/com/dfsek/terra/world/population/StructurePopulator.java b/common/src/main/java/com/dfsek/terra/world/population/StructurePopulator.java index 17bb41221..30346917c 100644 --- a/common/src/main/java/com/dfsek/terra/world/population/StructurePopulator.java +++ b/common/src/main/java/com/dfsek/terra/world/population/StructurePopulator.java @@ -9,10 +9,10 @@ import com.dfsek.terra.api.structures.structure.Rotation; import com.dfsek.terra.api.util.FastRandom; import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.provider.BiomeProvider; +import com.dfsek.terra.api.world.generation.Chunkified; import com.dfsek.terra.api.world.generation.TerraBlockPopulator; -import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.WorldConfig; -import com.dfsek.terra.profiler.ProfileFuture; +import com.dfsek.terra.profiler.ProfileFrame; import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.population.items.TerraStructure; import net.jafama.FastMath; @@ -20,7 +20,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Random; -public class StructurePopulator implements TerraBlockPopulator { +public class StructurePopulator implements TerraBlockPopulator, Chunkified { private final TerraPlugin main; public StructurePopulator(TerraPlugin main) { @@ -31,7 +31,9 @@ public class StructurePopulator implements TerraBlockPopulator { @Override public void populate(@NotNull World world, @NotNull Chunk chunk) { TerraWorld tw = main.getWorld(world); - try(ProfileFuture ignored = tw.getProfiler().measure("StructureTime")) { + try(ProfileFrame ignore = main.getProfiler().profile("structure")) { + if(tw.getConfig().getTemplate().disableStructures()) return; + int cx = (chunk.getX() << 4); int cz = (chunk.getZ() << 4); if(!tw.isSafe()) return; diff --git a/common/src/main/java/com/dfsek/terra/world/population/TreePopulator.java b/common/src/main/java/com/dfsek/terra/world/population/TreePopulator.java index 82bf925c7..5dd58fb92 100644 --- a/common/src/main/java/com/dfsek/terra/world/population/TreePopulator.java +++ b/common/src/main/java/com/dfsek/terra/world/population/TreePopulator.java @@ -8,7 +8,7 @@ import com.dfsek.terra.api.util.world.PopulationUtil; import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.provider.BiomeProvider; import com.dfsek.terra.api.world.generation.TerraBlockPopulator; -import com.dfsek.terra.profiler.ProfileFuture; +import com.dfsek.terra.profiler.ProfileFrame; import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.population.items.tree.TreeLayer; import net.jafama.FastMath; @@ -32,7 +32,9 @@ public class TreePopulator implements TerraBlockPopulator { @SuppressWarnings("try") public void populate(@NotNull World world, @NotNull Chunk chunk) { TerraWorld tw = main.getWorld(world); - try(ProfileFuture ignored = tw.getProfiler().measure("TreeTime")) { + try(ProfileFrame ignore = main.getProfiler().profile("tree")) { + if(tw.getConfig().getTemplate().disableTrees()) return; + if(!tw.isSafe()) return; BiomeProvider provider = tw.getBiomeProvider(); Random random = PopulationUtil.getRandom(chunk); @@ -40,8 +42,9 @@ public class TreePopulator implements TerraBlockPopulator { for(int z = 0; z < 16; z += 2) { UserDefinedBiome biome = (UserDefinedBiome) provider.getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z); for(TreeLayer layer : biome.getConfig().getTrees()) { - if(layer.getDensity() >= random.nextDouble() * 100) + if(layer.getDensity() >= random.nextDouble() * 100) { layer.place(chunk, new Vector2(offset(random, x), offset(random, z))); + } } } } diff --git a/common/src/main/java/com/dfsek/terra/world/population/items/ores/Ore.java b/common/src/main/java/com/dfsek/terra/world/population/items/ores/Ore.java index 541a7dacf..e7f3faedd 100644 --- a/common/src/main/java/com/dfsek/terra/world/population/items/ores/Ore.java +++ b/common/src/main/java/com/dfsek/terra/world/population/items/ores/Ore.java @@ -16,7 +16,6 @@ public abstract class Ore { protected TerraPlugin main; public Ore(BlockData material, MaterialSet replaceable, boolean applyGravity, TerraPlugin main) { - this.material = material; this.replaceable = replaceable; this.applyGravity = applyGravity; diff --git a/common/src/main/java/com/dfsek/terra/world/population/items/ores/OreHolder.java b/common/src/main/java/com/dfsek/terra/world/population/items/ores/OreHolder.java index e7cc81d5c..43e461895 100644 --- a/common/src/main/java/com/dfsek/terra/world/population/items/ores/OreHolder.java +++ b/common/src/main/java/com/dfsek/terra/world/population/items/ores/OreHolder.java @@ -1,6 +1,7 @@ package com.dfsek.terra.world.population.items.ores; import com.dfsek.terra.api.util.GlueList; +import com.dfsek.terra.api.util.generic.pair.ImmutablePair; import java.util.List; import java.util.function.BiConsumer; @@ -11,22 +12,24 @@ import java.util.function.BiConsumer; public class OreHolder { private final List entries = new GlueList<>(); - public void forEach(BiConsumer consumer) { - entries.forEach(entry -> consumer.accept(entry.getOre(), entry.getConfig())); + public void forEach(BiConsumer> consumer) { + entries.forEach(entry -> consumer.accept(entry.getId(), ImmutablePair.of(entry.getOre(), entry.getConfig()))); } - public OreHolder add(Ore ore, OreConfig config) { - entries.add(new Entry(ore, config)); + public OreHolder add(Ore ore, OreConfig config, String id) { + entries.add(new Entry(ore, config, id)); return this; } private static final class Entry { private final Ore ore; private final OreConfig config; + private final String id; - private Entry(Ore ore, OreConfig config) { + private Entry(Ore ore, OreConfig config, String id) { this.ore = ore; this.config = config; + this.id = id; } public OreConfig getConfig() { @@ -36,5 +39,9 @@ public class OreHolder { public Ore getOre() { return ore; } + + public String getId() { + return id; + } } } diff --git a/common/src/main/resources/config.yml b/common/src/main/resources/config.yml index adb7d8522..e7572c935 100644 --- a/common/src/main/resources/config.yml +++ b/common/src/main/resources/config.yml @@ -7,7 +7,6 @@ cache: carver: 512 structure: 32 sampler: 128 -master-disable: - caves: false + biome-provider: 32 script: max-recursion: 1000 \ No newline at end of file diff --git a/common/src/main/resources/lang/cs_cz.yml b/common/src/main/resources/lang/cs_cz.yml new file mode 100644 index 000000000..c01c6fcf9 --- /dev/null +++ b/common/src/main/resources/lang/cs_cz.yml @@ -0,0 +1,122 @@ +enable: + - "Pokud se vám líbí Terra, můžete nás podpořit na Patreon!" + - "Získáte přístup k experimentálním funkcím, než budou vydány!" + - "Podpořit projekt můžete zde: https://www.patreon.com/dfsek" +disable: + - "Děkujeme, že používáte Terra!" +command: + debug-only: "Tento příkaz musí být použit se zapnutým debug modem!" + player-only: "Tento příkaz je pouze pro hráče!" + invalid: "Neznámý příkaz. (Očekáváno %1$s argumentů, nalezeno %2$s)." + players-only: "Příkaz je pouze pro použití hráčem." + world: "Tento příkaz musí být použit v Terra světě!" + reload: "Obnoven Terra konfig." + reload-error: "Nastaly chyby při obnově Terra konfigurace. Podívejte se do logů pro více informací." + version: "Na tomto serveru běží Terra verze \"%1$s\", na platformě \"%2$s\"" + main-menu: + - "--------------------Terra--------------------" + - "reload - Obnoví konfigurační data" + - "biome - Ukáže aktuální biom" + - "ore - Generuje žílu rud v lokaci, ve směru kam se koukáte (Pro debugging)" + - "save-data - Uložit populační data" + - "structure - Nahrát a exportovat struktury" + - "profile - Profiler možnosti" + - "image - Obrázek/GUI možnosti" + biome: + biome-found: "Lokalizován biom na (%1$s, %2$s)" + unable-to-locate: "Nelze lokalizovat biom." + invalid-radius: "Nesprávný radius: \"%s\"" + invalid: "Nesprávné ID bomu: \"%s\"" + in: "Jste v \"%s\"" + packs: + main: "Aktuální instalované konfigurační balíčky:" + pack: " - %1$s v%3$s od %2$s" + none: "Žádné konfigurační balíčky nejsou nainstalovány." + ore: + main-menu: + - "---------------Terra/ore---------------" + - "Generuje žílu rud na bloku, na který koukáte." + out-of-range: "Blok mimo dosah" + invalid-ore: "Nelze nalézt Rudu \"%s\"" + geometry: + main-menu: + - "---------------Terra/geometry----------------" + - "Různé voxel geometrické debugging příkazy" + - "sphere - Generuje kouli" + - "deformsphere - Generuje deformovanou kouli" + - "tube - Generuje válec" + deform: + invalid-radius: "Nesprávný radius: \"%s\"" + invalid-deform: "Nesprávná deformace: \"%s\"" + invalid-frequency: "Nesprávná frekvence: \"%s\"" + sphere: + invalid-radius: "Nesprávný radius: \"%s\"" + tube: + invalid-radius: "Nesprávný radius: \"%s\"" + image: + main-menu: + - "---------------Terra/image---------------" + - "render - Vykreslí obrázek s danou šířkou a výškou, který může být později importován jako svět." + - "gui - Otevřít debug GUI (Musí být zapnuto v konfigu)" + gui: + main-menu: + - "-------------Terra/image/gui-------------" + - "raw - Otevře GUI s raw Biom daty" + - "step - Znovu vykreslí data k lepšímu zobrazení borderu" + debug: "Debug mod musí být zapnutý pro použití této možnosti! Debug GUI NENÍ PRODUKČNĚ BEZPEČNÉ!" + render: + save: "Uložen obrázek jako \"%s\"" + error: "Nastala chyba při generování obrázku!" + profile: + main-menu: + - "---------------Terra/profile---------------" + - "start - Zapne profiler" + - "stop - Vypne profiler" + - "query - Ukáže profiler data" + - "reset - Resetuje profiler data" + reset: "Profiler byl resetován." + start: "Profiler byl zapnut." + stop: "Profiler byl vypnut." + structure: + main-menu: + - "---------------Terra/structure---------------" + - "export - Export vašeho výběru WorldEdit jako Terra struktura." + - "load - Načíst Terra strukturu" + invalid-radius: "Nesprávný radius: \"%s\"" + invalid-rotation: "Nesprávná rotace: \"%s\"" + invalid: "Nesprávné ID struktury: \"%s\"" + export: "Uložena struktura do \"%s\"" +world-config: + load: "Načteny konfigurační hodnoty světa pro svět \"%s\"..." + not-found: "Konfigurace pro svět \"%s\" nenalezena. Kopíruji defaultní konfig." + using-image: "Načítám svět z obrázku." + error: "Nelze načíst konfigurace pro svět %s" + done: "Načítání světa \"%1$s\" dokončeno. Uplynulý čas: %2$sms" +config-pack: + loaded: "Konfigurační balíček %1$s v%4$s od %3$s byl načten za %2$sms." +config: + loaded: "Načten %1$s ze souboru %2$s" + loaded-all: "Načteno %1$s %2$s(s) za %3$sms." + error: + invalid-failover: "Nesprávný typ failover: \"%s\"" + duplicate: "Duplikační ID nalezeno v souboru: %s" + file: + - "Konfigurační chyba pro Terra objekt. Soubor: %1$s" + - "%2$s" + - "Opravte jej před pokračováním!" + generic: + - "Nastala chyba při načítání konfigurace." + - "Prosíme nahlašte to na Terra." +warning: + no-population: "Žádné populační chunky nebyly načteny. Pokud je to poprvé, co zapínáte server s Terra, či vytváříte nový svět, je tohle normální." +error: + severe-config: "Vážná konfigurační chyba zabránila Terra ze správné generace terénu na koordinátech: %1$s, %2$s. Prosíme zkontrolujte konfiguraci pro chyby. Jakékoli konfigurační chyby jsou vypsány výše." +debug: + data-save : "Uložena populační data." +use-paper: + - "Vypadá to, že používáte Spigot/CraftBukkit." + - "Ačkoli Terra &ofunguje&r na Spigot, některé funkce budou ztraceny. (Terra je netestována na CraftBukkit; žádná podpora nebude dána pro CraftBukkit)." + - "Pro nejvíce funkcí s Terra, použijte Paper." + - "Navíc, Paper přináší obrovské výkonnostní zlepešení než Spigot, a všechny Spigot pluginy by měli fungovat s Paper!" + - "Pro nejlepší zážitek s Terra a všemi pluginy, použijte prosím Paper." + - "Více info najdete na stránce Paperu: https://papermc.io/" diff --git a/common/src/test/java/biome/DistributionTest.java b/common/src/test/java/biome/DistributionTest.java index 226da5898..11b3dcef8 100644 --- a/common/src/test/java/biome/DistributionTest.java +++ b/common/src/test/java/biome/DistributionTest.java @@ -39,6 +39,8 @@ import com.dfsek.terra.config.loaders.config.biome.templates.provider.SingleBiom import com.dfsek.terra.config.loaders.config.sampler.NoiseSamplerBuilderLoader; import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.templates.AbstractableTemplate; +import com.dfsek.terra.config.templates.BiomeTemplate; +import com.dfsek.terra.profiler.Profiler; import com.dfsek.terra.registry.config.BiomeRegistry; import com.dfsek.terra.registry.config.NoiseRegistry; import com.dfsek.terra.world.TerraWorld; @@ -136,6 +138,11 @@ public class DistributionTest { return null; } + @Override + public Profiler getProfiler() { + return null; + } + @Override public void register(TypeRegistry registry) { @@ -152,7 +159,7 @@ public class DistributionTest { new GenericLoaders(MAIN).register(loader); BiomeRegistry biomeRegistry = new BiomeRegistry(); - folderLoader.open("biomes", ".yml").then(inputStreams -> ConfigPack.buildAll((template, main) -> template, biomeRegistry, loader.load(inputStreams, TestBiome::new), MAIN)); + folderLoader.open("biomes", ".yml").then(inputStreams -> ConfigPack.buildAll((template, main) -> template, biomeRegistry, loader.loadConfigs(inputStreams, TestBiome::new), MAIN)); BiomeProviderTemplate template = new BiomeProviderTemplate(); ConfigLoader pipeLoader = new ConfigLoader() @@ -306,6 +313,11 @@ public class DistributionTest { return null; } + @Override + public BiomeTemplate getTemplate() { + return null; + } + @Override public Generator getGenerator(World w) { return null; diff --git a/common/src/test/java/biome/ImageTest.java b/common/src/test/java/biome/ImageTest.java index ddc96dafc..04bcee35b 100644 --- a/common/src/test/java/biome/ImageTest.java +++ b/common/src/test/java/biome/ImageTest.java @@ -46,7 +46,7 @@ public class ImageTest { OpenRegistry biomeRegistry = new OpenRegistry() { }; - folderLoader.open("biomes", ".yml").then(inputStreams -> ConfigPack.buildAll((template, main) -> template, biomeRegistry, loader.load(inputStreams, TestBiome::new), null)); + folderLoader.open("biomes", ".yml").then(inputStreams -> ConfigPack.buildAll((template, main) -> template, biomeRegistry, loader.loadConfigs(inputStreams, TestBiome::new), null)); return new ImageBiomeProvider(biomeRegistry.entries(), ImageIO.read(ImageTest.class.getResourceAsStream("/map.jpg")), 1, ImageBiomeProvider.Align.CENTER); } diff --git a/common/src/test/java/profiler/ProfilerTest.java b/common/src/test/java/profiler/ProfilerTest.java new file mode 100644 index 000000000..b4d97856d --- /dev/null +++ b/common/src/test/java/profiler/ProfilerTest.java @@ -0,0 +1,63 @@ +package profiler; + +import com.dfsek.terra.profiler.Profiler; +import com.dfsek.terra.profiler.ProfilerImpl; + +public class ProfilerTest { + private static final Profiler PROFILER = new ProfilerImpl(); + //@Test + public static void main(String... a) throws InterruptedException { + //PROFILER.start(); + for(int i = 0; i < 1000; i++) { + doThing(); + } + + for(int i = 0; i < 100; i++) { + doThirdOtherThing(); + } + + for(int i = 0; i < 100; i++) { + doOtherThing(); + } + PROFILER.stop(); + PROFILER.push("thing"); + PROFILER.push("thing2"); + PROFILER.start(); + PROFILER.pop("thing2"); + PROFILER.pop("thing"); + PROFILER.push("thing4"); + PROFILER.pop("thing4"); + + PROFILER.getTimings().forEach((id, timings) -> { + System.out.println(id + ": " + timings.toString()); + }); + } + + private static void doThing() throws InterruptedException { + PROFILER.push("thing"); + Thread.sleep(1); + doOtherThing(); + thing4(); + PROFILER.pop("thing"); + } + + private static void doOtherThing() throws InterruptedException { + PROFILER.push("thing2"); + Thread.sleep(2); + doThirdOtherThing(); + thing4(); + PROFILER.pop("thing2"); + } + + private static void doThirdOtherThing() throws InterruptedException { + PROFILER.push("thing3"); + Thread.sleep(2); + PROFILER.pop("thing3"); + } + + private static void thing4() throws InterruptedException { + PROFILER.push("thing4"); + Thread.sleep(2); + PROFILER.pop("thing4"); + } +} diff --git a/common/src/test/java/structure/ParserTest.java b/common/src/test/java/structure/ParserTest.java index 1b18d2a3c..fd1ff30c1 100644 --- a/common/src/test/java/structure/ParserTest.java +++ b/common/src/test/java/structure/ParserTest.java @@ -13,6 +13,7 @@ import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; import java.io.IOException; +import java.nio.charset.Charset; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -20,7 +21,7 @@ import java.util.Map; public class ParserTest { @Test public void parse() throws IOException, ParseException { - Parser parser = new Parser(IOUtils.toString(getClass().getResourceAsStream("/test.tesf"))); + Parser parser = new Parser(IOUtils.toString(getClass().getResourceAsStream("/test.tesf"), Charset.defaultCharset())); parser.registerFunction("test", new FunctionBuilder() { @Override diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 000000000..59ea64a1a --- /dev/null +++ b/gradle.properties @@ -0,0 +1,15 @@ +# Magic options for more perf +org.gradle.jvmargs=-Xmx2048M -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC +org.gradle.vfs.watch=true +kapt.use.worker.api=true +kapt.include.compile.classpath=false +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.warning.mode=all +#org.gradle.logging.level=info +# Project information +terra.description=A fast, modern, extensible, platform-agnostic data-driven world generator +terra.source=https://github.com/PolyhedralDev/Terra +terra.issues=https://github.com/PolyhedralDev/Terra/issues +terra.wiki=https://github.com/PolyhedralDev/Terra/wiki +terra.license=GNU General Public License, version 3.0 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aec6aa17c..a29c5e5c5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists org.gradle.jvmargs=-Xmx4096m \ No newline at end of file diff --git a/platforms/bukkit/build.gradle.kts b/platforms/bukkit/build.gradle.kts index dbf066f36..1a4d3cce6 100644 --- a/platforms/bukkit/build.gradle.kts +++ b/platforms/bukkit/build.gradle.kts @@ -15,12 +15,12 @@ configureCommon() group = "com.dfsek.terra.bukkit" -repositories { - mavenCentral() - maven { url = uri("http://maven.enginehub.org/repo/") } - maven { url = uri("https://repo.codemc.org/repository/maven-public") } - maven { url = uri("https://papermc.io/repo/repository/maven-public/") } -} +val mcVersion = "1.16.5" +val testDir = "target/server" +val testMem = "3G" + +val paperURL = "https://papermc.io/api/v1/paper/%version%/latest/download/" +val purpurURL = "https://ci.pl3x.net/job/Purpur/lastSuccessfulBuild/artifact/final/purpurclip.jar" dependencies { "shadedApi"(project(":common")) @@ -32,76 +32,159 @@ dependencies { "compileOnly"("com.sk89q.worldedit:worldedit-bukkit:7.2.0-SNAPSHOT") - "shadedImplementation"("com.google.guava:guava:30.0-jre") + "shadedApi"("com.google.guava:guava:30.0-jre") } -val testDir = "target/server/" +val jvmFlags = listOf("-XX:+UseG1GC", "-XX:+ParallelRefProcEnabled", "-XX:MaxGCPauseMillis=200", + "-XX:+UnlockExperimentalVMOptions", "-XX:+DisableExplicitGC", "-XX:+AlwaysPreTouch", + "-XX:G1NewSizePercent=30", "-XX:G1MaxNewSizePercent=40", "-XX:G1HeapRegionSize=8M", + "-XX:G1ReservePercent=20", "-XX:G1HeapWastePercent=5", "-XX:G1MixedGCCountTarget=4", + "-XX:InitiatingHeapOccupancyPercent=15", "-XX:G1MixedGCLiveThresholdPercent=90", + "-XX:G1RSetUpdatingPauseTimePercent=5", "-XX:SurvivorRatio=32", "-XX:+PerfDisableSharedMem", + "-XX:MaxTenuringThreshold=1", "-Dusing.aikars.flags=https://mcflags.emc.gs", + "-Daikars.new.flags=true", "-DIReallyKnowWhatIAmDoingISwear", "-javaagent:paperclip.jar") -val setupServer = tasks.create("setupServer") { - dependsOn("shadowJar") - doFirst { - // clean - file("${testDir}/").deleteRecursively() - file("${testDir}/plugins").mkdirs() +fun downloadPaperclip(url: String, dir: String) { + val clip = URL(url.replace("%version%", mcVersion)) + val clipReadableByteChannel = Channels.newChannel(clip.openStream()) + val clipFile = file("$testDir/$dir/paperclip.jar") + val clipOutputStream = clipFile.outputStream() + val clipFileChannel = clipOutputStream.channel + clipFileChannel.transferFrom(clipReadableByteChannel, 0, Long.MAX_VALUE) +} - // Downloading latest paper jar. - val paperUrl = URL("https://papermc.io/api/v1/paper/1.16.4/latest/download") - val paperReadableByteChannel = Channels.newChannel(paperUrl.openStream()) - val paperFile = file("${testDir}/paper.jar") - val paperFileOutputStream = paperFile.outputStream() - val paperFileChannel = paperFileOutputStream.channel - paperFileChannel.transferFrom(paperReadableByteChannel, 0, Long.MAX_VALUE) - - // Cloning test setup. - gitClone("https://github.com/PolyhedralDev/WorldGenTestServer") - // Copying plugins - Files.move(Paths.get("WorldGenTestServer/plugins"), - Paths.get("$testDir/plugins"), - StandardCopyOption.REPLACE_EXISTING) - // Copying config - val serverText = URL("https://raw.githubusercontent.com/PolyhedralDev/WorldGenTestServer/master/server.properties").readText() - file("${testDir}/server.properties").writeText(serverText) - val bukkitText = URL("https://raw.githubusercontent.com/PolyhedralDev/WorldGenTestServer/master/bukkit.yml").readText() - file("${testDir}/bukkit.yml").writeText(bukkitText.replace("\${world}", "world").replace("\${gen}", "Terra:DEFAULT")) - - File("${testDir}/eula.txt").writeText("eula=true") - - // clean up - file("WorldGenTestServer").deleteRecursively() +fun copyTerra(dir: String) { + file("$testDir/$dir").walk().forEach { + if(it.name.startsWith("Terra-") && it.name.endsWith(".jar")) it.delete() // Delete old Terra jar(s) + } + copy { + from("$buildDir/libs/Terra-bukkit-$version-shaded.jar") + into("$testDir/$dir/plugins/") } } -val testWithPaper = task(name = "testWithPaper") { +fun installServer(dir: String) { + // clean + file("$testDir/$dir").deleteRecursively() + file("$testDir/$dir/plugins").mkdirs() + // Cloning test setup. + gitClone("https://github.com/PolyhedralDev/WorldGenTestServer") + // Copying plugins + Files.move(file("WorldGenTestServer/plugins").toPath(), + file("$testDir/$dir/plugins").toPath(), + StandardCopyOption.REPLACE_EXISTING) + // Copying config + val serverText = URL("https://raw.githubusercontent.com/PolyhedralDev/WorldGenTestServer/master/server.properties").readText() + file("$testDir/$dir/server.properties").writeText(serverText) + val bukkitText = URL("https://raw.githubusercontent.com/PolyhedralDev/WorldGenTestServer/master/bukkit.yml").readText() + file("$testDir/$dir/bukkit.yml").writeText(bukkitText.replace("\${world}", "world").replace("\${gen}", "Terra:DEFAULT")) + + println("By proceeding, you are agreeing to the Minecraft EULA: https://account.mojang.com/documents/minecraft_eula") + file("$testDir/$dir/eula.txt").writeText("eula=true") + + // clean up + file("WorldGenTestServer").deleteRecursively() +} + +fun deleteFolder(folder: File) { + if(folder.exists()) folder.deleteRecursively() +} + +fun deleteFile(file: File) { + if(file.exists()) file.delete() +} + +tasks.create("cleanWorlds") { + group = "bukkit" + doFirst { + deleteFolder(file("$testDir/paper/world")) + deleteFolder(file("$testDir/paper/world_nether")) + deleteFolder(file("$testDir/paper/world_the_end")) + + deleteFolder(file("$testDir/purpur/world")) + deleteFolder(file("$testDir/purpur/world_nether")) + deleteFolder(file("$testDir/purpur/world_the_end")) + } +} + +tasks.create("updatePaper") { + group = "bukkit" + doFirst { + deleteFile(file("$testDir/paper/paperclip.jar")) + downloadPaperclip(paperURL, "paper") + } +} + +tasks.create("updatePurpur") { + group = "bukkit" + doFirst { + deleteFile(file("$testDir/paper/paperclip.jar")) + downloadPaperclip(purpurURL, "purpur") + } +} + +tasks.create("installPaper") { + group = "bukkit" + dependsOn("shadowJar") + doFirst { + installServer("paper") + // Downloading latest paper jar. + downloadPaperclip(paperURL, "paper") + } +} + +tasks.create("installPurpur") { + group = "bukkit" + dependsOn("shadowJar") + doFirst { + installServer("purpur") + // Downloading latest paper jar. + downloadPaperclip(purpurURL, "purpur") + } +} + +task(name = "runPaper") { + group = "bukkit" standardInput = System.`in` dependsOn("shadowJar") // Copy Terra into dir doFirst { - copy { - from("${buildDir}/libs/Terra-bukkit-${version}-shaded.jar") - into("${testDir}/plugins/") - } + copyTerra("paper") } main = "io.papermc.paperclip.Paperclip" - jvmArgs = listOf("-XX:+UseG1GC", "-XX:+ParallelRefProcEnabled", "-XX:MaxGCPauseMillis=200", - "-XX:+UnlockExperimentalVMOptions", "-XX:+DisableExplicitGC", "-XX:+AlwaysPreTouch", - "-XX:G1NewSizePercent=30", "-XX:G1MaxNewSizePercent=40", "-XX:G1HeapRegionSize=8M", - "-XX:G1ReservePercent=20", "-XX:G1HeapWastePercent=5", "-XX:G1MixedGCCountTarget=4", - "-XX:InitiatingHeapOccupancyPercent=15", "-XX:G1MixedGCLiveThresholdPercent=90", - "-XX:G1RSetUpdatingPauseTimePercent=5", "-XX:SurvivorRatio=32", "-XX:+PerfDisableSharedMem", - "-XX:MaxTenuringThreshold=1", "-Dusing.aikars.flags=https://mcflags.emc.gs", - "-Daikars.new.flags=true", "-DIReallyKnowWhatIAmDoingISwear") - maxHeapSize = "4G" - minHeapSize = "4G" + jvmArgs = jvmFlags + maxHeapSize = testMem + minHeapSize = testMem //args = listOf("nogui") - workingDir = file("${testDir}/") - classpath = files("${testDir}/paper.jar") + workingDir = file("$testDir/paper") + classpath = files("$testDir/paper/paperclip.jar") +} + +task(name = "runPurpur") { + group = "bukkit" + standardInput = System.`in` + dependsOn("shadowJar") + // Copy Terra into dir + doFirst { + copyTerra("purpur") + } + + main = "io.papermc.paperclip.Paperclip" + jvmArgs = jvmFlags + maxHeapSize = testMem + minHeapSize = testMem + //args = listOf("nogui") + workingDir = file("$testDir/purpur") + classpath = files("$testDir/purpur/paperclip.jar") } tasks.named("shadowJar") { relocate("org.bstats.bukkit", "com.dfsek.terra.lib.bstats") relocate("io.papermc.lib", "com.dfsek.terra.lib.paperlib") + relocate("com.google.common", "com.dfsek.terra.lib.google.common") } + publishing { publications { create("mavenJava") { @@ -112,7 +195,6 @@ publishing { repositories { val mavenUrl = "https://repo.codemc.io/repository/maven-releases/" - val mavenSnapshotUrl = "https://repo.codemc.io/repository/maven-snapshots/" maven(mavenUrl) { val mavenUsername: String? by project diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/TerraBukkitPlugin.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/TerraBukkitPlugin.java index e1254e51e..a2388d121 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/TerraBukkitPlugin.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/TerraBukkitPlugin.java @@ -34,12 +34,15 @@ import com.dfsek.terra.bukkit.listeners.SpigotListener; import com.dfsek.terra.bukkit.listeners.TerraListener; import com.dfsek.terra.bukkit.util.PaperUtil; import com.dfsek.terra.bukkit.world.BukkitBiome; +import com.dfsek.terra.bukkit.world.BukkitWorld; import com.dfsek.terra.commands.CommandUtil; import com.dfsek.terra.config.GenericLoaders; import com.dfsek.terra.config.PluginConfig; import com.dfsek.terra.config.lang.LangUtil; import com.dfsek.terra.config.lang.Language; import com.dfsek.terra.config.pack.ConfigPack; +import com.dfsek.terra.profiler.Profiler; +import com.dfsek.terra.profiler.ProfilerImpl; import com.dfsek.terra.registry.master.AddonRegistry; import com.dfsek.terra.registry.master.ConfigRegistry; import com.dfsek.terra.world.TerraWorld; @@ -64,6 +67,8 @@ public class TerraBukkitPlugin extends JavaPlugin implements TerraPlugin { private final Map worldMap = new HashMap<>(); private final Map worlds = new HashMap<>(); + private final Profiler profiler = new ProfilerImpl(); + private final ConfigRegistry registry = new ConfigRegistry(); private final CheckedRegistry checkedRegistry = new CheckedRegistry<>(registry); @@ -141,6 +146,11 @@ public class TerraBukkitPlugin extends JavaPlugin implements TerraPlugin { Bukkit.getScheduler().runTask(this, task); } + @Override + public Profiler getProfiler() { + return profiler; + } + @Override public void onDisable() { BukkitChunkGeneratorWrapper.saveAll(); @@ -259,14 +269,15 @@ public class TerraBukkitPlugin extends JavaPlugin implements TerraPlugin { return checkedRegistry; } - public TerraWorld getWorld(World w) { - if(!TerraWorld.isTerraWorld(w)) + public TerraWorld getWorld(World world) { + BukkitWorld w = (BukkitWorld) world; + if(!w.isTerraWorld()) throw new IllegalArgumentException("Not a Terra world! " + w.getGenerator()); if(!worlds.containsKey(w.getName())) { getLogger().warning("Unexpected world load detected: \"" + w.getName() + "\""); return new TerraWorld(w, ((TerraChunkGenerator) w.getGenerator().getHandle()).getConfigPack(), this); } - return worldMap.computeIfAbsent(w, world -> new TerraWorld(w, worlds.get(w.getName()), this)); + return worldMap.computeIfAbsent(w, w2 -> new TerraWorld(w, worlds.get(w.getName()), this)); } @Override diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/generator/BukkitChunkGeneratorWrapper.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/generator/BukkitChunkGeneratorWrapper.java index 092b486f2..67b7148e6 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/generator/BukkitChunkGeneratorWrapper.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/generator/BukkitChunkGeneratorWrapper.java @@ -3,19 +3,11 @@ package com.dfsek.terra.bukkit.generator; import com.dfsek.terra.api.TerraPlugin; import com.dfsek.terra.api.platform.world.Chunk; import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; -import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.api.world.generation.TerraChunkGenerator; import com.dfsek.terra.bukkit.population.PopulationManager; import com.dfsek.terra.bukkit.world.BukkitAdapter; import com.dfsek.terra.bukkit.world.BukkitBiomeGrid; -import com.dfsek.terra.profiler.DataType; -import com.dfsek.terra.profiler.Measurement; import com.dfsek.terra.world.TerraWorld; -import com.dfsek.terra.world.population.CavePopulator; -import com.dfsek.terra.world.population.FloraPopulator; -import com.dfsek.terra.world.population.OrePopulator; -import com.dfsek.terra.world.population.StructurePopulator; -import com.dfsek.terra.world.population.TreePopulator; import org.bukkit.World; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; @@ -23,12 +15,11 @@ import org.jetbrains.annotations.NotNull; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Arrays; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; -import java.util.stream.Collectors; public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements GeneratorWrapper { @@ -40,20 +31,13 @@ public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements Gener private final TerraPlugin main; - private final List populators = new LinkedList<>(); - private boolean needsLoad = true; public BukkitChunkGeneratorWrapper(TerraChunkGenerator delegate) { this.delegate = delegate; this.main = delegate.getMain(); - popMan = new PopulationManager(main); - popMan.attach(new OrePopulator(main)); - popMan.attach(new TreePopulator(main)); - popMan.attach(new FloraPopulator(main)); - populators.add(new CavePopulator(main)); - populators.add(new StructurePopulator(main)); - populators.add(popMan); + this.popMan = new PopulationManager(delegate, main); + } @@ -68,7 +52,7 @@ public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements Gener } public static synchronized void fixChunk(Chunk c) { - if(!TerraWorld.isTerraWorld(c.getWorld())) throw new IllegalArgumentException(); + if(!c.getWorld().isTerraWorld()) throw new IllegalArgumentException(); popMap.get(c.getWorld()).checkNeighbors(c.getX(), c.getZ(), c.getWorld()); } @@ -81,8 +65,6 @@ public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements Gener e.printStackTrace(); } popMap.put(w, popMan); - main.getWorld(w).getProfiler().addMeasurement(new Measurement(15000000, DataType.PERIOD_MILLISECONDS), "PopulationManagerTime"); - popMan.attachProfiler(main.getWorld(w).getProfiler()); needsLoad = false; } @@ -96,7 +78,7 @@ public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements Gener @Override public @NotNull List getDefaultPopulators(@NotNull World world) { - return populators.stream().map(BukkitPopulatorWrapper::new).collect(Collectors.toList()); + return Arrays.asList(popMan, new BukkitPopulatorWrapper(delegate)); } @Override diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/generator/BukkitPopulator.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/generator/BukkitPopulator.java deleted file mode 100644 index fabb1d53c..000000000 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/generator/BukkitPopulator.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.dfsek.terra.bukkit.generator; - -import com.dfsek.terra.api.platform.world.Chunk; -import com.dfsek.terra.api.platform.world.World; -import com.dfsek.terra.api.platform.world.generator.BlockPopulator; -import com.dfsek.terra.bukkit.world.BukkitChunk; -import com.dfsek.terra.bukkit.world.BukkitWorld; - -import java.util.Random; - -public class BukkitPopulator implements BlockPopulator { - private final org.bukkit.generator.BlockPopulator handle; - - public BukkitPopulator(org.bukkit.generator.BlockPopulator handle) { - this.handle = handle; - } - - @Override - public void populate(World world, Random random, Chunk chunk) { - handle.populate(((BukkitWorld) world).getHandle(), random, ((BukkitChunk) chunk).getHandle()); - } - - @Override - public org.bukkit.generator.BlockPopulator getHandle() { - return handle; - } -} diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/generator/BukkitPopulatorWrapper.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/generator/BukkitPopulatorWrapper.java index 22b1a2a53..eb57cb2dc 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/generator/BukkitPopulatorWrapper.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/generator/BukkitPopulatorWrapper.java @@ -1,6 +1,8 @@ package com.dfsek.terra.bukkit.generator; +import com.dfsek.terra.api.world.generation.Chunkified; import com.dfsek.terra.api.world.generation.TerraBlockPopulator; +import com.dfsek.terra.api.world.generation.TerraChunkGenerator; import com.dfsek.terra.bukkit.world.BukkitAdapter; import org.bukkit.Chunk; import org.bukkit.World; @@ -10,14 +12,18 @@ import org.jetbrains.annotations.NotNull; import java.util.Random; public class BukkitPopulatorWrapper extends BlockPopulator { - private final TerraBlockPopulator delegate; + private final TerraChunkGenerator delegate; - public BukkitPopulatorWrapper(TerraBlockPopulator delegate) { + public BukkitPopulatorWrapper(TerraChunkGenerator delegate) { this.delegate = delegate; } @Override public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk source) { - delegate.populate(BukkitAdapter.adapt(world), BukkitAdapter.adapt(source)); + delegate.getPopulators().forEach(populator -> { + if(populator instanceof Chunkified) { + populator.populate(BukkitAdapter.adapt(world), BukkitAdapter.adapt(source)); + } + }); } } diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/CommonListener.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/CommonListener.java index 5c6d31183..ac0fd6356 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/CommonListener.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/CommonListener.java @@ -44,7 +44,7 @@ public class CommonListener implements Listener { public void onSaplingGrow(StructureGrowEvent e) { if(e.isCancelled()) return; World bukkit = BukkitAdapter.adapt(e.getWorld()); - if(!TerraWorld.isTerraWorld(bukkit)) return; + if(!bukkit.isTerraWorld()) return; TerraWorld tw = main.getWorld(bukkit); WorldConfig c = tw.getConfig(); if(c.getTemplate().isDisableSaplings()) return; diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/PaperListener.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/PaperListener.java index 3945451f4..2e98877f8 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/PaperListener.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/PaperListener.java @@ -18,8 +18,7 @@ public class PaperListener implements Listener { @EventHandler public void onStructureLocate(StructureLocateEvent e) { - if(!TerraWorld.isTerraWorld(BukkitAdapter.adapt(e.getWorld()))) return; - e.setResult(null); // Assume no result. + if(!BukkitAdapter.adapt(e.getWorld()).isTerraWorld()) return; String name = "minecraft:" + e.getType().getName(); main.getDebugLogger().info("Overriding structure location for \"" + name + "\""); TerraWorld tw = main.getWorld(BukkitAdapter.adapt(e.getWorld())); @@ -32,10 +31,8 @@ public class PaperListener implements Listener { }, main); finder.run(); // Do this synchronously. } else { - main.logger().warning("No overrides are defined for \"" + name + "\""); + e.setResult(e.getOrigin()); + main.logger().warning("No overrides are defined for \"" + name + "\". Locating this structure will NOT work properly!"); } - } - - } diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/SpigotListener.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/SpigotListener.java index fe4681e61..5034c4863 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/SpigotListener.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/SpigotListener.java @@ -34,7 +34,7 @@ public class SpigotListener implements Listener { Entity entity = e.getEntity(); if(e.getEntityType().equals(EntityType.ENDER_SIGNAL)) { main.getDebugLogger().info("Detected Ender Signal..."); - if(!TerraWorld.isTerraWorld(BukkitAdapter.adapt(e.getEntity().getWorld()))) return; + if(!BukkitAdapter.adapt(e.getEntity().getWorld()).isTerraWorld()) return; TerraWorld tw = main.getWorld(BukkitAdapter.adapt(e.getEntity().getWorld())); EnderSignal signal = (EnderSignal) entity; TerraStructure config = tw.getConfig().getStructureRegistry().get(tw.getConfig().getTemplate().getLocatable().get("STRONGHOLD")); @@ -53,7 +53,7 @@ public class SpigotListener implements Listener { @EventHandler public void onCartographerChange(VillagerAcquireTradeEvent e) { - if(!TerraWorld.isTerraWorld(BukkitAdapter.adapt(e.getEntity().getWorld()))) return; + if(!BukkitAdapter.adapt(e.getEntity().getWorld()).isTerraWorld()) return; if(!(e.getEntity() instanceof Villager)) return; if(((Villager) e.getEntity()).getProfession().equals(Villager.Profession.CARTOGRAPHER)) { main.logger().severe("Prevented server crash by stopping Cartographer villager from spawning."); @@ -65,7 +65,7 @@ public class SpigotListener implements Listener { @EventHandler public void onCartographerLevel(VillagerCareerChangeEvent e) { - if(!TerraWorld.isTerraWorld(BukkitAdapter.adapt(e.getEntity().getWorld()))) return; + if(!BukkitAdapter.adapt(e.getEntity().getWorld()).isTerraWorld()) return; if(e.getProfession().equals(Villager.Profession.CARTOGRAPHER)) { main.logger().severe("Prevented server crash by stopping Cartographer villager from spawning."); main.logger().severe("Please upgrade to Paper, which has a StructureLocateEvent that fixes this issue"); diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/TerraListener.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/TerraListener.java index 78615d036..4eb05f91e 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/TerraListener.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/TerraListener.java @@ -22,7 +22,9 @@ public class TerraListener implements EventListener { public void injectTrees(ConfigPackPreLoadEvent event) { for(TreeType value : TreeType.values()) { try { - event.getPack().getTreeRegistry().add(BukkitAdapter.TREE_TRANSFORMER.translate(value), new BukkitTree(value, main)); + String id = BukkitAdapter.TREE_TRANSFORMER.translate(value); + event.getPack().getTreeRegistry().add(id, new BukkitTree(value, main)); + event.getPack().getTreeRegistry().get(id); // Platform trees should never be marked "dead" } catch(DuplicateEntryException ignore) { // If another addon has already registered trees, do nothing. } } diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/population/ChunkCoordinate.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/population/ChunkCoordinate.java index 6e2c87e00..a68e575fa 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/population/ChunkCoordinate.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/population/ChunkCoordinate.java @@ -2,6 +2,7 @@ package com.dfsek.terra.bukkit.population; import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.bukkit.world.BukkitWorld; import java.io.Serializable; import java.util.UUID; @@ -21,7 +22,7 @@ public class ChunkCoordinate implements Serializable { public ChunkCoordinate(Chunk c) { this.x = c.getX(); this.z = c.getZ(); - this.worldID = c.getWorld().getUID(); + this.worldID = ((BukkitWorld) c.getWorld()).getUID(); } public UUID getWorldID() { diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/population/Gaea.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/population/Gaea.java deleted file mode 100644 index 0b38547d6..000000000 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/population/Gaea.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.dfsek.terra.bukkit.population; - - -import com.dfsek.terra.api.platform.world.World; - -import java.io.File; - -public class Gaea { - private static boolean debug; - - public static File getGaeaFolder(World w) { - File f = new File(w.getWorldFolder(), "gaea"); - f.mkdirs(); - return f; - } - - public static boolean isDebug() { - return debug; - } -} diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/population/PopulationManager.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/population/PopulationManager.java index 34ec49d68..e783c0118 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/population/PopulationManager.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/population/PopulationManager.java @@ -4,80 +4,57 @@ import com.dfsek.terra.api.TerraPlugin; import com.dfsek.terra.api.platform.world.Chunk; import com.dfsek.terra.api.platform.world.World; import com.dfsek.terra.api.util.FastRandom; -import com.dfsek.terra.api.util.GlueList; -import com.dfsek.terra.api.world.generation.TerraBlockPopulator; +import com.dfsek.terra.api.world.generation.Chunkified; +import com.dfsek.terra.api.world.generation.TerraChunkGenerator; import com.dfsek.terra.bukkit.TerraBukkitPlugin; -import com.dfsek.terra.profiler.ProfileFuture; -import com.dfsek.terra.profiler.WorldProfiler; +import com.dfsek.terra.bukkit.world.BukkitAdapter; +import com.dfsek.terra.bukkit.world.BukkitWorld; +import com.dfsek.terra.profiler.ProfileFrame; +import org.bukkit.generator.BlockPopulator; import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; import java.util.HashSet; -import java.util.List; import java.util.Random; /** * Cursed management class for the horrors of Bukkit population */ -public class PopulationManager implements TerraBlockPopulator { - private final List attachedPopulators = new GlueList<>(); +public class PopulationManager extends BlockPopulator { + private final TerraChunkGenerator generator; private final HashSet needsPop = new HashSet<>(); private final TerraPlugin main; - private WorldProfiler profiler; - public PopulationManager(TerraPlugin main) { + public PopulationManager(TerraChunkGenerator generator, TerraPlugin main) { + this.generator = generator; this.main = main; } - public void attach(TerraBlockPopulator populator) { - this.attachedPopulators.add(populator); - } - - @Override - @SuppressWarnings("try") - public void populate(@NotNull World world, @NotNull Chunk chunk) { - try(ProfileFuture ignored = measure()) { - needsPop.add(new ChunkCoordinate(chunk)); - int x = chunk.getX(); - int z = chunk.getZ(); - if(((TerraBukkitPlugin) main).isEnabled()) { - for(int xi = -1; xi <= 1; xi++) { - for(int zi = -1; zi <= 1; zi++) { - if(xi == 0 && zi == 0) continue; - if(world.isChunkGenerated(xi + x, zi + z)) checkNeighbors(xi + x, zi + z, world); - } - } - } - } - } - - private ProfileFuture measure() { - if(profiler != null) return profiler.measure("PopulationManagerTime"); - return null; - } - - public void attachProfiler(WorldProfiler p) { - this.profiler = p; - } - @SuppressWarnings("unchecked") public synchronized void saveBlocks(World w) throws IOException { - File f = new File(Gaea.getGaeaFolder(w), "chunks.bin"); + File f = new File(getDataFolder(w), "chunks.bin"); f.createNewFile(); SerializationUtil.toFile((HashSet) needsPop.clone(), f); } @SuppressWarnings("unchecked") public synchronized void loadBlocks(World w) throws IOException, ClassNotFoundException { - File f = new File(Gaea.getGaeaFolder(w), "chunks.bin"); + File f = new File(getDataFolder(w), "chunks.bin"); needsPop.addAll((HashSet) SerializationUtil.fromFile(f)); } + public static File getDataFolder(World w) { + File f = new File(((BukkitWorld) w).getWorldFolder(), "gaea"); + f.mkdirs(); + return f; + } + // Synchronize to prevent chunks from being queued for population multiple times. - public synchronized void checkNeighbors(int x, int z, World w) { - ChunkCoordinate c = new ChunkCoordinate(x, z, w.getUID()); + public synchronized void checkNeighbors(int x, int z, World world) { + BukkitWorld w = (BukkitWorld) world; + ChunkCoordinate c = new ChunkCoordinate(x, z, (w).getUID()); if(w.isChunkGenerated(x + 1, z) && w.isChunkGenerated(x - 1, z) && w.isChunkGenerated(x, z + 1) @@ -87,10 +64,31 @@ public class PopulationManager implements TerraBlockPopulator { long zRand = (random.nextLong() / 2L << 1L) + 1L; random.setSeed((long) x * xRand + (long) z * zRand ^ w.getSeed()); Chunk currentChunk = w.getChunkAt(x, z); - for(TerraBlockPopulator r : attachedPopulators) { - r.populate(w, currentChunk); - } + generator.getPopulators().forEach(populator -> { + if(!(populator instanceof Chunkified)) { + populator.populate(w, currentChunk); + } + }); needsPop.remove(c); } } + + @Override + @SuppressWarnings("try") + public void populate(org.bukkit.@NotNull World world, @NotNull Random random, org.bukkit.@NotNull Chunk source) { + try(ProfileFrame ignore = main.getProfiler().profile("popman")) { + Chunk chunk = BukkitAdapter.adapt(source); + needsPop.add(new ChunkCoordinate(chunk)); + int x = chunk.getX(); + int z = chunk.getZ(); + if(((TerraBukkitPlugin) main).isEnabled()) { + for(int xi = -1; xi <= 1; xi++) { + for(int zi = -1; zi <= 1; zi++) { + if(xi == 0 && zi == 0) continue; + if(world.isChunkGenerated(xi + x, zi + z)) checkNeighbors(xi + x, zi + z, BukkitAdapter.adapt(world)); + } + } + } + } + } } \ No newline at end of file diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/BukkitTree.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/BukkitTree.java index b502639a4..41445619f 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/BukkitTree.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/BukkitTree.java @@ -5,8 +5,10 @@ import com.dfsek.terra.api.math.vector.Location; import com.dfsek.terra.api.platform.handle.WorldHandle; import com.dfsek.terra.api.platform.world.Tree; import com.dfsek.terra.api.util.collections.MaterialSet; +import com.dfsek.terra.profiler.ProfileFrame; import org.bukkit.TreeType; +import java.util.Locale; import java.util.Random; public class BukkitTree implements Tree { @@ -40,8 +42,11 @@ public class BukkitTree implements Tree { } @Override + @SuppressWarnings("try") public boolean plant(Location l, Random r) { - return ((BukkitWorld) l.getWorld()).getHandle().generateTree(BukkitAdapter.adapt(l), delegate); + try(ProfileFrame ignore = main.getProfiler().profile("bukkit_tree:" + delegate.toString().toLowerCase(Locale.ROOT))) { + return ((BukkitWorld) l.getWorld()).getHandle().generateTree(BukkitAdapter.adapt(l), delegate); + } } @Override diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/BukkitWorld.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/BukkitWorld.java index 294314cbf..48f3b7863 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/BukkitWorld.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/BukkitWorld.java @@ -37,17 +37,14 @@ public class BukkitWorld implements World { return new BukkitChunkGenerator(delegate.getGenerator()); } - @Override public String getName() { return delegate.getName(); } - @Override public UUID getUID() { return delegate.getUID(); } - @Override public boolean isChunkGenerated(int x, int z) { return delegate.isChunkGenerated(x, z); } @@ -57,7 +54,6 @@ public class BukkitWorld implements World { return BukkitAdapter.adapt(delegate.getChunkAt(x, z)); } - @Override public File getWorldFolder() { return delegate.getWorldFolder(); } diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitSign.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitSign.java index fe19fac78..05625cc1a 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitSign.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitSign.java @@ -4,6 +4,7 @@ import com.dfsek.terra.api.platform.block.state.SerialState; import com.dfsek.terra.api.platform.block.state.Sign; import org.jetbrains.annotations.NotNull; +@SuppressWarnings("deprecation") public class BukkitSign extends BukkitBlockState implements Sign { protected BukkitSign(org.bukkit.block.Sign block) { super(block); diff --git a/platforms/bukkit/src/main/resources/plugin.yml b/platforms/bukkit/src/main/resources/plugin.yml index 0b0a1fafc..1daeb981f 100644 --- a/platforms/bukkit/src/main/resources/plugin.yml +++ b/platforms/bukkit/src/main/resources/plugin.yml @@ -3,16 +3,13 @@ main: "com.dfsek.terra.bukkit.TerraBukkitPlugin" version: "@VERSION@" load: "STARTUP" author: dfsek +website: "@WIKI@" api-version: "1.13" -description: "An insanely powerful free & open-source data-driven world generator." +description: "@DESCRIPTION@" softdepend: [ "WorldEdit" ] commands: terra: description: "Terra base command" usage: "/terra " aliases: [ "te" ] - permission: "terra.command" - locate: - description: "Locate a Terra Structure" - usage: "/locate " - permission: "terra.locate" \ No newline at end of file + permission: "terra.command" \ No newline at end of file diff --git a/platforms/fabric/build.gradle.kts b/platforms/fabric/build.gradle.kts index b66976f8e..2e153b14f 100644 --- a/platforms/fabric/build.gradle.kts +++ b/platforms/fabric/build.gradle.kts @@ -1,11 +1,14 @@ import com.dfsek.terra.configureCommon import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.modrinth.minotaur.TaskModrinthUpload import net.fabricmc.loom.LoomGradleExtension import net.fabricmc.loom.task.RemapJarTask plugins { `java-library` + `maven-publish` id("fabric-loom").version("0.6-SNAPSHOT") + id("com.modrinth.minotaur").version("1.1.0") } configureCommon() @@ -19,14 +22,18 @@ group = "com.dfsek.terra.fabric" dependencies { "shadedApi"(project(":common")) - "shadedImplementation"("org.yaml:snakeyaml:1.27") - "shadedImplementation"("com.googlecode.json-simple:json-simple:1.1.1") "minecraft"("com.mojang:minecraft:21w11a") "mappings"("net.fabricmc:yarn:21w11a+build.3:v2") "modImplementation"("net.fabricmc:fabric-loader:0.11.3") - "modImplementation"("net.fabricmc.fabric-api:fabric-api:0.32.4+1.17") + "modCompileOnly"("com.sk89q.worldedit:worldedit-fabric-mc1.16:7.2.0-SNAPSHOT") { + exclude(group = "com.google.guava", module = "guava") + exclude(group = "com.google.code.gson", module = "gson") + exclude(group = "it.unimi.dsi", module = "fastutil") + exclude(group = "org.apache.logging.log4j", module = "log4j-api") + exclude(group = "org.apache.logging.log4j", module = "log4j-core") + } } tasks.named("shadowJar") { @@ -37,9 +44,11 @@ tasks.named("shadowJar") { configure { accessWidener("src/main/resources/terra.accesswidener") + refmapName = "terra-refmap.json" } -tasks.register("remapShadedJar") { +val remapped = tasks.register("remapShadedJar") { + group = "fabric" val shadowJar = tasks.getByName("shadowJar") dependsOn(shadowJar) input.set(shadowJar.archiveFile) @@ -47,3 +56,41 @@ tasks.register("remapShadedJar") { addNestedDependencies.set(true) remapAccessWidener.set(true) } + + +tasks.register("publishModrinthFabric") { + dependsOn("remapShadedJar") + group = "fabric" + token = System.getenv("MODRINTH_SECRET") + projectId = "FIlZB9L0" + versionNumber = "${project.version}-fabric" + uploadFile = remapped.get().archiveFile.get().asFile + releaseType = "beta" + addGameVersion("1.16.4") + addGameVersion("1.16.5") + addLoader("fabric") +} + +publishing { + publications { + create("mavenJava") { + artifact(tasks["sourcesJar"]) + artifact(tasks["jar"]) + } + } + + repositories { + val mavenUrl = "https://repo.codemc.io/repository/maven-releases/" + + maven(mavenUrl) { + val mavenUsername: String? by project + val mavenPassword: String? by project + if (mavenUsername != null && mavenPassword != null) { + credentials { + username = mavenUsername + password = mavenPassword + } + } + } + } +} \ No newline at end of file diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/TerraFabricPlugin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/TerraFabricPlugin.java index a046b5cf7..6a0a3ea00 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/TerraFabricPlugin.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/TerraFabricPlugin.java @@ -1,5 +1,7 @@ package com.dfsek.terra.fabric; +import com.dfsek.tectonic.exception.ConfigException; +import com.dfsek.tectonic.exception.LoadException; import com.dfsek.tectonic.loading.TypeRegistry; import com.dfsek.terra.api.TerraPlugin; import com.dfsek.terra.api.addons.TerraAddon; @@ -8,15 +10,14 @@ import com.dfsek.terra.api.addons.annotations.Author; import com.dfsek.terra.api.addons.annotations.Version; import com.dfsek.terra.api.command.CommandManager; import com.dfsek.terra.api.command.TerraCommandManager; -import com.dfsek.terra.api.command.exception.CommandException; import com.dfsek.terra.api.command.exception.MalformedCommandException; import com.dfsek.terra.api.event.EventListener; import com.dfsek.terra.api.event.EventManager; import com.dfsek.terra.api.event.TerraEventManager; import com.dfsek.terra.api.event.annotations.Global; import com.dfsek.terra.api.event.annotations.Priority; +import com.dfsek.terra.api.event.events.config.ConfigPackPostLoadEvent; import com.dfsek.terra.api.event.events.config.ConfigPackPreLoadEvent; -import com.dfsek.terra.api.platform.CommandSender; import com.dfsek.terra.api.platform.block.BlockData; import com.dfsek.terra.api.platform.handle.ItemHandle; import com.dfsek.terra.api.platform.handle.WorldHandle; @@ -24,125 +25,116 @@ import com.dfsek.terra.api.platform.world.Tree; 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.transform.NotNullValidator; import com.dfsek.terra.api.transform.Transformer; +import com.dfsek.terra.api.transform.Validator; +import com.dfsek.terra.api.util.generic.pair.Pair; import com.dfsek.terra.api.util.logging.DebugLogger; import com.dfsek.terra.api.util.logging.Logger; import com.dfsek.terra.commands.CommandUtil; import com.dfsek.terra.config.GenericLoaders; import com.dfsek.terra.config.PluginConfig; -import com.dfsek.terra.config.builder.BiomeBuilder; import com.dfsek.terra.config.lang.LangUtil; import com.dfsek.terra.config.lang.Language; import com.dfsek.terra.config.pack.ConfigPack; -import com.dfsek.terra.fabric.inventory.FabricItemHandle; -import com.dfsek.terra.fabric.mixin.GeneratorTypeAccessor; -import com.dfsek.terra.fabric.world.FabricAdapter; -import com.dfsek.terra.fabric.world.FabricBiome; -import com.dfsek.terra.fabric.world.FabricTree; -import com.dfsek.terra.fabric.world.FabricWorldHandle; -import com.dfsek.terra.fabric.world.TerraBiomeSource; -import com.dfsek.terra.fabric.world.features.PopulatorFeature; -import com.dfsek.terra.fabric.world.generator.FabricChunkGenerator; -import com.dfsek.terra.fabric.world.generator.FabricChunkGeneratorWrapper; +import com.dfsek.terra.fabric.config.PostLoadCompatibilityOptions; +import com.dfsek.terra.fabric.config.PreLoadCompatibilityOptions; +import com.dfsek.terra.fabric.generation.FabricChunkGeneratorWrapper; +import com.dfsek.terra.fabric.generation.PopulatorFeature; +import com.dfsek.terra.fabric.generation.TerraBiomeSource; +import com.dfsek.terra.fabric.handle.FabricItemHandle; +import com.dfsek.terra.fabric.handle.FabricWorldHandle; +import com.dfsek.terra.fabric.util.FabricUtil; +import com.dfsek.terra.profiler.Profiler; +import com.dfsek.terra.profiler.ProfilerImpl; import com.dfsek.terra.registry.exception.DuplicateEntryException; import com.dfsek.terra.registry.master.AddonRegistry; import com.dfsek.terra.registry.master.ConfigRegistry; import com.dfsek.terra.world.TerraWorld; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.RequiredArgumentBuilder; -import net.fabricmc.api.EnvType; import net.fabricmc.api.ModInitializer; -import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.block.Blocks; -import net.minecraft.client.world.GeneratorType; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.text.LiteralText; +import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; import net.minecraft.util.registry.BuiltinRegistries; import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.WorldAccess; import net.minecraft.world.biome.Biome; -import net.minecraft.world.biome.BiomeEffects; -import net.minecraft.world.biome.GenerationSettings; -import net.minecraft.world.biome.SpawnSettings; -import net.minecraft.world.gen.GenerationStep; -import net.minecraft.world.gen.chunk.ChunkGenerator; -import net.minecraft.world.gen.chunk.ChunkGeneratorSettings; +import net.minecraft.world.dimension.DimensionType; import net.minecraft.world.gen.decorator.Decorator; import net.minecraft.world.gen.decorator.NopeDecoratorConfig; import net.minecraft.world.gen.feature.ConfiguredFeature; import net.minecraft.world.gen.feature.ConfiguredFeatures; -import net.minecraft.world.gen.feature.DefaultBiomeFeatures; import net.minecraft.world.gen.feature.DefaultFeatureConfig; import net.minecraft.world.gen.feature.FeatureConfig; -import net.minecraft.world.gen.surfacebuilder.SurfaceBuilder; -import net.minecraft.world.gen.surfacebuilder.TernarySurfaceConfig; import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.LogManager; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; -import java.util.List; -import java.util.Locale; import java.util.Map; -import static net.minecraft.server.command.CommandManager.argument; -import static net.minecraft.server.command.CommandManager.literal; - public class TerraFabricPlugin implements TerraPlugin, ModInitializer { - + private final org.apache.logging.log4j.Logger log4jLogger = LogManager.getLogger(); public static final PopulatorFeature POPULATOR_FEATURE = new PopulatorFeature(DefaultFeatureConfig.CODEC); public static final ConfiguredFeature POPULATOR_CONFIGURED_FEATURE = POPULATOR_FEATURE.configure(FeatureConfig.DEFAULT).decorate(Decorator.NOPE.configure(NopeDecoratorConfig.INSTANCE)); private static TerraFabricPlugin instance; - private final Map worldMap = new HashMap<>(); + private final Map> worldMap = new HashMap<>(); + + public Map> getWorldMap() { + return worldMap; + } + private final EventManager eventManager = new TerraEventManager(this); private final GenericLoaders genericLoaders = new GenericLoaders(this); - private final Logger logger = new Logger() { - private final org.apache.logging.log4j.Logger logger = LogManager.getLogger(); + private final Profiler profiler = new ProfilerImpl(); + + private final Logger logger = new Logger() { @Override public void info(String message) { - logger.info(message); + log4jLogger.info(message); } @Override public void warning(String message) { - logger.warn(message); + log4jLogger.warn(message); } @Override public void severe(String message) { - logger.error(message); + log4jLogger.error(message); } }; + private final DebugLogger debugLogger = new DebugLogger(logger); private final ItemHandle itemHandle = new FabricItemHandle(); private final WorldHandle worldHandle = new FabricWorldHandle(); private final ConfigRegistry registry = new ConfigRegistry(); private final CheckedRegistry checkedRegistry = new CheckedRegistry<>(registry); - private final AddonRegistry addonRegistry = new AddonRegistry(new FabricAddon(this), this); + + private final FabricAddon fabricAddon = new FabricAddon(this); + private final AddonRegistry addonRegistry = new AddonRegistry(fabricAddon, this); private final LockedRegistry addonLockedRegistry = new LockedRegistry<>(addonRegistry); + private final PluginConfig config = new PluginConfig(); + private final Transformer biomeFixer = new Transformer.Builder() - .addTransform(id -> BuiltinRegistries.BIOME.get(Identifier.tryParse(id)), new NotNullValidator<>()) - .addTransform(id -> BuiltinRegistries.BIOME.get(Identifier.tryParse("minecraft:" + id.toLowerCase())), new NotNullValidator<>()).build(); + .addTransform(id -> BuiltinRegistries.BIOME.get(Identifier.tryParse(id)), Validator.notNull()) + .addTransform(id -> BuiltinRegistries.BIOME.get(Identifier.tryParse("minecraft:" + id.toLowerCase())), Validator.notNull()).build(); private File dataFolder; + private final CommandManager manager = new TerraCommandManager(this); + + public CommandManager getManager() { + return manager; + } public static TerraFabricPlugin getInstance() { return instance; } - public static String createBiomeID(ConfigPack pack, String biomeID) { - return pack.getTemplate().getID().toLowerCase() + "/" + biomeID.toLowerCase(Locale.ROOT); - } - @Override public WorldHandle getWorldHandle() { return worldHandle; @@ -150,11 +142,13 @@ public class TerraFabricPlugin implements TerraPlugin, ModInitializer { @Override public TerraWorld getWorld(World world) { - if(worldMap.size() > 1) System.out.println(worldMap.size()); - return worldMap.computeIfAbsent(world.getSeed(), w -> { - logger.info("Loading world " + w); - return new TerraWorld(world, ((FabricChunkGeneratorWrapper) ((FabricChunkGenerator) world.getGenerator()).getHandle()).getPack(), this); - }); + return getWorld(((WorldAccess) world).getDimension()); + } + + public TerraWorld getWorld(DimensionType type) { + TerraWorld world = worldMap.get(type).getRight(); + if(world == null) throw new IllegalArgumentException("No world exists with dimension type " + type); + return world; } @Override @@ -174,7 +168,7 @@ public class TerraFabricPlugin implements TerraPlugin, ModInitializer { @Override public boolean isDebug() { - return true; + return config.isDebug(); } @Override @@ -197,14 +191,11 @@ public class TerraFabricPlugin implements TerraPlugin, ModInitializer { config.load(this); LangUtil.load(config.getLanguage(), this); // Load language. boolean succeed = registry.loadAll(this); - Map newMap = new HashMap<>(); - worldMap.forEach((seed, tw) -> { - tw.getConfig().getSamplerCache().clear(); - String packID = tw.getConfig().getTemplate().getID(); - newMap.put(seed, new TerraWorld(tw.getWorld(), registry.get(packID), this)); + worldMap.forEach((seed, pair) -> { + pair.getRight().getConfig().getSamplerCache().clear(); + String packID = pair.getRight().getConfig().getTemplate().getID(); + pair.setRight(new TerraWorld(pair.getRight().getWorld(), registry.get(packID), this)); }); - worldMap.clear(); - worldMap.putAll(newMap); return succeed; } @@ -238,44 +229,23 @@ public class TerraFabricPlugin implements TerraPlugin, ModInitializer { genericLoaders.register(registry); registry .registerLoader(BlockData.class, (t, o, l) -> worldHandle.createBlockData((String) o)) - .registerLoader(com.dfsek.terra.api.platform.world.Biome.class, (t, o, l) -> new FabricBiome(biomeFixer.translate((String) o))); + .registerLoader(com.dfsek.terra.api.platform.world.Biome.class, (t, o, l) -> biomeFixer.translate((String) o)) + .registerLoader(Identifier.class, (t, o, l) -> { + Identifier identifier = Identifier.tryParse((String) o); + if(identifier == null) throw new LoadException("Invalid identifier: " + o); + return identifier; + }); } - private Biome createBiome(BiomeBuilder biome) { - SpawnSettings.Builder spawnSettings = new SpawnSettings.Builder(); - DefaultBiomeFeatures.addFarmAnimals(spawnSettings); - DefaultBiomeFeatures.addMonsters(spawnSettings, 95, 5, 100); + public void packInit() { + logger.info("Loading config packs..."); + registry.loadAll(this); - Biome vanilla = ((FabricBiome) new ArrayList<>(biome.getVanillaBiomes().getContents()).get(0)).getHandle(); + registry.forEach(pack -> pack.getBiomeRegistry().forEach((id, biome) -> Registry.register(BuiltinRegistries.BIOME, new Identifier("terra", FabricUtil.createBiomeID(pack, id)), FabricUtil.createBiome(fabricAddon, biome, pack)))); // Register all Terra biomes. - GenerationSettings.Builder generationSettings = new GenerationSettings.Builder(); - generationSettings.surfaceBuilder(SurfaceBuilder.DEFAULT.withConfig(new TernarySurfaceConfig(Blocks.GRASS_BLOCK.getDefaultState(), Blocks.DIRT.getDefaultState(), Blocks.GRAVEL.getDefaultState()))); // It needs a surfacebuilder, even though we dont use it. - generationSettings.feature(GenerationStep.Feature.VEGETAL_DECORATION, POPULATOR_CONFIGURED_FEATURE); - - - BiomeEffects.Builder effects = new BiomeEffects.Builder() - .waterColor(vanilla.getEffects().waterColor) - .waterFogColor(vanilla.getEffects().waterFogColor) - .fogColor(vanilla.getEffects().fogColor) - .skyColor(vanilla.getEffects().skyColor) - .grassColorModifier(vanilla.getEffects().grassColorModifier); - vanilla.getEffects().grassColor.ifPresent(effects::grassColor); - vanilla.getEffects().foliageColor.ifPresent(effects::foliageColor); - - return (new Biome.Builder()) - .precipitation(vanilla.getPrecipitation()) - .category(vanilla.getCategory()) - .depth(vanilla.getDepth()) - .scale(vanilla.getScale()) - .temperature(vanilla.getTemperature()) - .downfall(vanilla.getDownfall()) - .effects(vanilla.getEffects()) // TODO: configurable - .spawnSettings(spawnSettings.build()) - .generationSettings(generationSettings.build()) - .build(); + logger.info("Loaded packs."); } - @SuppressWarnings("unchecked") @Override public void onInitialize() { instance = this; @@ -283,6 +253,7 @@ public class TerraFabricPlugin implements TerraPlugin, ModInitializer { this.dataFolder = new File(FabricLoader.getInstance().getConfigDir().toFile(), "Terra"); saveDefaultConfig(); config.load(this); + debugLogger.setDebug(config.isDebug()); LangUtil.load(config.getLanguage(), this); logger.info("Initializing Terra..."); @@ -291,86 +262,21 @@ public class TerraFabricPlugin implements TerraPlugin, ModInitializer { } logger.info("Loaded addons."); - registry.loadAll(this); - logger.info("Loaded packs."); - - Registry.register(Registry.FEATURE, new Identifier("terra", "flora_populator"), POPULATOR_FEATURE); - RegistryKey> floraKey = RegistryKey.of(Registry.CONFIGURED_FEATURE_WORLDGEN, new Identifier("terra", "flora_populator")); + Registry.register(Registry.FEATURE, new Identifier("terra", "populator"), POPULATOR_FEATURE); + RegistryKey> floraKey = RegistryKey.of(Registry.CONFIGURED_FEATURE_WORLDGEN, new Identifier("terra", "populator")); Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, floraKey.getValue(), POPULATOR_CONFIGURED_FEATURE); - registry.forEach(pack -> pack.getBiomeRegistry().forEach((id, biome) -> Registry.register(BuiltinRegistries.BIOME, new Identifier("terra", createBiomeID(pack, id)), createBiome(biome)))); // Register all Terra biomes. Registry.register(Registry.CHUNK_GENERATOR, new Identifier("terra:terra"), FabricChunkGeneratorWrapper.CODEC); Registry.register(Registry.BIOME_SOURCE, new Identifier("terra:terra"), TerraBiomeSource.CODEC); - if(FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) { - registry.forEach(pack -> { - final GeneratorType generatorType = new GeneratorType("terra." + pack.getTemplate().getID()) { - @Override - protected ChunkGenerator getChunkGenerator(Registry biomeRegistry, Registry chunkGeneratorSettingsRegistry, long seed) { - return new FabricChunkGeneratorWrapper(new TerraBiomeSource(biomeRegistry, seed, pack), seed, pack); - } - }; - //noinspection ConstantConditions - ((GeneratorTypeAccessor) generatorType).setTranslationKey(new LiteralText("Terra:" + pack.getTemplate().getID())); - GeneratorTypeAccessor.getVALUES().add(generatorType); - }); - } - - CommandManager manager = new TerraCommandManager(this); try { CommandUtil.registerAll(manager); } catch(MalformedCommandException e) { e.printStackTrace(); // TODO do something here even though this should literally never happen } - - CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> { - int max = manager.getMaxArgumentDepth(); - System.out.println("MAX:" + max); - RequiredArgumentBuilder arg = argument("arg" + (max - 1), StringArgumentType.word()); - for(int i = 0; i < max; i++) { - System.out.println("arg " + i); - RequiredArgumentBuilder next = argument("arg" + (max - i - 1), StringArgumentType.word()); - - arg = next.then(assemble(arg, manager)); - } - - dispatcher.register(literal("terra").executes(context -> 1).then(assemble(arg, manager))); - dispatcher.register(literal("te").executes(context -> 1).then(assemble(arg, manager))); - //dispatcher.register(literal("te").redirect(root)); - } - ); - - } - - private RequiredArgumentBuilder assemble(RequiredArgumentBuilder in, CommandManager manager) { - return in.suggests((context, builder) -> { - List args = parseCommand(context.getInput()); - CommandSender sender = FabricAdapter.adapt(context.getSource()); - try { - manager.tabComplete(args.remove(0), sender, args).forEach(builder::suggest); - } catch(CommandException e) { - sender.sendMessage(e.getMessage()); - } - return builder.buildFuture(); - }).executes(context -> { - List args = parseCommand(context.getInput()); - try { - manager.execute(args.remove(0), FabricAdapter.adapt(context.getSource()), args); - } catch(CommandException e) { - context.getSource().sendError(new LiteralText(e.getMessage())); - } - return 1; - }); - } - - private List parseCommand(String command) { - if(command.startsWith("/terra ")) command = command.substring("/terra ".length()); - else if(command.startsWith("/te ")) command = command.substring("/te ".length()); - List c = new ArrayList<>(Arrays.asList(command.split(" "))); - if(command.endsWith(" ")) c.add(""); - return c; + logger.info("Finished initialization."); } @@ -379,13 +285,20 @@ public class TerraFabricPlugin implements TerraPlugin, ModInitializer { return eventManager; } + @Override + public Profiler getProfiler() { + return profiler; + } + @Addon("Terra-Fabric") @Author("Terra") @Version("1.0.0") - private static final class FabricAddon extends TerraAddon implements EventListener { + public static final class FabricAddon extends TerraAddon implements EventListener { private final TerraPlugin main; + private final Map> templates = new HashMap<>(); + private FabricAddon(TerraPlugin main) { this.main = main; } @@ -418,14 +331,52 @@ public class TerraFabricPlugin implements TerraPlugin, ModInitializer { injectTree(treeRegistry, "MEGA_SPRUCE", ConfiguredFeatures.MEGA_SPRUCE); injectTree(treeRegistry, "CRIMSON_FUNGUS", ConfiguredFeatures.CRIMSON_FUNGI); injectTree(treeRegistry, "WARPED_FUNGUS", ConfiguredFeatures.WARPED_FUNGI); + + PreLoadCompatibilityOptions template = new PreLoadCompatibilityOptions(); + try { + event.loadTemplate(template); + } catch(ConfigException e) { + e.printStackTrace(); + } + + if(template.doRegistryInjection()) { + BuiltinRegistries.CONFIGURED_FEATURE.getEntries().forEach(entry -> { + if(!template.getExcludedRegistryFeatures().contains(entry.getKey().getValue())) { + try { + event.getPack().getTreeRegistry().add(entry.getKey().getValue().toString(), (Tree) entry.getValue()); + main.getDebugLogger().info("Injected ConfiguredFeature " + entry.getKey().getValue() + " as Tree."); + } catch(DuplicateEntryException ignored) { + } + } + }); + } + templates.put(event.getPack(), Pair.of(template, null)); + } + + @Priority(Priority.HIGHEST) + @Global + public void createInjectionOptions(ConfigPackPostLoadEvent event) { + PostLoadCompatibilityOptions template = new PostLoadCompatibilityOptions(); + + try { + event.loadTemplate(template); + } catch(ConfigException e) { + e.printStackTrace(); + } + + templates.get(event.getPack()).setRight(template); } private void injectTree(CheckedRegistry registry, String id, ConfiguredFeature tree) { try { - registry.add(id, new FabricTree(tree)); + registry.add(id, (Tree) tree); } catch(DuplicateEntryException ignore) { } } + + public Map> getTemplates() { + return templates; + } } } diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/FabricBlock.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/FabricBlock.java similarity index 78% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/FabricBlock.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/FabricBlock.java index 9379380ca..78c1211b5 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/FabricBlock.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/FabricBlock.java @@ -1,4 +1,4 @@ -package com.dfsek.terra.fabric.world.block; +package com.dfsek.terra.fabric.block; import com.dfsek.terra.api.math.vector.Location; import com.dfsek.terra.api.platform.block.Block; @@ -6,9 +6,9 @@ import com.dfsek.terra.api.platform.block.BlockData; import com.dfsek.terra.api.platform.block.BlockFace; import com.dfsek.terra.api.platform.block.BlockType; import com.dfsek.terra.api.platform.block.state.BlockState; -import com.dfsek.terra.fabric.world.FabricAdapter; -import com.dfsek.terra.fabric.world.block.state.FabricBlockState; -import com.dfsek.terra.fabric.world.handles.world.FabricWorldAccess; +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.fabric.util.FabricAdapter; +import net.minecraft.block.FluidBlock; import net.minecraft.util.math.BlockPos; import net.minecraft.world.WorldAccess; @@ -21,7 +21,10 @@ public class FabricBlock implements Block { @Override public void setBlockData(BlockData data, boolean physics) { - delegate.worldAccess.setBlockState(delegate.position, ((FabricBlockData) data).getHandle(), physics ? 3 : 1042, 0); + delegate.worldAccess.setBlockState(delegate.position, ((FabricBlockData) data).getHandle(), physics ? 3 : 1042); + if(physics && ((FabricBlockData) data).getHandle().getBlock() instanceof FluidBlock) { + delegate.worldAccess.getFluidTickScheduler().schedule(delegate.position, ((FluidBlock) ((FabricBlockData) data).getHandle().getBlock()).getFluidState(((FabricBlockData) data).getHandle()).getFluid(), 0); + } } @Override @@ -31,7 +34,7 @@ public class FabricBlock implements Block { @Override public BlockState getState() { - return FabricBlockState.newInstance(this); + return FabricAdapter.adapt(this); } @Override @@ -47,7 +50,7 @@ public class FabricBlock implements Block { @Override public Location getLocation() { - return FabricAdapter.adapt(delegate.position).toLocation(new FabricWorldAccess(delegate.worldAccess)); + return FabricAdapter.adapt(delegate.position).toLocation((World) delegate.worldAccess); } @Override diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/FabricBlockData.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/FabricBlockData.java similarity index 85% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/FabricBlockData.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/FabricBlockData.java index 2467e992a..6fd723bfb 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/FabricBlockData.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/FabricBlockData.java @@ -1,11 +1,10 @@ -package com.dfsek.terra.fabric.world.block; +package com.dfsek.terra.fabric.block; import com.dfsek.terra.api.platform.block.BlockData; import com.dfsek.terra.api.platform.block.BlockType; -import com.dfsek.terra.fabric.world.FabricAdapter; +import com.dfsek.terra.fabric.mixin.access.StateAccessor; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; -import net.minecraft.state.State; import net.minecraft.util.registry.Registry; import java.util.stream.Collectors; @@ -19,7 +18,7 @@ public class FabricBlockData implements BlockData { @Override public BlockType getBlockType() { - return FabricAdapter.adapt(delegate.getBlock()); + return (BlockType) delegate.getBlock(); } @Override @@ -41,7 +40,7 @@ public class FabricBlockData implements BlockData { StringBuilder data = new StringBuilder(Registry.BLOCK.getId(delegate.getBlock()).toString()); if(!delegate.getEntries().isEmpty()) { data.append('['); - data.append(delegate.getEntries().entrySet().stream().map(State.PROPERTY_MAP_PRINTER).collect(Collectors.joining(","))); + data.append(delegate.getEntries().entrySet().stream().map(StateAccessor.getPropertyMapPrinter()).collect(Collectors.joining(","))); data.append(']'); } return data.toString(); diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricAnaloguePowerable.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricAnaloguePowerable.java similarity index 84% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricAnaloguePowerable.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricAnaloguePowerable.java index 2e7a55347..b2fab164e 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricAnaloguePowerable.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricAnaloguePowerable.java @@ -1,7 +1,7 @@ -package com.dfsek.terra.fabric.world.block.data; +package com.dfsek.terra.fabric.block.data; import com.dfsek.terra.api.platform.block.data.AnaloguePowerable; -import com.dfsek.terra.fabric.world.block.FabricBlockData; +import com.dfsek.terra.fabric.block.FabricBlockData; import net.minecraft.block.BlockState; /** diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricDirectional.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricDirectional.java similarity index 88% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricDirectional.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricDirectional.java index 21584a8e8..be4eb316b 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricDirectional.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricDirectional.java @@ -1,9 +1,9 @@ -package com.dfsek.terra.fabric.world.block.data; +package com.dfsek.terra.fabric.block.data; import com.dfsek.terra.api.platform.block.BlockFace; import com.dfsek.terra.api.platform.block.data.Directional; -import com.dfsek.terra.fabric.world.FabricAdapter; -import com.dfsek.terra.fabric.world.block.FabricBlockData; +import com.dfsek.terra.fabric.block.FabricBlockData; +import com.dfsek.terra.fabric.util.FabricAdapter; import net.minecraft.block.BlockState; import net.minecraft.state.property.DirectionProperty; diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricMultipleFacing.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricMultipleFacing.java similarity index 95% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricMultipleFacing.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricMultipleFacing.java index c4c1bf40f..d1caae8e1 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricMultipleFacing.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricMultipleFacing.java @@ -1,8 +1,8 @@ -package com.dfsek.terra.fabric.world.block.data; +package com.dfsek.terra.fabric.block.data; import com.dfsek.terra.api.platform.block.BlockFace; import com.dfsek.terra.api.platform.block.data.MultipleFacing; -import com.dfsek.terra.fabric.world.block.FabricBlockData; +import com.dfsek.terra.fabric.block.FabricBlockData; import net.minecraft.block.BlockState; import net.minecraft.state.property.Properties; diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricOrientable.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricOrientable.java similarity index 75% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricOrientable.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricOrientable.java index 5c9e179d0..d8e88d566 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricOrientable.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricOrientable.java @@ -1,8 +1,9 @@ -package com.dfsek.terra.fabric.world.block.data; +package com.dfsek.terra.fabric.block.data; import com.dfsek.terra.api.platform.block.Axis; import com.dfsek.terra.api.platform.block.data.Orientable; -import com.dfsek.terra.fabric.world.block.FabricBlockData; +import com.dfsek.terra.fabric.block.FabricBlockData; +import com.dfsek.terra.fabric.util.FabricAdapter; import net.minecraft.block.BlockState; import net.minecraft.state.property.EnumProperty; import net.minecraft.util.math.Direction; @@ -26,11 +27,11 @@ public class FabricOrientable extends FabricBlockData implements Orientable { @Override public Axis getAxis() { - return FabricEnumAdapter.adapt(getHandle().get(property)); + return FabricAdapter.adapt(getHandle().get(property)); } @Override public void setAxis(Axis axis) { - delegate = delegate.with(property, FabricEnumAdapter.adapt(axis)); + delegate = delegate.with(property, FabricAdapter.adapt(axis)); } } diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricRotatable.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricRotatable.java similarity index 97% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricRotatable.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricRotatable.java index a238f21e1..4b7a971ea 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricRotatable.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricRotatable.java @@ -1,8 +1,8 @@ -package com.dfsek.terra.fabric.world.block.data; +package com.dfsek.terra.fabric.block.data; import com.dfsek.terra.api.platform.block.BlockFace; import com.dfsek.terra.api.platform.block.data.Rotatable; -import com.dfsek.terra.fabric.world.block.FabricBlockData; +import com.dfsek.terra.fabric.block.FabricBlockData; import net.minecraft.block.BlockState; import net.minecraft.state.property.Properties; diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricSlab.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricSlab.java similarity index 61% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricSlab.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricSlab.java index a6ca5f5de..cf0d0c277 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricSlab.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricSlab.java @@ -1,6 +1,7 @@ -package com.dfsek.terra.fabric.world.block.data; +package com.dfsek.terra.fabric.block.data; import com.dfsek.terra.api.platform.block.data.Slab; +import com.dfsek.terra.fabric.util.FabricAdapter; import net.minecraft.block.BlockState; import net.minecraft.state.property.Properties; @@ -11,11 +12,11 @@ public class FabricSlab extends FabricWaterlogged implements Slab { @Override public Type getType() { - return FabricEnumAdapter.adapt(delegate.get(Properties.SLAB_TYPE)); + return FabricAdapter.adapt(delegate.get(Properties.SLAB_TYPE)); } @Override public void setType(Type type) { - delegate = delegate.with(Properties.SLAB_TYPE, FabricEnumAdapter.adapt(type)); + delegate = delegate.with(Properties.SLAB_TYPE, FabricAdapter.adapt(type)); } } diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricStairs.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricStairs.java similarity index 67% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricStairs.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricStairs.java index ec268f784..d18ec13da 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricStairs.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricStairs.java @@ -1,7 +1,8 @@ -package com.dfsek.terra.fabric.world.block.data; +package com.dfsek.terra.fabric.block.data; import com.dfsek.terra.api.platform.block.BlockFace; import com.dfsek.terra.api.platform.block.data.Stairs; +import com.dfsek.terra.fabric.util.FabricAdapter; import net.minecraft.block.BlockState; import net.minecraft.state.property.Properties; @@ -12,31 +13,31 @@ public class FabricStairs extends FabricWaterlogged implements Stairs { @Override public Shape getShape() { - return FabricEnumAdapter.adapt(getHandle().get(Properties.STAIR_SHAPE)); + return FabricAdapter.adapt(getHandle().get(Properties.STAIR_SHAPE)); } @Override public void setShape(Shape shape) { - super.delegate = getHandle().with(Properties.STAIR_SHAPE, FabricEnumAdapter.adapt(shape)); + super.delegate = getHandle().with(Properties.STAIR_SHAPE, FabricAdapter.adapt(shape)); } @Override public Half getHalf() { - return FabricEnumAdapter.adapt(getHandle().get(Properties.BLOCK_HALF)); + return FabricAdapter.adapt(getHandle().get(Properties.BLOCK_HALF)); } @Override public void setHalf(Half half) { - super.delegate = getHandle().with(Properties.BLOCK_HALF, FabricEnumAdapter.adapt(half)); + super.delegate = getHandle().with(Properties.BLOCK_HALF, FabricAdapter.adapt(half)); } @Override public BlockFace getFacing() { - return FabricEnumAdapter.adapt(getHandle().get(Properties.HORIZONTAL_FACING)); + return FabricAdapter.adapt(getHandle().get(Properties.HORIZONTAL_FACING)); } @Override public void setFacing(BlockFace facing) { - super.delegate = getHandle().with(Properties.HORIZONTAL_FACING, FabricEnumAdapter.adapt(facing)); + super.delegate = getHandle().with(Properties.HORIZONTAL_FACING, FabricAdapter.adapt(facing)); } } diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricWaterlogged.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricWaterlogged.java similarity index 84% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricWaterlogged.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricWaterlogged.java index 95960e295..7e3636bde 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricWaterlogged.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/block/data/FabricWaterlogged.java @@ -1,7 +1,7 @@ -package com.dfsek.terra.fabric.world.block.data; +package com.dfsek.terra.fabric.block.data; import com.dfsek.terra.api.platform.block.data.Waterlogged; -import com.dfsek.terra.fabric.world.block.FabricBlockData; +import com.dfsek.terra.fabric.block.FabricBlockData; import net.minecraft.block.BlockState; import net.minecraft.state.property.Properties; diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/command/FabricCommandAdapter.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/command/FabricCommandAdapter.java deleted file mode 100644 index a8db1bb54..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/command/FabricCommandAdapter.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.dfsek.terra.fabric.command; - -public class FabricCommandAdapter { - public static void register() { - - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/command/StringListArgumentType.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/command/StringListArgumentType.java deleted file mode 100644 index 392aaf1af..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/command/StringListArgumentType.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.dfsek.terra.fabric.command; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.arguments.ArgumentType; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class StringListArgumentType implements ArgumentType> { - @Override - public List parse(StringReader reader) { - final String text = reader.getRemaining(); - reader.setCursor(reader.getTotalLength()); - return new ArrayList<>(Arrays.asList(text.split(" "))); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/config/PostLoadCompatibilityOptions.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/config/PostLoadCompatibilityOptions.java new file mode 100644 index 000000000..ae812bed5 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/config/PostLoadCompatibilityOptions.java @@ -0,0 +1,30 @@ +package com.dfsek.terra.fabric.config; + +import com.dfsek.tectonic.annotations.Default; +import com.dfsek.tectonic.annotations.Value; +import com.dfsek.tectonic.config.ConfigTemplate; +import com.dfsek.terra.config.builder.BiomeBuilder; +import net.minecraft.util.Identifier; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +@SuppressWarnings("FieldMayBeFinal") +public class PostLoadCompatibilityOptions implements ConfigTemplate { + @Value("structures.inject-biome.exclude-biomes") + @Default + private Map> excludedPerBiomeStructures = new HashMap<>(); + + @Value("features.inject-biome.exclude-biomes") + @Default + private Map> excludedPerBiomeFeatures = new HashMap<>(); + + public Map> getExcludedPerBiomeFeatures() { + return excludedPerBiomeFeatures; + } + + public Map> getExcludedPerBiomeStructures() { + return excludedPerBiomeStructures; + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/config/PreLoadCompatibilityOptions.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/config/PreLoadCompatibilityOptions.java new file mode 100644 index 000000000..0dfbccf00 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/config/PreLoadCompatibilityOptions.java @@ -0,0 +1,52 @@ +package com.dfsek.terra.fabric.config; + +import com.dfsek.tectonic.annotations.Default; +import com.dfsek.tectonic.annotations.Value; +import com.dfsek.tectonic.config.ConfigTemplate; +import net.minecraft.util.Identifier; + +import java.util.HashSet; +import java.util.Set; + +@SuppressWarnings("FieldMayBeFinal") +public class PreLoadCompatibilityOptions implements ConfigTemplate { + @Value("features.inject-registry.enable") + @Default + private boolean doRegistryInjection = false; + + @Value("features.inject-biome.enable") + @Default + private boolean doBiomeInjection = false; + + @Value("features.inject-registry.excluded-features") + @Default + private Set excludedRegistryFeatures = new HashSet<>(); + + @Value("features.inject-biome.excluded-features") + @Default + private Set excludedBiomeFeatures = new HashSet<>(); + + @Value("structures.inject-biome.excluded-features") + @Default + private Set excludedBiomeStructures = new HashSet<>(); + + public boolean doBiomeInjection() { + return doBiomeInjection; + } + + public boolean doRegistryInjection() { + return doRegistryInjection; + } + + public Set getExcludedBiomeFeatures() { + return excludedBiomeFeatures; + } + + public Set getExcludedRegistryFeatures() { + return excludedRegistryFeatures; + } + + public Set getExcludedBiomeStructures() { + return excludedBiomeStructures; + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/FabricChunkGeneratorWrapper.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/FabricChunkGeneratorWrapper.java new file mode 100644 index 000000000..1c9d587ed --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/FabricChunkGeneratorWrapper.java @@ -0,0 +1,195 @@ +package com.dfsek.terra.fabric.generation; + +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.api.platform.world.generator.ChunkData; +import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; +import com.dfsek.terra.api.util.FastRandom; +import com.dfsek.terra.api.world.biome.UserDefinedBiome; +import com.dfsek.terra.api.world.generation.TerraChunkGenerator; +import com.dfsek.terra.api.world.locate.AsyncStructureFinder; +import com.dfsek.terra.config.pack.ConfigPack; +import com.dfsek.terra.fabric.TerraFabricPlugin; +import com.dfsek.terra.fabric.util.FabricAdapter; +import com.dfsek.terra.world.TerraWorld; +import com.dfsek.terra.world.generation.generators.DefaultChunkGenerator3D; +import com.dfsek.terra.world.generation.math.samplers.Sampler; +import com.dfsek.terra.world.population.items.TerraStructure; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.jafama.FastMath; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.structure.StructureManager; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.BlockView; +import net.minecraft.world.ChunkRegion; +import net.minecraft.world.Heightmap; +import net.minecraft.world.SpawnHelper; +import net.minecraft.world.WorldAccess; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.source.BiomeAccess; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.dimension.DimensionType; +import net.minecraft.world.gen.ChunkRandom; +import net.minecraft.world.gen.GenerationStep; +import net.minecraft.world.gen.StructureAccessor; +import net.minecraft.world.gen.chunk.ChunkGenerator; +import net.minecraft.world.gen.chunk.StructuresConfig; +import net.minecraft.world.gen.chunk.VerticalBlockSample; +import net.minecraft.world.gen.feature.StructureFeature; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class FabricChunkGeneratorWrapper extends ChunkGenerator implements GeneratorWrapper { + private final long seed; + private final DefaultChunkGenerator3D delegate; + private final TerraBiomeSource biomeSource; + public static final Codec PACK_CODEC = (RecordCodecBuilder.create(config -> config.group( + Codec.STRING.fieldOf("pack").forGetter(pack -> pack.getTemplate().getID()) + ).apply(config, config.stable(TerraFabricPlugin.getInstance().getConfigRegistry()::get)))); + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + TerraBiomeSource.CODEC.fieldOf("biome_source").forGetter(generator -> generator.biomeSource), + Codec.LONG.fieldOf("seed").stable().forGetter(generator -> generator.seed), + PACK_CODEC.fieldOf("pack").stable().forGetter(generator -> generator.pack)) + .apply(instance, instance.stable(FabricChunkGeneratorWrapper::new))); + private final ConfigPack pack; + private DimensionType dimensionType; + + public ConfigPack getPack() { + return pack; + } + + public FabricChunkGeneratorWrapper(TerraBiomeSource biomeSource, long seed, ConfigPack configPack) { + super(biomeSource, new StructuresConfig(false)); + this.pack = configPack; + + this.delegate = new DefaultChunkGenerator3D(pack, TerraFabricPlugin.getInstance()); + delegate.getMain().logger().info("Loading world with config pack " + pack.getTemplate().getID()); + this.biomeSource = biomeSource; + + this.seed = seed; + } + + + @Override + protected Codec getCodec() { + return CODEC; + } + + @Override + public ChunkGenerator withSeed(long seed) { + return new FabricChunkGeneratorWrapper((TerraBiomeSource) this.biomeSource.withSeed(seed), seed, pack); + } + + @Override + public void buildSurface(ChunkRegion region, Chunk chunk) { + + } + + public void setDimensionType(DimensionType dimensionType) { + this.dimensionType = dimensionType; + } + + @Nullable + @Override + public BlockPos locateStructure(ServerWorld world, StructureFeature feature, BlockPos center, int radius, boolean skipExistingChunks) { + if(!pack.getTemplate().disableStructures()) { + String name = Objects.requireNonNull(Registry.STRUCTURE_FEATURE.getId(feature)).toString(); + TerraWorld terraWorld = TerraFabricPlugin.getInstance().getWorld((World) world); + TerraStructure located = pack.getStructure(pack.getTemplate().getLocatable().get(name)); + if(located != null) { + CompletableFuture result = new CompletableFuture<>(); + AsyncStructureFinder finder = new AsyncStructureFinder(terraWorld.getBiomeProvider(), located, FabricAdapter.adapt(center).toLocation((World) world), 0, 500, location -> { + result.complete(FabricAdapter.adapt(location)); + }, TerraFabricPlugin.getInstance()); + finder.run(); // Do this synchronously. + try { + return result.get(); + } catch(InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + } + return super.locateStructure(world, feature, center, radius, skipExistingChunks); + } + + @Override + public void populateNoise(WorldAccess world, StructureAccessor accessor, Chunk chunk) { + delegate.generateChunkData((World) world, new FastRandom(), chunk.getPos().x, chunk.getPos().z, (ChunkData) chunk); + } + + @Override + public void carve(long seed, BiomeAccess access, Chunk chunk, GenerationStep.Carver carver) { + if(pack.getTemplate().vanillaCaves()) super.carve(seed, access, chunk, carver); + } + + @Override + public void setStructureStarts(DynamicRegistryManager dynamicRegistryManager, StructureAccessor structureAccessor, Chunk chunk, StructureManager structureManager, long worldSeed) { + if(pack.getTemplate().vanillaStructures()) + super.setStructureStarts(dynamicRegistryManager, structureAccessor, chunk, structureManager, worldSeed); + } + + @Override + public boolean isStrongholdStartingChunk(ChunkPos chunkPos) { + if(pack.getTemplate().vanillaStructures()) return super.isStrongholdStartingChunk(chunkPos); + return false; + } + + @Override + public int getHeight(int x, int z, Heightmap.Type heightmapType) { + TerraWorld world = TerraFabricPlugin.getInstance().getWorld(dimensionType); + Sampler sampler = world.getConfig().getSamplerCache().getChunk(FastMath.floorDiv(x, 16), FastMath.floorDiv(z, 16)); + int cx = FastMath.floorMod(x, 16); + int cz = FastMath.floorMod(z, 16); + + int height = world.getWorld().getMaxHeight(); + + while(height >= 0 && sampler.sample(cx, height-1, cz) < 0) height--; + + return height; + } + + @Override + public BlockView getColumnSample(int x, int z) { + TerraWorld world = TerraFabricPlugin.getInstance().getWorld(dimensionType); + int height = getHeight(x, z, Heightmap.Type.WORLD_SURFACE); + BlockState[] array = new BlockState[256]; + for(int y = 255; y >= 0; y--) { + if(y > height) { + if(y > ((UserDefinedBiome) world.getBiomeProvider().getBiome(x, z)).getConfig().getSeaLevel()) { + array[y] = Blocks.AIR.getDefaultState(); + } else { + array[y] = Blocks.WATER.getDefaultState(); + } + } else { + array[y] = Blocks.STONE.getDefaultState(); + } + } + + return new VerticalBlockSample(array); + } + + @Override + public void populateEntities(ChunkRegion region) { + if(pack.getTemplate().vanillaMobs()) { + int cx = region.getCenterChunkX(); + int cy = region.getCenterChunkZ(); + Biome biome = region.getBiome((new ChunkPos(cx, cy)).getStartPos()); + ChunkRandom chunkRandom = new ChunkRandom(); + chunkRandom.setPopulationSeed(region.getSeed(), cx << 4, cy << 4); + SpawnHelper.populateEntities(region, biome, cx, cy, chunkRandom); + } + } + + @Override + public TerraChunkGenerator getHandle() { + return delegate; + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/PopulatorFeature.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/PopulatorFeature.java new file mode 100644 index 000000000..5b009ae1f --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/PopulatorFeature.java @@ -0,0 +1,29 @@ +package com.dfsek.terra.fabric.generation; + +import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.api.platform.world.World; +import com.mojang.serialization.Codec; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.StructureWorldAccess; +import net.minecraft.world.gen.chunk.ChunkGenerator; +import net.minecraft.world.gen.feature.DefaultFeatureConfig; +import net.minecraft.world.gen.feature.Feature; + +import java.util.Random; + +/** + * Feature wrapper for Terra populator + */ +public class PopulatorFeature extends Feature { + public PopulatorFeature(Codec codec) { + super(codec); + } + + @Override + public boolean generate(StructureWorldAccess world, ChunkGenerator chunkGenerator, Random random, BlockPos pos, DefaultFeatureConfig config) { + if(!(chunkGenerator instanceof FabricChunkGeneratorWrapper)) return true; + FabricChunkGeneratorWrapper gen = (FabricChunkGeneratorWrapper) chunkGenerator; + gen.getHandle().getPopulators().forEach(populator -> populator.populate((World) world, (Chunk) world)); + return true; + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/TerraBiomeSource.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/TerraBiomeSource.java similarity index 83% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/TerraBiomeSource.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/TerraBiomeSource.java index f64fc55c6..d8ee36830 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/TerraBiomeSource.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/TerraBiomeSource.java @@ -1,9 +1,10 @@ -package com.dfsek.terra.fabric.world; +package com.dfsek.terra.fabric.generation; import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.provider.BiomeProvider; import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.fabric.TerraFabricPlugin; +import com.dfsek.terra.fabric.util.FabricUtil; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.util.Identifier; @@ -11,8 +12,8 @@ import net.minecraft.util.dynamic.RegistryLookupCodec; import net.minecraft.util.registry.Registry; import net.minecraft.world.biome.Biome; import net.minecraft.world.biome.source.BiomeSource; -import net.minecraft.world.gen.feature.StructureFeature; +import java.util.Objects; import java.util.stream.Collectors; public class TerraBiomeSource extends BiomeSource { @@ -31,7 +32,9 @@ public class TerraBiomeSource extends BiomeSource { private final ConfigPack pack; public TerraBiomeSource(Registry biomes, long seed, ConfigPack pack) { - super(biomes.stream().collect(Collectors.toList())); + super(biomes.stream() + .filter(biome -> Objects.requireNonNull(biomes.getId(biome)).getNamespace().equals("terra")) // Filter out non-Terra biomes. + .collect(Collectors.toList())); this.biomeRegistry = biomes; this.seed = seed; this.grid = pack.getBiomeProviderBuilder().build(seed); @@ -51,14 +54,6 @@ public class TerraBiomeSource extends BiomeSource { @Override public Biome getBiomeForNoiseGen(int biomeX, int biomeY, int biomeZ) { UserDefinedBiome biome = (UserDefinedBiome) grid.getBiome(biomeX << 2, biomeZ << 2); - return biomeRegistry.get(new Identifier("terra", TerraFabricPlugin.createBiomeID(pack, biome.getID()))); + return biomeRegistry.get(new Identifier("terra", FabricUtil.createBiomeID(pack, biome.getID()))); } - - - @Override - public boolean hasStructureFeature(StructureFeature feature) { - return false; - } - - } diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/TerraGeneratorType.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/TerraGeneratorType.java new file mode 100644 index 000000000..6ae8dac89 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/TerraGeneratorType.java @@ -0,0 +1,22 @@ +package com.dfsek.terra.fabric.generation; + +import com.dfsek.terra.config.pack.ConfigPack; +import net.minecraft.client.world.GeneratorType; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.gen.chunk.ChunkGenerator; +import net.minecraft.world.gen.chunk.ChunkGeneratorSettings; + +public class TerraGeneratorType extends GeneratorType { + private final ConfigPack pack; + + public TerraGeneratorType(ConfigPack pack) { + super("terra." + pack.getTemplate().getID()); + this.pack = pack; + } + + @Override + protected ChunkGenerator getChunkGenerator(Registry biomeRegistry, Registry chunkGeneratorSettingsRegistry, long seed) { + return new FabricChunkGeneratorWrapper(new TerraBiomeSource(biomeRegistry, seed, pack), seed, pack); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricItemHandle.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/handle/FabricItemHandle.java similarity index 69% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricItemHandle.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/handle/FabricItemHandle.java index ef77d9928..4ea99a542 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricItemHandle.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/handle/FabricItemHandle.java @@ -1,9 +1,8 @@ -package com.dfsek.terra.fabric.inventory; +package com.dfsek.terra.fabric.handle; import com.dfsek.terra.api.platform.handle.ItemHandle; import com.dfsek.terra.api.platform.inventory.Item; import com.dfsek.terra.api.platform.inventory.item.Enchantment; -import com.dfsek.terra.fabric.world.FabricAdapter; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.command.argument.ItemStackArgumentType; @@ -18,7 +17,7 @@ public class FabricItemHandle implements ItemHandle { @Override public Item createItem(String data) { try { - return FabricAdapter.adapt(new ItemStackArgumentType().parse(new StringReader(data)).getItem()); + return (Item) new ItemStackArgumentType().parse(new StringReader(data)).getItem(); } catch(CommandSyntaxException e) { throw new IllegalArgumentException("Invalid item data \"" + data + "\"", e); } @@ -26,11 +25,11 @@ public class FabricItemHandle implements ItemHandle { @Override public Enchantment getEnchantment(String id) { - return FabricAdapter.adapt(Registry.ENCHANTMENT.get(Identifier.tryParse(id))); + return (Enchantment) (Registry.ENCHANTMENT.get(Identifier.tryParse(id))); } @Override public Set getEnchantments() { - return Registry.ENCHANTMENT.stream().map(FabricAdapter::adapt).collect(Collectors.toSet()); + return Registry.ENCHANTMENT.stream().map(enchantment -> (Enchantment) enchantment).collect(Collectors.toSet()); } } diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/FabricWorldHandle.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/handle/FabricWorldHandle.java similarity index 61% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/FabricWorldHandle.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/handle/FabricWorldHandle.java index 8d6919d56..1f801f1d0 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/FabricWorldHandle.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/handle/FabricWorldHandle.java @@ -1,8 +1,13 @@ -package com.dfsek.terra.fabric.world; +package com.dfsek.terra.fabric.handle; +import com.dfsek.terra.api.math.vector.Location; import com.dfsek.terra.api.platform.entity.EntityType; +import com.dfsek.terra.api.platform.entity.Player; import com.dfsek.terra.api.platform.handle.WorldHandle; -import com.dfsek.terra.fabric.world.block.FabricBlockData; +import com.dfsek.terra.api.util.generic.pair.Pair; +import com.dfsek.terra.fabric.block.FabricBlockData; +import com.dfsek.terra.fabric.util.FabricAdapter; +import com.dfsek.terra.fabric.util.WorldEditUtil; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.block.BlockState; @@ -30,6 +35,16 @@ public class FabricWorldHandle implements WorldHandle { public EntityType getEntity(String id) { Identifier identifier = Identifier.tryParse(id); if(identifier == null) identifier = Identifier.tryParse("minecraft:" + id.toLowerCase(Locale.ROOT)); - return FabricAdapter.adapt(Registry.ENTITY_TYPE.get(identifier)); + return (EntityType) Registry.ENTITY_TYPE.get(identifier); + } + + @Override + public Pair getSelectedLocation(Player player) { + try { + Class.forName("com.sk89q.worldedit.WorldEdit"); + } catch(ClassNotFoundException e) { + throw new IllegalStateException("WorldEdit is not installed."); + } + return WorldEditUtil.getSelection(player); } } diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricEnchantment.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricEnchantment.java deleted file mode 100644 index 347f53315..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricEnchantment.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.dfsek.terra.fabric.inventory; - -import com.dfsek.terra.api.platform.inventory.ItemStack; -import com.dfsek.terra.api.platform.inventory.item.Enchantment; -import com.dfsek.terra.fabric.world.FabricAdapter; -import net.minecraft.util.registry.Registry; - -import java.util.Objects; - -public class FabricEnchantment implements Enchantment { - private final net.minecraft.enchantment.Enchantment enchantment; - - public FabricEnchantment(net.minecraft.enchantment.Enchantment enchantment) { - this.enchantment = enchantment; - } - - @Override - public net.minecraft.enchantment.Enchantment getHandle() { - return enchantment; - } - - @Override - public boolean canEnchantItem(ItemStack itemStack) { - return enchantment.isAcceptableItem(FabricAdapter.adapt(itemStack)); - } - - @Override - public String getID() { - return Objects.requireNonNull(Registry.ENCHANTMENT.getId(enchantment)).toString(); - } - - @Override - public boolean conflictsWith(Enchantment other) { - return !enchantment.canCombine(FabricAdapter.adapt(other)); - } - - @Override - public int getMaxLevel() { - return enchantment.getMaxLevel(); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricInventory.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricInventory.java deleted file mode 100644 index 8b86f657d..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricInventory.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.dfsek.terra.fabric.inventory; - -import com.dfsek.terra.api.platform.inventory.Inventory; -import com.dfsek.terra.api.platform.inventory.ItemStack; -import com.dfsek.terra.fabric.world.FabricAdapter; -import net.minecraft.item.Items; - -public class FabricInventory implements Inventory { - private final net.minecraft.inventory.Inventory delegate; - - public FabricInventory(net.minecraft.inventory.Inventory delegate) { - this.delegate = delegate; - } - - @Override - public net.minecraft.inventory.Inventory getHandle() { - return delegate; - } - - @Override - public int getSize() { - return delegate.size(); - } - - @Override - public ItemStack getItem(int slot) { - net.minecraft.item.ItemStack itemStack = delegate.getStack(slot); - return itemStack.getItem() == Items.AIR ? null : FabricAdapter.adapt(itemStack); - } - - @Override - public void setItem(int slot, ItemStack newStack) { - delegate.setStack(slot, FabricAdapter.adapt(newStack)); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricItem.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricItem.java deleted file mode 100644 index 40c683119..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricItem.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.dfsek.terra.fabric.inventory; - -import com.dfsek.terra.api.platform.inventory.Item; -import com.dfsek.terra.api.platform.inventory.ItemStack; - -public class FabricItem implements Item { - private final net.minecraft.item.Item delegate; - - public FabricItem(net.minecraft.item.Item delegate) { - this.delegate = delegate; - } - - @Override - public net.minecraft.item.Item getHandle() { - return delegate; - } - - @Override - public ItemStack newItemStack(int amount) { - return new FabricItemStack(new net.minecraft.item.ItemStack(delegate, amount)); - } - - @Override - public double getMaxDurability() { - return delegate.getMaxDamage(); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricItemStack.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricItemStack.java deleted file mode 100644 index 19d33b524..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/FabricItemStack.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.dfsek.terra.fabric.inventory; - -import com.dfsek.terra.api.platform.inventory.Item; -import com.dfsek.terra.api.platform.inventory.ItemStack; -import com.dfsek.terra.api.platform.inventory.item.ItemMeta; -import com.dfsek.terra.fabric.inventory.meta.FabricDamageable; -import com.dfsek.terra.fabric.inventory.meta.FabricItemMeta; - -public class FabricItemStack implements ItemStack { - private net.minecraft.item.ItemStack delegate; - - public FabricItemStack(net.minecraft.item.ItemStack delegate) { - this.delegate = delegate; - } - - @Override - public int getAmount() { - return delegate.getCount(); - } - - @Override - public void setAmount(int i) { - delegate.setCount(i); - } - - @Override - public Item getType() { - return new FabricItem(delegate.getItem()); - } - - @Override - public ItemMeta getItemMeta() { - if(delegate.isDamageable()) return new FabricDamageable(delegate.copy()); - return new FabricItemMeta(delegate.copy()); - } - - @Override - public void setItemMeta(ItemMeta meta) { - this.delegate = ((FabricItemMeta) meta).getHandle(); - } - - @Override - public net.minecraft.item.ItemStack getHandle() { - return delegate; - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/meta/FabricDamageable.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/meta/FabricDamageable.java deleted file mode 100644 index 4a4cc918e..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/meta/FabricDamageable.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.dfsek.terra.fabric.inventory.meta; - -import com.dfsek.terra.api.platform.inventory.item.Damageable; -import net.minecraft.item.ItemStack; - -public class FabricDamageable extends FabricItemMeta implements Damageable { - public FabricDamageable(ItemStack delegate) { - super(delegate); - } - - @Override - public int getDamage() { - return delegate.getDamage(); - } - - @Override - public void setDamage(int damage) { - delegate.setDamage(damage); - } - - @Override - public boolean hasDamage() { - return delegate.isDamageable(); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/meta/FabricItemMeta.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/meta/FabricItemMeta.java deleted file mode 100644 index 2c4d9d079..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/inventory/meta/FabricItemMeta.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.dfsek.terra.fabric.inventory.meta; - -import com.dfsek.terra.api.platform.inventory.item.Enchantment; -import com.dfsek.terra.api.platform.inventory.item.ItemMeta; -import com.dfsek.terra.fabric.world.FabricAdapter; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.util.registry.Registry; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public class FabricItemMeta implements ItemMeta { - protected final ItemStack delegate; - - public FabricItemMeta(ItemStack delegate) { - this.delegate = delegate; - } - - @Override - public ItemStack getHandle() { - return delegate; - } - - @Override - public Map getEnchantments() { - if(!delegate.hasEnchantments()) return Collections.emptyMap(); - Map map = new HashMap<>(); - - delegate.getEnchantments().forEach(enchantment -> { - CompoundTag eTag = (CompoundTag) enchantment; - map.put(FabricAdapter.adapt(Registry.ENCHANTMENT.get(eTag.getInt("id"))), eTag.getInt("lvl")); - }); - return map; - } - - @Override - public void addEnchantment(Enchantment enchantment, int level) { - delegate.addEnchantment(FabricAdapter.adapt(enchantment), level); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/CommandManagerMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/CommandManagerMixin.java new file mode 100644 index 000000000..2e98614df --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/CommandManagerMixin.java @@ -0,0 +1,86 @@ +package com.dfsek.terra.fabric.mixin; + +import com.dfsek.terra.api.command.exception.CommandException; +import com.dfsek.terra.api.platform.CommandSender; +import com.dfsek.terra.api.platform.entity.Entity; +import com.dfsek.terra.fabric.TerraFabricPlugin; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.LiteralText; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static net.minecraft.server.command.CommandManager.argument; +import static net.minecraft.server.command.CommandManager.literal; + +@Mixin(CommandManager.class) +public abstract class CommandManagerMixin { + @Shadow + @Final + private CommandDispatcher dispatcher; + + @Inject(method = "", at = @At(value = "INVOKE", target = "Lcom/mojang/brigadier/CommandDispatcher;findAmbiguities(Lcom/mojang/brigadier/AmbiguityConsumer;)V", remap = false)) + private void injectTerraCommands(CommandManager.RegistrationEnvironment environment, CallbackInfo ci) { + com.dfsek.terra.api.command.CommandManager manager = TerraFabricPlugin.getInstance().getManager(); + int max = manager.getMaxArgumentDepth(); + RequiredArgumentBuilder arg = argument("arg" + (max - 1), StringArgumentType.word()); + for(int i = 0; i < max; i++) { + RequiredArgumentBuilder next = argument("arg" + (max - i - 1), StringArgumentType.word()); + + arg = next.then(assemble(arg, manager)); + } + + dispatcher.register(literal("terra").executes(context -> 1).then(assemble(arg, manager))); + dispatcher.register(literal("te").executes(context -> 1).then(assemble(arg, manager))); + } + + private RequiredArgumentBuilder assemble(RequiredArgumentBuilder in, com.dfsek.terra.api.command.CommandManager manager) { + return in.suggests((context, builder) -> { + List args = parseCommand(context.getInput()); + CommandSender sender = (CommandSender) context.getSource(); + try { + sender = (Entity) context.getSource().getEntityOrThrow(); + } catch(CommandSyntaxException ignore) { + } + try { + manager.tabComplete(args.remove(0), sender, args).forEach(builder::suggest); + } catch(CommandException e) { + sender.sendMessage(e.getMessage()); + } + return builder.buildFuture(); + }).executes(context -> { + List args = parseCommand(context.getInput()); + CommandSender sender = (CommandSender) context.getSource(); + try { + sender = (Entity) context.getSource().getEntityOrThrow(); + } catch(CommandSyntaxException ignore) { + } + try { + manager.execute(args.remove(0), sender, args); + } catch(CommandException e) { + context.getSource().sendError(new LiteralText(e.getMessage())); + } + return 1; + }); + } + + private List parseCommand(String command) { + if(command.startsWith("/terra ")) command = command.substring("/terra ".length()); + else if(command.startsWith("/te ")) command = command.substring("/te ".length()); + List c = new ArrayList<>(Arrays.asList(command.split(" "))); + if(command.endsWith(" ")) c.add(""); + return c; + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/MixinGeneratorOptions.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/GeneratorOptionsMixin.java similarity index 83% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/MixinGeneratorOptions.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/GeneratorOptionsMixin.java index aa588249a..3eafe5469 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/MixinGeneratorOptions.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/GeneratorOptionsMixin.java @@ -2,8 +2,8 @@ package com.dfsek.terra.fabric.mixin; import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.fabric.TerraFabricPlugin; -import com.dfsek.terra.fabric.world.TerraBiomeSource; -import com.dfsek.terra.fabric.world.generator.FabricChunkGeneratorWrapper; +import com.dfsek.terra.fabric.generation.TerraBiomeSource; +import com.dfsek.terra.fabric.generation.FabricChunkGeneratorWrapper; import com.google.common.base.MoreObjects; import net.minecraft.util.registry.DynamicRegistryManager; import net.minecraft.util.registry.Registry; @@ -13,16 +13,17 @@ import net.minecraft.world.dimension.DimensionOptions; import net.minecraft.world.dimension.DimensionType; import net.minecraft.world.gen.GeneratorOptions; import net.minecraft.world.gen.chunk.ChunkGeneratorSettings; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.Properties; import java.util.Random; -// Mixins commented out until loom fixes multi-project builds. - -//@Mixin(GeneratorOptions.class) -public class MixinGeneratorOptions { - //@Inject(method = "fromProperties(Lnet/minecraft/util/registry/DynamicRegistryManager;Ljava/util/Properties;)Lnet/minecraft/world/gen/GeneratorOptions;", at = @At("HEAD"), cancellable = true) +@Mixin(GeneratorOptions.class) +public abstract class GeneratorOptionsMixin { + @Inject(method = "fromProperties(Lnet/minecraft/util/registry/DynamicRegistryManager;Ljava/util/Properties;)Lnet/minecraft/world/gen/GeneratorOptions;", at = @At("HEAD"), cancellable = true) private static void fromProperties(DynamicRegistryManager dynamicRegistryManager, Properties properties, CallbackInfoReturnable cir) { if(properties.get("level-type") == null) { return; diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/ServerWorldMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/ServerWorldMixin.java new file mode 100644 index 000000000..b3d5d7c97 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/ServerWorldMixin.java @@ -0,0 +1,35 @@ +package com.dfsek.terra.fabric.mixin; + +import com.dfsek.terra.api.util.generic.pair.Pair; +import com.dfsek.terra.fabric.TerraFabricPlugin; +import com.dfsek.terra.fabric.generation.FabricChunkGeneratorWrapper; +import com.dfsek.terra.world.TerraWorld; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.WorldGenerationProgressListener; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.World; +import net.minecraft.world.dimension.DimensionType; +import net.minecraft.world.gen.Spawner; +import net.minecraft.world.gen.chunk.ChunkGenerator; +import net.minecraft.world.level.ServerWorldProperties; +import net.minecraft.world.level.storage.LevelStorage; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; +import java.util.concurrent.Executor; + +@Mixin(ServerWorld.class) +public abstract class ServerWorldMixin { + @Inject(method = "", at = @At(value = "RETURN")) + public void injectConstructor(MinecraftServer server, Executor workerExecutor, LevelStorage.Session session, ServerWorldProperties properties, RegistryKey registryKey, DimensionType dimensionType, WorldGenerationProgressListener worldGenerationProgressListener, ChunkGenerator chunkGenerator, boolean debugWorld, long l, List list, boolean bl, CallbackInfo ci) { + if(chunkGenerator instanceof FabricChunkGeneratorWrapper) { + TerraFabricPlugin.getInstance().getWorldMap().put(dimensionType, Pair.of((ServerWorld) (Object) this, new TerraWorld((com.dfsek.terra.api.platform.world.World) this, ((FabricChunkGeneratorWrapper) chunkGenerator).getPack(), TerraFabricPlugin.getInstance()))); + ((FabricChunkGeneratorWrapper) chunkGenerator).setDimensionType(dimensionType); + TerraFabricPlugin.getInstance().logger().info("Registered world " + this + " to dimension type " + dimensionType); + } + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/access/BiomeEffectsAccessor.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/access/BiomeEffectsAccessor.java new file mode 100644 index 000000000..2fe2391dc --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/access/BiomeEffectsAccessor.java @@ -0,0 +1,31 @@ +package com.dfsek.terra.fabric.mixin.access; + +import net.minecraft.world.biome.BiomeEffects; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Optional; + +@Mixin(BiomeEffects.class) +public interface BiomeEffectsAccessor { + @Accessor("fogColor") + int getFogColor(); + + @Accessor("waterColor") + int getWaterColor(); + + @Accessor("waterFogColor") + int getWaterFogColor(); + + @Accessor("skyColor") + int getSkyColor(); + + @Accessor("foliageColor") + Optional getFoliageColor(); + + @Accessor("grassColor") + Optional getGrassColor(); + + @Accessor("grassColorModifier") + BiomeEffects.GrassColorModifier getGrassColorModifier(); +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/GeneratorTypeAccessor.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/access/GeneratorTypeAccessor.java similarity index 74% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/GeneratorTypeAccessor.java rename to platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/access/GeneratorTypeAccessor.java index 07449ae3a..bd315cc54 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/GeneratorTypeAccessor.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/access/GeneratorTypeAccessor.java @@ -1,4 +1,4 @@ -package com.dfsek.terra.fabric.mixin; +package com.dfsek.terra.fabric.mixin.access; import net.minecraft.client.world.GeneratorType; import net.minecraft.text.Text; @@ -10,12 +10,12 @@ import java.util.List; @Mixin(GeneratorType.class) public interface GeneratorTypeAccessor { - @Accessor - static List getVALUES() { + @Accessor("VALUES") + static List getValues() { throw new UnsupportedOperationException(); } @Mutable - @Accessor + @Accessor("translationKey") void setTranslationKey(Text translationKey); } diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/access/MobSpawnerLogicAccessor.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/access/MobSpawnerLogicAccessor.java new file mode 100644 index 000000000..4b7eb1f4f --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/access/MobSpawnerLogicAccessor.java @@ -0,0 +1,12 @@ +package com.dfsek.terra.fabric.mixin.access; + +import net.minecraft.util.Identifier; +import net.minecraft.world.MobSpawnerLogic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(MobSpawnerLogic.class) +public interface MobSpawnerLogicAccessor { + @Invoker("getEntityId") + Identifier callGetEntityId(); +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/access/StateAccessor.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/access/StateAccessor.java new file mode 100644 index 000000000..8518443af --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/access/StateAccessor.java @@ -0,0 +1,17 @@ +package com.dfsek.terra.fabric.mixin.access; + +import net.minecraft.state.State; +import net.minecraft.state.property.Property; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; +import java.util.function.Function; + +@Mixin(State.class) +public interface StateAccessor { + @Accessor("PROPERTY_MAP_PRINTER") + static Function, Comparable>, String> getPropertyMapPrinter() { + throw new UnsupportedOperationException(); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/BiomeMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/BiomeMixin.java new file mode 100644 index 000000000..4a928cbb6 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/BiomeMixin.java @@ -0,0 +1,16 @@ +package com.dfsek.terra.fabric.mixin.implementations; + +import net.minecraft.world.biome.Biome; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(Biome.class) +@Implements(@Interface(iface = com.dfsek.terra.api.platform.world.Biome.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class BiomeMixin { + @Intrinsic + public Object terra$getHandle() { + return this; + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/ChunkGeneratorMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/ChunkGeneratorMixin.java new file mode 100644 index 000000000..cc7b82e80 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/ChunkGeneratorMixin.java @@ -0,0 +1,16 @@ +package com.dfsek.terra.fabric.mixin.implementations; + +import net.minecraft.world.gen.chunk.ChunkGenerator; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ChunkGenerator.class) +@Implements(@Interface(iface = com.dfsek.terra.api.platform.world.generator.ChunkGenerator.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ChunkGeneratorMixin { + @Intrinsic + public Object terra$getHandle() { + return this; + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/ConfiguredFeatureMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/ConfiguredFeatureMixin.java new file mode 100644 index 000000000..0398a2d4c --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/ConfiguredFeatureMixin.java @@ -0,0 +1,42 @@ +package com.dfsek.terra.fabric.mixin.implementations; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.world.Tree; +import com.dfsek.terra.api.util.collections.MaterialSet; +import com.dfsek.terra.fabric.TerraFabricPlugin; +import com.dfsek.terra.profiler.ProfileFrame; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.BuiltinRegistries; +import net.minecraft.world.StructureWorldAccess; +import net.minecraft.world.gen.chunk.ChunkGenerator; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Locale; +import java.util.Random; + +@Mixin(ConfiguredFeature.class) +@Implements(@Interface(iface = Tree.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ConfiguredFeatureMixin { + @Shadow + public abstract boolean generate(StructureWorldAccess world, ChunkGenerator chunkGenerator, Random random, BlockPos pos); + + @SuppressWarnings({"ConstantConditions", "try"}) + public boolean terra$plant(Location l, Random r) { + String id = BuiltinRegistries.CONFIGURED_FEATURE.getId((ConfiguredFeature) (Object) this).toString(); + try(ProfileFrame ignore = TerraFabricPlugin.getInstance().getProfiler().profile("fabric_tree:" + id.toLowerCase(Locale.ROOT))) { + StructureWorldAccess fabricWorldAccess = ((StructureWorldAccess) l.getWorld()); + ChunkGenerator generatorWrapper = (ChunkGenerator) l.getWorld().getGenerator(); + return generate(fabricWorldAccess, generatorWrapper, r, new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ())); + } + } + + public MaterialSet terra$getSpawnable() { + return MaterialSet.get(TerraFabricPlugin.getInstance().getWorldHandle().createBlockData("minecraft:grass_block"), + TerraFabricPlugin.getInstance().getWorldHandle().createBlockData("minecraft:podzol"), + TerraFabricPlugin.getInstance().getWorldHandle().createBlockData("minecraft:mycelium")); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/BlockEntityMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/BlockEntityMixin.java new file mode 100644 index 000000000..74c5ccd23 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/BlockEntityMixin.java @@ -0,0 +1,62 @@ +package com.dfsek.terra.fabric.mixin.implementations.block; + +import com.dfsek.terra.api.platform.block.Block; +import com.dfsek.terra.api.platform.block.BlockData; +import com.dfsek.terra.api.platform.block.state.BlockState; +import com.dfsek.terra.fabric.block.FabricBlock; +import com.dfsek.terra.fabric.util.FabricAdapter; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(BlockEntity.class) +@Implements(@Interface(iface = BlockState.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class BlockEntityMixin { + @Shadow + protected BlockPos pos; + @Shadow + @Nullable + protected World world; + + @Shadow + public abstract net.minecraft.block.BlockState getCachedState(); + + @Shadow + public abstract boolean hasWorld(); + + @Intrinsic + public Object terra$getHandle() { + return this; + } + + public Block terra$getBlock() { + return new FabricBlock(pos, world); + } + + public int terra$getX() { + return pos.getX(); + } + + public int terra$getY() { + return pos.getY(); + } + + public int terra$getZ() { + return pos.getZ(); + } + + public BlockData terra$getBlockData() { + return FabricAdapter.adapt(getCachedState()); + } + + public boolean terra$update(boolean applyPhysics) { + if(hasWorld()) world.getChunk(pos).setBlockEntity(pos, (BlockEntity) (Object) this); + return true; + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/BlockMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/BlockMixin.java new file mode 100644 index 000000000..579b404f8 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/BlockMixin.java @@ -0,0 +1,38 @@ +package com.dfsek.terra.fabric.mixin.implementations.block; + +import com.dfsek.terra.api.platform.block.BlockData; +import com.dfsek.terra.api.platform.block.BlockType; +import com.dfsek.terra.fabric.util.FabricAdapter; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(Block.class) +@Implements(@Interface(iface = BlockType.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class BlockMixin { + @Shadow + private BlockState defaultState; + + @Intrinsic + public Object terra$getHandle() { + return this; + } + + public BlockData terra$getDefaultData() { + return FabricAdapter.adapt(defaultState); + } + + public boolean terra$isSolid() { + return defaultState.isOpaque(); + } + + @SuppressWarnings("ConstantConditions") + public boolean terra$isWater() { + return ((Object) this) == Blocks.WATER; + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/state/LootableContainerBlockEntityMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/state/LootableContainerBlockEntityMixin.java new file mode 100644 index 000000000..f72957753 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/state/LootableContainerBlockEntityMixin.java @@ -0,0 +1,23 @@ +package com.dfsek.terra.fabric.mixin.implementations.block.state; + +import com.dfsek.terra.api.platform.block.state.Container; +import com.dfsek.terra.api.platform.inventory.Inventory; +import com.dfsek.terra.fabric.mixin.implementations.block.BlockEntityMixin; +import net.minecraft.block.entity.LootableContainerBlockEntity; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(LootableContainerBlockEntity.class) +@Implements(@Interface(iface = Container.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class LootableContainerBlockEntityMixin extends BlockEntityMixin { + public Inventory terra$getInventory() { + return (Inventory) this; + } + + @Intrinsic + public Object terra$getHandle() { + return this; + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/state/MobSpawnerBlockEntityMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/state/MobSpawnerBlockEntityMixin.java new file mode 100644 index 000000000..8f664787e --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/state/MobSpawnerBlockEntityMixin.java @@ -0,0 +1,119 @@ +package com.dfsek.terra.fabric.mixin.implementations.block.state; + +import com.dfsek.terra.api.platform.block.state.MobSpawner; +import com.dfsek.terra.api.platform.block.state.SerialState; +import com.dfsek.terra.api.platform.entity.EntityType; +import com.dfsek.terra.fabric.TerraFabricPlugin; +import com.dfsek.terra.fabric.mixin.access.MobSpawnerLogicAccessor; +import net.minecraft.block.entity.MobSpawnerBlockEntity; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.MobSpawnerLogic; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(MobSpawnerBlockEntity.class) +@Implements(@Interface(iface = MobSpawner.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class MobSpawnerBlockEntityMixin { + @Shadow + public abstract MobSpawnerLogic getLogic(); + + public EntityType terra$getSpawnedType() { + return (EntityType) Registry.ENTITY_TYPE.get(((MobSpawnerLogicAccessor) getLogic()).callGetEntityId()); + } + + public void terra$setSpawnedType(@NotNull EntityType creatureType) { + getLogic().setEntityId((net.minecraft.entity.EntityType) creatureType); + } + + public int terra$getDelay() { + return 0; + } + + public void terra$setDelay(int delay) { + + } + + public int terra$getMinSpawnDelay() { + return 0; + } + + public void terra$setMinSpawnDelay(int delay) { + + } + + public int terra$getMaxSpawnDelay() { + return 0; + } + + public void terra$setMaxSpawnDelay(int delay) { + + } + + public int terra$getSpawnCount() { + return 0; + } + + public void terra$setSpawnCount(int spawnCount) { + + } + + public int terra$getMaxNearbyEntities() { + return 0; + } + + public void terra$setMaxNearbyEntities(int maxNearbyEntities) { + + } + + public int terra$getRequiredPlayerRange() { + return 0; + } + + public void terra$setRequiredPlayerRange(int requiredPlayerRange) { + + } + + public int terra$getSpawnRange() { + return 0; + } + + public void terra$setSpawnRange(int spawnRange) { + + } + + public void terra$applyState(String state) { + SerialState.parse(state).forEach((k, v) -> { + switch(k) { + case "type": + terra$setSpawnedType(TerraFabricPlugin.getInstance().getWorldHandle().getEntity(v)); + return; + case "delay": + terra$setDelay(Integer.parseInt(v)); + return; + case "min_delay": + terra$setMinSpawnDelay(Integer.parseInt(v)); + return; + case "max_delay": + terra$setMaxSpawnDelay(Integer.parseInt(v)); + return; + case "spawn_count": + terra$setSpawnCount(Integer.parseInt(v)); + return; + case "spawn_range": + terra$setSpawnRange(Integer.parseInt(v)); + return; + case "max_nearby": + terra$setMaxNearbyEntities(Integer.parseInt(v)); + return; + case "required_player_range": + terra$setRequiredPlayerRange(Integer.parseInt(v)); + return; + default: + throw new IllegalArgumentException("Invalid property: " + k); + } + }); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/state/SignBlockEntityMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/state/SignBlockEntityMixin.java new file mode 100644 index 000000000..f8ab0ee3f --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/block/state/SignBlockEntityMixin.java @@ -0,0 +1,47 @@ +package com.dfsek.terra.fabric.mixin.implementations.block.state; + +import com.dfsek.terra.api.platform.block.state.SerialState; +import com.dfsek.terra.api.platform.block.state.Sign; +import net.minecraft.block.entity.SignBlockEntity; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(SignBlockEntity.class) +@Implements(@Interface(iface = Sign.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class SignBlockEntityMixin { + @Shadow + public abstract void setTextOnRow(int row, Text text); + + @Shadow + @Final + private Text[] text; + + public @NotNull String[] terra$getLines() { + String[] lines = new String[text.length]; + for(int i = 0; i < text.length; i++) { + lines[i] = text[i].asString(); + } + return lines; + } + + public @NotNull String terra$getLine(int index) throws IndexOutOfBoundsException { + return text[index].asString(); + } + + public void terra$setLine(int index, @NotNull String line) throws IndexOutOfBoundsException { + setTextOnRow(index, new LiteralText(line)); + } + + public void terra$applyState(String state) { + SerialState.parse(state).forEach((k, v) -> { + if(!k.startsWith("text")) throw new IllegalArgumentException("Invalid property: " + k); + terra$setLine(Integer.parseInt(k.substring(4)), v); + }); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/chunk/ChunkRegionMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/chunk/ChunkRegionMixin.java new file mode 100644 index 000000000..7b12e34d9 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/chunk/ChunkRegionMixin.java @@ -0,0 +1,55 @@ +package com.dfsek.terra.fabric.mixin.implementations.chunk; + +import com.dfsek.terra.api.platform.block.Block; +import com.dfsek.terra.api.platform.block.BlockData; +import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.fabric.block.FabricBlock; +import com.dfsek.terra.fabric.block.FabricBlockData; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ChunkRegion; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ChunkRegion.class) +@Implements(@Interface(iface = Chunk.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ChunkRegionMixin { + @Final + @Shadow + private int centerChunkX; + + @Final + @Shadow + private int centerChunkZ; + + public int terra$getX() { + return centerChunkX; + } + + public int terra$getZ() { + return centerChunkZ; + } + + public World terra$getWorld() { + return (World) this; + } + + public Block terra$getBlock(int x, int y, int z) { + BlockPos pos = new BlockPos(x + (centerChunkX << 4), y, z + (centerChunkZ << 4)); + return new FabricBlock(pos, (ChunkRegion) (Object) this); + } + + public @NotNull BlockData terra$getBlockData(int x, int y, int z) { + return terra$getBlock(x, y, z).getBlockData(); + } + + public void terra$setBlock(int x, int y, int z, @NotNull BlockData blockData) { + ((ChunkRegion) (Object) this).setBlockState(new BlockPos(x + (centerChunkX << 4), y, z + (centerChunkZ << 4)), ((FabricBlockData) blockData).getHandle(), 0); + } + + // getHandle already added in world/ChunkRegionMixin. +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/chunk/WorldChunkMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/chunk/WorldChunkMixin.java new file mode 100644 index 000000000..56681d0ab --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/chunk/WorldChunkMixin.java @@ -0,0 +1,55 @@ +package com.dfsek.terra.fabric.mixin.implementations.chunk; + +import com.dfsek.terra.api.platform.block.Block; +import com.dfsek.terra.api.platform.block.BlockData; +import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.fabric.block.FabricBlock; +import com.dfsek.terra.fabric.block.FabricBlockData; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.chunk.WorldChunk; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(WorldChunk.class) +@Implements(@Interface(iface = Chunk.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class WorldChunkMixin { + @Final + @Shadow + private net.minecraft.world.World world; + + public int terra$getX() { + return ((net.minecraft.world.chunk.Chunk) this).getPos().x; + } + + public int terra$getZ() { + return ((net.minecraft.world.chunk.Chunk) this).getPos().z; + } + + public World terra$getWorld() { + return (World) world; + } + + public Block terra$getBlock(int x, int y, int z) { + BlockPos pos = new BlockPos(x + (terra$getX() << 4), y, z + (terra$getZ() << 4)); + return new FabricBlock(pos, world); + } + + public @NotNull BlockData terra$getBlockData(int x, int y, int z) { + return terra$getBlock(x, y, z).getBlockData(); + } + + public void terra$setBlock(int x, int y, int z, @NotNull BlockData blockData) { + ((net.minecraft.world.chunk.Chunk) this).setBlockState(new BlockPos(x, y, z), ((FabricBlockData) blockData).getHandle(), false); + } + + @Intrinsic + public Object terra$getHandle() { + return this; + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/chunk/data/ProtoChunkMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/chunk/data/ProtoChunkMixin.java new file mode 100644 index 000000000..2b2b87eb4 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/chunk/data/ProtoChunkMixin.java @@ -0,0 +1,38 @@ +package com.dfsek.terra.fabric.mixin.implementations.chunk.data; + +import com.dfsek.terra.api.platform.block.BlockData; +import com.dfsek.terra.api.platform.world.generator.ChunkData; +import com.dfsek.terra.fabric.block.FabricBlockData; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.chunk.ProtoChunk; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ProtoChunk.class) +@Implements(@Interface(iface = ChunkData.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ProtoChunkMixin { + @Shadow + public abstract BlockState getBlockState(BlockPos pos); + + public @NotNull BlockData terra$getBlockData(int x, int y, int z) { + return new FabricBlockData(getBlockState(new BlockPos(x, y, z))); + } + + public void terra$setBlock(int x, int y, int z, @NotNull BlockData blockData) { + ((net.minecraft.world.chunk.Chunk) this).setBlockState(new BlockPos(x, y, z), ((FabricBlockData) blockData).getHandle(), false); + } + + @Intrinsic + public Object terra$getHandle() { + return this; + } + + public int terra$getMaxHeight() { + return 255; // TODO: 1.17 - Implement dynamic height. + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/entity/EntityMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/entity/EntityMixin.java new file mode 100644 index 000000000..d829f7fe3 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/entity/EntityMixin.java @@ -0,0 +1,53 @@ +package com.dfsek.terra.fabric.mixin.implementations.entity; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.fabric.util.FabricAdapter; +import net.minecraft.entity.Entity; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.UUID; + +@Mixin(Entity.class) +@Implements(@Interface(iface = com.dfsek.terra.api.platform.entity.Entity.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class EntityMixin { + @Shadow + public net.minecraft.world.World world; + + @Shadow + private BlockPos blockPos; + + @Shadow + public abstract void teleport(double destX, double destY, double destZ); + + @Shadow + public abstract void sendSystemMessage(Text message, UUID senderUuid); + + @Intrinsic + public Object terra$getHandle() { + return this; + } + + public Location terra$getLocation() { + return new Location((World) world, FabricAdapter.adapt(blockPos)); + } + + public void terra$setLocation(Location location) { + teleport(location.getX(), location.getY(), location.getZ()); + } + + public World terra$getWorld() { + return (World) world; + } + + public void terra$sendMessage(String message) { + sendSystemMessage(new LiteralText(message), UUID.randomUUID()); // TODO: look into how this actually works and make it less jank + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/entity/EntityTypeMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/entity/EntityTypeMixin.java new file mode 100644 index 000000000..85ffb4c25 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/entity/EntityTypeMixin.java @@ -0,0 +1,16 @@ +package com.dfsek.terra.fabric.mixin.implementations.entity; + +import net.minecraft.entity.EntityType; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(EntityType.class) +@Implements(@Interface(iface = com.dfsek.terra.api.platform.entity.EntityType.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class EntityTypeMixin { + @Intrinsic + public Object terra$getHandle() { + return this; + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/entity/PlayerEntityMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/entity/PlayerEntityMixin.java new file mode 100644 index 000000000..b4bf0d35e --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/entity/PlayerEntityMixin.java @@ -0,0 +1,13 @@ +package com.dfsek.terra.fabric.mixin.implementations.entity; + +import com.dfsek.terra.api.platform.entity.Player; +import net.minecraft.entity.player.PlayerEntity; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(PlayerEntity.class) +@Implements(@Interface(iface = Player.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class PlayerEntityMixin extends EntityMixin { + +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/entity/ServerCommandSourceMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/entity/ServerCommandSourceMixin.java new file mode 100644 index 000000000..9b0593521 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/entity/ServerCommandSourceMixin.java @@ -0,0 +1,27 @@ +package com.dfsek.terra.fabric.mixin.implementations.entity; + +import com.dfsek.terra.api.platform.CommandSender; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ServerCommandSource.class) +@Implements(@Interface(iface = CommandSender.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ServerCommandSourceMixin { + @Shadow + public abstract void sendFeedback(Text message, boolean broadcastToOps); + + public void terra$sendMessage(String message) { + sendFeedback(new LiteralText(message), true); + } + + @Intrinsic + public Object terra$getHandle() { + return this; + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/LockableContainerBlockEntityMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/LockableContainerBlockEntityMixin.java new file mode 100644 index 000000000..cff0892bb --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/LockableContainerBlockEntityMixin.java @@ -0,0 +1,34 @@ +package com.dfsek.terra.fabric.mixin.implementations.inventory; + +import com.dfsek.terra.api.platform.inventory.Inventory; +import com.dfsek.terra.api.platform.inventory.ItemStack; +import net.minecraft.block.entity.LockableContainerBlockEntity; +import net.minecraft.item.Items; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(LockableContainerBlockEntity.class) +@Implements(@Interface(iface = Inventory.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public class LockableContainerBlockEntityMixin { + @Intrinsic + public Object terra$getHandle() { + return this; + } + + public int terra$getSize() { + return ((LockableContainerBlockEntity) (Object) this).size(); + } + + @SuppressWarnings("ConstantConditions") + public ItemStack terra$getItem(int slot) { + net.minecraft.item.ItemStack itemStack = ((LockableContainerBlockEntity) (Object) this).getStack(slot); + return itemStack.getItem() == Items.AIR ? null : (ItemStack) (Object) itemStack; + } + + @SuppressWarnings("ConstantConditions") + public void terra$setItem(int slot, ItemStack newStack) { + ((LockableContainerBlockEntity) (Object) this).setStack(slot, (net.minecraft.item.ItemStack) (Object) newStack); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/item/ItemMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/item/ItemMixin.java new file mode 100644 index 000000000..b14939c1c --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/item/ItemMixin.java @@ -0,0 +1,30 @@ +package com.dfsek.terra.fabric.mixin.implementations.inventory.item; + +import com.dfsek.terra.api.platform.inventory.ItemStack; +import net.minecraft.item.Item; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(Item.class) +@Implements(@Interface(iface = com.dfsek.terra.api.platform.inventory.Item.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ItemMixin { + @Shadow + public abstract int getMaxDamage(); + + @Intrinsic + public Object terra$getHandle() { + return this; + } + + @SuppressWarnings("ConstantConditions") + public ItemStack terra$newItemStack(int amount) { + return (ItemStack) (Object) new net.minecraft.item.ItemStack((Item) (Object) this, amount); + } + + public double terra$getMaxDurability() { + return getMaxDamage(); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/item/ItemStackMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/item/ItemStackMixin.java new file mode 100644 index 000000000..2ae18cdbd --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/item/ItemStackMixin.java @@ -0,0 +1,62 @@ +package com.dfsek.terra.fabric.mixin.implementations.inventory.item; + +import com.dfsek.terra.api.platform.inventory.Item; +import com.dfsek.terra.api.platform.inventory.item.ItemMeta; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundTag; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ItemStack.class) +@Implements(@Interface(iface = com.dfsek.terra.api.platform.inventory.ItemStack.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ItemStackMixin { + @Shadow + public abstract int getCount(); + + @Shadow + public abstract void setCount(int count); + + @Shadow + public abstract net.minecraft.item.Item getItem(); + + @Shadow + public abstract boolean isDamageable(); + + @Shadow + public abstract void setTag(@Nullable CompoundTag tag); + + public int terra$getAmount() { + return getCount(); + } + + public void terra$setAmount(int i) { + setCount(i); + } + + public Item terra$getType() { + return (Item) getItem(); + } + + public ItemMeta terra$getItemMeta() { + return (ItemMeta) this; + } + + @SuppressWarnings("ConstantConditions") + public void terra$setItemMeta(ItemMeta meta) { + setTag(((ItemStack) (Object) meta).getTag()); + } + + @Intrinsic + public Object terra$getHandle() { + return this; + } + + @Intrinsic + public boolean terra$isDamageable() { + return isDamageable(); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/meta/EnchantmentMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/meta/EnchantmentMixin.java new file mode 100644 index 000000000..eb47a6498 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/meta/EnchantmentMixin.java @@ -0,0 +1,40 @@ +package com.dfsek.terra.fabric.mixin.implementations.inventory.meta; + +import com.dfsek.terra.api.platform.inventory.ItemStack; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.util.registry.Registry; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Objects; + +@Mixin(Enchantment.class) +@Implements(@Interface(iface = com.dfsek.terra.api.platform.inventory.item.Enchantment.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class EnchantmentMixin { + @Shadow + public abstract boolean isAcceptableItem(net.minecraft.item.ItemStack stack); + + @Shadow + public abstract boolean canCombine(Enchantment other); + + @Intrinsic + public Object terra$getHandle() { + return this; + } + + @SuppressWarnings("ConstantConditions") + public boolean terra$canEnchantItem(ItemStack itemStack) { + return isAcceptableItem((net.minecraft.item.ItemStack) (Object) itemStack); + } + + public String terra$getID() { + return Objects.requireNonNull(Registry.ENCHANTMENT.getId((Enchantment) (Object) this)).toString(); + } + + public boolean terra$conflictsWith(com.dfsek.terra.api.platform.inventory.item.Enchantment other) { + return !canCombine((Enchantment) other); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/meta/ItemStackDamageableMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/meta/ItemStackDamageableMixin.java new file mode 100644 index 000000000..0d3a2fd06 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/meta/ItemStackDamageableMixin.java @@ -0,0 +1,36 @@ +package com.dfsek.terra.fabric.mixin.implementations.inventory.meta; + +import com.dfsek.terra.api.platform.inventory.item.Damageable; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ItemStack.class) +@Implements(@Interface(iface = Damageable.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ItemStackDamageableMixin { + @Shadow + public abstract boolean isDamaged(); + + @Shadow + public abstract int getDamage(); + + @Shadow + public abstract void setDamage(int damage); + + public boolean terra$hasDamage() { + return isDamaged(); + } + + @Intrinsic + public void terra$setDamage(int damage) { + setDamage(damage); + } + + @Intrinsic + public int terra$getDamage() { + return getDamage(); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/meta/ItemStackMetaMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/meta/ItemStackMetaMixin.java new file mode 100644 index 000000000..23950f944 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/inventory/meta/ItemStackMetaMixin.java @@ -0,0 +1,51 @@ +package com.dfsek.terra.fabric.mixin.implementations.inventory.meta; + +import com.dfsek.terra.api.platform.inventory.item.Enchantment; +import com.dfsek.terra.api.platform.inventory.item.ItemMeta; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.util.registry.Registry; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +@Mixin(ItemStack.class) +@Implements(@Interface(iface = ItemMeta.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ItemStackMetaMixin { + @Shadow + public abstract boolean hasEnchantments(); + + @Shadow + public abstract ListTag getEnchantments(); + + @Shadow + public abstract void addEnchantment(net.minecraft.enchantment.Enchantment enchantment, int level); + + @Intrinsic + public Object terra$getHandle() { + return this; + } + + @Intrinsic(displace = true) + public Map terra$getEnchantments() { + if(!hasEnchantments()) return Collections.emptyMap(); + Map map = new HashMap<>(); + + getEnchantments().forEach(enchantment -> { + CompoundTag eTag = (CompoundTag) enchantment; + map.put((Enchantment) Registry.ENCHANTMENT.get(eTag.getInt("id")), eTag.getInt("lvl")); + }); + return map; + } + + public void terra$addEnchantment(Enchantment enchantment, int level) { + addEnchantment((net.minecraft.enchantment.Enchantment) enchantment, level); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/package-info.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/package-info.java new file mode 100644 index 000000000..ec0453641 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/package-info.java @@ -0,0 +1,5 @@ +/** + * Mixins in this package implement Terra + * interfaces in Minecraft classes. + */ +package com.dfsek.terra.fabric.mixin.implementations; \ No newline at end of file diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/world/ChunkRegionMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/world/ChunkRegionMixin.java new file mode 100644 index 000000000..427a7ae85 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/world/ChunkRegionMixin.java @@ -0,0 +1,110 @@ +package com.dfsek.terra.fabric.mixin.implementations.world; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.block.Block; +import com.dfsek.terra.api.platform.entity.Entity; +import com.dfsek.terra.api.platform.entity.EntityType; +import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.api.platform.world.generator.ChunkGenerator; +import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; +import com.dfsek.terra.api.world.generation.TerraChunkGenerator; +import com.dfsek.terra.fabric.block.FabricBlock; +import com.dfsek.terra.fabric.generation.FabricChunkGeneratorWrapper; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ChunkRegion; +import net.minecraft.world.ServerWorldAccess; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ChunkRegion.class) +@Implements(@Interface(iface = World.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ChunkRegionMixin { + @Shadow + @Final + private ServerWorld world; + + @Shadow + @Final + private long seed; + + public int terra$getMaxHeight() { + return ((ChunkRegion) (Object) this).getDimensionHeight(); + } + + @SuppressWarnings("deprecation") + public ChunkGenerator terra$getGenerator() { + return (ChunkGenerator) ((ChunkRegion) (Object) this).toServerWorld().getChunkManager().getChunkGenerator(); + } + + public Chunk terra$getChunkAt(int x, int z) { + return (Chunk) ((ChunkRegion) (Object) this).getChunk(x, z); + } + + public Block terra$getBlockAt(int x, int y, int z) { + return new FabricBlock(new BlockPos(x, y, z), ((ChunkRegion) (Object) this)); + } + + @SuppressWarnings("deprecation") + public Entity terra$spawnEntity(Location location, EntityType entityType) { + net.minecraft.entity.Entity entity = ((net.minecraft.entity.EntityType) entityType).create(((ChunkRegion) (Object) this).toServerWorld()); + entity.setPos(location.getX(), location.getY(), location.getZ()); + ((ChunkRegion) (Object) this).spawnEntity(entity); + return (Entity) entity; + } + + @Intrinsic + public long terra$getSeed() { + return seed; + } + + public int terra$getMinHeight() { + return 0; + } + + @Intrinsic + public Object terra$getHandle() { + return this; + } + + public boolean terra$isTerraWorld() { + return terra$getGenerator() instanceof GeneratorWrapper; + } + + public TerraChunkGenerator terra$getTerraGenerator() { + return ((FabricChunkGeneratorWrapper) terra$getGenerator()).getHandle(); + } + + /** + * We need regions delegating to the same world + * to have the same hashcode. This + * minimizes cache misses. + *

+ * This is sort of jank, but shouldn't(tm) + * break any other mods, unless they're doing + * something they really shouldn't, since + * ChunkRegions are not supposed to persist. + */ + @Override + public int hashCode() { + return world.hashCode(); + } + + /** + * Overridden in the same manner as {@link #hashCode()} + * + * @param other Another object + * @return Whether this world is the same as other. + * @see #hashCode() + */ + @Override + public boolean equals(Object other) { + if(!(other instanceof ServerWorldAccess)) return false; + return world.equals(((ServerWorldAccess) other).toServerWorld()); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/world/ServerWorldMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/world/ServerWorldMixin.java new file mode 100644 index 000000000..ee5b7755e --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/implementations/world/ServerWorldMixin.java @@ -0,0 +1,87 @@ +package com.dfsek.terra.fabric.mixin.implementations.world; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.block.Block; +import com.dfsek.terra.api.platform.entity.Entity; +import com.dfsek.terra.api.platform.entity.EntityType; +import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.api.platform.world.generator.ChunkGenerator; +import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; +import com.dfsek.terra.api.world.generation.TerraChunkGenerator; +import com.dfsek.terra.fabric.block.FabricBlock; +import com.dfsek.terra.fabric.generation.FabricChunkGeneratorWrapper; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ServerWorldAccess; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ServerWorld.class) +@Implements(@Interface(iface = World.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ServerWorldMixin { + @Shadow + public abstract long getSeed(); + + public int terra$getMaxHeight() { + return ((ServerWorld) (Object) this).getDimensionHeight(); + } + + public ChunkGenerator terra$getGenerator() { + return (ChunkGenerator) ((ServerWorld) (Object) this).getChunkManager().getChunkGenerator(); + } + + public Chunk terra$getChunkAt(int x, int z) { + return (Chunk) ((ServerWorld) (Object) this).getChunk(x, z); + } + + public Block terra$getBlockAt(int x, int y, int z) { + return new FabricBlock(new BlockPos(x, y, z), ((ServerWorld) (Object) this)); + } + + public Entity terra$spawnEntity(Location location, EntityType entityType) { + net.minecraft.entity.Entity entity = ((net.minecraft.entity.EntityType) entityType).create(((ServerWorld) (Object) this)); + entity.setPos(location.getX(), location.getY(), location.getZ()); + ((ServerWorld) (Object) this).spawnEntity(entity); + return (Entity) entity; + } + + @Intrinsic + public long terra$getSeed() { + return getSeed(); + } + + public int terra$getMinHeight() { + return 0; + } + + @Intrinsic + public Object terra$getHandle() { + return this; + } + + public boolean terra$isTerraWorld() { + return terra$getGenerator() instanceof GeneratorWrapper; + } + + public TerraChunkGenerator terra$getTerraGenerator() { + return ((FabricChunkGeneratorWrapper) terra$getGenerator()).getHandle(); + } + + /** + * Overridden in the same manner as {@link ChunkRegionMixin#hashCode()} + * + * @param other Another object + * @return Whether this world is the same as other. + * @see ChunkRegionMixin#hashCode() + */ + @SuppressWarnings("ConstantConditions") + @Override + public boolean equals(Object other) { + if(!(other instanceof ServerWorldAccess)) return false; + return (ServerWorldAccess) this == (((ServerWorldAccess) other).toServerWorld()); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/init/MinecraftClientMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/init/MinecraftClientMixin.java new file mode 100644 index 000000000..59dba3484 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/init/MinecraftClientMixin.java @@ -0,0 +1,29 @@ +package com.dfsek.terra.fabric.mixin.init; + +import com.dfsek.terra.fabric.TerraFabricPlugin; +import com.dfsek.terra.fabric.generation.TerraGeneratorType; +import com.dfsek.terra.fabric.mixin.access.GeneratorTypeAccessor; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.RunArgs; +import net.minecraft.client.world.GeneratorType; +import net.minecraft.text.LiteralText; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(MinecraftClient.class) +public class MinecraftClientMixin { + @Inject(method = "", at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/util/WindowProvider;createWindow(Lnet/minecraft/client/WindowSettings;Ljava/lang/String;Ljava/lang/String;)Lnet/minecraft/client/util/Window;", // sorta arbitrary position, after mod init, before window opens + shift = At.Shift.BEFORE)) + public void injectConstructor(RunArgs args, CallbackInfo callbackInfo) { + TerraFabricPlugin.getInstance().packInit(); // Load during MinecraftClient construction, after other mods have registered blocks and stuff + TerraFabricPlugin.getInstance().getConfigRegistry().forEach(pack -> { + final GeneratorType generatorType = new TerraGeneratorType(pack); + //noinspection ConstantConditions + ((GeneratorTypeAccessor) generatorType).setTranslationKey(new LiteralText("Terra:" + pack.getTemplate().getID())); + GeneratorTypeAccessor.getValues().add(1, generatorType); + }); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/init/ServerMainMixin.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/init/ServerMainMixin.java new file mode 100644 index 000000000..99cc5cfcb --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/mixin/init/ServerMainMixin.java @@ -0,0 +1,16 @@ +package com.dfsek.terra.fabric.mixin.init; + +import com.dfsek.terra.fabric.TerraFabricPlugin; +import net.minecraft.server.Main; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Main.class) +public class ServerMainMixin { + @Inject(method = "main([Ljava/lang/String;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/registry/DynamicRegistryManager;create()Lnet/minecraft/util/registry/DynamicRegistryManager$Impl;")) + private static void injectConstructor(String[] args, CallbackInfo ci) { + TerraFabricPlugin.getInstance().packInit(); // Load during MinecraftServer construction, after other mods have registered blocks and stuff + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/util/FabricAdapter.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/util/FabricAdapter.java new file mode 100644 index 000000000..8c197d573 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/util/FabricAdapter.java @@ -0,0 +1,224 @@ +package com.dfsek.terra.fabric.util; + +import com.dfsek.terra.api.math.vector.Vector3; +import com.dfsek.terra.api.platform.block.Axis; +import com.dfsek.terra.api.platform.block.BlockFace; +import com.dfsek.terra.api.platform.block.data.Bisected; +import com.dfsek.terra.api.platform.block.data.Slab; +import com.dfsek.terra.api.platform.block.data.Stairs; +import com.dfsek.terra.api.platform.block.state.Container; +import com.dfsek.terra.api.platform.block.state.MobSpawner; +import com.dfsek.terra.api.platform.block.state.Sign; +import com.dfsek.terra.fabric.block.FabricBlockData; +import com.dfsek.terra.fabric.block.data.FabricDirectional; +import com.dfsek.terra.fabric.block.data.FabricMultipleFacing; +import com.dfsek.terra.fabric.block.data.FabricOrientable; +import com.dfsek.terra.fabric.block.data.FabricRotatable; +import com.dfsek.terra.fabric.block.data.FabricSlab; +import com.dfsek.terra.fabric.block.data.FabricStairs; +import com.dfsek.terra.fabric.block.data.FabricWaterlogged; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.LootableContainerBlockEntity; +import net.minecraft.block.entity.MobSpawnerBlockEntity; +import net.minecraft.block.entity.SignBlockEntity; +import net.minecraft.block.enums.BlockHalf; +import net.minecraft.block.enums.SlabType; +import net.minecraft.block.enums.StairShape; +import net.minecraft.state.property.Properties; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.WorldAccess; + +import java.util.Arrays; + +public final class FabricAdapter { + public static BlockPos adapt(Vector3 v) { + return new BlockPos(v.getBlockX(), v.getBlockY(), v.getBlockZ()); + } + + public static Vector3 adapt(BlockPos pos) { + return new Vector3(pos.getX(), pos.getY(), pos.getZ()); + } + + public static FabricBlockData adapt(BlockState state) { + if(state.contains(Properties.STAIR_SHAPE)) return new FabricStairs(state); + + if(state.contains(Properties.SLAB_TYPE)) return new FabricSlab(state); + + if(state.contains(Properties.AXIS)) return new FabricOrientable(state, Properties.AXIS); + if(state.contains(Properties.HORIZONTAL_AXIS)) return new FabricOrientable(state, Properties.HORIZONTAL_AXIS); + + if(state.contains(Properties.ROTATION)) return new FabricRotatable(state); + + if(state.contains(Properties.FACING)) return new FabricDirectional(state, Properties.FACING); + if(state.contains(Properties.HOPPER_FACING)) return new FabricDirectional(state, Properties.HOPPER_FACING); + if(state.contains(Properties.HORIZONTAL_FACING)) return new FabricDirectional(state, Properties.HORIZONTAL_FACING); + + if(state.getProperties().containsAll(Arrays.asList(Properties.NORTH, Properties.SOUTH, Properties.EAST, Properties.WEST))) + return new FabricMultipleFacing(state); + if(state.contains(Properties.WATERLOGGED)) return new FabricWaterlogged(state); + return new FabricBlockData(state); + } + + public static Direction adapt(BlockFace face) { + switch(face) { + case NORTH: + return Direction.NORTH; + case WEST: + return Direction.WEST; + case SOUTH: + return Direction.SOUTH; + case EAST: + return Direction.EAST; + case UP: + return Direction.UP; + case DOWN: + return Direction.DOWN; + default: + throw new IllegalArgumentException("Illegal direction: " + face); + } + } + + + public static com.dfsek.terra.api.platform.block.state.BlockState adapt(com.dfsek.terra.api.platform.block.Block block) { + WorldAccess worldAccess = (WorldAccess) block.getLocation().getWorld(); + + BlockEntity entity = worldAccess.getBlockEntity(adapt(block.getLocation().toVector())); + if(entity instanceof SignBlockEntity) { + return (Sign) entity; + } else if(entity instanceof MobSpawnerBlockEntity) { + return (MobSpawner) entity; + } else if(entity instanceof LootableContainerBlockEntity) { + return (Container) entity; + } + return null; + } + + public static Stairs.Shape adapt(StairShape shape) { + switch(shape) { + case OUTER_RIGHT: + return Stairs.Shape.OUTER_RIGHT; + case INNER_RIGHT: + return Stairs.Shape.INNER_RIGHT; + case OUTER_LEFT: + return Stairs.Shape.OUTER_LEFT; + case INNER_LEFT: + return Stairs.Shape.INNER_LEFT; + case STRAIGHT: + return Stairs.Shape.STRAIGHT; + default: + throw new IllegalStateException(); + } + } + + public static Bisected.Half adapt(BlockHalf half) { + switch(half) { + case BOTTOM: + return Bisected.Half.BOTTOM; + case TOP: + return Bisected.Half.TOP; + default: + throw new IllegalStateException(); + } + } + + public static BlockFace adapt(Direction direction) { + switch(direction) { + case DOWN: + return BlockFace.DOWN; + case UP: + return BlockFace.UP; + case WEST: + return BlockFace.WEST; + case EAST: + return BlockFace.EAST; + case NORTH: + return BlockFace.NORTH; + case SOUTH: + return BlockFace.SOUTH; + default: + throw new IllegalStateException(); + } + } + + public static Slab.Type adapt(SlabType type) { + switch(type) { + case BOTTOM: + return Slab.Type.BOTTOM; + case TOP: + return Slab.Type.TOP; + case DOUBLE: + return Slab.Type.DOUBLE; + default: + throw new IllegalStateException(); + } + } + + public static StairShape adapt(Stairs.Shape shape) { + switch(shape) { + case STRAIGHT: + return StairShape.STRAIGHT; + case INNER_LEFT: + return StairShape.INNER_LEFT; + case OUTER_LEFT: + return StairShape.OUTER_LEFT; + case INNER_RIGHT: + return StairShape.INNER_RIGHT; + case OUTER_RIGHT: + return StairShape.OUTER_RIGHT; + default: + throw new IllegalStateException(); + } + } + + public static BlockHalf adapt(Bisected.Half half) { + switch(half) { + case TOP: + return BlockHalf.TOP; + case BOTTOM: + return BlockHalf.BOTTOM; + default: + throw new IllegalStateException(); + } + } + + public static SlabType adapt(Slab.Type type) { + switch(type) { + case DOUBLE: + return SlabType.DOUBLE; + case TOP: + return SlabType.TOP; + case BOTTOM: + return SlabType.BOTTOM; + default: + throw new IllegalStateException(); + } + } + + public static Axis adapt(Direction.Axis axis) { + switch(axis) { + case X: + return Axis.X; + case Y: + return Axis.Y; + case Z: + return Axis.Z; + default: + throw new IllegalStateException(); + } + } + + public static Direction.Axis adapt(Axis axis) { + switch(axis) { + case Z: + return Direction.Axis.Z; + case Y: + return Direction.Axis.Y; + case X: + return Direction.Axis.X; + default: + throw new IllegalStateException(); + } + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/util/FabricUtil.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/util/FabricUtil.java new file mode 100644 index 000000000..c761cade4 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/util/FabricUtil.java @@ -0,0 +1,117 @@ +package com.dfsek.terra.fabric.util; + +import com.dfsek.terra.api.util.generic.pair.Pair; +import com.dfsek.terra.config.builder.BiomeBuilder; +import com.dfsek.terra.config.pack.ConfigPack; +import com.dfsek.terra.config.templates.BiomeTemplate; +import com.dfsek.terra.fabric.TerraFabricPlugin; +import com.dfsek.terra.fabric.config.PostLoadCompatibilityOptions; +import com.dfsek.terra.fabric.config.PreLoadCompatibilityOptions; +import com.dfsek.terra.fabric.mixin.access.BiomeEffectsAccessor; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.BuiltinRegistries; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeEffects; +import net.minecraft.world.biome.GenerationSettings; +import net.minecraft.world.gen.GenerationStep; +import net.minecraft.world.gen.carver.ConfiguredCarver; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import net.minecraft.world.gen.feature.ConfiguredStructureFeature; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Locale; +import java.util.Map; +import java.util.function.Supplier; + +public final class FabricUtil { + public static String createBiomeID(ConfigPack pack, String biomeID) { + return pack.getTemplate().getID().toLowerCase() + "/" + biomeID.toLowerCase(Locale.ROOT); + } + + /** + * Clones a Vanilla biome and injects Terra data to create a Terra-vanilla biome delegate. + * + * @param fabricAddon The Fabric addon instance. + * @param biome The Terra BiomeBuilder. + * @param pack The ConfigPack this biome belongs to. + * @return The Minecraft delegate biome. + */ + public static Biome createBiome(TerraFabricPlugin.FabricAddon fabricAddon, BiomeBuilder biome, ConfigPack pack) { + BiomeTemplate template = biome.getTemplate(); + Map colors = template.getColors(); + + Biome vanilla = (Biome) (new ArrayList<>(biome.getVanillaBiomes().getContents()).get(0)).getHandle(); + + GenerationSettings.Builder generationSettings = new GenerationSettings.Builder(); + + generationSettings.surfaceBuilder(vanilla.getGenerationSettings().getSurfaceBuilder()); // It needs a surfacebuilder, even though we dont use it. + + generationSettings.feature(GenerationStep.Feature.VEGETAL_DECORATION, TerraFabricPlugin.POPULATOR_CONFIGURED_FEATURE); + + if(pack.getTemplate().vanillaCaves()) { + for(GenerationStep.Carver carver : GenerationStep.Carver.values()) { + for(Supplier> configuredCarverSupplier : vanilla.getGenerationSettings().getCarversForStep(carver)) { + generationSettings.carver(carver, configuredCarverSupplier.get()); + } + } + } + + Pair pair = fabricAddon.getTemplates().get(pack); + PreLoadCompatibilityOptions compatibilityOptions = pair.getLeft(); + PostLoadCompatibilityOptions postLoadCompatibilityOptions = pair.getRight(); + + TerraFabricPlugin.getInstance().getDebugLogger().info("Injecting Vanilla structures and features into Terra biome " + biome.getTemplate().getID()); + + for(Supplier> structureFeature : vanilla.getGenerationSettings().getStructureFeatures()) { + Identifier key = BuiltinRegistries.CONFIGURED_STRUCTURE_FEATURE.getId(structureFeature.get()); + if(!compatibilityOptions.getExcludedBiomeStructures().contains(key) && !postLoadCompatibilityOptions.getExcludedPerBiomeStructures().getOrDefault(biome, Collections.emptySet()).contains(key)) { + generationSettings.structureFeature(structureFeature.get()); + TerraFabricPlugin.getInstance().getDebugLogger().info("Injected structure " + key); + } + } + + if(compatibilityOptions.doBiomeInjection()) { + for(int step = 0; step < vanilla.getGenerationSettings().getFeatures().size(); step++) { + for(Supplier> featureSupplier : vanilla.getGenerationSettings().getFeatures().get(step)) { + Identifier key = BuiltinRegistries.CONFIGURED_FEATURE.getId(featureSupplier.get()); + if(!compatibilityOptions.getExcludedBiomeFeatures().contains(key) && !postLoadCompatibilityOptions.getExcludedPerBiomeFeatures().getOrDefault(biome, Collections.emptySet()).contains(key)) { + generationSettings.feature(step, featureSupplier); + TerraFabricPlugin.getInstance().getDebugLogger().info("Injected feature " + key + " at stage " + step); + } + } + } + } + + BiomeEffectsAccessor accessor = (BiomeEffectsAccessor) vanilla.getEffects(); + BiomeEffects.Builder effects = new BiomeEffects.Builder() + .waterColor(colors.getOrDefault("water", accessor.getWaterColor())) + .waterFogColor(colors.getOrDefault("water-fog", accessor.getWaterFogColor())) + .fogColor(colors.getOrDefault("fog", accessor.getFogColor())) + .skyColor(colors.getOrDefault("sky", accessor.getSkyColor())) + .grassColorModifier(accessor.getGrassColorModifier()); + + if(colors.containsKey("grass")) { + effects.grassColor(colors.get("grass")); + } else { + accessor.getGrassColor().ifPresent(effects::grassColor); + } + if(colors.containsKey("foliage")) { + effects.foliageColor(colors.get("foliage")); + } else { + accessor.getFoliageColor().ifPresent(effects::foliageColor); + } + + return new Biome.Builder() + .precipitation(vanilla.getPrecipitation()) + .category(vanilla.getCategory()) + .depth(vanilla.getDepth()) + .scale(vanilla.getScale()) + .temperature(vanilla.getTemperature()) + .downfall(vanilla.getDownfall()) + .effects(effects.build()) + .spawnSettings(vanilla.getSpawnSettings()) + .generationSettings(generationSettings.build()) + .build(); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/util/WorldEditUtil.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/util/WorldEditUtil.java new file mode 100644 index 000000000..159e4fbc8 --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/util/WorldEditUtil.java @@ -0,0 +1,29 @@ +package com.dfsek.terra.fabric.util; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.entity.Player; +import com.dfsek.terra.api.util.generic.pair.Pair; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.world.World; + +public final class WorldEditUtil { + public static Pair getSelection(Player player) { + WorldEdit worldEdit = WorldEdit.getInstance(); + try { + Region selection = worldEdit.getSessionManager() + .get(com.sk89q.worldedit.fabric.FabricAdapter.adaptPlayer((ServerPlayerEntity) player)) + .getSelection(com.sk89q.worldedit.fabric.FabricAdapter.adapt((World) player.getWorld())); + BlockVector3 min = selection.getMinimumPoint(); + BlockVector3 max = selection.getMaximumPoint(); + Location l1 = new Location(player.getWorld(), min.getBlockX(), min.getBlockY(), min.getBlockZ()); + Location l2 = new Location(player.getWorld(), max.getBlockX(), max.getBlockY(), max.getBlockZ()); + return Pair.of(l1, l2); + } catch(IncompleteRegionException e) { + throw new IllegalStateException("No selection has been made", e); + } + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/FabricAdapter.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/FabricAdapter.java deleted file mode 100644 index e151013f0..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/FabricAdapter.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.dfsek.terra.fabric.world; - -import com.dfsek.terra.api.math.vector.Vector3; -import com.dfsek.terra.api.platform.CommandSender; -import com.dfsek.terra.api.platform.block.BlockFace; -import com.dfsek.terra.api.platform.block.BlockType; -import com.dfsek.terra.api.platform.entity.EntityType; -import com.dfsek.terra.api.platform.inventory.item.Enchantment; -import com.dfsek.terra.fabric.inventory.FabricEnchantment; -import com.dfsek.terra.fabric.inventory.FabricItem; -import com.dfsek.terra.fabric.inventory.FabricItemStack; -import com.dfsek.terra.fabric.world.block.FabricBlockData; -import com.dfsek.terra.fabric.world.block.FabricBlockType; -import com.dfsek.terra.fabric.world.block.data.FabricDirectional; -import com.dfsek.terra.fabric.world.block.data.FabricMultipleFacing; -import com.dfsek.terra.fabric.world.block.data.FabricOrientable; -import com.dfsek.terra.fabric.world.block.data.FabricRotatable; -import com.dfsek.terra.fabric.world.block.data.FabricSlab; -import com.dfsek.terra.fabric.world.block.data.FabricStairs; -import com.dfsek.terra.fabric.world.block.data.FabricWaterlogged; -import com.dfsek.terra.fabric.world.entity.FabricCommandSender; -import com.dfsek.terra.fabric.world.entity.FabricEntityType; -import com.dfsek.terra.fabric.world.entity.FabricPlayer; -import com.dfsek.terra.fabric.world.handles.world.FabricWorldHandle; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.state.property.Properties; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.world.WorldAccess; - -import java.util.Arrays; - -public final class FabricAdapter { - public static BlockPos adapt(Vector3 v) { - return new BlockPos(v.getBlockX(), v.getBlockY(), v.getBlockZ()); - } - - public static Vector3 adapt(BlockPos pos) { - return new Vector3(pos.getX(), pos.getY(), pos.getZ()); - } - - public static FabricBlockData adapt(BlockState state) { - if(state.contains(Properties.STAIR_SHAPE)) return new FabricStairs(state); - - if(state.contains(Properties.SLAB_TYPE)) return new FabricSlab(state); - - if(state.contains(Properties.AXIS)) return new FabricOrientable(state, Properties.AXIS); - if(state.contains(Properties.HORIZONTAL_AXIS)) return new FabricOrientable(state, Properties.HORIZONTAL_AXIS); - - if(state.contains(Properties.ROTATION)) return new FabricRotatable(state); - - if(state.contains(Properties.FACING)) return new FabricDirectional(state, Properties.FACING); - if(state.contains(Properties.HOPPER_FACING)) return new FabricDirectional(state, Properties.HOPPER_FACING); - if(state.contains(Properties.HORIZONTAL_FACING)) return new FabricDirectional(state, Properties.HORIZONTAL_FACING); - - if(state.getProperties().containsAll(Arrays.asList(Properties.NORTH, Properties.SOUTH, Properties.EAST, Properties.WEST))) - return new FabricMultipleFacing(state); - if(state.contains(Properties.WATERLOGGED)) return new FabricWaterlogged(state); - return new FabricBlockData(state); - } - - public static CommandSender adapt(ServerCommandSource serverCommandSource) { - if(serverCommandSource.getEntity() instanceof PlayerEntity) return new FabricPlayer((PlayerEntity) serverCommandSource.getEntity()); - return new FabricCommandSender(serverCommandSource); - } - - public static Direction adapt(BlockFace face) { - switch(face) { - case NORTH: - return Direction.NORTH; - case WEST: - return Direction.WEST; - case SOUTH: - return Direction.SOUTH; - case EAST: - return Direction.EAST; - case UP: - return Direction.UP; - case DOWN: - return Direction.DOWN; - default: - throw new IllegalArgumentException("Illegal direction: " + face); - } - } - - public static BlockType adapt(Block block) { - return new FabricBlockType(block); - } - - public static EntityType adapt(net.minecraft.entity.EntityType entityType) { - return new FabricEntityType(entityType); - } - - public static net.minecraft.entity.EntityType adapt(EntityType entityType) { - return ((FabricEntityType) entityType).getHandle(); - } - - public static ItemStack adapt(com.dfsek.terra.api.platform.inventory.ItemStack itemStack) { - return ((FabricItemStack) itemStack).getHandle(); - } - - public static com.dfsek.terra.api.platform.inventory.ItemStack adapt(ItemStack itemStack) { - return new FabricItemStack(itemStack); - } - - public static com.dfsek.terra.api.platform.inventory.Item adapt(Item item) { - return new FabricItem(item); - } - - public static Enchantment adapt(net.minecraft.enchantment.Enchantment enchantment) { - return new FabricEnchantment(enchantment); - } - - public static net.minecraft.enchantment.Enchantment adapt(Enchantment enchantment) { - return ((FabricEnchantment) enchantment).getHandle(); - } - - public WorldAccess adapt(FabricWorldHandle worldHandle) { - return worldHandle.getWorld(); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/FabricBiome.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/FabricBiome.java deleted file mode 100644 index 99dab1c2c..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/FabricBiome.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.dfsek.terra.fabric.world; - -import com.dfsek.terra.api.platform.world.Biome; - -public class FabricBiome implements Biome { - private final net.minecraft.world.biome.Biome delegate; - - public FabricBiome(net.minecraft.world.biome.Biome delegate) { - this.delegate = delegate; - } - - - @Override - public net.minecraft.world.biome.Biome getHandle() { - return delegate; - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/FabricTree.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/FabricTree.java deleted file mode 100644 index 3a9567a15..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/FabricTree.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.dfsek.terra.fabric.world; - -import com.dfsek.terra.api.math.vector.Location; -import com.dfsek.terra.api.platform.world.Tree; -import com.dfsek.terra.api.util.collections.MaterialSet; -import com.dfsek.terra.fabric.TerraFabricPlugin; -import com.dfsek.terra.fabric.world.generator.FabricChunkGenerator; -import com.dfsek.terra.fabric.world.handles.world.FabricWorldAccess; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.StructureWorldAccess; -import net.minecraft.world.gen.chunk.ChunkGenerator; -import net.minecraft.world.gen.feature.ConfiguredFeature; - -import java.util.Random; - -public class FabricTree implements Tree { - private final ConfiguredFeature delegate; - - public FabricTree(ConfiguredFeature delegate) { - this.delegate = delegate; - } - - @Override - public boolean plant(Location l, Random r) { - FabricWorldAccess fabricWorldAccess = ((FabricWorldAccess) l.getWorld()); - ChunkGenerator generatorWrapper = ((FabricChunkGenerator) fabricWorldAccess.getGenerator()).getHandle(); - return delegate.generate((StructureWorldAccess) fabricWorldAccess.getHandle(), generatorWrapper, r, new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ())); - } - - @Override - public MaterialSet getSpawnable() { - return MaterialSet.get(TerraFabricPlugin.getInstance().getWorldHandle().createBlockData("minecraft:grass_block"), - TerraFabricPlugin.getInstance().getWorldHandle().createBlockData("minecraft:podzol"), - TerraFabricPlugin.getInstance().getWorldHandle().createBlockData("minecraft:mycelium")); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/FabricBlockType.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/FabricBlockType.java deleted file mode 100644 index 26427bb2f..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/FabricBlockType.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.dfsek.terra.fabric.world.block; - -import com.dfsek.terra.api.platform.block.BlockData; -import com.dfsek.terra.api.platform.block.BlockType; -import com.dfsek.terra.fabric.world.FabricAdapter; -import net.minecraft.block.Block; -import net.minecraft.block.Blocks; - -public class FabricBlockType implements BlockType { - private final Block delegate; - - public FabricBlockType(Block delegate) { - this.delegate = delegate; - } - - @Override - public Block getHandle() { - return delegate; - } - - @Override - public BlockData getDefaultData() { - return FabricAdapter.adapt(delegate.getDefaultState()); - } - - @Override - public boolean isSolid() { - return delegate.getDefaultState().isOpaque(); - } - - @Override - public boolean isWater() { - return delegate == Blocks.WATER; - } - - @Override - public int hashCode() { - return delegate.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if(!(obj instanceof FabricBlockType)) return false; - return ((FabricBlockType) obj).delegate == delegate; - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricBlockState.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricBlockState.java deleted file mode 100644 index 2f7a68889..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricBlockState.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.dfsek.terra.fabric.world.block.state; - -import com.dfsek.terra.api.platform.block.Block; -import com.dfsek.terra.api.platform.block.BlockData; -import com.dfsek.terra.api.platform.block.state.BlockState; -import com.dfsek.terra.fabric.world.FabricAdapter; -import com.dfsek.terra.fabric.world.block.FabricBlock; -import com.dfsek.terra.fabric.world.handles.world.FabricWorldHandle; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.block.entity.LootableContainerBlockEntity; -import net.minecraft.block.entity.MobSpawnerBlockEntity; -import net.minecraft.block.entity.SignBlockEntity; -import net.minecraft.world.WorldAccess; - -public class FabricBlockState implements BlockState { - protected final BlockEntity blockEntity; - private final WorldAccess worldAccess; - - public FabricBlockState(BlockEntity blockEntity, WorldAccess worldAccess) { - this.blockEntity = blockEntity; - this.worldAccess = worldAccess; - } - - public static FabricBlockState newInstance(Block block) { - WorldAccess worldAccess = ((FabricWorldHandle) block.getLocation().getWorld()).getWorld(); - - BlockEntity entity = worldAccess.getBlockEntity(FabricAdapter.adapt(block.getLocation().toVector())); - if(entity instanceof SignBlockEntity) { - return new FabricSign((SignBlockEntity) entity, worldAccess); - } else if(entity instanceof MobSpawnerBlockEntity) { - return new FabricMobSpawner((MobSpawnerBlockEntity) entity, worldAccess); - } else if(entity instanceof LootableContainerBlockEntity) { - return new FabricContainer((LootableContainerBlockEntity) entity, worldAccess); - } - return null; - } - - @Override - public BlockEntity getHandle() { - return blockEntity; - } - - @Override - public Block getBlock() { - return new FabricBlock(blockEntity.getPos(), blockEntity.getWorld()); - } - - @Override - public int getX() { - return blockEntity.getPos().getX(); - } - - @Override - public int getY() { - return blockEntity.getPos().getY(); - } - - @Override - public int getZ() { - return blockEntity.getPos().getZ(); - } - - @Override - public BlockData getBlockData() { - return FabricAdapter.adapt(blockEntity.getCachedState()); - } - - @Override - public boolean update(boolean applyPhysics) { - worldAccess.getChunk(blockEntity.getPos()).setBlockEntity(blockEntity); - return true; - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricContainer.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricContainer.java deleted file mode 100644 index 743c06d83..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricContainer.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.dfsek.terra.fabric.world.block.state; - -import com.dfsek.terra.api.platform.block.state.Container; -import com.dfsek.terra.api.platform.inventory.Inventory; -import com.dfsek.terra.fabric.inventory.FabricInventory; -import net.minecraft.block.entity.LootableContainerBlockEntity; -import net.minecraft.world.WorldAccess; - -public class FabricContainer extends FabricBlockState implements Container { - public FabricContainer(LootableContainerBlockEntity blockEntity, WorldAccess worldAccess) { - super(blockEntity, worldAccess); - } - - @Override - public Inventory getInventory() { - return new FabricInventory(((LootableContainerBlockEntity) blockEntity)); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricMobSpawner.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricMobSpawner.java deleted file mode 100644 index dd0012e37..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricMobSpawner.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.dfsek.terra.fabric.world.block.state; - -import com.dfsek.terra.api.platform.block.state.MobSpawner; -import com.dfsek.terra.api.platform.block.state.SerialState; -import com.dfsek.terra.api.platform.entity.EntityType; -import com.dfsek.terra.fabric.TerraFabricPlugin; -import com.dfsek.terra.fabric.world.FabricAdapter; -import net.minecraft.block.entity.MobSpawnerBlockEntity; -import net.minecraft.util.registry.Registry; -import net.minecraft.world.WorldAccess; -import org.jetbrains.annotations.NotNull; - -public class FabricMobSpawner extends FabricBlockState implements MobSpawner { // TODO: finish implementation / refactor API because bukkit doesnt expose most of the stuff spawners can do - - - public FabricMobSpawner(MobSpawnerBlockEntity blockEntity, WorldAccess worldAccess) { - super(blockEntity, worldAccess); - } - - @Override - public EntityType getSpawnedType() { - return FabricAdapter.adapt(Registry.ENTITY_TYPE.get(((MobSpawnerBlockEntity) blockEntity).getLogic().getEntityId(blockEntity.getWorld(), blockEntity.getPos()))); - } - - @Override - public void setSpawnedType(@NotNull EntityType creatureType) { - ((MobSpawnerBlockEntity) blockEntity).getLogic().setEntityId(FabricAdapter.adapt(creatureType)); - } - - @Override - public int getDelay() { - return 0; - } - - @Override - public void setDelay(int delay) { - - } - - @Override - public int getMinSpawnDelay() { - return 0; - } - - @Override - public void setMinSpawnDelay(int delay) { - - } - - @Override - public int getMaxSpawnDelay() { - return 0; - } - - @Override - public void setMaxSpawnDelay(int delay) { - - } - - @Override - public int getSpawnCount() { - return 0; - } - - @Override - public void setSpawnCount(int spawnCount) { - - } - - @Override - public int getMaxNearbyEntities() { - return 0; - } - - @Override - public void setMaxNearbyEntities(int maxNearbyEntities) { - - } - - @Override - public int getRequiredPlayerRange() { - return 0; - } - - @Override - public void setRequiredPlayerRange(int requiredPlayerRange) { - - } - - @Override - public int getSpawnRange() { - return 0; - } - - @Override - public void setSpawnRange(int spawnRange) { - - } - - @Override - public void applyState(String state) { - SerialState.parse(state).forEach((k, v) -> { - switch(k) { - case "type": - setSpawnedType(TerraFabricPlugin.getInstance().getWorldHandle().getEntity(v)); - return; - case "delay": - setDelay(Integer.parseInt(v)); - return; - case "min_delay": - setMinSpawnDelay(Integer.parseInt(v)); - return; - case "max_delay": - setMaxSpawnDelay(Integer.parseInt(v)); - return; - case "spawn_count": - setSpawnCount(Integer.parseInt(v)); - return; - case "spawn_range": - setSpawnRange(Integer.parseInt(v)); - return; - case "max_nearby": - setMaxNearbyEntities(Integer.parseInt(v)); - return; - case "required_player_range": - setRequiredPlayerRange(Integer.parseInt(v)); - return; - default: - throw new IllegalArgumentException("Invalid property: " + k); - } - }); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricSign.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricSign.java deleted file mode 100644 index 367b4a511..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricSign.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.dfsek.terra.fabric.world.block.state; - -import com.dfsek.terra.api.platform.block.state.SerialState; -import com.dfsek.terra.api.platform.block.state.Sign; -import net.minecraft.block.entity.SignBlockEntity; -import net.minecraft.text.LiteralText; -import net.minecraft.world.WorldAccess; -import org.jetbrains.annotations.NotNull; - -public class FabricSign extends FabricBlockState implements Sign { - public FabricSign(SignBlockEntity blockEntity, WorldAccess worldAccess) { - super(blockEntity, worldAccess); - } - - @Override - public @NotNull String[] getLines() { - SignBlockEntity sign = (SignBlockEntity) blockEntity; - - return new String[] { - sign.getTextOnRow(0, false).asString(), - sign.getTextOnRow(1, false).asString(), - sign.getTextOnRow(2, false).asString(), - sign.getTextOnRow(3, false).asString() - }; - } - - @Override - public @NotNull String getLine(int index) throws IndexOutOfBoundsException { - return ((SignBlockEntity) blockEntity).getTextOnRow(index, false).asString(); - } - - @Override - public void setLine(int index, @NotNull String line) throws IndexOutOfBoundsException { - ((SignBlockEntity) blockEntity).setTextOnRow(index, new LiteralText(line)); - } - - @Override - public void applyState(String state) { - SerialState.parse(state).forEach((k, v) -> { - if(!k.startsWith("text")) throw new IllegalArgumentException("Invalid property: " + k); - setLine(Integer.parseInt(k.substring(4)), v); - }); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/entity/FabricCommandSender.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/entity/FabricCommandSender.java deleted file mode 100644 index e7f946008..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/entity/FabricCommandSender.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.dfsek.terra.fabric.world.entity; - -import com.dfsek.terra.api.platform.CommandSender; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.text.LiteralText; - -public class FabricCommandSender implements CommandSender { - private final ServerCommandSource delegate; - - public FabricCommandSender(ServerCommandSource delegate) { - this.delegate = delegate; - } - - @Override - public void sendMessage(String message) { - delegate.sendFeedback(new LiteralText(message), true); - } - - @Override - public Object getHandle() { - return delegate; - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/entity/FabricEntity.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/entity/FabricEntity.java deleted file mode 100644 index 65c26849d..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/entity/FabricEntity.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.dfsek.terra.fabric.world.entity; - -import com.dfsek.terra.api.math.vector.Location; -import com.dfsek.terra.api.platform.entity.Entity; -import com.dfsek.terra.api.platform.world.World; -import com.dfsek.terra.fabric.world.FabricAdapter; -import com.dfsek.terra.fabric.world.handles.world.FabricWorldAccess; -import com.dfsek.terra.fabric.world.handles.world.FabricWorldHandle; -import net.minecraft.server.world.ServerWorld; - -public class FabricEntity implements Entity { - private final net.minecraft.entity.Entity delegate; - - public FabricEntity(net.minecraft.entity.Entity delegate) { - this.delegate = delegate; - } - - @Override - public void sendMessage(String message) { - - } - - @Override - public Object getHandle() { - return null; - } - - @Override - public Location getLocation() { - return new Location(new FabricWorldAccess(delegate.world), FabricAdapter.adapt(delegate.getBlockPos())); - } - - @Override - public void setLocation(Location location) { - delegate.teleport(location.getX(), location.getY(), location.getZ()); - delegate.moveToWorld((ServerWorld) ((FabricWorldHandle) location).getWorld()); - } - - @Override - public World getWorld() { - return new FabricWorldAccess(delegate.world); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/entity/FabricEntityType.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/entity/FabricEntityType.java deleted file mode 100644 index 31d6aceb1..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/entity/FabricEntityType.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.dfsek.terra.fabric.world.entity; - -import com.dfsek.terra.api.platform.entity.EntityType; - -public class FabricEntityType implements EntityType { - private final net.minecraft.entity.EntityType type; - - public FabricEntityType(net.minecraft.entity.EntityType type) { - this.type = type; - } - - @Override - public net.minecraft.entity.EntityType getHandle() { - return type; - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/entity/FabricPlayer.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/entity/FabricPlayer.java deleted file mode 100644 index 143ad024c..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/entity/FabricPlayer.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.dfsek.terra.fabric.world.entity; - -import com.dfsek.terra.api.math.vector.Location; -import com.dfsek.terra.api.platform.entity.Player; -import com.dfsek.terra.api.platform.world.World; -import com.dfsek.terra.fabric.world.FabricAdapter; -import com.dfsek.terra.fabric.world.handles.world.FabricWorldAccess; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.text.LiteralText; - -public class FabricPlayer implements Player { - private final PlayerEntity delegate; - - public FabricPlayer(PlayerEntity delegate) { - this.delegate = delegate; - } - - @Override - public void sendMessage(String message) { - delegate.sendMessage(new LiteralText(message), false); - } - - @Override - public Object getHandle() { - return delegate; - } - - @Override - public Location getLocation() { - return FabricAdapter.adapt(delegate.getBlockPos()).toLocation(new FabricWorldAccess(delegate.world)); - } - - @Override - public World getWorld() { - return new FabricWorldAccess(delegate.world); - } - - @Override - public void setLocation(Location location) { - delegate.teleport(location.getX(), location.getY(), location.getZ()); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/features/PopulatorFeature.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/features/PopulatorFeature.java deleted file mode 100644 index 886b0f99c..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/features/PopulatorFeature.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.dfsek.terra.fabric.world.features; - -import com.dfsek.terra.fabric.world.generator.FabricChunkGenerator; -import com.dfsek.terra.fabric.world.generator.FabricChunkGeneratorWrapper; -import com.dfsek.terra.fabric.world.handles.FabricWorld; -import com.dfsek.terra.fabric.world.handles.chunk.FabricChunkWorldAccess; -import com.mojang.serialization.Codec; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.StructureWorldAccess; -import net.minecraft.world.gen.chunk.ChunkGenerator; -import net.minecraft.world.gen.feature.DefaultFeatureConfig; -import net.minecraft.world.gen.feature.Feature; -import net.minecraft.world.gen.feature.util.FeatureContext; - -/** - * Feature wrapper for Terra populator - */ -public class PopulatorFeature extends Feature { - public PopulatorFeature(Codec codec) { - super(codec); - } - - @Override - public boolean generate(FeatureContext context) { - ChunkGenerator chunkGenerator = context.getGenerator(); - StructureWorldAccess world = context.getWorld(); - BlockPos pos = context.getOrigin(); - FabricChunkGeneratorWrapper gen = (FabricChunkGeneratorWrapper) chunkGenerator; - FabricChunkWorldAccess chunk = new FabricChunkWorldAccess(world, pos.getX() >> 4, pos.getZ() >> 4); - FabricWorld world1 = new FabricWorld(world.toServerWorld(), new FabricChunkGenerator(chunkGenerator)); - gen.getOrePopulator().populate(world1, chunk); - gen.getTreePopulator().populate(world1, chunk); - gen.getFloraPopulator().populate(world1, chunk); - return true; - - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/generator/FabricChunkData.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/generator/FabricChunkData.java deleted file mode 100644 index 24ea33e9b..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/generator/FabricChunkData.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.dfsek.terra.fabric.world.generator; - -import com.dfsek.terra.api.platform.block.BlockData; -import com.dfsek.terra.api.platform.world.generator.ChunkData; -import com.dfsek.terra.fabric.world.block.FabricBlockData; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.chunk.Chunk; -import org.jetbrains.annotations.NotNull; - -public class FabricChunkData implements ChunkData { - private final Chunk handle; - - public FabricChunkData(Chunk handle) { - this.handle = handle; - } - - @Override - public Chunk getHandle() { - return handle; - } - - @Override - public int getMaxHeight() { - return handle.getHeight(); - } - - @Override - public void setBlock(int x, int y, int z, @NotNull BlockData blockData) { - handle.setBlockState(new BlockPos(x, y, z), ((FabricBlockData) blockData).getHandle(), false); - } - - @Override - public @NotNull BlockData getBlockData(int x, int y, int z) { - return new FabricBlockData(handle.getBlockState(new BlockPos(x, y, z))); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/generator/FabricChunkGenerator.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/generator/FabricChunkGenerator.java deleted file mode 100644 index ae7526326..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/generator/FabricChunkGenerator.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.dfsek.terra.fabric.world.generator; - -import com.dfsek.terra.api.platform.world.generator.ChunkGenerator; - -public class FabricChunkGenerator implements ChunkGenerator { - private final net.minecraft.world.gen.chunk.ChunkGenerator delegate; - - public FabricChunkGenerator(net.minecraft.world.gen.chunk.ChunkGenerator delegate) { - this.delegate = delegate; - } - - @Override - public net.minecraft.world.gen.chunk.ChunkGenerator getHandle() { - return delegate; - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/generator/FabricChunkGeneratorWrapper.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/generator/FabricChunkGeneratorWrapper.java deleted file mode 100644 index cb0eb1092..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/generator/FabricChunkGeneratorWrapper.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.dfsek.terra.fabric.world.generator; - -import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; -import com.dfsek.terra.api.util.FastRandom; -import com.dfsek.terra.api.world.generation.TerraChunkGenerator; -import com.dfsek.terra.config.pack.ConfigPack; -import com.dfsek.terra.fabric.TerraFabricPlugin; -import com.dfsek.terra.fabric.mixin.StructureAccessorAccessor; -import com.dfsek.terra.fabric.world.TerraBiomeSource; -import com.dfsek.terra.fabric.world.handles.chunk.FabricChunk; -import com.dfsek.terra.fabric.world.handles.world.FabricSeededWorldAccess; -import com.dfsek.terra.world.generation.generators.DefaultChunkGenerator3D; -import com.dfsek.terra.world.population.CavePopulator; -import com.dfsek.terra.world.population.FloraPopulator; -import com.dfsek.terra.world.population.OrePopulator; -import com.dfsek.terra.world.population.StructurePopulator; -import com.dfsek.terra.world.population.TreePopulator; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; -import net.minecraft.structure.StructureManager; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.util.registry.DynamicRegistryManager; -import net.minecraft.world.ChunkRegion; -import net.minecraft.world.HeightLimitView; -import net.minecraft.world.Heightmap; -import net.minecraft.world.biome.source.BiomeAccess; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.gen.GenerationStep; -import net.minecraft.world.gen.StructureAccessor; -import net.minecraft.world.gen.chunk.ChunkGenerator; -import net.minecraft.world.gen.chunk.StructuresConfig; -import net.minecraft.world.gen.chunk.VerticalBlockSample; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; - -public class FabricChunkGeneratorWrapper extends ChunkGenerator implements GeneratorWrapper { - private final long seed; - private final DefaultChunkGenerator3D delegate; - private final TerraBiomeSource biomeSource; - public static final Codec PACK_CODEC = (RecordCodecBuilder.create(config -> config.group( - Codec.STRING.fieldOf("pack").forGetter(pack -> pack.getTemplate().getID()) - ).apply(config, config.stable(TerraFabricPlugin.getInstance().getConfigRegistry()::get)))); - public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - TerraBiomeSource.CODEC.fieldOf("biome_source").forGetter(generator -> generator.biomeSource), - Codec.LONG.fieldOf("seed").stable().forGetter(generator -> generator.seed), - PACK_CODEC.fieldOf("pack").stable().forGetter(generator -> generator.pack)) - .apply(instance, instance.stable(FabricChunkGeneratorWrapper::new))); - private final ConfigPack pack; - - public ConfigPack getPack() { - return pack; - } - - private final FloraPopulator floraPopulator = new FloraPopulator(TerraFabricPlugin.getInstance()); - private final OrePopulator orePopulator = new OrePopulator(TerraFabricPlugin.getInstance()); - private final TreePopulator treePopulator = new TreePopulator(TerraFabricPlugin.getInstance()); - private final StructurePopulator structurePopulator = new StructurePopulator(TerraFabricPlugin.getInstance()); - private final CavePopulator cavePopulator = new CavePopulator(TerraFabricPlugin.getInstance()); - - public TreePopulator getTreePopulator() { - return treePopulator; - } - - public OrePopulator getOrePopulator() { - return orePopulator; - } - - public FloraPopulator getFloraPopulator() { - return floraPopulator; - } - - public FabricChunkGeneratorWrapper(TerraBiomeSource biomeSource, long seed, ConfigPack configPack) { - super(biomeSource, new StructuresConfig(false)); - this.pack = configPack; - - this.delegate = new DefaultChunkGenerator3D(pack, TerraFabricPlugin.getInstance()); - delegate.getMain().logger().info("Loading world with config pack " + pack.getTemplate().getID()); - this.biomeSource = biomeSource; - - this.seed = seed; - } - - - @Override - protected Codec getCodec() { - return CODEC; - } - - @Override - public ChunkGenerator withSeed(long seed) { - return new FabricChunkGeneratorWrapper((TerraBiomeSource) this.biomeSource.withSeed(seed), seed, pack); - } - - @Override - public void buildSurface(ChunkRegion region, Chunk chunk) { - - } - - @Override - public void generateFeatures(ChunkRegion region, StructureAccessor accessor) { - super.generateFeatures(region, accessor); - } - - @Override - public void carve(long seed, BiomeAccess access, Chunk chunk, GenerationStep.Carver carver) { - // No caves - } - - @Override - public void setStructureStarts(DynamicRegistryManager dynamicRegistryManager, StructureAccessor structureAccessor, Chunk chunk, StructureManager structureManager, long worldSeed) { - - } - - @Override - public CompletableFuture populateNoise(Executor executor, StructureAccessor accessor, Chunk chunk) { - return CompletableFuture.supplyAsync(() -> { - FabricSeededWorldAccess worldAccess = new FabricSeededWorldAccess(((StructureAccessorAccessor) accessor).getWorld(), seed, this); - com.dfsek.terra.api.platform.world.Chunk c = new FabricChunk(worldAccess, chunk); - - delegate.generateChunkData(worldAccess, new FastRandom(), chunk.getPos().x, chunk.getPos().z, new FabricChunkData(chunk)); - cavePopulator.populate(worldAccess, c); - structurePopulator.populate(worldAccess, c); - return chunk; - }, executor); - } - - @Override - public int getHeight(int x, int z, Heightmap.Type heightmap, HeightLimitView world) { - return 0; - } - - @Override - public VerticalBlockSample getColumnSample(int x, int z, HeightLimitView world) { - int height = 64; // TODO: implementation - BlockState[] array = new BlockState[256]; - for(int y = 255; y >= 0; y--) { - if(y > height) { - if(y > getSeaLevel()) { - array[y] = Blocks.AIR.getDefaultState(); - } else { - array[y] = Blocks.WATER.getDefaultState(); - } - } else { - array[y] = Blocks.STONE.getDefaultState(); - } - } - - return new VerticalBlockSample(world.getBottomY(), array); - } - - @Override - public boolean isStrongholdStartingChunk(ChunkPos chunkPos) { - return false; - } - - - @Override - public TerraChunkGenerator getHandle() { - return delegate; - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/FabricWorld.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/FabricWorld.java deleted file mode 100644 index 272cd6760..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/FabricWorld.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.dfsek.terra.fabric.world.handles; - -import com.dfsek.terra.api.math.vector.Location; -import com.dfsek.terra.api.platform.block.Block; -import com.dfsek.terra.api.platform.entity.Entity; -import com.dfsek.terra.api.platform.entity.EntityType; -import com.dfsek.terra.api.platform.world.Chunk; -import com.dfsek.terra.api.platform.world.World; -import com.dfsek.terra.api.platform.world.generator.ChunkGenerator; -import com.dfsek.terra.fabric.world.FabricAdapter; -import com.dfsek.terra.fabric.world.block.FabricBlock; -import com.dfsek.terra.fabric.world.entity.FabricEntity; -import com.dfsek.terra.fabric.world.handles.chunk.FabricChunk; -import com.dfsek.terra.fabric.world.handles.world.FabricWorldHandle; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.ServerWorldAccess; -import net.minecraft.world.WorldAccess; - -import java.io.File; -import java.util.UUID; - -public class FabricWorld implements World, FabricWorldHandle { - - private final Handle delegate; - - public FabricWorld(ServerWorld world, ChunkGenerator generator) { - this.delegate = new Handle(world, generator); - } - - @Override - public long getSeed() { - return delegate.world.getSeed(); - } - - @Override - public int getMaxHeight() { - return delegate.world.getHeight() + delegate.world.getBottomY(); - } - - @Override - public ChunkGenerator getGenerator() { - return delegate.generator; - } - - @Override - public String getName() { - return delegate.world.worldProperties.getLevelName(); - } - - @Override - public UUID getUID() { - return null; - } - - @Override - public boolean isChunkGenerated(int x, int z) { - return false; - } - - @Override - public Chunk getChunkAt(int x, int z) { - return new FabricChunk(this, delegate.world.getChunk(x, z)); - } - - @Override - public File getWorldFolder() { - return null; - } - - @Override - public Block getBlockAt(int x, int y, int z) { - BlockPos pos = new BlockPos(x, y, z); - return new FabricBlock(pos, delegate.world); - } - - @Override - public int hashCode() { - return ((ServerWorldAccess) delegate.world).toServerWorld().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if(!(obj instanceof FabricWorld)) return false; - return ((ServerWorldAccess) ((FabricWorld) obj).delegate.world).toServerWorld().equals(((ServerWorldAccess) delegate.world).toServerWorld()); - } - - @Override - public Entity spawnEntity(Location location, EntityType entityType) { - net.minecraft.entity.Entity entity = FabricAdapter.adapt(entityType).create(delegate.world); - entity.setPos(location.getX(), location.getY(), location.getZ()); - delegate.world.spawnEntity(entity); - return new FabricEntity(entity); - } - - @Override - public int getMinHeight() { - return delegate.world.getBottomY(); - } - - @Override - public Handle getHandle() { - return null; - } - - @Override - public WorldAccess getWorld() { - return delegate.getWorld(); - } - - public static final class Handle { - private final ServerWorld world; - private final ChunkGenerator generator; - - private Handle(ServerWorld world, ChunkGenerator generator) { - this.world = world; - this.generator = generator; - } - - public ChunkGenerator getGenerator() { - return generator; - } - - public ServerWorld getWorld() { - return world; - } - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/chunk/FabricChunk.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/chunk/FabricChunk.java deleted file mode 100644 index bc78f34c8..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/chunk/FabricChunk.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.dfsek.terra.fabric.world.handles.chunk; - -import com.dfsek.terra.api.platform.block.Block; -import com.dfsek.terra.api.platform.block.BlockData; -import com.dfsek.terra.api.platform.world.Chunk; -import com.dfsek.terra.api.platform.world.World; -import com.dfsek.terra.fabric.world.block.FabricBlock; -import com.dfsek.terra.fabric.world.block.FabricBlockData; -import com.dfsek.terra.fabric.world.handles.world.FabricWorldHandle; -import net.minecraft.util.math.BlockPos; -import org.jetbrains.annotations.NotNull; - -public class FabricChunk implements Chunk { - private final net.minecraft.world.chunk.Chunk chunk; - private final FabricWorldHandle worldHandle; - - public FabricChunk(FabricWorldHandle worldHandle, net.minecraft.world.chunk.Chunk chunk) { - this.worldHandle = worldHandle; - this.chunk = chunk; - } - - @Override - public int getX() { - return chunk.getPos().x; - } - - @Override - public int getZ() { - return chunk.getPos().z; - } - - @Override - public World getWorld() { - return worldHandle; - } - - @Override - public Block getBlock(int x, int y, int z) { - BlockPos pos = new BlockPos(x + (chunk.getPos().x << 4), y, z + (chunk.getPos().z << 4)); - return new FabricBlock(pos, worldHandle.getWorld()); - } - - @Override - public net.minecraft.world.chunk.Chunk getHandle() { - return chunk; - } - - @Override - public void setBlock(int x, int y, int z, @NotNull BlockData blockData) { - chunk.setBlockState(new BlockPos(x, y, z), ((FabricBlockData) blockData).getHandle(), false); - } - - @Override - public @NotNull BlockData getBlockData(int x, int y, int z) { - return getBlock(x, y, z).getBlockData(); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/chunk/FabricChunkWorldAccess.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/chunk/FabricChunkWorldAccess.java deleted file mode 100644 index 3eb0662e7..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/chunk/FabricChunkWorldAccess.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.dfsek.terra.fabric.world.handles.chunk; - -import com.dfsek.terra.api.platform.block.Block; -import com.dfsek.terra.api.platform.block.BlockData; -import com.dfsek.terra.api.platform.world.Chunk; -import com.dfsek.terra.api.platform.world.World; -import com.dfsek.terra.fabric.world.block.FabricBlock; -import com.dfsek.terra.fabric.world.block.FabricBlockData; -import com.dfsek.terra.fabric.world.handles.world.FabricWorldAccess; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.WorldAccess; -import org.jetbrains.annotations.NotNull; - -public class FabricChunkWorldAccess implements Chunk { - private final WorldAccess chunkRegion; - private final int x; - private final int z; - - public FabricChunkWorldAccess(WorldAccess chunkRegion, int x, int z) { - this.chunkRegion = chunkRegion; - this.x = x << 4; - this.z = z << 4; - } - - @Override - public int getX() { - return x >> 4; - } - - @Override - public int getZ() { - return z >> 4; - } - - @Override - public World getWorld() { - return new FabricWorldAccess(chunkRegion); - } - - @Override - public Block getBlock(int x, int y, int z) { - BlockPos pos = new BlockPos(x + this.x, y, z + this.z); - return new FabricBlock(pos, chunkRegion); - } - - @Override - public WorldAccess getHandle() { - return chunkRegion; - } - - @Override - public void setBlock(int x, int y, int z, @NotNull BlockData blockData) { - chunkRegion.setBlockState(new BlockPos(x + this.x, y, z + this.z), ((FabricBlockData) blockData).getHandle(), 0); - } - - @Override - public @NotNull BlockData getBlockData(int x, int y, int z) { - return getBlock(x, y, z).getBlockData(); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/world/FabricSeededWorldAccess.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/world/FabricSeededWorldAccess.java deleted file mode 100644 index 2adf9fe69..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/world/FabricSeededWorldAccess.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.dfsek.terra.fabric.world.handles.world; - -import com.dfsek.terra.api.math.vector.Location; -import com.dfsek.terra.api.platform.block.Block; -import com.dfsek.terra.api.platform.entity.Entity; -import com.dfsek.terra.api.platform.entity.EntityType; -import com.dfsek.terra.api.platform.world.Chunk; -import com.dfsek.terra.api.platform.world.World; -import com.dfsek.terra.api.platform.world.generator.ChunkGenerator; -import com.dfsek.terra.fabric.world.FabricAdapter; -import com.dfsek.terra.fabric.world.block.FabricBlock; -import com.dfsek.terra.fabric.world.entity.FabricEntity; -import com.dfsek.terra.fabric.world.generator.FabricChunkGenerator; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.ServerWorldAccess; -import net.minecraft.world.WorldAccess; - -import java.io.File; -import java.util.UUID; - -public class FabricSeededWorldAccess implements World, FabricWorldHandle { - - private final Handle handle; - - public FabricSeededWorldAccess(WorldAccess access, long seed, net.minecraft.world.gen.chunk.ChunkGenerator generator) { - this.handle = new Handle(access, seed, generator); - } - - @Override - public long getSeed() { - return handle.getSeed(); - } - - @Override - public int getMaxHeight() { - return handle.worldAccess.getHeight() + handle.worldAccess.getBottomY(); - } - - @Override - public ChunkGenerator getGenerator() { - return new FabricChunkGenerator(handle.getGenerator()); - } - - @Override - public String getName() { - return handle.toString(); // TODO: implementation - } - - @Override - public UUID getUID() { - return null; // TODO: implementation - } - - @Override - public boolean isChunkGenerated(int x, int z) { - return false; - } - - @Override - public Chunk getChunkAt(int x, int z) { - return null; - } - - @Override - public File getWorldFolder() { - return null; - } - - @Override - public Block getBlockAt(int x, int y, int z) { - BlockPos pos = new BlockPos(x, y, z); - return new FabricBlock(pos, handle.worldAccess); - } - - @Override - public Entity spawnEntity(Location location, EntityType entityType) { - net.minecraft.entity.Entity entity = FabricAdapter.adapt(entityType).create((ServerWorld) handle.worldAccess); - entity.setPos(location.getX(), location.getY(), location.getZ()); - handle.worldAccess.spawnEntity(entity); - return new FabricEntity(entity); - } - - @Override - public int getMinHeight() { - return handle.worldAccess.getBottomY(); - } - - @Override - public int hashCode() { - return ((ServerWorldAccess) handle.worldAccess).toServerWorld().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if(!(obj instanceof FabricSeededWorldAccess)) return false; - return ((ServerWorldAccess) ((FabricSeededWorldAccess) obj).handle.worldAccess).toServerWorld().equals(((ServerWorldAccess) handle.worldAccess).toServerWorld()); - } - - @Override - public Handle getHandle() { - return handle; - } - - @Override - public WorldAccess getWorld() { - return handle.worldAccess; - } - - public static class Handle { - private final WorldAccess worldAccess; - private final long seed; - private final net.minecraft.world.gen.chunk.ChunkGenerator generator; - - public Handle(WorldAccess worldAccess, long seed, net.minecraft.world.gen.chunk.ChunkGenerator generator) { - this.worldAccess = worldAccess; - this.seed = seed; - this.generator = generator; - } - - public net.minecraft.world.gen.chunk.ChunkGenerator getGenerator() { - return generator; - } - - public long getSeed() { - return seed; - } - - public WorldAccess getWorldAccess() { - return worldAccess; - } - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/world/FabricWorldAccess.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/world/FabricWorldAccess.java deleted file mode 100644 index 787ed999c..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/world/FabricWorldAccess.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.dfsek.terra.fabric.world.handles.world; - -import com.dfsek.terra.api.math.vector.Location; -import com.dfsek.terra.api.platform.block.Block; -import com.dfsek.terra.api.platform.entity.Entity; -import com.dfsek.terra.api.platform.entity.EntityType; -import com.dfsek.terra.api.platform.world.Chunk; -import com.dfsek.terra.api.platform.world.World; -import com.dfsek.terra.api.platform.world.generator.ChunkGenerator; -import com.dfsek.terra.fabric.world.FabricAdapter; -import com.dfsek.terra.fabric.world.block.FabricBlock; -import com.dfsek.terra.fabric.world.entity.FabricEntity; -import com.dfsek.terra.fabric.world.generator.FabricChunkGenerator; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.ServerWorldAccess; -import net.minecraft.world.StructureWorldAccess; -import net.minecraft.world.WorldAccess; - -import java.io.File; -import java.util.UUID; - -public class FabricWorldAccess implements World, FabricWorldHandle { - private final WorldAccess delegate; - - public FabricWorldAccess(WorldAccess delegate) { - this.delegate = delegate; - } - - @Override - public long getSeed() { - return ((StructureWorldAccess) delegate).getSeed(); - } - - @Override - public int getMaxHeight() { - return delegate.getHeight() + delegate.getBottomY(); - } - - @Override - public ChunkGenerator getGenerator() { - return new FabricChunkGenerator(((ServerWorldAccess) delegate).toServerWorld().getChunkManager().getChunkGenerator()); - } - - @Override - public String getName() { - return ((ServerWorldAccess) delegate).toServerWorld().worldProperties.getLevelName(); - } - - @Override - public UUID getUID() { - return null; - } - - @Override - public boolean isChunkGenerated(int x, int z) { - return false; - } - - @Override - public Chunk getChunkAt(int x, int z) { - return null; - } - - @Override - public File getWorldFolder() { - return null; - } - - @Override - public Block getBlockAt(int x, int y, int z) { - BlockPos pos = new BlockPos(x, y, z); - return new FabricBlock(pos, delegate); - } - - @Override - public Entity spawnEntity(Location location, EntityType entityType) { - net.minecraft.entity.Entity entity = FabricAdapter.adapt(entityType).create(((ServerWorldAccess) delegate).toServerWorld()); - entity.setPos(location.getX(), location.getY(), location.getZ()); - delegate.spawnEntity(entity); - return new FabricEntity(entity); - } - - @Override - public int getMinHeight() { - return delegate.getBottomY(); - } - - @Override - public WorldAccess getHandle() { - return delegate; - } - - @Override - public WorldAccess getWorld() { - return delegate; - } - - @Override - public int hashCode() { - return ((ServerWorldAccess) delegate).toServerWorld().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if(!(obj instanceof FabricWorldAccess)) return false; - return ((ServerWorldAccess) ((FabricWorldAccess) obj).delegate).toServerWorld().equals(((ServerWorldAccess) delegate).toServerWorld()); - } -} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/world/FabricWorldHandle.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/world/FabricWorldHandle.java deleted file mode 100644 index 223b0c07f..000000000 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/world/FabricWorldHandle.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.dfsek.terra.fabric.world.handles.world; - -import com.dfsek.terra.api.platform.world.World; -import net.minecraft.world.WorldAccess; - -public interface FabricWorldHandle extends World { - WorldAccess getWorld(); -} diff --git a/platforms/fabric/src/main/resources/fabric.mod.json b/platforms/fabric/src/main/resources/fabric.mod.json index 09b2cb8fc..cdc7b0742 100644 --- a/platforms/fabric/src/main/resources/fabric.mod.json +++ b/platforms/fabric/src/main/resources/fabric.mod.json @@ -3,15 +3,16 @@ "id": "terra", "version": "@VERSION@", "name": "Terra", - "description": "An insanely powerful free & open-source data-driven world generator.", + "description": "@DESCRIPTION@", "authors": [ "dfsek" ], "contact": { - "homepage": "https://github.com/PolyhedralDev/Terra/wiki", - "sources": "https://github.com/PolyhedralDev/Terra" + "homepage": "@WIKI@", + "sources": "@SOURCE@", + "issues": "@ISSUES@" }, - "license": "GPL-3.0", + "license": "@LICENSE@", "icon": "assets/terra/icon.png", "environment": "*", "entrypoints": { @@ -24,7 +25,6 @@ ], "depends": { "fabricloader": ">=0.7.4", - "fabric": "*", "minecraft": "1.17.x" }, "accessWidener": "terra.accesswidener" diff --git a/platforms/fabric/src/main/resources/terra-refmap.json b/platforms/fabric/src/main/resources/terra-refmap.json deleted file mode 100644 index 8dbf41449..000000000 --- a/platforms/fabric/src/main/resources/terra-refmap.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "mappings": { - "com/dfsek/terra/fabric/mixin/GeneratorTypeAccessor": { - "VALUES": "field_25052:Ljava/util/List;", - "translationKey": "field_25060:Lnet/minecraft/class_2561;" - } - }, - "data": { - "named:intermediary": { - "com/dfsek/terra/fabric/mixin/GeneratorTypeAccessor": { - "VALUES": "field_25052:Ljava/util/List;", - "translationKey": "field_25060:Lnet/minecraft/class_2561;" - } - } - } -} \ No newline at end of file diff --git a/platforms/fabric/src/main/resources/terra.accesswidener b/platforms/fabric/src/main/resources/terra.accesswidener index 497a498f5..377aee552 100644 --- a/platforms/fabric/src/main/resources/terra.accesswidener +++ b/platforms/fabric/src/main/resources/terra.accesswidener @@ -1,20 +1,3 @@ accessWidener v1 named extendable method net/minecraft/client/world/GeneratorType (Ljava/lang/String;)V - -accessible field net/minecraft/server/world/ServerWorld worldProperties Lnet/minecraft/world/level/ServerWorldProperties; - -accessible method net/minecraft/world/MobSpawnerLogic getEntityId (Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/util/Identifier; - -accessible field net/minecraft/state/State PROPERTY_MAP_PRINTER Ljava/util/function/Function; - - -accessible field net/minecraft/world/biome/BiomeEffects fogColor I -accessible field net/minecraft/world/biome/BiomeEffects waterColor I -accessible field net/minecraft/world/biome/BiomeEffects waterFogColor I -accessible field net/minecraft/world/biome/BiomeEffects skyColor I - -accessible field net/minecraft/world/biome/BiomeEffects foliageColor Ljava/util/Optional; -accessible field net/minecraft/world/biome/BiomeEffects grassColor Ljava/util/Optional; -accessible field net/minecraft/world/biome/BiomeEffects grassColorModifier Lnet/minecraft/world/biome/BiomeEffects$GrassColorModifier; - diff --git a/platforms/fabric/src/main/resources/terra.mixins.json b/platforms/fabric/src/main/resources/terra.mixins.json index 2bc4388a9..436c5dd9c 100644 --- a/platforms/fabric/src/main/resources/terra.mixins.json +++ b/platforms/fabric/src/main/resources/terra.mixins.json @@ -5,11 +5,43 @@ "compatibilityLevel": "JAVA_8", "mixins": [ "StructureAccessorAccessor" + "CommandManagerMixin", + "GeneratorOptionsMixin", + "ServerWorldMixin", + "access.BiomeEffectsAccessor", + "access.MobSpawnerLogicAccessor", + "access.StateAccessor", + "implementations.BiomeMixin", + "implementations.ChunkGeneratorMixin", + "implementations.ConfiguredFeatureMixin", + "implementations.block.BlockEntityMixin", + "implementations.block.BlockMixin", + "implementations.block.state.LootableContainerBlockEntityMixin", + "implementations.block.state.MobSpawnerBlockEntityMixin", + "implementations.block.state.SignBlockEntityMixin", + "implementations.chunk.ChunkRegionMixin", + "implementations.chunk.WorldChunkMixin", + "implementations.chunk.data.ProtoChunkMixin", + "implementations.entity.EntityMixin", + "implementations.entity.EntityTypeMixin", + "implementations.entity.PlayerEntityMixin", + "implementations.entity.ServerCommandSourceMixin", + "implementations.inventory.LockableContainerBlockEntityMixin", + "implementations.inventory.item.ItemMixin", + "implementations.inventory.item.ItemStackMixin", + "implementations.inventory.meta.EnchantmentMixin", + "implementations.inventory.meta.ItemStackDamageableMixin", + "implementations.inventory.meta.ItemStackMetaMixin", + "implementations.world.ChunkRegionMixin", + "implementations.world.ServerWorldMixin" ], "client": [ - "GeneratorTypeAccessor" + "access.GeneratorTypeAccessor", + "init.MinecraftClientMixin" + ], + "server": [ + "init.ServerMainMixin" ], - "server": [], "injectors": { "defaultRequire": 1 }, diff --git a/platforms/forge/build.gradle.kts b/platforms/forge/build.gradle.kts new file mode 100644 index 000000000..682981977 --- /dev/null +++ b/platforms/forge/build.gradle.kts @@ -0,0 +1,142 @@ +import com.dfsek.terra.configureCommon +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import net.minecraftforge.gradle.common.util.RunConfig +import net.minecraftforge.gradle.userdev.UserDevExtension +import net.minecraftforge.gradle.userdev.tasks.RenameJarInPlace + +buildscript { + repositories { + maven { url = uri("https://files.minecraftforge.net/maven") } + jcenter() + mavenCentral() + maven { url = uri("https://repo.spongepowered.org/repository/maven-public/") } + } + dependencies { + classpath(group = "net.minecraftforge.gradle", name = "ForgeGradle", version = "4.1.+") + classpath("org.spongepowered:mixingradle:0.7-SNAPSHOT") + } +} +apply(plugin = "net.minecraftforge.gradle") +apply(plugin = "org.spongepowered.mixin") + +configure { + add(sourceSets.main.get(), "terra-refmap.json") +} + +plugins { + java + id("com.modrinth.minotaur").version("1.1.0") +} + +configureCommon() + +group = "com.dfsek.terra.forge" + +repositories { + maven { url = uri("https://files.minecraftforge.net/maven") } + jcenter() + mavenCentral() + maven { url = uri("https://repo.spongepowered.org/repository/maven-public/") } +} + +val forgeVersion = "36.1.13" +val mcVersion = "1.16.5" +dependencies { + "shadedApi"(project(":common")) + "minecraft"("net.minecraftforge:forge:$mcVersion-$forgeVersion") + "annotationProcessor"("org.spongepowered:mixin:0.8.2:processor") +} + +if ("true" == System.getProperty("idea.sync.active")) { + afterEvaluate { + tasks.withType().all { + options.annotationProcessorPath = files() + } + } +} + + +tasks.named("shadowJar") { + archiveBaseName.set(tasks.getByName("jar").archiveBaseName.orNull) // Pain. Agony, even. + archiveClassifier.set("") // Suffering, if you will. +} + +afterEvaluate { + tasks.named("reobfJar") { + val shadow = tasks.getByName("shadowJar"); + dependsOn(shadow) + input = shadow.archiveFile.orNull?.asFile + } +} + +configure { + mappings(mapOf( + "channel" to "official", + "version" to mcVersion + )) + runs { + val runConfig = Action { + properties(mapOf( + //"forge.logging.markers" to "SCAN,REGISTRIES,REGISTRYDUMP", + "forge.logging.console.level" to "debug" + )) + arg("-mixin.config=terra.mixins.json") + workingDirectory = project.file("run").canonicalPath + source(sourceSets["main"]) + } + create("client", runConfig) + create("server", runConfig) + } +} + +tasks.register("deobfJar") { + from(sourceSets["main"].output) + archiveClassifier.set("dev") + group = "forge" +} + +val deobfElements = configurations.register("deobfElements") { + isVisible = false + description = "De-obfuscated elements for libs" + isCanBeResolved = false + isCanBeConsumed = true + attributes { + attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.JAVA_API)) + attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY)) + attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling.EXTERNAL)) + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.JAR)) + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 8) + } + outgoing.artifact(tasks.named("deobfJar")) +} + +val javaComponent = components["java"] as AdhocComponentWithVariants +javaComponent.addVariantsFromConfiguration(deobfElements.get()) { + mapToMavenScope("runtime") +} + +tasks.jar { + manifest { + attributes(mapOf( + "Specification-Title" to "terra", + "Specification-Vendor" to "Terra", + "Specification-Version" to "1.0", + "Implementation-Title" to "Terra", + "Implementation-Version" to project.version, + "Implementation-Vendor" to "terra", + "MixinConfigs" to "terra.mixins.json" + )) + } +} + +tasks.register("publishModrinthForge") { + dependsOn("reobfJar") + group = "forge" + token = System.getenv("MODRINTH_SECRET") + projectId = "FIlZB9L0" + versionNumber = "${project.version}-forge" + uploadFile = tasks.named("reobfJar").get().input.absoluteFile + releaseType = "alpha" + addGameVersion("1.16.5") + addLoader("forge") +} \ No newline at end of file diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/ForgeAdapter.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/ForgeAdapter.java new file mode 100644 index 000000000..6b9f585d3 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/ForgeAdapter.java @@ -0,0 +1,90 @@ +package com.dfsek.terra.forge; + +import com.dfsek.terra.api.math.vector.Vector3; +import com.dfsek.terra.api.platform.block.BlockFace; +import com.dfsek.terra.api.platform.block.state.Container; +import com.dfsek.terra.api.platform.block.state.MobSpawner; +import com.dfsek.terra.api.platform.block.state.Sign; +import com.dfsek.terra.forge.block.ForgeBlockData; +import com.dfsek.terra.forge.block.data.ForgeDirectional; +import com.dfsek.terra.forge.block.data.ForgeMultipleFacing; +import com.dfsek.terra.forge.block.data.ForgeOrientable; +import com.dfsek.terra.forge.block.data.ForgeRotatable; +import com.dfsek.terra.forge.block.data.ForgeSlab; +import com.dfsek.terra.forge.block.data.ForgeStairs; +import com.dfsek.terra.forge.block.data.ForgeWaterlogged; +import net.minecraft.block.BlockState; +import net.minecraft.state.properties.BlockStateProperties; +import net.minecraft.tileentity.LockableLootTileEntity; +import net.minecraft.tileentity.MobSpawnerTileEntity; +import net.minecraft.tileentity.SignTileEntity; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IWorld; + +import java.util.Arrays; + +public final class ForgeAdapter { + public static BlockPos adapt(Vector3 v) { + return new BlockPos(v.getBlockX(), v.getBlockY(), v.getBlockZ()); + } + + public static Vector3 adapt(BlockPos pos) { + return new Vector3(pos.getX(), pos.getY(), pos.getZ()); + } + + public static ForgeBlockData adapt(BlockState state) { + if(state.hasProperty(BlockStateProperties.STAIRS_SHAPE)) return new ForgeStairs(state); + + if(state.hasProperty(BlockStateProperties.SLAB_TYPE)) return new ForgeSlab(state); + + if(state.hasProperty(BlockStateProperties.AXIS)) return new ForgeOrientable(state, BlockStateProperties.AXIS); + if(state.hasProperty(BlockStateProperties.HORIZONTAL_AXIS)) return new ForgeOrientable(state, BlockStateProperties.HORIZONTAL_AXIS); + + if(state.hasProperty(BlockStateProperties.ROTATION_16)) return new ForgeRotatable(state); + + if(state.hasProperty(BlockStateProperties.FACING)) return new ForgeDirectional(state, BlockStateProperties.FACING); + if(state.hasProperty(BlockStateProperties.FACING_HOPPER)) return new ForgeDirectional(state, BlockStateProperties.FACING_HOPPER); + if(state.hasProperty(BlockStateProperties.HORIZONTAL_FACING)) + return new ForgeDirectional(state, BlockStateProperties.HORIZONTAL_FACING); + + if(state.getProperties().containsAll(Arrays.asList(BlockStateProperties.NORTH, BlockStateProperties.SOUTH, BlockStateProperties.EAST, BlockStateProperties.WEST))) + return new ForgeMultipleFacing(state); + if(state.hasProperty(BlockStateProperties.WATERLOGGED)) return new ForgeWaterlogged(state); + return new ForgeBlockData(state); + } + + public static com.dfsek.terra.api.platform.block.state.BlockState adapt(com.dfsek.terra.api.platform.block.Block block) { + IWorld worldAccess = (IWorld) block.getLocation().getWorld(); + + TileEntity entity = worldAccess.getBlockEntity(adapt(block.getLocation().toVector())); + if(entity instanceof SignTileEntity) { + return (Sign) entity; + } else if(entity instanceof MobSpawnerTileEntity) { + return (MobSpawner) entity; + } else if(entity instanceof LockableLootTileEntity) { + return (Container) entity; + } + return null; + } + + public static Direction adapt(BlockFace face) { + switch(face) { + case NORTH: + return Direction.NORTH; + case WEST: + return Direction.WEST; + case SOUTH: + return Direction.SOUTH; + case EAST: + return Direction.EAST; + case UP: + return Direction.UP; + case DOWN: + return Direction.DOWN; + default: + throw new IllegalArgumentException("Illegal direction: " + face); + } + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/ForgeUtil.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/ForgeUtil.java new file mode 100644 index 000000000..e7f77feec --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/ForgeUtil.java @@ -0,0 +1,109 @@ +package com.dfsek.terra.forge; + +import com.dfsek.terra.api.util.generic.pair.Pair; +import com.dfsek.terra.config.builder.BiomeBuilder; +import com.dfsek.terra.config.pack.ConfigPack; +import com.dfsek.terra.config.templates.BiomeTemplate; +import com.dfsek.terra.forge.config.PostLoadCompatibilityOptions; +import com.dfsek.terra.forge.config.PreLoadCompatibilityOptions; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.WorldGenRegistries; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeAmbience; +import net.minecraft.world.biome.BiomeGenerationSettings; +import net.minecraft.world.gen.GenerationStage; +import net.minecraft.world.gen.carver.ConfiguredCarver; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import net.minecraft.world.gen.feature.StructureFeature; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Locale; +import java.util.Map; +import java.util.function.Supplier; + +public final class ForgeUtil { + public static String createBiomeID(ConfigPack pack, String biomeID) { + return pack.getTemplate().getID().toLowerCase() + "/" + biomeID.toLowerCase(Locale.ROOT); + } + + public static Biome createBiome(BiomeBuilder biome, ConfigPack pack, TerraForgePlugin.ForgeAddon forgeAddon) { + BiomeTemplate template = biome.getTemplate(); + Map colors = template.getColors(); + + Biome vanilla = (Biome) (new ArrayList<>(biome.getVanillaBiomes().getContents()).get(0)).getHandle(); + + BiomeGenerationSettings.Builder generationSettings = new BiomeGenerationSettings.Builder(); + + generationSettings.surfaceBuilder(vanilla.getGenerationSettings().getSurfaceBuilder()); // It needs a surfacebuilder, even though we dont use it. + + generationSettings.addFeature(GenerationStage.Decoration.VEGETAL_DECORATION, TerraForgePlugin.POPULATOR_CONFIGURED_FEATURE); + + if(pack.getTemplate().vanillaCaves()) { + for(GenerationStage.Carving carver : GenerationStage.Carving.values()) { + for(Supplier> configuredCarverSupplier : vanilla.getGenerationSettings().getCarvers(carver)) { + generationSettings.addCarver(carver, configuredCarverSupplier.get()); + } + } + } + + Pair pair = forgeAddon.getTemplates().get(pack); + PreLoadCompatibilityOptions compatibilityOptions = pair.getLeft(); + PostLoadCompatibilityOptions postLoadCompatibilityOptions = pair.getRight(); + + TerraForgePlugin.getInstance().getDebugLogger().info("Injecting Vanilla structures and features into Terra biome " + biome.getTemplate().getID()); + + for(Supplier> structureFeature : vanilla.getGenerationSettings().structures()) { + ResourceLocation key = WorldGenRegistries.CONFIGURED_STRUCTURE_FEATURE.getKey(structureFeature.get()); + if(!compatibilityOptions.getExcludedBiomeStructures().contains(key) && !postLoadCompatibilityOptions.getExcludedPerBiomeStructures().getOrDefault(biome, Collections.emptySet()).contains(key)) { + generationSettings.addStructureStart(structureFeature.get()); + TerraForgePlugin.getInstance().getDebugLogger().info("Injected structure " + key); + } + } + + if(compatibilityOptions.doBiomeInjection()) { + for(int step = 0; step < vanilla.getGenerationSettings().features().size(); step++) { + for(Supplier> featureSupplier : vanilla.getGenerationSettings().features().get(step)) { + ResourceLocation key = WorldGenRegistries.CONFIGURED_FEATURE.getKey(featureSupplier.get()); + if(!compatibilityOptions.getExcludedBiomeFeatures().contains(key) && !postLoadCompatibilityOptions.getExcludedPerBiomeFeatures().getOrDefault(biome, Collections.emptySet()).contains(key)) { + generationSettings.addFeature(step, featureSupplier); + TerraForgePlugin.getInstance().getDebugLogger().info("Injected feature " + key + " at stage " + step); + } + } + } + } + + BiomeAmbience vanillaEffects = vanilla.getSpecialEffects(); + BiomeAmbience.Builder effects = new BiomeAmbience.Builder() + .waterColor(colors.getOrDefault("water", vanillaEffects.getWaterColor())) + .waterFogColor(colors.getOrDefault("water-fog", vanillaEffects.getWaterFogColor())) + .fogColor(colors.getOrDefault("fog", vanillaEffects.getFogColor())) + .skyColor(colors.getOrDefault("sky", vanillaEffects.getSkyColor())) + .grassColorModifier(vanillaEffects.getGrassColorModifier()); + + if(colors.containsKey("grass")) { + effects.grassColorOverride(colors.get("grass")); + } else { + vanillaEffects.getGrassColorOverride().ifPresent(effects::grassColorOverride); + } + + if(colors.containsKey("foliage")) { + effects.foliageColorOverride(colors.get("foliage")); + } else { + vanillaEffects.getFoliageColorOverride().ifPresent(effects::foliageColorOverride); + } + + return new Biome.Builder() + .precipitation(vanilla.getPrecipitation()) + .biomeCategory(vanilla.getBiomeCategory()) + .depth(vanilla.getDepth()) + .scale(vanilla.getScale()) + .temperature(vanilla.getBaseTemperature()) + .downfall(vanilla.getDownfall()) + .specialEffects(effects.build()) + .mobSpawnSettings(vanilla.getMobSettings()) + .generationSettings(generationSettings.build()) + .build() + .setRegistryName("terra", createBiomeID(template.getPack(), template.getID())); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/TerraForgePlugin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/TerraForgePlugin.java new file mode 100644 index 000000000..e24229ac7 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/TerraForgePlugin.java @@ -0,0 +1,408 @@ +package com.dfsek.terra.forge; + +import com.dfsek.tectonic.exception.ConfigException; +import com.dfsek.tectonic.exception.LoadException; +import com.dfsek.tectonic.loading.TypeRegistry; +import com.dfsek.terra.api.TerraPlugin; +import com.dfsek.terra.api.addons.TerraAddon; +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 com.dfsek.terra.api.command.CommandManager; +import com.dfsek.terra.api.command.TerraCommandManager; +import com.dfsek.terra.api.command.exception.MalformedCommandException; +import com.dfsek.terra.api.event.EventListener; +import com.dfsek.terra.api.event.EventManager; +import com.dfsek.terra.api.event.TerraEventManager; +import com.dfsek.terra.api.event.annotations.Global; +import com.dfsek.terra.api.event.annotations.Priority; +import com.dfsek.terra.api.event.events.config.ConfigPackPostLoadEvent; +import com.dfsek.terra.api.event.events.config.ConfigPackPreLoadEvent; +import com.dfsek.terra.api.platform.block.BlockData; +import com.dfsek.terra.api.platform.handle.ItemHandle; +import com.dfsek.terra.api.platform.handle.WorldHandle; +import com.dfsek.terra.api.platform.world.Tree; +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.transform.Transformer; +import com.dfsek.terra.api.transform.Validator; +import com.dfsek.terra.api.util.JarUtil; +import com.dfsek.terra.api.util.generic.pair.Pair; +import com.dfsek.terra.api.util.logging.DebugLogger; +import com.dfsek.terra.commands.CommandUtil; +import com.dfsek.terra.config.GenericLoaders; +import com.dfsek.terra.config.PluginConfig; +import com.dfsek.terra.config.lang.LangUtil; +import com.dfsek.terra.config.lang.Language; +import com.dfsek.terra.config.pack.ConfigPack; +import com.dfsek.terra.forge.config.PostLoadCompatibilityOptions; +import com.dfsek.terra.forge.config.PreLoadCompatibilityOptions; +import com.dfsek.terra.forge.generation.ForgeChunkGeneratorWrapper; +import com.dfsek.terra.forge.generation.PopulatorFeature; +import com.dfsek.terra.forge.generation.TerraBiomeSource; +import com.dfsek.terra.forge.handle.ForgeItemHandle; +import com.dfsek.terra.forge.handle.ForgeWorldHandle; +import com.dfsek.terra.profiler.Profiler; +import com.dfsek.terra.profiler.ProfilerImpl; +import com.dfsek.terra.registry.exception.DuplicateEntryException; +import com.dfsek.terra.registry.master.AddonRegistry; +import com.dfsek.terra.registry.master.ConfigRegistry; +import com.dfsek.terra.world.TerraWorld; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.WorldGenRegistries; +import net.minecraft.world.DimensionType; +import net.minecraft.world.IWorld; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import net.minecraft.world.gen.feature.Features; +import net.minecraft.world.gen.feature.IFeatureConfig; +import net.minecraft.world.gen.feature.NoFeatureConfig; +import net.minecraft.world.gen.placement.DecoratedPlacement; +import net.minecraft.world.gen.placement.NoPlacementConfig; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.ForgeRegistry; +import org.apache.commons.io.FileUtils; +import org.apache.logging.log4j.LogManager; +import org.objectweb.asm.Type; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.jar.JarFile; +import java.util.zip.ZipFile; + +@Mod("terra") +@Mod.EventBusSubscriber(modid = "terra", bus = Mod.EventBusSubscriber.Bus.MOD) +public class TerraForgePlugin implements TerraPlugin { + public static final PopulatorFeature POPULATOR_FEATURE = (PopulatorFeature) new PopulatorFeature(NoFeatureConfig.CODEC).setRegistryName("terra", "terra"); + public static final ConfiguredFeature POPULATOR_CONFIGURED_FEATURE = POPULATOR_FEATURE.configured(IFeatureConfig.NONE).decorated(DecoratedPlacement.NOPE.configured(NoPlacementConfig.INSTANCE)); + + private static TerraForgePlugin INSTANCE; + private final Map> worldMap = new HashMap<>(); + private final EventManager eventManager = new TerraEventManager(this); + private final GenericLoaders genericLoaders = new GenericLoaders(this); + private final Profiler profiler = new ProfilerImpl(); + + private final CommandManager manager = new TerraCommandManager(this); + + private final com.dfsek.terra.api.util.logging.Logger logger = new com.dfsek.terra.api.util.logging.Logger() { + private final org.apache.logging.log4j.Logger logger = LogManager.getLogger(); + + @Override + public void info(String message) { + logger.info(message); + } + + @Override + public void warning(String message) { + logger.warn(message); + } + + @Override + public void severe(String message) { + logger.error(message); + } + }; + + private final DebugLogger debugLogger = new DebugLogger(logger); + private final ItemHandle itemHandle = new ForgeItemHandle(); + private final WorldHandle worldHandle = new ForgeWorldHandle(); + private final ConfigRegistry registry = new ConfigRegistry(); + private final CheckedRegistry checkedRegistry = new CheckedRegistry<>(registry); + + private final ForgeAddon addon = new ForgeAddon(this); + + private final AddonRegistry addonRegistry = new AddonRegistry(addon, this); + private final LockedRegistry addonLockedRegistry = new LockedRegistry<>(addonRegistry); + private final PluginConfig config = new PluginConfig(); + private final Transformer biomeFixer = new Transformer.Builder() + .addTransform(id -> ForgeRegistries.BIOMES.getValue(ResourceLocation.tryParse(id)), Validator.notNull()) + .addTransform(id -> ForgeRegistries.BIOMES.getValue(ResourceLocation.tryParse("minecraft:" + id.toLowerCase())), Validator.notNull()).build(); + private final File dataFolder; + + public TerraForgePlugin() { + if(INSTANCE != null) throw new IllegalStateException("Only one TerraPlugin instance may exist."); + INSTANCE = this; + this.dataFolder = Paths.get("config", "Terra").toFile(); + saveDefaultConfig(); + config.load(this); + debugLogger.setDebug(config.isDebug()); + LangUtil.load(config.getLanguage(), this); + try { + CommandUtil.registerAll(manager); + } catch(MalformedCommandException e) { + e.printStackTrace(); // TODO do something here even though this should literally never happen + } + } + + public static TerraForgePlugin getInstance() { + return INSTANCE; + } + + @SubscribeEvent + public static void setupListener(FMLCommonSetupEvent event) { + event.enqueueWork(() -> { + Registry.register(Registry.BIOME_SOURCE, "terra:biome", TerraBiomeSource.CODEC); + Registry.register(Registry.CHUNK_GENERATOR, "terra:generator", ForgeChunkGeneratorWrapper.CODEC); + }); + } + + public void init() { + logger.info("Initializing Terra..."); + + if(!addonRegistry.loadAll()) { + throw new IllegalStateException("Failed to load addons. Please correct addon installations to continue."); + } + logger.info("Loaded addons."); + + registry.loadAll(this); + logger.info("Loaded packs."); + + ((ForgeRegistry) ForgeRegistries.BIOMES).unfreeze(); // Evil + getConfigRegistry().forEach(pack -> pack.getBiomeRegistry().forEach((id, biome) -> ForgeRegistries.BIOMES.register(ForgeUtil.createBiome(biome, pack, addon)))); // Register all Terra biomes. + ((ForgeRegistry) ForgeRegistries.BIOMES).freeze(); + } + + @Override + public WorldHandle getWorldHandle() { + return worldHandle; + } + + @Override + public TerraWorld getWorld(World world) { + return getWorld(((IWorld) world).dimensionType()); + } + + public TerraWorld getWorld(DimensionType type) { + TerraWorld world = worldMap.get(type).getRight(); + if(world == null) throw new IllegalArgumentException("No world exists with dimension type " + type); + return world; + } + + /** + * evil code brought to you by Forge Mod Loader + *

+ * Forge changes the JAR URI to something that cannot + * be resolved back to the original JAR, so we have to + * do this to get our JAR. + */ + @Override + public JarFile getModJar() throws URISyntaxException, IOException { + File modsDir = new File("./mods"); + + if(!modsDir.exists()) return JarUtil.getJarFile(); + + for(File file : Objects.requireNonNull(modsDir.listFiles((dir, name) -> name.endsWith(".jar")))) { + try(ZipFile zipFile = new ZipFile(file)) { + if(zipFile.getEntry(Type.getInternalName(TerraPlugin.class) + ".class") != null) { + return new JarFile(file); + } + } + } + return JarUtil.getJarFile(); + } + + @Override + public com.dfsek.terra.api.util.logging.Logger logger() { + return logger; + } + + @Override + public PluginConfig getTerraConfig() { + return config; + } + + @Override + public File getDataFolder() { + return dataFolder; + } + + @Override + public boolean isDebug() { + return config.isDebug(); + } + + @Override + public Language getLanguage() { + return LangUtil.getLanguage(); + } + + @Override + public CheckedRegistry getConfigRegistry() { + return checkedRegistry; + } + + @Override + public LockedRegistry getAddons() { + return addonLockedRegistry; + } + + @Override + public boolean reload() { + config.load(this); + LangUtil.load(config.getLanguage(), this); // Load language. + boolean succeed = registry.loadAll(this); + worldMap.forEach((seed, pair) -> { + pair.getRight().getConfig().getSamplerCache().clear(); + String packID = pair.getRight().getConfig().getTemplate().getID(); + pair.setRight(new TerraWorld(pair.getRight().getWorld(), registry.get(packID), this)); + }); + return succeed; + } + + @Override + public ItemHandle getItemHandle() { + return itemHandle; + } + + @Override + public void saveDefaultConfig() { + try(InputStream stream = getClass().getResourceAsStream("/config.yml")) { + File configFile = new File(getDataFolder(), "config.yml"); + if(!configFile.exists()) FileUtils.copyInputStreamToFile(stream, configFile); + } catch(IOException e) { + e.printStackTrace(); + } + } + + @Override + public String platformName() { + return "Forge"; + } + + @Override + public DebugLogger getDebugLogger() { + return debugLogger; + } + + @Override + public void register(TypeRegistry registry) { + genericLoaders.register(registry); + registry + .registerLoader(BlockData.class, (t, o, l) -> worldHandle.createBlockData((String) o)) + .registerLoader(com.dfsek.terra.api.platform.world.Biome.class, (t, o, l) -> biomeFixer.translate((String) o)) + .registerLoader(ResourceLocation.class, (t, o, l) -> { + ResourceLocation identifier = ResourceLocation.tryParse((String) o); + if(identifier == null) throw new LoadException("Invalid identifier: " + o); + return identifier; + }); + } + + @Override + public EventManager getEventManager() { + return eventManager; + } + + @Override + public Profiler getProfiler() { + return profiler; + } + + public CommandManager getManager() { + return manager; + } + + public Map> getWorldMap() { + return worldMap; + } + + @Addon("Terra-Forge") + @Author("Terra") + @Version("1.0.0") + public static final class ForgeAddon extends TerraAddon implements EventListener { + + private final Map> templates = new HashMap<>(); + + private final TerraPlugin main; + + private ForgeAddon(TerraPlugin main) { + this.main = main; + } + + @Override + public void initialize() { + main.getEventManager().registerListener(this, this); + } + + @Priority(Priority.LOWEST) + @Global + public void injectTrees(ConfigPackPreLoadEvent event) { + CheckedRegistry treeRegistry = event.getPack().getTreeRegistry(); + injectTree(treeRegistry, "BROWN_MUSHROOM", Features.HUGE_BROWN_MUSHROOM); + injectTree(treeRegistry, "RED_MUSHROOM", Features.HUGE_RED_MUSHROOM); + injectTree(treeRegistry, "JUNGLE", Features.MEGA_JUNGLE_TREE); + injectTree(treeRegistry, "JUNGLE_COCOA", Features.JUNGLE_TREE); + injectTree(treeRegistry, "LARGE_OAK", Features.FANCY_OAK); + injectTree(treeRegistry, "LARGE_SPRUCE", Features.PINE); + injectTree(treeRegistry, "SMALL_JUNGLE", Features.JUNGLE_TREE); + injectTree(treeRegistry, "SWAMP_OAK", Features.SWAMP_TREE); + injectTree(treeRegistry, "TALL_BIRCH", Features.BIRCH_TALL); + injectTree(treeRegistry, "ACACIA", Features.ACACIA); + injectTree(treeRegistry, "BIRCH", Features.BIRCH); + injectTree(treeRegistry, "DARK_OAK", Features.DARK_OAK); + injectTree(treeRegistry, "OAK", Features.OAK); + injectTree(treeRegistry, "CHORUS_PLANT", Features.CHORUS_PLANT); + injectTree(treeRegistry, "SPRUCE", Features.SPRUCE); + injectTree(treeRegistry, "JUNGLE_BUSH", Features.JUNGLE_BUSH); + injectTree(treeRegistry, "MEGA_SPRUCE", Features.MEGA_SPRUCE); + injectTree(treeRegistry, "CRIMSON_FUNGUS", Features.CRIMSON_FUNGI); + injectTree(treeRegistry, "WARPED_FUNGUS", Features.WARPED_FUNGI); + PreLoadCompatibilityOptions template = new PreLoadCompatibilityOptions(); + try { + event.loadTemplate(template); + } catch(ConfigException e) { + e.printStackTrace(); + } + + if(template.doRegistryInjection()) { + WorldGenRegistries.CONFIGURED_FEATURE.entrySet().forEach(entry -> { + if(!template.getExcludedRegistryFeatures().contains(entry.getKey().getRegistryName())) { + try { + event.getPack().getTreeRegistry().add(entry.getKey().getRegistryName().toString(), (Tree) entry.getValue()); + main.getDebugLogger().info("Injected ConfiguredFeature " + entry.getKey().getRegistryName() + " as Tree: " + entry.getValue()); + } catch(DuplicateEntryException ignored) { + } + } + }); + } + templates.put(event.getPack(), Pair.of(template, null)); + } + + @Priority(Priority.HIGHEST) + @Global + public void createInjectionOptions(ConfigPackPostLoadEvent event) { + PostLoadCompatibilityOptions template = new PostLoadCompatibilityOptions(); + + try { + event.loadTemplate(template); + } catch(ConfigException e) { + e.printStackTrace(); + } + + templates.get(event.getPack()).setRight(template); + } + + + private void injectTree(CheckedRegistry registry, String id, ConfiguredFeature tree) { + try { + registry.add(id, (Tree) tree); + } catch(DuplicateEntryException ignore) { + } + } + + public Map> getTemplates() { + return templates; + } + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/block/ForgeBlock.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/ForgeBlock.java new file mode 100644 index 000000000..98ad4d61e --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/ForgeBlock.java @@ -0,0 +1,95 @@ +package com.dfsek.terra.forge.block; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.block.Block; +import com.dfsek.terra.api.platform.block.BlockData; +import com.dfsek.terra.api.platform.block.BlockFace; +import com.dfsek.terra.api.platform.block.BlockType; +import com.dfsek.terra.api.platform.block.state.BlockState; +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.forge.ForgeAdapter; +import net.minecraft.block.FlowingFluidBlock; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IWorld; + +public class ForgeBlock implements Block { + private final Handle delegate; + + public ForgeBlock(BlockPos position, IWorld worldAccess) { + this.delegate = new Handle(position, worldAccess); + } + + @Override + public void setBlockData(BlockData data, boolean physics) { + delegate.worldAccess.setBlock(delegate.position, ((ForgeBlockData) data).getHandle(), physics ? 3 : 1042); + if(physics && ((ForgeBlockData) data).getHandle().getBlock() instanceof FlowingFluidBlock) { + delegate.worldAccess.getLiquidTicks().scheduleTick(delegate.position, ((FlowingFluidBlock) ((ForgeBlockData) data).getHandle().getBlock()).getFluidState(((ForgeBlockData) data).getHandle()).getFluidState().getType(), 0); + } + } + + @Override + public BlockData getBlockData() { + return new ForgeBlockData(delegate.worldAccess.getBlockState(delegate.position)); + } + + @Override + public BlockState getState() { + return ForgeAdapter.adapt(this); + } + + @Override + public Block getRelative(BlockFace face, int len) { + BlockPos newPos = delegate.position.offset(face.getModX() * len, face.getModY() * len, face.getModZ() * len); + return new ForgeBlock(newPos, delegate.worldAccess); + } + + @Override + public boolean isEmpty() { + return getBlockData().isAir(); + } + + @Override + public Location getLocation() { + return ForgeAdapter.adapt(delegate.position).toLocation((World) delegate.worldAccess); + } + + @Override + public BlockType getType() { + return getBlockData().getBlockType(); + } + + @Override + public int getX() { + return delegate.position.getX(); + } + + @Override + public int getZ() { + return delegate.position.getZ(); + } + + @Override + public int getY() { + return delegate.position.getY(); + } + + @Override + public boolean isPassable() { + return isEmpty(); + } + + @Override + public Handle getHandle() { + return delegate; + } + + public static final class Handle { + private final BlockPos position; + private final IWorld worldAccess; + + public Handle(BlockPos position, IWorld worldAccess) { + this.position = position; + this.worldAccess = worldAccess; + } + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/block/ForgeBlockData.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/ForgeBlockData.java new file mode 100644 index 000000000..1fb22ec35 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/ForgeBlockData.java @@ -0,0 +1,83 @@ +package com.dfsek.terra.forge.block; + +import com.dfsek.terra.api.platform.block.BlockData; +import com.dfsek.terra.api.platform.block.BlockType; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.state.Property; +import net.minecraftforge.registries.ForgeRegistries; + +import javax.annotation.Nullable; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class ForgeBlockData implements BlockData { + private static final Function, Comparable>, String> PROPERTY_MAPPER = new Function, Comparable>, String>() { + public String apply(@Nullable Map.Entry, Comparable> entry) { + if (entry == null) { + return ""; + } else { + Property property = entry.getKey(); + return property.getName() + "=" + this.getName(property, entry.getValue()); + } + } + + @SuppressWarnings("unchecked") + private > String getName(Property property, Comparable comparable) { + return property.getName((T)comparable); + } + }; + + protected BlockState delegate; + + public ForgeBlockData(BlockState delegate) { + this.delegate = delegate; + } + + @Override + public BlockType getBlockType() { + return (BlockType) delegate.getBlock(); + } + + @Override + public boolean matches(BlockData other) { + return delegate.getBlock() == ((ForgeBlockData) other).delegate.getBlock(); + } + + @Override + public BlockData clone() { + try { + return (ForgeBlockData) super.clone(); + } catch(CloneNotSupportedException e) { + throw new Error(e); + } + } + + @Override + public String getAsString() { + StringBuilder data = new StringBuilder(Objects.requireNonNull(ForgeRegistries.BLOCKS.getKey(delegate.getBlock())).toString()); + if(!delegate.getProperties().isEmpty()) { + data.append('['); + data.append(delegate.getValues().entrySet().stream().map(PROPERTY_MAPPER).collect(Collectors.joining(","))); + data.append(']'); + } + return data.toString(); + } + + @Override + public boolean isAir() { + return delegate.isAir(); + } + + @Override + public boolean isStructureVoid() { + return delegate.getBlock() == Blocks.STRUCTURE_VOID; + } + + @Override + public BlockState getHandle() { + return delegate; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeAnaloguePowerable.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeAnaloguePowerable.java new file mode 100644 index 000000000..f8455c5c7 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeAnaloguePowerable.java @@ -0,0 +1,29 @@ +package com.dfsek.terra.forge.block.data; + +import com.dfsek.terra.api.platform.block.data.AnaloguePowerable; +import com.dfsek.terra.forge.block.ForgeBlockData; +import net.minecraft.block.BlockState; + +/** + * None of this actually has implementation, TODO: implement this if we ever end up needing it. + */ +public class ForgeAnaloguePowerable extends ForgeBlockData implements AnaloguePowerable { + public ForgeAnaloguePowerable(BlockState delegate) { + super(delegate); + } + + @Override + public int getMaximumPower() { + return 0; + } + + @Override + public int getPower() { + return 0; + } + + @Override + public void setPower(int power) { + + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeDirectional.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeDirectional.java new file mode 100644 index 000000000..c4cc15636 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeDirectional.java @@ -0,0 +1,42 @@ +package com.dfsek.terra.forge.block.data; + +import com.dfsek.terra.api.platform.block.BlockFace; +import com.dfsek.terra.api.platform.block.data.Directional; +import com.dfsek.terra.forge.ForgeAdapter; +import com.dfsek.terra.forge.block.ForgeBlockData; +import net.minecraft.block.BlockState; +import net.minecraft.state.DirectionProperty; + +public class ForgeDirectional extends ForgeBlockData implements Directional { + private final DirectionProperty property; + + public ForgeDirectional(BlockState delegate, DirectionProperty property) { + super(delegate); + this.property = property; + } + + @Override + public BlockFace getFacing() { + switch(delegate.getValue(property)) { + case SOUTH: + return BlockFace.SOUTH; + case DOWN: + return BlockFace.DOWN; + case UP: + return BlockFace.UP; + case EAST: + return BlockFace.EAST; + case WEST: + return BlockFace.WEST; + case NORTH: + return BlockFace.NORTH; + default: + throw new IllegalStateException(); + } + } + + @Override + public void setFacing(BlockFace facing) { + delegate = delegate.setValue(property, ForgeAdapter.adapt(facing)); + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricEnumAdapter.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeEnumAdapter.java similarity index 82% rename from platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricEnumAdapter.java rename to platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeEnumAdapter.java index 98f883059..16ba0bb8a 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/data/FabricEnumAdapter.java +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeEnumAdapter.java @@ -1,17 +1,17 @@ -package com.dfsek.terra.fabric.world.block.data; +package com.dfsek.terra.forge.block.data; import com.dfsek.terra.api.platform.block.Axis; import com.dfsek.terra.api.platform.block.BlockFace; import com.dfsek.terra.api.platform.block.data.Bisected; import com.dfsek.terra.api.platform.block.data.Slab; import com.dfsek.terra.api.platform.block.data.Stairs; -import net.minecraft.block.enums.BlockHalf; -import net.minecraft.block.enums.SlabType; -import net.minecraft.block.enums.StairShape; -import net.minecraft.util.math.Direction; +import net.minecraft.state.properties.Half; +import net.minecraft.state.properties.SlabType; +import net.minecraft.state.properties.StairsShape; +import net.minecraft.util.Direction; -public final class FabricEnumAdapter { - public static Stairs.Shape adapt(StairShape shape) { +public final class ForgeEnumAdapter { + public static Stairs.Shape adapt(StairsShape shape) { switch(shape) { case OUTER_RIGHT: return Stairs.Shape.OUTER_RIGHT; @@ -28,7 +28,7 @@ public final class FabricEnumAdapter { } } - public static Bisected.Half adapt(BlockHalf half) { + public static Bisected.Half adapt(Half half) { switch(half) { case BOTTOM: return Bisected.Half.BOTTOM; @@ -71,29 +71,29 @@ public final class FabricEnumAdapter { } } - public static StairShape adapt(Stairs.Shape shape) { + public static StairsShape adapt(Stairs.Shape shape) { switch(shape) { case STRAIGHT: - return StairShape.STRAIGHT; + return StairsShape.STRAIGHT; case INNER_LEFT: - return StairShape.INNER_LEFT; + return StairsShape.INNER_LEFT; case OUTER_LEFT: - return StairShape.OUTER_LEFT; + return StairsShape.OUTER_LEFT; case INNER_RIGHT: - return StairShape.INNER_RIGHT; + return StairsShape.INNER_RIGHT; case OUTER_RIGHT: - return StairShape.OUTER_RIGHT; + return StairsShape.OUTER_RIGHT; default: throw new IllegalStateException(); } } - public static BlockHalf adapt(Bisected.Half half) { + public static Half adapt(Bisected.Half half) { switch(half) { case TOP: - return BlockHalf.TOP; + return Half.TOP; case BOTTOM: - return BlockHalf.BOTTOM; + return Half.BOTTOM; default: throw new IllegalStateException(); } diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeMultipleFacing.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeMultipleFacing.java new file mode 100644 index 000000000..7e9510282 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeMultipleFacing.java @@ -0,0 +1,69 @@ +package com.dfsek.terra.forge.block.data; + +import com.dfsek.terra.api.platform.block.BlockFace; +import com.dfsek.terra.api.platform.block.data.MultipleFacing; +import com.dfsek.terra.forge.block.ForgeBlockData; +import net.minecraft.block.BlockState; +import net.minecraft.state.properties.BlockStateProperties; + +import java.util.HashSet; +import java.util.Set; + +public class ForgeMultipleFacing extends ForgeBlockData implements MultipleFacing { + public ForgeMultipleFacing(BlockState delegate) { + super(delegate); + } + + @Override + public Set getFaces() { + Set set = new HashSet<>(); + if(delegate.getValue(BlockStateProperties.NORTH)) set.add(BlockFace.NORTH); + if(delegate.getValue(BlockStateProperties.SOUTH)) set.add(BlockFace.SOUTH); + if(delegate.getValue(BlockStateProperties.EAST)) set.add(BlockFace.EAST); + if(delegate.getValue(BlockStateProperties.WEST)) set.add(BlockFace.WEST); + if(delegate.hasProperty(BlockStateProperties.UP) && delegate.getValue(BlockStateProperties.UP)) set.add(BlockFace.UP); + if(delegate.hasProperty(BlockStateProperties.DOWN) && delegate.getValue(BlockStateProperties.DOWN)) set.add(BlockFace.DOWN); + return set; + } + + @Override + public void setFace(BlockFace face, boolean facing) { + switch(face) { + case NORTH: + delegate = delegate.setValue(BlockStateProperties.NORTH, facing); + break; + case SOUTH: + delegate = delegate.setValue(BlockStateProperties.SOUTH, facing); + break; + case EAST: + delegate = delegate.setValue(BlockStateProperties.EAST, facing); + break; + case WEST: + delegate = delegate.setValue(BlockStateProperties.WEST, facing); + break; + case UP: + delegate = delegate.setValue(BlockStateProperties.UP, facing); + break; + case DOWN: + delegate = delegate.setValue(BlockStateProperties.DOWN, facing); + break; + } + } + + @Override + public Set getAllowedFaces() { + Set set = new HashSet<>(); + if(delegate.hasProperty(BlockStateProperties.NORTH)) set.add(BlockFace.NORTH); + if(delegate.hasProperty(BlockStateProperties.SOUTH)) set.add(BlockFace.SOUTH); + if(delegate.hasProperty(BlockStateProperties.EAST)) set.add(BlockFace.EAST); + if(delegate.hasProperty(BlockStateProperties.WEST)) set.add(BlockFace.WEST); + if(delegate.hasProperty(BlockStateProperties.UP)) set.add(BlockFace.UP); + if(delegate.hasProperty(BlockStateProperties.DOWN)) set.add(BlockFace.DOWN); + return set; + } + + @Override + public boolean hasFace(BlockFace f) { + return getFaces().contains(f); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeOrientable.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeOrientable.java new file mode 100644 index 000000000..9f3e53f2d --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeOrientable.java @@ -0,0 +1,36 @@ +package com.dfsek.terra.forge.block.data; + +import com.dfsek.terra.api.platform.block.Axis; +import com.dfsek.terra.api.platform.block.data.Orientable; +import com.dfsek.terra.forge.block.ForgeBlockData; +import net.minecraft.block.BlockState; +import net.minecraft.state.EnumProperty; +import net.minecraft.util.Direction; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +public class ForgeOrientable extends ForgeBlockData implements Orientable { + private final EnumProperty property; + + public ForgeOrientable(BlockState delegate, EnumProperty property) { + super(delegate); + this.property = property; + } + + @Override + public Set getAxes() { + return Arrays.stream(Axis.values()).collect(Collectors.toSet()); + } + + @Override + public Axis getAxis() { + return ForgeEnumAdapter.adapt(getHandle().getValue(property)); + } + + @Override + public void setAxis(Axis axis) { + delegate = delegate.setValue(property, ForgeEnumAdapter.adapt(axis)); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeRotatable.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeRotatable.java new file mode 100644 index 000000000..b64727b59 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeRotatable.java @@ -0,0 +1,111 @@ +package com.dfsek.terra.forge.block.data; + +import com.dfsek.terra.api.platform.block.BlockFace; +import com.dfsek.terra.api.platform.block.data.Rotatable; +import com.dfsek.terra.forge.block.ForgeBlockData; +import net.minecraft.block.BlockState; +import net.minecraft.state.properties.BlockStateProperties; + +public class ForgeRotatable extends ForgeBlockData implements Rotatable { + public ForgeRotatable(BlockState delegate) { + super(delegate); + } + + @Override + public BlockFace getRotation() { + int r = delegate.getValue(BlockStateProperties.ROTATION_16); + switch(r) { + case 0: + return BlockFace.SOUTH; + case 1: + return BlockFace.SOUTH_SOUTH_WEST; + case 2: + return BlockFace.SOUTH_WEST; + case 3: + return BlockFace.WEST_SOUTH_WEST; + case 4: + return BlockFace.WEST; + case 5: + return BlockFace.WEST_NORTH_WEST; + case 6: + return BlockFace.NORTH_WEST; + case 7: + return BlockFace.NORTH_NORTH_WEST; + case 8: + return BlockFace.NORTH; + case 9: + return BlockFace.NORTH_NORTH_EAST; + case 10: + return BlockFace.NORTH_EAST; + case 11: + return BlockFace.EAST_NORTH_EAST; + case 12: + return BlockFace.EAST; + case 13: + return BlockFace.EAST_SOUTH_EAST; + case 14: + return BlockFace.SOUTH_EAST; + case 15: + return BlockFace.SOUTH_SOUTH_EAST; + default: + throw new IllegalArgumentException("Unknown rotation " + r); + } + } + + @Override + public void setRotation(BlockFace face) { + switch(face) { + case UP: + case DOWN: + throw new IllegalArgumentException("Illegal rotation " + face); + case SOUTH: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 0); + return; + case SOUTH_SOUTH_WEST: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 1); + return; + case SOUTH_WEST: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 2); + return; + case WEST_SOUTH_WEST: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 3); + return; + case WEST: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 4); + return; + case WEST_NORTH_WEST: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 5); + return; + case NORTH_WEST: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 6); + return; + case NORTH_NORTH_WEST: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 7); + return; + case NORTH: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 8); + return; + case NORTH_NORTH_EAST: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 9); + return; + case NORTH_EAST: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 10); + return; + case EAST_NORTH_EAST: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 11); + return; + case EAST: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 12); + return; + case EAST_SOUTH_EAST: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 13); + return; + case SOUTH_EAST: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 14); + return; + case SOUTH_SOUTH_EAST: + delegate = delegate.setValue(BlockStateProperties.ROTATION_16, 15); + return; + } + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeSlab.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeSlab.java new file mode 100644 index 000000000..4f294604e --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeSlab.java @@ -0,0 +1,21 @@ +package com.dfsek.terra.forge.block.data; + +import com.dfsek.terra.api.platform.block.data.Slab; +import net.minecraft.block.BlockState; +import net.minecraft.state.properties.BlockStateProperties; + +public class ForgeSlab extends ForgeWaterlogged implements Slab { + public ForgeSlab(BlockState delegate) { + super(delegate); + } + + @Override + public Type getType() { + return ForgeEnumAdapter.adapt(delegate.getValue(BlockStateProperties.SLAB_TYPE)); + } + + @Override + public void setType(Type type) { + delegate = delegate.setValue(BlockStateProperties.SLAB_TYPE, ForgeEnumAdapter.adapt(type)); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeStairs.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeStairs.java new file mode 100644 index 000000000..ef9c6e8ff --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeStairs.java @@ -0,0 +1,42 @@ +package com.dfsek.terra.forge.block.data; + +import com.dfsek.terra.api.platform.block.BlockFace; +import com.dfsek.terra.api.platform.block.data.Stairs; +import net.minecraft.block.BlockState; +import net.minecraft.state.properties.BlockStateProperties; + +public class ForgeStairs extends ForgeWaterlogged implements Stairs { + public ForgeStairs(BlockState delegate) { + super(delegate); + } + + @Override + public Shape getShape() { + return ForgeEnumAdapter.adapt(getHandle().getValue(BlockStateProperties.STAIRS_SHAPE)); + } + + @Override + public void setShape(Shape shape) { + super.delegate = getHandle().setValue(BlockStateProperties.STAIRS_SHAPE, ForgeEnumAdapter.adapt(shape)); + } + + @Override + public Half getHalf() { + return ForgeEnumAdapter.adapt(getHandle().getValue(BlockStateProperties.HALF)); + } + + @Override + public void setHalf(Half half) { + super.delegate = getHandle().setValue(BlockStateProperties.HALF, ForgeEnumAdapter.adapt(half)); + } + + @Override + public BlockFace getFacing() { + return ForgeEnumAdapter.adapt(getHandle().getValue(BlockStateProperties.HORIZONTAL_FACING)); + } + + @Override + public void setFacing(BlockFace facing) { + super.delegate = getHandle().setValue(BlockStateProperties.HORIZONTAL_FACING, ForgeEnumAdapter.adapt(facing)); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeWaterlogged.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeWaterlogged.java new file mode 100644 index 000000000..ed6abffbe --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/block/data/ForgeWaterlogged.java @@ -0,0 +1,22 @@ +package com.dfsek.terra.forge.block.data; + +import com.dfsek.terra.api.platform.block.data.Waterlogged; +import com.dfsek.terra.forge.block.ForgeBlockData; +import net.minecraft.block.BlockState; +import net.minecraft.state.properties.BlockStateProperties; + +public class ForgeWaterlogged extends ForgeBlockData implements Waterlogged { + public ForgeWaterlogged(BlockState delegate) { + super(delegate); + } + + @Override + public boolean isWaterlogged() { + return delegate.getValue(BlockStateProperties.WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + super.delegate = delegate.setValue(BlockStateProperties.WATERLOGGED, waterlogged); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/config/PostLoadCompatibilityOptions.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/config/PostLoadCompatibilityOptions.java new file mode 100644 index 000000000..7d0bd3e5b --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/config/PostLoadCompatibilityOptions.java @@ -0,0 +1,30 @@ +package com.dfsek.terra.forge.config; + +import com.dfsek.tectonic.annotations.Default; +import com.dfsek.tectonic.annotations.Value; +import com.dfsek.tectonic.config.ConfigTemplate; +import com.dfsek.terra.config.builder.BiomeBuilder; +import net.minecraft.util.ResourceLocation; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +@SuppressWarnings("FieldMayBeFinal") +public class PostLoadCompatibilityOptions implements ConfigTemplate { + @Value("structures.inject-biome.exclude-biomes") + @Default + private Map> excludedPerBiomeStructures = new HashMap<>(); + + @Value("features.inject-biome.exclude-biomes") + @Default + private Map> excludedPerBiomeFeatures = new HashMap<>(); + + public Map> getExcludedPerBiomeFeatures() { + return excludedPerBiomeFeatures; + } + + public Map> getExcludedPerBiomeStructures() { + return excludedPerBiomeStructures; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/config/PreLoadCompatibilityOptions.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/config/PreLoadCompatibilityOptions.java new file mode 100644 index 000000000..575327b59 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/config/PreLoadCompatibilityOptions.java @@ -0,0 +1,52 @@ +package com.dfsek.terra.forge.config; + +import com.dfsek.tectonic.annotations.Default; +import com.dfsek.tectonic.annotations.Value; +import com.dfsek.tectonic.config.ConfigTemplate; +import net.minecraft.util.ResourceLocation; + +import java.util.HashSet; +import java.util.Set; + +@SuppressWarnings("FieldMayBeFinal") +public class PreLoadCompatibilityOptions implements ConfigTemplate { + @Value("features.inject-registry.enable") + @Default + private boolean doRegistryInjection = false; + + @Value("features.inject-biome.enable") + @Default + private boolean doBiomeInjection = false; + + @Value("features.inject-registry.excluded-features") + @Default + private Set excludedRegistryFeatures = new HashSet<>(); + + @Value("features.inject-biome.excluded-features") + @Default + private Set excludedBiomeFeatures = new HashSet<>(); + + @Value("structures.inject-biome.excluded-features") + @Default + private Set excludedBiomeStructures = new HashSet<>(); + + public boolean doBiomeInjection() { + return doBiomeInjection; + } + + public boolean doRegistryInjection() { + return doRegistryInjection; + } + + public Set getExcludedBiomeFeatures() { + return excludedBiomeFeatures; + } + + public Set getExcludedRegistryFeatures() { + return excludedRegistryFeatures; + } + + public Set getExcludedBiomeStructures() { + return excludedBiomeStructures; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/ForgeChunkGeneratorWrapper.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/ForgeChunkGeneratorWrapper.java new file mode 100644 index 000000000..3a34608b6 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/ForgeChunkGeneratorWrapper.java @@ -0,0 +1,195 @@ +package com.dfsek.terra.forge.generation; + +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.api.platform.world.generator.ChunkData; +import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; +import com.dfsek.terra.api.util.FastRandom; +import com.dfsek.terra.api.world.biome.UserDefinedBiome; +import com.dfsek.terra.api.world.generation.TerraChunkGenerator; +import com.dfsek.terra.api.world.locate.AsyncStructureFinder; +import com.dfsek.terra.config.pack.ConfigPack; +import com.dfsek.terra.forge.ForgeAdapter; +import com.dfsek.terra.forge.TerraForgePlugin; +import com.dfsek.terra.world.TerraWorld; +import com.dfsek.terra.world.generation.generators.DefaultChunkGenerator3D; +import com.dfsek.terra.world.generation.math.samplers.Sampler; +import com.dfsek.terra.world.population.items.TerraStructure; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.jafama.FastMath; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.util.SharedSeedRandom; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.registry.DynamicRegistries; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.Blockreader; +import net.minecraft.world.DimensionType; +import net.minecraft.world.IBlockReader; +import net.minecraft.world.IWorld; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeManager; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.GenerationStage; +import net.minecraft.world.gen.Heightmap; +import net.minecraft.world.gen.WorldGenRegion; +import net.minecraft.world.gen.feature.structure.Structure; +import net.minecraft.world.gen.feature.structure.StructureManager; +import net.minecraft.world.gen.feature.template.TemplateManager; +import net.minecraft.world.gen.settings.DimensionStructuresSettings; +import net.minecraft.world.server.ServerWorld; +import net.minecraft.world.spawner.WorldEntitySpawner; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class ForgeChunkGeneratorWrapper extends ChunkGenerator implements GeneratorWrapper { + private final long seed; + private final DefaultChunkGenerator3D delegate; + private final TerraBiomeSource biomeSource; + public static final Codec PACK_CODEC = (RecordCodecBuilder.create(config -> config.group( + Codec.STRING.fieldOf("pack").forGetter(pack -> pack.getTemplate().getID()) + ).apply(config, config.stable(TerraForgePlugin.getInstance().getConfigRegistry()::get)))); + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + TerraBiomeSource.CODEC.fieldOf("biome_source").forGetter(generator -> generator.biomeSource), + Codec.LONG.fieldOf("seed").stable().forGetter(generator -> generator.seed), + PACK_CODEC.fieldOf("pack").stable().forGetter(generator -> generator.pack)) + .apply(instance, instance.stable(ForgeChunkGeneratorWrapper::new))); + private final ConfigPack pack; + + public ConfigPack getPack() { + return pack; + } + + private DimensionType dimensionType; + + public ForgeChunkGeneratorWrapper(TerraBiomeSource biomeSource, long seed, ConfigPack configPack) { + super(biomeSource, new DimensionStructuresSettings(false)); + this.pack = configPack; + + this.delegate = new DefaultChunkGenerator3D(pack, TerraForgePlugin.getInstance()); + delegate.getMain().logger().info("Loading world with config pack " + pack.getTemplate().getID()); + this.biomeSource = biomeSource; + + this.seed = seed; + } + + @Override + protected @NotNull Codec codec() { + return CODEC; + } + + @Override + public @NotNull ChunkGenerator withSeed(long seed) { + return new ForgeChunkGeneratorWrapper((TerraBiomeSource) this.biomeSource.withSeed(seed), seed, pack); + } + + @Override + public void buildSurfaceAndBedrock(@NotNull WorldGenRegion p_225551_1_, @NotNull IChunk p_225551_2_) { + + } + + @Nullable + @Override + public BlockPos findNearestMapFeature(@NotNull ServerWorld world, @NotNull Structure feature, @NotNull BlockPos center, int radius, boolean skipExistingChunks) { + if(!pack.getTemplate().disableStructures()) { + String name = Objects.requireNonNull(Registry.STRUCTURE_FEATURE.getKey(feature)).toString(); + TerraWorld terraWorld = TerraForgePlugin.getInstance().getWorld((World) world); + TerraStructure located = pack.getStructure(pack.getTemplate().getLocatable().get(name)); + if(located != null) { + CompletableFuture result = new CompletableFuture<>(); + AsyncStructureFinder finder = new AsyncStructureFinder(terraWorld.getBiomeProvider(), located, ForgeAdapter.adapt(center).toLocation((World) world), 0, 500, location -> { + result.complete(ForgeAdapter.adapt(location)); + }, TerraForgePlugin.getInstance()); + finder.run(); // Do this synchronously. + try { + return result.get(); + } catch(InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + } + return super.findNearestMapFeature(world, feature, center, radius, skipExistingChunks); + } + + @Override + public boolean hasStronghold(@NotNull ChunkPos p_235952_1_) { + if(pack.getTemplate().vanillaStructures()) return super.hasStronghold(p_235952_1_); + return false; + } + + @Override + public void createStructures(@NotNull DynamicRegistries dynamicRegistries, @NotNull StructureManager manager, @NotNull IChunk chunk, @NotNull TemplateManager templateManager, long p_242707_5_) { + if(pack.getTemplate().vanillaStructures()) super.createStructures(dynamicRegistries, manager, chunk, templateManager, p_242707_5_); + } + + @Override + public void applyCarvers(long p_230350_1_, @NotNull BiomeManager biomeManager, @NotNull IChunk chunk, GenerationStage.@NotNull Carving carving) { + if(pack.getTemplate().vanillaCaves()) super.applyCarvers(p_230350_1_, biomeManager, chunk, carving); + } + + @Override + public void fillFromNoise(@NotNull IWorld world, @NotNull StructureManager p_230352_2_, @NotNull IChunk chunk) { + delegate.generateChunkData((World) world, new FastRandom(), chunk.getPos().x, chunk.getPos().z, (ChunkData) chunk); + } + + @Override + public int getBaseHeight(int x, int z, Heightmap.@NotNull Type p_222529_3_) { + TerraWorld world = TerraForgePlugin.getInstance().getWorld(dimensionType); + Sampler sampler = world.getConfig().getSamplerCache().getChunk(FastMath.floorDiv(x, 16), FastMath.floorDiv(z, 16)); + int cx = FastMath.floorMod(x, 16); + int cz = FastMath.floorMod(z, 16); + + int height = world.getWorld().getMaxHeight(); + + while(height >= 0 && sampler.sample(cx, height - 1, cz) < 0) height--; + + return height; + } + + @Override + public @NotNull IBlockReader getBaseColumn(int x, int z) { + TerraWorld world = TerraForgePlugin.getInstance().getWorld(dimensionType); + int height = getBaseHeight(x, z, Heightmap.Type.WORLD_SURFACE); + BlockState[] array = new BlockState[256]; + for(int y = 255; y >= 0; y--) { + if(y > height) { + if(y > ((UserDefinedBiome) world.getBiomeProvider().getBiome(x, z)).getConfig().getSeaLevel()) { + array[y] = Blocks.AIR.defaultBlockState(); + } else { + array[y] = Blocks.WATER.defaultBlockState(); + } + } else { + array[y] = Blocks.STONE.defaultBlockState(); + } + } + + return new Blockreader(array); + } + + @Override + public void spawnOriginalMobs(WorldGenRegion region) { + if(pack.getTemplate().vanillaMobs()) { + int cx = region.getCenterX(); + int cy = region.getCenterZ(); + Biome biome = region.getBiome((new ChunkPos(cx, cy)).getWorldPosition()); + SharedSeedRandom chunkRandom = new SharedSeedRandom(); + chunkRandom.setDecorationSeed(region.getSeed(), cx << 4, cy << 4); + WorldEntitySpawner.spawnMobsForChunkGeneration(region, biome, cx, cy, chunkRandom); + } + } + + @Override + public TerraChunkGenerator getHandle() { + return delegate; + } + + public void setDimensionType(DimensionType dimensionType) { + this.dimensionType = dimensionType; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/PopulatorFeature.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/PopulatorFeature.java new file mode 100644 index 000000000..e586e82d4 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/PopulatorFeature.java @@ -0,0 +1,30 @@ +package com.dfsek.terra.forge.generation; + +import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.api.platform.world.World; +import com.mojang.serialization.Codec; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ISeedReader; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.NoFeatureConfig; +import org.jetbrains.annotations.NotNull; + +import java.util.Random; + +/** + * Feature wrapper for Terra populator + */ +public class PopulatorFeature extends Feature { + public PopulatorFeature(Codec codec) { + super(codec); + } + + @Override + public boolean place(@NotNull ISeedReader world, @NotNull ChunkGenerator generator, @NotNull Random random, @NotNull BlockPos pos, @NotNull NoFeatureConfig config) { + ForgeChunkGeneratorWrapper gen = (ForgeChunkGeneratorWrapper) generator; + gen.getHandle().getPopulators().forEach(populator -> populator.populate((World) world, (Chunk) world)); + return true; + } + +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/TerraBiomeSource.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/TerraBiomeSource.java new file mode 100644 index 000000000..3220cfd58 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/TerraBiomeSource.java @@ -0,0 +1,57 @@ +package com.dfsek.terra.forge.generation; + +import com.dfsek.terra.api.world.biome.UserDefinedBiome; +import com.dfsek.terra.api.world.biome.provider.BiomeProvider; +import com.dfsek.terra.config.pack.ConfigPack; +import com.dfsek.terra.forge.ForgeUtil; +import com.dfsek.terra.forge.TerraForgePlugin; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryLookupCodec; +import net.minecraft.world.biome.Biome; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.stream.Collectors; + +public class TerraBiomeSource extends net.minecraft.world.biome.provider.BiomeProvider { + public static final Codec PACK_CODEC = (RecordCodecBuilder.create(config -> config.group( + Codec.STRING.fieldOf("pack").forGetter(pack -> pack.getTemplate().getID()) + ).apply(config, config.stable(TerraForgePlugin.getInstance().getConfigRegistry()::get)))); + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + RegistryLookupCodec.create(Registry.BIOME_REGISTRY).forGetter(source -> source.biomeRegistry), + Codec.LONG.fieldOf("seed").stable().forGetter(source -> source.seed), + PACK_CODEC.fieldOf("pack").stable().forGetter(source -> source.pack)) + .apply(instance, instance.stable(TerraBiomeSource::new))); + + private final Registry biomeRegistry; + private final long seed; + private final BiomeProvider grid; + private final ConfigPack pack; + + public TerraBiomeSource(Registry biomes, long seed, ConfigPack pack) { + super(biomes.stream().collect(Collectors.toList())); + this.biomeRegistry = biomes; + this.seed = seed; + this.grid = pack.getBiomeProviderBuilder().build(seed); + this.pack = pack; + } + + @Override + protected @NotNull Codec codec() { + return CODEC; + } + + @Override + public net.minecraft.world.biome.provider.@NotNull BiomeProvider withSeed(long seed) { + return new TerraBiomeSource(this.biomeRegistry, seed, pack); + } + + @Override + public @NotNull Biome getNoiseBiome(int biomeX, int biomeY, int biomeZ) { + UserDefinedBiome biome = (UserDefinedBiome) grid.getBiome(biomeX << 2, biomeZ << 2); + return Objects.requireNonNull(biomeRegistry.get(new ResourceLocation("terra", ForgeUtil.createBiomeID(pack, biome.getID())))); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/TerraGeneratorType.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/TerraGeneratorType.java new file mode 100644 index 000000000..91b8fc852 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/TerraGeneratorType.java @@ -0,0 +1,24 @@ +package com.dfsek.terra.forge.generation; + +import com.dfsek.terra.config.pack.ConfigPack; +import net.minecraft.client.gui.screen.BiomeGeneratorTypeScreens; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.DimensionSettings; +import org.jetbrains.annotations.NotNull; + +public class TerraGeneratorType extends BiomeGeneratorTypeScreens { + private final ConfigPack pack; + + public TerraGeneratorType(ConfigPack pack) { + super(new StringTextComponent("Terra:" + pack.getTemplate().getID())); + this.pack = pack; + } + + @Override + protected @NotNull ChunkGenerator generator(@NotNull Registry biomeRegistry, @NotNull Registry chunkGeneratorSettingsRegistry, long seed) { + return new ForgeChunkGeneratorWrapper(new TerraBiomeSource(biomeRegistry, seed, pack), seed, pack); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/handle/ForgeItemHandle.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/handle/ForgeItemHandle.java new file mode 100644 index 000000000..e2dc4185a --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/handle/ForgeItemHandle.java @@ -0,0 +1,35 @@ +package com.dfsek.terra.forge.handle; + +import com.dfsek.terra.api.platform.handle.ItemHandle; +import com.dfsek.terra.api.platform.inventory.Item; +import com.dfsek.terra.api.platform.inventory.item.Enchantment; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.command.arguments.ItemArgument; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.registries.ForgeRegistries; + +import java.util.Set; +import java.util.stream.Collectors; + +public class ForgeItemHandle implements ItemHandle { + + @Override + public Item createItem(String data) { + try { + return (Item) new ItemArgument().parse(new StringReader(data)).getItem(); + } catch(CommandSyntaxException e) { + throw new IllegalArgumentException("Invalid item data \"" + data + "\"", e); + } + } + + @Override + public Enchantment getEnchantment(String id) { + return (Enchantment) ForgeRegistries.ENCHANTMENTS.getValue(ResourceLocation.tryParse(id)); + } + + @Override + public Set getEnchantments() { + return ForgeRegistries.ENCHANTMENTS.getEntries().stream().map(entry -> (Enchantment) entry.getValue()).collect(Collectors.toSet()); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/handle/ForgeWorldHandle.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/handle/ForgeWorldHandle.java new file mode 100644 index 000000000..96c2e37fa --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/handle/ForgeWorldHandle.java @@ -0,0 +1,36 @@ +package com.dfsek.terra.forge.handle; + +import com.dfsek.terra.api.platform.entity.EntityType; +import com.dfsek.terra.api.platform.handle.WorldHandle; +import com.dfsek.terra.forge.ForgeAdapter; +import com.dfsek.terra.forge.block.ForgeBlockData; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.block.BlockState; +import net.minecraft.command.arguments.BlockStateParser; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.registries.ForgeRegistries; + +import java.util.Locale; + +public class ForgeWorldHandle implements WorldHandle { + + @Override + public ForgeBlockData createBlockData(String data) { + BlockStateParser parser = new BlockStateParser(new StringReader(data), true); + try { + BlockState state = parser.parse(true).getState(); + if(state == null) throw new IllegalArgumentException("Invalid data: " + data); + return ForgeAdapter.adapt(state); + } catch(CommandSyntaxException e) { + throw new IllegalArgumentException(e); + } + } + + @Override + public EntityType getEntity(String id) { + ResourceLocation identifier = ResourceLocation.tryParse(id); + if(identifier == null) identifier = ResourceLocation.tryParse("minecraft:" + id.toLowerCase(Locale.ROOT)); + return (EntityType) ForgeRegistries.ENTITIES.getValue(identifier); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/listener/ForgeListener.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/listener/ForgeListener.java new file mode 100644 index 000000000..35f0d9d63 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/listener/ForgeListener.java @@ -0,0 +1,81 @@ +package com.dfsek.terra.forge.listener; + +import com.dfsek.terra.api.command.CommandManager; +import com.dfsek.terra.api.command.exception.CommandException; +import com.dfsek.terra.api.platform.CommandSender; +import com.dfsek.terra.api.platform.entity.Entity; +import com.dfsek.terra.forge.TerraForgePlugin; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.command.CommandSource; +import net.minecraft.util.text.StringTextComponent; +import net.minecraftforge.event.RegisterCommandsEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal; +import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument; + +@Mod.EventBusSubscriber(modid = "terra", bus = Mod.EventBusSubscriber.Bus.FORGE) +public class ForgeListener { + private static final TerraForgePlugin INSTANCE = TerraForgePlugin.getInstance(); + + @SuppressWarnings({"unchecked", "rawtypes"}) + @SubscribeEvent + public static void registerCommands(RegisterCommandsEvent event) { + int max = INSTANCE.getManager().getMaxArgumentDepth(); + RequiredArgumentBuilder arg = argument("arg" + (max - 1), StringArgumentType.word()); + for(int i = 0; i < max; i++) { + RequiredArgumentBuilder next = argument("arg" + (max - i - 1), StringArgumentType.word()); + + arg = next.then(assemble(arg, INSTANCE.getManager())); + } + + event.getDispatcher().register(literal("terra").executes(context -> 1).then((ArgumentBuilder) assemble(arg, INSTANCE.getManager()))); + event.getDispatcher().register(literal("te").executes(context -> 1).then((ArgumentBuilder) assemble(arg, INSTANCE.getManager()))); + } + + public static RequiredArgumentBuilder assemble(RequiredArgumentBuilder in, CommandManager manager) { + return in.suggests((context, builder) -> { + List args = parseCommand(context.getInput()); + CommandSender sender = (CommandSender) context.getSource(); + try { + sender = (Entity) context.getSource().getEntityOrException(); + } catch(CommandSyntaxException ignore) { + } + try { + manager.tabComplete(args.remove(0), sender, args).forEach(builder::suggest); + } catch(CommandException e) { + sender.sendMessage(e.getMessage()); + } + return builder.buildFuture(); + }).executes(context -> { + List args = parseCommand(context.getInput()); + try { + CommandSender sender = (CommandSender) context.getSource(); + try { + sender = (Entity) context.getSource().getEntityOrException(); + } catch(CommandSyntaxException ignore) { + } + manager.execute(args.remove(0), sender, args); + } catch(CommandException e) { + context.getSource().sendFailure(new StringTextComponent(e.getMessage())); + } + return 1; + }); + } + + private static List parseCommand(String command) { + if(command.startsWith("/terra ")) command = command.substring("/terra ".length()); + else if(command.startsWith("/te ")) command = command.substring("/te ".length()); + List c = new ArrayList<>(Arrays.asList(command.split(" "))); + if(command.endsWith(" ")) c.add(""); + return c; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/listener/RegistryListener.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/listener/RegistryListener.java new file mode 100644 index 000000000..19847bf0a --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/listener/RegistryListener.java @@ -0,0 +1,15 @@ +package com.dfsek.terra.forge.listener; + +import com.dfsek.terra.forge.TerraForgePlugin; +import net.minecraft.world.gen.feature.Feature; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) +public class RegistryListener { + @SubscribeEvent + public static void registerPop(RegistryEvent.Register> event) { + event.getRegistry().register(TerraForgePlugin.POPULATOR_FEATURE); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/DimensionGeneratorSettingsMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/DimensionGeneratorSettingsMixin.java new file mode 100644 index 000000000..ca899217a --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/DimensionGeneratorSettingsMixin.java @@ -0,0 +1,64 @@ +package com.dfsek.terra.forge.mixin; + +import com.dfsek.terra.config.pack.ConfigPack; +import com.dfsek.terra.forge.TerraForgePlugin; +import com.dfsek.terra.forge.generation.ForgeChunkGeneratorWrapper; +import com.dfsek.terra.forge.generation.TerraBiomeSource; +import com.google.common.base.MoreObjects; +import net.minecraft.util.registry.DynamicRegistries; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.SimpleRegistry; +import net.minecraft.world.Dimension; +import net.minecraft.world.DimensionType; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.gen.DimensionSettings; +import net.minecraft.world.gen.settings.DimensionGeneratorSettings; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Properties; +import java.util.Random; + +@Mixin(DimensionGeneratorSettings.class) +public abstract class DimensionGeneratorSettingsMixin { + @Inject(method = "create(Lnet/minecraft/util/registry/DynamicRegistries;Ljava/util/Properties;)Lnet/minecraft/world/gen/settings/DimensionGeneratorSettings;", at = @At("HEAD"), cancellable = true) + private static void fromProperties(DynamicRegistries dynamicRegistries, Properties properties, CallbackInfoReturnable cir) { + if(properties.get("level-type") == null) { + return; + } + + String prop = properties.get("level-type").toString().trim(); + if(prop.startsWith("Terra")) { + String seed = (String) MoreObjects.firstNonNull(properties.get("level-seed"), ""); + long l = new Random().nextLong(); + if(!seed.isEmpty()) { + try { + long m = Long.parseLong(seed); + if(m != 0L) { + l = m; + } + } catch(NumberFormatException exception) { + l = seed.hashCode(); + } + } + + String generate_structures = (String) properties.get("generate-structures"); + boolean generateStructures = generate_structures == null || Boolean.parseBoolean(generate_structures); + Registry dimensionTypes = dynamicRegistries.registryOrThrow(Registry.DIMENSION_TYPE_REGISTRY); + Registry biomes = dynamicRegistries.registryOrThrow(Registry.BIOME_REGISTRY); + Registry chunkGeneratorSettings = dynamicRegistries.registryOrThrow(Registry.NOISE_GENERATOR_SETTINGS_REGISTRY); + SimpleRegistry dimensionOptions = DimensionType.defaultDimensions(dimensionTypes, biomes, chunkGeneratorSettings, l); + + prop = prop.substring(prop.indexOf(":") + 1); + + ConfigPack pack = TerraForgePlugin.getInstance().getConfigRegistry().get(prop); + + if(pack == null) throw new IllegalArgumentException("No such pack " + prop); + + TerraForgePlugin.getInstance().logger().info("Using config pack " + pack.getTemplate().getID()); + cir.setReturnValue(new DimensionGeneratorSettings(l, generateStructures, false, DimensionGeneratorSettings.withOverworld(dimensionTypes, dimensionOptions, new ForgeChunkGeneratorWrapper(new TerraBiomeSource(biomes, l, pack), l, pack)))); + } + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/ServerWorldMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/ServerWorldMixin.java new file mode 100644 index 000000000..39cdce89d --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/ServerWorldMixin.java @@ -0,0 +1,37 @@ +package com.dfsek.terra.forge.mixin; + +import com.dfsek.terra.api.util.generic.pair.Pair; +import com.dfsek.terra.forge.TerraForgePlugin; +import com.dfsek.terra.forge.generation.ForgeChunkGeneratorWrapper; +import com.dfsek.terra.world.TerraWorld; +import net.minecraft.world.DimensionType; +import net.minecraft.world.World; +import net.minecraft.world.server.ServerChunkProvider; +import net.minecraft.world.server.ServerWorld; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(ServerWorld.class) +public abstract class ServerWorldMixin { + @Shadow + @Final + private ServerChunkProvider chunkSource; + + @Shadow + protected abstract void initCapabilities(); + + @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/server/ServerWorld;initCapabilities()V")) + public void injectConstructor(ServerWorld serverWorld) { + if(chunkSource.getGenerator() instanceof ForgeChunkGeneratorWrapper) { + ForgeChunkGeneratorWrapper chunkGeneratorWrapper = (ForgeChunkGeneratorWrapper) chunkSource.getGenerator(); + DimensionType dimensionType = ((World) (Object) this).dimensionType(); + TerraForgePlugin.getInstance().getWorldMap().put(dimensionType, Pair.of((ServerWorld) (Object) this, new TerraWorld((com.dfsek.terra.api.platform.world.World) this, chunkGeneratorWrapper.getPack(), TerraForgePlugin.getInstance()))); + chunkGeneratorWrapper.setDimensionType(dimensionType); + TerraForgePlugin.getInstance().logger().info("Registered world " + this + " to dimension type " + dimensionType); + } + initCapabilities(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/AbstractSpawnerAccessor.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/AbstractSpawnerAccessor.java new file mode 100644 index 000000000..5783c350f --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/AbstractSpawnerAccessor.java @@ -0,0 +1,12 @@ +package com.dfsek.terra.forge.mixin.access; + +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.spawner.AbstractSpawner; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(AbstractSpawner.class) +public interface AbstractSpawnerAccessor { + @Invoker("getEntityId") + ResourceLocation callGetEntityId(); +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/BiomeGeneratorTypeScreensAccessor.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/BiomeGeneratorTypeScreensAccessor.java new file mode 100644 index 000000000..b7d7363b5 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/BiomeGeneratorTypeScreensAccessor.java @@ -0,0 +1,21 @@ +package com.dfsek.terra.forge.mixin.access; + +import net.minecraft.client.gui.screen.BiomeGeneratorTypeScreens; +import net.minecraft.util.text.ITextComponent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(BiomeGeneratorTypeScreens.class) +public interface BiomeGeneratorTypeScreensAccessor { + @Accessor("PRESETS") + static List getPresets() { + throw new UnsupportedOperationException(); + } + + @Mutable + @Accessor + void setDescription(ITextComponent description); +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/BiomeMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/BiomeMixin.java new file mode 100644 index 000000000..317893290 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/BiomeMixin.java @@ -0,0 +1,14 @@ +package com.dfsek.terra.forge.mixin.implementations; + +import net.minecraft.world.biome.Biome; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(Biome.class) +@Implements(@Interface(iface = com.dfsek.terra.api.platform.world.Biome.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class BiomeMixin { + public Object terra$getHandle() { + return this; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/ChunkGeneratorMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/ChunkGeneratorMixin.java new file mode 100644 index 000000000..14fa0e2c5 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/ChunkGeneratorMixin.java @@ -0,0 +1,14 @@ +package com.dfsek.terra.forge.mixin.implementations; + +import net.minecraft.world.gen.ChunkGenerator; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ChunkGenerator.class) +@Implements(@Interface(iface = com.dfsek.terra.api.platform.world.generator.ChunkGenerator.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ChunkGeneratorMixin { + public Object terra$getHandle() { + return this; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/ConfiguredFeatureMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/ConfiguredFeatureMixin.java new file mode 100644 index 000000000..f0e1d0b4a --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/ConfiguredFeatureMixin.java @@ -0,0 +1,40 @@ +package com.dfsek.terra.forge.mixin.implementations; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.world.Tree; +import com.dfsek.terra.api.util.collections.MaterialSet; +import com.dfsek.terra.forge.TerraForgePlugin; +import com.dfsek.terra.profiler.ProfileFrame; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ISeedReader; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Random; + +@Mixin(ConfiguredFeature.class) +@Implements(@Interface(iface = Tree.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ConfiguredFeatureMixin { + + @Shadow + public abstract boolean place(ISeedReader p_242765_1_, ChunkGenerator p_242765_2_, Random p_242765_3_, BlockPos p_242765_4_); + + @SuppressWarnings({"try"}) + public boolean terra$plant(Location l, Random r) { + try(ProfileFrame ignore = TerraForgePlugin.getInstance().getProfiler().profile("forge_tree")) { + ISeedReader world = ((ISeedReader) l.getWorld()); + ChunkGenerator generatorWrapper = (ChunkGenerator) l.getWorld().getGenerator(); + return place(world, generatorWrapper, r, new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ())); + } + } + + public MaterialSet terra$getSpawnable() { + return MaterialSet.get(TerraForgePlugin.getInstance().getWorldHandle().createBlockData("minecraft:grass_block"), + TerraForgePlugin.getInstance().getWorldHandle().createBlockData("minecraft:podzol"), + TerraForgePlugin.getInstance().getWorldHandle().createBlockData("minecraft:mycelium")); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/BlockMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/BlockMixin.java new file mode 100644 index 000000000..ee4bf13ed --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/BlockMixin.java @@ -0,0 +1,36 @@ +package com.dfsek.terra.forge.mixin.implementations.block; + +import com.dfsek.terra.api.platform.block.BlockData; +import com.dfsek.terra.api.platform.block.BlockType; +import com.dfsek.terra.forge.ForgeAdapter; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(Block.class) +@Implements(@Interface(iface = BlockType.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class BlockMixin { + @Shadow + private BlockState defaultBlockState; + + public Object terra$getHandle() { + return this; + } + + public BlockData terra$getDefaultData() { + return ForgeAdapter.adapt(defaultBlockState); + } + + public boolean terra$isSolid() { + return defaultBlockState.canOcclude(); + } + + @SuppressWarnings("ConstantConditions") + public boolean terra$isWater() { + return ((Object) this) == Blocks.WATER; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/TileEntityMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/TileEntityMixin.java new file mode 100644 index 000000000..2f7bd3918 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/TileEntityMixin.java @@ -0,0 +1,63 @@ +package com.dfsek.terra.forge.mixin.implementations.block; + +import com.dfsek.terra.api.platform.block.Block; +import com.dfsek.terra.api.platform.block.BlockData; +import com.dfsek.terra.api.platform.block.state.BlockState; +import com.dfsek.terra.forge.ForgeAdapter; +import com.dfsek.terra.forge.block.ForgeBlock; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import javax.annotation.Nullable; + +@Mixin(TileEntity.class) +@Implements(@Interface(iface = BlockState.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class TileEntityMixin { + @Shadow + protected BlockPos worldPosition; + + @Shadow + @Nullable + protected World level; + + @Shadow + @Nullable + private net.minecraft.block.BlockState blockState; + + @Shadow + public abstract boolean hasLevel(); + + public Object terra$getHandle() { + return this; + } + + public Block terra$getBlock() { + return new ForgeBlock(worldPosition, level); + } + + public int terra$getX() { + return worldPosition.getX(); + } + + public int terra$getY() { + return worldPosition.getY(); + } + + public int terra$getZ() { + return worldPosition.getZ(); + } + + public BlockData terra$getBlockData() { + return ForgeAdapter.adapt(blockState); + } + + public boolean terra$update(boolean applyPhysics) { + if(hasLevel()) level.getChunk(worldPosition).setBlockEntity(worldPosition, (TileEntity) (Object) this); + return true; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/state/LockableLootTileEntityMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/state/LockableLootTileEntityMixin.java new file mode 100644 index 000000000..977309616 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/state/LockableLootTileEntityMixin.java @@ -0,0 +1,21 @@ +package com.dfsek.terra.forge.mixin.implementations.block.state; + +import com.dfsek.terra.api.platform.block.state.Container; +import com.dfsek.terra.api.platform.inventory.Inventory; +import com.dfsek.terra.forge.mixin.implementations.block.TileEntityMixin; +import net.minecraft.tileentity.LockableLootTileEntity; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(LockableLootTileEntity.class) +@Implements(@Interface(iface = Container.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class LockableLootTileEntityMixin extends TileEntityMixin { + public Inventory terra$getInventory() { + return (Inventory) this; + } + + public Object terra$getHandle() { + return this; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/state/MobSpawnerTileEntityMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/state/MobSpawnerTileEntityMixin.java new file mode 100644 index 000000000..1f73b7583 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/state/MobSpawnerTileEntityMixin.java @@ -0,0 +1,120 @@ +package com.dfsek.terra.forge.mixin.implementations.block.state; + +import com.dfsek.terra.api.platform.block.state.MobSpawner; +import com.dfsek.terra.api.platform.block.state.SerialState; +import com.dfsek.terra.api.platform.entity.EntityType; +import com.dfsek.terra.forge.TerraForgePlugin; +import com.dfsek.terra.forge.mixin.access.AbstractSpawnerAccessor; +import com.dfsek.terra.forge.mixin.implementations.block.TileEntityMixin; +import net.minecraft.tileentity.MobSpawnerTileEntity; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.spawner.AbstractSpawner; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(MobSpawnerTileEntity.class) +@Implements(@Interface(iface = MobSpawner.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class MobSpawnerTileEntityMixin extends TileEntityMixin { + @Shadow + public abstract AbstractSpawner getSpawner(); + + public EntityType terra$getSpawnedType() { + return (EntityType) Registry.ENTITY_TYPE.get(((AbstractSpawnerAccessor) getSpawner()).callGetEntityId()); + } + + public void terra$setSpawnedType(@NotNull EntityType creatureType) { + getSpawner().setEntityId((net.minecraft.entity.EntityType) creatureType); + } + + public int terra$getDelay() { + return 0; + } + + public void terra$setDelay(int delay) { + + } + + public int terra$getMinSpawnDelay() { + return 0; + } + + public void terra$setMinSpawnDelay(int delay) { + + } + + public int terra$getMaxSpawnDelay() { + return 0; + } + + public void terra$setMaxSpawnDelay(int delay) { + + } + + public int terra$getSpawnCount() { + return 0; + } + + public void terra$setSpawnCount(int spawnCount) { + + } + + public int terra$getMaxNearbyEntities() { + return 0; + } + + public void terra$setMaxNearbyEntities(int maxNearbyEntities) { + + } + + public int terra$getRequiredPlayerRange() { + return 0; + } + + public void terra$setRequiredPlayerRange(int requiredPlayerRange) { + + } + + public int terra$getSpawnRange() { + return 0; + } + + public void terra$setSpawnRange(int spawnRange) { + + } + + public void terra$applyState(String state) { + SerialState.parse(state).forEach((k, v) -> { + switch(k) { + case "type": + terra$setSpawnedType(TerraForgePlugin.getInstance().getWorldHandle().getEntity(v)); + return; + case "delay": + terra$setDelay(Integer.parseInt(v)); + return; + case "min_delay": + terra$setMinSpawnDelay(Integer.parseInt(v)); + return; + case "max_delay": + terra$setMaxSpawnDelay(Integer.parseInt(v)); + return; + case "spawn_count": + terra$setSpawnCount(Integer.parseInt(v)); + return; + case "spawn_range": + terra$setSpawnRange(Integer.parseInt(v)); + return; + case "max_nearby": + terra$setMaxNearbyEntities(Integer.parseInt(v)); + return; + case "required_player_range": + terra$setRequiredPlayerRange(Integer.parseInt(v)); + return; + default: + throw new IllegalArgumentException("Invalid property: " + k); + } + }); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/state/SignTileEntityMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/state/SignTileEntityMixin.java new file mode 100644 index 000000000..40ec0368c --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/block/state/SignTileEntityMixin.java @@ -0,0 +1,48 @@ +package com.dfsek.terra.forge.mixin.implementations.block.state; + +import com.dfsek.terra.api.platform.block.state.SerialState; +import com.dfsek.terra.api.platform.block.state.Sign; +import net.minecraft.tileentity.SignTileEntity; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(SignTileEntity.class) +@Implements(@Interface(iface = Sign.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class SignTileEntityMixin { + @Shadow + @Final + private ITextComponent[] messages; + + @Shadow + public abstract void setMessage(int p_212365_1_, ITextComponent p_212365_2_); + + public @NotNull String[] terra$getLines() { + String[] lines = new String[messages.length]; + for(int i = 0; i < messages.length; i++) { + lines[i] = messages[i].getString(); + } + return lines; + } + + public @NotNull String terra$getLine(int index) throws IndexOutOfBoundsException { + + return messages[index].getString(); + } + + public void terra$setLine(int index, @NotNull String line) throws IndexOutOfBoundsException { + setMessage(index, new StringTextComponent(line)); + } + + public void terra$applyState(String state) { + SerialState.parse(state).forEach((k, v) -> { + if(!k.startsWith("text")) throw new IllegalArgumentException("Invalid property: " + k); + terra$setLine(Integer.parseInt(k.substring(4)), v); + }); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/chunk/ChunkMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/chunk/ChunkMixin.java new file mode 100644 index 000000000..6d93b8cef --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/chunk/ChunkMixin.java @@ -0,0 +1,54 @@ +package com.dfsek.terra.forge.mixin.implementations.chunk; + +import com.dfsek.terra.api.platform.block.Block; +import com.dfsek.terra.api.platform.block.BlockData; +import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.forge.block.ForgeBlock; +import com.dfsek.terra.forge.block.ForgeBlockData; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.chunk.IChunk; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(net.minecraft.world.chunk.Chunk.class) +@Implements(@Interface(iface = Chunk.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ChunkMixin { + + @Shadow + @Final + private net.minecraft.world.World level; + + public int terra$getX() { + return ((IChunk) this).getPos().x; + } + + public int terra$getZ() { + return ((IChunk) this).getPos().z; + } + + public World terra$getWorld() { + return (World) level; + } + + public Block terra$getBlock(int x, int y, int z) { + BlockPos pos = new BlockPos(x + (terra$getX() << 4), y, z + (terra$getZ() << 4)); + return new ForgeBlock(pos, level); + } + + public @NotNull BlockData terra$getBlockData(int x, int y, int z) { + return terra$getBlock(x, y, z).getBlockData(); + } + + public void terra$setBlock(int x, int y, int z, @NotNull BlockData blockData) { + ((IChunk) this).setBlockState(new BlockPos(x, y, z), ((ForgeBlockData) blockData).getHandle(), false); + } + + public Object terra$getHandle() { + return this; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/chunk/WorldGenRegionMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/chunk/WorldGenRegionMixin.java new file mode 100644 index 000000000..7844a9637 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/chunk/WorldGenRegionMixin.java @@ -0,0 +1,57 @@ +package com.dfsek.terra.forge.mixin.implementations.chunk; + +import com.dfsek.terra.api.platform.block.Block; +import com.dfsek.terra.api.platform.block.BlockData; +import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.forge.block.ForgeBlock; +import com.dfsek.terra.forge.block.ForgeBlockData; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.gen.WorldGenRegion; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(WorldGenRegion.class) +@Implements(@Interface(iface = Chunk.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class WorldGenRegionMixin { + @Final + @Shadow + private int x; + + @Final + @Shadow + private int z; + + public int terra$getX() { + return x; + } + + public int terra$getZ() { + return z; + } + + public World terra$getWorld() { + return (World) this; + } + + public Block terra$getBlock(int x, int y, int z) { + BlockPos pos = new BlockPos(x + (this.x << 4), y, z + (this.z << 4)); + return new ForgeBlock(pos, (WorldGenRegion) (Object) this); + } + + public @NotNull BlockData terra$getBlockData(int x, int y, int z) { + return terra$getBlock(x, y, z).getBlockData(); + } + + public void terra$setBlock(int x, int y, int z, @NotNull BlockData blockData) { + ((WorldGenRegion) (Object) this).setBlock(new BlockPos(x + (this.x << 4), y, z + (this.z << 4)), ((ForgeBlockData) blockData).getHandle(), 0); + } + + public Object terra$getHandle() { + return this; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/chunk/data/ChunkPrimerMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/chunk/data/ChunkPrimerMixin.java new file mode 100644 index 000000000..5e5404406 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/chunk/data/ChunkPrimerMixin.java @@ -0,0 +1,37 @@ +package com.dfsek.terra.forge.mixin.implementations.chunk.data; + +import com.dfsek.terra.api.platform.block.BlockData; +import com.dfsek.terra.api.platform.world.generator.ChunkData; +import com.dfsek.terra.forge.block.ForgeBlockData; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.chunk.ChunkPrimer; +import net.minecraft.world.chunk.IChunk; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ChunkPrimer.class) +@Implements(@Interface(iface = ChunkData.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ChunkPrimerMixin { + @Shadow + public abstract BlockState getBlockState(BlockPos pos); + + public @NotNull BlockData terra$getBlockData(int x, int y, int z) { + return new ForgeBlockData(getBlockState(new BlockPos(x, y, z))); + } + + public void terra$setBlock(int x, int y, int z, @NotNull BlockData blockData) { + ((IChunk) this).setBlockState(new BlockPos(x, y, z), ((ForgeBlockData) blockData).getHandle(), false); + } + + public Object terra$getHandle() { + return this; + } + + public int terra$getMaxHeight() { + return 255; // TODO: 1.17 - Implement dynamic height. + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/entity/CommandSourceMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/entity/CommandSourceMixin.java new file mode 100644 index 000000000..83df7b48c --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/entity/CommandSourceMixin.java @@ -0,0 +1,25 @@ +package com.dfsek.terra.forge.mixin.implementations.entity; + +import com.dfsek.terra.api.platform.CommandSender; +import net.minecraft.command.CommandSource; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(CommandSource.class) +@Implements(@Interface(iface = CommandSender.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class CommandSourceMixin { + @Shadow + public abstract void sendSuccess(ITextComponent p_197030_1_, boolean p_197030_2_); + + public void terra$sendMessage(String message) { + sendSuccess(new StringTextComponent(message), true); + } + + public Object terra$getHandle() { + return this; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/entity/EntityMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/entity/EntityMixin.java new file mode 100644 index 000000000..7cc684c35 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/entity/EntityMixin.java @@ -0,0 +1,52 @@ +package com.dfsek.terra.forge.mixin.implementations.entity; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.forge.ForgeAdapter; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.UUID; + +@Mixin(Entity.class) +@Implements(@Interface(iface = com.dfsek.terra.api.platform.entity.Entity.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class EntityMixin { + @Shadow + public net.minecraft.world.World level; + + @Shadow + private BlockPos blockPosition; + + @Shadow + public abstract void teleportTo(double destX, double destY, double destZ); + + + @Shadow + public abstract void sendMessage(ITextComponent p_145747_1_, UUID p_145747_2_); + + public Object terra$getHandle() { + return this; + } + + public Location terra$getLocation() { + return new Location((World) level, ForgeAdapter.adapt(blockPosition)); + } + + public void terra$setLocation(Location location) { + teleportTo(location.getX(), location.getY(), location.getZ()); + } + + public World terra$getWorld() { + return (World) level; + } + + public void terra$sendMessage(String message) { + sendMessage(new StringTextComponent(message), UUID.randomUUID()); // TODO: look into how this actually works and make it less jank + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/entity/EntityTypeMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/entity/EntityTypeMixin.java new file mode 100644 index 000000000..e7bb15a70 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/entity/EntityTypeMixin.java @@ -0,0 +1,14 @@ +package com.dfsek.terra.forge.mixin.implementations.entity; + +import net.minecraft.entity.EntityType; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(EntityType.class) +@Implements(@Interface(iface = com.dfsek.terra.api.platform.entity.EntityType.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class EntityTypeMixin { + public Object terra$getHandle() { + return this; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/entity/PlayerEntityMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/entity/PlayerEntityMixin.java new file mode 100644 index 000000000..7ee5d6731 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/entity/PlayerEntityMixin.java @@ -0,0 +1,13 @@ +package com.dfsek.terra.forge.mixin.implementations.entity; + +import com.dfsek.terra.api.platform.entity.Player; +import net.minecraft.entity.player.PlayerEntity; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(PlayerEntity.class) +@Implements(@Interface(iface = Player.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class PlayerEntityMixin extends EntityMixin { + +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/LockableTileEntityMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/LockableTileEntityMixin.java new file mode 100644 index 000000000..06a279ed3 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/LockableTileEntityMixin.java @@ -0,0 +1,32 @@ +package com.dfsek.terra.forge.mixin.implementations.inventory; + +import com.dfsek.terra.api.platform.inventory.Inventory; +import com.dfsek.terra.api.platform.inventory.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.tileentity.LockableTileEntity; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(LockableTileEntity.class) +@Implements(@Interface(iface = Inventory.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public class LockableTileEntityMixin { + public Object terra$getHandle() { + return this; + } + + public int terra$getSize() { + return ((LockableTileEntity) (Object) this).getContainerSize(); + } + + @SuppressWarnings("ConstantConditions") + public ItemStack terra$getItem(int slot) { + net.minecraft.item.ItemStack itemStack = ((LockableTileEntity) (Object) this).getItem(slot); + return itemStack.getItem() == Items.AIR ? null : (ItemStack) (Object) itemStack; + } + + @SuppressWarnings("ConstantConditions") + public void terra$setItem(int slot, ItemStack newStack) { + ((LockableTileEntity) (Object) this).setItem(slot, (net.minecraft.item.ItemStack) (Object) newStack); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/item/ItemMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/item/ItemMixin.java new file mode 100644 index 000000000..a7efd1706 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/item/ItemMixin.java @@ -0,0 +1,28 @@ +package com.dfsek.terra.forge.mixin.implementations.inventory.item; + +import com.dfsek.terra.api.platform.inventory.ItemStack; +import net.minecraft.item.Item; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(Item.class) +@Implements(@Interface(iface = com.dfsek.terra.api.platform.inventory.Item.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ItemMixin { + @Shadow + public abstract int getMaxDamage(); + + public Object terra$getHandle() { + return this; + } + + @SuppressWarnings("ConstantConditions") + public ItemStack terra$newItemStack(int amount) { + return (ItemStack) (Object) new net.minecraft.item.ItemStack((Item) (Object) this, amount); + } + + public double terra$getMaxDurability() { + return getMaxDamage(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/item/ItemStackMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/item/ItemStackMixin.java new file mode 100644 index 000000000..714dcab02 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/item/ItemStackMixin.java @@ -0,0 +1,62 @@ +package com.dfsek.terra.forge.mixin.implementations.inventory.item; + +import com.dfsek.terra.api.platform.inventory.Item; +import com.dfsek.terra.api.platform.inventory.item.ItemMeta; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ItemStack.class) +@Implements(@Interface(iface = com.dfsek.terra.api.platform.inventory.ItemStack.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ItemStackMixin { + @Shadow + public abstract int getCount(); + + @Shadow + public abstract void setCount(int count); + + @Shadow + public abstract net.minecraft.item.Item getItem(); + + + @Shadow + public abstract boolean isDamageableItem(); + + @Shadow + public abstract void setTag(@Nullable CompoundNBT p_77982_1_); + + public int terra$getAmount() { + return getCount(); + } + + public void terra$setAmount(int i) { + setCount(i); + } + + public Item terra$getType() { + return (Item) getItem(); + } + + public ItemMeta terra$getItemMeta() { + return (ItemMeta) this; + } + + @SuppressWarnings("ConstantConditions") + public void terra$setItemMeta(ItemMeta meta) { + setTag(((ItemStack) (Object) meta).getTag()); + } + + public Object terra$getHandle() { + return this; + } + + @Intrinsic + public boolean terra$isDamageable() { + return isDamageableItem(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/meta/EnchantmentMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/meta/EnchantmentMixin.java new file mode 100644 index 000000000..69bbfecaa --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/meta/EnchantmentMixin.java @@ -0,0 +1,39 @@ +package com.dfsek.terra.forge.mixin.implementations.inventory.meta; + +import com.dfsek.terra.api.platform.inventory.ItemStack; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.util.registry.Registry; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Objects; + +@Mixin(Enchantment.class) +@Implements(@Interface(iface = com.dfsek.terra.api.platform.inventory.item.Enchantment.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class EnchantmentMixin { + + @Shadow + public abstract boolean isCompatibleWith(Enchantment p_191560_1_); + + @Shadow + public abstract boolean canEnchant(net.minecraft.item.ItemStack p_92089_1_); + + public Object terra$getHandle() { + return this; + } + + @SuppressWarnings("ConstantConditions") + public boolean terra$canEnchantItem(ItemStack itemStack) { + return canEnchant((net.minecraft.item.ItemStack) (Object) itemStack); + } + + public String terra$getID() { + return Objects.requireNonNull(Registry.ENCHANTMENT.getId((Enchantment) (Object) this)).toString(); + } + + public boolean terra$conflictsWith(com.dfsek.terra.api.platform.inventory.item.Enchantment other) { + return !isCompatibleWith((Enchantment) other); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/meta/ItemStackDamageableMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/meta/ItemStackDamageableMixin.java new file mode 100644 index 000000000..8a1b438f5 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/meta/ItemStackDamageableMixin.java @@ -0,0 +1,36 @@ +package com.dfsek.terra.forge.mixin.implementations.inventory.meta; + +import com.dfsek.terra.api.platform.inventory.item.Damageable; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ItemStack.class) +@Implements(@Interface(iface = Damageable.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ItemStackDamageableMixin { + @Shadow + public abstract boolean isDamaged(); + + @Shadow + public abstract int getDamageValue(); + + @Shadow + public abstract void setDamageValue(int p_196085_1_); + + public boolean terra$hasDamage() { + return isDamaged(); + } + + @Intrinsic + public void terra$setDamage(int damage) { + setDamageValue(damage); + } + + @Intrinsic + public int terra$getDamage() { + return getDamageValue(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/meta/ItemStackMetaMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/meta/ItemStackMetaMixin.java new file mode 100644 index 000000000..aa9b2a41b --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/inventory/meta/ItemStackMetaMixin.java @@ -0,0 +1,50 @@ +package com.dfsek.terra.forge.mixin.implementations.inventory.meta; + +import com.dfsek.terra.api.platform.inventory.item.Enchantment; +import com.dfsek.terra.api.platform.inventory.item.ItemMeta; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.util.registry.Registry; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +@Mixin(ItemStack.class) +@Implements(@Interface(iface = ItemMeta.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ItemStackMetaMixin { + @Shadow + public abstract ListNBT getEnchantmentTags(); + + @Shadow + public abstract boolean isEnchanted(); + + @Shadow + public abstract void enchant(net.minecraft.enchantment.Enchantment p_77966_1_, int p_77966_2_); + + public Object terra$getHandle() { + return this; + } + + @Intrinsic(displace = true) + public Map terra$getEnchantments() { + if(!isEnchanted()) return Collections.emptyMap(); + Map map = new HashMap<>(); + + getEnchantmentTags().forEach(enchantment -> { + CompoundNBT eTag = (CompoundNBT) enchantment; + map.put((Enchantment) Registry.ENCHANTMENT.byId(eTag.getInt("id")), eTag.getInt("lvl")); + }); + return map; + } + + public void terra$addEnchantment(Enchantment enchantment, int level) { + enchant((net.minecraft.enchantment.Enchantment) enchantment, level); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/package-info.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/package-info.java new file mode 100644 index 000000000..0fae9920e --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/package-info.java @@ -0,0 +1,5 @@ +/** + * Mixins in this package implement Terra + * interfaces in Minecraft classes. + */ +package com.dfsek.terra.forge.mixin.implementations; \ No newline at end of file diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/world/ServerWorldMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/world/ServerWorldMixin.java new file mode 100644 index 000000000..024b4970d --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/world/ServerWorldMixin.java @@ -0,0 +1,86 @@ +package com.dfsek.terra.forge.mixin.implementations.world; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.block.Block; +import com.dfsek.terra.api.platform.entity.Entity; +import com.dfsek.terra.api.platform.entity.EntityType; +import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.api.platform.world.generator.ChunkGenerator; +import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; +import com.dfsek.terra.api.world.generation.TerraChunkGenerator; +import com.dfsek.terra.forge.block.ForgeBlock; +import com.dfsek.terra.forge.generation.ForgeChunkGeneratorWrapper; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IServerWorld; +import net.minecraft.world.server.ServerWorld; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ServerWorld.class) +@Implements(@Interface(iface = World.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class ServerWorldMixin { + @Shadow + public abstract long getSeed(); + + public int terra$getMaxHeight() { + return ((ServerWorld) (Object) this).getMaxBuildHeight(); + } + + public ChunkGenerator terra$getGenerator() { + return (ChunkGenerator) ((ServerWorld) (Object) this).getChunkSource().getGenerator(); + } + + public Chunk terra$getChunkAt(int x, int z) { + return (Chunk) ((ServerWorld) (Object) this).getChunk(x, z); + } + + public Block terra$getBlockAt(int x, int y, int z) { + return new ForgeBlock(new BlockPos(x, y, z), ((ServerWorld) (Object) this)); + } + + public Entity terra$spawnEntity(Location location, EntityType entityType) { + net.minecraft.entity.Entity entity = ((net.minecraft.entity.EntityType) entityType).create(((ServerWorld) (Object) this)); + entity.setPos(location.getX(), location.getY(), location.getZ()); + ((ServerWorld) (Object) this).addFreshEntity(entity); + return (Entity) entity; + } + + @Intrinsic + public long terra$getSeed() { + return getSeed(); + } + + public int terra$getMinHeight() { + return 0; + } + + public Object terra$getHandle() { + return this; + } + + public boolean terra$isTerraWorld() { + return terra$getGenerator() instanceof GeneratorWrapper; + } + + public TerraChunkGenerator terra$getTerraGenerator() { + return ((ForgeChunkGeneratorWrapper) terra$getGenerator()).getHandle(); + } + + /** + * Overridden in the same manner as {@link WorldGenRegionMixin#hashCode()} + * + * @param other Another object + * @return Whether this world is the same as other. + * @see WorldGenRegionMixin#hashCode() + */ + @SuppressWarnings("ConstantConditions") + @Override + public boolean equals(Object other) { + if(!(other instanceof IServerWorld)) return false; + return (IServerWorld) this == (((IServerWorld) other).getLevel()); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/world/WorldGenRegionMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/world/WorldGenRegionMixin.java new file mode 100644 index 000000000..0b162b591 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/world/WorldGenRegionMixin.java @@ -0,0 +1,108 @@ +package com.dfsek.terra.forge.mixin.implementations.world; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.block.Block; +import com.dfsek.terra.api.platform.entity.Entity; +import com.dfsek.terra.api.platform.entity.EntityType; +import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.api.platform.world.generator.ChunkGenerator; +import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; +import com.dfsek.terra.api.world.generation.TerraChunkGenerator; +import com.dfsek.terra.forge.block.ForgeBlock; +import com.dfsek.terra.forge.generation.ForgeChunkGeneratorWrapper; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.gen.WorldGenRegion; +import net.minecraft.world.server.ServerWorld; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(WorldGenRegion.class) +@Implements(@Interface(iface = World.class, prefix = "terra$", remap = Interface.Remap.NONE)) +public abstract class WorldGenRegionMixin { + @Shadow + @Final + private ServerWorld level; + + @Shadow + @Final + private long seed; + + public int terra$getMaxHeight() { + return ((WorldGenRegion) (Object) this).getMaxBuildHeight(); + } + + @SuppressWarnings("deprecation") + public ChunkGenerator terra$getGenerator() { + return (ChunkGenerator) ((WorldGenRegion) (Object) this).getLevel().getChunkSource().getGenerator(); + } + + public Chunk terra$getChunkAt(int x, int z) { + return (Chunk) ((WorldGenRegion) (Object) this).getChunk(x, z); + } + + public Block terra$getBlockAt(int x, int y, int z) { + return new ForgeBlock(new BlockPos(x, y, z), ((WorldGenRegion) (Object) this)); + } + + @SuppressWarnings("deprecation") + public Entity terra$spawnEntity(Location location, EntityType entityType) { + net.minecraft.entity.Entity entity = ((net.minecraft.entity.EntityType) entityType).create(((WorldGenRegion) (Object) this).getLevel()); + entity.setPos(location.getX(), location.getY(), location.getZ()); + ((WorldGenRegion) (Object) this).addFreshEntity(entity); + return (Entity) entity; + } + + @Intrinsic + public long terra$getSeed() { + return seed; + } + + public int terra$getMinHeight() { + return 0; + } + + public Object terra$getHandle() { + return this; + } + + public boolean terra$isTerraWorld() { + return terra$getGenerator() instanceof GeneratorWrapper; + } + + public TerraChunkGenerator terra$getTerraGenerator() { + return ((ForgeChunkGeneratorWrapper) terra$getGenerator()).getHandle(); + } + + /** + * We need regions delegating to the same world + * to have the same hashcode. This + * minimizes cache misses. + *

+ * This is sort of jank, but shouldn't(tm) + * break any other mods, unless they're doing + * something they really shouldn't, since + * ChunkRegions are not supposed to persist. + */ + @Override + public int hashCode() { + return level.hashCode(); + } + + /** + * Overridden in the same manner as {@link #hashCode()} + * + * @param other Another object + * @return Whether this world is the same as other. + * @see #hashCode() + */ + @Override + public boolean equals(Object other) { + if(!(other instanceof WorldGenRegion)) return false; + return level.equals(((WorldGenRegion) other).getLevel()); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/init/MinecraftClientMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/init/MinecraftClientMixin.java new file mode 100644 index 000000000..84245b37e --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/init/MinecraftClientMixin.java @@ -0,0 +1,29 @@ +package com.dfsek.terra.forge.mixin.init; + +import com.dfsek.terra.forge.TerraForgePlugin; +import com.dfsek.terra.forge.generation.TerraGeneratorType; +import com.dfsek.terra.forge.mixin.access.BiomeGeneratorTypeScreensAccessor; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screen.BiomeGeneratorTypeScreens; +import net.minecraft.resources.ResourcePackList; +import net.minecraft.util.text.StringTextComponent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(Minecraft.class) +public abstract class MinecraftClientMixin { + @Redirect(method = "", at = @At(value = "INVOKE", + target = "Lnet/minecraft/resources/ResourcePackList;reload()V" // sorta arbitrary position, after mod init, before window opens + )) + public void injectConstructor(ResourcePackList resourcePackList) { + TerraForgePlugin.getInstance().init(); // Load during MinecraftClient construction, after other mods have registered blocks and stuff + TerraForgePlugin.getInstance().getConfigRegistry().forEach(pack -> { + final BiomeGeneratorTypeScreens generatorType = new TerraGeneratorType(pack); + //noinspection ConstantConditions + ((BiomeGeneratorTypeScreensAccessor) generatorType).setDescription(new StringTextComponent("Terra:" + pack.getTemplate().getID())); + BiomeGeneratorTypeScreensAccessor.getPresets().add(1, generatorType); + }); + resourcePackList.reload(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/init/MinecraftServerMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/init/MinecraftServerMixin.java new file mode 100644 index 000000000..d8fed32cf --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/init/MinecraftServerMixin.java @@ -0,0 +1,16 @@ +package com.dfsek.terra.forge.mixin.init; + +import com.dfsek.terra.forge.TerraForgePlugin; +import net.minecraft.server.Main; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Main.class) +public class MinecraftServerMixin { + @Inject(method = "main([Ljava/lang/String;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/registry/DynamicRegistries;builtin()Lnet/minecraft/util/registry/DynamicRegistries$Impl;")) + private static void injectConstructor(String[] args, CallbackInfo ci) { + TerraForgePlugin.getInstance().init(); // Load during MinecraftServer construction, after other mods have registered blocks and stuff + } +} diff --git a/platforms/forge/src/main/resources/META-INF/mods.toml b/platforms/forge/src/main/resources/META-INF/mods.toml new file mode 100644 index 000000000..a306898d9 --- /dev/null +++ b/platforms/forge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,17 @@ +modLoader = "javafml" +loaderVersion = "[36,)" +license = "@LICENSE@" +issueTrackerURL = "@ISSUES@" +[[mods]] +modId = "terra" +version = "@VERSION@" +displayName = "Terra" +displayURL = "@WIKI@" +authors = "dfsek & Terra contributors" +description = "@DESCRIPTION@" +[[dependencies.terra]] +modId = "forge" +mandatory = true +versionRange = "[36,)" +ordering = "NONE" +side = "BOTH" \ No newline at end of file diff --git a/platforms/forge/src/main/resources/pack.mcmeta b/platforms/forge/src/main/resources/pack.mcmeta new file mode 100644 index 000000000..e33bc0eb4 --- /dev/null +++ b/platforms/forge/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "Terra Resources", + "pack_format": 6 + } +} \ No newline at end of file diff --git a/platforms/forge/src/main/resources/terra.mixins.json b/platforms/forge/src/main/resources/terra.mixins.json new file mode 100644 index 000000000..5aefcf5f0 --- /dev/null +++ b/platforms/forge/src/main/resources/terra.mixins.json @@ -0,0 +1,45 @@ +{ + "required": true, + "package": "com.dfsek.terra.forge.mixin", + "compatibilityLevel": "JAVA_8", + "refmap": "terra-refmap.json", + "mixins": [ + "DimensionGeneratorSettingsMixin", + "ServerWorldMixin", + "access.AbstractSpawnerAccessor", + "access.BiomeGeneratorTypeScreensAccessor", + "implementations.BiomeMixin", + "implementations.ChunkGeneratorMixin", + "implementations.ConfiguredFeatureMixin", + "implementations.block.BlockMixin", + "implementations.block.TileEntityMixin", + "implementations.block.state.LockableLootTileEntityMixin", + "implementations.block.state.MobSpawnerTileEntityMixin", + "implementations.block.state.SignTileEntityMixin", + "implementations.chunk.ChunkMixin", + "implementations.chunk.WorldGenRegionMixin", + "implementations.chunk.data.ChunkPrimerMixin", + "implementations.entity.CommandSourceMixin", + "implementations.entity.EntityMixin", + "implementations.entity.EntityTypeMixin", + "implementations.entity.PlayerEntityMixin", + "implementations.inventory.LockableTileEntityMixin", + "implementations.inventory.item.ItemMixin", + "implementations.inventory.item.ItemStackMixin", + "implementations.inventory.meta.EnchantmentMixin", + "implementations.inventory.meta.ItemStackDamageableMixin", + "implementations.inventory.meta.ItemStackMetaMixin", + "implementations.world.ServerWorldMixin", + "implementations.world.WorldGenRegionMixin" + ], + "client": [ + "init.MinecraftClientMixin" + ], + "server": [ + "init.MinecraftServerMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "minVersion": "0.8" +} \ No newline at end of file diff --git a/platforms/forge/src/test/resources/META-INF/mods.toml b/platforms/forge/src/test/resources/META-INF/mods.toml new file mode 100644 index 000000000..bb8120be8 --- /dev/null +++ b/platforms/forge/src/test/resources/META-INF/mods.toml @@ -0,0 +1,20 @@ +modLoader = "javafml" +loaderVersion = "[33,)" +license = "GNU General Public License, version 3.0" +issueTrackerURL = "https://github.com/PolyhedralDev/Terra/issues" +[[mods]] +modId = "terra" +version = "@VERSION@" +displayName = "Terra" +displayURL = "https://github.com/PolyhedralDev/Terra" +logoFile = "logo_text.png" +authors = "dfsek & Terra contributors" +description = ''' +Powerful data-driven world generator +''' +[[dependencies.terra]] +modId = "forge" +mandatory = true +versionRange = "[36.0.4,)" +ordering = "NONE" +side = "BOTH" \ No newline at end of file diff --git a/platforms/forge/src/test/resources/pack.mcmeta b/platforms/forge/src/test/resources/pack.mcmeta new file mode 100644 index 000000000..5ed246b25 --- /dev/null +++ b/platforms/forge/src/test/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "Terra resources", + "pack_format": 6 + } +} \ No newline at end of file diff --git a/platforms/region/src/main/java/com/dfsek/terra/StandalonePlugin.java b/platforms/region/src/main/java/com/dfsek/terra/StandalonePlugin.java index a1dd2e4c9..ff426a5c0 100644 --- a/platforms/region/src/main/java/com/dfsek/terra/StandalonePlugin.java +++ b/platforms/region/src/main/java/com/dfsek/terra/StandalonePlugin.java @@ -21,6 +21,8 @@ import com.dfsek.terra.config.lang.Language; import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.platform.RawBiome; import com.dfsek.terra.platform.RawWorldHandle; +import com.dfsek.terra.profiler.Profiler; +import com.dfsek.terra.profiler.ProfilerImpl; import com.dfsek.terra.registry.master.AddonRegistry; import com.dfsek.terra.registry.master.ConfigRegistry; import com.dfsek.terra.world.TerraWorld; @@ -39,6 +41,8 @@ public class StandalonePlugin implements TerraPlugin { private final RawWorldHandle worldHandle = new RawWorldHandle(); private final EventManager eventManager = new TerraEventManager(this); + private final Profiler profiler = new ProfilerImpl(); + @Override public WorldHandle getWorldHandle() { return worldHandle; @@ -147,4 +151,9 @@ public class StandalonePlugin implements TerraPlugin { public EventManager getEventManager() { return eventManager; } + + @Override + public Profiler getProfiler() { + return profiler; + } } diff --git a/platforms/region/src/main/java/com/dfsek/terra/platform/DirectWorld.java b/platforms/region/src/main/java/com/dfsek/terra/platform/DirectWorld.java index 3aacfabb9..84b90b7d8 100644 --- a/platforms/region/src/main/java/com/dfsek/terra/platform/DirectWorld.java +++ b/platforms/region/src/main/java/com/dfsek/terra/platform/DirectWorld.java @@ -19,7 +19,6 @@ import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.UUID; public class DirectWorld implements World { private final long seed; @@ -46,21 +45,6 @@ public class DirectWorld implements World { return generator; } - @Override - public String getName() { - return null; - } - - @Override - public UUID getUID() { - return null; - } - - @Override - public boolean isChunkGenerated(int x, int z) { - return false; - } - @Override public Chunk getChunkAt(int x, int z) { MCAFile file = compute(x, z); @@ -72,11 +56,6 @@ public class DirectWorld implements World { return new DirectChunkData(chunk, this, x, z); } - @Override - public File getWorldFolder() { - return null; - } - @Override public Block getBlockAt(int x, int y, int z) { return new DirectBlock(this, new Vector3(x, y, z)); diff --git a/platforms/sponge/src/main/java/com/dfsek/terra/sponge/TerraSpongePlugin.java b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/TerraSpongePlugin.java index 78baf45d5..536850875 100644 --- a/platforms/sponge/src/main/java/com/dfsek/terra/sponge/TerraSpongePlugin.java +++ b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/TerraSpongePlugin.java @@ -14,6 +14,7 @@ import com.dfsek.terra.api.util.logging.DebugLogger; import com.dfsek.terra.config.PluginConfig; import com.dfsek.terra.config.lang.Language; import com.dfsek.terra.config.pack.ConfigPack; +import com.dfsek.terra.profiler.Profiler; import com.dfsek.terra.registry.master.AddonRegistry; import com.dfsek.terra.registry.master.ConfigRegistry; import com.dfsek.terra.sponge.world.SpongeWorldHandle; @@ -138,4 +139,9 @@ public class TerraSpongePlugin implements TerraPlugin { public EventManager getEventManager() { return eventManager; } + + @Override + public Profiler getProfiler() { + return null; + } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 303d70045..8cd013d8d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,10 +13,11 @@ include("platforms:bukkit") include("platforms:fabric") include("platforms:region") include("platforms:sponge") +include("platforms:forge") pluginManagement { repositories { - maven(url = "http://maven.fabricmc.net") { + maven(url = "https://maven.fabricmc.net") { name = "Fabric" } gradlePluginPortal()