/// <summary> /// This method will periodically tick and check if a client has sent data. /// It will run on its own thread and is the main operation of the server. /// </summary> private void startMonitoringForData() { const int waitTime = 10; while (true) { try { if (returns.Count > 0) enterPendingPlayers(); //pause(); lock (lockObject) { modifiedClientList = false; foreach (String s in clientList.Keys) { if (!CSCommon.isLiveConnection(clientList[s].client)) { String name = clientList[s].name; removeFromGame(s, false, DisconnectMethod.midGame); sendMessage(name + " has been unexpectedly disconnected.", null); } else performCMDRCV(s, clientList[s].client); //In case player wanted to be disconnected if (modifiedClientList) break; } //for } //lock if (!modifiedClientList) Thread.Sleep(waitTime); if (isGameEnd()) { output(LoggingLevels.debug, "Doing game ended routine"); if (!forceGameEnd) //don't give points if team death player disconnected allocatePoints(); else { if (reason == null) sendMessage("The game has ended.", null); else sendMessage("The game has ended because " + reason, null); propogate(CSCommon.buildCMDString(CSCommon.cmd_gameEnded), null); while (clientList.Count != 0) { foreach (String s in clientList.Keys) { removeFromGame(s, true, DisconnectMethod.midGame); //remove clients one by one break; //release enumerator } //foreach } //while } //if forced to end the game by server-side event. if (gameFinished != null) gameFinished(this); return; } //if game ended sendCriticalMessage(); } catch (Exception e) { output(LoggingLevels.error, e.Message + e.StackTrace); setForceGameEnd("there was a problem with the game."); } } //while }
/// <summary> /// gets data from the TCPClient passed and does a command based on the given data. This command could result in information being passed to other TCPClient objects, for instance if we've recieved information about an aircraft's state that needs to be propogated. /// </summary> /// <param name="tag">The GUID of the player to perform commands on</param> /// <param name="client">The player's TcpClient object</param> private void performCMDRCV(String tag, TcpClient client) { if (!CSCommon.isLiveConnection(client)) return; MemoryStream stream = CSCommon.getData(client); if (stream == null) return; BinaryReader rcvData = new BinaryReader(stream); //rcvData is the list of serverCommands. sbyte c = 0; long start = 0L; byte command = 0; try { while (rcvData.BaseStream.Length > rcvData.BaseStream.Position) { start = rcvData.BaseStream.Position; c = rcvData.ReadSByte(); if (c > 4) return; if (c == 1) { command = rcvData.ReadByte(); switch (command) { case CSCommon.cmd_test: int testAmount = recordWin("6SRKJ695G", "ABCDEFGHI"); CSCommon.sendData(client, CSCommon.buildCMDString(CSCommon.cmd_newval, testAmount)); break; case CSCommon.cmd_createBot: //The player who creates the bot will spawn a thread to control them. createBot(tag, ObjectType.aircraft); break; case CSCommon.cmd_removeBot: String rBotId = removeBot(0); if (rBotId == null) break; sendMessage(rBotId + " has been dropped from the server", null); propogate(CSCommon.buildCMDString(CSCommon.cmd_forceDisconnect, rBotId), null); break; case CSCommon.cmd_whois: using (BinaryWriter whoWriter = new BinaryWriter(new MemoryStream())) { whoWriter.Write((short)clientList.Count); foreach (Player p in clientList.Values) { whoWriter.Write(p.tag); whoWriter.Write(p.name + ((p.admin) ? " (GM)" : "")); } CSCommon.sendResponse(client, whoWriter); } //using break; case CSCommon.cmd_requestStartGame: if (type == GameType.oneOnOne && clientList.Count == 2) gameStarted = true; if (type == GameType.teamDeath) { if (getNumberOfTeams() >= 2) gameStarted = true; } //if team death CSCommon.sendResponse(client, gameStarted); if (gameStarted) { propogate(CSCommon.buildCMDString(CSCommon.cmd_startGame), client); } break; case CSCommon.cmd_updatePoints: Player winner = getPlayerByID(rcvData.ReadString()); if (winner == null) //winner logged off; kill doesn't count! break; //The client sending this command is the loser. int amount = recordWin(winner.tag, tag); CSCommon.sendData(winner.client, CSCommon.buildCMDString(CSCommon.cmd_newval, amount)); break; case CSCommon.cmd_startGame: //Send cmd_startGame to all clients so they can //signal start locally. propogate(CSCommon.buildCMDString(CSCommon.cmd_startGame), client); break; case CSCommon.cmd_chat: bool isPrivate = rcvData.ReadBoolean(); String chatMsg = null; if (isPrivate) { String recipient = rcvData.ReadString(); chatMsg = clientList[tag].name + " (private): " + rcvData.ReadString(); sendPrivateChatMessage(recipient, chatMsg); } else { chatMsg = rcvData.ReadString(); sendChatMessage(tag, chatMsg, MessageType.normal); } break; case CSCommon.cmd_serverMessage: sendMessage(rcvData.ReadString(), client); break; case CSCommon.cmd_disconnectMe: String name = clientList[tag].name; removeFromGame(tag, true, DisconnectMethod.midGame); sendMessage(name + " has left the game.", null); return; //no need to process things further. case CSCommon.cmd_deleteFromGame: //silent exit removeFromGame(tag, true, DisconnectMethod.gameEnded); //delete will only be sent on successful game end return; //no need to process data further. } //switch } //if explicit command else { byte[] buffer = new byte[rcvData.ReadInt32()]; string id = rcvData.ReadString(); bool updateBot = false; int botIndex = 0; if (c == 3) { if ((botIndex = getBot(id)) > -1) { rcvData.ReadInt16(); //passed numArgs while (rcvData.ReadSByte() != 1) ; if (rcvData.ReadInt32() > 0) updateBot = true; else removeBot(botIndex); } //if bot exists } //if this is a bot update rcvData.BaseStream.Position = start; rcvData.BaseStream.Read(buffer, 0, buffer.Length); if (updateBot) bots[botIndex].data = buffer; propogate(buffer, client); } //if something else besides cmd_command. } //foreach serverCommand } catch (Exception e) { output("Error while reading data from " + tag + ". Char = " + c + Environment.NewLine + "Last command: " + command + Environment.NewLine + "Stack trace: " + e.Message + e.StackTrace); } //catch }