ovh.tgrhavoc.aibot.protocol.v78.Protocol78.java Source code

Java tutorial

Introduction

Here is the source code for ovh.tgrhavoc.aibot.protocol.v78.Protocol78.java

Source

/*******************************************************************************
 *     Copyright (C) 2015 Jordan Dalton (jordan.8474@gmail.com)
 *
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License along
 *     with this program; if not, write to the Free Software Foundation, Inc.,
 *     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *******************************************************************************/
package ovh.tgrhavoc.aibot.protocol.v78;

import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.util.Arrays;

import javax.crypto.SecretKey;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

import ovh.tgrhavoc.aibot.MinecraftBot;
import ovh.tgrhavoc.aibot.auth.*;
import ovh.tgrhavoc.aibot.event.*;
import ovh.tgrhavoc.aibot.event.io.*;
import ovh.tgrhavoc.aibot.event.protocol.client.*;
import ovh.tgrhavoc.aibot.event.protocol.server.*;
import ovh.tgrhavoc.aibot.event.protocol.server.EntitySpawnEvent.SpawnLocation;
import ovh.tgrhavoc.aibot.event.protocol.server.LivingEntitySpawnEvent.*;
import ovh.tgrhavoc.aibot.event.protocol.server.ObjectEntitySpawnEvent.*;
import ovh.tgrhavoc.aibot.event.protocol.server.PaintingSpawnEvent.PaintingSpawnLocation;
import ovh.tgrhavoc.aibot.event.protocol.server.RotatedEntitySpawnEvent.RotatedSpawnLocation;
import ovh.tgrhavoc.aibot.protocol.*;
import ovh.tgrhavoc.aibot.protocol.v78.packets.*;
import ovh.tgrhavoc.aibot.protocol.v78.packets.Packet18Animation.Animation;
import ovh.tgrhavoc.aibot.protocol.v78.packets.Packet19EntityAction.Action;
import ovh.tgrhavoc.aibot.world.entity.MainPlayerEntity;
import ovh.tgrhavoc.aibot.world.item.*;

public final class Protocol78 extends AbstractProtocol implements EventListener {
    public static final int VERSION = 78;
    public static final String VERSION_NAME = "1.6.4";

    private final MinecraftBot bot;

    public Protocol78(MinecraftBot bot) {
        super(VERSION);
        this.bot = bot;

        register(Packet0KeepAlive.class);
        register(Packet1Login.class);
        register(Packet2Handshake.class);
        register(Packet3Chat.class);
        register(Packet4UpdateTime.class);
        register(Packet5PlayerInventory.class);
        register(Packet6SpawnPosition.class);
        register(Packet7UseEntity.class);
        register(Packet8UpdateHealth.class);
        register(Packet9Respawn.class);
        register(Packet10Flying.class);
        register(Packet11PlayerPosition.class);
        register(Packet12PlayerLook.class);
        register(Packet13PlayerLookMove.class);
        register(Packet14BlockDig.class);
        register(Packet15Place.class);
        register(Packet16BlockItemSwitch.class);
        register(Packet17Sleep.class);
        register(Packet18Animation.class);
        register(Packet19EntityAction.class);
        register(Packet20NamedEntitySpawn.class);
        register(Packet21PickupSpawn.class);
        register(Packet22Collect.class);
        register(Packet23VehicleSpawn.class);
        register(Packet24MobSpawn.class);
        register(Packet25EntityPainting.class);
        register(Packet26EntityExpOrb.class);
        register(Packet27SteerVehicle.class);
        register(Packet28EntityVelocity.class);
        register(Packet29DestroyEntity.class);
        register(Packet30Entity.class);
        register(Packet31RelEntityMove.class);
        register(Packet32EntityLook.class);
        register(Packet33RelEntityMoveLook.class);
        register(Packet34EntityTeleport.class);
        register(Packet35EntityHeadRotation.class);
        register(Packet38EntityStatus.class);
        register(Packet39AttachEntity.class);
        register(Packet40EntityMetadata.class);
        register(Packet41EntityEffect.class);
        register(Packet42RemoveEntityEffect.class);
        register(Packet43Experience.class);
        register(Packet44EntityProperties.class);
        register(Packet51MapChunk.class);
        register(Packet52MultiBlockChange.class);
        register(Packet53BlockChange.class);
        register(Packet54PlayNoteBlock.class);
        register(Packet55BlockDestroy.class);
        register(Packet56MapChunks.class);
        register(Packet60Explosion.class);
        register(Packet61DoorChange.class);
        register(Packet62NamedSoundEffect.class);
        register(Packet63Particle.class);
        register(Packet70ChangeGameState.class);
        register(Packet71Weather.class);
        register(Packet100OpenWindow.class);
        register(Packet101CloseWindow.class);
        register(Packet102WindowClick.class);
        register(Packet103SetSlot.class);
        register(Packet104WindowItems.class);
        register(Packet105UpdateProgressbar.class);
        register(Packet106Transaction.class);
        register(Packet107CreativeSetSlot.class);
        register(Packet108EnchantItem.class);
        register(Packet130UpdateSign.class);
        register(Packet131MapData.class);
        register(Packet132TileEntityData.class);
        register(Packet133OpenTileEditor.class);
        register(Packet200Statistic.class);
        register(Packet201PlayerInfo.class);
        register(Packet202PlayerAbilities.class);
        register(Packet203AutoComplete.class);
        register(Packet204ClientInfo.class);
        register(Packet205ClientCommand.class);
        register(Packet206SetObjective.class);
        register(Packet207SetScore.class);
        register(Packet208SetDisplayObjective.class);
        register(Packet209SetPlayerTeam.class);
        register(Packet250CustomPayload.class);
        register(Packet252SharedKey.class);
        register(Packet253EncryptionKeyRequest.class);
        register(Packet254ServerPing.class);
        register(Packet255KickDisconnect.class);

        bot.getEventBus().register(this);
    }

    @EventHandler
    public void onHandshake(HandshakeEvent event) {
        bot.getConnectionHandler().sendPacket(new Packet2Handshake(VERSION, event.getSession().getUsername(),
                event.getServer(), event.getPort()));
    }

    @EventHandler
    public void onInventoryChange(InventoryChangeEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        Packet102WindowClick packet = new Packet102WindowClick();
        packet.windowId = event.getInventory().getWindowId();
        packet.slot = event.getSlot();
        packet.button = event.getButton();
        packet.action = event.getTransactionId();
        packet.item = event.getItem();
        packet.shift = event.isShiftHeld();
        handler.sendPacket(packet);
    }

    @EventHandler
    public void onInventoryClose(InventoryCloseEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        handler.sendPacket(new Packet101CloseWindow(event.getInventory().getWindowId()));
    }

    @EventHandler
    public void onHeldItemDrop(HeldItemDropEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        handler.sendPacket(new Packet14BlockDig(event.isEntireStack() ? 3 : 4, 0, 0, 0, 0));
    }

    @EventHandler
    public void onHeldItemChange(HeldItemChangeEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        handler.sendPacket(new Packet16BlockItemSwitch(event.getNewSlot()));
    }

    @EventHandler
    public void onEntityInteract(EntityInteractEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        int mode;
        if (event instanceof EntityHitEvent)
            mode = 1;
        else if (event instanceof EntityUseEvent)
            mode = 0;
        else
            return;
        handler.sendPacket(new Packet7UseEntity(bot.getPlayer().getId(), event.getEntity().getId(), mode));
    }

    @EventHandler
    public void onArmSwing(ArmSwingEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        handler.sendPacket(new Packet18Animation(bot.getPlayer().getId(), Animation.SWING_ARM));
    }

    @EventHandler
    public void onCrouchUpdate(CrouchUpdateEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        handler.sendPacket(new Packet19EntityAction(bot.getPlayer().getId(),
                event.isCrouching() ? Action.CROUCH : Action.UNCROUCH, 0));
    }

    @EventHandler
    public void onSprintUpdate(SprintUpdateEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        handler.sendPacket(new Packet19EntityAction(bot.getPlayer().getId(),
                event.isSprinting() ? Action.START_SPRINTING : Action.STOP_SPRINTING, 0));
    }

    @EventHandler
    public void onBedLeave(BedLeaveEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        handler.sendPacket(new Packet19EntityAction(bot.getPlayer().getId(), Action.LEAVE_BED, 0));
    }

    @EventHandler
    public void onChatSent(ChatSentEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        handler.sendPacket(new Packet3Chat(event.getMessage()));
    }

    @EventHandler
    public void onBlockBreak(BlockBreakEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        int code;
        if (event instanceof BlockBreakStartEvent)
            code = 0;
        else if (event instanceof BlockBreakStopEvent)
            code = 1;
        else if (event instanceof BlockBreakCompleteEvent)
            code = 2;
        else
            return;
        handler.sendPacket(new Packet14BlockDig(code, event.getX(), event.getY(), event.getZ(), event.getFace()));
    }

    @EventHandler
    public void onBlockPlace(BlockPlaceEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        Packet15Place packet = new Packet15Place();
        packet.xPosition = event.getX();
        packet.yPosition = event.getY();
        packet.zPosition = event.getZ();
        packet.direction = event.getFace();
        packet.xOffset = event.getXOffset();
        packet.yOffset = event.getYOffset();
        packet.zOffset = event.getZOffset();
        packet.itemStack = event.getItem();
        handler.sendPacket(packet);
    }

    @EventHandler
    public void onPlayerUpdate(PlayerUpdateEvent event) {
        MainPlayerEntity player = event.getEntity();
        double x = player.getX(), y = player.getY(), z = player.getZ(), yaw = player.getYaw(),
                pitch = player.getPitch();
        boolean move = x != player.getLastX() || y != player.getLastY() || z != player.getLastZ();
        boolean rotate = yaw != player.getLastYaw() || pitch != player.getLastPitch();
        boolean onGround = player.isOnGround();
        Packet10Flying packet;
        if (move && rotate)
            packet = new Packet13PlayerLookMove(x, y, y + 1.62000000476837, z, (float) yaw, (float) pitch,
                    onGround);
        else if (move)
            packet = new Packet11PlayerPosition(x, y, y + 1.62000000476837, z, onGround);
        else if (rotate)
            packet = new Packet12PlayerLook((float) yaw, (float) pitch, onGround);
        else
            packet = new Packet10Flying(onGround);
        ConnectionHandler handler = bot.getConnectionHandler();
        handler.sendPacket(packet);
    }

    @EventHandler
    public void onItemUse(ItemUseEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        Packet15Place placePacket = new Packet15Place();
        placePacket.xPosition = -1;
        placePacket.yPosition = -1;
        placePacket.zPosition = -1;
        placePacket.itemStack = event.getItem();
        if (placePacket.itemStack != null && placePacket.itemStack.getId() == 346)
            placePacket.direction = 255;
        else
            placePacket.direction = -1;
        handler.sendPacket(placePacket);
    }

    @EventHandler
    public void onRequestRespawn(RequestRespawnEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        handler.sendPacket(new Packet205ClientCommand(1));
    }

    @EventHandler
    public void onRequestDisconnect(RequestDisconnectEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        handler.sendPacket(new Packet255KickDisconnect(event.getReason()));
    }

    @EventHandler
    public void onPacketProcess(PacketProcessEvent event) {
        Packet packet = event.getPacket();
        ConnectionHandler connectionHandler = bot.getConnectionHandler();
        EventBus eventBus = bot.getEventBus();

        switch (packet.getId()) {
        // Awkward brace style to prevent accidental field name overlap, and
        // switch rather than instanceof for efficiency
        case 0: {
            Packet0KeepAlive keepAlivePacket = (Packet0KeepAlive) packet;
            connectionHandler.sendPacket(keepAlivePacket);
            break;
        }
        case 1: {
            Packet1Login loginPacket = (Packet1Login) packet;
            eventBus.fire(new LoginEvent(loginPacket.playerId, loginPacket.worldType, loginPacket.gameMode,
                    loginPacket.dimension, loginPacket.difficulty, loginPacket.worldHeight,
                    loginPacket.maxPlayers));
            connectionHandler.sendPacket(new Packet204ClientInfo("en_US", 1, 0, true, 2, true));
            break;
        }
        case 3: {
            Packet3Chat chatPacket = (Packet3Chat) packet;
            String message = chatPacket.message;
            try {
                JSONParser parser = new JSONParser();
                JSONObject json = (JSONObject) parser.parse(message);
                // TODO: Actually attempt to handle new chat format
                if (json.containsKey("text"))
                    eventBus.fire(new ChatReceivedEvent((String) json.get("text")));
            } catch (Exception exception) {
            }
            break;
        }
        case 4: {
            Packet4UpdateTime timePacket = (Packet4UpdateTime) packet;
            eventBus.fire(new TimeUpdateEvent(timePacket.time, timePacket.otherTime));
            break;
        }
        case 5: {
            Packet5PlayerInventory inventoryPacket = (Packet5PlayerInventory) packet;
            eventBus.fire(new PlayerEquipmentUpdateEvent(inventoryPacket.entityID, inventoryPacket.slot,
                    inventoryPacket.item));
            break;
        }
        case 8: {
            Packet8UpdateHealth updateHealthPacket = (Packet8UpdateHealth) packet;
            int health = (int) Math.ceil(updateHealthPacket.healthMP);
            eventBus.fire(
                    new HealthUpdateEvent(health, updateHealthPacket.food, updateHealthPacket.foodSaturation));
            break;
        }
        case 9: {
            Packet9Respawn respawnPacket = (Packet9Respawn) packet;
            eventBus.fire(new RespawnEvent(respawnPacket.respawnDimension, respawnPacket.difficulty,
                    respawnPacket.gameMode, respawnPacket.worldType, respawnPacket.worldHeight));
            break;
        }
        case 13: {
            Packet13PlayerLookMove lookMovePacket = (Packet13PlayerLookMove) packet;
            connectionHandler.sendPacket(lookMovePacket);
            eventBus.fire(new TeleportEvent(lookMovePacket.x, lookMovePacket.y, lookMovePacket.z,
                    lookMovePacket.stance, lookMovePacket.yaw, lookMovePacket.pitch));
            break;
        }
        case 17: {
            Packet17Sleep sleepPacket = (Packet17Sleep) packet;
            eventBus.fire(
                    new SleepEvent(sleepPacket.entityID, sleepPacket.bedX, sleepPacket.bedY, sleepPacket.bedZ));
            break;
        }
        case 18: {
            Packet18Animation animationPacket = (Packet18Animation) packet;
            if (animationPacket.animation == Animation.EAT_FOOD)
                eventBus.fire(new EntityEatEvent(animationPacket.entityId));
            break;
        }
        case 20: {
            Packet20NamedEntitySpawn spawnPacket = (Packet20NamedEntitySpawn) packet;
            RotatedSpawnLocation location = new RotatedSpawnLocation(spawnPacket.xPosition / 32D,
                    spawnPacket.yPosition / 32D, spawnPacket.zPosition / 32D, (spawnPacket.rotation * 360) / 256F,
                    (spawnPacket.pitch * 360) / 256F);
            ItemStack heldItem = new BasicItemStack(spawnPacket.currentItem, 1, 0);
            eventBus.fire(new PlayerSpawnEvent(spawnPacket.entityId, spawnPacket.name, heldItem, location,
                    spawnPacket.data));
            break;
        }
        case 22: {
            Packet22Collect collectPacket = (Packet22Collect) packet;
            eventBus.fire(new EntityCollectEvent(collectPacket.collectedEntityId, collectPacket.collectorEntityId));
            break;
        }
        case 23: {
            Packet23VehicleSpawn spawnPacket = (Packet23VehicleSpawn) packet;
            RotatedSpawnLocation location = new RotatedSpawnLocation(spawnPacket.xPosition / 32D,
                    spawnPacket.yPosition / 32D, spawnPacket.zPosition / 32D, (spawnPacket.yaw * 360) / 256F,
                    (spawnPacket.pitch * 360) / 256F);
            ObjectSpawnData spawnData;
            if (spawnPacket.throwerEntityId != 0)
                spawnData = new ThrownObjectSpawnData(spawnPacket.type, spawnPacket.throwerEntityId,
                        spawnPacket.speedX / 8000D, spawnPacket.speedY / 8000D, spawnPacket.speedZ / 8000D);
            else
                spawnData = new ObjectSpawnData(spawnPacket.type);
            eventBus.fire(new ObjectEntitySpawnEvent(spawnPacket.entityId, location, spawnData));
            break;
        }
        case 24: {
            Packet24MobSpawn spawnPacket = (Packet24MobSpawn) packet;
            LivingEntitySpawnLocation location = new LivingEntitySpawnLocation(spawnPacket.xPosition / 32D,
                    spawnPacket.yPosition / 32D, spawnPacket.zPosition / 32D, (spawnPacket.yaw * 360) / 256F,
                    (spawnPacket.pitch * 360) / 256F, (spawnPacket.headYaw * 360) / 256F);
            LivingEntitySpawnData data = new LivingEntitySpawnData(spawnPacket.type, spawnPacket.velocityX / 8000D,
                    spawnPacket.velocityY / 8000D, spawnPacket.velocityZ / 8000D);
            eventBus.fire(new LivingEntitySpawnEvent(spawnPacket.entityId, location, data, spawnPacket.metadata));
            break;
        }
        case 25: {
            Packet25EntityPainting spawnPacket = (Packet25EntityPainting) packet;
            PaintingSpawnLocation location = new PaintingSpawnLocation(spawnPacket.xPosition, spawnPacket.yPosition,
                    spawnPacket.zPosition, spawnPacket.direction);
            eventBus.fire(new PaintingSpawnEvent(spawnPacket.entityId, location, spawnPacket.title));
            break;
        }
        case 26: {
            Packet26EntityExpOrb spawnPacket = (Packet26EntityExpOrb) packet;
            SpawnLocation location = new SpawnLocation(spawnPacket.posX / 32D, spawnPacket.posY / 32D,
                    spawnPacket.posZ / 32D);
            eventBus.fire(new ExpOrbSpawnEvent(spawnPacket.entityId, location, spawnPacket.xpValue));
            break;
        }
        case 28: {
            Packet28EntityVelocity velocityPacket = (Packet28EntityVelocity) packet;
            eventBus.fire(new EntityVelocityEvent(velocityPacket.entityId, velocityPacket.motionX / 8000D,
                    velocityPacket.motionY / 8000D, velocityPacket.motionZ / 8000D));
            break;
        }
        case 29: {
            Packet29DestroyEntity destroyEntityPacket = (Packet29DestroyEntity) packet;
            for (int id : destroyEntityPacket.entityIds)
                eventBus.fire(new EntityDespawnEvent(id));
            break;
        }
        case 30:
        case 31:
        case 32:
        case 33: {
            Packet30Entity entityPacket = (Packet30Entity) packet;
            if (packet instanceof Packet31RelEntityMove || packet instanceof Packet33RelEntityMoveLook)
                eventBus.fire(new EntityMoveEvent(entityPacket.entityId, entityPacket.xPosition / 32D,
                        entityPacket.yPosition / 32D, entityPacket.zPosition / 32D));
            if (packet instanceof Packet32EntityLook || packet instanceof Packet33RelEntityMoveLook)
                eventBus.fire(new EntityRotateEvent(entityPacket.entityId, (entityPacket.yaw * 360) / 256F,
                        (entityPacket.pitch * 360) / 256F));
            break;
        }
        case 34: {
            Packet34EntityTeleport teleportPacket = (Packet34EntityTeleport) packet;
            eventBus.fire(new EntityTeleportEvent(teleportPacket.entityId, teleportPacket.xPosition / 32D,
                    teleportPacket.yPosition / 32D, teleportPacket.zPosition / 32D,
                    (teleportPacket.yaw * 360) / 256F, (teleportPacket.pitch * 360) / 256F));
            break;
        }
        case 35: {
            Packet35EntityHeadRotation headRotatePacket = (Packet35EntityHeadRotation) packet;
            eventBus.fire(new EntityHeadRotateEvent(headRotatePacket.entityId,
                    (headRotatePacket.headRotationYaw * 360) / 256F));
            break;
        }
        case 38: {
            Packet38EntityStatus statusPacket = (Packet38EntityStatus) packet;
            if (statusPacket.entityStatus == 2)
                eventBus.fire(new EntityHurtEvent(statusPacket.entityId));
            else if (statusPacket.entityStatus == 3)
                eventBus.fire(new EntityDeathEvent(statusPacket.entityId));
            else if (statusPacket.entityStatus == 9)
                eventBus.fire(new EntityStopEatingEvent(statusPacket.entityId));
            break;
        }
        case 39: {
            Packet39AttachEntity attachEntityPacket = (Packet39AttachEntity) packet;
            if (attachEntityPacket.leashed)
                break;
            if (attachEntityPacket.vehicleEntityId != -1)
                eventBus.fire(
                        new EntityMountEvent(attachEntityPacket.entityId, attachEntityPacket.vehicleEntityId));
            else
                eventBus.fire(new EntityDismountEvent(attachEntityPacket.entityId));
            break;
        }
        case 40: {
            Packet40EntityMetadata metadataPacket = (Packet40EntityMetadata) packet;
            eventBus.fire(new EntityMetadataUpdateEvent(metadataPacket.entityId, metadataPacket.metadata));
            break;
        }
        case 43: {
            Packet43Experience experiencePacket = (Packet43Experience) packet;
            eventBus.fire(
                    new ExperienceUpdateEvent(experiencePacket.experienceLevel, experiencePacket.experienceTotal));
            break;
        }
        case 51: {
            Packet51MapChunk mapChunkPacket = (Packet51MapChunk) packet;
            processChunk(mapChunkPacket.x, mapChunkPacket.z, mapChunkPacket.chunkData, mapChunkPacket.bitmask,
                    mapChunkPacket.additionalBitmask, true, mapChunkPacket.biomes);
            break;
        }
        case 52: {
            Packet52MultiBlockChange multiBlockChangePacket = (Packet52MultiBlockChange) packet;
            if (multiBlockChangePacket.metadataArray == null)
                return;
            DataInputStream in = new DataInputStream(
                    new ByteArrayInputStream(multiBlockChangePacket.metadataArray));
            try {
                for (int i = 0; i < multiBlockChangePacket.size; i++) {
                    short word0 = in.readShort();
                    short word1 = in.readShort();
                    int id = (word1 & 0xfff) >> 4;
                    int metadata = word1 & 0xf;
                    int x = word0 >> 12 & 0xf;
                    int z = word0 >> 8 & 0xf;
                    int y = word0 & 0xff;
                    eventBus.fire(new BlockChangeEvent(id, metadata, (multiBlockChangePacket.xPosition * 16) + x, y,
                            (multiBlockChangePacket.zPosition * 16) + z));
                }
            } catch (IOException exception) {
                exception.printStackTrace();
            }
            break;
        }
        case 53: {
            Packet53BlockChange blockChangePacket = (Packet53BlockChange) packet;
            eventBus.fire(new BlockChangeEvent(blockChangePacket.type, blockChangePacket.metadata,
                    blockChangePacket.xPosition, blockChangePacket.yPosition, blockChangePacket.zPosition));
            break;
        }
        case 56: {
            if (bot.isMovementDisabled())
                return;
            Packet56MapChunks chunkPacket = (Packet56MapChunks) packet;
            for (int i = 0; i < chunkPacket.primaryBitmap.length; i++)
                processChunk(chunkPacket.chunkX[i], chunkPacket.chunkZ[i], chunkPacket.chunkData[i],
                        chunkPacket.primaryBitmap[i], chunkPacket.secondaryBitmap[i], chunkPacket.skylight, true);
            break;
        }
        case 100: {
            Packet100OpenWindow openWindowPacket = (Packet100OpenWindow) packet;
            eventBus.fire(new WindowOpenEvent(openWindowPacket.windowId, openWindowPacket.inventoryType,
                    openWindowPacket.flag ? openWindowPacket.windowTitle : "", openWindowPacket.slotsCount));
            break;
        }
        case 101: {
            Packet101CloseWindow closeWindowPacket = (Packet101CloseWindow) packet;
            eventBus.fire(new WindowCloseEvent(closeWindowPacket.windowId));
            break;
        }
        case 103: {
            Packet103SetSlot slotPacket = (Packet103SetSlot) packet;
            eventBus.fire(
                    new WindowSlotChangeEvent(slotPacket.windowId, slotPacket.itemSlot, slotPacket.itemStack));
            break;
        }
        case 104: {
            Packet104WindowItems itemsPacket = (Packet104WindowItems) packet;
            eventBus.fire(new WindowUpdateEvent(itemsPacket.windowId, itemsPacket.itemStack));
            break;
        }
        case 132: {
            Packet132TileEntityData tileEntityPacket = (Packet132TileEntityData) packet;
            eventBus.fire(new TileEntityUpdateEvent(tileEntityPacket.xPosition, tileEntityPacket.yPosition,
                    tileEntityPacket.zPosition, tileEntityPacket.actionType, tileEntityPacket.compound));
            break;
        }
        case 130: {
            Packet130UpdateSign signPacket = (Packet130UpdateSign) packet;
            eventBus.fire(new SignUpdateEvent(signPacket.x, signPacket.y, signPacket.z, signPacket.text));
            break;
        }
        case 133: {
            Packet133OpenTileEditor editPacket = (Packet133OpenTileEditor) packet;
            eventBus.fire(new EditTileEntityEvent(editPacket.x, editPacket.y, editPacket.z));
            break;
        }
        case 201: {
            Packet201PlayerInfo infoPacket = (Packet201PlayerInfo) packet;
            if (infoPacket.isConnected)
                eventBus.fire(new PlayerListUpdateEvent(infoPacket.playerName, infoPacket.ping));
            else
                eventBus.fire(new PlayerListRemoveEvent(infoPacket.playerName));
            break;
        }
        case 252: {
            connectionHandler.sendPacket(new Packet205ClientCommand(0));
            break;
        }
        case 253: {
            handleServerAuthData((Packet253EncryptionKeyRequest) packet);
            break;
        }
        case 255: {
            Packet255KickDisconnect kickPacket = (Packet255KickDisconnect) packet;
            eventBus.fire(new KickEvent(kickPacket.reason));
            break;
        }
        }
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public void handleServerAuthData(Packet253EncryptionKeyRequest keyRequest) {
        String serverId = keyRequest.serverId.trim();
        PublicKey publicKey = keyRequest.publicKey;
        SecretKey secretKey = EncryptionUtil.generateSecretKey();
        ConnectionHandler connectionHandler = bot.getConnectionHandler();

        if (!serverId.equals("-")) {
            try {
                AuthService service = bot.getAuthService();
                Session session = bot.getSession();

                String hash = new BigInteger(EncryptionUtil.encrypt(serverId, publicKey, secretKey)).toString(16);
                service.authenticate(service.validateSession(session), hash);
            } catch (InvalidSessionException exception) {
                connectionHandler.disconnect("Session invalid: " + exception);
            } catch (NoSuchAlgorithmException | UnsupportedEncodingException exception) {
                connectionHandler.disconnect("Unable to hash: " + exception);
            } catch (AuthenticationException | IOException exception) {
                connectionHandler.disconnect("Unable to authenticate: " + exception);
            }
        }

        connectionHandler.sendPacket(new Packet252SharedKey(secretKey, publicKey, keyRequest.verifyToken));
    }

    private void processChunk(int x, int z, byte[] data, int bitmask, int additionalBitmask, boolean addSkylight,
            boolean addBiomes) {
        if (data == null)
            return;
        int chunksChanged = 0;
        for (int i = 0; i < 16; i++)
            if ((bitmask & (1 << i)) != 0)
                chunksChanged++;
        if (chunksChanged == 0)
            return;
        int[] yValues = new int[chunksChanged];
        byte[][] allBlocks = new byte[chunksChanged][], allMetadata = new byte[chunksChanged][],
                allLight = new byte[chunksChanged][], allSkylight = new byte[chunksChanged][];
        byte[] biomes = new byte[256];
        int i = 0;
        for (int y = 0; y < 16; y++) {
            if ((bitmask & (1 << y)) == 0)
                continue;
            yValues[i] = y;
            int dataIndex = i * 4096;
            byte[] blocks = Arrays.copyOfRange(data, dataIndex, dataIndex + 4096);
            dataIndex += ((chunksChanged - i) * 4096) + (i * 2048);
            byte[] metadata = Arrays.copyOfRange(data, dataIndex, dataIndex + 2048);
            dataIndex += chunksChanged * 2048;
            byte[] light = Arrays.copyOfRange(data, dataIndex, dataIndex + 2048);
            dataIndex += chunksChanged * 2048;
            byte[] skylight = null;
            if (addSkylight)
                skylight = Arrays.copyOfRange(data, dataIndex, dataIndex + 2048);

            byte[] perBlockMetadata = new byte[4096];
            byte[] perBlockLight = new byte[4096];
            byte[] perBlockSkylight = new byte[4096];

            for (int j = 0; j < 2048; j++) {
                int k = j * 2;
                perBlockMetadata[k] = (byte) (metadata[j] & 0x0F);
                perBlockLight[k] = (byte) (light[j] & 0x0F);
                if (addSkylight)
                    perBlockSkylight[k] = (byte) (skylight[j] & 0x0F);
                k++;
                perBlockMetadata[k] = (byte) (metadata[j] >> 4);
                perBlockLight[k] = (byte) (light[j] >> 4);
                if (addSkylight)
                    perBlockSkylight[k] = (byte) (skylight[j] >> 4);
            }

            allBlocks[i] = blocks;
            allMetadata[i] = perBlockMetadata;
            allLight[i] = perBlockLight;
            allSkylight[i] = perBlockSkylight;
            i++;
        }
        System.arraycopy(data, data.length - 256, biomes, 0, 256);
        EventBus eventBus = bot.getEventBus();
        for (i = 0; i < chunksChanged; i++) {
            ChunkLoadEvent event = new ChunkLoadEvent(x, yValues[i], z, allBlocks[i], allMetadata[i], allLight[i],
                    allSkylight[i], biomes.clone());
            eventBus.fire(event);
        }
    }

    @EventHandler
    public void onPacketReceived(PacketReceivedEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        Packet packet = event.getPacket();
        if (packet instanceof Packet252SharedKey) {
            if (!handler.supportsEncryption()) {
                handler.disconnect("ConnectionHandler does not support encryption!");
                return;
            }
            if (handler.getSharedKey() == null) {
                handler.disconnect("No shared key!");
                return;
            }
            if (!handler.isDecrypting())
                handler.enableDecryption();
        } else if (packet instanceof Packet106Transaction) {
            Packet106Transaction transactionPacket = (Packet106Transaction) packet;
            bot.getEventBus().fire(new WindowTransactionCompleteEvent(transactionPacket.windowId,
                    transactionPacket.shortWindowId, transactionPacket.accepted));
            transactionPacket.accepted = true;
            handler.sendPacket(transactionPacket);
        }
    }

    @EventHandler
    public void onPacketSent(PacketSentEvent event) {
        ConnectionHandler handler = bot.getConnectionHandler();
        Packet packet = event.getPacket();
        if (packet instanceof Packet252SharedKey) {
            if (!handler.supportsEncryption()) {
                handler.disconnect("ConnectionHandler does not support encryption!");
                return;
            }
            if (handler.getSharedKey() != null) {
                handler.disconnect("Shared key already installed!");
                return;
            }
            if (!handler.isEncrypting()) {
                handler.setSharedKey(((Packet252SharedKey) packet).sharedKey);
                handler.enableEncryption();
            }
        }
    }

    public static final class Provider extends ProtocolProvider<Protocol78> {
        @Override
        public Protocol78 getProtocolInstance(MinecraftBot bot) {
            return new Protocol78(bot);
        }

        @Override
        public int getSupportedVersion() {
            return VERSION;
        }

        @Override
        public String getMinecraftVersion() {
            return VERSION_NAME;
        }
    }
}