ru.jts_dev.gameserver.packets.GameClientPacketHandler.java Source code

Java tutorial

Introduction

Here is the source code for ru.jts_dev.gameserver.packets.GameClientPacketHandler.java

Source

/*
 * Copyright (c) 2015, 2016, 2017 JTS-Team authors and/or its affiliates. All rights reserved.
 *
 * This file is part of JTS-V3 Project.
 *
 * JTS-V3 Project 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 3 of the License, or
 * (at your option) any later version.
 *
 * JTS-V3 Project 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 JTS-V3 Project.  If not, see <http://www.gnu.org/licenses/>.
 */

package ru.jts_dev.gameserver.packets;

import io.netty.buffer.ByteBuf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import ru.jts_dev.common.packets.IncomingMessageWrapper;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

import static org.springframework.integration.ip.IpHeaders.CONNECTION_ID;
import static ru.jts_dev.gameserver.packets.Opcode.CLIENT_SWITCH_OPCODE;

/**
 * @author Camelion
 * @since 12.12.15
 */
@Component
public class GameClientPacketHandler {
    private static final int BYTES_COUNT = -Byte.MIN_VALUE + Byte.MAX_VALUE;
    private static final Logger log = LoggerFactory.getLogger(GameClientPacketHandler.class);

    private final ApplicationContext context;

    private Map<Integer, Object> packets;

    @Autowired
    public GameClientPacketHandler(ApplicationContext context) {
        this.context = context;
    }

    /**
     * Find beans with {@link Opcode} annotation, and put it to {@link #packets} map
     * where key is 'first' (or single) part of packet identifier
     * and value is a bean name of packet or {@link Map} with 'second' part of
     * packet identifier as key, and packet bean name as value
     *
     * @see IncomingMessageWrapper
     */
    @PostConstruct
    private void postConstruct() {
        final String[] packetBeanNames = context.getBeanNamesForAnnotation(Opcode.class);

        packets = new HashMap<>(BYTES_COUNT, 1.0f);
        for (final String beanName : packetBeanNames) {
            final Opcode opcode = context.findAnnotationOnBean(beanName, Opcode.class);

            assert opcode != null : "opcode annotation not present for bean " + beanName;

            final int firstOpcode = opcode.first();
            final int secondOpcode = opcode.second();
            if (secondOpcode != Integer.MIN_VALUE) {
                final Map<Integer, Object> secondOpcodesMap = (Map<Integer, Object>) packets
                        .getOrDefault(firstOpcode, new HashMap<Integer, Object>());

                assert !secondOpcodesMap.containsKey(secondOpcode) : "duplicate second opcode for " + beanName
                        + ", old is " + secondOpcodesMap.get(secondOpcode);

                secondOpcodesMap.put(secondOpcode, beanName);
                packets.putIfAbsent(firstOpcode, secondOpcodesMap);
            }

            assert firstOpcode == CLIENT_SWITCH_OPCODE
                    || !packets.containsKey(firstOpcode) : "duplicate first opcode for " + beanName + ", old is "
                            + packets.get(firstOpcode);

            packets.putIfAbsent(firstOpcode, beanName);
        }
    }

    /**
     * Handle incoming packet by first bytes (opcode)
     *
     * @param buf          - packet data
     * @param connectionId - client connectionId from Spring Integration
     * @return - handled packet
     */
    public IncomingMessageWrapper handle(ByteBuf buf, @Header(CONNECTION_ID) String connectionId) {
        if (buf.readableBytes() == 0)
            throw new RuntimeException("At least 1 readable byte excepted in buffer");

        int opcode = buf.readUnsignedByte();

        if (!packets.containsKey(opcode))
            throw new RuntimeException("Invalid first packet opcode: " + String.format("0x%02X", (byte) opcode));

        IncomingMessageWrapper msg;

        Object node = packets.get(opcode);
        if (node instanceof String) {
            msg = context.getBean((String) node, IncomingMessageWrapper.class);
        } else { // (node instanceof Map)
            opcode = buf.readUnsignedShort();

            if (!((Map) node).containsKey(opcode))
                throw new RuntimeException(
                        "Invalid second packet opcode: " + String.format("0x%02X", (byte) opcode));

            node = ((Map) node).get(opcode);
            msg = context.getBean((String) node, IncomingMessageWrapper.class);
        }

        ByteBuf data = buf.slice();

        log.debug("received packet: {}, length: {}", msg.getClass().getSimpleName(), data.readableBytes());

        msg.getHeaders().put(CONNECTION_ID, connectionId);
        msg.setPayload(data);

        return msg;
    }
}