mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-07-01 23:47:21 +00:00
It go fast
This commit is contained in:
parent
c209895389
commit
cc70a30315
34
build.gradle
34
build.gradle
@ -12,19 +12,9 @@ jar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects
|
||||||
|
{
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'io.freefair.lombok'
|
|
||||||
|
|
||||||
configurations {
|
|
||||||
testImplementation.extendsFrom annotationProcessor
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
toolchain {
|
|
||||||
languageVersion = JavaLanguageVersion.of(17)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven { url "https://dl.cloudsmith.io/public/arcane/archive/maven/" }
|
maven { url "https://dl.cloudsmith.io/public/arcane/archive/maven/" }
|
||||||
@ -42,26 +32,6 @@ allprojects {
|
|||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
|
||||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JavaVersion.current() != JavaVersion.VERSION_1_8 &&
|
|
||||||
sourceSets.main.allJava.files.any { it.name == "module-info.java" }) {
|
|
||||||
tasks.withType(JavaCompile) {
|
|
||||||
options.compilerArgs += ['-Xplugin:Manifold', '--module-path', it.classpath.asPath]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tasks.withType(JavaCompile) {
|
|
||||||
options.compilerArgs += ['-Xplugin:Manifold']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
subprojects {
|
|
||||||
apply plugin: 'java'
|
|
||||||
apply plugin: 'io.freefair.lombok'
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation rootProject
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -1,11 +1,285 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
|
id "io.freefair.lombok" version "6.3.0"
|
||||||
|
id "com.github.johnrengelman.shadow" version "7.1.2"
|
||||||
|
id "de.undercouch.download" version "5.0.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
group rootProject.group
|
group rootProject.group
|
||||||
version rootProject.version
|
version rootProject.version
|
||||||
|
|
||||||
|
def nmsVersion = "1.19"
|
||||||
|
def apiVersion = '1.19'
|
||||||
|
def spigotJarVersion = '1.19-R0.1-SNAPSHOT'
|
||||||
|
def name = getRootProject().getName() // Defined in settings.gradle
|
||||||
|
def main = 'com.volmit.iris.platform.bukkit.IrisBukkit'
|
||||||
|
|
||||||
|
// ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED
|
||||||
|
// ======================== WINDOWS =============================
|
||||||
|
registerCustomOutputTask('Cyberpwn', 'C://Users/cyberpwn/Documents/development/server/plugins')
|
||||||
|
registerCustomOutputTask('Psycho', 'D://Dan/MinecraftDevelopment/server/plugins')
|
||||||
|
registerCustomOutputTask('ArcaneArts', 'C://Users/arcane/Documents/development/server/plugins')
|
||||||
|
registerCustomOutputTask('Coco', 'D://Documents/MC/plugins')
|
||||||
|
registerCustomOutputTask('Strange', 'D://Servers/1.17 Test Server/plugins')
|
||||||
|
registerCustomOutputTask('Vatuu', 'D://Minecraft/Servers/1.19/plugins')
|
||||||
|
// ========================== UNIX ==============================
|
||||||
|
registerCustomOutputTaskUnix('CyberpwnLT', '/Users/danielmills/development/server/plugins')
|
||||||
|
registerCustomOutputTaskUnix('PsychoLT', '/Users/brianfopiano/Desktop/REMOTES/RemoteMinecraft/plugins')
|
||||||
|
// ==============================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gradle is weird sometimes, we need to delete the plugin yml from the build folder to actually filter properly.
|
||||||
|
*/
|
||||||
|
file(jar.archiveFile.get().getAsFile().getParentFile().getParentFile().getParentFile().getAbsolutePath() + '/build/resources/main/plugin.yml').delete()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand properties into plugin yml
|
||||||
|
*/
|
||||||
|
processResources {
|
||||||
|
filesMatching('**/plugin.yml') {
|
||||||
|
expand(
|
||||||
|
'name': name.toString(),
|
||||||
|
'version': version.toString(),
|
||||||
|
'main': main.toString(),
|
||||||
|
'apiversion': apiVersion.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We need parameter meta for the decree command system
|
||||||
|
*/
|
||||||
|
compileJava {
|
||||||
|
options.compilerArgs << '-parameters'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure Iris for shading
|
||||||
|
*/
|
||||||
|
shadowJar {
|
||||||
|
//minimize()
|
||||||
|
append("plugin.yml")
|
||||||
|
relocate 'com.volmit.fukkit', 'com.volmit.iris.util.fukkit'
|
||||||
|
relocate 'art.arcane.amulet', 'com.volmit.iris.util.amulet'
|
||||||
|
relocate 'art.arcane.source', 'com.volmit.iris.util.source'
|
||||||
|
dependencies {
|
||||||
|
include(dependency('art.arcane.source:Source'))
|
||||||
|
include(dependency('art.arcane:Amulet'))
|
||||||
|
include(dependency('com.volmit:Fukkit'))
|
||||||
|
include(dependency('systems.manifold:'))
|
||||||
|
include(dependency(":engine"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Faster dependency caches
|
||||||
|
*/
|
||||||
|
configurations.all {
|
||||||
|
resolutionStrategy.cacheChangingModulesFor 60, 'minutes'
|
||||||
|
resolutionStrategy.cacheDynamicVersionsFor 60, 'minutes'
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
testImplementation.extendsFrom annotationProcessor
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven { url "https://dl.cloudsmith.io/public/arcane/archive/maven/" }
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.spigotmc:spigot-api:1.19-R0.1-SNAPSHOT'
|
implementation 'org.spigotmc:spigot-api:1.19-R0.1-SNAPSHOT'
|
||||||
implementation 'com.volmit:Fukkit:22.6.13'
|
implementation 'com.volmit:Fukkit:22.6.13'
|
||||||
|
implementation project(":engine")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (JavaVersion.current() != JavaVersion.VERSION_1_8 &&
|
||||||
|
sourceSets.main.allJava.files.any { it.name == "module-info.java" }) {
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.compilerArgs += ['-Xplugin:Manifold', '--module-path', it.classpath.asPath]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.compilerArgs += ['-Xplugin:Manifold']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JavaVersion.current().toString() != "17") {
|
||||||
|
System.err.println()
|
||||||
|
System.err.println("=========================================================================================================")
|
||||||
|
System.err.println("You must run gradle on Java 17. You are using " + JavaVersion.current())
|
||||||
|
System.err.println()
|
||||||
|
System.err.println("=== For IDEs ===")
|
||||||
|
System.err.println("1. Configure the project for Java 17")
|
||||||
|
System.err.println("2. Configure the bundled gradle to use Java 17 in settings")
|
||||||
|
System.err.println()
|
||||||
|
System.err.println("=== For Command Line (gradlew) ===")
|
||||||
|
System.err.println("1. Install JDK 17 from https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html")
|
||||||
|
System.err.println("2. Set JAVA_HOME environment variable to the new jdk installation folder such as C:\\Program Files\\Java\\jdk-17.0.1")
|
||||||
|
System.err.println("3. Open a new command prompt window to get the new environment variables if need be.")
|
||||||
|
System.err.println("=========================================================================================================")
|
||||||
|
System.err.println()
|
||||||
|
System.exit(69);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
System.out.println(buildDir);
|
||||||
|
def buildToolsJar = new File(buildDir, "buildtools/BuildTools.jar");
|
||||||
|
def specialSourceJar = new File(buildDir, "specialsource/SpecialSource.jar");
|
||||||
|
def buildToolsFolder = new File(buildDir, "buildtools");
|
||||||
|
def specialSourceFolder = new File(buildDir, "specialsource");
|
||||||
|
def buildToolsHint = new File(buildDir, "buildtools/craftbukkit-" + nmsVersion + ".jar");
|
||||||
|
def outputShadeJar = new File(buildDir, "libs/bukkit-" + version + "-all.jar");
|
||||||
|
def ssiJar = new File(buildDir, "specialsource/bukkit-" + version + "-all.jar");
|
||||||
|
def ssobfJar = new File(buildDir, "specialsource/Iris-" + version + "-rmo.jar");
|
||||||
|
def ssJar = new File(buildDir, "specialsource/Iris-" + version + "-rma.jar");
|
||||||
|
def homePath = System.properties['user.home']
|
||||||
|
def m2 = new File(homePath + "/.m2/repository")
|
||||||
|
def m2s = m2.getAbsolutePath();
|
||||||
|
|
||||||
|
// ======================== Building Mapped Jars =============================
|
||||||
|
task downloadBuildtools(type: Download) {
|
||||||
|
group "remapping"
|
||||||
|
src 'https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar'
|
||||||
|
dest buildToolsJar
|
||||||
|
onlyIf {
|
||||||
|
!buildToolsJar.exists()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task downloadSpecialSource(type: Download) {
|
||||||
|
group "remapping"
|
||||||
|
src 'https://repo.maven.apache.org/maven2/net/md-5/SpecialSource/1.10.0/SpecialSource-1.10.0-shaded.jar'
|
||||||
|
dest specialSourceJar
|
||||||
|
onlyIf {
|
||||||
|
!specialSourceJar.exists()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task executeBuildTools(dependsOn: downloadBuildtools, type: JavaExec)
|
||||||
|
{
|
||||||
|
group "remapping"
|
||||||
|
classpath = files(buildToolsJar)
|
||||||
|
workingDir = buildToolsFolder
|
||||||
|
args = [
|
||||||
|
"--rev",
|
||||||
|
nmsVersion,
|
||||||
|
"--compile",
|
||||||
|
"craftbukkit",
|
||||||
|
"--remap"
|
||||||
|
]
|
||||||
|
onlyIf {
|
||||||
|
!buildToolsHint.exists()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task copyBuildToSpecialSource(type: Copy)
|
||||||
|
{
|
||||||
|
group "remapping"
|
||||||
|
from outputShadeJar
|
||||||
|
into specialSourceFolder
|
||||||
|
dependsOn(downloadSpecialSource, shadowJar)
|
||||||
|
}
|
||||||
|
|
||||||
|
task specialSourceRemapObfuscate(type: JavaExec)
|
||||||
|
{
|
||||||
|
group "remapping"
|
||||||
|
dependsOn(copyBuildToSpecialSource, downloadSpecialSource, shadowJar)
|
||||||
|
workingDir = specialSourceFolder
|
||||||
|
classpath = files(specialSourceJar,
|
||||||
|
new File(m2s + "/org/spigotmc/spigot/" + spigotJarVersion + "/spigot-" + spigotJarVersion + "-remapped-mojang.jar"))
|
||||||
|
mainClass = "net.md_5.specialsource.SpecialSource"
|
||||||
|
args = [
|
||||||
|
"--live",
|
||||||
|
"-i",
|
||||||
|
ssiJar.getName(),
|
||||||
|
"-o",
|
||||||
|
ssobfJar.getName(),
|
||||||
|
"-m",
|
||||||
|
m2s + "/org/spigotmc/minecraft-server/" + spigotJarVersion + "/minecraft-server-" + spigotJarVersion + "-maps-mojang.txt",
|
||||||
|
"--reverse",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
task specialSourceRemap(type: JavaExec)
|
||||||
|
{
|
||||||
|
group "remapping"
|
||||||
|
dependsOn(specialSourceRemapObfuscate)
|
||||||
|
workingDir = specialSourceFolder
|
||||||
|
classpath = files(specialSourceJar,
|
||||||
|
new File(m2s + "/org/spigotmc/spigot/" + spigotJarVersion + "/spigot-" + spigotJarVersion + "-remapped-obf.jar"))
|
||||||
|
mainClass = "net.md_5.specialsource.SpecialSource"
|
||||||
|
args = [
|
||||||
|
"--live",
|
||||||
|
"-i",
|
||||||
|
ssobfJar.getName(),
|
||||||
|
"-o",
|
||||||
|
ssJar.getName(),
|
||||||
|
"-m",
|
||||||
|
m2s + "/org/spigotmc/minecraft-server/" + spigotJarVersion + "/minecraft-server-" + spigotJarVersion + "-maps-spigot.csrg"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.compileJava.dependsOn(executeBuildTools)
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
task setup()
|
||||||
|
{
|
||||||
|
group("iris")
|
||||||
|
dependsOn(clean, executeBuildTools)
|
||||||
|
}
|
||||||
|
|
||||||
|
task iris(type: Copy)
|
||||||
|
{
|
||||||
|
group "iris"
|
||||||
|
from ssJar
|
||||||
|
into buildDir
|
||||||
|
rename { String fileName ->
|
||||||
|
fileName.replace('Iris-' + version + '-rma.jar', "Iris-" + version + ".jar")
|
||||||
|
}
|
||||||
|
dependsOn(specialSourceRemap)
|
||||||
|
}
|
||||||
|
|
||||||
|
def registerCustomOutputTask(name, path) {
|
||||||
|
if (!System.properties['os.name'].toLowerCase().contains('windows')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('build' + name, Copy) {
|
||||||
|
group('development')
|
||||||
|
outputs.upToDateWhen { false }
|
||||||
|
dependsOn(iris)
|
||||||
|
from(new File(buildDir, "Iris-" + version + ".jar"))
|
||||||
|
into(file(path))
|
||||||
|
rename { String fileName ->
|
||||||
|
fileName.replace("Iris-" + version + ".jar", "Iris.jar")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def registerCustomOutputTaskUnix(name, path) {
|
||||||
|
if (System.properties['os.name'].toLowerCase().contains('windows')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('build' + name, Copy) {
|
||||||
|
group('development')
|
||||||
|
outputs.upToDateWhen { false }
|
||||||
|
dependsOn(iris)
|
||||||
|
from(new File(buildDir, "Iris-" + version + ".jar"))
|
||||||
|
into(file(path))
|
||||||
|
rename { String fileName ->
|
||||||
|
fileName.replace("Iris-" + version + ".jar", "Iris.jar")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +1,39 @@
|
|||||||
package com.volmit.iris.platform.bukkit;
|
package com.volmit.iris.platform.bukkit;
|
||||||
|
|
||||||
|
import com.volmit.iris.engine.EngineConfiguration;
|
||||||
import com.volmit.iris.platform.IrisPlatform;
|
import com.volmit.iris.platform.IrisPlatform;
|
||||||
import com.volmit.iris.platform.PlatformBiome;
|
import com.volmit.iris.platform.PlatformBiome;
|
||||||
import com.volmit.iris.platform.PlatformBlock;
|
import com.volmit.iris.platform.PlatformBlock;
|
||||||
|
import com.volmit.iris.platform.PlatformNamespaceKey;
|
||||||
import com.volmit.iris.platform.PlatformWorld;
|
import com.volmit.iris.platform.PlatformWorld;
|
||||||
|
import com.volmit.iris.platform.bukkit.wrapper.BukkitKey;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.WorldCreator;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = false)
|
|
||||||
public class IrisBukkit extends JavaPlugin implements IrisPlatform {
|
public class IrisBukkit extends JavaPlugin implements IrisPlatform {
|
||||||
private static IrisBukkit instance;
|
private static IrisBukkit instance;
|
||||||
|
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
instance = this;
|
instance = this;
|
||||||
|
|
||||||
|
getServer().getScheduler().scheduleSyncDelayedTask(this, () -> {
|
||||||
|
World world = Bukkit.createWorld(new WorldCreator("iristests/" + UUID.randomUUID()).generator(new IrisBukkitChunkGenerator(this, EngineConfiguration.builder()
|
||||||
|
.threads(4)
|
||||||
|
.mutable(true)
|
||||||
|
.timings(true)
|
||||||
|
.build())));
|
||||||
|
}, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
@ -41,7 +53,9 @@ public class IrisBukkit extends JavaPlugin implements IrisPlatform {
|
|||||||
public Stream<PlatformBlock> getBlocks() {
|
public Stream<PlatformBlock> getBlocks() {
|
||||||
//This is because it's a method extension
|
//This is because it's a method extension
|
||||||
//noinspection Convert2MethodRef
|
//noinspection Convert2MethodRef
|
||||||
return Arrays.stream(Material.values()).parallel().filter(Material::isBlock).map(Material::createBlockData).map(i -> i.bukkitBlock());
|
return Arrays.stream(Material.values()).parallel().filter((i) -> !i.isLegacy())
|
||||||
|
.filter(Material::isBlock)
|
||||||
|
.map(Material::createBlockData).map(i -> i.bukkitBlock());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -67,4 +81,23 @@ public class IrisBukkit extends JavaPlugin implements IrisPlatform {
|
|||||||
|
|
||||||
return w.bukkitWorld();
|
return w.bukkitWorld();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlatformBlock parseBlock(String raw) {
|
||||||
|
return Bukkit.createBlockData(raw).bukkitBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlatformNamespaceKey key(String namespace, String key) {
|
||||||
|
return BukkitKey.of(namespace, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
|
||||||
|
return new IrisBukkitChunkGenerator(this, EngineConfiguration.builder()
|
||||||
|
.threads(4)
|
||||||
|
.mutable(true)
|
||||||
|
.timings(true)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
package com.volmit.iris.platform.bukkit;
|
||||||
|
|
||||||
|
import art.arcane.amulet.collections.hunk.Hunk;
|
||||||
|
import art.arcane.amulet.metric.Average;
|
||||||
|
import art.arcane.amulet.metric.PrecisionStopwatch;
|
||||||
|
import com.volmit.iris.engine.EngineConfiguration;
|
||||||
|
import com.volmit.iris.engine.IrisEngine;
|
||||||
|
import com.volmit.iris.engine.feature.IrisFeatureSizedTarget;
|
||||||
|
import com.volmit.iris.engine.feature.IrisFeatureTarget;
|
||||||
|
import com.volmit.iris.engine.feature.standard.FeatureTerrain;
|
||||||
|
import com.volmit.iris.platform.IrisPlatform;
|
||||||
|
import com.volmit.iris.platform.PlatformBlock;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
|
import com.volmit.iris.platform.bukkit.util.ChunkDataHunkView;
|
||||||
|
import org.bukkit.generator.WorldInfo;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
public class IrisBukkitChunkGenerator extends ChunkGenerator {
|
||||||
|
private final IrisPlatform platform;
|
||||||
|
private final EngineConfiguration configuration;
|
||||||
|
private final AtomicReference<IrisEngine> engine;
|
||||||
|
private final ReentrantLock engineLock;
|
||||||
|
private final AtomicInteger perSecond;
|
||||||
|
private final PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||||
|
private final Average a = new Average(128);
|
||||||
|
|
||||||
|
public IrisBukkitChunkGenerator(IrisPlatform platform, EngineConfiguration configuration)
|
||||||
|
{
|
||||||
|
this.perSecond = new AtomicInteger(0);
|
||||||
|
this.platform = platform;
|
||||||
|
this.configuration = configuration;
|
||||||
|
engine = new AtomicReference<>();
|
||||||
|
engineLock = new ReentrantLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChunkData generateChunkData(World world, Random random, int x, int z, BiomeGrid biome) {
|
||||||
|
PrecisionStopwatch pp = PrecisionStopwatch.start();
|
||||||
|
initEngine(world);
|
||||||
|
ChunkData data = Bukkit.createChunkData(world);
|
||||||
|
Hunk<PlatformBlock> chunk = new ChunkDataHunkView(data);
|
||||||
|
IrisFeatureSizedTarget targetSize = IrisFeatureSizedTarget.builder()
|
||||||
|
.width(chunk.getWidth())
|
||||||
|
.height(chunk.getHeight())
|
||||||
|
.depth(chunk.getDepth())
|
||||||
|
.offsetX(x << 4)
|
||||||
|
.offsetZ(z << 4)
|
||||||
|
.offsetY(0)
|
||||||
|
.build();
|
||||||
|
FeatureTerrain.TerrainFeatureState state = engine.get().getTerrainFeature().prepare(engine.get(), targetSize);
|
||||||
|
engine.get().getTerrainFeature().generate(engine.get(), state, new IrisFeatureTarget<>(chunk, targetSize));
|
||||||
|
perSecond.incrementAndGet();
|
||||||
|
a.put(pp.getMilliseconds());
|
||||||
|
|
||||||
|
if(p.getMilliseconds() > 1000)
|
||||||
|
{
|
||||||
|
p.reset();
|
||||||
|
p.begin();
|
||||||
|
System.out.println("PERSECOND: " + perSecond.getAndSet(0) + " AMS: " + ((int)Math.round(a.getAverage())) + "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initEngine(World world) {
|
||||||
|
if(engine.get() == null)
|
||||||
|
{
|
||||||
|
engineLock.lock();
|
||||||
|
|
||||||
|
if(engine.get() == null)
|
||||||
|
{
|
||||||
|
engine.set(new IrisEngine(platform, world.bukkitWorld(), configuration));
|
||||||
|
}
|
||||||
|
|
||||||
|
engineLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSpawn(World world, int x, int z) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.volmit.iris.platform.bukkit.util;
|
||||||
|
|
||||||
|
import art.arcane.amulet.collections.hunk.Hunk;
|
||||||
|
import com.volmit.iris.platform.PlatformBlock;
|
||||||
|
import com.volmit.iris.platform.bukkit.wrapper.BukkitBlock;
|
||||||
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
|
|
||||||
|
@SuppressWarnings("ClassCanBeRecord")
|
||||||
|
public class ChunkDataHunkView implements Hunk<PlatformBlock> {
|
||||||
|
private final ChunkGenerator.ChunkData chunk;
|
||||||
|
|
||||||
|
public ChunkDataHunkView(ChunkGenerator.ChunkData chunk) {
|
||||||
|
this.chunk = chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWidth() {
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDepth() {
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeight() {
|
||||||
|
return chunk.getMaxHeight() - chunk.getMinHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(int x1, int y1, int z1, int x2, int y2, int z2, PlatformBlock t) {
|
||||||
|
if(t == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.setRegion(x1, y1 + chunk.getMinHeight(), z1, x2, y2 + chunk.getMinHeight(), z2, ((BukkitBlock)t).getDelegate());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRaw(int x, int y, int z, PlatformBlock t) {
|
||||||
|
if(t == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.setBlock(x, y + chunk.getMinHeight(), z, ((BukkitBlock)t).getDelegate());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlatformBlock getRaw(int x, int y, int z) {
|
||||||
|
return chunk.getBlockData(x, y + chunk.getMinHeight(), z).bukkitBlock();
|
||||||
|
}
|
||||||
|
}
|
3
bukkit/src/main/resources/plugin.yml
Normal file
3
bukkit/src/main/resources/plugin.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
name: Iris
|
||||||
|
main: com.volmit.iris.platform.bukkit.IrisBukkit
|
||||||
|
version: 1.0.0
|
34
engine/build.gradle
Normal file
34
engine/build.gradle
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id "io.freefair.lombok" version "6.3.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
group rootProject.group
|
||||||
|
version rootProject.version
|
||||||
|
|
||||||
|
jar {
|
||||||
|
manifest {
|
||||||
|
attributes('Contains-Sources': 'java,class')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
testImplementation.extendsFrom annotationProcessor
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JavaVersion.current() != JavaVersion.VERSION_1_8 &&
|
||||||
|
sourceSets.main.allJava.files.any { it.name == "module-info.java" }) {
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.compilerArgs += ['-Xplugin:Manifold', '--module-path', it.classpath.asPath]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.compilerArgs += ['-Xplugin:Manifold']
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.volmit.iris.engine;
|
||||||
|
|
||||||
|
import com.volmit.iris.platform.PlatformBlock;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class EngineBlockCache
|
||||||
|
{
|
||||||
|
private final IrisEngine engine;
|
||||||
|
private final Map<String, PlatformBlock> cache;
|
||||||
|
|
||||||
|
public EngineBlockCache(IrisEngine engine)
|
||||||
|
{
|
||||||
|
this.engine = engine;
|
||||||
|
this.cache = new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlatformBlock get(String t)
|
||||||
|
{
|
||||||
|
return cache.computeIfAbsent(t, (key) -> engine.getPlatform().parseBlock(key));
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package com.volmit.iris.engine;
|
package com.volmit.iris.engine;
|
||||||
|
|
||||||
|
import com.volmit.iris.platform.PlatformWorld;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
44
engine/src/main/java/com/volmit/iris/engine/IrisEngine.java
Normal file
44
engine/src/main/java/com/volmit/iris/engine/IrisEngine.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package com.volmit.iris.engine;
|
||||||
|
|
||||||
|
import com.volmit.iris.engine.feature.IrisFeature;
|
||||||
|
import com.volmit.iris.engine.feature.standard.FeatureTerrain;
|
||||||
|
import com.volmit.iris.platform.IrisPlatform;
|
||||||
|
import com.volmit.iris.platform.PlatformBlock;
|
||||||
|
import com.volmit.iris.platform.PlatformNamespaceKey;
|
||||||
|
import com.volmit.iris.platform.PlatformRegistry;
|
||||||
|
import com.volmit.iris.platform.PlatformWorld;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class IrisEngine {
|
||||||
|
private final IrisPlatform platform;
|
||||||
|
private final EngineRegistry registry;
|
||||||
|
private final EngineConfiguration configuration;
|
||||||
|
private final PlatformWorld world;
|
||||||
|
private final EngineBlockCache blockCache;
|
||||||
|
|
||||||
|
private final FeatureTerrain terrainFeature;
|
||||||
|
|
||||||
|
public IrisEngine(IrisPlatform platform, PlatformWorld world, EngineConfiguration configuration) {
|
||||||
|
this.configuration = configuration;
|
||||||
|
this.platform = platform;
|
||||||
|
this.world = world;
|
||||||
|
this.blockCache = new EngineBlockCache(this);
|
||||||
|
this.registry = EngineRegistry.builder()
|
||||||
|
.blockRegistry(new PlatformRegistry<>(platform.getBlocks()))
|
||||||
|
.biomeRegistry(new PlatformRegistry<>(platform.getBiomes()))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
terrainFeature = new FeatureTerrain(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlatformBlock block(String block)
|
||||||
|
{
|
||||||
|
return blockCache.get(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlatformNamespaceKey key(String nsk)
|
||||||
|
{
|
||||||
|
return getPlatform().key(nsk);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.volmit.iris.engine.feature;
|
||||||
|
|
||||||
|
import com.volmit.iris.engine.IrisEngine;
|
||||||
|
import com.volmit.iris.platform.PlatformNamespaced;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public abstract class IrisFeature<T extends PlatformNamespaced, S extends IrisFeatureState> {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public IrisFeature(String name, IrisEngine engine)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract S prepare(IrisEngine engine, IrisFeatureSizedTarget target);
|
||||||
|
|
||||||
|
public abstract void generate(IrisEngine engine, S state, IrisFeatureTarget<T> target);
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
package com.volmit.iris.engine.feature;
|
||||||
|
|
||||||
|
import art.arcane.amulet.collections.hunk.Hunk;
|
||||||
|
import art.arcane.amulet.geometry.Vec;
|
||||||
|
import art.arcane.amulet.range.IntegerRange;
|
||||||
|
import com.volmit.iris.platform.PlatformNamespaced;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Data
|
||||||
|
public class IrisFeatureSizedTarget {
|
||||||
|
@Builder.Default
|
||||||
|
private final int width = 16;
|
||||||
|
@Builder.Default
|
||||||
|
private final int height = 0;
|
||||||
|
@Builder.Default
|
||||||
|
private final int depth = 16;
|
||||||
|
@Builder.Default
|
||||||
|
private final int offsetX = 0;
|
||||||
|
@Builder.Default
|
||||||
|
private final int offsetY = 0;
|
||||||
|
@Builder.Default
|
||||||
|
private final int offsetZ = 0;
|
||||||
|
|
||||||
|
public int getAbsoluteMaxX()
|
||||||
|
{
|
||||||
|
return getOffsetX() + getWidth() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAbsoluteMaxY()
|
||||||
|
{
|
||||||
|
return getOffsetY() + getHeight() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAbsoluteMaxZ()
|
||||||
|
{
|
||||||
|
return getOffsetZ() + getDepth() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntegerRange x()
|
||||||
|
{
|
||||||
|
return new IntegerRange(getOffsetX(), getAbsoluteMaxX());
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntegerRange y()
|
||||||
|
{
|
||||||
|
return new IntegerRange(getOffsetY(), getAbsoluteMaxY());
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntegerRange z()
|
||||||
|
{
|
||||||
|
return new IntegerRange(getOffsetZ(), getAbsoluteMaxZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntegerRange localX()
|
||||||
|
{
|
||||||
|
return new IntegerRange(0, getWidth() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntegerRange localY()
|
||||||
|
{
|
||||||
|
return new IntegerRange(0, getHeight() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntegerRange localZ()
|
||||||
|
{
|
||||||
|
return new IntegerRange(0, getDepth() - 1);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.volmit.iris.engine.feature;
|
||||||
|
|
||||||
|
public interface IrisFeatureState {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.volmit.iris.engine.feature;
|
||||||
|
|
||||||
|
import art.arcane.amulet.collections.hunk.Hunk;
|
||||||
|
import com.volmit.iris.platform.PlatformNamespaced;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class IrisFeatureTarget<T extends PlatformNamespaced> extends IrisFeatureSizedTarget {
|
||||||
|
private final Hunk<T> hunk;
|
||||||
|
|
||||||
|
public IrisFeatureTarget(Hunk<T> hunk, int offsetX, int offsetY, int offsetZ)
|
||||||
|
{
|
||||||
|
super(hunk.getWidth(), hunk.getHeight(), hunk.getDepth(), offsetX, offsetY, offsetZ);
|
||||||
|
this.hunk = hunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IrisFeatureTarget(Hunk<T> hunk, IrisFeatureSizedTarget target)
|
||||||
|
{
|
||||||
|
this(hunk, target.getOffsetX(), target.getOffsetY(), target.getOffsetZ());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package com.volmit.iris.engine.feature.standard;
|
||||||
|
|
||||||
|
import art.arcane.amulet.range.IntegerRange;
|
||||||
|
import art.arcane.source.api.noise.Generator;
|
||||||
|
import art.arcane.source.api.noise.provider.SimplexProvider;
|
||||||
|
import com.volmit.iris.engine.IrisEngine;
|
||||||
|
import com.volmit.iris.engine.feature.IrisFeature;
|
||||||
|
import com.volmit.iris.engine.feature.IrisFeatureSizedTarget;
|
||||||
|
import com.volmit.iris.engine.feature.IrisFeatureState;
|
||||||
|
import com.volmit.iris.engine.feature.IrisFeatureTarget;
|
||||||
|
import com.volmit.iris.platform.PlatformBlock;
|
||||||
|
import com.volmit.iris.util.ShortNoiseCache;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
public class FeatureTerrain extends IrisFeature<PlatformBlock, FeatureTerrain.TerrainFeatureState>
|
||||||
|
{
|
||||||
|
private final PlatformBlock stone;
|
||||||
|
private final Generator generator;
|
||||||
|
|
||||||
|
public FeatureTerrain(IrisEngine engine)
|
||||||
|
{
|
||||||
|
super("terrain", engine);
|
||||||
|
stone = engine.block("stone");
|
||||||
|
this.generator = new Generator(new SimplexProvider(engine.getWorld().getSeed()))
|
||||||
|
.maxOutput(64)
|
||||||
|
.minOutput(0)
|
||||||
|
.scale(0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TerrainFeatureState prepare(IrisEngine engine, IrisFeatureSizedTarget target) {
|
||||||
|
final ShortNoiseCache noise = new ShortNoiseCache(target.getWidth(), target.getDepth());
|
||||||
|
int cx,cz;
|
||||||
|
|
||||||
|
for(int x : target.x())
|
||||||
|
{
|
||||||
|
cx = x - target.getOffsetX();
|
||||||
|
|
||||||
|
for(int z : target.z())
|
||||||
|
{
|
||||||
|
cz = z - target.getOffsetZ();
|
||||||
|
noise.set(cx, cz, (short) generator.noise(x, z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TerrainFeatureState(noise);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generate(IrisEngine engine, TerrainFeatureState state, IrisFeatureTarget<PlatformBlock> target) {
|
||||||
|
for(int x : target.localX()) {
|
||||||
|
for(int z : target.localZ()) {
|
||||||
|
int h = state.getNoise().get(x, z);
|
||||||
|
for(int y : new IntegerRange(target.y().getLeftEndpoint(), Math.min(target.y().getRightEndpoint(), h)))
|
||||||
|
{
|
||||||
|
target.getHunk().set(x, y, z, stone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class TerrainFeatureState implements IrisFeatureState {
|
||||||
|
private final ShortNoiseCache noise;
|
||||||
|
}
|
||||||
|
}
|
@ -12,4 +12,13 @@ public interface IrisPlatform {
|
|||||||
boolean isWorldLoaded(String name);
|
boolean isWorldLoaded(String name);
|
||||||
|
|
||||||
PlatformWorld getWorld(String name);
|
PlatformWorld getWorld(String name);
|
||||||
|
|
||||||
|
PlatformBlock parseBlock(String raw);
|
||||||
|
|
||||||
|
PlatformNamespaceKey key(String namespace, String key);
|
||||||
|
|
||||||
|
default PlatformNamespaceKey key(String nsk)
|
||||||
|
{
|
||||||
|
return key("minecraft", nsk);
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.volmit.iris.util;
|
||||||
|
|
||||||
|
public class FloatNoiseCache {
|
||||||
|
private final int width;
|
||||||
|
private final int height;
|
||||||
|
private final float[] cache;
|
||||||
|
|
||||||
|
public FloatNoiseCache(int width, int height)
|
||||||
|
{
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
cache = new float[width * height];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int x, int y, float v) {
|
||||||
|
this.cache[y % this.height * this.width + x % this.width] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float get(int x, int y) {
|
||||||
|
return this.cache[y % this.height * this.width + x % this.width];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.volmit.iris.util;
|
||||||
|
|
||||||
|
public class ShortNoiseCache {
|
||||||
|
private final int width;
|
||||||
|
private final int height;
|
||||||
|
private final short[] cache;
|
||||||
|
|
||||||
|
public ShortNoiseCache(int width, int height)
|
||||||
|
{
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
cache = new short[width * height];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int x, int y, short v) {
|
||||||
|
this.cache[y % this.height * this.width + x % this.width] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short get(int x, int y) {
|
||||||
|
return this.cache[y % this.height * this.width + x % this.width];
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,14 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
maven { url "https://dl.cloudsmith.io/public/arcane/archive/maven/" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rootProject.name = 'Iris'
|
rootProject.name = 'Iris'
|
||||||
include 'bukkit'
|
include 'bukkit'
|
||||||
|
include 'engine'
|
||||||
|
include 'engine'
|
||||||
|
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
package com.volmit.iris.engine;
|
|
||||||
|
|
||||||
import com.volmit.iris.platform.IrisPlatform;
|
|
||||||
import com.volmit.iris.platform.PlatformRegistry;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class IrisEngine {
|
|
||||||
private IrisPlatform platform;
|
|
||||||
private EngineRegistry registry;
|
|
||||||
private EngineConfiguration configuration;
|
|
||||||
|
|
||||||
public IrisEngine(IrisPlatform platform, EngineConfiguration configuration) {
|
|
||||||
this.configuration = configuration;
|
|
||||||
this.platform = platform;
|
|
||||||
this.registry = EngineRegistry.builder()
|
|
||||||
.blockRegistry(new PlatformRegistry<>(platform.getBlocks()))
|
|
||||||
.biomeRegistry(new PlatformRegistry<>(platform.getBiomes()))
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user