package net.minecraft.server; import co.aikar.timings.MinecraftTimings; import co.aikar.timings.Timing; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectIterator; import java.util.Iterator; import java.util.Random; import javax.annotation.Nullable; public abstract class StructureGenerator extends WorldGenBase { private final Timing timing = MinecraftTimings.getStructureTiming(this); // Paper private PersistentStructure a; protected Long2ObjectMap<StructureStart> c = new Long2ObjectOpenHashMap(1024); public StructureGenerator() {} public String getName() { return a(); } // Paper // OBF HELPER public abstract String a(); @Override protected final synchronized void a(World world, final int i, final int j, int k, int l, ChunkSnapshot chunksnapshot) { this.a(world); if (!this.c.containsKey(ChunkCoordIntPair.chunkXZ2Int(i, j))) { this.f.nextInt(); try { if (this.a(i, j)) { StructureStart structurestart = this.b(i, j); this.c.put(ChunkCoordIntPair.chunkXZ2Int(i, j), structurestart); if (structurestart.a()) { this.a(i, j, structurestart); } } } catch (Throwable throwable) { CrashReport crashreport = CrashReport.a(throwable, "Exception preparing structure feature"); CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Feature being prepared"); crashreportsystemdetails.a("Is feature chunk", new CrashReportCallable() { public String a() throws Exception { return StructureGenerator.this.a(i, j) ? "True" : "False"; } @Override public Object call() throws Exception { return this.a(); } }); crashreportsystemdetails.a("Chunk location", String.format("%d,%d", new Object[] { Integer.valueOf(i), Integer.valueOf(j)})); crashreportsystemdetails.a("Chunk pos hash", new CrashReportCallable() { public String a() throws Exception { return String.valueOf(ChunkCoordIntPair.chunkXZ2Int(i, j)); } @Override public Object call() throws Exception { return this.a(); } }); crashreportsystemdetails.a("Structure type", new CrashReportCallable() { public String a() throws Exception { return StructureGenerator.this.getClass().getCanonicalName(); } @Override public Object call() throws Exception { return this.a(); } }); throw new ReportedException(crashreport); } } } public synchronized boolean a(World world, Random random, ChunkCoordIntPair chunkcoordintpair) { timing.startTiming(); // Paper this.a(world); int i = (chunkcoordintpair.x << 4) + 8; int j = (chunkcoordintpair.z << 4) + 8; boolean flag = false; ObjectIterator objectiterator = this.c.values().iterator(); while (objectiterator.hasNext()) { StructureStart structurestart = (StructureStart) objectiterator.next(); if (structurestart.a() && structurestart.a(chunkcoordintpair) && structurestart.b().a(i, j, i + 15, j + 15)) { structurestart.a(world, random, new StructureBoundingBox(i, j, i + 15, j + 15)); structurestart.b(chunkcoordintpair); flag = true; this.a(structurestart.e(), structurestart.f(), structurestart); } } timing.stopTiming(); // Paper return flag; } public boolean b(BlockPosition blockposition) { if (this.g == null) return false; // Paper this.a(this.g); return this.c(blockposition) != null; } @Nullable protected StructureStart c(BlockPosition blockposition) { ObjectIterator objectiterator = this.c.values().iterator(); while (objectiterator.hasNext()) { StructureStart structurestart = (StructureStart) objectiterator.next(); if (structurestart.a() && structurestart.b().b(blockposition)) { Iterator iterator = structurestart.c().iterator(); while (iterator.hasNext()) { StructurePiece structurepiece = (StructurePiece) iterator.next(); if (structurepiece.d().b(blockposition)) { return structurestart; } } } } return null; } public boolean a(World world, BlockPosition blockposition) { if (this.g == null) return false; // Paper this.a(world); ObjectIterator objectiterator = this.c.values().iterator(); StructureStart structurestart; do { if (!objectiterator.hasNext()) { return false; } structurestart = (StructureStart) objectiterator.next(); } while (!structurestart.a() || !structurestart.b().b(blockposition)); return true; } @Nullable public abstract BlockPosition getNearestGeneratedFeature(World world, BlockPosition blockposition, boolean flag); protected void a(World world) { if (this.a == null) { // Spigot Start if (world.spigotConfig.saveStructureInfo && !this.a().equals( "Mineshaft" )) { this.a = (PersistentStructure) world.a(PersistentStructure.class, this.a()); } else { this.a = new PersistentStructure(this.a()); } // Spigot End if (this.a == null) { this.a = new PersistentStructure(this.a()); world.a(this.a(), this.a); } else { NBTTagCompound nbttagcompound = this.a.a(); Iterator iterator = nbttagcompound.c().iterator(); while (iterator.hasNext()) { String s = (String) iterator.next(); NBTBase nbtbase = nbttagcompound.get(s); if (nbtbase.getTypeId() == 10) { NBTTagCompound nbttagcompound1 = (NBTTagCompound) nbtbase; if (nbttagcompound1.hasKey("ChunkX") && nbttagcompound1.hasKey("ChunkZ")) { int i = nbttagcompound1.getInt("ChunkX"); int j = nbttagcompound1.getInt("ChunkZ"); StructureStart structurestart = WorldGenFactory.a(nbttagcompound1, world); if (structurestart != null) { this.c.put(ChunkCoordIntPair.chunkXZ2Int(i, j), structurestart); } } } } } } } private void a(int i, int j, StructureStart structurestart) { this.a.a(structurestart.a(i, j), i, j); this.a.c(); } protected abstract boolean a(int i, int j); protected abstract StructureStart b(int i, int j); protected static BlockPosition a(World world, StructureGenerator structuregenerator, BlockPosition blockposition, int i, int j, int k, boolean flag, int l, boolean flag1) { int i1 = blockposition.getX() >> 4; int j1 = blockposition.getZ() >> 4; int k1 = 0; for (Random random = new Random(); k1 <= l; ++k1) { for (int l1 = -k1; l1 <= k1; ++l1) { boolean flag2 = l1 == -k1 || l1 == k1; for (int i2 = -k1; i2 <= k1; ++i2) { boolean flag3 = i2 == -k1 || i2 == k1; if (flag2 || flag3) { int j2 = i1 + i * l1; int k2 = j1 + i * i2; if (j2 < 0) { j2 -= i - 1; } if (k2 < 0) { k2 -= i - 1; } int l2 = j2 / i; int i3 = k2 / i; Random random1 = world.a(l2, i3, k); l2 *= i; i3 *= i; if (flag) { l2 += (random1.nextInt(i - j) + random1.nextInt(i - j)) / 2; i3 += (random1.nextInt(i - j) + random1.nextInt(i - j)) / 2; } else { l2 += random1.nextInt(i - j); i3 += random1.nextInt(i - j); } WorldGenBase.a(world.getSeed(), random, l2, i3); random.nextInt(); if (!world.getWorldBorder().isChunkInBounds(l2, i3)) { continue; } // Paper if (structuregenerator.a(l2, i3)) { if (!flag1 || !world.b(l2, i3)) { return new BlockPosition((l2 << 4) + 8, 64, (i3 << 4) + 8); } } else if (k1 == 0) { break; } } } if (k1 == 0) { break; } } } return null; } }