async Task RunAsync(string[] args) { // FIBS test user string user = "******"; string pw = "dotnetcli1"; using (fibs = new FibsSession()) { // catch Ctrl+C Console.CancelKeyPress += (sender, e) => { SendAsync("bye").GetAwaiter().GetResult(); Process(fibs.ReceiveAsync().GetAwaiter().GetResult()).GetAwaiter().GetResult(); }; // login, set the right properties and watch someone play await Process(await fibs.LoginAsync(user, pw)); await SendAsync("set boardstyle 3"); // pick the player provided on the command line or the one least idle if (args.Length != 0) { player = players.Find(p => string.Compare(p.Name, args[0], true) == 0); if (player == null || player.Opponent == null) { Log($"{args[0]} not playing"); return; } } else { player = players.Where(p => p.Opponent != null).OrderBy(p => p.Idle).First(); } Log($"{player.Name}: idle {player.Idle} seconds"); await SendAsync($"watch {player.Name}"); while (processing) { await Process(await fibs.ReceiveAsync()); } } }
async Task FibsLoop(WebSocket socket) { using (var fibs = new FibsSession()) { var cancel = CancellationToken.None; var socketSegment = new ArraySegment <byte>(new byte[4096]); // wait for login Task <WebSocketReceiveResult> socketReceiveTask = socket.ReceiveAsync(socketSegment, cancel); var socketInput = await socketReceiveTask; if (socketInput.MessageType != WebSocketMessageType.Text) { await socket.CloseAsync(WebSocketCloseStatus.InvalidMessageType, "text messages only", cancel); return; } // check for login var s = Encoding.UTF8.GetString(socketSegment.Array, 0, socketInput.Count); var re = new Regex(@"login (?<user>[^ ]+) (?<password>[^ ]+)"); var match = re.Match(s); if (!match.Success) { await socket.CloseAsync(WebSocketCloseStatus.ProtocolError, "must login first", cancel); return; } // login to FIBS // TODO: support creating new users // TODO: support other servers besides fibs.com // TODO: support message queueing while there awaiting a response, otherwise FIBS drops requests // (and remember to take the hack out of the client...) var user = match.Groups["user"].Value; var password = match.Groups["password"].Value; Task <CookieMessage[]> fibsReadTask = fibs.LoginAsync(user, password); Debug.WriteLine($"FibsLoop: logging into FIBS for {user}"); // get more socket input socketReceiveTask = socket.ReceiveAsync(socketSegment, cancel); while (socket.State == WebSocketState.Open) { var task = await Task.WhenAny(socketReceiveTask, fibsReadTask); //if (task.IsCanceled) { break; } if (task.Equals(socketReceiveTask)) { socketInput = await socketReceiveTask; if (socketInput.MessageType == WebSocketMessageType.Close) { await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", cancel); continue; } if (socketInput.MessageType != WebSocketMessageType.Text) { await socket.CloseAsync(WebSocketCloseStatus.InvalidMessageType, "text messages only", cancel); continue; } if (!socketInput.EndOfMessage) { await socket.CloseAsync(WebSocketCloseStatus.MessageTooBig, "message too big", cancel); continue; } s = Encoding.UTF8.GetString(socketSegment.Array, 0, socketInput.Count); if (string.IsNullOrWhiteSpace(s)) { await socket.CloseAsync(WebSocketCloseStatus.ProtocolError, "no empty messages", cancel); continue; } // write socket input to FIBS and wait for more socket input await fibs.SendAsync(s); socketReceiveTask = socket.ReceiveAsync(socketSegment, cancel); } else if (task.Equals(fibsReadTask)) { // read messages from FIBS var messages = (await fibsReadTask).Where(cm => !MessagesToSkip.Contains(cm.Cookie)).ToArray(); if (messages.Length > 0) { // write messages to socket var jsonBytes = Encoding.UTF8.GetBytes(messages.ToJson()); await socket.SendAsync(new ArraySegment <byte>(jsonBytes, 0, jsonBytes.Length), WebSocketMessageType.Text, true, cancel); if (messages.Any(cm => cm.Cookie == FibsCookie.FIBS_Goodbye)) { await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", cancel); break; } } // wait for more FIBS input fibsReadTask = fibs.ReceiveAsync(); } else { Debug.Assert(false, "Unknown task"); } } Debug.WriteLine($"FibsLoop: socket closed for {user}"); } }