using System; using System.Linq; using System.Threading; using System.Collections.Generic; using System.Text.RegularExpressions; namespace WorldOfPeacecraft { public class Parser { public const string ValueAns = "ans"; public const string ValueHeight = "height"; public const string ValueWidth = "width"; public const string ValueSource = "src"; public const string ValueSourceId = "srcid"; public const string ValueText = "txt"; public const string ValueRound = "round"; public const string ValueRunning = "running"; public const string ValueDelay = "delay"; public const string ValueId = "id"; public const string ValuePoints = "points"; public const string ValueTotal = "total"; public const string ValueDescription = "desc"; public const string ValueBusy = "busy"; public const string ValueX = "x"; public const string ValueY = "y"; public const string ValueCol = "col"; public const string ValueRow = "row"; public const string EValueDragon = "DRAGON"; public const string EValueStagunt = "STAGHUNT"; public const string EValueSkirmish = "SKIRMISH"; public const string EValueWalkable = "WALKABLE"; public const string EValueWall = "WALL"; public const string EValueForest = "FOREST"; public const string EValueWater = "WATER"; public const string EValueHuntable = "HUNTABLE"; public const string MessUpdate = "upd"; public const string MessDelete = "del"; public const string MessMap = "map"; public const string MessMessage = "mes"; public const string MessResult = "result"; public const string MessChallenge = "challenge"; public const string MessPlayer = "player"; public const string MessYourid = "yourid"; public const string MessTime = "time"; public const string MessOnline = "online"; public const string MessEntities = "ents"; public const string MessPlayers = "players"; public const string MessDragon = "dragon"; public const string MessMapcell = "cell"; private Buffer InputBuffer; private Thread ParserThread; private LinkedList Message; private Regex LastLineRegex; private Backend Backend; public Parser (Backend backend, Buffer buffer) { InputBuffer = buffer; Backend = backend; Message = new LinkedList (); LastLineRegex = new Regex ("^end:[0-9]+$"); ParserThread = new Thread (new ThreadStart (this.RunParser)); } private void RunParser () { while (true) { Message.AddLast (InputBuffer.NextLine ()); Console.WriteLine(Message.Last.Value); if (IsCompletePackage ()) { Parse (); } } } private void Parse () { string[] aMessage = Enumerable.ToArray (Message); Message.Clear(); try { Block mainBlock = new Block (aMessage, 0, aMessage.Length - 1); ProcessData (mainBlock); } catch (ParsingException e) { string errormsg = "Error while parsing message: " + e.Message + "\n"; errormsg += "Message:"; foreach (string line in aMessage) { errormsg += "\n\t" + line; } Console.WriteLine(errormsg); } } private void ProcessData (Block parentBlock) { if (parentBlock.ValueExists (ValueAns)) { ProcessAnswer (parentBlock); return; } CheckBlocksSize (parentBlock, 1, 1); Block block = parentBlock.GetBlocks ().First.Value; switch (block.GetName ()) { case MessUpdate: ProcessUpdate (block); break; case MessDelete: ProcessDelete (block); break; case MessMap: ProcessMap (block); break; case MessMessage: ProcessMessage (block); break; case MessResult: ProcessResult (block); break; case MessChallenge: ProcessChallenge (block); break; case MessPlayer: ProcessPlayerSelf (block); break; case MessYourid: ProcessYourid (block); break; case MessTime: ProcessTime (block); break; case MessOnline: ProcessOnline (block); break; case MessEntities: ProcessEntities (block); break; case MessPlayers: ProcessEntities (block); break; default: ThrowUnknownBlockException (parentBlock, block); break; } } private void ProcessUpdate (Block updateBlock) { CheckBlocksSize (updateBlock, 1, 1); LinkedList blocks = updateBlock.GetBlocks (); Block block = blocks.First.Value; switch (block.GetName ()) { case MessDragon: ProcessDragon (block); break; case MessPlayer: ProcessPlayer (block); break; case MessMapcell: ProcessMapcell (block); break; default: ThrowUnknownBlockException (updateBlock, block); break; } } private void ProcessDelete (Block deleteBlock) { CheckBlocksSize (deleteBlock, 1, 1); LinkedList blocks = deleteBlock.GetBlocks (); Block block = blocks.First.Value; switch (block.GetName ()) { case MessPlayer: Player player = MapPlayer (block); lock (Backend) { Backend.removePlayer (player); } Backend.RefreshGui (); break; case MessDragon: Dragon dragon = MapDragon (block); lock (Backend) { Backend.removeDragon (dragon); } Backend.RefreshGui (); break; default: ThrowUnknownBlockException(deleteBlock, block); break; } } private void ProcessMap (Block mapBlock) { CheckBlocksSize (mapBlock, 1, 1); Block cellsBlock = mapBlock.GetBlocks ().First.Value; int height = mapBlock.GetIntValue (ValueHeight); int width = mapBlock.GetIntValue (ValueWidth); try { CheckBlocksSize (cellsBlock, width * height, width * height); } catch (ParsingException e) { throw new ParsingException ("The received map is " + width + "x" + height + "=" + (width * height) + " cells large, but the received cellblock contained " + cellsBlock.GetBlocks ().Count, e); } Map map = new Map(height, width); foreach (Block cell in cellsBlock.GetBlocks()) { map.SetTile(MapMapcell(cell)); } lock (Backend) { Backend.SetMap (map); } Backend.RefreshGui(); } private void ProcessMessage (Block mesBlock) { // TODO Ausnahmebehandlung von mehrzeiligen Nachrichten? int srcid = mesBlock.GetIntValue (ValueSourceId); string src = mesBlock.GetStringValue (ValueSource); string txt = mesBlock.GetStringValue (ValueText); lock (Backend) { Backend.AddChatMessage (new Message (srcid, src, txt)); } Backend.RefreshGui (); } private void ProcessAnswer (Block block) { // TODO or not TODO } private void ProcessResult (Block procBlock) { CheckBlocksSize (procBlock, 1, 1); int round = procBlock.GetIntValue (ValueRound); bool running = procBlock.GetBoolValue (ValueRunning); int delay = procBlock.GetIntValue (ValueDelay); LinkedList block = procBlock.GetBlocks (); ProcessOpponent (block.First.Value); Result r = new Result(round, running, delay); } private void ProcessOpponent(Block oppBlock) { int id = oppBlock.GetIntValue (ValueId); int points = oppBlock.GetIntValue (ValuePoints); int total = oppBlock.GetIntValue (ValueTotal); LinkedList unnamedValue = oppBlock.GetUnnamedValues (); string stringValue = unnamedValue.First.Value; Decision d; if (stringValue == EValueDragon) d = Decision.DRAGON; else if (stringValue == EValueStagunt) d = Decision.STAGHUNT; else if (stringValue == EValueSkirmish) d = Decision.SKIRMISH; else throw new ParsingException("Wrong param"); // TODO Better message Opponent o = new Opponent (id, points, total, d); } private void ProcessDecision(Block block) { //TODO when "ProcessAnswer" is done } private void ProcessChallenge (Block challengeBlock) { int id = challengeBlock.GetIntValue(ValueId); String type; LinkedList value = challengeBlock.GetUnnamedValues(); // TODO check value size, better name switch (value.First.Value) { case EValueDragon: type = "Dragon"; break; case EValueStagunt: type = "Staghunt"; break; case EValueSkirmish: type = "Skirmisch"; break; default: throw new ParsingException("Invalid type"); // TODO Better message } bool accepted = challengeBlock.GetBoolValue("accepted"); Challenge c = new Challenge(id, type, accepted); } private void ProcessPlayerSelf (Block playerBlock) { Player self = MapPlayer (playerBlock); lock (Backend) { Backend.SetSelfId(self.GetId()); Backend.SetPlayer(self); } Backend.RefreshGui (); } private void ProcessPlayer (Block playerBlock) { lock (Backend) { Backend.SetPlayer (MapPlayer (playerBlock)); } Backend.RefreshGui (); } private void ProcessMapcell (Block mapcellBlock) { Tile tile = MapMapcell (mapcellBlock); lock (Backend) { Backend.getMapObject ().SetTile (tile); } Backend.RefreshGui (); } private void ProcessYourid (Block yourIdBlock) { LinkedList unnamedValues = yourIdBlock.GetUnnamedValues (); string stringValue = unnamedValues.First.Value; int intValue = int.Parse (stringValue); YourID id = new YourID (intValue); } private void ProcessTime (Block timeBlock) { LinkedList unnamedValues = timeBlock.GetUnnamedValues (); string stringValue = unnamedValues.First.Value; long longValue = long.Parse (stringValue); Time t = new Time(longValue); } private void ProcessOnline (Block onlineBlock) { LinkedList unnamedValues = onlineBlock.GetUnnamedValues (); string stringValue = unnamedValues.First.Value; int intValue = int.Parse (stringValue); Online o = new Online(intValue); } private void ProcessEntities (Block entitiesBlock) { // This lock can be placed more efficiently. Does it make sense? lock (Backend) { Backend.clearDragons (); Backend.clearPlayers (); foreach (Block entityBlock in entitiesBlock.GetBlocks ()) { switch (entityBlock.GetName ()) { case MessPlayer: lock (Backend) { Backend.SetPlayer (MapPlayer (entityBlock)); } break; case MessDragon: Backend.SetDragon (MapDragon (entityBlock)); break; default: ThrowUnknownBlockException (entitiesBlock, entityBlock); break; } } } Backend.RefreshGui(); } private void ProcessPlayers (Block playersBlock) { // This lock can be placed more efficiently. Does it make sense? lock (Backend) { Backend.clearPlayers (); foreach (Block playerBlock in playersBlock.GetBlocks ()) { Backend.SetPlayer (MapPlayer (playerBlock)); } } Backend.RefreshGui (); } private void ProcessDragon (Block dragonBlock) { Dragon dragon = MapDragon (dragonBlock); lock (Backend) { Backend.SetDragon (dragon); } Backend.RefreshGui (); } private Dragon MapDragon (Block dragonBlock) { int id = dragonBlock.GetIntValue (ValueId); bool busy = dragonBlock.GetBoolValue (ValueBusy); string desc = dragonBlock.GetStringValue (ValueDescription); int x = dragonBlock.GetIntValue (ValueX); int y = dragonBlock.GetIntValue (ValueY); return new Dragon (id, x, y, desc, busy); } private Player MapPlayer (Block playerBlock) { int id = playerBlock.GetIntValue (ValueId); int score = playerBlock.GetIntValue (ValuePoints); bool busy = playerBlock.GetBoolValue (ValueBusy); string desc = playerBlock.GetStringValue (ValueDescription); int x = playerBlock.GetIntValue (ValueX); int y = playerBlock.GetIntValue (ValueY); return new Player (id, x, y, desc, busy, score); } private Tile MapMapcell (Block cellBlock) { CheckBlocksSize (cellBlock, 1, 1); int x = cellBlock.GetIntValue (ValueRow); int y = cellBlock.GetIntValue (ValueCol); Block propsBlock = cellBlock.GetBlocks ().First.Value; bool walkable = false; bool wall = false; bool forest = false; bool water = false; bool huntable = false; foreach (string prop in propsBlock.GetUnnamedValues()) { switch (prop) { case EValueWalkable: walkable = true; break; case EValueWall: wall = true; break; case EValueForest: forest = true; break; case EValueWater: water = true; break; case EValueHuntable: huntable = true; break; default: throw new ParsingException("Unknown mapcell property '" + prop + "'"); } } return new Tile(x,y,walkable, wall, forest, huntable, water); } private void ThrowUnknownBlockException (Block parentBlock, Block childBlock) { throw new ParsingException ("Unknown Block: '" + childBlock.GetName () + "' (as subblock of '" + parentBlock.GetName () + "')"); } private void CheckBlocksSize (Block parentBlock, int min, int max) { LinkedList blocks = parentBlock.GetBlocks (); if (blocks.Count < min || (max >= 0 && blocks.Count > max)) { if (min == max) { throw new ParsingException ("The block '" + parentBlock.GetName () + "' has to contain exactly '" + min + "' Block, but contained '" + blocks.Count + "'"); } else if (max < 0) { throw new ParsingException ("The block '" + parentBlock.GetName () + "' has to contain at least '" + min + "' Blocks, but contained '" + blocks.Count + "'"); } else { throw new ParsingException ("The block '" + parentBlock.GetName () + "' has to contain between '" + min + "' and '" + max + "' Blocks, but contained '" + blocks.Count + "'"); } } } private bool IsCompletePackage () { string lastLine = Message.Last.Value; return LastLineRegex.IsMatch (lastLine); } public void Start () { ParserThread.Start(); } public void Stop () { ParserThread.Abort (); ParserThread.Join (); } private class Block { private string Name; private LinkedList Blocks = new LinkedList (); private Dictionary Values = new Dictionary (); private LinkedList UnnamedValues = new LinkedList(); public Block (String[] message, int start, int end) { int pos = start; Name = StringUtils.SubstringAfter (message [pos], ":"); pos++; while (pos < end) { // Is the next element a block or a value? if (message [pos].StartsWith ("begin:")) { // It's a block int blockstart = pos; int begins = 1; while (begins > 0) { pos++; if (pos >= end) throw new ParsingException ("The message is missing end:-lines"); if (message [pos].StartsWith ("end:")) begins--; else if (message [pos].StartsWith ("begin:")) begins++; } Blocks.AddLast (new Block (message, blockstart, pos)); } else if (message[pos].Contains(":")) { // It's a value string name = StringUtils.SubstringBefore (message [pos], ":"); string val = StringUtils.SubstringAfter (message [pos], ":"); Values [name] = val; pos++; } else { UnnamedValues.AddLast(message[pos]); pos++; } } } public string GetName () { return Name; } public LinkedList GetUnnamedValues() { return UnnamedValues; } public LinkedList GetBlocks () { return Blocks; } public bool ValueExists (string name) { return Values.ContainsKey (name); } public string GetStringValue (string name) { try { return Values [name]; } catch (KeyNotFoundException e) { throw new ParsingException("The parameter '" + name + "' does not exist in block '" + Name + "'", e); } } public int GetIntValue (string name) { try { return int.Parse (Values [name]); } catch (KeyNotFoundException e) { throw new ParsingException("The parameter '" + name + "' does not exist in block '" + Name + "'", e); } catch (FormatException e) { throw new ParsingException("The parameter '" + name + "' in block '" + Name + "' is not an integer (it was '" + Values [name] + "')", e); } catch (OverflowException e) { throw new ParsingException("The parameter '" + name + "' in blo '" + Name + "' does not fit into an integer (it was '" + Values [name] + "')", e); } } public long GetLongValue (string name) { try { return long.Parse (Values [name]); } catch (KeyNotFoundException e) { throw new ParsingException("The parameter '" + name + "' does not exist in block '" + Name + "'", e); } catch (FormatException e) { throw new ParsingException("The parameter '" + name + "' in block '" + Name + "' is not a long (it was '" + Values [name] + "')", e); } catch (OverflowException e) { throw new ParsingException("The parameter '" + name + "' in blo '" + Name + "' does not fit into a long (it was '" + Values [name] + "')", e); } } public bool GetBoolValue (string name) { try { return bool.Parse (Values [name]); } catch (KeyNotFoundException e) { throw new ParsingException ("The parameter '" + name + "' does not exist in block '" + Name + "'", e); } catch (FormatException e) { throw new ParsingException ("The parameter '" + name + "' in block '" + Name + "' is not a bool (it was '" + Values [name] + "')", e); } } } } }