private IgsCode ExtractCodeFromLine(string line) { if (line != "") { string[] split = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); string firstWord = split[0]; int firstWordAsInteger; if (int.TryParse(firstWord, out firstWordAsInteger)) { IgsCode code = (IgsCode)firstWordAsInteger; return(code); } } return(IgsCode.Unknown); }
private async Task HandleIncomingData(StreamReader sr) { bool thisIsNotAMove = false; bool weAreHandlingAnInterrupt = false; bool interruptIsImpossible = false; List <IgsLine> currentLineBatch = new List <IgsLine>(); while (true) { string line; try { line = await sr.ReadLineAsync(); } catch (Exception) { line = null; } if (line == null) { ConnectionLost(); return; } line = line.Trim(); IgsCode code = ExtractCodeFromLine(line); IgsLine igsLine = new IgsLine(code, line); Events.OnIncomingLine((weAreHandlingAnInterrupt ? "(INTERRUPT) " : "") + (interruptIsImpossible ? "(INTERRUPT IMPOSSIBLE) " : "") + line); // IGS occasionally sends blank lines, I don't know why. They serve no reason. if (line == "") { continue; } switch (this.Composure) { case IgsComposure.Confused: case IgsComposure.Ok: case IgsComposure.Disconnected: // No special mode. break; case IgsComposure.InitialHandshake: if (igsLine.EntireLine.Trim() == "1 5") { this.Composure = IgsComposure.Ok; continue; } else { // Ignore. continue; } case IgsComposure.LoggingIn: if (igsLine.EntireLine.Contains("Invalid password.")) { this.Composure = IgsComposure.Confused; this._loginError = "The password is incorrect."; continue; } if (igsLine.EntireLine.Contains("Sorry, names can be")) { this.Composure = IgsComposure.Confused; this._loginError = "Your name is too long."; continue; } if (igsLine.EntireLine.Contains("This is a guest account.")) { this.Composure = IgsComposure.Confused; this._loginError = "The username does not exist."; continue; } if (igsLine.EntireLine.Contains("1 5")) { this.Composure = IgsComposure.Ok; continue; } break; } if (igsLine.Code == IgsCode.Error) { Events.OnErrorMessageReceived(igsLine.PureLine); } currentLineBatch.Add(igsLine); if (weAreHandlingAnInterrupt && code == IgsCode.Prompt) { // Interrupt message is over, let's wait for a new message weAreHandlingAnInterrupt = false; HandleFullInterrupt(currentLineBatch); thisIsNotAMove = false; interruptIsImpossible = false; currentLineBatch = new List <IgsLine>(); continue; } if (code == IgsCode.Prompt) { thisIsNotAMove = false; currentLineBatch = new List <IgsLine>(); interruptIsImpossible = false; if (this._ignoreNextPrompt) { this._ignoreNextPrompt = false; continue; } } if (code == IgsCode.Kibitz) { weAreHandlingAnInterrupt = true; continue; } if (code == IgsCode.Beep) { Events.OnBeep(); continue; } if (!interruptIsImpossible) { if (code == IgsCode.Tell) { if (igsLine.PureLine.StartsWith("*SYSTEM*")) { weAreHandlingAnInterrupt = true; continue; } HandleIncomingChatMessage(line); weAreHandlingAnInterrupt = true; continue; } if (code == IgsCode.SayInformation) { weAreHandlingAnInterrupt = true; continue; } if (code == IgsCode.Status) { weAreHandlingAnInterrupt = true; continue; } if (code == IgsCode.Shout) { HandleIncomingShoutMessage(line); weAreHandlingAnInterrupt = true; continue; } if (code == IgsCode.StoneRemoval) { Tuple <int, Position> removedStone = IgsRegex.ParseStoneRemoval(igsLine); OnIncomingStoneRemoval(removedStone.Item1, removedStone.Item2); continue; } if (code == IgsCode.Move) { var heading = IgsRegex.ParseGameHeading(igsLine); if (heading != null) { this.Data.LastReceivedGameHeading = heading; } if (!thisIsNotAMove) { HandleIncomingMove(igsLine); weAreHandlingAnInterrupt = true; } continue; } if (code == IgsCode.Undo) { thisIsNotAMove = true; weAreHandlingAnInterrupt = true; continue; } } if (code == IgsCode.Info) { // 9 Adding game to observation list. if (igsLine.EntireLine.Contains("9 Adding game to observation list.")) { interruptIsImpossible = true; } if (!interruptIsImpossible) { if (igsLine.PureLine == "yes") { // This is "ayt" response, ignore it. weAreHandlingAnInterrupt = true; continue; } if (igsLine.EntireLine == "9 You can check your score with the score command, type 'done' when finished.") { weAreHandlingAnInterrupt = true; continue; } if (igsLine.PureLine.Contains("accepted.")) { weAreHandlingAnInterrupt = true; continue; } if (igsLine.PureLine.Contains("Removing @")) { weAreHandlingAnInterrupt = true; continue; } if (igsLine.PureLine.Contains("has run out of time")) { weAreHandlingAnInterrupt = true; string whoRanOutOfTime = IgsRegex.WhoRanOutOfTime(igsLine); foreach (var game in GetGamesIncluding(whoRanOutOfTime).ToList()) { game.Controller.IgsConnector.EndTheGame( GameEndInformation.CreateTimeout( game.Controller.Players.First(pl => pl.Info.Name == whoRanOutOfTime), game.Controller.Players) ); } continue; } if (igsLine.PureLine.Contains("White resigns.}")) { int gameInWhichSomebodyResigned = IgsRegex.WhatObservedGameWasResigned(igsLine); ResignObservedGame(gameInWhichSomebodyResigned, StoneColor.White); weAreHandlingAnInterrupt = true; continue; } if (igsLine.PureLine.Contains("Black resigns.}")) { int gameInWhichSomebodyResigned = IgsRegex.WhatObservedGameWasResigned(igsLine); ResignObservedGame(gameInWhichSomebodyResigned, StoneColor.Black); weAreHandlingAnInterrupt = true; continue; } if (igsLine.PureLine.Contains("has resigned the game")) { string whoResigned = IgsRegex.WhoResignedTheGame(igsLine); if (whoResigned != this._username) { // .ToList() is used because the collection may be modified foreach (var game in GetGamesIncluding(whoResigned).ToList()) { HandleIncomingResignation(game.Info, whoResigned); } } weAreHandlingAnInterrupt = true; continue; } if (igsLine.PureLine.Contains("has typed done.")) { string username = IgsRegex.GetFirstWord(igsLine); weAreHandlingAnInterrupt = true; foreach (var game in GetGamesIncluding(username)) { var player = game.Controller.Players.First(pl => pl.Info.Name == username); game.Controller.IgsConnector.RaiseServerSaidDone(player); } continue; } if (igsLine.PureLine.Contains("Board is restored to what it was when you started scoring")) { foreach ( var game in this.GamesYouHaveOpened.Where( gi => gi.Controller.Phase.Type == GamePhaseType.LifeDeathDetermination)) { GetConnector(game.Info).ForceLifeDeathUndoDeathMarks(); } weAreHandlingAnInterrupt = true; continue; } if (igsLine.PureLine.Contains("Removed game file")) { weAreHandlingAnInterrupt = true; continue; } if (igsLine.PureLine.Contains("game completed.")) { weAreHandlingAnInterrupt = true; continue; } if (igsLine.PureLine.StartsWith("!!*Pandanet*!!:")) { // Advertisement weAreHandlingAnInterrupt = true; continue; } if (IgsRegex.IsIrrelevantInterruptLine(igsLine)) { weAreHandlingAnInterrupt = true; continue; } if (igsLine.PureLine.StartsWith("Increase ")) { weAreHandlingAnInterrupt = true; string person = IgsRegex.ParseIncreaseXTimeByYMinute(igsLine); foreach (var game in this.GamesYouHaveOpened) { if (game.Info.Black.Name == person || game.Info.White.Name == person) { MakeUnattendedRequest("refresh " + game.Info.IgsIndex); } } } if (igsLine.PureLine.EndsWith("declines undo.")) { string username = IgsRegex.WhoDeclinesUndo(igsLine); foreach (var game in GetGamesIncluding(username)) { Events.OnUndoDeclined(game.Info); } weAreHandlingAnInterrupt = true; continue; } if (igsLine.PureLine.EndsWith("declines your request for a match.")) { Events.OnMatchRequestDeclined(igsLine.PureLine.Substring(0, igsLine.PureLine.IndexOf(' '))); weAreHandlingAnInterrupt = true; continue; } IgsMatchRequest matchRequest = IgsRegex.ParseMatchRequest(igsLine); if (matchRequest != null) { this._incomingMatchRequests.Add(matchRequest); Events.OnIncomingMatchRequest(matchRequest); weAreHandlingAnInterrupt = true; continue; } } } if (!weAreHandlingAnInterrupt) { // We cannot handle this generally - let's hand it off to whoever made the request for this information. lock (this._mutex) { if (this._requestInProgress != null) { this._requestInProgress.IncomingLines.Post(igsLine); } else { if (this.Composure == IgsComposure.Ok) { Events.OnUnhandledLine(igsLine.EntireLine); } } } } } }
public IgsLine(IgsCode code, string line) { Code = code; EntireLine = line; }