public InitializationResponse ConnectToServer()
        {
            client.Connect(server_endpoint);

            this.private_endpoint = ((IPEndPoint)client.LocalEndPoint).Convert();

            System.Console.WriteLine($"{client.LocalEndPoint}");
            System.Console.WriteLine($"Local endpoint: {private_endpoint.GetAddress()}:{private_endpoint.Port}");

            this.state  = Tcp_State.Initialization;
            this.stream = new NetworkStream(client);

            InitializationRequest info = new InitializationRequest
            {
                ClientId        = id,
                PrivateEndpoint = private_endpoint
            };

            info.WriteDelimitedTo(stream);

            var response = InitializationResponse.Parser.ParseDelimitedFrom(stream);

            this.state = Tcp_State.WithoutLobby;

            return(response);
        }
        public PeerWithinLobbyResponse ReceiveWithinLobbyPeerRequest()
        {
            // same spiel as above goes for here as well
            if (!TryGetMessageOrStateChange(out PeerWithinLobbyRequest request))
            {
                return(null);
            }

            switch (request.MessageCase)
            {
            case PeerWithinLobbyRequest.MessageOneofCase.LeaveLobbyRequest:
            {
                LeaveLobby();
                state = Tcp_State.WithoutLobby;
                var response = new LeaveLobbyResponse();
                response.Success = true;

                return(new PeerWithinLobbyResponse
                    {
                        LeaveLobbyResponse = response
                    });
            }

            default:
                Log($"Unexpected within lobby request message.");
                return(null);
            }
        }
        public bool TryJoinLobby(int id, string password)
        {
            var request = new WithoutLobbyRequest();
            var message = new JoinLobbyRequest();

            message.Password         = password;
            message.LobbyId          = id;
            request.JoinLobbyRequest = message;

            System.Console.WriteLine($"Joining lobby {id}");

            request.WriteDelimitedTo(stream);

            var response = JoinLobbyResponse.Parser.ParseDelimitedFrom(stream);

            if (response.LobbyInfo != null)
            {
                System.Console.WriteLine($"Successfully joined lobby: {response.LobbyInfo}");
                this.state        = Tcp_State.PeerWithinLobby;
                this.joined_lobby = response.LobbyInfo;
                return(true);
            }

            this.state = Tcp_State.WithoutLobby;
            return(false);
        }
 public Client(IPEndPoint server_endpoint)
 {
     this.server_endpoint = server_endpoint;
     this.client          = CreateSocket();
     this.id    = new Random().Next();
     this.state = Tcp_State.Connecting;
 }
        public bool TryCreateLobby(string password)
        {
            var request = new WithoutLobbyRequest();
            var message = new CreateLobbyRequest();

            message.Password           = password;
            message.Capacity           = 2;
            request.CreateLobbyRequest = message;

            System.Console.WriteLine($"Creating lobby {id}");

            request.WriteDelimitedTo(stream);

            var response = CreateLobbyResponse.Parser.ParseDelimitedFrom(stream);

            if (response.LobbyId != 0)
            {
                System.Console.WriteLine($"Successfully created lobby {response.LobbyId}");
                this.state        = Tcp_State.HostWithinLobby;
                this.joined_lobby = new LobbyInfo
                {
                    HostId   = id,
                    LobbyId  = response.LobbyId,
                    Capacity = message.Capacity
                };
                return(true);
            }

            this.state = Tcp_State.WithoutLobby;
            return(false);
        }
        public void Start()
        {
            try
            {
                Initialize();
                server.sessions.Add(id, this);
                state = Tcp_State.WithoutLobby;
                while (state != Tcp_State.Closed && client.Connected)
                {
                    switch (state)
                    {
                    case Tcp_State.WithoutLobby:
                        ReceiveMessageAndRespond(ReceiveWithoutLobbyRequest);
                        break;

                    case Tcp_State.PeerWithinLobby:
                        ReceiveMessageAndRespond(ReceiveWithinLobbyPeerRequest);
                        break;

                    case Tcp_State.HostWithinLobby:
                        ReceiveMessageAndRespond(ReceiveWithinLobbyHostRequest);
                        break;

                    case Tcp_State.Closing:
                        // for now, just end the session
                        Log("Closing session in 10 seconds, since lobby has been locked.");
                        Thread.Sleep(10 * 1000);
                        state = Tcp_State.Closed;
                        break;
                    }
                }
                Log($"Ending session.");
            }
            catch (System.Exception e)
            {
                Log($"Session prematurely ended due to the exception: {e}");
            }

            server.sessions.Remove(id);
            if (joined_lobby != null)
            {
                LeaveLobby();
                foreach (var lobby in server.lobbies.Values)
                {
                    server.Log(lobby.GetInfo().ToString());
                }
            }
            if (client.Connected)
            {
                client.Close();
            }
        }
        public void ListenHost()
        {
            var response = ReceiveMessage <HostWithinLobbyResponse>();

            switch (response.MessageCase)
            {
            case HostWithinLobbyResponse.MessageOneofCase.PeerJoinedNotification:
                System.Console.WriteLine("Peer joined");
                AddPeerToLobby(response.PeerJoinedNotification);
                break;

            case MakeHostResponse:
                System.Console.WriteLine("Made smb host");
                break;

            case HostWithinLobbyResponse.MessageOneofCase.LeaveLobbyResponse:
                System.Console.WriteLine("Leave lobby response");
                break;

            case GoResponse:
                if (response.GoResponse.PeerAddressInfo.Count > 0)
                {
                    var tasks = new Task <Socket> [response.GoResponse.PeerAddressInfo.Count];
                    for (int i = 0; i < tasks.Length; i++)
                    {
                        var info = response.GoResponse.PeerAddressInfo[i];
                        tasks[i] = Task.Run(() => EstablishOutboundTcp(info));
                        System.Console.WriteLine(info.ToPrettyString());
                    }
                    Task.WaitAll(tasks);

                    for (int i = 0; i < tasks.Length; i++)
                    {
                        System.Console.WriteLine($"{i}: Connection established? {tasks[i].Result != null}");
                    }
                }
                ConnectedToPeerEvent?.Invoke();
                state = Tcp_State.Closing;
                break;

            default:
                System.Console.WriteLine("Unexpected response/notification");
                break;
            }
        }
        public void StartReceiving()
        {
            while (state != Tcp_State.Closed && client.Connected)
            {
                switch (state)
                {
                case Tcp_State.PeerWithinLobby:
                    ListenPeer();
                    break;

                case Tcp_State.HostWithinLobby:
                    ListenHost();
                    break;

                case Tcp_State.Closing:
                    System.Console.WriteLine("Closing");
                    state = Tcp_State.Closed;
                    break;
                }
            }
        }
        public void ListenPeer()
        {
            var response = ReceiveMessage <PeerWithinLobbyResponse>();

            switch (response.MessageCase)
            {
            case PeerWithinLobbyResponse.MessageOneofCase.PeerJoinedNotification:
                AddPeerToLobby(response.PeerJoinedNotification);
                System.Console.WriteLine("Peer joined");
                break;

            case LeaveLobbyNotification:
                System.Console.WriteLine("Leave lobby notification");
                break;

            case BecomeHostNotification:
                System.Console.WriteLine("Promoted to host");
                break;

            case PeerWithinLobbyResponse.MessageOneofCase.LeaveLobbyResponse:
                System.Console.WriteLine("Leave lobby response");
                break;

            case HostAddressInfo:
            {
                var info = response.HostAddressInfo;
                System.Console.WriteLine(info.ToPrettyString());
                var task = Task.Run(() => EstablishOutboundTcp(response.HostAddressInfo));
                Task.WaitAll(task);
                System.Console.WriteLine($"Connection to host established? {task.Result != null}");
                state = Tcp_State.Closing;
                ConnectedToPeerEvent?.Invoke();
                break;
            }

            default:
                System.Console.WriteLine("Unexpected response/notification");
                break;
            }
        }
        public HostWithinLobbyResponse ReceiveWithinLobbyHostRequest()
        {
            // same spiel as above goes for here as well
            if (!TryGetMessageOrStateChange(out HostWithinLobbyRequest request))
            {
                return(null);
            }

            var outerResponse = new HostWithinLobbyResponse();

            switch (request.MessageCase)
            {
            case HostWithinLobbyRequest.MessageOneofCase.LeaveLobbyRequest:
            {
                LeaveLobby();

                var response = new LeaveLobbyResponse();
                response.Success = true;
                state            = Tcp_State.WithoutLobby;

                return(new HostWithinLobbyResponse
                    {
                        LeaveLobbyResponse = response
                    });
            }

            case MakeHostRequest:
            {
                var response = new MakeHostResponse();
                int peer_id  = request.MakeHostRequest.PeerId;

                if (id != peer_id &&      // since we are host
                    joined_lobby.peers.ContainsKey(peer_id))
                {
                    response.NewHostId = peer_id;
                    MakeHost(joined_lobby.peers[peer_id]);
                }

                return(new HostWithinLobbyResponse
                    {
                        MakeHostResponse = response
                    });
            }

            case GoRequest:
            {
                var host_response        = new GoResponse();
                var host_address_message = CreateAddressMessage();

                var peer_notification = new PeerWithinLobbyResponse
                {
                    HostAddressInfo = host_address_message
                };

                // TODO: run concurrently (although sending is pretty fast, I suppose)
                foreach (var peer_id in joined_lobby.GetNonHostPeerIds())
                {
                    var peer = joined_lobby.peers[peer_id];
                    try
                    {
                        peer.joined_lobby = null;
                        peer.TransitionState(Tcp_State.Closing);
                        peer_notification.WriteDelimitedTo(peer.stream);
                        host_response.PeerAddressInfo.Add(peer.CreateAddressMessage());
                    }
                    catch
                    {
                        peer.Log($"An error has been catched while trying to send PeerAddressInfo");
                    }
                }
                server.lobbies.Remove(joined_lobby.id);
                joined_lobby = null;
                state        = Tcp_State.Closing;
                return(new HostWithinLobbyResponse
                    {
                        GoResponse = host_response
                    });
            }

            default:
                Log($"Unexpected within lobby request message.");
                return(null);
            }
        }
 public Tcp_Session(Socket client, Server server)
 {
     this.client = client;
     this.state  = Tcp_State.Connecting;
     this.server = server;
 }
        public IMessage ReceiveWithoutLobbyRequest()
        {
            // so, this happens in 2 cases:
            // 1. either a state has been changed, which would rerun the loop condition and
            //    it would exit into the main loop;
            // 2. or it were not, in which case the reading of stream failed.
            //    Either invalid data has been received or the client has diconnected.
            //    This is also checked in the while conditon.
            // UDPATE: the client.Connected property does not reflect the fact whether the
            //         client is connected. However, the stream does get closed when the connection dies down.
            if (!TryGetMessageOrStateChange(out WithoutLobbyRequest request))
            {
                return(null);
            }

            switch (request.MessageCase)
            {
            // TODO: do stuff with the password
            case CreateLobbyRequest:
            {
                Log("Creating a new lobby...");
                CreateLobbyResponse response = new CreateLobbyResponse();
                if (server.TryCreateLobby(id, out Lobby lobby) && lobby.TryJoin(this))
                {
                    Log($"Creating a new lobby {lobby.id}");
                    joined_lobby     = lobby;
                    response.LobbyId = lobby.id;
                    state            = Tcp_State.HostWithinLobby;
                }
                return(response);
            }

            case JoinLobbyRequest:
            {
                Log($"Joining lobby {request.JoinLobbyRequest.LobbyId}...");
                JoinLobbyResponse response = new JoinLobbyResponse();
                if (server.lobbies.TryGetValue(request.JoinLobbyRequest.LobbyId, out Lobby lobby) &&
                    lobby.TryJoin(this))
                {
                    Log($"Joined lobby {request.JoinLobbyRequest.LobbyId}.");
                    joined_lobby       = lobby;
                    response.LobbyInfo = lobby.GetInfo();
                    state = Tcp_State.PeerWithinLobby;

                    // notify other peers
                    var peer_joined_notification = new PeerJoinedNotification();
                    peer_joined_notification.PeerId = id;

                    foreach (var peer_id in lobby.peers.Keys)
                    {
                        if (peer_id != id)
                        {
                            lobby.peers[peer_id].SendPeerJoinedNotification(peer_joined_notification);
                        }
                    }
                }
                return(response);
            }

            case MyAddressInfoRequest:
            {
                Log($"Sending address info message.");
                return(CreateAddressMessage());
            }

            default:
                Log($"Unexpected WithoutLobby request message. {request}");
                return(null);
            }
        }
 /*
  *  This method is useful when changing the state of some other session from without
  *  its thread, so e.g. if thread of session with id = 1 transitions thread with id = 2
  *  into another state.
  *
  *  This potentially might be dangerous to do while the other thread is in the process
  *  of receiving messages, which is why:
  *      a. the listen task gets cancelled to not misinterpret the message.
  *      b. if in the middle of parsing, the message and the buffered data is all discarded.
  *
  *  An ideal solution would be to finish parsing, if in the middle of parsing, but then discard
  *  the message, however, I have no way of knowing that information, since I'm using protobuf for packets.
  */
 public void TransitionState(Tcp_State state)
 {
     this.state = state;
     this.change_state_task_completion_source.SetResult(state);
 }