582 lines
17 KiB
C#
582 lines
17 KiB
C#
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 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<string> Message;
|
|
private Regex LastLineRegex;
|
|
private Backend Backend;
|
|
|
|
public Parser (Backend backend, Buffer buffer)
|
|
{
|
|
InputBuffer = buffer;
|
|
Backend = backend;
|
|
Message = new LinkedList<string> ();
|
|
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:
|
|
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<Block> 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<Block> 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> 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 <string> 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<string> 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 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 <string> unnamedValues = yourIdBlock.GetUnnamedValues ();
|
|
string stringValue = unnamedValues.First.Value;
|
|
int intValue = int.Parse (stringValue);
|
|
YourID id = new YourID (intValue);
|
|
}
|
|
|
|
private void ProcessTime (Block timeBlock)
|
|
{
|
|
LinkedList <string> unnamedValues = timeBlock.GetUnnamedValues ();
|
|
string stringValue = unnamedValues.First.Value;
|
|
long longValue = long.Parse (stringValue);
|
|
Time t = new Time(longValue);
|
|
}
|
|
|
|
private void ProcessOnline (Block onlineBlock)
|
|
{
|
|
LinkedList <string> 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<Block> 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 ();
|
|
}
|
|
|
|
private class Block
|
|
{
|
|
private string Name;
|
|
private LinkedList<Block> Blocks = new LinkedList<Block> ();
|
|
private Dictionary<String, String> Values = new Dictionary<String, String> ();
|
|
private LinkedList<String> UnnamedValues = new LinkedList<String>();
|
|
|
|
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<string> GetUnnamedValues()
|
|
{
|
|
return UnnamedValues;
|
|
}
|
|
|
|
public LinkedList<Block> 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|