001/*
002 * Trident - A Multithreaded Server Alternative
003 * Copyright 2014 The TridentSDK Team
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package net.tridentsdk.server.netty.packet;
019
020import io.netty.channel.ChannelHandlerContext;
021import io.netty.channel.SimpleChannelInboundHandler;
022import net.tridentsdk.Trident;
023import net.tridentsdk.meta.MessageBuilder;
024import net.tridentsdk.server.TridentServer;
025import net.tridentsdk.server.netty.ClientConnection;
026import net.tridentsdk.server.netty.protocol.Protocol;
027import net.tridentsdk.server.packets.login.PacketLoginOutDisconnect;
028import net.tridentsdk.server.packets.play.in.PacketPlayInPlayerFall;
029import net.tridentsdk.server.packets.play.in.PacketPlayInPlayerMove;
030import net.tridentsdk.server.packets.play.out.PacketPlayOutDisconnect;
031import net.tridentsdk.server.player.PlayerConnection;
032import net.tridentsdk.util.TridentLogger;
033
034import javax.annotation.concurrent.ThreadSafe;
035
036/**
037 * The channel handler that is placed into the netty connection bootstrap to process inbound messages from clients (not
038 * just onlinePlayers)
039 *
040 * @author The TridentSDK Team
041 */
042@ThreadSafe
043public class PacketHandler extends SimpleChannelInboundHandler<PacketData> {
044    private final Protocol protocol;
045    private ClientConnection connection;
046
047    public PacketHandler() {
048        this.protocol = ((TridentServer) Trident.instance()).protocol();
049    }
050
051    @Override
052    public void handlerAdded(ChannelHandlerContext context) {
053        this.connection = ClientConnection.connection(context);
054    }
055
056    /**
057     * Converts the PacketData to a Packet depending on the ConnectionStage of the Client  {@inheritDoc}
058     */
059    @Override
060    protected void messageReceived(ChannelHandlerContext context, PacketData data) throws Exception {
061        Packet packet = this.protocol.getPacket(data.id(), this.connection.stage(), PacketDirection.IN);
062
063        //If packet is unknown disconnect the client, as said client seems to be modified
064        if (packet.id() == -1) {
065            this.connection.logout();
066
067            if(connection instanceof PlayerConnection) {
068                PlayerConnection con = (PlayerConnection) connection;
069
070                TridentLogger.get().log(con.player().displayName() + " has been disconnected from the server " +
071                        "for sending an invalid packet (" +
072                        con.address().getHostString() + "," + con.player().uniqueId().toString() +
073                        "," + data.id() + ")");
074            }
075            return;
076        }
077
078        // decode and handle the packet
079        packet.decode(data.data());
080
081        try {
082            // DEBUG =====
083            if (!(packet instanceof PacketPlayInPlayerFall) && !(packet instanceof PacketPlayInPlayerMove))
084                TridentLogger.get().debug("Received packet " + packet.getClass().getSimpleName());
085            // =====
086
087            //TODO: add plugin registration for packet handling
088
089            packet.handleReceived(this.connection);
090
091            if (connection instanceof PlayerConnection) {
092                ((PlayerConnection) connection).resetReadCounter();
093            }
094        } catch (Exception ex) {
095            TridentLogger.get().error(ex);
096
097            switch (this.connection.stage()) {
098                case LOGIN:
099                    PacketLoginOutDisconnect disconnect = new PacketLoginOutDisconnect();
100
101                    disconnect.setJsonMessage(ex.getMessage());
102
103                    this.connection.sendPacket(disconnect);
104                    this.connection.logout();
105
106                    // fall through
107
108                case PLAY:
109                    PacketPlayOutDisconnect quit = new PacketPlayOutDisconnect();
110
111                    quit.set("reason", new MessageBuilder("\"Internal Error: " + ex.getClass().getName() +
112                            ((ex.getMessage() != null) ? ": " + ex.getMessage() : "") + "\"").build().asJson());
113
114                    this.connection.sendPacket(quit);
115                    this.connection.logout();
116
117                    // fall through
118
119                default:
120                    break;
121            }
122        }
123    }
124
125    public void updateConnection(PlayerConnection connection) {
126        this.connection = connection;
127    }
128}