private void addGameManifestToSaveTransaction(SaveTransactionBuilder saveTransactionBuilder) {
    BlockManager blockManager = CoreRegistry.get(BlockManager.class);
    BiomeManager biomeManager = CoreRegistry.get(BiomeManager.class);
    WorldProvider worldProvider = CoreRegistry.get(WorldProvider.class);
    Time time = CoreRegistry.get(Time.class);
    Game game = CoreRegistry.get(Game.class);

    GameManifest gameManifest =
        new GameManifest(game.getName(), game.getSeed(), time.getGameTimeInMs());
    for (Module module : CoreRegistry.get(ModuleManager.class).getEnvironment()) {
      gameManifest.addModule(module.getId(), module.getVersion());
    }

    List<String> registeredBlockFamilies = Lists.newArrayList();
    for (BlockFamily family : blockManager.listRegisteredBlockFamilies()) {
      registeredBlockFamilies.add(family.getURI().toString());
    }
    gameManifest.setRegisteredBlockFamilies(registeredBlockFamilies);
    gameManifest.setBlockIdMap(blockManager.getBlockIdMap());
    List<Biome> biomes = biomeManager.getBiomes();
    Map<String, Short> biomeIdMap = new HashMap<>(biomes.size());
    for (Biome biome : biomes) {
      short shortId = biomeManager.getBiomeShortId(biome);
      String id = biomeManager.getBiomeId(biome);
      biomeIdMap.put(id, shortId);
    }
    gameManifest.setBiomeIdMap(biomeIdMap);
    gameManifest.addWorld(worldProvider.getWorldInfo());
    saveTransactionBuilder.setGameManifest(gameManifest);
  }
 private void processBiomeChanges(NetData.NetMessage message) {
   for (NetData.BiomeChangeMessage biomeChange : message.getBiomeChangeList()) {
     logger.debug(
         "Received block change to {}", blockManager.getBlock((short) biomeChange.getNewBiome()));
     // TODO: Store changes to blocks that aren't ready to be modified (the surrounding chunks
     // aren't available)
     WorldProvider worldProvider = CoreRegistry.get(WorldProvider.class);
     Vector3i pos = NetMessageUtil.convert(biomeChange.getPos());
     if (worldProvider.isBlockRelevant(pos)) {
       Biome newBiome = biomeManager.getBiomeByShortId((short) biomeChange.getNewBiome());
       worldProvider.setBiome(pos, newBiome);
     } else {
       awaitingChunkReadyBiomeUpdates.put(ChunkMath.calcChunkPos(pos), biomeChange);
     }
   }
 }
  @Override
  public void onChunkReady(Vector3i chunkPos) {
    WorldProvider worldProvider = CoreRegistry.get(WorldProvider.class);

    List<NetData.BlockChangeMessage> updateBlockMessages =
        awaitingChunkReadyBlockUpdates.removeAll(chunkPos);
    for (NetData.BlockChangeMessage message : updateBlockMessages) {
      Vector3i pos = NetMessageUtil.convert(message.getPos());
      Block newBlock = blockManager.getBlock((short) message.getNewBlock());
      worldProvider.setBlock(pos, newBlock);
    }

    List<NetData.BiomeChangeMessage> updateBiomeMessages =
        awaitingChunkReadyBiomeUpdates.removeAll(chunkPos);
    for (NetData.BiomeChangeMessage message : updateBiomeMessages) {
      Vector3i pos = NetMessageUtil.convert(message.getPos());
      Biome newBiome = biomeManager.getBiomeByShortId((short) message.getNewBiome());
      worldProvider.setBiome(pos, newBiome);
    }
  }