tools/hw2irc/net/ercatec/hw/ProtocolMessage.java
author unC0Rr
Fri, 19 Apr 2024 17:45:05 +0200
changeset 16011 52b51d92e88d
parent 15784 823cf18be1fc
permissions -rw-r--r--
Fix build

/*
 * Java net client for Hedgewars, a free turn based strategy game
 * Copyright (c) 2011 Richard Karolyi <sheepluva@ercatec.net>
 *
 * 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; version 2 of the License
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

package net.ercatec.hw;

import java.util.Arrays;
import java.util.List;
import java.util.Iterator;

public final class ProtocolMessage
{
    public static final int ROOM_FIELD_COUNT = 9;

    private static int minServerVersion = 49;

    public static enum Type {
        // for unknown message types
        _UNKNOWN_MESSAGETYPE_,
        // server messages
        ERROR,
        PING,
        PONG,
        NICK,
        PROTO,
        ASKPASSWORD,
        SERVER_AUTH,
        CONNECTED,
        SERVER_MESSAGE,
        BYE,
        INFO,
        NOTICE,
        CHAT,
        LOBBY__JOINED,
        LOBBY__LEFT,
        ROOMS,
        ROOM,
        ROOM_ADD,
        ROOM_DEL,
        ROOM_UPD,
        ROOM__JOINED,
        ROOM__LEFT,
        CFG,
        TOGGLE_RESTRICT_TEAMS,
        CLIENT_FLAGS,
        CF, // this just an alias and will be mapped to CLIENT_FLAGS
        EM // engine messages
    }

    public final boolean isValid;
    private Type type;
    private String[] args;

/*
    public ProtocolMessage(String messageType)
    {
        args = new String[0];

        try
        {
            type = Type.valueOf(messageType);
            isValid = messageSyntaxIsValid();
        }
        catch (IllegalArgumentException whoops)
        {
            type = Type._UNKNOWN_MESSAGETYPE_;
            args = new String[] { messageType };
            isValid = false;
        }
    }
*/

    private final String[] emptyArgs = new String[0];

    private String[] withoutFirst(final String[] array, final int amount) {
        return Arrays.copyOfRange(array, amount, array.length);
    }

    private final List<String> parts;

    // invalid Message
    public ProtocolMessage() {
        this.parts =  Arrays.asList(emptyArgs);
        this.args = emptyArgs;
        this.isValid = false;
    }

    public ProtocolMessage(final List<String> parts)
    {
        this.parts = parts;
        this.args = emptyArgs;

        final int partc = parts.size();

        if (partc < 1) {
            isValid = false;
            return;
        }

        try {
            type = Type.valueOf(parts.get(0).replaceAll(":", "__"));
        }
        catch (IllegalArgumentException whoops) {
            type = Type._UNKNOWN_MESSAGETYPE_;
        }

        if (type == Type._UNKNOWN_MESSAGETYPE_) {
            args = parts.toArray(args);
            isValid = false;
        }
        else {
            // all parts after command become arguments
            if (partc > 1)
                args = withoutFirst(parts.toArray(args), 1);
            isValid = checkMessage();
        }
    }

    private boolean checkMessage()
    {
        int argc = args.length;

        switch (type)
        {
            // no arguments allowed
            case PING:
            case PONG:
            case TOGGLE_RESTRICT_TEAMS:
                if (argc != 0)
                    return false;
                break;

            // one argument or more
            case EM: // engine messages
            case LOBBY__JOINED: // list of joined players
            case ROOM__JOINED: // list of joined players
                if (argc < 1)
                    return false;
                break;

            // one argument
            case SERVER_MESSAGE:
            case BYE: // disconnect reason
            case ERROR: // error message
            case NICK: // nickname
            case PROTO: // protocol version
            case SERVER_AUTH: // last stage of mutual of authentication
            case ASKPASSWORD: // request for auth with salt
                if (argc != 1)
                    return false;
                break;

            case NOTICE: // argument should be a number
                if (argc != 1)
                    return false;
                try {
                    Integer.parseInt(args[0]);
                } 
                catch (NumberFormatException e) {
                    return false;
                }
                break;

            // two arguments
            case CONNECTED: // server description and version
            case CHAT: // player nick and chat message
            case LOBBY__LEFT: // player nick and leave reason
            case ROOM__LEFT: // player nick and leave reason
                if (argc != 2)
                    return false;
                break;
                
            case ROOM: // "ADD" (or "UPD" + room name ) + room attrs or "DEL" and room name
                if(argc < 2)
                    return false;

                final String subC = args[0];

                if (subC.equals("ADD")) {
                    if(argc != ROOM_FIELD_COUNT + 1)
                        return false;
                    this.type = Type.ROOM_ADD;
                    this.args = withoutFirst(args, 1);
                }
                else if (subC.equals("UPD")) {
                    if(argc != ROOM_FIELD_COUNT + 2)
                        return false;
                    this.type = Type.ROOM_UPD;
                    this.args = withoutFirst(args, 1);
                }
                else if (subC.equals("DEL") && (argc == 2)) {
                    this.type = Type.ROOM_DEL;
                    this.args = withoutFirst(args, 1);
                }
                else
                    return false;
                break;

            // two arguments or more
            case CFG: // setting name and list of setting parameters
                if (argc < 2)
                    return false;
                break;
            case CLIENT_FLAGS: // string of changed flags and player name(s)
            case CF: // alias of CLIENT_FLAGS
                if (argc < 2)
                    return false;
                if (this.type == Type.CF)
                    this.type = Type.CLIENT_FLAGS;
                break;

            // four arguments
            case INFO: // info about a player, name, ip/id, version, room
                if (argc != 4)
                    return false;
                break;

            // multiple of ROOM_FIELD_COUNT arguments (incl. 0)
            case ROOMS:
                if (argc % ROOM_FIELD_COUNT != 0)
                    return false;
                break;
        }

        return true;
    }

    private void maybeSendPassword() {
        
    }

    public Type getType()
    {
        return type;
    }

    public String[] getArguments()
    {
        return args;
    }

    public boolean isValid()
    {
        return isValid;
    }

    public static String partsToString(final List<String> parts)
    {
        final Iterator<String> iter = parts.iterator();

        if (!iter.hasNext())
            return "( -EMPTY- )";

        String result = "(\"" + iter.next();

        while (iter.hasNext()) {
            result += "\", \"" + iter.next();
        }

        return result + "\")";
    }

    public String toString() {
        return partsToString(this.parts);
    }
}