Example #1
0
static void NET_SV_AdvanceWindow(void)
{
    unsigned int lowtic;
    int i;

    if (NET_SV_NumPlayers() <= 0)
    {
        return;
    }

    lowtic = NET_SV_LatestAcknowledged();

    // Advance the recv window until it catches up with lowtic

    while (recvwindow_start < lowtic)
    {    
        boolean should_advance;

        // Check we have tics from all players for first tic in
        // the recv window
        
        should_advance = true;

        for (i=0; i<NET_MAXPLAYERS; ++i)
        {
            if (sv_players[i] == NULL || !ClientConnected(sv_players[i]))
            {
                continue;
            }

            if (!recvwindow[0][i].active)
            {
                should_advance = false;
                break;
            }
        }

        if (!should_advance)
        {
            // The first tic is not complete: ie. we have not 
            // received tics from all connected players.  This can
            // happen if only one player is in the game.

            break;
        }
        
        // Advance the window

        memmove(recvwindow, recvwindow + 1,
                sizeof(*recvwindow) * (BACKUPTICS - 1));
        memset(&recvwindow[BACKUPTICS-1], 0, sizeof(*recvwindow));
        ++recvwindow_start;

        //printf("SV: advanced to %i\n", recvwindow_start);
    }
}
Example #2
0
static void NET_SV_SendWaitingData(net_client_t *client)
{
    net_waitdata_t wait_data;
    net_packet_t *packet;
    net_client_t *controller;
    int i;

    NET_SV_AssignPlayers();

    controller = NET_SV_Controller();

    wait_data.num_players = NET_SV_NumPlayers();
    wait_data.num_drones = NET_SV_NumDrones();
    wait_data.ready_players = NET_SV_NumReadyPlayers();
    wait_data.max_players = NET_SV_MaxPlayers();
    wait_data.is_controller = (client == controller);
    wait_data.consoleplayer = client->player_number;

    // Send the WAD and dehacked checksums of the controlling client.
    // If no controller found (?), send the details that the client
    // is expecting anyway.

    if (controller == NULL)
    {
        controller = client;
    }

    memcpy(&wait_data.wad_sha1sum, &controller->wad_sha1sum,
           sizeof(sha1_digest_t));
    memcpy(&wait_data.deh_sha1sum, &controller->deh_sha1sum,
           sizeof(sha1_digest_t));
    wait_data.is_freedoom = controller->is_freedoom;

    // set name and address of each player:

    for (i = 0; i < wait_data.num_players; ++i)
    {
        M_StringCopy(wait_data.player_names[i],
                     sv_players[i]->name,
                     MAXPLAYERNAME);
        M_StringCopy(wait_data.player_addrs[i],
                     NET_AddrToString(sv_players[i]->addr),
                     MAXPLAYERNAME);
    }

    // Construct packet:

    packet = NET_NewPacket(10);
    NET_WriteInt16(packet, NET_PACKET_TYPE_WAITING_DATA);
    NET_WriteWaitData(packet, &wait_data);

    // Send packet to client and free

    NET_Conn_SendPacket(&client->connection, packet);
    NET_FreePacket(packet);
}
void NET_SV_SendQueryResponse(net_addr_t *addr)
{
    net_packet_t *reply;
    net_querydata_t querydata;
    int p;

    // Version

    querydata.version = PACKAGE_STRING;

    // Server state

    querydata.server_state = server_state;

    // Number of players/maximum players

    querydata.num_players = NET_SV_NumPlayers();
    querydata.max_players = NET_SV_MaxPlayers();

    // Game mode/mission

    querydata.gamemode = sv_gamemode;
    querydata.gamemission = sv_gamemission;

    //!
    // @category net
    // @arg <name>
    //
    // When starting a network server, specify a name for the server.
    //

    p = M_CheckParmWithArgs("-servername", 1);

    if (p > 0)
    {
        querydata.description = myargv[p + 1];
    }
    else
    {
        querydata.description = "Unnamed server";
    }

    // Send it and we're done.
    NET_Log("server: sending query response to %s", NET_AddrToString(addr));
    reply = NET_NewPacket(64);
    NET_WriteInt16(reply, NET_PACKET_TYPE_QUERY_RESPONSE);
    NET_WriteQueryData(reply, &querydata);
    NET_SendPacket(addr, reply);
    NET_FreePacket(reply);
}
static void NET_SV_ParseLaunch(net_packet_t *packet, net_client_t *client)
{
    net_packet_t *launchpacket;
    int num_players;
    unsigned int i;

    NET_Log("server: processing launch packet");

    // Only the controller can launch the game.

    if (client != NET_SV_Controller())
    {
        NET_Log("server: error: this client isn't the controller, %d != %d",
                client, NET_SV_Controller());
        return;
    }

    // Can only launch when we are in the waiting state.

    if (server_state != SERVER_WAITING_LAUNCH)
    {
        NET_Log("server: error: not in waiting launch state, state=%d",
                server_state);
        return;
    }

    // Forward launch on to all clients.
    NET_Log("server: sending launch to all clients");
    NET_SV_AssignPlayers();
    num_players = NET_SV_NumPlayers();

    for (i=0; i<MAXNETNODES; ++i)
    {
        if (!ClientConnected(&clients[i]))
            continue;

        launchpacket = NET_Conn_NewReliable(&clients[i].connection,
                                            NET_PACKET_TYPE_LAUNCH);
        NET_WriteInt8(launchpacket, num_players);
    }

    // Now in launch state.

    server_state = SERVER_WAITING_START;
}
Example #5
0
static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
{
    net_gamesettings_t settings;
    net_packet_t *startpacket;
    int nowtime;
    int i;
    
    if (client != NET_SV_Controller())
    {
        // Only the controller can start a new game

        return;
    }

    if (!NET_ReadSettings(packet, &settings))
    {
        // Malformed packet

        return;
    }

    // Check the game settings are valid

    if (!NET_ValidGameSettings(sv_gamemode, sv_gamemission, &settings))
    {
        return;
    }

    if (server_state != SERVER_WAITING_START)
    {
        // Can only start a game if we are in the waiting start state.

        return;
    }

    // Assign player numbers

    NET_SV_AssignPlayers();

    // Check if anyone is recording a demo and set lowres_turn if so.

    settings.lowres_turn = false;

    for (i=0; i<MAXPLAYERS; ++i)
    {
        if (sv_players[i] != NULL && sv_players[i]->recording_lowres)
        {
            settings.lowres_turn = true;
        }
    }

    nowtime = I_GetTimeMS();

    // Send start packets to each connected node

    for (i=0; i<MAXNETNODES; ++i) 
    {
        if (!ClientConnected(&clients[i]))
            continue;

        clients[i].last_gamedata_time = nowtime;

        startpacket = NET_Conn_NewReliable(&clients[i].connection,
                                           NET_PACKET_TYPE_GAMESTART);

        NET_WriteInt8(startpacket, NET_SV_NumPlayers());
        NET_WriteInt8(startpacket, clients[i].player_number);
        NET_WriteSettings(startpacket, &settings);
    }

    // Change server state

    server_state = SERVER_IN_GAME;
    sv_settings = settings;

    memset(recvwindow, 0, sizeof(recvwindow));
    recvwindow_start = 0;
}
Example #6
0
static void NET_SV_ParseSYN(net_packet_t *packet, 
                            net_client_t *client,
                            net_addr_t *addr)
{
    unsigned int magic;
    unsigned int cl_gamemode, cl_gamemission;
    unsigned int cl_recording_lowres;
    unsigned int cl_drone;
    unsigned int is_freedoom;
    md5_digest_t deh_md5sum, wad_md5sum;
    char *player_name;
    char *client_version;
    int i;

    // read the magic number

    if (!NET_ReadInt32(packet, &magic))
    {
        return;
    }

    if (magic != NET_MAGIC_NUMBER)
    {
        // invalid magic number

        return;
    }

    // Check the client version is the same as the server

    client_version = NET_ReadString(packet);

    if (client_version == NULL)
    {
        return;
    }

    if (strcmp(client_version, PACKAGE_STRING) != 0)
    {
        //!
        // @category net
        //
        // When running a netgame server, ignore version mismatches between
        // the server and the client. Using this option may cause game
        // desyncs to occur, or differences in protocol may mean the netgame
        // will simply not function at all.
        //

        if (M_CheckParm("-ignoreversion") == 0) 
        {
            NET_SV_SendReject(addr,
                              "Version mismatch: server version is: "
                              PACKAGE_STRING);
            return;
        }
    }

    // read the game mode and mission

    if (!NET_ReadInt16(packet, &cl_gamemode) 
     || !NET_ReadInt16(packet, &cl_gamemission)
     || !NET_ReadInt8(packet, &cl_recording_lowres)
     || !NET_ReadInt8(packet, &cl_drone)
     || !NET_ReadMD5Sum(packet, wad_md5sum)
     || !NET_ReadMD5Sum(packet, deh_md5sum)
     || !NET_ReadInt8(packet, &is_freedoom))
    {
        return;
    }

    if (!NET_ValidGameMode(cl_gamemode, cl_gamemission))
    {
        return;
    }

    // read the player's name

    player_name = NET_ReadString(packet);

    if (player_name == NULL)
    {
        return;
    }
    
    // received a valid SYN

    // not accepting new connections?
    
    if (server_state != SERVER_WAITING_START)
    {
        NET_SV_SendReject(addr, "Server is not currently accepting connections");
        return;
    }
    
    // allocate a client slot if there isn't one already

    if (client == NULL)
    {
        // find a slot, or return if none found

        for (i=0; i<MAXNETNODES; ++i)
        {
            if (!clients[i].active)
            {
                client = &clients[i];
                break;
            }
        }

        if (client == NULL)
        {
            return;
        }
    }
    else
    {
        // If this is a recently-disconnected client, deactivate
        // to allow immediate reconnection

        if (client->connection.state == NET_CONN_STATE_DISCONNECTED)
        {
            client->active = false;
        }
    }

    // New client?

    if (!client->active)
    {
        int num_players;

        // Before accepting a new client, check that there is a slot
        // free

        NET_SV_AssignPlayers();
        num_players = NET_SV_NumPlayers();

        if ((!cl_drone && num_players >= MAXPLAYERS)
         || NET_SV_NumClients() >= MAXNETNODES)
        {
            NET_SV_SendReject(addr, "Server is full!");
            return;
        }

        // TODO: Add server option to allow rejecting clients which
        // set lowres_turn.  This is potentially desirable as the 
        // presence of such clients affects turning resolution.

        // Adopt the game mode and mission of the first connecting client

        if (num_players == 0 && !cl_drone)
        {
            sv_gamemode = cl_gamemode;
            sv_gamemission = cl_gamemission;
        }

        // Save the MD5 checksums

        memcpy(client->wad_md5sum, wad_md5sum, sizeof(md5_digest_t));
        memcpy(client->deh_md5sum, deh_md5sum, sizeof(md5_digest_t));
        client->is_freedoom = is_freedoom;

        // Check the connecting client is playing the same game as all
        // the other clients

        if (cl_gamemode != sv_gamemode || cl_gamemission != sv_gamemission)
        {
            NET_SV_SendReject(addr, "You are playing the wrong game!");
            return;
        }
        
        // Activate, initialize connection

        NET_SV_InitNewClient(client, addr, player_name);

        client->recording_lowres = cl_recording_lowres;
        client->drone = cl_drone;
    }

    if (client->connection.state == NET_CONN_STATE_WAITING_ACK)
    {
        // force an acknowledgement
        client->connection.last_send_time = -1;
    }
}
Example #7
0
static void NET_SV_RunClient(net_client_t *client)
{
    // Run common code

    NET_Conn_Run(&client->connection);
    
    if (client->connection.state == NET_CONN_STATE_DISCONNECTED
     && client->connection.disconnect_reason == NET_DISCONNECT_TIMEOUT)
    {
        NET_SV_BroadcastMessage("Client '%s' timed out and disconnected",
                                client->name);
    }
    
    // Is this client disconnected?

    if (client->connection.state == NET_CONN_STATE_DISCONNECTED)
    {
        // deactivate and free back 

        client->active = false;
        free(client->name);
        NET_FreeAddress(client->addr);

        // Are there any clients left connected?  If not, return the
        // server to the waiting-for-players state.
        //
	// Disconnect any drones still connected.

        if (NET_SV_NumPlayers() <= 0)
        {
            NET_SV_GameEnded();
        }
    }
    
    if (!ClientConnected(client))
    {
        // client has not yet finished connecting

        return;
    }

    if (server_state == SERVER_WAITING_START)
    {
        // Waiting for the game to start

        // Send information once every second

        if (client->last_send_time < 0 
         || I_GetTimeMS() - client->last_send_time > 1000)
        {
            NET_SV_SendWaitingData(client);
            client->last_send_time = I_GetTimeMS();
        }
    }

    if (server_state == SERVER_IN_GAME)
    {
        NET_SV_PumpSendQueue(client);
        NET_SV_CheckDeadlock(client);
    }
}
Example #8
0
static void NET_SV_SendWaitingData(net_client_t *client)
{
    net_packet_t *packet;
    net_client_t *controller;
    int num_players;
    int i;

    NET_SV_AssignPlayers();

    controller = NET_SV_Controller();

    num_players = NET_SV_NumPlayers();

    // time to send the client another status packet

    packet = NET_NewPacket(10);
    NET_WriteInt16(packet, NET_PACKET_TYPE_WAITING_DATA);

    // include the number of players waiting

    NET_WriteInt8(packet, num_players);

    // send the number of drone clients

    NET_WriteInt8(packet, NET_SV_NumDrones());

    // indicate whether the client is the controller

    NET_WriteInt8(packet, client == controller);

    // send the player number of this client

    NET_WriteInt8(packet, client->player_number);

    // send the addresses of all players

    for (i=0; i<num_players; ++i)
    {
        char *addr;

        // name

        NET_WriteString(packet, sv_players[i]->name);

        // address

        addr = NET_AddrToString(sv_players[i]->addr);

        NET_WriteString(packet, addr);
    }

    // Send the WAD and dehacked checksums of the controlling client.

    if (controller != NULL)
    {
        NET_WriteMD5Sum(packet, controller->wad_md5sum);
        NET_WriteMD5Sum(packet, controller->deh_md5sum);
        NET_WriteInt8(packet, controller->is_freedoom);
    }
    else
    {
        NET_WriteMD5Sum(packet, client->wad_md5sum);
        NET_WriteMD5Sum(packet, client->deh_md5sum);
        NET_WriteInt8(packet, client->is_freedoom);
    }

    // send packet to client and free

    NET_Conn_SendPacket(&client->connection, packet);
    NET_FreePacket(packet);
}
Example #9
0
static void StartGame(void)
{
    net_packet_t *startpacket;
    unsigned int i;
    int nowtime;

    // Assign player numbers

    NET_SV_AssignPlayers();

    // Check if anyone is recording a demo and set lowres_turn if so.

    sv_settings.lowres_turn = false;

    for (i = 0; i < NET_MAXPLAYERS; ++i)
    {
        if (sv_players[i] != NULL && sv_players[i]->recording_lowres)
        {
            sv_settings.lowres_turn = true;
        }
    }

    sv_settings.num_players = NET_SV_NumPlayers();

    // Copy player classes:

    for (i = 0; i < NET_MAXPLAYERS; ++i)
    {
        if (sv_players[i] != NULL)
        {
            sv_settings.player_classes[i] = sv_players[i]->player_class;
        }
        else
        {
            sv_settings.player_classes[i] = 0;
        }
    }

    nowtime = I_GetTimeMS();

    // Send start packets to each connected node

    for (i = 0; i < MAXNETNODES; ++i)
    {
        if (!ClientConnected(&clients[i]))
            continue;

        clients[i].last_gamedata_time = nowtime;

        startpacket = NET_Conn_NewReliable(&clients[i].connection,
                                           NET_PACKET_TYPE_GAMESTART);

        sv_settings.consoleplayer = clients[i].player_number;

        NET_WriteSettings(startpacket, &sv_settings);
    }

    // Change server state

    server_state = SERVER_IN_GAME;

    memset(recvwindow, 0, sizeof(recvwindow));
    recvwindow_start = 0;
}
static void NET_SV_ParseSYN(net_packet_t *packet, net_client_t *client,
                            net_addr_t *addr)
{
    unsigned int magic;
    net_connect_data_t data;
    net_packet_t *reply;
    net_protocol_t protocol;
    char *player_name;
    char *client_version;
    int num_players;
    int i;

    NET_Log("server: processing SYN packet");

    // Read the magic number and check it is the expected one.
    if (!NET_ReadInt32(packet, &magic))
    {
        NET_Log("server: error: no magic number for SYN");
        return;
    }

    switch (magic)
    {
        case NET_MAGIC_NUMBER:
            break;

        case NET_OLD_MAGIC_NUMBER:
            NET_Log("server: error: client using old magic number: %d", magic);
            NET_SV_SendReject(addr,
                "You are using an old client version that is not supported by "
                "this server. This server is running " PACKAGE_STRING ".");
            return;

        default:
            NET_Log("server: error: wrong magic number: %d", magic);
            return;
    }

    // Read the client version string. We actually now only use this when
    // sending a reject message, as we only reject if we can't negotiate a
    // common protocol (below).
    client_version = NET_ReadString(packet);
    if (client_version == NULL)
    {
        NET_Log("server: error: no version from client");
        return;
    }

    // Read the client's list of accepted protocols. Net play between forks
    // of Chocolate Doom is accepted provided that they can negotiate a
    // common accepted protocol.
    protocol = NET_ReadProtocolList(packet);
    if (protocol == NET_PROTOCOL_UNKNOWN)
    {
        char reject_msg[256];

        M_snprintf(reject_msg, sizeof(reject_msg),
            "Version mismatch: server version is: " PACKAGE_STRING "; "
            "client is: %s. No common compatible protocol could be "
            "negotiated.", client_version);
        NET_SV_SendReject(addr, reject_msg);
        NET_Log("server: error: no common protocol");
        return;
    }

    // Read connect data, and check that the game mode/mission are valid
    // and the max_players value is in a sensible range.
    if (!NET_ReadConnectData(packet, &data))
    {
        NET_Log("server: error: failed to read connect data");
        return;
    }

    if (!D_ValidGameMode(data.gamemission, data.gamemode)
     || data.max_players > NET_MAXPLAYERS)
    {
        NET_Log("server: error: invalid connect data, max_players=%d, "
                "gamemission=%d, gamemode=%d",
                data.max_players, data.gamemission, data.gamemode);
        return;
    }

    // Read the player's name
    player_name = NET_ReadString(packet);
    if (player_name == NULL)
    {
        NET_Log("server: error: failed to read player name");
        return;
    }

    // At this point we have received a valid SYN.

    // Not accepting new connections?
    if (server_state != SERVER_WAITING_LAUNCH)
    {
        NET_Log("server: error: not in waiting launch state, server_state=%d",
                server_state);
        NET_SV_SendReject(addr,
                          "Server is not currently accepting connections");
        return;
    }

    // Before accepting a new client, check that there is a slot free.
    NET_SV_AssignPlayers();
    num_players = NET_SV_NumPlayers();

    if ((!data.drone && num_players >= NET_SV_MaxPlayers())
     || NET_SV_NumClients() >= MAXNETNODES)
    {
        NET_Log("server: no more players, num_players=%d, max=%d",
                num_players, NET_SV_MaxPlayers());
        NET_SV_SendReject(addr, "Server is full!");
        return;
    }

    // TODO: Add server option to allow rejecting clients which set
    // lowres_turn.  This is potentially desirable as the presence of such
    // clients affects turning resolution.

    // Adopt the game mode and mission of the first connecting client:
    if (num_players == 0 && !data.drone)
    {
        sv_gamemode = data.gamemode;
        sv_gamemission = data.gamemission;
        NET_Log("server: new game, mode=%d, mission=%d",
                sv_gamemode, sv_gamemission);
    }

    // Check the connecting client is playing the same game as all
    // the other clients
    if (data.gamemode != sv_gamemode || data.gamemission != sv_gamemission)
    {
        char msg[128];
        NET_Log("server: wrong mode/mission, %d != %d || %d != %d",
                data.gamemode, sv_gamemode, data.gamemission, sv_gamemission);
        M_snprintf(msg, sizeof(msg),
                   "Game mismatch: server is %s (%s), client is %s (%s)",
                   D_GameMissionString(sv_gamemission),
                   D_GameModeString(sv_gamemode),
                   D_GameMissionString(data.gamemission),
                   D_GameModeString(data.gamemode));

        NET_SV_SendReject(addr, msg);
        return;
    }

    // Allocate a client slot if there isn't one already
    if (client == NULL)
    {
        // find a slot, or return if none found

        for (i=0; i<MAXNETNODES; ++i)
        {
            if (!clients[i].active)
            {
                client = &clients[i];
                break;
            }
        }

        if (client == NULL)
        {
            return;
        }
    }
    else
    {
        // If this is a recently-disconnected client, deactivate
        // to allow immediate reconnection

        if (client->connection.state == NET_CONN_STATE_DISCONNECTED)
        {
            client->active = false;
        }
    }

    // Client already connected?
    if (client->active)
    {
        NET_Log("server: client is already initialized (duplicate SYN?)");
        return;
    }

    // Activate, initialize connection
    NET_SV_InitNewClient(client, addr, protocol);

    // Save the SHA1 checksums and other details.
    memcpy(client->wad_sha1sum, data.wad_sha1sum, sizeof(sha1_digest_t));
    memcpy(client->deh_sha1sum, data.deh_sha1sum, sizeof(sha1_digest_t));
    client->is_freedoom = data.is_freedoom;
    client->max_players = data.max_players;
    client->name = M_StringDuplicate(player_name);
    client->recording_lowres = data.lowres_turn;
    client->drone = data.drone;
    client->player_class = data.player_class;

    // Send a reply back to the client, indicating a successful connection
    // and specifying the protocol that will be used for communications.
    reply = NET_Conn_NewReliable(&client->connection, NET_PACKET_TYPE_SYN);
    NET_WriteString(reply, PACKAGE_STRING);
    NET_WriteProtocol(reply, protocol);
}
static void NET_SV_RunClient(net_client_t *client)
{
    // Run common code

    NET_Conn_Run(&client->connection);

    if (client->connection.state == NET_CONN_STATE_DISCONNECTED
     && client->connection.disconnect_reason == NET_DISCONNECT_TIMEOUT)
    {
        NET_Log("server: client at %s timed out",
                NET_AddrToString(client->addr));
        NET_SV_BroadcastMessage("Client '%s' timed out and disconnected",
                                client->name);
    }

    // Is this client disconnected?

    if (client->connection.state == NET_CONN_STATE_DISCONNECTED)
    {
        client->active = false;

        // If we were about to start a game, any player disconnecting
        // should cause an abort.

        if (server_state == SERVER_WAITING_START && !client->drone)
        {
            NET_SV_BroadcastMessage("Game startup aborted because "
                                    "player '%s' disconnected.",
                                    client->name);
            NET_SV_GameEnded();
        }

        free(client->name);
        NET_ReleaseAddress(client->addr);

        // Are there any clients left connected?  If not, return the
        // server to the waiting-for-players state.
        //
	// Disconnect any drones still connected.

        if (NET_SV_NumPlayers() <= 0)
        {
            NET_Log("server: no player clients left, game ended");
            NET_SV_GameEnded();
        }
    }

    if (!ClientConnected(client))
    {
        // client has not yet finished connecting

        return;
    }

    if (server_state == SERVER_WAITING_LAUNCH)
    {
        // Waiting for the game to start

        // Send information once every second

        if (client->last_send_time < 0 
         || I_GetTimeMS() - client->last_send_time > 1000)
        {
            NET_SV_SendWaitingData(client);
            client->last_send_time = I_GetTimeMS();
        }
    }

    if (server_state == SERVER_IN_GAME)
    {
        NET_SV_PumpSendQueue(client);
        NET_SV_CheckDeadlock(client);
    }
}