From f1852fac237b4b2be46051e45f27acf6beef5257 Mon Sep 17 00:00:00 2001 From: rafern Date: Mon, 9 Aug 2021 02:23:54 +0100 Subject: [PATCH] Add LocateFeature command --- .../impl/command/LocateFeatureCommand.java | 139 ++++++++++++++++++ .../impl/management/CommandManager.java | 1 + 2 files changed, 140 insertions(+) create mode 100644 src/main/java/me/rigamortis/seppuku/impl/command/LocateFeatureCommand.java diff --git a/src/main/java/me/rigamortis/seppuku/impl/command/LocateFeatureCommand.java b/src/main/java/me/rigamortis/seppuku/impl/command/LocateFeatureCommand.java new file mode 100644 index 0000000..4aa8ae6 --- /dev/null +++ b/src/main/java/me/rigamortis/seppuku/impl/command/LocateFeatureCommand.java @@ -0,0 +1,139 @@ +package me.rigamortis.seppuku.impl.command; + +import me.rigamortis.seppuku.Seppuku; +import me.rigamortis.seppuku.api.command.Command; +import me.rigamortis.seppuku.api.util.StringUtil; +import me.rigamortis.seppuku.impl.management.WorldManager; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.client.multiplayer.WorldClient; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.WorldSettings; +import net.minecraft.world.chunk.IChunkProvider; +import net.minecraft.world.gen.IChunkGenerator; +import net.minecraft.world.gen.MapGenBase; +import net.minecraft.world.gen.structure.MapGenStructure; +import net.minecraft.world.storage.SaveHandlerMP; +import net.minecraft.world.storage.WorldInfo; +import net.minecraftforge.common.DimensionManager; + +import java.lang.reflect.Field; + +public final class LocateFeatureCommand extends Command { + + public LocateFeatureCommand() { + super("LocateFeature", new String[]{"LocFeature", "LocateFeat", "LF"}, "Like /locate, but client-side and with an option to override the origin for calculating the nearest feature", "LocateFeature \nLocate "); + } + + @Override + public void exec(String input) { + if (!this.clamp(input, 2, 4)) { + this.printUsage(); + return; + } + + final String[] split = input.split(" "); + + if (split.length == 3) { + this.printUsage(); + return; + } + + final BlockPos originPos; + final String originHint; + if (split.length == 4) { + if (StringUtil.isInt(split[2])) { + if (StringUtil.isInt(split[3])) { + originPos = new BlockPos(Integer.parseInt(split[2]), 64, Integer.parseInt(split[3])); + } else { + Seppuku.INSTANCE.errorChat("Unknown number " + "\247f\"" + split[3] + "\""); + return; + } + } else { + Seppuku.INSTANCE.errorChat("Unknown number " + "\247f\"" + split[2] + "\""); + return; + } + originHint = "to " + String.valueOf(originPos.getX()) + ", " + String.valueOf(originPos.getZ()); + } else { + originPos = new BlockPos(Minecraft.getMinecraft().player.posX, 64, Minecraft.getMinecraft().player.posZ); + originHint = "you"; + } + + final ServerData serverData = Minecraft.getMinecraft().getCurrentServerData(); + if (serverData != null) { + final WorldManager.WorldData worldData = Seppuku.INSTANCE.getWorldManager().find(serverData.serverIP); + if (worldData != null) { + final FakeWorld world = new FakeWorld(worldData.getSeed()); + final BlockPos pos = world.getNearestStructurePos(split[1], originPos); + if (pos == null) { + Seppuku.INSTANCE.errorChat("No structure found " + "\247f\"" + split[1] + "\""); + } else { + Seppuku.INSTANCE.logChat(split[1] + " nearest " + originHint + " is at " + String.valueOf(pos.getX()) + ", " + String.valueOf(pos.getZ())); + } + } else { + Seppuku.INSTANCE.errorChat("Seed not set. Use the seed command first"); + } + } else { + Seppuku.INSTANCE.errorChat("Cannot locate feature on localhost, since localhost has no seed"); + } + } + + private class FakeWorld extends World { + final IChunkGenerator chunkGenerator; + + public FakeWorld(long seed) { + this(Minecraft.getMinecraft().world, seed); + } + + public FakeWorld(WorldClient worldClient, long seed) { + super(new SaveHandlerMP(), new WorldInfo(new WorldSettings(seed, worldClient.getWorldInfo().getGameType(), true, worldClient.getWorldInfo().isHardcoreModeEnabled(), worldClient.getWorldInfo().getTerrainType()), "MpServer"), DimensionManager.createProviderFor(worldClient.provider.getDimension()), null, true); + this.provider.setWorld(this); + this.chunkGenerator = this.provider.createChunkGenerator(); + + // I would use getDeclaredField("world") here, but it fails outside the dev environment + // I also tried using ObfuscationReflectionHelper but it wouldn't compile, so I gave up + Field worldField = null; + for(Field field : MapGenBase.class.getDeclaredFields()) { + if (World.class.isAssignableFrom(field.getType())) { + worldField = field; + break; + } + } + + if (worldField == null) { + Seppuku.INSTANCE.errorChat("Could not find \"world\" field; feature location will fail"); + return; + } + + worldField.setAccessible(true); + + for(Field field : this.chunkGenerator.getClass().getDeclaredFields()) { + try { + if (MapGenStructure.class.isAssignableFrom(field.getType())) { + field.setAccessible(true); + MapGenBase structureGenerator = (MapGenBase) field.get(this.chunkGenerator); + worldField.set(structureGenerator, this); + } + } catch(IllegalAccessException e) { + Seppuku.INSTANCE.errorChat("Potential feature type skipped due to exception"); + e.printStackTrace(); + } + } + } + + protected IChunkProvider createChunkProvider() + { + return null; + } + + protected boolean isChunkLoaded(int x, int z, boolean allowEmpty) + { + return false; + } + + public BlockPos getNearestStructurePos(String structureName, BlockPos position) { + return this.chunkGenerator.getNearestStructurePos(this, structureName, position, false); + } + } +} diff --git a/src/main/java/me/rigamortis/seppuku/impl/management/CommandManager.java b/src/main/java/me/rigamortis/seppuku/impl/management/CommandManager.java index 4722d0c..e77196c 100644 --- a/src/main/java/me/rigamortis/seppuku/impl/management/CommandManager.java +++ b/src/main/java/me/rigamortis/seppuku/impl/management/CommandManager.java @@ -72,6 +72,7 @@ public final class CommandManager { this.commandList.add(new LastInvCommand()); this.commandList.add(new SearchCommand()); this.commandList.add(new PlayCommand()); + this.commandList.add(new LocateFeatureCommand()); //create commands for every value within every module loadValueCommands();