using System; using System.Linq; using System.Threading; using System.Collections.Generic; using System.Text.RegularExpressions; using Frontend; namespace WorldOfPeacecraft { public class Parser { 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 Queue Buffer = new Queue (); private AutoResetEvent BufferFilledEvent = new AutoResetEvent (false); private Thread ParserThread; private LinkedList Message; private Regex LastLineRegex; private Backend backend; public Parser () { ParserThread = new Thread (new ThreadStart (this.RunParser)); Message = new LinkedList (); LastLineRegex = new Regex ("^end:[0-9]+$"); } private void RunParser () { while (true) { bool waitRequired = false; lock (Buffer) { if (Buffer.Count == 0) { waitRequired = true; BufferFilledEvent.Reset (); } } if (waitRequired) { BufferFilledEvent.WaitOne (); } lock (Buffer) { Message.AddLast (Buffer.Dequeue ()); } if (IsCompletePackage ()) { Parse (); } // TODO Try-catch. IMPORTANT! } } private void Parse () { String[] aMessage = Enumerable.ToArray (Message); Block mainBlock = new Block (aMessage, 0, aMessage.Length - 1); ProcessData (mainBlock); } private void ProcessData (Block parentBlock) { if (parentBlock.GetStringValue ("ans") != null) { 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: ProcessPlayer (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); backend.removePlayer (player); break; case MessDragon: Dragon dragon = MapDragon (block); backend.removeDragon (dragon); break; } } private void ProcessMap (Block mapBlock) { CheckBlocksSize (mapBlock, 1, 1); Block cellsBlock = mapBlock.GetBlocks ().First.Value; int height = mapBlock.GetIntValue ("height"); int width = mapBlock.GetIntValue ("width"); 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)); } backend.SetMap(map); } private void ProcessMessage (Block mesBlock) { int srcid = mesBlock.GetIntValue ("srcid"); string src = mesBlock.GetStringValue ("src"); string txt = mesBlock.GetStringValue ("txt"); //Message m = new Message (srcid, src, txt); //TODO Herausfinden wie wir das oben lösen (gilt für alle Funktionenstümpfe) } private void ProcessAnswer (Block block) { } private void ProcessResult (Block procBlock) { CheckBlocksSize (procBlock, 1, 1); int round = procBlock.GetIntValue ("round"); bool running = procBlock.GetBoolValue ("running"); int delay = procBlock.GetIntValue ("delay"); //ProcessOpponent (procBlock.GetBlocks ()); //Result r = new Result(round, running, delay); } private void ProcessOpponent(Block oppBlock) { CheckBlocksSize (oppBlock, 1, 1); int id = oppBlock.GetIntValue ("id"); int points = oppBlock.GetIntValue ("points"); int total = oppBlock.GetIntValue ("total"); //TYPE decision = ProcessDecision(oppBlock.getBlocks()); } //"id:"INT,"decision:",DECISION,"points:",INT,"total:",INT," private void ProcessDecision(Block block) { //TODO -> Wafa //OK das ist hier so'n bissle der gleiche scheiß wie "Answer" wie soll das gehn, ohne Block!? //Maybe not TODO? } private void ProcessChallenge (Block challengeBlock) { //TODO -> Samed int id = challengeBlock.GetIntValue("id"); String type; switch (challengeBlock.GetStringValue()) { case "DRAGON": type = "Dragon"; break; case "STAGHUNT": type = "Staghunt"; break; case "SKIRMISCH": type = "Skirmisch"; break; } bool accepted = challengeBlock.GetBoolValue("accepted"); //Challenge c = new Challenge(id, type, accepted); //CHALLENGE: "begin:challenge","id:",INT,"type:",("DRAGON"|"STAGHUNT"|"SKIRMISH"),"accepted:",BOOLEAN,"end:challenge" } private void ProcessPlayer (Block playerBlock) { int points = playerBlock.GetIntValue ("points"); int id = playerBlock.GetIntValue ("id"); bool busy = playerBlock.GetBoolValue ("busy"); string desc = playerBlock.GetStringValue ("desc"); int x = playerBlock.GetIntValue ("x"); int y = playerBlock.GetIntValue ("y"); //Player p = new Player(points, id, busy, desc, x, y,); } private void ProcessMapcell (Block mapcellBlock) { backend.getMapObject().SetTile(MapMapcell(mapcellBlock)); } private void ProcessYourid (Block yourIdBlock) { LinkedList unnamedValues = yourIdBlock.GetUnnamedValues (); string stringValue = unnamedValues.First.Value; int intValue = int.Parse (stringValue); //YourID id = new YourID (intValue); //TODO Herausfinden wie wir das oben lösen } 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) { backend.clearDragons (); backend.clearPlayers (); foreach (Block entityBlock in entitiesBlock.GetBlocks ()) { switch (entityBlock.GetName()) { case MessPlayer: backend.SetPlayer(MapPlayer(entityBlock)); break; case MessDragon: backend.SetDragon(MapDragon(entityBlock)); break; default: ThrowUnknownBlockException(entitiesBlock, entityBlock); break; } } } private void ProcessPlayers (Block playersBlock) { backend.clearPlayers (); foreach (Block playerBlock in playersBlock.GetBlocks ()) { backend.SetPlayer(MapPlayer(playerBlock)); } } private void ProcessDragon (Block dragonBlock) { backend.SetDragon(MapDragon(dragonBlock)); } private Dragon MapDragon (Block dragonBlock) { int id = dragonBlock.GetIntValue ("id"); bool busy = dragonBlock.GetBoolValue ("busy"); string desc = dragonBlock.GetStringValue ("desc"); int x = dragonBlock.GetIntValue ("x"); int y = dragonBlock.GetIntValue ("y"); return new Dragon (id, x, y, desc, busy); } private Player MapPlayer (Block playerBlock) { int id = playerBlock.GetIntValue ("id"); int score = playerBlock.GetIntValue ("points"); bool busy = playerBlock.GetBoolValue ("busy"); string desc = playerBlock.GetStringValue ("desc"); int x = playerBlock.GetIntValue ("x"); int y = playerBlock.GetIntValue ("y"); return new Player (id, x, y, desc, busy, score); } private Tile MapMapcell (Block cellBlock) { CheckBlocksSize (cellBlock, 1, 1); int x = cellBlock.GetIntValue ("col"); int y = cellBlock.GetIntValue ("row"); 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 "WALKABLE": walkable = true; break; case "WALL": wall = true; break; case "FOREST": forest = true; break; case "WATER": water = true; break; case "HUNTABLE": 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 Stop () { ParserThread.Abort (); } public void AddToBuffer (string s) { lock (Buffer) { Buffer.Enqueue (s); BufferFilledEvent.Set (); } } private class Block { private string Name; private LinkedList Blocks = new LinkedList (); private Dictionary Values = new Dictionary (); private LinkedList UnnamedValues; 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 string GetStringValue (string name) { return Values [name]; } public int GetIntValue (string name) { return int.Parse (Values [name]); } public long GetLongValue (string name) { return long.Parse (Values [name]); } public bool GetBoolValue (string name) { return bool.Parse (Values [name]); } } } }