package openmods.world; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import cpw.mods.fml.relauncher.ReflectionHelper; import cpw.mods.fml.relauncher.ReflectionHelper.UnableToAccessFieldException; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import net.minecraft.world.ChunkPosition; import net.minecraft.world.WorldServer; import net.minecraft.world.chunk.IChunkProvider; import net.minecraft.world.gen.ChunkProviderFlat; import net.minecraft.world.gen.ChunkProviderGenerate; import net.minecraft.world.gen.ChunkProviderHell; import net.minecraft.world.gen.ChunkProviderServer; import net.minecraft.world.gen.structure.MapGenStructure; import openmods.Log; public class StructureRegistry { private static <T> void addMapGen(Collection<MapGenStructure> output, Class<T> klazz, T provider, String... names) { try { MapGenStructure struct = ReflectionHelper.getPrivateValue(klazz, provider, names); if (struct != null) output.add(struct); } catch (UnableToAccessFieldException e) { Log.warn(e, "Can't access fields %s from provider %s. Some structures may not be detected", Arrays.toString(names), provider); } } private StructureRegistry() { ImmutableList.Builder<IStructureGenProvider> builder = ImmutableList.builder(); builder.add(new IStructureGenProvider() { @Override public boolean canUseOnProvider(IChunkProvider provider) { return provider instanceof ChunkProviderGenerate; } @Override public Collection<MapGenStructure> listProviders(IChunkProvider provider) { ChunkProviderGenerate cp = (ChunkProviderGenerate)provider; List<MapGenStructure> result = Lists.newArrayList(); addMapGen(result, ChunkProviderGenerate.class, cp, "strongholdGenerator", "field_73225_u"); addMapGen(result, ChunkProviderGenerate.class, cp, "villageGenerator", "field_73224_v"); addMapGen(result, ChunkProviderGenerate.class, cp, "mineshaftGenerator", "field_73223_w"); addMapGen(result, ChunkProviderGenerate.class, cp, "scatteredFeatureGenerator", "field_73233_x"); return result; } }); builder.add(new IStructureGenProvider() { @Override public boolean canUseOnProvider(IChunkProvider provider) { return provider instanceof ChunkProviderFlat; } @Override public Collection<MapGenStructure> listProviders(IChunkProvider provider) { ChunkProviderFlat cp = (ChunkProviderFlat)provider; List<MapGenStructure> result = Lists.newArrayList(); try { List<MapGenStructure> gen = ReflectionHelper.getPrivateValue(ChunkProviderFlat.class, cp, "structureGenerators", "field_82696_f"); if (gen != null) result.addAll(gen); } catch (UnableToAccessFieldException e) { Log.warn(e, "Can't access map gen list from provider %s. Some structures may not be detected", provider); } return result; } }); builder.add(new IStructureGenProvider() { @Override public boolean canUseOnProvider(IChunkProvider provider) { return provider instanceof ChunkProviderHell; } @Override public Collection<MapGenStructure> listProviders(IChunkProvider provider) { ChunkProviderHell cp = (ChunkProviderHell)provider; List<MapGenStructure> result = Lists.newArrayList(); addMapGen(result, ChunkProviderHell.class, cp, "genNetherBridge", "field_73172_c"); return result; } }); providers = builder.build(); } public final static StructureRegistry instance = new StructureRegistry(); private List<IStructureNamer> names = Lists.newArrayList(); private List<IStructureGenProvider> providers; private static IChunkProvider getWrappedChunkProvider(ChunkProviderServer provider) { try { return ReflectionHelper.getPrivateValue(ChunkProviderServer.class, provider, "currentChunkProvider", "field_73246_d"); } catch (UnableToAccessFieldException e) { Log.warn(e, "Can't access chunk provider data. No structures will be detected"); return null; } } private interface IStructureVisitor { public void visit(MapGenStructure structure); } private void visitStructures(WorldServer world, IStructureVisitor visitor) { ChunkProviderServer provider = world.theChunkProviderServer; IChunkProvider inner = getWrappedChunkProvider(provider); if (inner != null) { for (IStructureGenProvider p : providers) if (p.canUseOnProvider(inner)) { for (MapGenStructure struct : p.listProviders(inner)) visitor.visit(struct); } } } private String identifyStructure(MapGenStructure structure) { for (IStructureNamer n : names) { String name = n.identify(structure); if (!Strings.isNullOrEmpty(name)) return name; } return structure.func_143025_a(); } public Map<String, ChunkPosition> getNearestStructures(final WorldServer world, final int x, final int y, final int z) { final ImmutableMap.Builder<String, ChunkPosition> result = ImmutableMap.builder(); visitStructures(world, new IStructureVisitor() { @Override public void visit(MapGenStructure structure) { try { ChunkPosition structPos = structure.func_151545_a(world, x, y, z); if (structPos != null) { String structType = identifyStructure(structure); if (!Strings.isNullOrEmpty(structType)) result.put(structType, structPos); } } catch (IndexOutOfBoundsException e) { // bug in MC, just ignore // hopefully fixed by magic of ASM } } }); return result.build(); } public Set<ChunkPosition> getNearestInstance(final String name, final WorldServer world, final int x, final int y, final int z) { final ImmutableSet.Builder<ChunkPosition> result = ImmutableSet.builder(); visitStructures(world, new IStructureVisitor() { @Override public void visit(MapGenStructure structure) { String structType = identifyStructure(structure); if (name.equals(structType)) { try { ChunkPosition structPos = structure.func_151545_a(world, x, y, z); if (structPos != null) result.add(structPos); } catch (IndexOutOfBoundsException e) { // bug in MC, just ignore // hopefully fixed by magic of ASM } } } }); return result.build(); } }