//async method for connecting to clients public static void connect(IAsyncResult ar) { //connection finished, allow others to connect connectionFound.Set(); Console.WriteLine("\nPlayer connected"); //hold data about the incoming request WaitConnection wait; byte[] guid = new byte[16]; Guid battleGUID; //tell player which spawn to use byte[] spawn = new byte[1]; //stores items for communications BattleState state = new BattleState(); //get socket for client Socket listener = (Socket)ar.AsyncState; Socket handler = listener.EndAccept(ar); try { byte[] id = new byte[16]; handler.Receive(id, 16, 0); state.ClientID = new Guid(id); } //separate try catches so second portion not hit until clientid set catch (Exception) { try { //try to cleanly close connection handler.Shutdown(SocketShutdown.Both); handler.Close(); } catch (Exception) { } } try { //retrieve player level from the db and use to create player stats state.ClientStats = new PlayerStats(new DatabaseConnect().getPlayerLevel(state.ClientID)); Console.WriteLine(new DatabaseConnect().getPlayerLevel(state.ClientID)); ClientState addSocket; lock (ConnectedPlayers.DICTIONARY_LOCK) { if (ConnectedPlayers.playerDetails.TryGetValue(state.ClientID, out addSocket)) { addSocket.BattleSocket = handler; } else { addSocket = new ClientState(null, null, handler, null); ConnectedPlayers.playerDetails.Add(state.ClientID, addSocket); } } //read the battle guid from the client handler.Receive(guid, 16, 0); battleGUID = new Guid(guid); bool inDict; //get exclusive access to waiting lock (DICT_LOCK) { //check if battle guid is already in disctionary, and save the value to wait if it is //that is, check if this is the first client to connect to the battle inDict = waiting.TryGetValue(battleGUID, out wait); //if this is the first player, add the guid to the dictionary with a waitconnection object holding this client's socket if (!inDict) { wait = new WaitConnection(handler, state.ClientStats); wait.WriteLock = new object(); wait.StatsLock = new object(); waiting.Add(battleGUID, wait); } } //if this is the second client to connect if (inDict) { //set second socket to this client's socket wait.Socket2 = handler; wait.P2 = state.ClientStats; //set this client's opponent to the first client in wait state.OpponentSocket = wait.Socket1; state.OpponentStats = wait.P1; state.WriteLock = wait.WriteLock; state.StatsLock = wait.StatsLock; //set event indicating both clients are connected wait.setWait(); //indicate to client to spawn at second location spawn[0] = 1; } else { //add some sort of timeout? //wait until second client connects if (!wait.Wait.Wait(5000)) { //remove from waiting list, and throw exception so connection cleaned up waiting.Remove(battleGUID); throw new Exception("Timeout"); } //pass lock to both clients for writing to each others socket state.WriteLock = wait.WriteLock; state.StatsLock = wait.StatsLock; //set this client's opponent to the second client in wait state.OpponentSocket = wait.Socket2; state.OpponentStats = wait.P2; //both clients are connected, remove from waiting //maybe move this later so players can reconnect waiting.Remove(battleGUID); //indicate to client to spawn at first location; spawn[0] = 0; } //set client's socket in state object state.ClientSocket = handler; //initialize buffer state.Update = new byte[UPDATE_SIZE]; //lock write operations on the socket lock (state.WriteLock) { byte[] opponentLevel = new byte[1]; opponentLevel[0] = (byte)((state.OpponentStats.MaxHP - 100) / 21); handler.Send(opponentLevel, 1, 0); //tell the client where to spawn handler.Send(spawn, 1, 0); } //start receiving client updates handler.BeginReceive(state.Update, 0, UPDATE_SIZE, 0, new AsyncCallback(readUpdate), state); } //catch connection errors catch (Exception) { lock (ConnectedPlayers.DICTIONARY_LOCK) { ClientState removeSocket; if (ConnectedPlayers.playerDetails.TryGetValue(state.ClientID, out removeSocket)) { removeSocket.BattleSocket = null; try { //if value not found then connection should already be closed by cleanup procedure handler.Shutdown(SocketShutdown.Both); handler.Close(); } catch (Exception) { } } } Console.WriteLine("\nPlayer disconnected"); } }
//read updates from clients public static void readUpdate(IAsyncResult ar) { //retreive the state object and socket BattleState state = (BattleState)ar.AsyncState; Socket handler = state.ClientSocket; try { //bool battleEnd; //bool win; //NEED TO DEAL WITH SIZES AND STUFF, MAYBE SEND A MESSAGE WITH THE SIZE //for now everything sent as 41 bytes ar.AsyncWaitHandle.WaitOne(); if (handler.EndReceive(ar) == 0) { //ANYTHING ELSE NEED TO BE DONE ON THE SIDE OF THE PERSON WHO DCED? return; } byte flags = state.Update[0]; //determine winner and if battle ended bool battleEnd = false; //currently treat loss and draw the same, so only indicate win bool win = false; byte clientWinLoseDraw = 0; //whichever update gets here first receives authority over end game, lock the other out until complete processing hits lock (state.StatsLock) { //if battle over do not evaluate this round of damage if (!(state.OpponentStats.HP <= 0 || state.ClientStats.HP <= 0)) { if (((flags >> 5) & 1) == 1) { state.ClientStats.HP -= 75; Console.WriteLine("mine"); Console.WriteLine("My life: " + state.ClientStats.HP.ToString()); } if (((flags >> 6) & 1) == 1) { state.OpponentStats.HP -= Math.Max(state.ClientStats.Attack - state.OpponentStats.Defense, 1); Console.WriteLine("shot"); Console.WriteLine("Opponent life: " + state.OpponentStats.HP.ToString()); } if (((flags >> 7) & 1) == 1) { state.OpponentStats.HP -= 75; Console.WriteLine("mine hit opponent"); Console.WriteLine("Opponent life: " + state.OpponentStats.HP.ToString()); } if (((flags >> 3) & 1) == 1) { if (state.ClientStats.HP + 50 <= state.ClientStats.MaxHP) { state.ClientStats.HP += 50; } else { state.ClientStats.HP = state.ClientStats.MaxHP; } Console.WriteLine("health pot"); Console.WriteLine("My life: " + state.ClientStats.HP.ToString()); } } } //if client has no hp indicate battle over and match lost if (state.ClientStats.HP <= 0 && state.OpponentStats.HP <= 0) { battleEnd = true; //draws are indicated by a 3, because apparently numbers go 0, 1, 3 clientWinLoseDraw = 3; } //if client has health and opponent doesnt indicate match over and battle won else if (state.ClientStats.HP <= 0) { battleEnd = true; //client lost, clientWinLoseDraw already initialized to 0 } else if (state.OpponentStats.HP <= 0) { battleEnd = true; win = true; //client won clientWinLoseDraw = 1; } //lock write operations and pass along and acknowledge update lock (state.WriteLock) { send(state, battleEnd, clientWinLoseDraw); } //recursively read updates until the battle is over if (!battleEnd) { handler.BeginReceive(state.Update, 0, UPDATE_SIZE, 0, new AsyncCallback(readUpdate), state); } else { //handle winner stuff (DB stuff, etc) if (win) { var dbCon = new DatabaseConnect(); dbCon.updatePlayerExpAfterBattle(state.ClientID); } //clean up lock (ConnectedPlayers.DICTIONARY_LOCK) { ClientState removeSocket; if (ConnectedPlayers.playerDetails.TryGetValue(state.ClientID, out removeSocket)) { removeSocket.BattleSocket = null; //if value not found then connection should already be closed handler.Shutdown(SocketShutdown.Both); handler.Close(); } } Console.WriteLine("\nPlayer disconnected"); } } //end communications gracefully if player disconnects before battle ends catch (Exception) { lock (ConnectedPlayers.DICTIONARY_LOCK) { ClientState removeSocket; if (ConnectedPlayers.playerDetails.TryGetValue(state.ClientID, out removeSocket)) { removeSocket.BattleSocket = null; try { //if value not found then connection should already be closed handler.Shutdown(SocketShutdown.Both); handler.Close(); } catch (Exception) { } } } Console.WriteLine("\nPlayer disconnected"); } }