0

today I am fighting with a game code. The game runs in DirectX9 and will be an online multiplayer (1v1 game). Thus I create 2 applications actually: 1 to act as a server and 1 to act as a client. However, while the games compile, the server does not want to display anything. Here's my code (not all of it, for clarity, but I can show more if asked)

SERVER:

int WINAPI WinMain(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wc;

    //settipg up DX3D window

    //setting up initial state of game

    //here we establish communication between server and client
    WORD winsock_version = 0x202;
    WSADATA winsock_data;

    int address_family = AF_INET;
    int type = SOCK_DGRAM;
    int protocol = IPPROTO_UDP;
    SOCKET sock = socket(address_family, type, protocol);

    SOCKADDR_IN local_address;
    local_address.sin_family = AF_INET;
    local_address.sin_port = htons(PORT);
    local_address.sin_addr.s_addr = INADDR_ANY;

    int8 buffer_in[SOCKET_BUFFER_SIZE];
    int8 buffer_out[SOCKET_BUFFER_SIZE];

    // enter the main loop:
    MSG msg;

    while (TRUE)
    {   
        int flags = 0;
        SOCKADDR_IN from;
        SOCKADDR* to = (SOCKADDR*)&from;
        //prepare state packet to send to a client
        int32 bytes_written = 0;
        memcpy(&buffer_out[bytes_written], &bat1.pos_x, sizeof(bat1.pos_x));

        //i read the rest of gamestate and prepare a data packet

        // send to client
        int to_length = sizeof(from);

        // get state packet from a client
        flags = 0;
        int from_size = sizeof(from);
        int bytes_received = recvfrom(sock, buffer_in, SOCKET_BUFFER_SIZE, flags, (SOCKADDR*)&from, &from_size);

        //get data from packet
        int read_index = 0;
        memcpy(&bat2.pos_x, &buffer_in[read_index], sizeof(bat2.pos_x));
        read_index += sizeof(bat2.pos_x);

//likewise, i get the rest of player 2 game state here

while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        if (msg.message == WM_QUIT)
            break;

//reading input 

            //game state update         
            ball1.move();
            //etc.
        }
        render_frame();
    }

    // clean up DirectX and COM
    cleanD3D();

    return msg.wParam;
}

The client code is quite similar, it sends the gamestate of player2 and tries to receive gamestate of player 1, so the structure of the code is the same, I only changed bat1 to bat2 etc.. Only, before the main loop there is a following piece of code setting up the ip of the server (hardcoded for now):

SOCKADDR_IN server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(PORT);
server_address.sin_addr.S_un.S_un_b.s_b1 = IP1;
server_address.sin_addr.S_un.S_un_b.s_b2 = IP2;
server_address.sin_addr.S_un.S_un_b.s_b3 = IP3;
server_address.sin_addr.S_un.S_un_b.s_b4 = IP4;

Now I have following problems:

  1. While the client compiles neatly and game runs when launched, server only shows white window and "thinking" cursor. Through testing I pinpointed a line that is causing the unwanted bahaviour to this one:

    int bytes_received = recvfrom(sock, buffer_in, SOCKET_BUFFER_SIZE, flags, (SOCKADDR*)&from, &from_size);

Now, how do I fix it? At this point I don't care as much about synchronization of gamestates as much (since that I can deal with later and the issue is not as relevant as areas for both players are separate), but I would like the game obviously to send some information and read each other gamestates.

  1. My second big question is: how do I avoid hardcoding server IP? I would like the client to be able to find server on its own. However, I seem to find no simple solution to this.
Yess
  • 41
  • 3
  • Your specific problem aside.. are you doing this for learning purpose? Because on long term it is a really frustrating idea to low-level program a game. Am not meaning you need to use something huge like Unity or Unreal, but there are abstracting libraries in C++ as well. – AlexGeorg Nov 26 '19 at 09:23
  • Yes, I am doing it for learning. Currently I am doing an assignment where the low-level programming is a goal, thus using any middleware is a big no for me. As I need to write a game from the grounds up I already managed to handle DirectX to display the game as I want it to and I managed to program the gamestate calculations and so the offline version of the game works perfectly. Now the last thing I need to grasp here is the network communication and I am struggling a bit with it, since I wrote a code that I think should work, yet it somehow does not. – Yess Nov 26 '19 at 09:40

2 Answers2

0

To generally avoid that the program hangs up on this, look at this question and implement a timeout: C++ recvfrom timeout That makes sense regardless of why the connection didn't work.

However as for why it would not connect, are you already testing this from different computers (since you talk about IPs)? If so, I'd assume it is a port issue. You have to enable in the firewall of your router that programs may send through that port. Try the program locally first with 127.0.0.1 which is the computer itself.


For your 2nd question, regarding how to automatically find servers. There isn't really an easy way if that should work across the WWW (there might be a solution on good old LAN). The way it is usually done is by having a web server that is reachable via an URL like any website and provides the player names and IP addresses of ready players.

With it, the procedure works like that:

  1. When a player wants to play, the game accesses said online server and downloads the current list of other players and IP addresses that are available.
  2. If there are any available, the player should be able to click the oponent's name in your game window. Then your game connects with the corresponding IP.
  3. If the player wants to host a game of their own, he types his name and the game should send his IP address together with the name to the server to display to other players. Right afterwards, the game turns into a server so it can retrieve attempts to start a game.

Professional solutions often use a server that also processes the game data. That means the players have clients only and all connect to the web server (via UDP or TCP) which forwards the game state to the oponnents.

The keyword to find more about this by the way is "matchmaking".

AlexGeorg
  • 967
  • 1
  • 7
  • 16
  • OK, here's a little update: The game still does not work when i write 127.0.0.1 address, also I try to set a timeout for socket with setsockopt(sock, SOL_SOCKET< SO_RCVTIMEO, &timeout, sizeof timeout);, but with no luck either – Yess Nov 26 '19 at 11:53
0

Okay, I managed to connect the client and server. Now, however, I am facing an issue with translation of the data packets into gamestate. Everything is okay with sending data (smaller packet) from client to server. Server reads it successfully with following code:

    int8 buffer_in[SOCKET_BUFFER_SIZE];
    int8 buffer_out[SOCKET_BUFFER_SIZE];
    flags = 0;
    int from_size = sizeof(from);
    int bytes_received = recvfrom(sock, buffer_in, SOCKET_BUFFER_SIZE, flags, (SOCKADDR*)&from, &from_size);
    int read_index = 0;
    memcpy(&bat2.pos_x, &buffer_in[read_index], sizeof bat2.pos_x);

Now, I have an issue when I try to send gamestate back to the client. I use the following lines:

    SOCKADDR_IN from;
    int from_size = sizeof(from);
    int bytes_received = recvfrom(sock, buffer_in, SOCKET_BUFFER_SIZE, flags, (SOCKADDR*)&from, &from_size);
    int read_index = 0;
    memcpy(&bat1.pos_x, &buffer_in[read_index], sizeof bat1.pos_x);
    bytes_written += sizeof bat1.pos_x;
    memcpy(&ball1.pos_x, &buffer_in[bytes_written], sizeof ball1.pos_x);
    memcpy(&tempno, &buffer_in[bytes_written], sizeof ball1.pos_x);
    bytes_written += sizeof ball1.pos_x;

This gives me undefined result. I use tempno to display the value which is assigned to x position of the ball to check if i am getting correct data. The data sent from the server seems fine (i read the buffer before sending, to make sure).

Yess
  • 41
  • 3