using System; using System.Collections.Generic; using RidR.Exceptions; using RidR.Extensions; namespace RidR.Protocol { /// /// The type of the message recieved from the client. /// public enum ClientMessageType { /// /// The PLAY command is send by a bot when regestering his team position: /// PLAY [TeamName] [TeamRole] /// Play, /// /// The STARTPOS command is send by a bot when picking a starting location: /// STARTPOS [PositionID] [xPos] [yPos] [Orientation] /// StartPosition, /// /// A MOVE command is send by a bot who wishes to move: /// MOVE [Action] [MoveCode] /// Move, /// /// A SHOOT command is send by a bot firing his laser: /// SHOOT [Range] [MoveCode] /// Shoot, /// /// A SPECTATE command, send by a visualizer: /// SPECTATE /// Spectate, /// /// The command is not a RidR protocol command. /// UnknownCommand } /// /// This class provides static methodes to help you format server messages and interpret client messages. /// public static class ServerProtocolHelper { #region Reading Client 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 ClientMessageType 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.PLAY: valid = args.Length == 2; return ClientMessageType.Play; case CommonProtocol.STARTPOS: valid = args.Length == 4; return ClientMessageType.StartPosition; case CommonProtocol.MOVE: valid = args.Length == 2; return ClientMessageType.Move; case CommonProtocol.SHOOT: valid = args.Length == 2; return ClientMessageType.Shoot; case CommonProtocol.SPECTATE: valid = true; return ClientMessageType.Spectate; } } args = new string[] { message }; valid = false; return ClientMessageType.UnknownCommand; } /// /// Interprets the arguments of a PLAY command. /// /// Argument list for the Play command. /// The team name /// The team role /// Thrown when the args parameter does not conform to the RidR protocol. public static void ReadPlayCommand(string[] args, out string teamName, out TeamRole role) { try { teamName = args[0]; role = (TeamRole)int.Parse(args[1]); } catch { throw new ProtocolException(); } } /// /// Interprets the arguments of a STARTPOS command. /// /// Argument list for the Start Position command. /// The ID of the start position set /// The position /// The desired starting orientation /// Thrown when the args parameter does not conform to the RidR protocol. public static void ReadStartPosCommand(string[] args, out int PositionID, out Position position, out Orientation orientation) { try { PositionID = int.Parse(args[0]); int xPos = int.Parse(args[1]); int yPos = int.Parse(args[2]); position = new Position(xPos, yPos); orientation = (Orientation)int.Parse(args[3]); } catch { throw new ProtocolException(); } } /// /// Interprets the arguments of a MOVE command. /// /// Argument list for the Move command. /// The desired move /// The move code /// Thrown when the args parameter does not conform to the RidR protocol. public static void ReadMoveCommand(string[] args, out Move move, out int moveCode) { try { move = (Move)int.Parse(args[0]); moveCode = int.Parse(args[1]); } catch { throw new ProtocolException(); } } /// /// Interprets the arguments of a SHOOT command /// /// Argument list for the Shoot command. /// The desired range /// The move code /// Thrown when the args parameter does not conform to the RidR protocol. public static void ReadShootCommand(string[] args, out int range, out int moveCode) { try { range = int.Parse(args[0]); moveCode = int.Parse(args[1]); } catch { throw new ProtocolException(); } } #endregion #region Format Server Messages /// /// Formats an ACK message. /// public static string FormatAcknowledgeCommand() { return CommonProtocol.ACK; } /// /// Formats a NAK message. /// public static string FormatNotAcknowledgedCommand() { return CommonProtocol.NAK; } /// /// Formats a TEAM message (for clients). /// /// Number of teams /// ID of this team public static string FormatTeamCommand(int TeamCount, int TeamNumber) { return CommonProtocol.TEAM + " " + TeamCount + " " + TeamNumber; } /// /// Formats a TEAM message (for spectators). /// /// The name of the team /// ID of this team public static string FormatTeamCommand(string TeamName, int TeamNumber) { return CommonProtocol.TEAM + " " + TeamName + " " + TeamNumber; } /// /// Formats an 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 public static string FormatInfoCommand(int roundTime, int sightRadius, int chargeTime, int shootTime, int stunTime) { return CommonProtocol.INFO + " " + roundTime + " " + sightRadius + " " + chargeTime + " " + shootTime + " " + stunTime; } /// /// Formats a MAP command, consisting of a list of messages. /// /// The current game map public static string[] FormatMapCommandList(TileType[,] map) { List sTiles = new List(); int width = map.GetLength(0); int height = map.GetLength(1); for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) { if (map[x, y] != TileType.Floor) { string tileString = map[x, y].toChar() + " " + x + " " + y; sTiles.Add(tileString); } } } int listCount = sTiles.Count; string[] commandList = new string[listCount + 1]; commandList[0] = CommonProtocol.MAP + " " + width + " " + height + " " + listCount; for (int i = 0; i < listCount; ++i) { commandList[i + 1] = sTiles[i]; } return commandList; } /// /// Formats a STARTPOS command, consisting of a list of messages. /// /// A list of possible start position sets public static string[] FormatStartPositionCommandList(IList startList) { // Met dank aan Matthijs int count = startList.Count; string[] commandList = new string[count * 3 + 1]; commandList[0] = CommonProtocol.STARTPOS + " " + count; int i = 1; foreach (StartPositionSet sps in startList) { commandList[i++] = sps.PositionID + " " + sps.P1.ToString(); commandList[i++] = sps.PositionID + " " + sps.P2.ToString(); commandList[i++] = sps.PositionID + " " + sps.P3.ToString(); } return commandList; } /// /// Formats a FALSE command. /// public static string FormatFalseCommand() { return CommonProtocol.FALSE; } /// /// Formats a DISQUALIFIED command (for clients). /// public static string FormatDisqualifiedCommand() { return CommonProtocol.DISQUALIFIED; } /// /// Formats a DISQUALIFIED command (for spectators). /// /// The ID of the robot being disqualified public static string FormatDisqualifiedCommand(int robotCode) { return CommonProtocol.DISQUALIFIED + " " + robotCode; } /// /// Formats a START command. /// /// The first move code for this bot public static string FormatStartCommand(int moveCode) { return CommonProtocol.START + " " + moveCode; } /// /// Formats a GAMESTATE command. /// /// The move code for the bot's next move /// A list of all the robots visable to the current bot, or, if this message is for a spectator, a list of all the bots in the game /// A list of laser ranges which fall within the current bot's sight radius, or, if this message is for a spectator, a list of all the active lasers /// A list of new scores since the last gamestate update /// A list of hits on the current bot, or, if this message is for a spectator, a list of all hits throughout the game /// Wheter this update is meant for a spectator public static string[] FormatGameStateCommandList(int moveCode, IList visableRobots, IList visableLasers, IList scores, IList hits, bool forSpectator = false) { int count = visableRobots.Count + visableLasers.Count + scores.Count + hits.Count; string[] commandList = new string[count + 1]; commandList[0] = CommonProtocol.GAMESTATE + " " + count + " " + moveCode; int c = 1; for (int i = 0; i < visableRobots.Count; ++i) { commandList[c++] = CommonProtocol.ROBOT + " " + visableRobots[i].BotCode + " " + visableRobots[i].Position.ToString() + " " + (int)visableRobots[i].Orientation; } for (int i = 0; i < visableLasers.Count; ++i) { commandList[c++] = CommonProtocol.LASER + " " + visableLasers[i].ToString(); } for (int i = 0; i < scores.Count; ++i) { commandList[c++] = CommonProtocol.CRYSTAL + " " + scores[i].ToString(); ; } for (int i = 0; i < hits.Count; ++i) { if (forSpectator) { commandList[c++] = CommonProtocol.HIT + " " + hits[i].BotCode; } else { commandList[c++] = CommonProtocol.HIT + " " + hits[i].RemainingStun; } } return commandList; } /// /// Formats a WIN command. /// /// ID of the winning team public static string FormatWinCommand(int winningTeamId) { return CommonProtocol.WIN + " " + winningTeamId; } /// /// Formats a SCORE command. /// /// Final score of team 1 /// Final score of team 2 /// Final score of team 3 /// Final score of team 4 public static string FormatScoreCommand(int scoreTeam1, int scoreTeam2, int scoreTeam3, int scoreTeam4) { return CommonProtocol.SCORE + " " + scoreTeam1 + " " + scoreTeam2 + " " + scoreTeam3 + " " + scoreTeam4; } /// /// Formats a GAMETIME command. /// /// Amount of time this round lasted in ms public static string FormatGameTimeCommand(int gameTime) { return CommonProtocol.GAMETIME + " " + gameTime; } /// /// Formats a FIRSTTOSCORE command. /// /// ID of the team /// Amount of time it took to score in ms public static string FormatFirstToScoreCommand(int teamID, int gameTime) { return CommonProtocol.FIRSTTOSCORE + " " + teamID + " " + gameTime; } /// /// Formats a 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 public static string FormatRobotStatCommand(int robotCode, float msgPerMin, int distanceTraveled, int lasersFired, int botsHit, int beenHit) { return CommonProtocol.ROBOTSTAT + " " + robotCode + " " + msgPerMin + " " + distanceTraveled + " " + lasersFired + " " + botsHit + " " + beenHit; } #endregion } }