Java tutorial
/* * SpawnChecker. * * (c) 2014 alalwww * https://github.com/alalwww * * This mod is distributed under the terms of the Minecraft Mod Public License 1.0, or MMPL. * Please check the contents of the license located in http://www.mod-buildcraft.com/MMPL-1.0.txt * * ?? MOD ??Minecraft Mod Public License (MMPL) 1.0 ??????????? * ??????????? http://www.mod-buildcraft.com/MMPL-1.0.txt */ package net.awairo.mcmod.spawnchecker.presetmode.spawncheck; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.LinkedHashMap; import java.util.Map; import java.util.Random; import com.google.common.base.Optional; import com.google.common.base.Supplier; import com.google.common.collect.Table; import com.google.common.collect.Tables; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import net.minecraft.client.Minecraft; import net.minecraft.server.MinecraftServer; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraft.world.biome.BiomeGenBase; import net.awairo.mcmod.spawnchecker.SpawnChecker; import net.awairo.mcmod.spawnchecker.client.common.ConstantsConfig; import net.awairo.mcmod.spawnchecker.client.common.CoordHelper; import net.awairo.mcmod.spawnchecker.client.common.MultiServerWorldSeedConfig; import net.awairo.mcmod.spawnchecker.client.common.Refrection; /** * ??. * * @author alalwww */ public class SlimeSpawnChecker implements SpawnCheck { private static final Logger LOGGER = LogManager.getLogger(SpawnChecker.MOD_ID); private static final Minecraft GAME = Minecraft.getMinecraft(); private static final ConstantsConfig CONSTS = ConstantsConfig.instance(); private static final SlimeSpawnChecker SEED_UNKNOWN = new SlimeSpawnChecker(); /** * ??????????. * * @return ? */ public static SlimeSpawnChecker newCheckerOfCurrentWorld() { if (isSinglePlayer()) { final MinecraftServer ms = GAME.getIntegratedServer(); final WorldServer ws = ms.worldServerForDimension(GAME.thePlayer.dimension); final long seed = ws.getSeed(); LOGGER.info("current world is single player world, world seed is {}.", seed); return new SlimeSpawnChecker(seed); } final Optional<Long> seed = findSeed(Refrection.getServerAddress()); if (seed.isPresent()) { LOGGER.info("world seed is {}.", seed.get()); return new SlimeSpawnChecker(seed.get()); } LOGGER.info("world seed is unknown."); return SEED_UNKNOWN; } private static Optional<Long> findSeed(final Optional<InetSocketAddress> address) { if (!address.isPresent()) return Optional.absent(); final InetAddress inetAddress = address.get().getAddress(); final String host = inetAddress.getHostName(); final String ip = inetAddress.getHostAddress(); final Integer port = address.get().getPort(); LOGGER.info("multiplayer server: host={}, ip={}, port={}", host, ip, port); return seedConfig().get(host, port).or(seedConfig().get(ip, port)); } private static MultiServerWorldSeedConfig seedConfig() { return MultiServerWorldSeedConfig.instance(); } //--------------------- private static final CopiedLogics COPIED_LOGICS = CopiedLogics.INSTANCE; private final Table<Integer, Integer, Boolean> keyTable = createKeytable(); /** ?. */ public final Optional<Long> worldSeed; private SlimeSpawnChecker(long worldSeed) { this.worldSeed = Optional.of(worldSeed); } private SlimeSpawnChecker() { this.worldSeed = Optional.absent(); } /** * ????????. * * @param x X * @param z Z * @return true??? */ public boolean isSlimeChunk(int x, int z) { if (!worldSeed.isPresent()) return false; final Integer chunkX = CoordHelper.toChunkCoord(x); final Integer chunkZ = CoordHelper.toChunkCoord(z); Boolean slimeChunk = keyTable.get(chunkX, chunkZ); if (slimeChunk != null) return slimeChunk.booleanValue(); slimeChunk = isSlimeChunk(getSlimeChunkSeed(worldSeed.get(), chunkX, chunkZ)); keyTable.put(chunkX, chunkZ, slimeChunk); return slimeChunk; } /** * ???????. * * @param x X * @param y Y * @param z Z * @return true???? */ @Override public boolean isSpawnable(int x, int y, int z) { if (isSpawnableSwampland(x, y, z) || isSpawnablePos(x, y, z)) return true; return false; } /** * ????????. * * @see net.minecraft.entity.monster.EntitySlime#getCanSpawnHere() */ private boolean isSpawnableSwampland(int x, int y, int z) { // ???? if (y <= CONSTS.slimeSpawnLimitMinYOnSwampland) return false; // ???? if (y >= CONSTS.slimeSpawnLimitMaxYOnSwampland) return false; // ?? final World world = GAME.theWorld; if (world == null || world.getBiomeGenForCoords(x, z) != BiomeGenBase.swampland) return false; return COPIED_LOGICS.canSpawnByLightLevel(x, y, z, CONSTS.spawnableLightLevel); } /** * ????????. * * @see net.minecraft.entity.monster.EntitySlime#getCanSpawnHere() */ private boolean isSpawnablePos(int x, int y, int z) { if (y >= CONSTS.slimeSpawnLimitMaxY) return false; return isSlimeChunk(x, z); } // ------------------ private static boolean isSinglePlayer() { return GAME.getIntegratedServer() != null; } /** * @see net.minecraft.entity.monster.EntitySlime#getCanSpawnHere() * @see net.minecraft.world.chunk.Chunk#getRandomWithSeed(long) */ private static long getSlimeChunkSeed(final long worldSeed, final int chunkX, final int chunkZ) { return worldSeed + chunkX * chunkX * CONSTS.chunkRandomSeedX1 + chunkX * CONSTS.chunkRandomSeedX2 + chunkZ * chunkZ * CONSTS.chunkRandomSeedZ1 + chunkZ * CONSTS.chunkRandomSeedZ2 ^ CONSTS.slimeRandomSeed; } /** * @see net.minecraft.entity.monster.EntitySlime#getCanSpawnHere() */ private static Boolean isSlimeChunk(final long slimeChunkSeed) { return Boolean.valueOf(new Random(slimeChunkSeed).nextInt(10) == 0); } // ------------------ private static Table<Integer, Integer, Boolean> createKeytable() { final Map<Integer, Map<Integer, Boolean>> backingMap = new CachedMap<>(); final Supplier<Map<Integer, Boolean>> factory = CachedMap.supplier(); return Tables.newCustomTable(backingMap, factory); } private static class CachedMap<K, V> extends LinkedHashMap<K, V> { @SuppressWarnings("rawtypes") enum SingletonSupplier implements Supplier<Map> { INSTANCE; @Override public CachedMap<Object, Object> get() { return new CachedMap<>(); } } @SuppressWarnings({ "rawtypes", "unchecked" }) static final <M extends Map> Supplier<M> supplier() { return (Supplier<M>) SingletonSupplier.INSTANCE; } private final int maxSize; CachedMap() { super(CONSTS.slimeChunkCacheSize, 0.75f, true); maxSize = CONSTS.slimeChunkCacheSize; } @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > maxSize; } } }