11 Commits
Author SHA1 Message Date
NotAlexNoyle 9f8bf11660 Exclude CODEOWNERS from language statistics 2026-10-29 16:18:54 -04:00
NotAlexNoyle f8dcf33fcb Add CODEOWNERS (one staff approval) 2026-10-29 16:18:54 -04:00
NotAlexNoyle 116976d8e8 Merge branch 'master' of github.com:true-og/Chunky 2026-06-17 06:47:56 -04:00
NotAlexNoyle 800a4fa743 Update config 2026-06-17 06:47:36 -04:00
NotAlexNoyle 3124d52e3b Merge pull request #2 from true-og/codex/fix-runtime-error-in-gradle-jar-build
Fix Bukkit plugin main class relocation in shaded JAR
2026-03-13 13:42:30 -04:00
NotAlexNoyle b973781649 Fix Bukkit shadowJar relocation for plugin main class 2026-03-13 13:42:19 -04:00
NotAlexNoyle 107bb79474 Merge pull request #1 from true-og/codex/fix-jar-build-for-minecraft-plugin
Produce shaded Bukkit plugin jar for Purpur 1.19.4 and bundle/relocate dependencies
2026-03-13 09:42:12 -04:00
NotAlexNoyle f35cb20714 Fix Bukkit plugin packaging and Purpur target 2026-03-13 09:40:54 -04:00
NotAlexNoyle aab04df0b6 Update gradle to 8.14.3 2025-09-17 10:24:14 -04:00
NotAlexNoyle eda3874ba7 Remove unneeded subprojects 2025-06-11 15:31:36 -04:00
NotAlexNoyle 609fea09f6 Fix build 2024-12-08 14:07:26 -05:00
79 changed files with 207 additions and 3871 deletions

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="output" path="bin/default"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/>
</classpath>
+1
View File
@@ -0,0 +1 @@
.gitea/CODEOWNERS linguist-detectable=false
+3
View File
@@ -0,0 +1,3 @@
# Pull requests request review from the eligible staff team(s).
# One approval from either an Admin or Developer satisfies branch protection.
* @trueog/Admins @trueog/Developers
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Chunky</name>
<comment>Pre-generates chunks, quickly, efficiently, and safely</comment>
<projects/>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments/>
</buildCommand>
</buildSpec>
<linkedResources/>
<filteredResources/>
</projectDescription>
+46 -24
View File
@@ -1,17 +1,21 @@
import java.io.ByteArrayOutputStream
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.api.tasks.Sync
import org.gradle.api.tasks.bundling.Jar
plugins {
eclipse
id("java-library")
id("maven-publish")
id("com.github.johnrengelman.shadow") version "8.1.1"
id("com.gradleup.shadow") version "8.3.9" apply false
}
subprojects {
plugins.apply("java-library")
plugins.apply("maven-publish")
plugins.apply("com.github.johnrengelman.shadow")
plugins.apply("com.gradleup.shadow")
group = "${project.property("group")}"
group = project.property("group") as String
version = "${project.property("version")}.${commitsSinceLastTag()}"
repositories {
@@ -20,9 +24,7 @@ subprojects {
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
withSourcesJar()
}
@@ -32,49 +34,69 @@ subprojects {
options.release = 17
options.compilerArgs.add("-Xlint:none")
}
jar {
withType<AbstractArchiveTask> {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
}
named<Jar>("jar") {
archiveClassifier.set("noshade")
}
shadowJar {
named<ShadowJar>("shadowJar") {
archiveClassifier.set("")
archiveFileName.set("${project.property("artifactName")}-${project.version}.jar")
isEnableRelocation = true
relocationPrefix = "${project.group}.${rootProject.name.lowercase()}.shadow"
}
build {
dependsOn(shadowJar)
named("build") {
dependsOn("shadowJar")
}
}
publishing {
repositories {
if (project.hasProperty("mavenUsername") && project.hasProperty("mavenPassword")) {
if (hasProperty("mavenUsername") && hasProperty("mavenPassword")) {
maven {
credentials {
username = "${project.property("mavenUsername")}"
password = "${project.property("mavenPassword")}"
username = project.property("mavenUsername") as String
password = project.property("mavenPassword") as String
}
url = uri("https://repo.codemc.io/repository/maven-releases/")
}
}
}
publications {
create<MavenPublication>("maven") {
groupId = "${project.group}"
artifactId = project.name
version = "${project.version}"
from(components["java"])
}
publications.create<MavenPublication>("maven") {
groupId = project.group.toString()
artifactId = project.name
version = project.version.toString()
from(components["java"])
}
}
}
tasks.named<Jar>("jar") {
enabled = false
}
val pluginArtifact by tasks.registering(Sync::class) {
dependsOn(":Chunky-bukkit:shadowJar")
from(project(":Chunky-bukkit").tasks.named("shadowJar"))
into(layout.buildDirectory.dir("libs"))
}
tasks.named("build") {
dependsOn(pluginArtifact)
}
fun commitsSinceLastTag(): String {
val tagDescription = ByteArrayOutputStream()
exec {
commandLine("git", "describe", "--tags")
standardOutput = tagDescription
}
if (tagDescription.toString().indexOf('-') < 0) {
return "0"
}
return tagDescription.toString().split('-')[1]
val desc = tagDescription.toString().trim()
return if ('-' !in desc) "0" else desc.split('-')[1]
}
+15 -8
View File
@@ -1,18 +1,23 @@
plugins {
id("com.gradleup.shadow")
}
repositories {
maven("https://repo.purpurmc.org/snapshots")
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://jitpack.io")
}
dependencies {
compileOnly(group = "org.spigotmc", name = "spigot-api", version = "1.20.4-R0.1-SNAPSHOT")
compileOnly(group = "org.purpurmc.purpur", name = "purpur-api", version = "1.19.4-R0.1-SNAPSHOT")
compileOnly(group = "com.github.Puremin0rez", name = "WorldBorder", version = "1.19") {
isTransitive = false
}
implementation(group = "org.bstats", name = "bstats-bukkit", version = "3.0.2")
implementation(project(":chunky-common"))
implementation(project(":chunky-paper"))
implementation(project(":chunky-folia"))
implementation(project(":Chunky-common"))
implementation(project(":Chunky-paper"))
implementation(project(":Chunky-folia"))
}
tasks {
@@ -28,11 +33,13 @@ tasks {
}
}
shadowJar {
archiveClassifier.set("")
isEnableRelocation = false
minimize {
exclude(project(":chunky-common"))
exclude(project(":chunky-paper"))
exclude(project(":chunky-folia"))
exclude(project(":Chunky-common"))
exclude(project(":Chunky-paper"))
exclude(project(":Chunky-folia"))
}
relocate("org.bstats", "${project.group}.${rootProject.name}.lib.bstats")
relocate("org.bstats", "${project.group}.${rootProject.name.lowercase()}.lib.bstats")
}
}
+4 -1
View File
@@ -1,6 +1,9 @@
# Chunky Configuration
# https://github.com/pop4959/Chunky/wiki/Configuration
version: 2
language: en
continue-on-restart: false
continue-on-restart: true
force-load-existing-chunks: false
silent: false
update-interval: 1
+1 -1
View File
@@ -2,7 +2,7 @@ dependencies {
compileOnly(group = "com.google.code.gson", name = "gson", version = "2.8.9")
testImplementation(group = "junit", name = "junit", version = "4.13.2")
testImplementation(group = "com.google.code.gson", name = "gson", version = "2.8.9")
implementation(project(":chunky-nbt"))
implementation(project(":Chunky-nbt"))
}
tasks {
-39
View File
@@ -1,39 +0,0 @@
plugins {
id("dev.architectury.loom") version "1.5-SNAPSHOT"
}
val shade: Configuration by configurations.creating
dependencies {
minecraft(group = "com.mojang", name = "minecraft", version = "1.20")
mappings(group = "net.fabricmc", name = "yarn", version = "1.20+build.1", classifier = "v2")
modImplementation(group = "net.fabricmc", name = "fabric-loader", version = "0.14.21")
modImplementation(group = "net.fabricmc.fabric-api", name = "fabric-api", version = "0.83.0+1.20")
modCompileOnly(group = "me.lucko", name = "fabric-permissions-api", version = "0.2-SNAPSHOT")
implementation(project(":chunky-common"))
shade(project(":chunky-common"))
}
tasks {
processResources {
filesMatching("fabric.mod.json") {
expand(
"id" to rootProject.name,
"version" to project.version,
"name" to project.property("artifactName"),
"description" to project.property("description"),
"author" to project.property("author"),
"github" to project.property("github")
)
}
}
shadowJar {
configurations = listOf(shade)
archiveClassifier.set("dev")
archiveFileName.set(null as String?)
}
remapJar {
inputFile.set(shadowJar.get().archiveFile)
archiveFileName.set("${project.property("artifactName")}-${project.version}.jar")
}
}
@@ -1,174 +0,0 @@
package org.popcraft.chunky;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.entity.boss.ServerBossBar;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import org.popcraft.chunky.command.ChunkyCommand;
import org.popcraft.chunky.command.CommandArguments;
import org.popcraft.chunky.command.CommandLiteral;
import org.popcraft.chunky.command.suggestion.SuggestionProviders;
import org.popcraft.chunky.event.task.GenerationTaskFinishEvent;
import org.popcraft.chunky.event.task.GenerationTaskUpdateEvent;
import org.popcraft.chunky.listeners.bossbar.BossBarTaskFinishListener;
import org.popcraft.chunky.listeners.bossbar.BossBarTaskUpdateListener;
import org.popcraft.chunky.platform.FabricPlayer;
import org.popcraft.chunky.platform.FabricSender;
import org.popcraft.chunky.platform.FabricServer;
import org.popcraft.chunky.platform.Sender;
import org.popcraft.chunky.platform.impl.GsonConfig;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
import static com.mojang.brigadier.arguments.StringArgumentType.string;
import static com.mojang.brigadier.arguments.StringArgumentType.word;
import static net.minecraft.command.argument.DimensionArgumentType.dimension;
import static net.minecraft.command.argument.EntityArgumentType.player;
import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;
public class ChunkyFabric implements ModInitializer {
private Chunky chunky;
private final Map<Identifier, ServerBossBar> bossBars = new ConcurrentHashMap<>();
@Override
public void onInitialize() {
ServerLifecycleEvents.SERVER_STARTED.register(minecraftServer -> {
final Path configPath = FabricLoader.getInstance().getConfigDir().resolve("chunky/config.json");
this.chunky = new Chunky(new FabricServer(this, minecraftServer), new GsonConfig(configPath));
if (chunky.getConfig().getContinueOnRestart()) {
chunky.getCommands().get(CommandLiteral.CONTINUE).execute(chunky.getServer().getConsole(), CommandArguments.empty());
}
chunky.getEventBus().subscribe(GenerationTaskUpdateEvent.class, new BossBarTaskUpdateListener(bossBars));
chunky.getEventBus().subscribe(GenerationTaskFinishEvent.class, new BossBarTaskFinishListener(bossBars));
FabricLoader.getInstance().getEntrypointContainers("chunky", ModInitializer.class)
.forEach(entryPoint -> entryPoint.getEntrypoint().onInitialize());
});
ServerLifecycleEvents.SERVER_STOPPING.register(minecraftServer -> {
if (chunky != null) {
chunky.disable();
}
});
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
final LiteralArgumentBuilder<ServerCommandSource> command = literal(CommandLiteral.CHUNKY)
.requires(serverCommandSource -> {
if (serverCommandSource.getServer().isSingleplayer()) {
return true;
}
return new FabricSender(serverCommandSource).hasPermission("chunky.command", true);
})
.executes(context -> {
final Sender sender;
if (context.getSource().getEntity() instanceof final ServerPlayerEntity player) {
sender = new FabricPlayer(player);
} else {
sender = new FabricSender(context.getSource());
}
final Map<String, ChunkyCommand> commands = chunky.getCommands();
final String input = context.getInput().substring(context.getLastChild().getNodes().get(0).getRange().getStart());
final String[] tokens = input.split(" ");
final String subCommand = tokens.length > 1 && commands.containsKey(tokens[1]) ? tokens[1] : CommandLiteral.HELP;
final CommandArguments arguments = tokens.length > 2 ? CommandArguments.of(Arrays.copyOfRange(tokens, 2, tokens.length)) : CommandArguments.empty();
commands.get(subCommand).execute(sender, arguments);
return Command.SINGLE_SUCCESS;
});
registerArguments(command, literal(CommandLiteral.CANCEL),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(command, literal(CommandLiteral.CENTER),
argument(CommandLiteral.X, word()),
argument(CommandLiteral.Z, word()));
registerArguments(command, literal(CommandLiteral.CONFIRM));
registerArguments(command, literal(CommandLiteral.CONTINUE),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(command, literal(CommandLiteral.CORNERS),
argument(CommandLiteral.X1, word()),
argument(CommandLiteral.Z1, word()),
argument(CommandLiteral.X2, word()),
argument(CommandLiteral.Z2, word()));
registerArguments(command, literal(CommandLiteral.HELP),
argument(CommandLiteral.PAGE, integer()));
registerArguments(command, literal(CommandLiteral.PATTERN),
argument(CommandLiteral.PATTERN, string()).suggests(SuggestionProviders.PATTERNS),
argument(CommandLiteral.VALUE, string()));
registerArguments(command, literal(CommandLiteral.PAUSE),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(command, literal(CommandLiteral.PROGRESS));
registerArguments(command, literal(CommandLiteral.QUIET),
argument(CommandLiteral.INTERVAL, integer()));
registerArguments(command, literal(CommandLiteral.RADIUS),
argument(CommandLiteral.RADIUS, word()),
argument(CommandLiteral.RADIUS, word()));
registerArguments(command, literal(CommandLiteral.RELOAD),
argument(CommandLiteral.TYPE, word()));
registerArguments(command, literal(CommandLiteral.SELECTION));
registerArguments(command, literal(CommandLiteral.SHAPE),
argument(CommandLiteral.SHAPE, string()).suggests(SuggestionProviders.SHAPES));
registerArguments(command, literal(CommandLiteral.SILENT));
registerArguments(command, literal(CommandLiteral.SPAWN));
registerArguments(command, literal(CommandLiteral.START),
argument(CommandLiteral.WORLD, dimension()),
argument(CommandLiteral.SHAPE, string()).suggests(SuggestionProviders.SHAPES),
argument(CommandLiteral.CENTER_X, word()),
argument(CommandLiteral.CENTER_Z, word()),
argument(CommandLiteral.RADIUS_X, word()),
argument(CommandLiteral.RADIUS_Z, word()));
registerArguments(command, literal(CommandLiteral.TRIM),
argument(CommandLiteral.WORLD, dimension()),
argument(CommandLiteral.SHAPE, string()).suggests(SuggestionProviders.SHAPES),
argument(CommandLiteral.CENTER_X, word()),
argument(CommandLiteral.CENTER_Z, word()),
argument(CommandLiteral.RADIUS_X, word()),
argument(CommandLiteral.RADIUS_Z, word()),
argument(CommandLiteral.TRIM_MODE, string()).suggests(SuggestionProviders.TRIM_MODES),
argument(CommandLiteral.INHABITED, word()));
registerArguments(command, literal(CommandLiteral.WORLDBORDER));
registerArguments(command, literal(CommandLiteral.WORLD),
argument(CommandLiteral.WORLD, dimension()));
final LiteralArgumentBuilder<ServerCommandSource> borderCommand = literal(CommandLiteral.BORDER)
.requires(serverCommandSource -> chunky != null && chunky.getCommands().containsKey(CommandLiteral.BORDER))
.executes(command.getCommand());
registerArguments(borderCommand, literal(CommandLiteral.ADD),
argument(CommandLiteral.WORLD, dimension()),
argument(CommandLiteral.SHAPE, string()).suggests(SuggestionProviders.SHAPES),
argument(CommandLiteral.CENTER_X, word()),
argument(CommandLiteral.CENTER_Z, word()),
argument(CommandLiteral.RADIUS_X, word()),
argument(CommandLiteral.RADIUS_Z, word()));
registerArguments(borderCommand, literal(CommandLiteral.BYPASS),
argument(CommandLiteral.PLAYER, player()));
registerArguments(borderCommand, literal(CommandLiteral.HELP));
registerArguments(borderCommand, literal(CommandLiteral.LIST));
registerArguments(borderCommand, literal(CommandLiteral.LOAD),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(borderCommand, literal(CommandLiteral.REMOVE),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(borderCommand, literal(CommandLiteral.WRAP),
argument(CommandLiteral.WRAP, word()));
registerArguments(command, borderCommand);
dispatcher.register(command);
});
}
@SafeVarargs
private <S> void registerArguments(final LiteralArgumentBuilder<S> command, final ArgumentBuilder<S, ?>... arguments) {
for (int i = arguments.length - 1; i > 0; --i) {
arguments[i - 1].then(arguments[i].executes(command.getCommand()));
}
command.then(arguments[0].executes(command.getCommand()));
}
public Chunky getChunky() {
return chunky;
}
}
@@ -1,28 +0,0 @@
package org.popcraft.chunky.command.suggestion;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.server.command.ServerCommandSource;
import org.popcraft.chunky.command.CommandLiteral;
import org.popcraft.chunky.iterator.PatternType;
import java.util.concurrent.CompletableFuture;
public class PatternSuggestionProvider implements SuggestionProvider<ServerCommandSource> {
@Override
public CompletableFuture<Suggestions> getSuggestions(final CommandContext<ServerCommandSource> context, final SuggestionsBuilder builder) {
try {
final String input = context.getArgument(CommandLiteral.PATTERN, String.class);
PatternType.ALL.forEach(pattern -> {
if (pattern.contains(input.toLowerCase())) {
builder.suggest(pattern);
}
});
} catch (IllegalArgumentException e) {
PatternType.ALL.forEach(builder::suggest);
}
return builder.buildFuture();
}
}
@@ -1,28 +0,0 @@
package org.popcraft.chunky.command.suggestion;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.server.command.ServerCommandSource;
import org.popcraft.chunky.command.CommandLiteral;
import org.popcraft.chunky.shape.ShapeType;
import java.util.concurrent.CompletableFuture;
public class ShapeSuggestionProvider implements SuggestionProvider<ServerCommandSource> {
@Override
public CompletableFuture<Suggestions> getSuggestions(final CommandContext<ServerCommandSource> context, final SuggestionsBuilder builder) {
try {
final String input = context.getArgument(CommandLiteral.SHAPE, String.class);
ShapeType.all().forEach(shape -> {
if (shape.contains(input.toLowerCase())) {
builder.suggest(shape);
}
});
} catch (IllegalArgumentException e) {
ShapeType.all().forEach(builder::suggest);
}
return builder.buildFuture();
}
}
@@ -1,19 +0,0 @@
package org.popcraft.chunky.command.suggestion;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import net.minecraft.server.command.ServerCommandSource;
public final class SuggestionProviders {
public static final SuggestionProvider<ServerCommandSource> PATTERNS;
public static final SuggestionProvider<ServerCommandSource> SHAPES;
public static final SuggestionProvider<ServerCommandSource> TRIM_MODES;
static {
PATTERNS = new PatternSuggestionProvider();
SHAPES = new ShapeSuggestionProvider();
TRIM_MODES = new TrimModeSuggestionProvider();
}
private SuggestionProviders() {
}
}
@@ -1,30 +0,0 @@
package org.popcraft.chunky.command.suggestion;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.server.command.ServerCommandSource;
import org.popcraft.chunky.command.CommandLiteral;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class TrimModeSuggestionProvider implements SuggestionProvider<ServerCommandSource> {
private static final List<String> TRIM_MODES = List.of("inside", "outside");
@Override
public CompletableFuture<Suggestions> getSuggestions(final CommandContext<ServerCommandSource> context, final SuggestionsBuilder builder) {
try {
final String input = context.getArgument(CommandLiteral.TRIM_MODE, String.class);
TRIM_MODES.forEach(shape -> {
if (shape.contains(input.toLowerCase())) {
builder.suggest(shape);
}
});
} catch (IllegalArgumentException e) {
TRIM_MODES.forEach(builder::suggest);
}
return builder.buildFuture();
}
}
@@ -1,33 +0,0 @@
package org.popcraft.chunky.listeners.bossbar;
import net.minecraft.entity.boss.ServerBossBar;
import net.minecraft.util.Identifier;
import org.popcraft.chunky.GenerationTask;
import org.popcraft.chunky.event.task.GenerationTaskFinishEvent;
import org.popcraft.chunky.platform.World;
import java.util.Map;
import java.util.function.Consumer;
public class BossBarTaskFinishListener implements Consumer<GenerationTaskFinishEvent> {
private final Map<Identifier, ServerBossBar> bossBars;
public BossBarTaskFinishListener(final Map<Identifier, ServerBossBar> bossBars) {
this.bossBars = bossBars;
}
@Override
public void accept(final GenerationTaskFinishEvent event) {
final GenerationTask task = event.generationTask();
final World world = task.getSelection().world();
final Identifier worldIdentifier = Identifier.tryParse(world.getKey());
if (worldIdentifier == null) {
return;
}
final ServerBossBar bossBar = bossBars.get(worldIdentifier);
if (bossBar != null) {
bossBar.clearPlayers();
bossBars.remove(worldIdentifier);
}
}
}
@@ -1,86 +0,0 @@
package org.popcraft.chunky.listeners.bossbar;
import net.minecraft.entity.boss.BossBar;
import net.minecraft.entity.boss.ServerBossBar;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.popcraft.chunky.Chunky;
import org.popcraft.chunky.GenerationTask;
import org.popcraft.chunky.event.task.GenerationTaskUpdateEvent;
import org.popcraft.chunky.platform.FabricWorld;
import org.popcraft.chunky.platform.World;
import java.util.Map;
import java.util.function.Consumer;
public class BossBarTaskUpdateListener implements Consumer<GenerationTaskUpdateEvent> {
private final Map<Identifier, ServerBossBar> bossBars;
public BossBarTaskUpdateListener(final Map<Identifier, ServerBossBar> bossBars) {
this.bossBars = bossBars;
}
@Override
public void accept(final GenerationTaskUpdateEvent event) {
final GenerationTask task = event.generationTask();
final Chunky chunky = task.getChunky();
final World world = task.getSelection().world();
final Identifier worldIdentifier = Identifier.tryParse(world.getKey());
if (worldIdentifier == null || !(world instanceof final FabricWorld fabricWorld)) {
return;
}
final ServerBossBar bossBar = bossBars.computeIfAbsent(worldIdentifier, x -> createNewBossBar(worldIdentifier));
final boolean silent = chunky.getConfig().isSilent();
if (silent == bossBar.isVisible()) {
bossBar.setVisible(!silent);
}
final MinecraftServer server = fabricWorld.getServerWorld().getServer();
for (final ServerPlayerEntity player : server.getPlayerManager().getPlayerList()) {
if (player.hasPermissionLevel(2)) {
bossBar.addPlayer(player);
} else {
bossBar.removePlayer(player);
}
}
final GenerationTask.Progress progress = task.getProgress();
bossBar.setName(Text.of(String.format("%s | %s%% | %s:%s:%s",
worldIdentifier,
String.format("%.2f", progress.getPercentComplete()),
String.format("%01d", progress.getHours()),
String.format("%02d", progress.getMinutes()),
String.format("%02d", progress.getSeconds()))));
bossBar.setPercent(progress.getPercentComplete() / 100f);
if (progress.isComplete()) {
bossBar.clearPlayers();
bossBars.remove(worldIdentifier);
}
}
private ServerBossBar createNewBossBar(final Identifier worldIdentifier) {
final ServerBossBar bossBar = new ServerBossBar(
Text.of(worldIdentifier.toString()),
bossBarColor(worldIdentifier),
BossBar.Style.PROGRESS
);
bossBar.setDarkenSky(false);
bossBar.setDragonMusic(false);
bossBar.setThickenFog(false);
return bossBar;
}
private static BossBar.Color bossBarColor(Identifier worldIdentifier) {
final BossBar.Color bossBarColor;
if (net.minecraft.world.World.OVERWORLD.getValue().equals(worldIdentifier)) {
bossBarColor = BossBar.Color.GREEN;
} else if (net.minecraft.world.World.NETHER.getValue().equals(worldIdentifier)) {
bossBarColor = BossBar.Color.RED;
} else if (net.minecraft.world.World.END.getValue().equals(worldIdentifier)) {
bossBarColor = BossBar.Color.PURPLE;
} else {
bossBarColor = BossBar.Color.BLUE;
}
return bossBarColor;
}
}
@@ -1,12 +0,0 @@
package org.popcraft.chunky.mixin;
import net.minecraft.server.world.ServerChunkManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(ServerChunkManager.class)
public interface ServerChunkManagerMixin {
@Invoker("tick")
@SuppressWarnings({"UnusedReturnValue", "UnnecessaryInterfaceModifier"})
public boolean invokeTick();
}
@@ -1,26 +0,0 @@
package org.popcraft.chunky.mixin;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.world.ChunkHolder;
import net.minecraft.server.world.ThreadedAnvilChunkStorage;
import net.minecraft.util.math.ChunkPos;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@SuppressWarnings("UnnecessaryInterfaceModifier")
@Mixin(ThreadedAnvilChunkStorage.class)
public interface ThreadedAnvilChunkStorageMixin {
@Invoker("getChunkHolder")
public ChunkHolder invokeGetChunkHolder(long pos);
@Invoker("getUpdatedChunkNbt")
public CompletableFuture<Optional<NbtCompound>> invokeGetUpdatedChunkNbt(ChunkPos pos);
@Accessor("chunksToUnload")
public Long2ObjectLinkedOpenHashMap<ChunkHolder> getChunksToUnload();
}
@@ -1,33 +0,0 @@
package org.popcraft.chunky.platform;
import net.minecraft.world.border.WorldBorder;
import org.popcraft.chunky.platform.util.Vector2;
import org.popcraft.chunky.shape.ShapeType;
public class FabricBorder implements Border {
private final WorldBorder worldBorder;
public FabricBorder(final WorldBorder worldBorder) {
this.worldBorder = worldBorder;
}
@Override
public Vector2 getCenter() {
return Vector2.of(worldBorder.getCenterX(), worldBorder.getCenterZ());
}
@Override
public double getRadiusX() {
return worldBorder.getSize() / 2d;
}
@Override
public double getRadiusZ() {
return worldBorder.getSize() / 2d;
}
@Override
public String getShape() {
return ShapeType.SQUARE;
}
}
@@ -1,62 +0,0 @@
package org.popcraft.chunky.platform;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import org.popcraft.chunky.platform.util.Location;
import java.util.UUID;
import static org.popcraft.chunky.util.Translator.translateKey;
public class FabricPlayer extends FabricSender implements Player {
private final ServerPlayerEntity player;
public FabricPlayer(final ServerPlayerEntity player) {
super(player.getCommandSource());
this.player = player;
}
@Override
public boolean isPlayer() {
return true;
}
@Override
public String getName() {
return player.getName().getString();
}
@Override
public World getWorld() {
return new FabricWorld(player.getServerWorld());
}
@Override
public Location getLocation() {
return new Location(getWorld(), player.getX(), player.getY(), player.getZ(), player.getYaw(), player.getPitch());
}
@Override
public void sendMessage(final String key, final boolean prefixed, final Object... args) {
player.sendMessage(formatColored(translateKey(key, prefixed, args)), false);
}
@Override
public UUID getUUID() {
return player.getUuid();
}
@Override
public void teleport(final Location location) {
player.teleport(((FabricWorld) location.getWorld()).getServerWorld(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
}
@Override
public void sendActionBar(final String key) {
player.sendMessage(formatColored(translateKey(key, false)), true);
}
private Text formatColored(final String message) {
return Text.of(message.replaceAll("&(?=[0-9a-fk-orA-FK-OR])", "§"));
}
}
@@ -1,76 +0,0 @@
package org.popcraft.chunky.platform;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.util.math.Position;
import net.minecraft.util.math.Vec2f;
import org.popcraft.chunky.platform.util.Location;
import static org.popcraft.chunky.util.Translator.translateKey;
public class FabricSender implements Sender {
private static final boolean HAS_PERMISSIONS;
static {
boolean hasPermissions;
try {
Class.forName("me.lucko.fabric.api.permissions.v0.Permissions");
hasPermissions = true;
} catch (ClassNotFoundException e) {
hasPermissions = false;
}
HAS_PERMISSIONS = hasPermissions;
}
private final ServerCommandSource source;
public FabricSender(final ServerCommandSource source) {
this.source = source;
}
@Override
public boolean isPlayer() {
return source.getEntity() instanceof ServerPlayerEntity;
}
@Override
public String getName() {
return source.getName();
}
@Override
public World getWorld() {
return new FabricWorld(source.getWorld());
}
@Override
public Location getLocation() {
final Position pos = source.getPosition();
final Vec2f rotation = source.getRotation();
return new Location(getWorld(), pos.getX(), pos.getY(), pos.getZ(), rotation.x, rotation.y);
}
@Override
public boolean hasPermission(final String permission) {
return hasPermission(permission, false);
}
public boolean hasPermission(final String permission, final boolean defaultOp) {
if (HAS_PERMISSIONS) {
if (defaultOp) {
return Permissions.check(source, permission, 2);
} else {
return Permissions.check(source, permission, false);
}
} else {
return source.hasPermissionLevel(2);
}
}
@Override
public void sendMessage(final String key, final boolean prefixed, final Object... args) {
source.sendFeedback(() -> Text.of(translateKey(key, prefixed, args).replaceAll("&[0-9a-fk-orA-FK-OR]", "")), false);
}
}
@@ -1,83 +0,0 @@
package org.popcraft.chunky.platform;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import org.popcraft.chunky.ChunkyFabric;
import org.popcraft.chunky.integration.Integration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class FabricServer implements Server {
private final ChunkyFabric plugin;
private final MinecraftServer server;
public FabricServer(final ChunkyFabric plugin, final MinecraftServer server) {
this.plugin = plugin;
this.server = server;
}
@Override
public Map<String, Integration> getIntegrations() {
return Map.of();
}
@Override
public Optional<World> getWorld(final String name) {
return Optional.ofNullable(Identifier.tryParse(name))
.map(worldIdentifier -> server.getWorld(RegistryKey.of(RegistryKeys.WORLD, worldIdentifier)))
.or(() -> {
for (final ServerWorld world : server.getWorlds()) {
if (name.equals(world.getRegistryKey().getValue().getPath())) {
return Optional.of(world);
}
}
return Optional.empty();
})
.map(FabricWorld::new);
}
@Override
public List<World> getWorlds() {
final List<World> worlds = new ArrayList<>();
server.getWorlds().forEach(world -> worlds.add(new FabricWorld(world)));
return worlds;
}
@Override
public int getMaxWorldSize() {
if (server instanceof final DedicatedServer dedicatedServer) {
return dedicatedServer.getProperties().maxWorldSize;
} else {
return server.getMaxWorldBorderRadius();
}
}
@Override
public Sender getConsole() {
return new FabricSender(server.getCommandSource());
}
@Override
public Collection<Player> getPlayers() {
return server.getPlayerManager().getPlayerList().stream().map(FabricPlayer::new).collect(Collectors.toList());
}
@Override
public Optional<Player> getPlayer(final String name) {
return Optional.ofNullable(server.getPlayerManager().getPlayer(name)).map(FabricPlayer::new);
}
@Override
public Config getConfig() {
return plugin.getChunky().getConfig();
}
}
@@ -1,194 +0,0 @@
package org.popcraft.chunky.platform;
import net.minecraft.block.BlockState;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtString;
import net.minecraft.nbt.scanner.NbtScanQuery;
import net.minecraft.nbt.scanner.SelectiveNbtCollector;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.world.ChunkHolder;
import net.minecraft.server.world.ChunkTicketType;
import net.minecraft.server.world.ServerChunkManager;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.server.world.ThreadedAnvilChunkStorage;
import net.minecraft.sound.SoundCategory;
import net.minecraft.util.Identifier;
import net.minecraft.util.Unit;
import net.minecraft.util.WorldSavePath;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.Heightmap;
import net.minecraft.world.chunk.ChunkStatus;
import net.minecraft.world.dimension.DimensionType;
import org.popcraft.chunky.mixin.ServerChunkManagerMixin;
import org.popcraft.chunky.mixin.ThreadedAnvilChunkStorageMixin;
import org.popcraft.chunky.platform.util.Location;
import org.popcraft.chunky.util.Input;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
public class FabricWorld implements World {
private static final int TICKING_LOAD_DURATION = Input.tryInteger(System.getProperty("chunky.tickingLoadDuration")).orElse(0);
private static final ChunkTicketType<Unit> CHUNKY = ChunkTicketType.create("chunky", (unit, unit2) -> 0);
private static final ChunkTicketType<Unit> CHUNKY_TICKING = ChunkTicketType.create("chunky_ticking", (unit, unit2) -> 0, TICKING_LOAD_DURATION * 20);
private static final boolean UPDATE_CHUNK_NBT = Boolean.getBoolean("chunky.updateChunkNbt");
private final ServerWorld serverWorld;
private final Border worldBorder;
public FabricWorld(final ServerWorld serverWorld) {
this.serverWorld = serverWorld;
this.worldBorder = new FabricBorder(serverWorld.getWorldBorder());
}
@Override
public String getName() {
return serverWorld.getRegistryKey().getValue().toString();
}
@Override
public String getKey() {
return getName();
}
@Override
public CompletableFuture<Boolean> isChunkGenerated(final int x, final int z) {
if (Thread.currentThread() != serverWorld.getServer().getThread()) {
return CompletableFuture.supplyAsync(() -> isChunkGenerated(x, z), serverWorld.getServer()).thenCompose(Function.identity());
} else {
final ChunkPos chunkPos = new ChunkPos(x, z);
final ServerChunkManager serverChunkManager = serverWorld.getChunkManager();
final ThreadedAnvilChunkStorage chunkStorage = serverChunkManager.threadedAnvilChunkStorage;
final ThreadedAnvilChunkStorageMixin chunkStorageMixin = (ThreadedAnvilChunkStorageMixin) chunkStorage;
final ChunkHolder loadedChunkHolder = chunkStorageMixin.invokeGetChunkHolder(chunkPos.toLong());
if (loadedChunkHolder != null && loadedChunkHolder.getCurrentStatus() == ChunkStatus.FULL) {
return CompletableFuture.completedFuture(true);
}
final ChunkHolder unloadedChunkHolder = chunkStorageMixin.getChunksToUnload().get(chunkPos.toLong());
if (unloadedChunkHolder != null && unloadedChunkHolder.getCurrentStatus() == ChunkStatus.FULL) {
return CompletableFuture.completedFuture(true);
}
if (UPDATE_CHUNK_NBT) {
return chunkStorageMixin.invokeGetUpdatedChunkNbt(chunkPos)
.thenApply(optionalNbt -> optionalNbt
.filter(chunkNbt -> chunkNbt.contains("Status", NbtElement.STRING_TYPE))
.map(chunkNbt -> chunkNbt.getString("Status"))
.map(status -> "minecraft:full".equals(status) || "full".equals(status))
.orElse(false));
}
final NbtScanQuery statusQuery = new NbtScanQuery(NbtString.TYPE, "Status");
final SelectiveNbtCollector statusCollector = new SelectiveNbtCollector(statusQuery);
return serverChunkManager.getChunkIoWorker().scanChunk(chunkPos, statusCollector)
.thenApply(ignored -> {
if (statusCollector.getRoot() instanceof final NbtCompound chunkNbt) {
final String status = chunkNbt.getString("Status");
return "minecraft:full".equals(status) || "full".equals(status);
}
return false;
});
}
}
@Override
public CompletableFuture<Void> getChunkAtAsync(final int x, final int z) {
if (Thread.currentThread() != serverWorld.getServer().getThread()) {
return CompletableFuture.supplyAsync(() -> getChunkAtAsync(x, z), serverWorld.getServer()).thenCompose(Function.identity());
} else {
final ChunkPos chunkPos = new ChunkPos(x, z);
final ServerChunkManager serverChunkManager = serverWorld.getChunkManager();
serverChunkManager.addTicket(CHUNKY, chunkPos, 0, Unit.INSTANCE);
if (TICKING_LOAD_DURATION > 0) {
serverChunkManager.addTicket(CHUNKY_TICKING, chunkPos, 1, Unit.INSTANCE);
}
((ServerChunkManagerMixin) serverChunkManager).invokeTick();
final ThreadedAnvilChunkStorage threadedAnvilChunkStorage = serverChunkManager.threadedAnvilChunkStorage;
final ThreadedAnvilChunkStorageMixin threadedAnvilChunkStorageMixin = (ThreadedAnvilChunkStorageMixin) threadedAnvilChunkStorage;
final ChunkHolder chunkHolder = threadedAnvilChunkStorageMixin.invokeGetChunkHolder(chunkPos.toLong());
final CompletableFuture<Void> chunkFuture = chunkHolder == null ? CompletableFuture.completedFuture(null) : CompletableFuture.allOf(chunkHolder.getChunkAt(ChunkStatus.FULL, threadedAnvilChunkStorage));
chunkFuture.whenCompleteAsync((ignored, throwable) -> serverChunkManager.removeTicket(CHUNKY, chunkPos, 0, Unit.INSTANCE), serverWorld.getServer());
return chunkFuture;
}
}
@Override
public UUID getUUID() {
throw new UnsupportedOperationException();
}
@Override
public int getSeaLevel() {
return serverWorld.getSeaLevel();
}
@Override
public Location getSpawn() {
final BlockPos pos = serverWorld.getSpawnPos();
final float angle = serverWorld.getSpawnAngle();
return new Location(this, pos.getX(), pos.getY(), pos.getZ(), angle, 0);
}
@Override
public Border getWorldBorder() {
return worldBorder;
}
@Override
public int getElevation(final int x, final int z) {
final int height = serverWorld.getChunk(x >> 4, z >> 4).sampleHeightmap(Heightmap.Type.MOTION_BLOCKING, x, z) + 1;
final int logicalHeight = serverWorld.getLogicalHeight();
if (height >= logicalHeight) {
BlockPos.Mutable pos = new BlockPos.Mutable(x, logicalHeight, z);
int air = 0;
while (pos.getY() > serverWorld.getBottomY()) {
pos = pos.move(Direction.DOWN);
final BlockState blockState = serverWorld.getBlockState(pos);
if (blockState.isSolid() && air > 1) {
return pos.getY() + 1;
}
air = blockState.isAir() ? air + 1 : 0;
}
}
return height;
}
@Override
public int getMaxElevation() {
return serverWorld.getLogicalHeight();
}
@Override
public void playEffect(final Player player, final String effect) {
final Location location = player.getLocation();
final BlockPos pos = BlockPos.ofFloored(location.getX(), location.getY(), location.getZ());
Input.tryInteger(effect).ifPresent(eventId -> serverWorld.syncWorldEvent(null, eventId, pos, 0));
}
@Override
public void playSound(final Player player, final String sound) {
final Location location = player.getLocation();
serverWorld.getServer()
.getRegistryManager()
.getOptional(RegistryKeys.SOUND_EVENT)
.flatMap(soundEventRegistry -> soundEventRegistry.getOrEmpty(Identifier.tryParse(sound)))
.ifPresent(soundEvent -> serverWorld.playSound(null, location.getX(), location.getY(), location.getZ(), soundEvent, SoundCategory.MASTER, 2f, 1f));
}
@Override
public Optional<Path> getDirectory(final String name) {
if (name == null) {
return Optional.empty();
}
final Path directory = DimensionType.getSaveDirectory(serverWorld.getRegistryKey(), serverWorld.getServer().getSavePath(WorldSavePath.ROOT)).normalize().resolve(name);
return Files.exists(directory) ? Optional.of(directory) : Optional.empty();
}
public ServerWorld getServerWorld() {
return serverWorld;
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

@@ -1,15 +0,0 @@
{
"required": true,
"minVersion": "0.8",
"package": "org.popcraft.chunky.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"ServerChunkManagerMixin",
"ThreadedAnvilChunkStorageMixin"
],
"client": [
],
"injectors": {
"defaultRequire": 1
}
}
-41
View File
@@ -1,41 +0,0 @@
{
"schemaVersion": 1,
"id": "${id}",
"version": "${version}",
"name": "${name}",
"description": "${description}",
"authors": [
"${author}"
],
"contact": {
"website": "https://www.curseforge.com/minecraft/mc-mods/chunky-pregenerator",
"homepage": "https://www.curseforge.com/minecraft/mc-mods/chunky-pregenerator",
"repo": "${github}",
"sources": "${github}",
"issues": "${github}/issues"
},
"license": "GPLv3",
"icon": "assets/chunky/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"org.popcraft.chunky.ChunkyFabric"
]
},
"mixins": [
"chunky.mixins.json"
],
"depends": {
"fabricloader": ">=0.14.21",
"fabric": "*",
"minecraft": ">=1.20.0",
"java": ">=17"
},
"custom": {
"modmenu": {
"links": {
"modmenu.discord": "https://discord.gg/ZwVJukcNQG"
}
}
}
}
-48
View File
@@ -1,48 +0,0 @@
plugins {
id("dev.architectury.loom") version "1.5-SNAPSHOT"
}
val shade: Configuration by configurations.creating
dependencies {
minecraft(group = "com.mojang", name = "minecraft", version = "1.20")
mappings(loom.officialMojangMappings())
forge(group = "net.minecraftforge", name = "forge", version = "1.20-46.0.10")
implementation(project(":chunky-common"))
shade(project(":chunky-common"))
}
tasks {
processResources {
filesMatching("META-INF/mods.toml") {
expand(
"github" to project.property("github"),
"id" to rootProject.name,
"version" to project.version,
"name" to project.property("artifactName"),
"author" to project.property("author"),
"description" to project.property("description")
)
}
}
jar {
manifest {
attributes(
mapOf(
"Implementation-Title" to rootProject.name,
"Implementation-Version" to project.version,
"Implementation-Vendor" to project.property("author")
)
)
}
}
shadowJar {
configurations = listOf(shade)
archiveClassifier.set("dev")
archiveFileName.set(null as String?)
}
remapJar {
inputFile.set(shadowJar.get().archiveFile)
archiveFileName.set("${project.property("artifactName")}-${project.version}.jar")
}
}
-1
View File
@@ -1 +0,0 @@
loom.platform=forge
@@ -1,185 +0,0 @@
package org.popcraft.chunky;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerBossEvent;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.event.server.ServerStoppingEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.loading.FMLPaths;
import org.popcraft.chunky.command.ChunkyCommand;
import org.popcraft.chunky.command.CommandArguments;
import org.popcraft.chunky.command.CommandLiteral;
import org.popcraft.chunky.command.suggestion.SuggestionProviders;
import org.popcraft.chunky.event.task.GenerationTaskFinishEvent;
import org.popcraft.chunky.event.task.GenerationTaskUpdateEvent;
import org.popcraft.chunky.listeners.bossbar.BossBarTaskFinishListener;
import org.popcraft.chunky.listeners.bossbar.BossBarTaskUpdateListener;
import org.popcraft.chunky.platform.ForgePlayer;
import org.popcraft.chunky.platform.ForgeSender;
import org.popcraft.chunky.platform.ForgeServer;
import org.popcraft.chunky.platform.Sender;
import org.popcraft.chunky.platform.impl.GsonConfig;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
import static com.mojang.brigadier.arguments.StringArgumentType.string;
import static com.mojang.brigadier.arguments.StringArgumentType.word;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
import static net.minecraft.commands.arguments.DimensionArgument.dimension;
import static net.minecraft.commands.arguments.EntityArgument.player;
@Mod(ChunkyForge.MOD_ID)
public class ChunkyForge {
public static final String MOD_ID = "chunky";
private Chunky chunky;
private final Map<ResourceLocation, ServerBossEvent> bossBars = new ConcurrentHashMap<>();
public ChunkyForge() {
MinecraftForge.EVENT_BUS.register(this);
}
@SubscribeEvent
public void onServerStarting(final ServerStartingEvent event) {
final MinecraftServer server = event.getServer();
final Path configPath = FMLPaths.CONFIGDIR.get().resolve("chunky/config.json");
this.chunky = new Chunky(new ForgeServer(this, server), new GsonConfig(configPath));
if (chunky.getConfig().getContinueOnRestart()) {
chunky.getCommands().get(CommandLiteral.CONTINUE).execute(chunky.getServer().getConsole(), CommandArguments.empty());
}
chunky.getEventBus().subscribe(GenerationTaskUpdateEvent.class, new BossBarTaskUpdateListener(bossBars));
chunky.getEventBus().subscribe(GenerationTaskFinishEvent.class, new BossBarTaskFinishListener(bossBars));
}
@SubscribeEvent
public void onRegisterCommands(final RegisterCommandsEvent event) {
final LiteralArgumentBuilder<CommandSourceStack> command = literal(CommandLiteral.CHUNKY)
.requires(serverCommandSource -> {
if (serverCommandSource.getServer().isSingleplayer()) {
return true;
}
return serverCommandSource.hasPermission(2);
})
.executes(context -> {
final Sender sender;
if (context.getSource().getEntity() instanceof final ServerPlayer player) {
sender = new ForgePlayer(player);
} else {
sender = new ForgeSender(context.getSource());
}
final Map<String, ChunkyCommand> commands = chunky.getCommands();
final String input = context.getInput().substring(context.getLastChild().getNodes().get(0).getRange().getStart());
final String[] tokens = input.split(" ");
final String subCommand = tokens.length > 1 && commands.containsKey(tokens[1]) ? tokens[1] : CommandLiteral.HELP;
final CommandArguments arguments = tokens.length > 2 ? CommandArguments.of(Arrays.copyOfRange(tokens, 2, tokens.length)) : CommandArguments.empty();
commands.get(subCommand).execute(sender, arguments);
return Command.SINGLE_SUCCESS;
});
registerArguments(command, literal(CommandLiteral.CANCEL),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(command, literal(CommandLiteral.CENTER),
argument(CommandLiteral.X, word()),
argument(CommandLiteral.Z, word()));
registerArguments(command, literal(CommandLiteral.CONFIRM));
registerArguments(command, literal(CommandLiteral.CONTINUE),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(command, literal(CommandLiteral.CORNERS),
argument(CommandLiteral.X1, word()),
argument(CommandLiteral.Z1, word()),
argument(CommandLiteral.X2, word()),
argument(CommandLiteral.Z2, word()));
registerArguments(command, literal(CommandLiteral.HELP),
argument(CommandLiteral.PAGE, integer()));
registerArguments(command, literal(CommandLiteral.PATTERN),
argument(CommandLiteral.PATTERN, string()).suggests(SuggestionProviders.PATTERNS),
argument(CommandLiteral.VALUE, string()));
registerArguments(command, literal(CommandLiteral.PAUSE),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(command, literal(CommandLiteral.PROGRESS));
registerArguments(command, literal(CommandLiteral.QUIET),
argument(CommandLiteral.INTERVAL, integer()));
registerArguments(command, literal(CommandLiteral.RADIUS),
argument(CommandLiteral.RADIUS, word()),
argument(CommandLiteral.RADIUS, word()));
registerArguments(command, literal(CommandLiteral.RELOAD),
argument(CommandLiteral.TYPE, word()));
registerArguments(command, literal(CommandLiteral.SELECTION));
registerArguments(command, literal(CommandLiteral.SHAPE),
argument(CommandLiteral.SHAPE, string()).suggests(SuggestionProviders.SHAPES));
registerArguments(command, literal(CommandLiteral.SILENT));
registerArguments(command, literal(CommandLiteral.SPAWN));
registerArguments(command, literal(CommandLiteral.START),
argument(CommandLiteral.WORLD, dimension()),
argument(CommandLiteral.SHAPE, string()).suggests(SuggestionProviders.SHAPES),
argument(CommandLiteral.CENTER_X, word()),
argument(CommandLiteral.CENTER_Z, word()),
argument(CommandLiteral.RADIUS_X, word()),
argument(CommandLiteral.RADIUS_Z, word()));
registerArguments(command, literal(CommandLiteral.TRIM),
argument(CommandLiteral.WORLD, dimension()),
argument(CommandLiteral.SHAPE, string()).suggests(SuggestionProviders.SHAPES),
argument(CommandLiteral.CENTER_X, word()),
argument(CommandLiteral.CENTER_Z, word()),
argument(CommandLiteral.RADIUS_X, word()),
argument(CommandLiteral.RADIUS_Z, word()),
argument(CommandLiteral.TRIM_MODE, string()).suggests(SuggestionProviders.TRIM_MODES),
argument(CommandLiteral.INHABITED, word()));
registerArguments(command, literal(CommandLiteral.WORLDBORDER));
registerArguments(command, literal(CommandLiteral.WORLD),
argument(CommandLiteral.WORLD, dimension()));
final LiteralArgumentBuilder<CommandSourceStack> borderCommand = literal(CommandLiteral.BORDER)
.requires(serverCommandSource -> chunky != null && chunky.getCommands().containsKey(CommandLiteral.BORDER))
.executes(command.getCommand());
registerArguments(borderCommand, literal(CommandLiteral.ADD),
argument(CommandLiteral.WORLD, dimension()),
argument(CommandLiteral.SHAPE, string()).suggests(SuggestionProviders.SHAPES),
argument(CommandLiteral.CENTER_X, word()),
argument(CommandLiteral.CENTER_Z, word()),
argument(CommandLiteral.RADIUS_X, word()),
argument(CommandLiteral.RADIUS_Z, word()));
registerArguments(borderCommand, literal(CommandLiteral.BYPASS),
argument(CommandLiteral.PLAYER, player()));
registerArguments(borderCommand, literal(CommandLiteral.HELP));
registerArguments(borderCommand, literal(CommandLiteral.LIST));
registerArguments(borderCommand, literal(CommandLiteral.LOAD),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(borderCommand, literal(CommandLiteral.REMOVE),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(borderCommand, literal(CommandLiteral.WRAP),
argument(CommandLiteral.WRAP, word()));
registerArguments(command, borderCommand);
event.getDispatcher().register(command);
}
@SafeVarargs
private <S> void registerArguments(final LiteralArgumentBuilder<S> command, final ArgumentBuilder<S, ?>... arguments) {
for (int i = arguments.length - 1; i > 0; --i) {
arguments[i - 1].then(arguments[i].executes(command.getCommand()));
}
command.then(arguments[0].executes(command.getCommand()));
}
@SubscribeEvent
public void onServerStopping(final ServerStoppingEvent event) {
if (chunky != null) {
chunky.disable();
}
}
public Chunky getChunky() {
return chunky;
}
}
@@ -1,28 +0,0 @@
package org.popcraft.chunky.command.suggestion;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.commands.CommandSourceStack;
import org.popcraft.chunky.command.CommandLiteral;
import org.popcraft.chunky.iterator.PatternType;
import java.util.concurrent.CompletableFuture;
public class PatternSuggestionProvider implements SuggestionProvider<CommandSourceStack> {
@Override
public CompletableFuture<Suggestions> getSuggestions(final CommandContext<CommandSourceStack> context, final SuggestionsBuilder builder) {
try {
final String input = context.getArgument(CommandLiteral.PATTERN, String.class);
PatternType.ALL.forEach(pattern -> {
if (pattern.contains(input.toLowerCase())) {
builder.suggest(pattern);
}
});
} catch (IllegalArgumentException e) {
PatternType.ALL.forEach(builder::suggest);
}
return builder.buildFuture();
}
}
@@ -1,28 +0,0 @@
package org.popcraft.chunky.command.suggestion;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.commands.CommandSourceStack;
import org.popcraft.chunky.command.CommandLiteral;
import org.popcraft.chunky.shape.ShapeType;
import java.util.concurrent.CompletableFuture;
public class ShapeSuggestionProvider implements SuggestionProvider<CommandSourceStack> {
@Override
public CompletableFuture<Suggestions> getSuggestions(final CommandContext<CommandSourceStack> context, final SuggestionsBuilder builder) {
try {
final String input = context.getArgument(CommandLiteral.SHAPE, String.class);
ShapeType.all().forEach(shape -> {
if (shape.contains(input.toLowerCase())) {
builder.suggest(shape);
}
});
} catch (IllegalArgumentException e) {
ShapeType.all().forEach(builder::suggest);
}
return builder.buildFuture();
}
}
@@ -1,19 +0,0 @@
package org.popcraft.chunky.command.suggestion;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import net.minecraft.commands.CommandSourceStack;
public final class SuggestionProviders {
public static final SuggestionProvider<CommandSourceStack> PATTERNS;
public static final SuggestionProvider<CommandSourceStack> SHAPES;
public static final SuggestionProvider<CommandSourceStack> TRIM_MODES;
static {
PATTERNS = new PatternSuggestionProvider();
SHAPES = new ShapeSuggestionProvider();
TRIM_MODES = new TrimModeSuggestionProvider();
}
private SuggestionProviders() {
}
}
@@ -1,30 +0,0 @@
package org.popcraft.chunky.command.suggestion;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.commands.CommandSourceStack;
import org.popcraft.chunky.command.CommandLiteral;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class TrimModeSuggestionProvider implements SuggestionProvider<CommandSourceStack> {
private static final List<String> TRIM_MODES = List.of("inside", "outside");
@Override
public CompletableFuture<Suggestions> getSuggestions(final CommandContext<CommandSourceStack> context, final SuggestionsBuilder builder) {
try {
final String input = context.getArgument(CommandLiteral.TRIM_MODE, String.class);
TRIM_MODES.forEach(shape -> {
if (shape.contains(input.toLowerCase())) {
builder.suggest(shape);
}
});
} catch (IllegalArgumentException e) {
TRIM_MODES.forEach(builder::suggest);
}
return builder.buildFuture();
}
}
@@ -1,33 +0,0 @@
package org.popcraft.chunky.listeners.bossbar;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerBossEvent;
import org.popcraft.chunky.GenerationTask;
import org.popcraft.chunky.event.task.GenerationTaskFinishEvent;
import org.popcraft.chunky.platform.World;
import java.util.Map;
import java.util.function.Consumer;
public class BossBarTaskFinishListener implements Consumer<GenerationTaskFinishEvent> {
private final Map<ResourceLocation, ServerBossEvent> bossBars;
public BossBarTaskFinishListener(final Map<ResourceLocation, ServerBossEvent> bossBars) {
this.bossBars = bossBars;
}
@Override
public void accept(final GenerationTaskFinishEvent event) {
final GenerationTask task = event.generationTask();
final World world = task.getSelection().world();
final ResourceLocation worldIdentifier = ResourceLocation.tryParse(world.getKey());
if (worldIdentifier == null) {
return;
}
final ServerBossEvent bossBar = bossBars.get(worldIdentifier);
if (bossBar != null) {
bossBar.removeAllPlayers();
bossBars.remove(worldIdentifier);
}
}
}
@@ -1,87 +0,0 @@
package org.popcraft.chunky.listeners.bossbar;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerBossEvent;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.BossEvent;
import net.minecraft.world.level.Level;
import org.popcraft.chunky.Chunky;
import org.popcraft.chunky.GenerationTask;
import org.popcraft.chunky.event.task.GenerationTaskUpdateEvent;
import org.popcraft.chunky.platform.ForgeWorld;
import org.popcraft.chunky.platform.World;
import java.util.Map;
import java.util.function.Consumer;
public class BossBarTaskUpdateListener implements Consumer<GenerationTaskUpdateEvent> {
private final Map<ResourceLocation, ServerBossEvent> bossBars;
public BossBarTaskUpdateListener(final Map<ResourceLocation, ServerBossEvent> bossBars) {
this.bossBars = bossBars;
}
@Override
public void accept(final GenerationTaskUpdateEvent event) {
final GenerationTask task = event.generationTask();
final Chunky chunky = task.getChunky();
final World world = task.getSelection().world();
final ResourceLocation worldIdentifier = ResourceLocation.tryParse(world.getKey());
if (worldIdentifier == null || !(world instanceof final ForgeWorld forgeWorld)) {
return;
}
final ServerBossEvent bossBar = bossBars.computeIfAbsent(worldIdentifier, x -> createNewBossBar(worldIdentifier));
final boolean silent = chunky.getConfig().isSilent();
if (silent == bossBar.isVisible()) {
bossBar.setVisible(!silent);
}
final MinecraftServer server = forgeWorld.getWorld().getServer();
for (final ServerPlayer player : server.getPlayerList().getPlayers()) {
if (player.hasPermissions(2)) {
bossBar.addPlayer(player);
} else {
bossBar.removePlayer(player);
}
}
final GenerationTask.Progress progress = task.getProgress();
bossBar.setName(Component.nullToEmpty(String.format("%s | %s%% | %s:%s:%s",
worldIdentifier,
String.format("%.2f", progress.getPercentComplete()),
String.format("%01d", progress.getHours()),
String.format("%02d", progress.getMinutes()),
String.format("%02d", progress.getSeconds()))));
bossBar.setProgress(task.getProgress().getPercentComplete() / 100f);
if (progress.isComplete()) {
bossBar.removeAllPlayers();
bossBars.remove(worldIdentifier);
}
}
private ServerBossEvent createNewBossBar(final ResourceLocation worldIdentifier) {
final ServerBossEvent bossBar = new ServerBossEvent(
Component.nullToEmpty(worldIdentifier.toString()),
bossBarColor(worldIdentifier),
BossEvent.BossBarOverlay.PROGRESS
);
bossBar.setDarkenScreen(false);
bossBar.setPlayBossMusic(false);
bossBar.setCreateWorldFog(false);
return bossBar;
}
private static BossEvent.BossBarColor bossBarColor(ResourceLocation worldIdentifier) {
final BossEvent.BossBarColor bossBarColor;
if (Level.OVERWORLD.location().equals(worldIdentifier)) {
bossBarColor = BossEvent.BossBarColor.GREEN;
} else if (Level.NETHER.location().equals(worldIdentifier)) {
bossBarColor = BossEvent.BossBarColor.RED;
} else if (Level.END.location().equals(worldIdentifier)) {
bossBarColor = BossEvent.BossBarColor.PURPLE;
} else {
bossBarColor = BossEvent.BossBarColor.BLUE;
}
return bossBarColor;
}
}
@@ -1,33 +0,0 @@
package org.popcraft.chunky.platform;
import net.minecraft.world.level.border.WorldBorder;
import org.popcraft.chunky.platform.util.Vector2;
import org.popcraft.chunky.shape.ShapeType;
public class ForgeBorder implements Border {
private final WorldBorder worldBorder;
public ForgeBorder(final WorldBorder worldBorder) {
this.worldBorder = worldBorder;
}
@Override
public Vector2 getCenter() {
return Vector2.of(worldBorder.getCenterX(), worldBorder.getCenterZ());
}
@Override
public double getRadiusX() {
return worldBorder.getSize() / 2d;
}
@Override
public double getRadiusZ() {
return worldBorder.getSize() / 2d;
}
@Override
public String getShape() {
return ShapeType.SQUARE;
}
}
@@ -1,62 +0,0 @@
package org.popcraft.chunky.platform;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import org.popcraft.chunky.platform.util.Location;
import java.util.UUID;
import static org.popcraft.chunky.util.Translator.translateKey;
public class ForgePlayer extends ForgeSender implements Player {
private final ServerPlayer player;
public ForgePlayer(final ServerPlayer player) {
super(player.createCommandSourceStack());
this.player = player;
}
@Override
public boolean isPlayer() {
return true;
}
@Override
public String getName() {
return player.getName().toString();
}
@Override
public World getWorld() {
return new ForgeWorld(player.serverLevel());
}
@Override
public Location getLocation() {
return new Location(getWorld(), player.getX(), player.getY(), player.getZ(), player.getXRot(), player.getYRot());
}
@Override
public void sendMessage(final String key, final boolean prefixed, final Object... args) {
player.sendSystemMessage(formatColored(translateKey(key, prefixed, args)));
}
@Override
public UUID getUUID() {
return player.getUUID();
}
@Override
public void teleport(final Location location) {
player.teleportTo(((ForgeWorld) location.getWorld()).getWorld(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
}
@Override
public void sendActionBar(final String key) {
player.displayClientMessage(formatColored(translateKey(key, false)), true);
}
private Component formatColored(final String message) {
return Component.nullToEmpty(message.replaceAll("&(?=[0-9a-fk-orA-FK-OR])", "§"));
}
}
@@ -1,50 +0,0 @@
package org.popcraft.chunky.platform;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import org.popcraft.chunky.platform.util.Location;
import static org.popcraft.chunky.util.Translator.translateKey;
public class ForgeSender implements Sender {
private final CommandSourceStack source;
public ForgeSender(final CommandSourceStack source) {
this.source = source;
}
@Override
public boolean isPlayer() {
return source.getEntity() instanceof ServerPlayer;
}
@Override
public String getName() {
return source.getTextName();
}
@Override
public World getWorld() {
return new ForgeWorld(source.getLevel());
}
@Override
public Location getLocation() {
final Vec3 pos = source.getPosition();
final Vec2 rot = source.getRotation();
return new Location(getWorld(), pos.x(), pos.y(), pos.z(), rot.x, rot.y);
}
@Override
public boolean hasPermission(final String permission) {
return source.hasPermission(2);
}
@Override
public void sendMessage(final String key, final boolean prefixed, final Object... args) {
source.sendSuccess(() -> Component.nullToEmpty(translateKey(key, prefixed, args).replaceAll("&[0-9a-fk-orA-FK-OR]", "")), false);
}
}
@@ -1,83 +0,0 @@
package org.popcraft.chunky.platform;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.ServerInterface;
import net.minecraft.server.level.ServerLevel;
import org.popcraft.chunky.ChunkyForge;
import org.popcraft.chunky.integration.Integration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class ForgeServer implements Server {
private final ChunkyForge plugin;
private final MinecraftServer server;
public ForgeServer(final ChunkyForge plugin, final MinecraftServer server) {
this.plugin = plugin;
this.server = server;
}
@Override
public Map<String, Integration> getIntegrations() {
return Map.of();
}
@Override
public Optional<World> getWorld(final String name) {
return Optional.ofNullable(ResourceLocation.tryParse(name))
.map(resourceLocation -> server.getLevel(ResourceKey.create(Registries.DIMENSION, resourceLocation)))
.or(() -> {
for (final ServerLevel level : server.getAllLevels()) {
if (name.equals(level.dimension().location().getPath())) {
return Optional.of(level);
}
}
return Optional.empty();
})
.map(ForgeWorld::new);
}
@Override
public List<World> getWorlds() {
final List<World> worlds = new ArrayList<>();
server.getAllLevels().forEach(world -> worlds.add(new ForgeWorld(world)));
return worlds;
}
@Override
public int getMaxWorldSize() {
if (server instanceof final ServerInterface serverInterface) {
return serverInterface.getProperties().maxWorldSize;
} else {
return server.getAbsoluteMaxWorldSize();
}
}
@Override
public Sender getConsole() {
return new ForgeSender(server.createCommandSourceStack());
}
@Override
public Collection<Player> getPlayers() {
return server.getPlayerList().getPlayers().stream().map(ForgePlayer::new).collect(Collectors.toList());
}
@Override
public Optional<Player> getPlayer(final String name) {
return Optional.ofNullable(server.getPlayerList().getPlayerByName(name)).map(ForgePlayer::new);
}
@Override
public Config getConfig() {
return plugin.getChunky().getConfig();
}
}
@@ -1,191 +0,0 @@
package org.popcraft.chunky.platform;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.visitors.CollectFields;
import net.minecraft.nbt.visitors.FieldSelector;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.TicketType;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Unit;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.storage.LevelResource;
import org.popcraft.chunky.ChunkyForge;
import org.popcraft.chunky.platform.util.Location;
import org.popcraft.chunky.util.Input;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
public class ForgeWorld implements World {
private static final int TICKING_LOAD_DURATION = Input.tryInteger(System.getProperty("chunky.tickingLoadDuration")).orElse(0);
private static final TicketType<Unit> CHUNKY = TicketType.create(ChunkyForge.MOD_ID, (unit, unit2) -> 0);
private static final TicketType<Unit> CHUNKY_TICKING = TicketType.create("%s_ticking".formatted(ChunkyForge.MOD_ID), (unit, unit2) -> 0, TICKING_LOAD_DURATION * 20);
private static final boolean UPDATE_CHUNK_NBT = Boolean.getBoolean("chunky.updateChunkNbt");
private final ServerLevel world;
private final Border worldBorder;
public ForgeWorld(final ServerLevel world) {
this.world = world;
this.worldBorder = new ForgeBorder(world.getWorldBorder());
}
@Override
public String getName() {
return world.dimension().location().toString();
}
@Override
public String getKey() {
return getName();
}
@Override
public CompletableFuture<Boolean> isChunkGenerated(final int x, final int z) {
if (Thread.currentThread() != world.getServer().getRunningThread()) {
return CompletableFuture.supplyAsync(() -> isChunkGenerated(x, z), world.getServer()).thenCompose(Function.identity());
} else {
final ChunkPos chunkPos = new ChunkPos(x, z);
final ServerChunkCache serverChunkCache = world.getChunkSource();
final ChunkMap chunkStorage = serverChunkCache.chunkMap;
final ChunkHolder loadedChunkHolder = chunkStorage.getVisibleChunkIfPresent(chunkPos.toLong());
if (loadedChunkHolder != null && loadedChunkHolder.getLastAvailableStatus() == ChunkStatus.FULL) {
return CompletableFuture.completedFuture(true);
}
final ChunkHolder unloadedChunkHolder = chunkStorage.pendingUnloads.get(chunkPos.toLong());
if (unloadedChunkHolder != null && unloadedChunkHolder.getLastAvailableStatus() == ChunkStatus.FULL) {
return CompletableFuture.completedFuture(true);
}
if (UPDATE_CHUNK_NBT) {
return chunkStorage.readChunk(chunkPos)
.thenApply(optionalNbt -> optionalNbt
.filter(chunkNbt -> chunkNbt.contains("Status", Tag.TAG_STRING))
.map(chunkNbt -> chunkNbt.getString("Status"))
.map(status -> "minecraft:full".equals(status) || "full".equals(status))
.orElse(false));
}
final FieldSelector statusSelector = new FieldSelector(StringTag.TYPE, "Status");
final CollectFields statusCollector = new CollectFields(statusSelector);
return serverChunkCache.chunkScanner().scanChunk(chunkPos, statusCollector)
.thenApply(ignored -> {
if (statusCollector.getResult() instanceof final CompoundTag chunkNbt) {
final String status = chunkNbt.getString("Status");
return "minecraft:full".equals(status) || "full".equals(status);
}
return false;
});
}
}
@Override
public CompletableFuture<Void> getChunkAtAsync(final int x, final int z) {
if (Thread.currentThread() != world.getServer().getRunningThread()) {
return CompletableFuture.supplyAsync(() -> getChunkAtAsync(x, z), world.getServer()).thenCompose(Function.identity());
} else {
final ChunkPos chunkPos = new ChunkPos(x, z);
final ServerChunkCache serverChunkCache = world.getChunkSource();
serverChunkCache.addRegionTicket(CHUNKY, chunkPos, 0, Unit.INSTANCE);
if (TICKING_LOAD_DURATION > 0) {
serverChunkCache.addRegionTicket(CHUNKY_TICKING, chunkPos, 1, Unit.INSTANCE);
}
serverChunkCache.runDistanceManagerUpdates();
final ChunkMap chunkManager = serverChunkCache.chunkMap;
final ChunkHolder chunkHolder = chunkManager.getVisibleChunkIfPresent(chunkPos.toLong());
final CompletableFuture<Void> chunkFuture = chunkHolder == null ? CompletableFuture.completedFuture(null) : CompletableFuture.allOf(chunkHolder.getOrScheduleFuture(ChunkStatus.FULL, chunkManager));
chunkFuture.whenCompleteAsync((ignored, throwable) -> serverChunkCache.removeRegionTicket(CHUNKY, chunkPos, 0, Unit.INSTANCE), world.getServer());
return chunkFuture;
}
}
@Override
public UUID getUUID() {
throw new UnsupportedOperationException();
}
@Override
public int getSeaLevel() {
return world.getSeaLevel();
}
@Override
public Location getSpawn() {
final BlockPos pos = world.getSharedSpawnPos();
final float rot = world.getSharedSpawnAngle();
return new Location(this, pos.getX(), pos.getY(), pos.getZ(), rot, 0);
}
@Override
public Border getWorldBorder() {
return worldBorder;
}
@Override
public int getElevation(final int x, final int z) {
final int height = world.getHeight(Heightmap.Types.MOTION_BLOCKING, x, z) + 1;
final int logicalHeight = world.getLogicalHeight();
if (height >= logicalHeight) {
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(x, logicalHeight, z);
int air = 0;
while (pos.getY() > world.getMinBuildHeight()) {
pos = pos.move(Direction.DOWN);
final BlockState blockState = world.getBlockState(pos);
if (blockState.isSolid() && air > 1) {
return pos.getY() + 1;
}
air = blockState.isAir() ? air + 1 : 0;
}
}
return height;
}
@Override
public int getMaxElevation() {
return world.getLogicalHeight();
}
@Override
public void playEffect(final Player player, final String effect) {
final Location location = player.getLocation();
final BlockPos pos = BlockPos.containing(location.getX(), location.getY(), location.getZ());
Input.tryInteger(effect).ifPresent(eventId -> world.levelEvent(eventId, pos, 0));
}
@Override
public void playSound(final Player player, final String sound) {
final Location location = player.getLocation();
world.getServer()
.registryAccess()
.registry(Registries.SOUND_EVENT)
.flatMap(soundEventRegistry -> soundEventRegistry.getOptional(ResourceLocation.tryParse(sound)))
.ifPresent(soundEvent -> world.playSound(null, location.getX(), location.getY(), location.getZ(), soundEvent, SoundSource.MASTER, 2f, 1f));
}
@Override
public Optional<Path> getDirectory(final String name) {
if (name == null) {
return Optional.empty();
}
final Path directory = DimensionType.getStorageFolder(world.dimension(), world.getServer().getWorldPath(LevelResource.ROOT)).normalize().resolve(name);
return Files.exists(directory) ? Optional.of(directory) : Optional.empty();
}
public ServerLevel getWorld() {
return world;
}
}
@@ -1,6 +0,0 @@
# ChunkMap
public net.minecraft.server.level.ChunkMap m_214963_(Lnet/minecraft/world/level/ChunkPos;)Ljava/util/concurrent/CompletableFuture; # readChunk
public net.minecraft.server.level.ChunkMap m_140327_(J)Lnet/minecraft/server/level/ChunkHolder; # getVisibleChunkIfPresent
public net.minecraft.server.level.ChunkMap f_140131_ # pendingUnloads
# ServerChunkCache
public net.minecraft.server.level.ServerChunkCache m_8489_()Z # runDistanceManagerUpdates
@@ -1,24 +0,0 @@
modLoader="javafml"
loaderVersion="[38,)"
license="GNU GPLv3"
issueTrackerURL="${github}/issues"
[[mods]]
modId="${id}"
version="${version}"
displayName="${name}"
displayURL="${github}"
logoFile="icon.png"
authors="${author}"
description="${description}"
[[dependencies.chunky]]
modId="forge"
mandatory=true
versionRange="[46,)"
ordering="NONE"
side="BOTH"
[[dependencies.chunky]]
modId="minecraft"
mandatory=true
versionRange="[1.20,1.21)"
ordering="NONE"
side="BOTH"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

-6
View File
@@ -1,6 +0,0 @@
{
"pack": {
"description": "chunky resources",
"pack_format": 8
}
}
Binary file not shown.
+2 -1
View File
@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+18 -12
View File
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -83,7 +85,8 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -111,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -130,10 +133,13 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
@@ -141,7 +147,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -149,7 +155,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -198,16 +204,16 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
Vendored
+94 -92
View File
@@ -1,92 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
-52
View File
@@ -1,52 +0,0 @@
plugins {
id("dev.architectury.loom") version "1.5-SNAPSHOT"
}
val shade: Configuration by configurations.creating
repositories {
maven("https://maven.neoforged.net/releases/")
}
dependencies {
minecraft(group = "com.mojang", name = "minecraft", version = "1.20.2")
mappings(loom.officialMojangMappings())
neoForge(group = "net.neoforged", name = "neoforge", version = "20.2.88")
implementation(project(":chunky-common"))
shade(project(":chunky-common"))
}
tasks {
processResources {
filesMatching("META-INF/mods.toml") {
expand(
"github" to project.property("github"),
"id" to rootProject.name,
"version" to project.version,
"name" to project.property("artifactName"),
"author" to project.property("author"),
"description" to project.property("description")
)
}
}
jar {
manifest {
attributes(
mapOf(
"Implementation-Title" to rootProject.name,
"Implementation-Version" to project.version,
"Implementation-Vendor" to project.property("author")
)
)
}
}
shadowJar {
configurations = listOf(shade)
archiveClassifier.set("dev")
archiveFileName.set(null as String?)
}
remapJar {
inputFile.set(shadowJar.get().archiveFile)
archiveFileName.set("${project.property("artifactName")}-${project.version}.jar")
}
}
-1
View File
@@ -1 +0,0 @@
loom.platform=neoforge
@@ -1,185 +0,0 @@
package org.popcraft.chunky;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerBossEvent;
import net.minecraft.server.level.ServerPlayer;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.loading.FMLPaths;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.RegisterCommandsEvent;
import net.neoforged.neoforge.event.server.ServerStartingEvent;
import net.neoforged.neoforge.event.server.ServerStoppingEvent;
import org.popcraft.chunky.command.ChunkyCommand;
import org.popcraft.chunky.command.CommandArguments;
import org.popcraft.chunky.command.CommandLiteral;
import org.popcraft.chunky.command.suggestion.SuggestionProviders;
import org.popcraft.chunky.event.task.GenerationTaskFinishEvent;
import org.popcraft.chunky.event.task.GenerationTaskUpdateEvent;
import org.popcraft.chunky.listeners.bossbar.BossBarTaskFinishListener;
import org.popcraft.chunky.listeners.bossbar.BossBarTaskUpdateListener;
import org.popcraft.chunky.platform.NeoForgePlayer;
import org.popcraft.chunky.platform.NeoForgeSender;
import org.popcraft.chunky.platform.NeoForgeServer;
import org.popcraft.chunky.platform.Sender;
import org.popcraft.chunky.platform.impl.GsonConfig;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
import static com.mojang.brigadier.arguments.StringArgumentType.string;
import static com.mojang.brigadier.arguments.StringArgumentType.word;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
import static net.minecraft.commands.arguments.DimensionArgument.dimension;
import static net.minecraft.commands.arguments.EntityArgument.player;
@Mod(ChunkyNeoForge.MOD_ID)
public class ChunkyNeoForge {
public static final String MOD_ID = "chunky";
private Chunky chunky;
private final Map<ResourceLocation, ServerBossEvent> bossBars = new ConcurrentHashMap<>();
public ChunkyNeoForge() {
NeoForge.EVENT_BUS.register(this);
}
@SubscribeEvent
public void onServerStarting(final ServerStartingEvent event) {
final MinecraftServer server = event.getServer();
final Path configPath = FMLPaths.CONFIGDIR.get().resolve("chunky/config.json");
this.chunky = new Chunky(new NeoForgeServer(this, server), new GsonConfig(configPath));
if (chunky.getConfig().getContinueOnRestart()) {
chunky.getCommands().get(CommandLiteral.CONTINUE).execute(chunky.getServer().getConsole(), CommandArguments.empty());
}
chunky.getEventBus().subscribe(GenerationTaskUpdateEvent.class, new BossBarTaskUpdateListener(bossBars));
chunky.getEventBus().subscribe(GenerationTaskFinishEvent.class, new BossBarTaskFinishListener(bossBars));
}
@SubscribeEvent
public void onRegisterCommands(final RegisterCommandsEvent event) {
final LiteralArgumentBuilder<CommandSourceStack> command = literal(CommandLiteral.CHUNKY)
.requires(serverCommandSource -> {
if (serverCommandSource.getServer().isSingleplayer()) {
return true;
}
return serverCommandSource.hasPermission(2);
})
.executes(context -> {
final Sender sender;
if (context.getSource().getEntity() instanceof final ServerPlayer player) {
sender = new NeoForgePlayer(player);
} else {
sender = new NeoForgeSender(context.getSource());
}
final Map<String, ChunkyCommand> commands = chunky.getCommands();
final String input = context.getInput().substring(context.getLastChild().getNodes().get(0).getRange().getStart());
final String[] tokens = input.split(" ");
final String subCommand = tokens.length > 1 && commands.containsKey(tokens[1]) ? tokens[1] : CommandLiteral.HELP;
final CommandArguments arguments = tokens.length > 2 ? CommandArguments.of(Arrays.copyOfRange(tokens, 2, tokens.length)) : CommandArguments.empty();
commands.get(subCommand).execute(sender, arguments);
return Command.SINGLE_SUCCESS;
});
registerArguments(command, literal(CommandLiteral.CANCEL),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(command, literal(CommandLiteral.CENTER),
argument(CommandLiteral.X, word()),
argument(CommandLiteral.Z, word()));
registerArguments(command, literal(CommandLiteral.CONFIRM));
registerArguments(command, literal(CommandLiteral.CONTINUE),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(command, literal(CommandLiteral.CORNERS),
argument(CommandLiteral.X1, word()),
argument(CommandLiteral.Z1, word()),
argument(CommandLiteral.X2, word()),
argument(CommandLiteral.Z2, word()));
registerArguments(command, literal(CommandLiteral.HELP),
argument(CommandLiteral.PAGE, integer()));
registerArguments(command, literal(CommandLiteral.PATTERN),
argument(CommandLiteral.PATTERN, string()).suggests(SuggestionProviders.PATTERNS),
argument(CommandLiteral.VALUE, string()));
registerArguments(command, literal(CommandLiteral.PAUSE),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(command, literal(CommandLiteral.PROGRESS));
registerArguments(command, literal(CommandLiteral.QUIET),
argument(CommandLiteral.INTERVAL, integer()));
registerArguments(command, literal(CommandLiteral.RADIUS),
argument(CommandLiteral.RADIUS, word()),
argument(CommandLiteral.RADIUS, word()));
registerArguments(command, literal(CommandLiteral.RELOAD),
argument(CommandLiteral.TYPE, word()));
registerArguments(command, literal(CommandLiteral.SELECTION));
registerArguments(command, literal(CommandLiteral.SHAPE),
argument(CommandLiteral.SHAPE, string()).suggests(SuggestionProviders.SHAPES));
registerArguments(command, literal(CommandLiteral.SILENT));
registerArguments(command, literal(CommandLiteral.SPAWN));
registerArguments(command, literal(CommandLiteral.START),
argument(CommandLiteral.WORLD, dimension()),
argument(CommandLiteral.SHAPE, string()).suggests(SuggestionProviders.SHAPES),
argument(CommandLiteral.CENTER_X, word()),
argument(CommandLiteral.CENTER_Z, word()),
argument(CommandLiteral.RADIUS_X, word()),
argument(CommandLiteral.RADIUS_Z, word()));
registerArguments(command, literal(CommandLiteral.TRIM),
argument(CommandLiteral.WORLD, dimension()),
argument(CommandLiteral.SHAPE, string()).suggests(SuggestionProviders.SHAPES),
argument(CommandLiteral.CENTER_X, word()),
argument(CommandLiteral.CENTER_Z, word()),
argument(CommandLiteral.RADIUS_X, word()),
argument(CommandLiteral.RADIUS_Z, word()),
argument(CommandLiteral.TRIM_MODE, string()).suggests(SuggestionProviders.TRIM_MODES),
argument(CommandLiteral.INHABITED, word()));
registerArguments(command, literal(CommandLiteral.WORLDBORDER));
registerArguments(command, literal(CommandLiteral.WORLD),
argument(CommandLiteral.WORLD, dimension()));
final LiteralArgumentBuilder<CommandSourceStack> borderCommand = literal(CommandLiteral.BORDER)
.requires(serverCommandSource -> chunky != null && chunky.getCommands().containsKey(CommandLiteral.BORDER))
.executes(command.getCommand());
registerArguments(borderCommand, literal(CommandLiteral.ADD),
argument(CommandLiteral.WORLD, dimension()),
argument(CommandLiteral.SHAPE, string()).suggests(SuggestionProviders.SHAPES),
argument(CommandLiteral.CENTER_X, word()),
argument(CommandLiteral.CENTER_Z, word()),
argument(CommandLiteral.RADIUS_X, word()),
argument(CommandLiteral.RADIUS_Z, word()));
registerArguments(borderCommand, literal(CommandLiteral.BYPASS),
argument(CommandLiteral.PLAYER, player()));
registerArguments(borderCommand, literal(CommandLiteral.HELP));
registerArguments(borderCommand, literal(CommandLiteral.LIST));
registerArguments(borderCommand, literal(CommandLiteral.LOAD),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(borderCommand, literal(CommandLiteral.REMOVE),
argument(CommandLiteral.WORLD, dimension()));
registerArguments(borderCommand, literal(CommandLiteral.WRAP),
argument(CommandLiteral.WRAP, word()));
registerArguments(command, borderCommand);
event.getDispatcher().register(command);
}
@SafeVarargs
private <S> void registerArguments(final LiteralArgumentBuilder<S> command, final ArgumentBuilder<S, ?>... arguments) {
for (int i = arguments.length - 1; i > 0; --i) {
arguments[i - 1].then(arguments[i].executes(command.getCommand()));
}
command.then(arguments[0].executes(command.getCommand()));
}
@SubscribeEvent
public void onServerStopping(final ServerStoppingEvent event) {
if (chunky != null) {
chunky.disable();
}
}
public Chunky getChunky() {
return chunky;
}
}
@@ -1,28 +0,0 @@
package org.popcraft.chunky.command.suggestion;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.commands.CommandSourceStack;
import org.popcraft.chunky.command.CommandLiteral;
import org.popcraft.chunky.iterator.PatternType;
import java.util.concurrent.CompletableFuture;
public class PatternSuggestionProvider implements SuggestionProvider<CommandSourceStack> {
@Override
public CompletableFuture<Suggestions> getSuggestions(final CommandContext<CommandSourceStack> context, final SuggestionsBuilder builder) {
try {
final String input = context.getArgument(CommandLiteral.PATTERN, String.class);
PatternType.ALL.forEach(pattern -> {
if (pattern.contains(input.toLowerCase())) {
builder.suggest(pattern);
}
});
} catch (IllegalArgumentException e) {
PatternType.ALL.forEach(builder::suggest);
}
return builder.buildFuture();
}
}
@@ -1,28 +0,0 @@
package org.popcraft.chunky.command.suggestion;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.commands.CommandSourceStack;
import org.popcraft.chunky.command.CommandLiteral;
import org.popcraft.chunky.shape.ShapeType;
import java.util.concurrent.CompletableFuture;
public class ShapeSuggestionProvider implements SuggestionProvider<CommandSourceStack> {
@Override
public CompletableFuture<Suggestions> getSuggestions(final CommandContext<CommandSourceStack> context, final SuggestionsBuilder builder) {
try {
final String input = context.getArgument(CommandLiteral.SHAPE, String.class);
ShapeType.all().forEach(shape -> {
if (shape.contains(input.toLowerCase())) {
builder.suggest(shape);
}
});
} catch (IllegalArgumentException e) {
ShapeType.all().forEach(builder::suggest);
}
return builder.buildFuture();
}
}
@@ -1,19 +0,0 @@
package org.popcraft.chunky.command.suggestion;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import net.minecraft.commands.CommandSourceStack;
public final class SuggestionProviders {
public static final SuggestionProvider<CommandSourceStack> PATTERNS;
public static final SuggestionProvider<CommandSourceStack> SHAPES;
public static final SuggestionProvider<CommandSourceStack> TRIM_MODES;
static {
PATTERNS = new PatternSuggestionProvider();
SHAPES = new ShapeSuggestionProvider();
TRIM_MODES = new TrimModeSuggestionProvider();
}
private SuggestionProviders() {
}
}
@@ -1,30 +0,0 @@
package org.popcraft.chunky.command.suggestion;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.commands.CommandSourceStack;
import org.popcraft.chunky.command.CommandLiteral;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class TrimModeSuggestionProvider implements SuggestionProvider<CommandSourceStack> {
private static final List<String> TRIM_MODES = List.of("inside", "outside");
@Override
public CompletableFuture<Suggestions> getSuggestions(final CommandContext<CommandSourceStack> context, final SuggestionsBuilder builder) {
try {
final String input = context.getArgument(CommandLiteral.TRIM_MODE, String.class);
TRIM_MODES.forEach(shape -> {
if (shape.contains(input.toLowerCase())) {
builder.suggest(shape);
}
});
} catch (IllegalArgumentException e) {
TRIM_MODES.forEach(builder::suggest);
}
return builder.buildFuture();
}
}
@@ -1,33 +0,0 @@
package org.popcraft.chunky.listeners.bossbar;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerBossEvent;
import org.popcraft.chunky.GenerationTask;
import org.popcraft.chunky.event.task.GenerationTaskFinishEvent;
import org.popcraft.chunky.platform.World;
import java.util.Map;
import java.util.function.Consumer;
public class BossBarTaskFinishListener implements Consumer<GenerationTaskFinishEvent> {
private final Map<ResourceLocation, ServerBossEvent> bossBars;
public BossBarTaskFinishListener(final Map<ResourceLocation, ServerBossEvent> bossBars) {
this.bossBars = bossBars;
}
@Override
public void accept(final GenerationTaskFinishEvent event) {
final GenerationTask task = event.generationTask();
final World world = task.getSelection().world();
final ResourceLocation worldIdentifier = ResourceLocation.tryParse(world.getKey());
if (worldIdentifier == null) {
return;
}
final ServerBossEvent bossBar = bossBars.get(worldIdentifier);
if (bossBar != null) {
bossBar.removeAllPlayers();
bossBars.remove(worldIdentifier);
}
}
}
@@ -1,87 +0,0 @@
package org.popcraft.chunky.listeners.bossbar;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerBossEvent;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.BossEvent;
import net.minecraft.world.level.Level;
import org.popcraft.chunky.Chunky;
import org.popcraft.chunky.GenerationTask;
import org.popcraft.chunky.event.task.GenerationTaskUpdateEvent;
import org.popcraft.chunky.platform.NeoForgeWorld;
import org.popcraft.chunky.platform.World;
import java.util.Map;
import java.util.function.Consumer;
public class BossBarTaskUpdateListener implements Consumer<GenerationTaskUpdateEvent> {
private final Map<ResourceLocation, ServerBossEvent> bossBars;
public BossBarTaskUpdateListener(final Map<ResourceLocation, ServerBossEvent> bossBars) {
this.bossBars = bossBars;
}
@Override
public void accept(final GenerationTaskUpdateEvent event) {
final GenerationTask task = event.generationTask();
final Chunky chunky = task.getChunky();
final World world = task.getSelection().world();
final ResourceLocation worldIdentifier = ResourceLocation.tryParse(world.getKey());
if (worldIdentifier == null || !(world instanceof final NeoForgeWorld neoForgeWorld)) {
return;
}
final ServerBossEvent bossBar = bossBars.computeIfAbsent(worldIdentifier, x -> createNewBossBar(worldIdentifier));
final boolean silent = chunky.getConfig().isSilent();
if (silent == bossBar.isVisible()) {
bossBar.setVisible(!silent);
}
final MinecraftServer server = neoForgeWorld.getWorld().getServer();
for (final ServerPlayer player : server.getPlayerList().getPlayers()) {
if (player.hasPermissions(2)) {
bossBar.addPlayer(player);
} else {
bossBar.removePlayer(player);
}
}
final GenerationTask.Progress progress = task.getProgress();
bossBar.setName(Component.nullToEmpty(String.format("%s | %s%% | %s:%s:%s",
worldIdentifier,
String.format("%.2f", progress.getPercentComplete()),
String.format("%01d", progress.getHours()),
String.format("%02d", progress.getMinutes()),
String.format("%02d", progress.getSeconds()))));
bossBar.setProgress(task.getProgress().getPercentComplete() / 100f);
if (progress.isComplete()) {
bossBar.removeAllPlayers();
bossBars.remove(worldIdentifier);
}
}
private ServerBossEvent createNewBossBar(final ResourceLocation worldIdentifier) {
final ServerBossEvent bossBar = new ServerBossEvent(
Component.nullToEmpty(worldIdentifier.toString()),
bossBarColor(worldIdentifier),
BossEvent.BossBarOverlay.PROGRESS
);
bossBar.setDarkenScreen(false);
bossBar.setPlayBossMusic(false);
bossBar.setCreateWorldFog(false);
return bossBar;
}
private static BossEvent.BossBarColor bossBarColor(ResourceLocation worldIdentifier) {
final BossEvent.BossBarColor bossBarColor;
if (Level.OVERWORLD.location().equals(worldIdentifier)) {
bossBarColor = BossEvent.BossBarColor.GREEN;
} else if (Level.NETHER.location().equals(worldIdentifier)) {
bossBarColor = BossEvent.BossBarColor.RED;
} else if (Level.END.location().equals(worldIdentifier)) {
bossBarColor = BossEvent.BossBarColor.PURPLE;
} else {
bossBarColor = BossEvent.BossBarColor.BLUE;
}
return bossBarColor;
}
}
@@ -1,33 +0,0 @@
package org.popcraft.chunky.platform;
import net.minecraft.world.level.border.WorldBorder;
import org.popcraft.chunky.platform.util.Vector2;
import org.popcraft.chunky.shape.ShapeType;
public class NeoForgeBorder implements Border {
private final WorldBorder worldBorder;
public NeoForgeBorder(final WorldBorder worldBorder) {
this.worldBorder = worldBorder;
}
@Override
public Vector2 getCenter() {
return Vector2.of(worldBorder.getCenterX(), worldBorder.getCenterZ());
}
@Override
public double getRadiusX() {
return worldBorder.getSize() / 2d;
}
@Override
public double getRadiusZ() {
return worldBorder.getSize() / 2d;
}
@Override
public String getShape() {
return ShapeType.SQUARE;
}
}
@@ -1,62 +0,0 @@
package org.popcraft.chunky.platform;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import org.popcraft.chunky.platform.util.Location;
import java.util.UUID;
import static org.popcraft.chunky.util.Translator.translateKey;
public class NeoForgePlayer extends NeoForgeSender implements Player {
private final ServerPlayer player;
public NeoForgePlayer(final ServerPlayer player) {
super(player.createCommandSourceStack());
this.player = player;
}
@Override
public boolean isPlayer() {
return true;
}
@Override
public String getName() {
return player.getName().toString();
}
@Override
public World getWorld() {
return new NeoForgeWorld(player.serverLevel());
}
@Override
public Location getLocation() {
return new Location(getWorld(), player.getX(), player.getY(), player.getZ(), player.getXRot(), player.getYRot());
}
@Override
public void sendMessage(final String key, final boolean prefixed, final Object... args) {
player.sendSystemMessage(formatColored(translateKey(key, prefixed, args)));
}
@Override
public UUID getUUID() {
return player.getUUID();
}
@Override
public void teleport(final Location location) {
player.teleportTo(((NeoForgeWorld) location.getWorld()).getWorld(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
}
@Override
public void sendActionBar(final String key) {
player.displayClientMessage(formatColored(translateKey(key, false)), true);
}
private Component formatColored(final String message) {
return Component.nullToEmpty(message.replaceAll("&(?=[0-9a-fk-orA-FK-OR])", "§"));
}
}
@@ -1,50 +0,0 @@
package org.popcraft.chunky.platform;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import org.popcraft.chunky.platform.util.Location;
import static org.popcraft.chunky.util.Translator.translateKey;
public class NeoForgeSender implements Sender {
private final CommandSourceStack source;
public NeoForgeSender(final CommandSourceStack source) {
this.source = source;
}
@Override
public boolean isPlayer() {
return source.getEntity() instanceof ServerPlayer;
}
@Override
public String getName() {
return source.getTextName();
}
@Override
public World getWorld() {
return new NeoForgeWorld(source.getLevel());
}
@Override
public Location getLocation() {
final Vec3 pos = source.getPosition();
final Vec2 rot = source.getRotation();
return new Location(getWorld(), pos.x(), pos.y(), pos.z(), rot.x, rot.y);
}
@Override
public boolean hasPermission(final String permission) {
return source.hasPermission(2);
}
@Override
public void sendMessage(final String key, final boolean prefixed, final Object... args) {
source.sendSuccess(() -> Component.nullToEmpty(translateKey(key, prefixed, args).replaceAll("&[0-9a-fk-orA-FK-OR]", "")), false);
}
}
@@ -1,83 +0,0 @@
package org.popcraft.chunky.platform;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.ServerInterface;
import net.minecraft.server.level.ServerLevel;
import org.popcraft.chunky.ChunkyNeoForge;
import org.popcraft.chunky.integration.Integration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class NeoForgeServer implements Server {
private final ChunkyNeoForge plugin;
private final MinecraftServer server;
public NeoForgeServer(final ChunkyNeoForge plugin, final MinecraftServer server) {
this.plugin = plugin;
this.server = server;
}
@Override
public Map<String, Integration> getIntegrations() {
return Map.of();
}
@Override
public Optional<World> getWorld(final String name) {
return Optional.ofNullable(ResourceLocation.tryParse(name))
.map(resourceLocation -> server.getLevel(ResourceKey.create(Registries.DIMENSION, resourceLocation)))
.or(() -> {
for (final ServerLevel level : server.getAllLevels()) {
if (name.equals(level.dimension().location().getPath())) {
return Optional.of(level);
}
}
return Optional.empty();
})
.map(NeoForgeWorld::new);
}
@Override
public List<World> getWorlds() {
final List<World> worlds = new ArrayList<>();
server.getAllLevels().forEach(world -> worlds.add(new NeoForgeWorld(world)));
return worlds;
}
@Override
public int getMaxWorldSize() {
if (server instanceof final ServerInterface serverInterface) {
return serverInterface.getProperties().maxWorldSize;
} else {
return server.getAbsoluteMaxWorldSize();
}
}
@Override
public Sender getConsole() {
return new NeoForgeSender(server.createCommandSourceStack());
}
@Override
public Collection<Player> getPlayers() {
return server.getPlayerList().getPlayers().stream().map(NeoForgePlayer::new).collect(Collectors.toList());
}
@Override
public Optional<Player> getPlayer(final String name) {
return Optional.ofNullable(server.getPlayerList().getPlayerByName(name)).map(NeoForgePlayer::new);
}
@Override
public Config getConfig() {
return plugin.getChunky().getConfig();
}
}
@@ -1,191 +0,0 @@
package org.popcraft.chunky.platform;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.visitors.CollectFields;
import net.minecraft.nbt.visitors.FieldSelector;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.TicketType;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Unit;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.storage.LevelResource;
import org.popcraft.chunky.ChunkyNeoForge;
import org.popcraft.chunky.platform.util.Location;
import org.popcraft.chunky.util.Input;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
public class NeoForgeWorld implements World {
private static final int TICKING_LOAD_DURATION = Input.tryInteger(System.getProperty("chunky.tickingLoadDuration")).orElse(0);
private static final TicketType<Unit> CHUNKY = TicketType.create(ChunkyNeoForge.MOD_ID, (unit, unit2) -> 0);
private static final TicketType<Unit> CHUNKY_TICKING = TicketType.create("%s_ticking".formatted(ChunkyNeoForge.MOD_ID), (unit, unit2) -> 0, TICKING_LOAD_DURATION * 20);
private static final boolean UPDATE_CHUNK_NBT = Boolean.getBoolean("chunky.updateChunkNbt");
private final ServerLevel world;
private final Border worldBorder;
public NeoForgeWorld(final ServerLevel world) {
this.world = world;
this.worldBorder = new NeoForgeBorder(world.getWorldBorder());
}
@Override
public String getName() {
return world.dimension().location().toString();
}
@Override
public String getKey() {
return getName();
}
@Override
public CompletableFuture<Boolean> isChunkGenerated(final int x, final int z) {
if (Thread.currentThread() != world.getServer().getRunningThread()) {
return CompletableFuture.supplyAsync(() -> isChunkGenerated(x, z), world.getServer()).thenCompose(Function.identity());
} else {
final ChunkPos chunkPos = new ChunkPos(x, z);
final ServerChunkCache serverChunkCache = world.getChunkSource();
final ChunkMap chunkStorage = serverChunkCache.chunkMap;
final ChunkHolder loadedChunkHolder = chunkStorage.getVisibleChunkIfPresent(chunkPos.toLong());
if (loadedChunkHolder != null && loadedChunkHolder.getLastAvailableStatus() == ChunkStatus.FULL) {
return CompletableFuture.completedFuture(true);
}
final ChunkHolder unloadedChunkHolder = chunkStorage.pendingUnloads.get(chunkPos.toLong());
if (unloadedChunkHolder != null && unloadedChunkHolder.getLastAvailableStatus() == ChunkStatus.FULL) {
return CompletableFuture.completedFuture(true);
}
if (UPDATE_CHUNK_NBT) {
return chunkStorage.readChunk(chunkPos)
.thenApply(optionalNbt -> optionalNbt
.filter(chunkNbt -> chunkNbt.contains("Status", Tag.TAG_STRING))
.map(chunkNbt -> chunkNbt.getString("Status"))
.map(status -> "minecraft:full".equals(status) || "full".equals(status))
.orElse(false));
}
final FieldSelector statusSelector = new FieldSelector(StringTag.TYPE, "Status");
final CollectFields statusCollector = new CollectFields(statusSelector);
return serverChunkCache.chunkScanner().scanChunk(chunkPos, statusCollector)
.thenApply(ignored -> {
if (statusCollector.getResult() instanceof final CompoundTag chunkNbt) {
final String status = chunkNbt.getString("Status");
return "minecraft:full".equals(status) || "full".equals(status);
}
return false;
});
}
}
@Override
public CompletableFuture<Void> getChunkAtAsync(final int x, final int z) {
if (Thread.currentThread() != world.getServer().getRunningThread()) {
return CompletableFuture.supplyAsync(() -> getChunkAtAsync(x, z), world.getServer()).thenCompose(Function.identity());
} else {
final ChunkPos chunkPos = new ChunkPos(x, z);
final ServerChunkCache serverChunkCache = world.getChunkSource();
serverChunkCache.addRegionTicket(CHUNKY, chunkPos, 0, Unit.INSTANCE);
if (TICKING_LOAD_DURATION > 0) {
serverChunkCache.addRegionTicket(CHUNKY_TICKING, chunkPos, 1, Unit.INSTANCE);
}
serverChunkCache.runDistanceManagerUpdates();
final ChunkMap chunkManager = serverChunkCache.chunkMap;
final ChunkHolder chunkHolder = chunkManager.getVisibleChunkIfPresent(chunkPos.toLong());
final CompletableFuture<Void> chunkFuture = chunkHolder == null ? CompletableFuture.completedFuture(null) : CompletableFuture.allOf(chunkHolder.getOrScheduleFuture(ChunkStatus.FULL, chunkManager));
chunkFuture.whenCompleteAsync((ignored, throwable) -> serverChunkCache.removeRegionTicket(CHUNKY, chunkPos, 0, Unit.INSTANCE), world.getServer());
return chunkFuture;
}
}
@Override
public UUID getUUID() {
throw new UnsupportedOperationException();
}
@Override
public int getSeaLevel() {
return world.getSeaLevel();
}
@Override
public Location getSpawn() {
final BlockPos pos = world.getSharedSpawnPos();
final float rot = world.getSharedSpawnAngle();
return new Location(this, pos.getX(), pos.getY(), pos.getZ(), rot, 0);
}
@Override
public Border getWorldBorder() {
return worldBorder;
}
@Override
public int getElevation(final int x, final int z) {
final int height = world.getHeight(Heightmap.Types.MOTION_BLOCKING, x, z) + 1;
final int logicalHeight = world.getLogicalHeight();
if (height >= logicalHeight) {
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(x, logicalHeight, z);
int air = 0;
while (pos.getY() > world.getMinBuildHeight()) {
pos = pos.move(Direction.DOWN);
final BlockState blockState = world.getBlockState(pos);
if (blockState.isSolid() && air > 1) {
return pos.getY() + 1;
}
air = blockState.isAir() ? air + 1 : 0;
}
}
return height;
}
@Override
public int getMaxElevation() {
return world.getLogicalHeight();
}
@Override
public void playEffect(final Player player, final String effect) {
final Location location = player.getLocation();
final BlockPos pos = BlockPos.containing(location.getX(), location.getY(), location.getZ());
Input.tryInteger(effect).ifPresent(eventId -> world.levelEvent(eventId, pos, 0));
}
@Override
public void playSound(final Player player, final String sound) {
final Location location = player.getLocation();
world.getServer()
.registryAccess()
.registry(Registries.SOUND_EVENT)
.flatMap(soundEventRegistry -> soundEventRegistry.getOptional(ResourceLocation.tryParse(sound)))
.ifPresent(soundEvent -> world.playSound(null, location.getX(), location.getY(), location.getZ(), soundEvent, SoundSource.MASTER, 2f, 1f));
}
@Override
public Optional<Path> getDirectory(final String name) {
if (name == null) {
return Optional.empty();
}
final Path directory = DimensionType.getStorageFolder(world.dimension(), world.getServer().getWorldPath(LevelResource.ROOT)).normalize().resolve(name);
return Files.exists(directory) ? Optional.of(directory) : Optional.empty();
}
public ServerLevel getWorld() {
return world;
}
}
@@ -1,6 +0,0 @@
# ChunkMap
public net.minecraft.server.level.ChunkMap readChunk(Lnet/minecraft/world/level/ChunkPos;)Ljava/util/concurrent/CompletableFuture;
public net.minecraft.server.level.ChunkMap getVisibleChunkIfPresent(J)Lnet/minecraft/server/level/ChunkHolder;
public net.minecraft.server.level.ChunkMap pendingUnloads
# ServerChunkCache
public net.minecraft.server.level.ServerChunkCache runDistanceManagerUpdates()Z
@@ -1,24 +0,0 @@
modLoader="javafml"
loaderVersion="[1,)"
license="GNU GPLv3"
issueTrackerURL="${github}/issues"
[[mods]]
modId="${id}"
version="${version}"
displayName="${name}"
displayURL="${github}"
logoFile="icon.png"
authors="${author}"
description="${description}"
[[dependencies.chunky]]
modId="neoforge"
mandatory=true
versionRange="[20.2,)"
ordering="NONE"
side="BOTH"
[[dependencies.chunky]]
modId="minecraft"
mandatory=true
versionRange="[1.20,1.21)"
ordering="NONE"
side="BOTH"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

-6
View File
@@ -1,6 +0,0 @@
{
"pack": {
"description": "chunky resources",
"pack_format": 8
}
}
+1 -5
View File
@@ -7,7 +7,7 @@ pluginManagement {
}
}
rootProject.name = "chunky"
rootProject.name = "Chunky"
sequenceOf(
"nbt",
@@ -15,10 +15,6 @@ sequenceOf(
"paper",
"folia",
"bukkit",
"fabric",
"forge",
"neoforge",
"sponge"
).forEach {
include("${rootProject.name}-$it")
project(":${rootProject.name}-$it").projectDir = file(it)
-37
View File
@@ -1,37 +0,0 @@
import org.spongepowered.gradle.plugin.config.PluginLoaders
import org.spongepowered.plugin.metadata.model.PluginDependency
plugins {
id("org.spongepowered.gradle.plugin") version "2.2.0"
}
dependencies {
implementation(project(":chunky-common"))
}
sponge {
apiVersion("8.1.0")
loader {
name(PluginLoaders.JAVA_PLAIN)
version("1.0")
}
license("GPL-3.0")
plugin(rootProject.name) {
displayName("${project.property("artifactName")}")
version("${project.version}")
entrypoint("${project.group}.${rootProject.name}.ChunkySponge")
description("${project.property("description")}")
links {
homepage("${project.property("github")}")
source("${project.property("github")}")
issues("${project.property("github")}/issues")
}
contributor("${project.property("author")}") {
description("Lead Developer")
}
dependency("spongeapi") {
loadOrder(PluginDependency.LoadOrder.AFTER)
optional(false)
}
}
}
@@ -1,352 +0,0 @@
package org.popcraft.chunky;
import com.google.inject.Inject;
import org.apache.logging.log4j.Logger;
import org.popcraft.chunky.command.CommandArguments;
import org.popcraft.chunky.command.CommandLiteral;
import org.popcraft.chunky.command.suggestion.SuggestionProviders;
import org.popcraft.chunky.platform.SpongeConfig;
import org.popcraft.chunky.platform.SpongeSender;
import org.popcraft.chunky.platform.SpongeServer;
import org.spongepowered.api.Game;
import org.spongepowered.api.ResourceKey;
import org.spongepowered.api.Server;
import org.spongepowered.api.command.Command;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.parameter.CommandContext;
import org.spongepowered.api.command.parameter.Parameter;
import org.spongepowered.api.config.ConfigDir;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.lifecycle.ConstructPluginEvent;
import org.spongepowered.api.event.lifecycle.LoadedGameEvent;
import org.spongepowered.api.event.lifecycle.RegisterCommandEvent;
import org.spongepowered.api.event.lifecycle.StoppingEngineEvent;
import org.spongepowered.api.world.server.ServerWorld;
import org.spongepowered.plugin.PluginContainer;
import org.spongepowered.plugin.builtin.jvm.Plugin;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
@Plugin("chunky")
public class ChunkySponge {
private Chunky chunky;
private PluginContainer container;
private Logger logger;
@Inject
private Game game;
@Inject
@ConfigDir(sharedRoot = false)
private Path configPath;
@Listener
public void onConstructPlugin(final ConstructPluginEvent event) {
this.container = event.plugin();
this.logger = event.plugin().logger();
}
@Listener
public void onLoadedGame(final LoadedGameEvent event) {
this.chunky = new Chunky(new SpongeServer(this), new SpongeConfig(this));
if (chunky.getConfig().getContinueOnRestart()) {
chunky.getCommands().get(CommandLiteral.CONTINUE).execute(chunky.getServer().getConsole(), CommandArguments.empty());
}
}
@Listener
public void onServerStopping(final StoppingEngineEvent<Server> event) {
if (chunky != null) {
chunky.disable();
}
}
@Listener
public void onRegisterCommand(final RegisterCommandEvent<Command.Parameterized> event) {
final Command.Parameterized cancelCommand = Command.builder()
.permission("chunky.command.cancel")
.addParameters(Parameter.world().key(CommandLiteral.WORLD).terminal().build())
.terminal(true)
.executor(ctx -> {
final List<String> args = new ArrayList<>();
ctx.one(Parameter.key(CommandLiteral.WORLD, ServerWorld.class)).map(ServerWorld::key).map(ResourceKey::asString).ifPresent(args::add);
executeSpongeCommand(ctx, CommandLiteral.CANCEL, args);
return CommandResult.success();
})
.build();
final Command.Parameterized centerCommand = Command.builder()
.permission("chunky.command.center")
.addParameters(
Parameter.string().key(CommandLiteral.X).terminal().build(),
Parameter.string().key(CommandLiteral.Z).terminal().build()
)
.terminal(true)
.executor(ctx -> {
final List<String> args = new ArrayList<>();
ctx.one(Parameter.key(CommandLiteral.X, String.class)).ifPresent(args::add);
ctx.one(Parameter.key(CommandLiteral.Z, String.class)).ifPresent(args::add);
executeSpongeCommand(ctx, CommandLiteral.CENTER, args);
return CommandResult.success();
})
.build();
final Command.Parameterized confirmCommand = Command.builder()
.permission("chunky.command.confirm")
.executor(ctx -> {
executeSpongeCommand(ctx, CommandLiteral.CONFIRM, List.of());
return CommandResult.success();
})
.build();
final Command.Parameterized continueCommand = Command.builder()
.permission("chunky.command.continue")
.addParameters(Parameter.world().key(CommandLiteral.WORLD).terminal().build())
.terminal(true)
.executor(ctx -> {
final List<String> args = new ArrayList<>();
ctx.one(Parameter.key(CommandLiteral.WORLD, ServerWorld.class)).map(ServerWorld::key).map(ResourceKey::asString).ifPresent(args::add);
executeSpongeCommand(ctx, CommandLiteral.CONTINUE, args);
return CommandResult.success();
})
.build();
final Command.Parameterized cornersCommand = Command.builder()
.permission("chunky.command.corners")
.addParameters(
Parameter.string().key(CommandLiteral.X1).build(),
Parameter.string().key(CommandLiteral.Z1).build(),
Parameter.string().key(CommandLiteral.X2).build(),
Parameter.string().key(CommandLiteral.Z2).build()
)
.executor(ctx -> {
final List<String> args = new ArrayList<>();
args.add(ctx.requireOne(Parameter.key(CommandLiteral.X1, String.class)));
args.add(ctx.requireOne(Parameter.key(CommandLiteral.Z1, String.class)));
args.add(ctx.requireOne(Parameter.key(CommandLiteral.X2, String.class)));
args.add(ctx.requireOne(Parameter.key(CommandLiteral.Z2, String.class)));
executeSpongeCommand(ctx, CommandLiteral.CORNERS, args);
return CommandResult.success();
})
.build();
final Command.Parameterized helpCommand = Command.builder()
.permission("chunky.command.help")
.addParameters(Parameter.integerNumber().key(CommandLiteral.PAGE).terminal().build())
.terminal(true)
.executor(ctx -> {
final List<String> args = new ArrayList<>();
ctx.one(Parameter.key(CommandLiteral.PAGE, Integer.class)).map(String::valueOf).ifPresent(args::add);
executeSpongeCommand(ctx, CommandLiteral.HELP, args);
return CommandResult.success();
})
.build();
final Command.Parameterized patternCommand = Command.builder()
.permission("chunky.command.pattern")
.addParameters(
Parameter.string().key(CommandLiteral.PATTERN).completer(SuggestionProviders.PATTERNS).terminal().build(),
Parameter.string().key(CommandLiteral.VALUE).terminal().build()
)
.executor(ctx -> {
final List<String> args = new ArrayList<>();
args.add(ctx.requireOne(Parameter.key(CommandLiteral.PATTERN, String.class)));
ctx.one(Parameter.key(CommandLiteral.VALUE, String.class)).ifPresent(args::add);
executeSpongeCommand(ctx, CommandLiteral.PATTERN, args);
return CommandResult.success();
})
.build();
final Command.Parameterized pauseCommand = Command.builder()
.permission("chunky.command.pause")
.addParameters(Parameter.world().key(CommandLiteral.WORLD).terminal().build())
.terminal(true)
.executor(ctx -> {
final List<String> args = new ArrayList<>();
ctx.one(Parameter.key(CommandLiteral.WORLD, ServerWorld.class)).map(ServerWorld::key).map(ResourceKey::asString).ifPresent(args::add);
executeSpongeCommand(ctx, CommandLiteral.PAUSE, args);
return CommandResult.success();
})
.build();
final Command.Parameterized progressCommand = Command.builder()
.permission("chunky.command.progress")
.executor(ctx -> {
executeSpongeCommand(ctx, CommandLiteral.PROGRESS, List.of());
return CommandResult.success();
})
.build();
final Command.Parameterized quietCommand = Command.builder()
.permission("chunky.command.quiet")
.addParameters(Parameter.integerNumber().key(CommandLiteral.INTERVAL).build())
.executor(ctx -> {
final List<String> args = new ArrayList<>();
args.add(String.valueOf(ctx.requireOne(Parameter.key(CommandLiteral.INTERVAL, Integer.class))));
executeSpongeCommand(ctx, CommandLiteral.QUIET, args);
return CommandResult.success();
})
.build();
final Command.Parameterized radiusCommand = Command.builder()
.permission("chunky.command.radius")
.addParameters(
Parameter.string().key(CommandLiteral.RADIUS_X).terminal().build(),
Parameter.string().key(CommandLiteral.RADIUS_Z).terminal().build()
)
.executor(ctx -> {
final List<String> args = new ArrayList<>();
args.add(ctx.requireOne(Parameter.key(CommandLiteral.RADIUS_X, String.class)));
ctx.one(Parameter.key(CommandLiteral.RADIUS_Z, String.class)).ifPresent(args::add);
executeSpongeCommand(ctx, CommandLiteral.RADIUS, args);
return CommandResult.success();
})
.build();
final Command.Parameterized reloadCommand = Command.builder()
.permission("chunky.command.reload")
.addParameters(Parameter.string().key(CommandLiteral.TYPE).terminal().build())
.terminal(true)
.executor(ctx -> {
final List<String> args = new ArrayList<>();
ctx.one(Parameter.key(CommandLiteral.TYPE, String.class)).ifPresent(args::add);
executeSpongeCommand(ctx, CommandLiteral.RELOAD, args);
return CommandResult.success();
})
.build();
final Command.Parameterized selectionCommand = Command.builder()
.permission("chunky.command.selection")
.executor(ctx -> {
executeSpongeCommand(ctx, CommandLiteral.SELECTION, List.of());
return CommandResult.success();
})
.build();
final Command.Parameterized shapeCommand = Command.builder()
.permission("chunky.command.shape")
.addParameters(Parameter.string().key(CommandLiteral.SHAPE).completer(SuggestionProviders.SHAPES).build())
.executor(ctx -> {
final List<String> args = new ArrayList<>();
args.add(ctx.requireOne(Parameter.key(CommandLiteral.SHAPE, String.class)));
executeSpongeCommand(ctx, CommandLiteral.SHAPE, args);
return CommandResult.success();
})
.build();
final Command.Parameterized silentCommand = Command.builder()
.permission("chunky.command.silent")
.executor(ctx -> {
executeSpongeCommand(ctx, CommandLiteral.SILENT, List.of());
return CommandResult.success();
})
.build();
final Command.Parameterized spawnCommand = Command.builder()
.permission("chunky.command.spawn")
.executor(ctx -> {
executeSpongeCommand(ctx, CommandLiteral.SPAWN, List.of());
return CommandResult.success();
})
.build();
final Command.Parameterized startCommand = Command.builder()
.permission("chunky.command.start")
.addParameters(
Parameter.world().key(CommandLiteral.WORLD).terminal().build(),
Parameter.string().key(CommandLiteral.SHAPE).terminal().completer(SuggestionProviders.SHAPES).build(),
Parameter.string().key(CommandLiteral.X).terminal().build(),
Parameter.string().key(CommandLiteral.Z).terminal().build(),
Parameter.string().key(CommandLiteral.RADIUS_X).terminal().build(),
Parameter.string().key(CommandLiteral.RADIUS_Z).terminal().build()
)
.terminal(true)
.executor(ctx -> {
final List<String> args = new ArrayList<>();
ctx.one(Parameter.key(CommandLiteral.WORLD, ServerWorld.class)).map(ServerWorld::key).map(ResourceKey::asString).ifPresent(args::add);
ctx.one(Parameter.key(CommandLiteral.SHAPE, String.class)).ifPresent(args::add);
ctx.one(Parameter.key(CommandLiteral.X, String.class)).ifPresent(args::add);
ctx.one(Parameter.key(CommandLiteral.Z, String.class)).ifPresent(args::add);
ctx.one(Parameter.key(CommandLiteral.RADIUS_X, String.class)).ifPresent(args::add);
ctx.one(Parameter.key(CommandLiteral.RADIUS_Z, String.class)).ifPresent(args::add);
executeSpongeCommand(ctx, CommandLiteral.START, args);
return CommandResult.success();
})
.build();
final Command.Parameterized trimCommand = Command.builder()
.permission("chunky.command.trim")
.addParameters(
Parameter.world().key(CommandLiteral.WORLD).terminal().build(),
Parameter.string().key(CommandLiteral.SHAPE).terminal().completer(SuggestionProviders.SHAPES).build(),
Parameter.string().key(CommandLiteral.X).terminal().build(),
Parameter.string().key(CommandLiteral.Z).terminal().build(),
Parameter.string().key(CommandLiteral.RADIUS_X).terminal().build(),
Parameter.string().key(CommandLiteral.RADIUS_Z).terminal().build()
)
.terminal(true)
.executor(ctx -> {
final List<String> args = new ArrayList<>();
ctx.one(Parameter.key(CommandLiteral.WORLD, ServerWorld.class)).map(ServerWorld::key).map(ResourceKey::asString).ifPresent(args::add);
ctx.one(Parameter.key(CommandLiteral.SHAPE, String.class)).ifPresent(args::add);
ctx.one(Parameter.key(CommandLiteral.X, String.class)).ifPresent(args::add);
ctx.one(Parameter.key(CommandLiteral.Z, String.class)).ifPresent(args::add);
ctx.one(Parameter.key(CommandLiteral.RADIUS_X, String.class)).ifPresent(args::add);
ctx.one(Parameter.key(CommandLiteral.RADIUS_Z, String.class)).ifPresent(args::add);
executeSpongeCommand(ctx, CommandLiteral.TRIM, args);
return CommandResult.success();
})
.build();
final Command.Parameterized worldborderCommand = Command.builder()
.permission("chunky.command.worldborder")
.executor(ctx -> {
executeSpongeCommand(ctx, CommandLiteral.WORLDBORDER, List.of());
return CommandResult.success();
})
.build();
final Command.Parameterized worldCommand = Command.builder()
.permission("chunky.command.world")
.addParameters(Parameter.world().key(CommandLiteral.WORLD).build())
.executor(ctx -> {
final List<String> args = new ArrayList<>();
ctx.one(Parameter.key(CommandLiteral.WORLD, ServerWorld.class)).map(ServerWorld::key).map(ResourceKey::asString).ifPresent(args::add);
executeSpongeCommand(ctx, CommandLiteral.WORLD, args);
return CommandResult.success();
})
.build();
event.register(this.container, Command.builder()
.permission("chunky.command.base")
.addChild(cancelCommand, CommandLiteral.CANCEL)
.addChild(centerCommand, CommandLiteral.CENTER)
.addChild(confirmCommand, CommandLiteral.CONFIRM)
.addChild(continueCommand, CommandLiteral.CONTINUE)
.addChild(cornersCommand, CommandLiteral.CORNERS)
.addChild(helpCommand, CommandLiteral.HELP)
.addChild(patternCommand, CommandLiteral.PATTERN)
.addChild(pauseCommand, CommandLiteral.PAUSE)
.addChild(progressCommand, CommandLiteral.PROGRESS)
.addChild(quietCommand, CommandLiteral.QUIET)
.addChild(radiusCommand, CommandLiteral.RADIUS)
.addChild(reloadCommand, CommandLiteral.RELOAD)
.addChild(selectionCommand, CommandLiteral.SELECTION)
.addChild(shapeCommand, CommandLiteral.SHAPE)
.addChild(silentCommand, CommandLiteral.SILENT)
.addChild(spawnCommand, CommandLiteral.SPAWN)
.addChild(startCommand, CommandLiteral.START)
.addChild(trimCommand, CommandLiteral.TRIM)
.addChild(worldborderCommand, CommandLiteral.WORLDBORDER)
.addChild(worldCommand, CommandLiteral.WORLD)
.executor(ctx -> {
executeSpongeCommand(ctx, CommandLiteral.HELP, List.of());
return CommandResult.success();
})
.build(), CommandLiteral.CHUNKY);
}
private void executeSpongeCommand(final CommandContext ctx, final String command, final List<String> args) {
chunky.getCommands().get(command).execute(new SpongeSender(ctx.cause().root()), CommandArguments.of(args));
}
public Chunky getChunky() {
return chunky;
}
public PluginContainer getContainer() {
return container;
}
public Logger getLogger() {
return logger;
}
public Game getGame() {
return game;
}
public Path getConfigPath() {
return configPath;
}
}
@@ -1,22 +0,0 @@
package org.popcraft.chunky.command.suggestion;
import org.popcraft.chunky.iterator.PatternType;
import org.spongepowered.api.command.CommandCompletion;
import org.spongepowered.api.command.parameter.CommandContext;
import org.spongepowered.api.command.parameter.managed.ValueCompleter;
import java.util.ArrayList;
import java.util.List;
public class PatternSuggestionProvider implements ValueCompleter {
@Override
public List<CommandCompletion> complete(final CommandContext context, final String currentInput) {
final List<CommandCompletion> completions = new ArrayList<>();
PatternType.ALL.forEach(pattern -> {
if (pattern.contains(currentInput.toLowerCase())) {
completions.add(CommandCompletion.of(pattern));
}
});
return completions;
}
}
@@ -1,22 +0,0 @@
package org.popcraft.chunky.command.suggestion;
import org.popcraft.chunky.shape.ShapeType;
import org.spongepowered.api.command.CommandCompletion;
import org.spongepowered.api.command.parameter.CommandContext;
import org.spongepowered.api.command.parameter.managed.ValueCompleter;
import java.util.ArrayList;
import java.util.List;
public class ShapeSuggestionProvider implements ValueCompleter {
@Override
public List<CommandCompletion> complete(final CommandContext context, final String currentInput) {
final List<CommandCompletion> completions = new ArrayList<>();
ShapeType.all().forEach(pattern -> {
if (pattern.contains(currentInput.toLowerCase())) {
completions.add(CommandCompletion.of(pattern));
}
});
return completions;
}
}
@@ -1,14 +0,0 @@
package org.popcraft.chunky.command.suggestion;
public final class SuggestionProviders {
public static final PatternSuggestionProvider PATTERNS;
public static final ShapeSuggestionProvider SHAPES;
static {
PATTERNS = new PatternSuggestionProvider();
SHAPES = new ShapeSuggestionProvider();
}
private SuggestionProviders() {
}
}
@@ -1,35 +0,0 @@
package org.popcraft.chunky.platform;
import org.popcraft.chunky.platform.util.Vector2;
import org.popcraft.chunky.shape.ShapeType;
import org.spongepowered.api.world.server.ServerWorld;
import org.spongepowered.math.vector.Vector2d;
public class SpongeBorder implements Border {
private final ServerWorld serverWorld;
public SpongeBorder(final ServerWorld serverWorld) {
this.serverWorld = serverWorld;
}
@Override
public Vector2 getCenter() {
final Vector2d center = serverWorld.border().center();
return Vector2.of(center.x(), center.y());
}
@Override
public double getRadiusX() {
return serverWorld.border().diameter() / 2d;
}
@Override
public double getRadiusZ() {
return serverWorld.border().diameter() / 2d;
}
@Override
public String getShape() {
return ShapeType.SQUARE;
}
}
@@ -1,129 +0,0 @@
package org.popcraft.chunky.platform;
import org.popcraft.chunky.ChunkySponge;
import org.popcraft.chunky.util.Input;
import org.popcraft.chunky.util.Translator;
import org.spongepowered.configurate.CommentedConfigurationNode;
import org.spongepowered.configurate.hocon.HoconConfigurationLoader;
import org.spongepowered.configurate.serialize.SerializationException;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
public class SpongeConfig implements Config {
private static final String CONFIG_FILE = "main.conf";
private static final String ROOT_CONFIG_NODE = "config";
private final ChunkySponge plugin;
private final HoconConfigurationLoader configLoader;
private CommentedConfigurationNode rootNode;
public SpongeConfig(final ChunkySponge plugin) {
this.plugin = plugin;
final Path defaultConfigPath = plugin.getConfigPath();
try {
Files.createDirectories(defaultConfigPath);
} catch (IOException e) {
e.printStackTrace();
}
final Path defaultConfigFile = new File(defaultConfigPath.toFile(), CONFIG_FILE).toPath();
this.configLoader = HoconConfigurationLoader.builder()
.path(defaultConfigFile)
.build();
try {
this.rootNode = configLoader.load();
} catch (IOException e) {
this.rootNode = configLoader.createNode();
e.printStackTrace();
}
final URL defaults = getClass().getClassLoader().getResource(CONFIG_FILE);
if (defaults != null) {
final HoconConfigurationLoader defaultConfigLoader = HoconConfigurationLoader.builder()
.url(defaults)
.build();
try {
final CommentedConfigurationNode defaultRootNode = defaultConfigLoader.load();
rootNode.mergeFrom(defaultRootNode);
} catch (IOException e) {
e.printStackTrace();
}
}
try {
configLoader.save(rootNode);
} catch (IOException e) {
e.printStackTrace();
}
Translator.setLanguage(getLanguage());
}
@Override
public Path getDirectory() {
return plugin.getConfigPath();
}
@Override
public int getVersion() {
return this.rootNode == null ? 0 : this.rootNode.node(ROOT_CONFIG_NODE, "version").getInt(0);
}
@Override
public String getLanguage() {
if (this.rootNode == null) {
return "en";
}
return Input.checkLanguage(this.rootNode.node(ROOT_CONFIG_NODE, "language").getString("en"));
}
@Override
public boolean getContinueOnRestart() {
return this.rootNode != null && this.rootNode.node(ROOT_CONFIG_NODE, "continue-on-restart").getBoolean(false);
}
@Override
public boolean isForceLoadExistingChunks() {
return this.rootNode != null && this.rootNode.node(ROOT_CONFIG_NODE, "force-load-existing-chunks").getBoolean(false);
}
@Override
public boolean isSilent() {
return this.rootNode != null && this.rootNode.node(ROOT_CONFIG_NODE, "silent").getBoolean(false);
}
@Override
public void setSilent(final boolean silent) {
if (this.rootNode != null) {
try {
this.rootNode.node(ROOT_CONFIG_NODE, "silent").set(silent);
} catch (SerializationException e) {
e.printStackTrace();
}
}
}
@Override
public int getUpdateInterval() {
return this.rootNode == null ? 1 : this.rootNode.node(ROOT_CONFIG_NODE, "update-interval").getInt(1);
}
@Override
public void setUpdateInterval(final int updateInterval) {
if (this.rootNode != null) {
try {
this.rootNode.node(ROOT_CONFIG_NODE, "update-interval").set(updateInterval);
} catch (SerializationException e) {
e.printStackTrace();
}
}
}
@Override
public void reload() {
try {
this.rootNode = configLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@@ -1,58 +0,0 @@
package org.popcraft.chunky.platform;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.popcraft.chunky.platform.util.Location;
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
import org.spongepowered.api.world.server.ServerLocation;
import org.spongepowered.math.vector.Vector3d;
import java.util.UUID;
import static org.popcraft.chunky.util.Translator.translateKey;
public class SpongePlayer extends SpongeSender implements Player {
private final ServerPlayer player;
public SpongePlayer(final ServerPlayer player) {
super(player);
this.player = player;
}
@Override
public boolean isPlayer() {
return true;
}
@Override
public String getName() {
return player.name();
}
@Override
public World getWorld() {
return new SpongeWorld(player.serverLocation().world());
}
@Override
public Location getLocation() {
final ServerLocation loc = player.serverLocation();
final Vector3d rot = player.rotation();
return new Location(getWorld(), loc.x(), loc.y(), loc.z(), rot.floorX(), rot.floorY());
}
@Override
public UUID getUUID() {
return player.uniqueId();
}
@Override
public void teleport(final Location location) {
player.setLocation(ServerLocation.of(((SpongeWorld) location.getWorld()).getWorld(), location.getX(), location.getY(), location.getZ()));
player.setRotation(Vector3d.from(location.getYaw(), location.getPitch(), 0));
}
@Override
public void sendActionBar(final String key) {
player.sendActionBar(LegacyComponentSerializer.legacyAmpersand().deserialize(translateKey(key, false)));
}
}
@@ -1,55 +0,0 @@
package org.popcraft.chunky.platform;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.popcraft.chunky.platform.util.Location;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.entity.living.player.User;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.world.DefaultWorldKeys;
import org.spongepowered.api.world.server.WorldManager;
import static org.popcraft.chunky.util.Translator.translateKey;
public class SpongeSender implements Sender {
private final Object source;
public SpongeSender(final Object source) {
this.source = source;
}
@Override
public boolean isPlayer() {
return source instanceof Player;
}
@Override
public String getName() {
return source instanceof final User user ? user.name() : "Console";
}
@Override
public World getWorld() {
final WorldManager worldManager = Sponge.game().server().worldManager();
return new SpongeWorld(worldManager.world(DefaultWorldKeys.DEFAULT).orElseThrow(IllegalStateException::new));
}
@Override
public Location getLocation() {
return new Location(getWorld(), 0, 0, 0, 0, 0);
}
@Override
public boolean hasPermission(final String permission) {
return source instanceof final Subject subject && subject.hasPermission(permission);
}
@Override
public void sendMessage(final String key, final boolean prefixed, final Object... args) {
if (source instanceof final Audience audience) {
audience.sendMessage(Identity.nil(), LegacyComponentSerializer.legacyAmpersand().deserialize(translateKey(key, prefixed, args)));
}
}
}
@@ -1,67 +0,0 @@
package org.popcraft.chunky.platform;
import org.popcraft.chunky.ChunkySponge;
import org.popcraft.chunky.integration.Integration;
import org.spongepowered.api.ResourceKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class SpongeServer implements Server {
private static final int MAX_WORLD_SIZE = 29999984;
private final ChunkySponge plugin;
private final Map<String, Integration> integrations;
public SpongeServer(final ChunkySponge plugin) {
this.plugin = plugin;
this.integrations = new HashMap<>();
}
@Override
public Map<String, Integration> getIntegrations() {
return integrations;
}
@Override
public Optional<World> getWorld(final String name) {
return plugin.getGame().server().worldManager().world(ResourceKey.resolve(name)).map(SpongeWorld::new);
}
@Override
public List<World> getWorlds() {
final List<World> worlds = new ArrayList<>();
plugin.getGame().server().worldManager().worlds().forEach(world -> worlds.add(new SpongeWorld(world)));
return worlds;
}
@Override
public int getMaxWorldSize() {
return MAX_WORLD_SIZE;
}
@Override
public Sender getConsole() {
return new SpongeSender(plugin.getGame().systemSubject());
}
@Override
public Collection<Player> getPlayers() {
final Collection<Player> players = new ArrayList<>();
plugin.getGame().server().onlinePlayers().forEach(player -> players.add(new SpongePlayer(player)));
return players;
}
@Override
public Optional<Player> getPlayer(final String name) {
return plugin.getGame().server().player(name).map(SpongePlayer::new);
}
@Override
public Config getConfig() {
return plugin.getChunky().getConfig();
}
}
@@ -1,115 +0,0 @@
package org.popcraft.chunky.platform;
import net.kyori.adventure.key.InvalidKeyException;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.sound.Sound;
import org.popcraft.chunky.platform.util.Location;
import org.spongepowered.api.ResourceKey;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.effect.particle.ParticleEffect;
import org.spongepowered.api.registry.RegistryKey;
import org.spongepowered.api.registry.RegistryTypes;
import org.spongepowered.api.world.server.ServerWorld;
import org.spongepowered.math.vector.Vector3d;
import org.spongepowered.math.vector.Vector3i;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class SpongeWorld implements World {
private final ServerWorld world;
private final Border worldBorder;
public SpongeWorld(final ServerWorld world) {
this.world = world;
this.worldBorder = new SpongeBorder(world);
}
@Override
public String getName() {
return world.key().asString();
}
@Override
public String getKey() {
return getName();
}
@Override
public CompletableFuture<Boolean> isChunkGenerated(final int x, final int z) {
return CompletableFuture.completedFuture(world.hasChunk(x, 0, z));
}
@Override
public CompletableFuture<Void> getChunkAtAsync(final int x, final int z) {
return CompletableFuture.allOf(CompletableFuture.completedFuture(world.loadChunk(x, 0, z, true)));
}
@Override
public UUID getUUID() {
return world.uniqueId();
}
@Override
public int getSeaLevel() {
return world.seaLevel();
}
@Override
public Location getSpawn() {
final Vector3i spawn = world.properties().spawnPosition();
return new Location(this, spawn.x(), spawn.y(), spawn.z(), 0, 0);
}
@Override
public Border getWorldBorder() {
return worldBorder;
}
@Override
public int getElevation(final int x, final int z) {
return world.highestYAt(x, z) + 1;
}
@Override
public int getMaxElevation() {
return world.maximumHeight();
}
@Override
public void playEffect(final Player player, final String effect) {
final Location location = player.getLocation();
final Vector3d vector = Vector3d.from(location.getX(), location.getY(), location.getZ());
RegistryKey.of(RegistryTypes.PARTICLE_TYPE, ResourceKey.resolve(effect)).asDefaultedReference(Sponge::game).find().ifPresent(particleType -> world.spawnParticles(ParticleEffect.builder().type(particleType).build(), vector));
}
@Override
public void playSound(final Player player, final String sound) {
final Key soundKey;
try {
//noinspection PatternValidation
soundKey = Key.key(sound);
} catch (final InvalidKeyException e) {
return;
}
final Location location = player.getLocation();
world.playSound(Sound.sound(soundKey, Sound.Source.MASTER, 2f, 1f), location.getX(), location.getY(), location.getZ());
}
@Override
public Optional<Path> getDirectory(final String name) {
if (name == null) {
return Optional.empty();
}
final Path regionDirectory = world.directory().resolve(name);
return Files.exists(regionDirectory) ? Optional.of(regionDirectory) : Optional.empty();
}
public ServerWorld getWorld() {
return world;
}
}
-10
View File
@@ -1,10 +0,0 @@
# Chunky Configuration
# https://github.com/pop4959/Chunky/wiki/Configuration
config {
continue-on-restart=false
force-load-existing-chunks=false
language=en
silent=false
update-interval=1
version=2
}