/*
* 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);
}
}