using System;
using System.Collections.Generic;
using System.IO;
using RidR.Exceptions;
using RidR.Extensions;
namespace RidR.Protocol
{
///
/// The type of the message recieved from the server.
///
public enum ServerMessageType
{
///
/// The server Acknowledged command:
/// ACK
///
Ack,
///
/// The server Not Acknowledged command:
/// NAK
///
Nak,
///
/// The TEAM command is sent by the server after team registration:
/// TEAM [TeamCount] [TeamNumber]
///
Team,
///
/// The Info command is sent by the server to inform the client of the game rules:
/// INFO [RoundTime] [SightRadius] [ChargeTime] [ShootTime] [StunTime]
///
Info,
///
/// The Map command is sent when the server wants to send the game map to the client:
/// MAP [Width] [Height] [ElementCount]
///
Map,
///
/// The StartPos command is sent to inform the client of all the posible spawn location:
/// STARTPOS [ElementCount]
///
StartPos,
///
/// The False command is sent when the client requested an invalid starting position:
/// FALSE
///
False,
///
/// The Disqualified command is sent when the client has been disqualified by the server:
/// DISQUALIFIED
///
Disqualified,
///
/// The Start command is sent when the game begins:
/// START [MoveCode]
///
Start,
///
/// The GameState command is sent when the server informs the client of the current game state:
/// GAMESTATE [ElementCount] [MoveCode]
///
GameState,
///
/// The Robot command is part of the gamestate command, informing the client of a robot position:
/// ROBOT [BotCode] [xPos] [yPos] [Orientation]
///
Robot,
///
/// The Laser command is part of the gamestate command, informing the client of a laser beam:
/// LASER [TeamID] [xBegin] [xEnd] [yBegin] [yEnd] [ActiveTime]
///
Laser,
///
/// The Crystal command is part of a gamestate command, informing the client of a new score:
/// Crystal [TeamID] [xCrystal] [yCrystal]
///
Crystal,
///
/// The Hit command is part of a gamestate command, informing the client that he has been hit:
/// HIT [RemainingStun]
///
Hit,
///
/// The Win command is sent when a team has won the current game:
/// WIN [WinningTeamID]
///
Win,
///
/// The Score command is sent after the game has ended to inform the client of the final score:
/// SCORE [ScoreTeam1] [ScoreTeam2] [ScoreTeam3] [ScoreTeam4]
///
Score,
///
/// The GameTime command is sent after the game has ended and informs the client of the total game time:
/// GAMETIME [GameTime]
///
GameTime,
///
/// The First to Score command is sent after the game has ended and informs the clients of the first team to score:
/// FIRSTTOSCORE [TeamID] [GameTime]
///
FirstToScore,
///
/// The Robot stat command shows statistics on the bots:
/// ROBOTSTAT [RobotCode] [MsgPerMin] [DistanceTraveled] [LasersFired] [BotsHit] [BeenHit]
///
RobotStats,
///
/// The command is not a RidR protocol command.
///
Unknown
}
///
/// This class provides static methodes to help you format client messages and interpret server messages.
///
public static class ClientProtocolHelper
{
#region Reading Server Messages
///
/// Reads the message and determines the command.
///
/// The message string
/// The arguments passed with the message
/// Returns if the message was formated correctly
/// The type of the message
public static ServerMessageType GetMessageType(string message,
out string[] args,
out bool valid)
{
if (!String.IsNullOrWhiteSpace(message))
{
string[] split = message.Split();
args = split.RemoveAt(0);
switch (split[0])
{
case CommonProtocol.ACK:
valid = true;
return ServerMessageType.Ack;
case CommonProtocol.NAK:
valid = true;
return ServerMessageType.Nak;
case CommonProtocol.TEAM:
valid = args.Length == 2;
return ServerMessageType.Team;
case CommonProtocol.INFO:
valid = args.Length == 5;
return ServerMessageType.Info;
case CommonProtocol.MAP:
valid = args.Length == 3;
return ServerMessageType.Map;
case CommonProtocol.STARTPOS:
valid = args.Length == 1;
return ServerMessageType.StartPos;
case CommonProtocol.FALSE:
valid = true;
return ServerMessageType.False;
case CommonProtocol.DISQUALIFIED:
valid = true;
return ServerMessageType.Disqualified;
case CommonProtocol.START:
valid = args.Length == 1;
return ServerMessageType.Start;
case CommonProtocol.GAMESTATE:
valid = args.Length == 2;
return ServerMessageType.GameState;
case CommonProtocol.ROBOT:
valid = args.Length == 4;
return ServerMessageType.Robot;
case CommonProtocol.LASER:
valid = args.Length == 6;
return ServerMessageType.Laser;
case CommonProtocol.CRYSTAL:
valid = args.Length == 3;
return ServerMessageType.Crystal;
case CommonProtocol.HIT:
valid = args.Length == 1;
return ServerMessageType.Hit;
case CommonProtocol.WIN:
valid = args.Length == 1;
return ServerMessageType.Win;
case CommonProtocol.SCORE:
valid = args.Length == 4;
return ServerMessageType.Score;
case CommonProtocol.GAMETIME:
valid = args.Length == 1;
return ServerMessageType.GameTime;
case CommonProtocol.FIRSTTOSCORE:
valid = args.Length == 2;
return ServerMessageType.FirstToScore;
case CommonProtocol.ROBOTSTAT:
valid = args.Length == 6;
return ServerMessageType.RobotStats;
}
}
args = new string[] { message };
valid = false;
return ServerMessageType.Unknown;
}
///
/// Interprets the arguments of a TEAM command.
///
/// Argument list for the TEAM command
/// The number of teams
/// The number of this team
/// Thrown when the args parameter does not conform to the RidR protocol.
public static void ReadTeamCommand(string[] args,
out int teamCount,
out int teamNumber)
{
try
{
teamCount = int.Parse(args[0]);
teamNumber = int.Parse(args[1]);
}
catch
{
throw new ProtocolException();
}
}
///
/// Interprets the arguments of an INFO command.
///
/// Argument list for the INFO command
/// Total time of this round in seconds
/// The radius in which clients can see
/// The amount of time it takes to charge 10 points of energy in milliseconds
/// The amount of time a laser persists in milliseconds
/// The amount of time a bot is stunned when hit in milliseconds
/// Thrown when the args parameter does not conform to the RidR protocol.
public static void ReadInfoCommand(string[] args,
out int roundTime,
out int sightRadius,
out int chargeTime,
out int shootTime,
out int stunTime)
{
try
{
roundTime = int.Parse(args[0]);
sightRadius = int.Parse(args[1]);
chargeTime = int.Parse(args[2]);
shootTime = int.Parse(args[3]);
stunTime = int.Parse(args[4]);
}
catch
{
throw new ProtocolException();
}
}
///
/// Interprets the arguments of a MAP command.
///
/// Argument list for the MAP command
/// The input stream from which to read the map
/// The current game map
/// Thrown when the args parameter does not conform to the RidR protocol.
public static void ReadMapCommand(string[] args,
StreamReader inputReader,
out TileType[,] map)
{
try
{
int width = int.Parse(args[0]);
int height = int.Parse(args[1]);
int msgCount = int.Parse(args[2]);
map = new TileType[width, height];
for (int i = 0; i < msgCount; ++i)
{
string[] split = inputReader.ReadLine().Split();
TileType tile;
switch (split[0])
{
case "R":
tile = TileType.Rift;
break;
case "W":
tile = TileType.Wall;
break;
case "C":
tile = TileType.Crystal;
break;
default:
tile = TileType.Floor;
break;
}
int x = int.Parse(split[1]);
int y = int.Parse(split[2]);
map[x, y] = tile;
}
}
catch
{
throw new ProtocolException();
}
}
///
/// Interprets the arguments of a STARTPOS command.
///
/// Argument list for the STARTPOS command
/// The input stream from which to read the position list
/// A list of possible start position sets
/// Thrown when the args parameter does not conform to the RidR protocol.
public static void ReadStartPosCommand(string[] args,
StreamReader inputReader,
out IList startList)
{
try
{
int count = int.Parse(args[0]);
startList = new StartPositionSet[count];
for (int i = 0; i < count * 3; ++i)
{
string[] split;
split = inputReader.ReadLine().Split();
int index = int.Parse(split[0]) - 1;
Position p = new Position(int.Parse(split[1]), int.Parse(split[2]));
StartPositionSet set = startList[index];
set.PositionID = index + 1;
if (set.P1.IsEmpty)
{ set.P1 = p; }
else if (set.P2.IsEmpty)
{ set.P2 = p; }
else
{ set.P3 = p; }
startList[index] = set;
}
}
catch
{
throw new ProtocolException();
}
}
///
/// Interprets the arguments of a START command.
///
/// Argument list for the START command
/// The first move code for this bot
/// Thrown when the args parameter does not conform to the RidR protocol.
public static void ReadStartCommand(string[] args,
out int moveCode)
{
try
{
moveCode = int.Parse(args[0]);
}
catch
{
throw new ProtocolException();
}
}
///
/// Interprets the arguments of a GAMESTATE command.
///
/// Argument list for the GAMESTATE command
/// The stream from which to read the game state
/// The move code for the bot's next move
/// A list of all the robots visable to the current bot
/// A list of laser ranges which fall within the current bot's sight radius
/// A list of new scores since the last gamestate update
/// A list of hits on the current bot
/// remaining timeout in milliseconds this bot has incurred due to laser hits
/// Thrown when the args parameter does not conform to the RidR protocol.
public static void ReadGameStateCommand(string[] args,
StreamReader inputReader,
out int moveCode,
out IList visableRobots,
out IList visableLasers,
out IList scores,
out int timeout)
{
try
{
visableRobots = new List();
visableLasers = new List();
scores = new List();
timeout = 0;
int count = int.Parse(args[0]);
moveCode = int.Parse(args[1]);
for (int i = 0; i < count; ++i)
{
string[] split = inputReader.ReadLine().Split();
switch (split[0])
{
case CommonProtocol.ROBOT:
{
Position p = new Position(int.Parse(split[2]), int.Parse(split[3]));
Orientation o = (Orientation)int.Parse(split[4]);
RobotInfo r = new RobotInfo(split[1], p, o);
visableRobots.Add(r);
break;
}
case CommonProtocol.LASER:
{
int id = int.Parse(split[1]);
Position s = new Position(int.Parse(split[2]), int.Parse(split[4]));
Position e = new Position(int.Parse(split[3]), int.Parse(split[5]));
LaserRange l = new LaserRange();
l.ActiveTime = int.Parse(split[6]);
l.StartPoint = s;
l.EndPoint = e;
visableLasers.Add(l);
break;
}
case CommonProtocol.CRYSTAL:
{
ScoreEvent s = new ScoreEvent();
s.TeamId = int.Parse(split[1]);
Position p = new Position(int.Parse(split[2]), int.Parse(split[3]));
s.Position = p;
scores.Add(s);
break;
}
case CommonProtocol.HIT:
{
timeout = Math.Max(timeout, int.Parse(split[1]));
break;
}
}
}
}
catch
{
throw new ProtocolException();
}
}
///
/// Interprets the arguments of a WIN command.
///
/// Argument list for the WIN command
/// ID of the winning team
/// Thrown when the args parameter does not conform to the RidR protocol.
public static void ReadWinCommand(string[] args,
out int winningTeamID)
{
try
{
winningTeamID = int.Parse(args[0]);
}
catch
{
throw new ProtocolException();
}
}
///
/// Interprets the arguments of a SCORE command.
///
/// Argument list for the SCORE command
/// Final score of team 1
/// Final score of team 2
/// Final score of team 3
/// Final score of team 4
/// Thrown when the args parameter does not conform to the RidR protocol.
public static void ReadFinalScoreCommand(string[] args,
out int scoreTeam1,
out int scoreTeam2,
out int scoreTeam3,
out int scoreTeam4)
{
try
{
scoreTeam1 = int.Parse(args[0]);
scoreTeam2 = int.Parse(args[1]);
scoreTeam3 = int.Parse(args[2]);
scoreTeam4 = int.Parse(args[3]);
}
catch
{
throw new ProtocolException();
}
}
///
/// Interprets the arguments of a GAMETIME command.
///
/// Argument list for the GAMETIME command
/// Amount of time this round lasted in ms
public static void ReadGameTimeCommand(string[] args,
out int gameTime)
{
try
{
gameTime = int.Parse(args[0]);
}
catch
{
throw new ProtocolException();
}
}
///
/// Interprets the arguments of a FIRSTTOSCORE command.
///
/// Argument list for the FIRSTTOSCORE command
/// ID of the team
/// Amount of time it took to score in ms
/// Thrown when the args parameter does not conform to the RidR protocol.
public static void ReadFirstToScoreCommand(string[] args,
out int teamID,
out int gameTime)
{
try
{
teamID = int.Parse(args[0]);
gameTime = int.Parse(args[1]);
}
catch
{
throw new ProtocolException();
}
}
///
/// Interprets the arguments of a ROBOTSTAT command.
///
/// Argument list for the ROBOTSTAT command
/// The robot ID
/// Messges per minute
/// Distance traveled by this bot
/// The amount of lasers fired by this bot
/// Amount of bots hit by this bot's lasers
/// Amount of times this bot was hit by a laser
/// Thrown when the args parameter does not conform to the RidR protocol.
public static void ReadRobotStatCommand(string[] args,
out int robotCode,
out float msgPerMin,
out int distanceTraveled,
out int lasersFired,
out int botsHit,
out int beenHit)
{
try
{
robotCode = int.Parse(args[0]);
msgPerMin = float.Parse(args[1]);
distanceTraveled = int.Parse(args[2]);
lasersFired = int.Parse(args[3]);
botsHit = int.Parse(args[4]);
beenHit = int.Parse(args[5]);
}
catch
{
throw new ProtocolException();
}
}
#endregion
#region Format Client Messages
///
/// Formats a PLAY command.
///
/// The team name
/// The team role
public static string FormatPlayCommand(string TeamName, TeamRole role)
{
return CommonProtocol.PLAY + " " + TeamName + " " + (int)role;
}
///
/// Formats a STARTPOS command.
///
/// Argument list for the Start Position command.
/// The ID of the start position set
/// The position
/// The desired starting orientation
public static string FormatStartPosCommand(int PositionID, Position position, Orientation orientation)
{
return CommonProtocol.STARTPOS + " " + PositionID + " " + position.ToString() + " " + (int)orientation;
}
///
/// Formats a MOVE command.
///
/// The desired move
/// The move code
public static string FormatMoveCommand(Move move, int moveCode)
{
return CommonProtocol.MOVE + " " + (int)move + " " + moveCode;
}
///
/// Formats a SHOOT command.
///
/// The desired range
/// The move code
public static string FormatShootCommand(int range, int moveCode)
{
return CommonProtocol.SHOOT + " " + range + " " + moveCode;
}
#endregion
}
}