gt:         int x = (hi & 0xF) + (getHashX(chunkHash) << 4);
context: package cn.nukkit.level;
  
  import cn.nukkit.Player;
  import cn.nukkit.Server;
  import cn.nukkit.block.Block;
  import cn.nukkit.block.BlockAir;
  import cn.nukkit.block.BlockRedstoneComparator;
  import cn.nukkit.block.BlockRedstoneDiode;
  import cn.nukkit.blockentity.BlockEntity;
  import cn.nukkit.blockentity.BlockEntityChest;
  import cn.nukkit.collection.PrimitiveList;
  import cn.nukkit.entity.Entity;
  import cn.nukkit.entity.item.EntityItem;
  import cn.nukkit.entity.item.EntityXPOrb;
  import cn.nukkit.entity.projectile.EntityArrow;
  import cn.nukkit.entity.weather.EntityLightning;
  import cn.nukkit.event.block.BlockBreakEvent;
  import cn.nukkit.event.block.BlockPlaceEvent;
  import cn.nukkit.event.block.BlockUpdateEvent;
  import cn.nukkit.event.level.*;
  import cn.nukkit.event.player.PlayerInteractEvent;
  import cn.nukkit.event.player.PlayerInteractEvent.Action;
  import cn.nukkit.event.weather.LightningStrikeEvent;
  import cn.nukkit.inventory.InventoryHolder;
  import cn.nukkit.item.Item;
  import cn.nukkit.item.ItemBlock;
  import cn.nukkit.item.enchantment.Enchantment;
  import cn.nukkit.level.format.Chunk;
  import cn.nukkit.level.format.ChunkSection;
  import cn.nukkit.level.format.FullChunk;
  import cn.nukkit.level.format.LevelProvider;
  import cn.nukkit.level.format.anvil.Anvil;
  import cn.nukkit.level.format.generic.BaseFullChunk;
  import cn.nukkit.level.format.generic.BaseLevelProvider;
  import cn.nukkit.level.format.generic.EmptyChunkSection;
  import cn.nukkit.level.format.leveldb.LevelDB;
  import cn.nukkit.level.format.mcregion.McRegion;
  import cn.nukkit.level.generator.Generator;
  import cn.nukkit.level.generator.task.*;
  import cn.nukkit.level.particle.DestroyBlockParticle;
  import cn.nukkit.level.particle.Particle;
  import cn.nukkit.math.*;
  import cn.nukkit.math.BlockFace.Plane;
  import cn.nukkit.metadata.BlockMetadataStore;
  import cn.nukkit.metadata.MetadataValue;
  import cn.nukkit.metadata.Metadatable;
  import cn.nukkit.nbt.NBTIO;
  import cn.nukkit.nbt.tag.*;
  import cn.nukkit.network.protocol.*;
  import cn.nukkit.plugin.Plugin;
  import cn.nukkit.potion.Effect;
  import cn.nukkit.scheduler.AsyncTask;
  import cn.nukkit.scheduler.BlockUpdateScheduler;
  import cn.nukkit.timings.LevelTimings;
  import cn.nukkit.utils.*;
  import co.aikar.timings.Timings;
  import co.aikar.timings.TimingsHistory;
  import com.google.common.base.Preconditions;
  import com.google.common.collect.ImmutableList;
  import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
  import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
  import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
  import it.unimi.dsi.fastutil.objects.ObjectIterator;
  import java.io.File;
  import java.io.IOException;
  import java.lang.ref.SoftReference;
  import java.util.*;
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.concurrent.ConcurrentLinkedQueue;
  import java.util.concurrent.ThreadLocalRandom;
  
  /**
   * author: MagicDroidX Nukkit Project
   */
  public class Level implements ChunkManager, Metadatable {
  
      private static int levelIdCounter = 1;
      private static int chunkLoaderCounter = 1;
      public static int COMPRESSION_LEVEL = 8;
  
      public static final int BLOCK_UPDATE_NORMAL = 1;
      public static final int BLOCK_UPDATE_RANDOM = 2;
      public static final int BLOCK_UPDATE_SCHEDULED = 3;
      public static final int BLOCK_UPDATE_WEAK = 4;
      public static final int BLOCK_UPDATE_TOUCH = 5;
      public static final int BLOCK_UPDATE_REDSTONE = 6;
      public static final int BLOCK_UPDATE_TICK = 7;
  
      public static final int TIME_DAY = 0;
      public static final int TIME_SUNSET = 12000;
      public static final int TIME_NIGHT = 14000;
      public static final int TIME_SUNRISE = 23000;
  
      public static final int TIME_FULL = 24000;
  
      public static final int DIMENSION_OVERWORLD = 0;
      public static final int DIMENSION_NETHER = 1;
  
      // Lower values use less memory
      public static final int MAX_BLOCK_CACHE = 512;
  
      // The blocks that can randomly tick
      private static final boolean[] randomTickBlocks = new boolean[256];
      static {
          randomTickBlocks[Block.GRASS] = true;
          randomTickBlocks[Block.FARMLAND] = true;
          randomTickBlocks[Block.MYCELIUM] = true;
          randomTickBlocks[Block.SAPLING] = true;
          randomTickBlocks[Block.LEAVES] = true;
          randomTickBlocks[Block.LEAVES2] = true;
          randomTickBlocks[Block.SNOW_LAYER] = true;
          randomTickBlocks[Block.ICE] = true;
          randomTickBlocks[Block.LAVA] = true;
          randomTickBlocks[Block.STILL_LAVA] = true;
          randomTickBlocks[Block.CACTUS] = true;
          randomTickBlocks[Block.BEETROOT_BLOCK] = true;
          randomTickBlocks[Block.CARROT_BLOCK] = true;
          randomTickBlocks[Block.POTATO_BLOCK] = true;
          randomTickBlocks[Block.MELON_STEM] = true;
          randomTickBlocks[Block.PUMPKIN_STEM] = true;
          randomTickBlocks[Block.WHEAT_BLOCK] = true;
          randomTickBlocks[Block.SUGARCANE_BLOCK] = true;
          randomTickBlocks[Block.RED_MUSHROOM] = true;
          randomTickBlocks[Block.BROWN_MUSHROOM] = true;
          randomTickBlocks[Block.NETHER_WART_BLOCK] = true;
          randomTickBlocks[Block.FIRE] = true;
          randomTickBlocks[Block.GLOWING_REDSTONE_ORE] = true;
          randomTickBlocks[Block.COCOA_BLOCK] = true;
      }
  
      private final Long2ObjectOpenHashMap<BlockEntity> blockEntities = new Long2ObjectOpenHashMap<>();
  
      private final Long2ObjectOpenHashMap<Player> players = new Long2ObjectOpenHashMap<>();
  
      private final Long2ObjectOpenHashMap<Entity> entities = new Long2ObjectOpenHashMap<>();
  
      public final Long2ObjectOpenHashMap<Entity> updateEntities = new Long2ObjectOpenHashMap<>();
  
      public final Long2ObjectOpenHashMap<BlockEntity> updateBlockEntities = new Long2ObjectOpenHashMap<>();
  
      private boolean cacheChunks = false;
  
      private final Server server;
  
      private final int levelId;
  
      private LevelProvider provider;
  
      private final Int2ObjectOpenHashMap<ChunkLoader> loaders = new Int2ObjectOpenHashMap<>();
  
      private final Map<Integer, Integer> loaderCounter = new HashMap<>();
  
      private final Long2ObjectOpenHashMap<Map<Integer, ChunkLoader>> chunkLoaders = new Long2ObjectOpenHashMap<>();
  
      private final Long2ObjectOpenHashMap<Map<Integer, Player>> playerLoaders = new Long2ObjectOpenHashMap<>();
  
      private Long2ObjectOpenHashMap<List<DataPacket>> chunkPackets = new Long2ObjectOpenHashMap<>();
  
      private final Long2ObjectOpenHashMap<Long> unloadQueue = new Long2ObjectOpenHashMap<>();
  
      private float time;
      public boolean stopTime;
  
      public float skyLightSubtracted;
  
      private String folderName;
  
      private Vector3 mutableBlock;
  
      // Avoid OOM, gc'd references result in whole chunk being sent (possibly higher cpu)
      private Long2ObjectOpenHashMap<SoftReference<Map<Character, Object>>> changedBlocks = new Long2ObjectOpenHashMap<>();
      // Storing the vector is redundant
      private final Object changeBlocksPresent = new Object();
      // Storing extra blocks past 512 is redundant
      private final Map<Character, Object> changeBlocksFullMap = new HashMap<Character, Object>() {
          @Override
          public int size() {
              return Character.MAX_VALUE;
          }
      };
  
  
      private final BlockUpdateScheduler updateQueue;
  //    private final TreeSet<BlockUpdateEntry> updateQueue = new TreeSet<>();
  //    private final List<BlockUpdateEntry> nextTickUpdates = Lists.newArrayList();
      //private final Map<BlockVector3, Integer> updateQueueIndex = new HashMap<>();
  
      private final Long2ObjectOpenHashMap<Map<Integer, Player>> chunkSendQueue = new Long2ObjectOpenHashMap<>();
      private final Long2ObjectOpenHashMap<Boolean> chunkSendTasks = new Long2ObjectOpenHashMap<>();
  
      private final Long2ObjectOpenHashMap<Boolean> chunkPopulationQueue = new Long2ObjectOpenHashMap<>();
      private final Long2ObjectOpenHashMap<Boolean> chunkPopulationLock = new Long2ObjectOpenHashMap<>();
      private final Long2ObjectOpenHashMap<Boolean> chunkGenerationQueue = new Long2ObjectOpenHashMap<>();
      private int chunkGenerationQueueSize = 8;
      private int chunkPopulationQueueSize = 2;
  
      private boolean autoSave = true;
  
      private BlockMetadataStore blockMetadata;
  
      private boolean useSections;
  
      private Position temporalPosition;
      private Vector3 temporalVector;
  
      private final Block[] blockStates;
  
      public int sleepTicks = 0;
  
      private int chunkTickRadius;
      private final Long2ObjectOpenHashMap<Integer> chunkTickList = new Long2ObjectOpenHashMap<>();
      private int chunksPerTicks;
      private boolean clearChunksOnTick;
  
      protected int updateLCG = (new Random()).nextInt();
  
      public LevelTimings timings;
  
      private int tickRate;
      public int tickRateTime = 0;
      public int tickRateCounter = 0;
  
      private Class<? extends Generator> generator;
      private Generator generatorInstance;
  
      private boolean raining = false;
      private int rainTime = 0;
      private boolean thundering = false;
      private int thunderTime = 0;
  
      private long levelCurrentTick = 0;
  
      private int dimension;
  
      public GameRules gameRules;
  
      public Level(Server server, String name, String path, Class<? extends LevelProvider> provider) {
          this.blockStates = Block.fullList;
          this.levelId = levelIdCounter++;
          this.blockMetadata = new BlockMetadataStore(this);
          this.server = server;
          this.autoSave = server.getAutoSave();
  
          boolean convert = provider == McRegion.class || provider == LevelDB.class;
          try {
              if (convert) {
                  String newPath = new File(path).getParent() + "/" + name + ".old/";
                  new File(path).renameTo(new File(newPath));
                  this.provider = provider.getConstructor(Level.class, String.class).newInstance(this, newPath);
              } else {
                  this.provider = provider.getConstructor(Level.class, String.class).newInstance(this, path);
              }
          } catch (Exception e) {
              throw new LevelException("Caused by " + Utils.getExceptionMessage(e));
          }
  
          this.timings = new LevelTimings(this);
  
          if (convert) {
              this.server.getLogger().info(this.server.getLanguage().translateString("nukkit.level.updating",
                      TextFormat.GREEN + this.provider.getName() + TextFormat.WHITE));
              LevelProvider old = this.provider;
              try {
                  this.provider = new LevelProviderConverter(this, path)
                          .from(old)
                          .to(Anvil.class)
                          .perform();
              } catch (IOException e) {
                  throw new RuntimeException(e);
              }
              old.close();
          }
  
          this.provider.updateLevelName(name);
  
          this.server.getLogger().info(this.server.getLanguage().translateString("nukkit.level.preparing",
                  TextFormat.GREEN + this.provider.getName() + TextFormat.WHITE));
  
          this.generator = Generator.getGenerator(this.provider.getGenerator());
  
          try {
              this.useSections = (boolean) provider.getMethod("usesChunkSection").invoke(null);
          } catch (Exception e) {
              throw new RuntimeException(e);
          }
  
          this.folderName = name;
          this.time = this.provider.getTime();
  
          this.raining = this.provider.isRaining();
          this.rainTime = this.provider.getRainTime();
          if (this.rainTime <= 0) {
              setRainTime(ThreadLocalRandom.current().nextInt(168000) + 12000);
          }
  
          this.thundering = this.provider.isThundering();
          this.thunderTime = this.provider.getThunderTime();
          if (this.thunderTime <= 0) {
              setThunderTime(ThreadLocalRandom.current().nextInt(168000) + 12000);
          }
  
          this.levelCurrentTick = this.provider.getCurrentTick();
          this.updateQueue = new BlockUpdateScheduler(this, levelCurrentTick);
  
          this.chunkTickRadius = Math.min(this.server.getViewDistance(),
                  Math.max(1, (Integer) this.server.getConfig("chunk-ticking.tick-radius", 4)));
          this.chunksPerTicks = (int) this.server.getConfig("chunk-ticking.per-tick", 40);
          this.chunkGenerationQueueSize = (int) this.server.getConfig("chunk-generation.queue-size", 8);
          this.chunkPopulationQueueSize = (int) this.server.getConfig("chunk-generation.population-queue-size", 2);
          this.chunkTickList.clear();
          this.clearChunksOnTick = (boolean) this.server.getConfig("chunk-ticking.clear-tick-list", true);
          this.cacheChunks = (boolean) this.server.getConfig("chunk-sending.cache-chunks", false);
          this.temporalPosition = new Position(0, 0, 0, this);
          this.temporalVector = new Vector3(0, 0, 0);
          this.tickRate = 1;
  
          this.skyLightSubtracted = this.calculateSkylightSubtracted(1);
      }
  
      public static long chunkHash(int x, int z) {
          return (((long) x) << 32) | (z & 0xffffffffL);
      }
  
      public static BlockVector3 blockHash(Vector3 block) {
          return blockHash(block.x, block.y, block.z);
      }
  
      public static char localBlockHash(double x, double y, double z) {
          byte hi = (byte) (((int) x & 15) + (((int) z & 15) << 4));
          byte lo = (byte) y;
          return (char) (((hi & 0xFF) << 8) | (lo & 0xFF));
      }
  
      public static Vector3 getBlockXYZ(long chunkHash, char blockHash) {
          int hi = (byte) (blockHash >>> 8);
          int lo = (byte) blockHash;
          int y = lo & 0xFF;
  