public static void ReadCallback(IAsyncResult ar) { String content = String.Empty; // Retrieve the state object and the handler socket // from the asynchronous state object. StateObject state = (StateObject)ar.AsyncState; Socket handler = state.workSocket; List <Player> toDelete = new List <Player>(); //important so we don't have a "collection modified" error Player thisPlayer = players.Where(x => x.socket == handler).SingleOrDefault(); bool isGameServer = gameServers.Contains(handler); string PhpResponse; int bytesRead; if (handler.Connected) { // Read data from the client socket. try { bytesRead = handler.EndReceive(ar); if (bytesRead > 0) { //Transform the array of bytes received from the user into an //intelligent form of object Data Data msgReceived = new Data(state.buffer); byte[] message; Player targetPlayer; Data msgToSend = new Data(); msgToSend.cmdCommand = msgReceived.cmdCommand; if (thisPlayer != null) { msgToSend.strName = thisPlayer.name; } msgToSend.strMessage = msgReceived.strMessage; switch (msgReceived.cmdCommand) { case Command.Login: Console.WriteLine("logged in: " + msgReceived.strName); if (msgReceived.strName == "i am a game server") //you can change this to some password or check the server ip here { gameServers.Add(handler); Console.WriteLine("added a game server"); SendGroupUpdate(); //TD: send this update only to the new server } else { Player newPlayer = new Player(handler); newPlayer.name = msgReceived.strName; if (CharacterIds.ContainsKey(newPlayer.name)) { newPlayer.id = CharacterIds[newPlayer.name]; } if (CharacterClans.ContainsKey(newPlayer.id)) { newPlayer.clan = CharacterClans[newPlayer.id]; } if (clanLeaders.Contains(newPlayer.id)) { newPlayer.isClanLeader = true; } players.RemoveAll(x => x.name == newPlayer.name); players.Add(newPlayer); foreach (var group in groups) { for (int i = 0; i < group.players.Count; i++) { if (group.players[i].name == newPlayer.name) { group.players[i] = newPlayer; newPlayer.group = group; } } } if (pendingKicks.ContainsKey(newPlayer.name)) //cancel automatic kick { pendingKicks[newPlayer.name].Stop(); pendingKicks.Remove(newPlayer.name); } Console.WriteLine("added a player"); } break; case Command.Message: if (thisPlayer == null) { break; } Console.WriteLine(thisPlayer.name + ": " + msgReceived.strMessage); message = msgToSend.ToBytes(); foreach (Player player in players) { try { player.socket.Send(message); } catch (SocketException socketException) { //WSAECONNRESET, the other side closed impolitely if (socketException.ErrorCode == 10054 || ((socketException.ErrorCode != 10004) && (socketException.ErrorCode != 10053))) { Console.WriteLine("receiver disconnected"); } else { Console.WriteLine(socketException.Message); } toDelete.Add(player); } catch (Exception e) { Console.WriteLine(e.Message + "\n" + e.StackTrace); toDelete.Add(player); } } break; case Command.PrivateMessage: if (thisPlayer == null) { break; } Console.WriteLine(thisPlayer.name + " whispers to " + msgReceived.strName + ": " + msgReceived.strMessage); targetPlayer = players.Where(x => String.Compare(x.name, msgReceived.strName, true) == 0).SingleOrDefault(); //case insensitive if (targetPlayer != null) { message = msgToSend.ToBytes(); targetPlayer.socket.Send(message); //send the message to the sender so it appears in the log: thisPlayer.socket.Send(message); } break; case Command.GroupMessage: if (thisPlayer == null || thisPlayer.group == null) { break; } Console.WriteLine("[GROUP] " + thisPlayer.name + ": " + msgReceived.strMessage); message = msgToSend.ToBytes(); foreach (Player groupMember in thisPlayer.group.players) { groupMember.socket.Send(message); } break; case Command.ClanMessage: if (thisPlayer == null || thisPlayer.clan == 0) { break; } Console.WriteLine("[CLAN] " + thisPlayer.name + ": " + msgReceived.strMessage); message = msgToSend.ToBytes(); foreach (Player clanMember in players.Where(x => x.clan == thisPlayer.clan)) { clanMember.socket.Send(message); } break; case Command.GroupInvite: if (thisPlayer == null) { break; } if (thisPlayer.group != null && thisPlayer.group.leader != thisPlayer) { break; //if the inviting player is not the group's leader } if (thisPlayer.group != null && thisPlayer.group.players.Count >= maxGroupSize) { break; //check max group size } //find the player that we want to invite targetPlayer = players.Where(x => x.name == msgReceived.strName).SingleOrDefault(); if (targetPlayer == null || targetPlayer == thisPlayer) { break; } if (targetPlayer.group == null) //if the player is not already in a group { if (targetPlayer.pendingInvite != null) { break; //check if there's a pending invite } targetPlayer.pendingInvite = thisPlayer; targetPlayer.hasPendingClanInvite = false; System.Timers.Timer myTimer = new System.Timers.Timer(); myTimer.Elapsed += (sender, args) => ClearPendingInvite(sender, targetPlayer); myTimer.Interval = 20000; // 1000 ms is one second myTimer.Start(); clearPendingInvites.Add(targetPlayer, myTimer); message = msgToSend.ToBytes(); targetPlayer.socket.Send(message); } break; case Command.AcceptInvite: if (thisPlayer == null) { break; } Player invitingPlayer = thisPlayer.pendingInvite; if (invitingPlayer == null || invitingPlayer.clan == 0) { break; } if (thisPlayer.hasPendingClanInvite) //clan { ClearInviteBeforeTick(thisPlayer); if (thisPlayer.clan != 0) { break; } PhpResponse = SendPhpRequest("mmo_clan_add_character", "{\"character_name\":\"" + thisPlayer.name + "\"," + "\"clan_id\":\"" + invitingPlayer.clan + "\"}" ); if (PhpResponse.Contains("OK")) { UpdateClans(); } } else //group { ClearInviteBeforeTick(thisPlayer); if (thisPlayer.group != null) { break; } bool joinedGroup = false; if (invitingPlayer.group == null) //create a new group { Group newGroup = new Group(invitingPlayer, thisPlayer); joinedGroup = true; groups.Add(newGroup); } else if (invitingPlayer.group.players.Count < maxGroupSize) //add the new player to the existing group { invitingPlayer.group.players.Add(thisPlayer); thisPlayer.group = invitingPlayer.group; joinedGroup = true; } if (joinedGroup) { SendGroupUpdate(); } } break; case Command.ClanCreate: if (thisPlayer == null) { break; } PhpResponse = SendPhpRequest("mmo_clan_create", "{\"character_name\":\"" + thisPlayer.name + "\"," + "\"clan_name\":\"" + msgReceived.strName + "\"}" ); if (PhpResponse.Contains("OK")) { UpdateClans(); } break; case Command.ClanInvite: if (thisPlayer == null || thisPlayer.clan == 0 || !thisPlayer.isClanLeader) { break; //check that the player is the clan leader } //find the player that we want to invite targetPlayer = players.Where(x => x.name == msgReceived.strName).SingleOrDefault(); if (targetPlayer == null || targetPlayer == thisPlayer) { break; } if (targetPlayer.clan == 0) //if the player is not already in a clan { if (targetPlayer.pendingInvite != null) { break; //check if there's a pending invite } targetPlayer.pendingInvite = thisPlayer; targetPlayer.hasPendingClanInvite = true; System.Timers.Timer myTimer = new System.Timers.Timer(); myTimer.Elapsed += (sender, args) => ClearPendingInvite(sender, targetPlayer); myTimer.Interval = 20000; // 1000 ms is one second myTimer.Start(); clearPendingInvites.Add(targetPlayer, myTimer); msgToSend.strMessage = ClanNames[thisPlayer.clan]; Console.WriteLine("clan: " + msgToSend.strMessage); message = msgToSend.ToBytes(); targetPlayer.socket.Send(message); //forward the ClanInvite message to the target player } break; case Command.ClanDisband: if (thisPlayer == null || thisPlayer.clan == 0 || !thisPlayer.isClanLeader) { break; } PhpResponse = SendPhpRequest("mmo_clan_disband", "{\"character_id\":\"" + thisPlayer.id + "\"}" ); if (PhpResponse.Contains("OK")) { UpdateClans(); } break; case Command.ClanLeave: if (thisPlayer == null || thisPlayer.clan == 0 || thisPlayer.isClanLeader) { break; //for now, the leader can't leave the clan } PhpResponse = SendPhpRequest("mmo_clan_remove_character", "{\"character_id\":\"" + thisPlayer.id + "\"}" ); if (PhpResponse.Contains("OK")) { UpdateClans(); } break; case Command.ClanKick: if (thisPlayer == null || thisPlayer.clan == 0 || !thisPlayer.isClanLeader) { break; } //find the player that we want to invite targetPlayer = players.Where(x => x.name == msgReceived.strName).SingleOrDefault(); if (thisPlayer.clan != targetPlayer.clan) { break; } PhpResponse = SendPhpRequest("mmo_clan_remove_character", "{\"character_id\":\"" + targetPlayer.id + "\"}" ); if (PhpResponse.Contains("OK")) { UpdateClans(); } break; case Command.DeclineInvite: if (thisPlayer == null) { break; } Player invitPlayer = thisPlayer.pendingInvite; message = msgToSend.ToBytes(); invitPlayer.socket.Send(message); ClearInviteBeforeTick(thisPlayer); break; case Command.GroupLeave: if (thisPlayer == null) { break; } RemoveFromGroup(thisPlayer); break; case Command.GroupKick: if (!isGameServer && (thisPlayer == null || thisPlayer.group == null || thisPlayer.group.leader != thisPlayer)) { break; } //find the player that we want to kick targetPlayer = players.Where(x => x.name == msgReceived.strName).SingleOrDefault(); if (targetPlayer == null || (!isGameServer && (targetPlayer.group != thisPlayer.group))) { break; } Console.WriteLine("received kick command: " + targetPlayer.name); if (isGameServer) //kick issued by server (because of disconnect), kick after a delay of X seconds { if (!pendingKicks.ContainsKey(targetPlayer.name)) { System.Timers.Timer myTimer = new System.Timers.Timer(); myTimer.Elapsed += (sender, args) => KickAfterDelay(sender, targetPlayer); myTimer.Interval = 30000; // 1000 ms is one second myTimer.Start(); pendingKicks.Add(targetPlayer.name, myTimer); } } else { RemoveFromGroup(targetPlayer); //kick issued by group leader, kick immediately //notify the player that they were kicked from the group: message = msgToSend.ToBytes(); targetPlayer.socket.Send(message); } break; } // listen again: state.sb.Clear(); handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); } } catch (SocketException socketException) { //WSAECONNRESET, the other side closed impolitely if (socketException.ErrorCode == 10054 || ((socketException.ErrorCode != 10004) && (socketException.ErrorCode != 10053))) { Console.WriteLine("remote client disconnected"); } else { Console.WriteLine(socketException.Message); } toDelete.Add(thisPlayer); handler = null; } catch (Exception e) { Console.WriteLine(e.Message + "\n" + e.StackTrace); toDelete.Add(thisPlayer); } } foreach (var playerToDelete in toDelete) { if (playerToDelete != null) { RemoveFromGroup(playerToDelete); } players.RemoveAll(x => x == playerToDelete); } }