Worldgen, Configs

This commit is contained in:
Sebastian Hartte
2020-07-09 23:56:54 +02:00
parent 2944873431
commit 1bd4bd00a8
13 changed files with 214 additions and 124 deletions
+17 -3
View File
@@ -33,18 +33,32 @@ import java.util.stream.Collectors;
public final class AEConfig {
public final ClientConfig clientConfig;
public final ConfigFileManager clientConfigManager;
public final CommonConfig commonConfig;
public final ConfigFileManager commonConfigManager;
AEConfig(File configDir) {
ConfigSection clientRoot = ConfigSection.createRoot();
clientConfig = new ClientConfig(clientRoot);
syncClientConfig();
clientConfigManager = createConfigFileManager(clientRoot, configDir, "appliedenergistics2/client.json");
ConfigSection commonRoot = ConfigSection.createRoot();
commonConfig = new CommonConfig(commonRoot);
syncCommonConfig();
commonConfigManager = createConfigFileManager(commonRoot, configDir, "appliedenergistics2/common.json");
// FIXME config loading/saving
syncClientConfig();
syncCommonConfig();
}
private static ConfigFileManager createConfigFileManager(ConfigSection commonRoot, File configDir, String filename) {
File configFile = new File(configDir, filename);
ConfigFileManager result = new ConfigFileManager(commonRoot, configFile);
if (!configFile.exists()) {
result.save(); // Save a default file
} else {
result.load();
}
return result;
}
// Default Energy Conversion Rates
+14 -2
View File
@@ -33,10 +33,12 @@ import appeng.items.tools.NetworkToolItem;
import appeng.me.cache.*;
import appeng.mixins.CriteriaRegisterMixin;
import appeng.recipes.handlers.*;
import appeng.server.AECommand;
import appeng.worldgen.ChargedQuartzOreConfig;
import appeng.worldgen.ChargedQuartzOreFeature;
import appeng.worldgen.meteorite.MeteoriteStructure;
import net.earthcomputer.libstructure.LibStructure;
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.block.BlockState;
@@ -56,7 +58,10 @@ import net.minecraft.world.gen.chunk.StructureConfig;
import net.minecraft.world.gen.decorator.Decorator;
import net.minecraft.world.gen.decorator.NopeDecoratorConfig;
import net.minecraft.world.gen.decorator.RangeDecoratorConfig;
import net.minecraft.world.gen.feature.*;
import net.minecraft.world.gen.feature.DefaultFeatureConfig;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.FeatureConfig;
import net.minecraft.world.gen.feature.OreFeatureConfig;
import java.util.function.Consumer;
@@ -90,10 +95,10 @@ public abstract class AppEngBase implements AppEng {
registerRecipeTypes();
registerRecipeSerializers();
registerWorldGen();
registerServerCommands();
setupInternalRegistries();
}
public static void setupInternalRegistries() {
@@ -350,4 +355,11 @@ public abstract class AppEngBase implements AppEng {
}
}
private void registerServerCommands() {
// The server commands need to know what the current minecraft server is.
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> {
new AECommand().register(dispatcher);
});
}
}
@@ -0,0 +1,65 @@
package appeng.core.config;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
public class ConfigFileManager {
private static Gson GSON = new GsonBuilder()
.setPrettyPrinting()
.setLenient()
.create();
private final ConfigSection rootSection;
private final File file;
private boolean loading;
public ConfigFileManager(ConfigSection rootSection, File file) {
this.rootSection = rootSection;
this.file = file;
rootSection.setChangeListener(() -> {
if (!loading) {
save();
}
});
}
public void load() {
loading = true;
try (InputStream in = new FileInputStream(file)) {
JsonObject rootObj = GSON.fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), JsonObject.class);
rootSection.read(rootObj);
} catch (FileNotFoundException ignored) {
} catch (Exception e) {
throw new RuntimeException("Failed to load AE2 config: " + file, e);
} finally {
loading = false;
}
}
public void save() {
File parent = file.getParentFile();
if (parent != null && !parent.exists()) {
try {
Files.createDirectories(parent.toPath());
} catch (IOException e) {
throw new RuntimeException("Failed to create AE2 config directory: " + parent);
}
}
try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
GSON.toJson(rootSection.write(), writer);
} catch (Exception e) {
throw new RuntimeException("Failed to write AE2 config: " + file, e);
}
rootSection.write();
}
}
@@ -1,11 +1,18 @@
package appeng.core.config;
import com.google.gson.JsonObject;
import java.util.ArrayList;
import java.util.List;
public class ConfigSection {
private final ConfigSection parent;
private final List<ConfigSection> subsections = new ArrayList<>();
private final List<BaseOption> options = new ArrayList<>();
private final String id;
private final String fullId;
@@ -41,7 +48,14 @@ public class ConfigSection {
}
public ConfigSection subsection(String id, String comment) {
return new ConfigSection(this, id, comment);
ConfigSection section = new ConfigSection(this, id, comment);
subsections.add(section);
return section;
}
private <T extends BaseOption> T addOption(T option) {
this.options.add(option);
return option;
}
public IntegerOption addInt(String id, int defaultValue) {
@@ -53,7 +67,7 @@ public class ConfigSection {
}
public IntegerOption addInt(String id, int defaultValue, int minValue, int maxValue, String comment) {
return new IntegerOption(this, id, comment, defaultValue, minValue, maxValue);
return addOption(new IntegerOption(this, id, comment, defaultValue, minValue, maxValue));
}
public DoubleOption addDouble(String id, double defaultValue) {
@@ -69,7 +83,7 @@ public class ConfigSection {
}
public DoubleOption addDouble(String id, double defaultValue, double minValue, double maxValue, String comment) {
return new DoubleOption(this, id, comment, defaultValue, minValue, maxValue);
return addOption(new DoubleOption(this, id, comment, defaultValue, minValue, maxValue));
}
public BooleanOption addBoolean(String id, boolean defaultValue) {
@@ -77,7 +91,7 @@ public class ConfigSection {
}
public BooleanOption addBoolean(String id, boolean defaultValue, String comment) {
return new BooleanOption(this, id, comment, defaultValue);
return addOption(new BooleanOption(this, id, comment, defaultValue));
}
public StringListOption addStringList(String id, List<String> defaultValue) {
@@ -85,7 +99,7 @@ public class ConfigSection {
}
public StringListOption addStringList(String id, List<String> defaultValue, String comment) {
return new StringListOption(this, id, comment, defaultValue);
return addOption(new StringListOption(this, id, comment, defaultValue));
}
public <T extends Enum<T>> EnumOption<T> addEnum(String id, T defaultValue) {
@@ -93,7 +107,7 @@ public class ConfigSection {
}
public <T extends Enum<T>> EnumOption<T> addEnum(String id, T defaultValue, String comment) {
return new EnumOption<>(this, id, comment, defaultValue);
return addOption(new EnumOption<>(this, id, comment, defaultValue));
}
public void setChangeListener(Runnable changeListener) {
@@ -109,5 +123,41 @@ public class ConfigSection {
}
}
public JsonObject write() {
JsonObject obj = new JsonObject();
if (comment != null) {
obj.addProperty("__comment", comment);
}
for (BaseOption option : options) {
if (option.comment != null) {
obj.addProperty("__comment", option.comment);
}
obj.add(option.id, option.write());
}
for (ConfigSection subsection : subsections) {
obj.add(subsection.id, subsection.write());
}
return obj;
}
public void read(JsonObject obj) {
for (BaseOption option : options) {
if (obj.has(option.id)) {
option.read(obj.get(option.id));
}
}
for (ConfigSection subsection : subsections) {
if (obj.has(subsection.id)) {
subsection.read(obj.getAsJsonObject(subsection.id));
}
}
}
}
@@ -0,0 +1,61 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.server;
import appeng.api.features.AEFeature;
import appeng.core.AEConfig;
import appeng.core.AppEng;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import static net.minecraft.server.command.CommandManager.literal;
public final class AECommand {
public void register(CommandDispatcher<ServerCommandSource> dispatcher) {
LiteralArgumentBuilder<ServerCommandSource> builder = literal("ae2");
for (Commands command : Commands.values()) {
if (command.test && !AEConfig.instance().isFeatureEnabled(AEFeature.UNSUPPORTED_DEVELOPER_TOOLS)) {
continue;
}
add(builder, command);
}
dispatcher.register(builder);
}
private void add(LiteralArgumentBuilder<ServerCommandSource> builder, Commands subCommand) {
LiteralArgumentBuilder<ServerCommandSource> subCommandBuilder = literal(subCommand.name().toLowerCase())
.requires(src -> src.hasPermissionLevel(subCommand.level));
subCommand.command.addArguments(subCommandBuilder);
subCommandBuilder.executes(ctx -> {
MinecraftServer server = AppEng.instance().getServer();
subCommand.command.call(server, ctx, ctx.getSource());
return 1;
});
builder.then(subCommandBuilder);
}
}
+45
View File
@@ -0,0 +1,45 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.server;
import appeng.server.subcommands.ChunkLogger;
import appeng.server.subcommands.Supporters;
import appeng.server.subcommands.TestMeteoritesCommand;
import appeng.server.subcommands.TestOreGenCommand;
public enum Commands {
Chunklogger(4, new ChunkLogger(), false), Supporters(0, new Supporters(), false),
TestOreGen(4, new TestOreGenCommand(), true), TestMeteorites(4, new TestMeteoritesCommand(), true);
public final int level;
public final ISubCommand command;
public boolean test;
Commands(final int level, final ISubCommand w, boolean test) {
this.level = level;
this.command = w;
this.test = test;
}
@Override
public String toString() {
return this.name();
}
}
@@ -0,0 +1,33 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.server;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.MinecraftServer;
public interface ISubCommand {
default void addArguments(LiteralArgumentBuilder<ServerCommandSource> builder) {
}
void call(MinecraftServer srv, CommandContext<ServerCommandSource> ctx, ServerCommandSource sender);
}
@@ -0,0 +1,88 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.server.subcommands;
import com.mojang.brigadier.context.CommandContext;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.TranslatableText;
import appeng.api.features.AEFeature;
import appeng.core.AEConfig;
import appeng.core.AELog;
import appeng.server.ISubCommand;
import net.minecraft.world.chunk.WorldChunk;
public class ChunkLogger implements ISubCommand {
private boolean enabled = false;
// Since we cannot unregister a listener once it's registered, but
// we still only want to register it if the command is ever used we track it here
private boolean listenerRegistered = false;
private void onChunkLoadEvent(ServerWorld world, WorldChunk chunk) {
if (!this.enabled) {
return;
}
AELog.info("Chunk Loaded: " + chunk.getPos().x + ", " + chunk.getPos().z);
this.displayStack();
}
private void onChunkUnloadEvent(ServerWorld world, WorldChunk chunk) {
if (!this.enabled) {
return;
}
AELog.info("Chunk Unloaded: " + chunk.getPos().x + ", " + chunk.getPos().z);
this.displayStack();
}
private void displayStack() {
if (AEConfig.instance().isFeatureEnabled(AEFeature.CHUNK_LOGGER_TRACE)) {
boolean output = false;
for (final StackTraceElement e : Thread.currentThread().getStackTrace()) {
if (output) {
AELog.info(
" " + e.getClassName() + '.' + e.getMethodName() + " (" + e.getLineNumber() + ')');
} else {
output = e.getClassName().contains("EventBus") && e.getMethodName().contains("post");
}
}
}
}
@Override
public synchronized void call(final MinecraftServer srv, final CommandContext<ServerCommandSource> data, final ServerCommandSource sender) {
this.enabled = !this.enabled;
if (this.enabled) {
if (!this.listenerRegistered) {
ServerChunkEvents.CHUNK_LOAD.register(this::onChunkLoadEvent);
ServerChunkEvents.CHUNK_UNLOAD.register(this::onChunkUnloadEvent);
this.listenerRegistered = true;
}
sender.sendFeedback(new TranslatableText("commands.ae2.ChunkLoggerOn"), true);
} else {
sender.sendFeedback(new TranslatableText("commands.ae2.ChunkLoggerOff"), true);
}
}
}
@@ -0,0 +1,38 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.server.subcommands;
import com.google.common.base.Joiner;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.MinecraftServer;
import net.minecraft.text.LiteralText;
import appeng.server.ISubCommand;
public class Supporters implements ISubCommand {
@Override
public void call(final MinecraftServer srv, final CommandContext<ServerCommandSource> data, final ServerCommandSource sender) {
final String[] who = { "Stig Halvorsen", "Josh Ricker", "Jenny \"Othlon\" Sutherland", "Hristo Bogdanov",
"BevoLJ" };
sender.sendFeedback(new LiteralText("Special thanks to " + Joiner.on(", ").join(who)), true);
}
}
@@ -0,0 +1,210 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.server.subcommands;
import static net.minecraft.server.command.CommandManager.literal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import appeng.core.AppEng;
import com.google.common.math.StatsAccumulator;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.structure.StructureStart;
import net.minecraft.text.*;
import net.minecraft.util.Formatting;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkStatus;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.Heightmap;
import net.minecraft.server.world.ServerWorld;
import appeng.server.ISubCommand;
import appeng.worldgen.meteorite.MeteoriteStructure;
import appeng.worldgen.meteorite.MeteoriteStructurePiece;
import appeng.worldgen.meteorite.PlacedMeteoriteSettings;
/**
* This is a testing command to validate quartz ore generation.
*/
public class TestMeteoritesCommand implements ISubCommand {
@Override
public void addArguments(LiteralArgumentBuilder<ServerCommandSource> builder) {
builder.then(literal("force").executes(ctx -> {
MinecraftServer server = AppEng.instance().getServer();
test(server, ctx.getSource(), true);
return 1;
}));
}
@Override
public void call(final MinecraftServer srv, final CommandContext<ServerCommandSource> ctx, final ServerCommandSource sender) {
test(srv, sender, false);
}
private static void test(MinecraftServer srv, final ServerCommandSource sender, boolean force) {
int radius = 100;
ServerPlayerEntity player = null;
try {
player = sender.getPlayer();
} catch (CommandSyntaxException ignored) {
}
ServerWorld world;
BlockPos centerBlock;
if (player != null) {
world = player.getServerWorld();
centerBlock = new BlockPos(player.getX(), 0, player.getZ());
} else {
world = srv.getOverworld();
centerBlock = world.getSpawnPos();
}
ChunkPos center = new ChunkPos(centerBlock);
ChunkGenerator generator = world.getChunkManager().getChunkGenerator();
// Find all meteorites in the given rectangle
List<PlacedMeteoriteSettings> found = new ArrayList<>();
int chunksChecked = 0;
for (int cx = center.x - radius; cx <= center.x + radius; cx++) {
for (int cz = center.z - radius; cz <= center.z + radius; cz++) {
chunksChecked++;
ChunkPos cp = new ChunkPos(cx, cz);
BlockPos p = new BlockPos(cp.getStartX(), 0, cp.getStartZ());
BlockPos nearest = generator.locateStructure(world, MeteoriteStructure.INSTANCE, p, 0, false);
if (nearest != null) {
Chunk chunk = world.getChunk(cx, cz, ChunkStatus.STRUCTURE_STARTS);
// The actual relevant information is in the structure piece
MeteoriteStructurePiece piece = getMeteoritePieceFromChunk(chunk);
if (piece != null) {
found.add(piece.getSettings());
}
}
}
}
// Create stats on how far apart the meteorites are
StatsAccumulator stats = new StatsAccumulator();
for (PlacedMeteoriteSettings settings : found) {
double closestOther = Double.NaN;
for (PlacedMeteoriteSettings otherSettings : found) {
if (otherSettings != settings) {
double d = settings.getPos().getSquaredDistance(otherSettings.getPos());
if (Double.isNaN(closestOther) || d < closestOther) {
closestOther = d;
}
}
}
if (!Double.isNaN(closestOther)) {
stats.add(Math.sqrt(closestOther));
}
}
found.sort(Comparator.comparingDouble(settings -> settings.getPos().getSquaredDistance(centerBlock)));
sendLine(sender, "Chunks checked: %d", chunksChecked);
sendLine(sender, "Meteorites found: %d", found.size());
sendLine(sender, "Closest: min=%.2f max=%.2f mean=%.2f stddev=%.2f", stats.min(), stats.max(), stats.mean(),
stats.populationStandardDeviation());
int closestCount = Math.min(10, found.size());
for (int i = 0; i < closestCount; i++) {
PlacedMeteoriteSettings settings = found.get(i);
BlockPos pos = settings.getPos();
String state = "not final";
if (force && settings.getFallout() == null) {
Chunk chunk = world.getChunk(pos);
MeteoriteStructurePiece piece = getMeteoritePieceFromChunk(chunk);
if (piece == null) {
state = "removed";
} else {
settings = piece.getSettings();
pos = settings.getPos();
}
}
Text restOfLine;
if (settings.getFallout() == null) {
restOfLine = new LiteralText(
String.format(Locale.ROOT, ", radius=%.2f [%s]", settings.getMeteoriteRadius(), state));
} else {
restOfLine = new LiteralText(String.format(Locale.ROOT, ", radius=%.2f, crater=%s, fallout=%s",
settings.getMeteoriteRadius(), settings.getCraterType().name().toLowerCase(),
settings.getFallout().name().toLowerCase()));
}
MutableText msg = new LiteralText(" #" + (i + 1) + " ");
msg.append(getClickablePosition(world, settings, pos)).append(restOfLine);
// Add a tooltip
MutableText tooltip = new LiteralText(settings.toString() + "\nBiome: ")
.append(world.getBiome(pos).getName());
msg.styled(style -> style.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, tooltip)));
sender.sendFeedback(msg, true);
}
}
// Add a clickable link to teleport the user to the Meteorite
private static Text getClickablePosition(ServerWorld world, PlacedMeteoriteSettings settings,
BlockPos pos) {
BlockPos tpPos = pos.up((int) Math.ceil(settings.getMeteoriteRadius()));
int surfaceY = world.getTopY(Heightmap.Type.WORLD_SURFACE, tpPos.getX(), tpPos.getZ());
if (surfaceY > tpPos.getY()) {
tpPos = new BlockPos(tpPos.getX(), surfaceY, tpPos.getZ());
}
String displayText = String.format(Locale.ROOT, "pos=%d,%d,%d", tpPos.getX(), tpPos.getY(), tpPos.getZ());
String tpCommand = String.format(Locale.ROOT, "/tp @s %d %d %d", tpPos.getX(), tpPos.getY(), tpPos.getZ());
return new LiteralText(displayText)
.formatted(Formatting.UNDERLINE)
.styled(style -> style.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, tpCommand)));
}
private static MeteoriteStructurePiece getMeteoritePieceFromChunk(Chunk chunk) {
StructureStart<?> start = chunk.getStructureStart(MeteoriteStructure.INSTANCE);
if (start != null && start.getChildren().size() > 0
&& start.getChildren().get(0) instanceof MeteoriteStructurePiece) {
return (MeteoriteStructurePiece) start.getChildren().get(0);
}
return null;
}
private static void sendLine(ServerCommandSource sender, String text, Object... args) {
sender.sendFeedback(new LiteralText(String.format(Locale.ROOT, text, args)), true);
}
}
@@ -0,0 +1,182 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.server.subcommands;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.ToDoubleFunction;
import java.util.stream.Collectors;
import com.google.common.math.StatsAccumulator;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.block.BlockState;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.text.LiteralText;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.chunk.ChunkStatus;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.server.world.ServerWorld;
import appeng.core.Api;
import appeng.server.ISubCommand;
/**
* This is a testing command to validate quartz ore generation.
*/
public class TestOreGenCommand implements ISubCommand {
private final BlockState quartzOre;
private final BlockState chargedQuartzOre;
public TestOreGenCommand() {
quartzOre = Api.INSTANCE.definitions().blocks().quartzOre().block().getDefaultState();
chargedQuartzOre = Api.INSTANCE.definitions().blocks().quartzOreCharged().block().getDefaultState();
}
@Override
public void call(final MinecraftServer srv, final CommandContext<ServerCommandSource> data, final ServerCommandSource sender) {
int radius = 1000;
ServerWorld world;
BlockPos center;
try {
ServerPlayerEntity player = sender.getPlayer();
world = player.getServerWorld();
center = new BlockPos(player.getX(), 0, player.getZ());
} catch (CommandSyntaxException e) {
world = srv.getOverworld();
center = world.getSpawnPos();
}
ChunkPos tl = new ChunkPos(center.add(-radius, 0, -radius));
ChunkPos br = new ChunkPos(center.add(radius, 0, radius));
Stats stats = new Stats();
for (int cx = tl.x; cx <= br.x; cx++) {
for (int cz = tl.z; cz <= br.z; cz++) {
ChunkPos cp = new ChunkPos(cx, cz);
checkChunk(sender, world, cp, stats);
}
}
AggregatedStats oreCount = AggregatedStats.create(stats.chunks, cs -> (double) cs.quartzOreCount);
List<ChunkStats> chunksWithOre = stats.chunks.stream().filter(c -> c.quartzOreCount > 0)
.collect(Collectors.toList());
AggregatedStats minHeight = AggregatedStats.create(chunksWithOre, cs -> (double) cs.minHeight);
AggregatedStats maxHeight = AggregatedStats.create(chunksWithOre, cs -> (double) cs.maxHeight);
AggregatedStats chargedCount = AggregatedStats.create(chunksWithOre, cs -> (double) cs.chargedOreCount);
sendLine(sender, "Checked %d chunks", stats.chunks.size());
sendLine(sender, " Count: %s", oreCount);
sendLine(sender, " Min-Height: %s", minHeight);
sendLine(sender, " Max-Height: %s", maxHeight);
sendLine(sender, " Sub-Type Count: %s", chargedCount);
}
private void checkChunk(ServerCommandSource sender, ServerWorld world, ChunkPos cp, Stats stats) {
Chunk chunk = world.getChunk(cp.x, cp.z, ChunkStatus.FULL, false);
if (chunk == null) {
sendLine(sender, "Skipping chunk %s", cp);
return;
}
ChunkStats chunkStats = new ChunkStats();
BlockPos.Mutable blockPos = new BlockPos.Mutable();
sendLine(sender, "Checking chunk %s", cp);
for (int x = cp.getStartX(); x <= cp.getEndX(); x++) {
blockPos.setX(x);
for (int z = cp.getStartZ(); z <= cp.getEndZ(); z++) {
blockPos.setZ(z);
for (int y = 0; y < world.getHeight(); y++) {
blockPos.setY(y);
BlockState state = chunk.getBlockState(blockPos);
if (state == quartzOre || state == chargedQuartzOre) {
chunkStats.minHeight = Math.min(chunkStats.minHeight, y);
chunkStats.maxHeight = Math.max(chunkStats.maxHeight, y);
chunkStats.quartzOreCount++;
if (state == chargedQuartzOre) {
chunkStats.chargedOreCount++;
}
}
}
}
}
stats.chunks.add(chunkStats);
}
private static void sendLine(ServerCommandSource sender, String text, Object... args) {
sender.sendFeedback(new LiteralText(String.format(Locale.ROOT, text, args)), true);
}
private static class Stats {
public final List<ChunkStats> chunks = new ArrayList<>();
}
private static class ChunkStats {
public int quartzOreCount = 0;
public int chargedOreCount = 0;
public int minHeight = Integer.MAX_VALUE;
public int maxHeight = Integer.MIN_VALUE;
}
private static class AggregatedStats {
public final double min;
public final double max;
public final double mean;
public final double stdDev;
public AggregatedStats(double min, double max, double mean, double stdDev) {
this.min = min;
this.max = max;
this.mean = mean;
this.stdDev = stdDev;
}
public static <T> AggregatedStats create(List<T> values, ToDoubleFunction<T> getter) {
if (values.isEmpty()) {
return new AggregatedStats(Double.NaN, Double.NaN, Double.NaN, Double.NaN);
}
StatsAccumulator accumulator = new StatsAccumulator();
for (T value : values) {
accumulator.add(getter.applyAsDouble(value));
}
return new AggregatedStats(accumulator.min(), accumulator.max(), accumulator.mean(),
accumulator.populationStandardDeviation());
}
@Override
public String toString() {
if (Double.isNaN(min)) {
return "Invalid";
}
return String.format(Locale.ROOT, "min=%.2f, max=%.2f, mean=%.2f, stdDev=%.2f", min, max, mean, stdDev);
}
}
}
@@ -101,11 +101,6 @@ public final class CompassService {
updateArea((ServerWorld) world, chunk);
}
public void updateArea(final ServerWorld w, ChunkPos chunkPos) {
Chunk chunk = w.getChunk(chunkPos.x, chunkPos.z);
updateArea(w, chunk);
}
public void updateArea(final ServerWorld w, Chunk chunk) {
this.updateArea(w, chunk, CHUNK_SIZE);
this.updateArea(w, chunk, CHUNK_SIZE + 32);