Merge pull request #943 from fragglet/sdl2-branch
Introduce a network protocol versioning scheme. This will allow backwards-compatible protocol changes in future, and also allow forks to remain compatible with Chocolate Doom.
This commit is contained in:
commit
78206090eb
10 changed files with 349 additions and 245 deletions
|
|
@ -426,6 +426,41 @@ void NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic)
|
|||
NET_CL_SendTics(starttic, endtic);
|
||||
}
|
||||
|
||||
// Parse a SYN packet received back from the server indicating a successful
|
||||
// connection attempt.
|
||||
static void NET_CL_ParseSYN(net_packet_t *packet)
|
||||
{
|
||||
net_protocol_t protocol;
|
||||
char *server_version;
|
||||
|
||||
server_version = NET_ReadSafeString(packet);
|
||||
if (server_version == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
protocol = NET_ReadProtocol(packet);
|
||||
if (protocol == NET_PROTOCOL_UNKNOWN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We are now successfully connected.
|
||||
client_connection.state = NET_CONN_STATE_CONNECTED;
|
||||
client_connection.protocol = protocol;
|
||||
|
||||
// Even though we have negotiated a compatible protocol, the game may still
|
||||
// desync. Chocolate Doom's philosophy makes this unlikely, but if we're
|
||||
// playing with a forked version, or even against a different version that
|
||||
// fixes a compatibility issue, we may still have problems.
|
||||
if (strcmp(server_version, PACKAGE_STRING) != 0)
|
||||
{
|
||||
fprintf(stderr, "NET_CL_ParseSYN: This is '%s', but the server is "
|
||||
"'%s'. It is possible that this mismatch may cause the game "
|
||||
"to desync.\n", PACKAGE_STRING, server_version);
|
||||
}
|
||||
}
|
||||
|
||||
// data received while we are waiting for the game to start
|
||||
|
||||
static void NET_CL_ParseWaitingData(net_packet_t *packet)
|
||||
|
|
@ -795,16 +830,14 @@ static void NET_CL_ParseConsoleMessage(net_packet_t *packet)
|
|||
{
|
||||
char *msg;
|
||||
|
||||
msg = NET_ReadString(packet);
|
||||
msg = NET_ReadSafeString(packet);
|
||||
|
||||
if (msg == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Message from server: ");
|
||||
|
||||
NET_SafePuts(msg);
|
||||
printf("Message from server:\n%s\n", msg);
|
||||
}
|
||||
|
||||
// parse a received packet
|
||||
|
|
@ -826,6 +859,10 @@ static void NET_CL_ParsePacket(net_packet_t *packet)
|
|||
{
|
||||
switch (packet_type)
|
||||
{
|
||||
case NET_PACKET_TYPE_SYN:
|
||||
NET_CL_ParseSYN(packet);
|
||||
break;
|
||||
|
||||
case NET_PACKET_TYPE_WAITING_DATA:
|
||||
NET_CL_ParseWaitingData(packet);
|
||||
break;
|
||||
|
|
@ -921,14 +958,14 @@ static void NET_CL_SendSYN(net_connect_data_t *data)
|
|||
NET_WriteInt16(packet, NET_PACKET_TYPE_SYN);
|
||||
NET_WriteInt32(packet, NET_MAGIC_NUMBER);
|
||||
NET_WriteString(packet, PACKAGE_STRING);
|
||||
NET_WriteProtocolList(packet);
|
||||
NET_WriteConnectData(packet, data);
|
||||
NET_WriteString(packet, net_player_name);
|
||||
NET_Conn_SendPacket(&client_connection, packet);
|
||||
NET_FreePacket(packet);
|
||||
}
|
||||
|
||||
// connect to a server
|
||||
|
||||
// Connect to a server
|
||||
boolean NET_CL_Connect(net_addr_t *addr, net_connect_data_t *data)
|
||||
{
|
||||
int start_time;
|
||||
|
|
@ -959,7 +996,7 @@ boolean NET_CL_Connect(net_addr_t *addr, net_connect_data_t *data)
|
|||
|
||||
// Initialize connection
|
||||
|
||||
NET_Conn_InitClient(&client_connection, addr);
|
||||
NET_Conn_InitClient(&client_connection, addr, NET_PROTOCOL_UNKNOWN);
|
||||
|
||||
// try to connect
|
||||
|
||||
|
|
|
|||
201
src/net_common.c
201
src/net_common.c
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "doomtype.h"
|
||||
#include "d_mode.h"
|
||||
#include "i_system.h"
|
||||
#include "i_timer.h"
|
||||
|
||||
#include "net_common.h"
|
||||
|
|
@ -34,9 +35,19 @@
|
|||
|
||||
#define KEEPALIVE_PERIOD 1
|
||||
|
||||
// String names for the enum values in net_protocol_t, which are what is
|
||||
// sent over the wire. Every enum value must have an entry in this list.
|
||||
static struct
|
||||
{
|
||||
net_protocol_t protocol;
|
||||
const char *name;
|
||||
} protocol_names[] = {
|
||||
{NET_PROTOCOL_CHOCOLATE_DOOM_0, "CHOCOLATE_DOOM_0"},
|
||||
};
|
||||
|
||||
// reliable packet that is guaranteed to reach its destination
|
||||
|
||||
struct net_reliable_packet_s
|
||||
struct net_reliable_packet_s
|
||||
{
|
||||
net_packet_t *packet;
|
||||
int last_send_time;
|
||||
|
|
@ -44,11 +55,13 @@ struct net_reliable_packet_s
|
|||
net_reliable_packet_t *next;
|
||||
};
|
||||
|
||||
static void NET_Conn_Init(net_connection_t *conn, net_addr_t *addr)
|
||||
static void NET_Conn_Init(net_connection_t *conn, net_addr_t *addr,
|
||||
net_protocol_t protocol)
|
||||
{
|
||||
conn->last_send_time = -1;
|
||||
conn->num_retries = 0;
|
||||
conn->addr = addr;
|
||||
conn->protocol = protocol;
|
||||
conn->reliable_packets = NULL;
|
||||
conn->reliable_send_seq = 0;
|
||||
conn->reliable_recv_seq = 0;
|
||||
|
|
@ -56,18 +69,20 @@ static void NET_Conn_Init(net_connection_t *conn, net_addr_t *addr)
|
|||
|
||||
// Initialize as a client connection
|
||||
|
||||
void NET_Conn_InitClient(net_connection_t *conn, net_addr_t *addr)
|
||||
void NET_Conn_InitClient(net_connection_t *conn, net_addr_t *addr,
|
||||
net_protocol_t protocol)
|
||||
{
|
||||
NET_Conn_Init(conn, addr);
|
||||
NET_Conn_Init(conn, addr, protocol);
|
||||
conn->state = NET_CONN_STATE_CONNECTING;
|
||||
}
|
||||
|
||||
// Initialize as a server connection
|
||||
|
||||
void NET_Conn_InitServer(net_connection_t *conn, net_addr_t *addr)
|
||||
void NET_Conn_InitServer(net_connection_t *conn, net_addr_t *addr,
|
||||
net_protocol_t protocol)
|
||||
{
|
||||
NET_Conn_Init(conn, addr);
|
||||
conn->state = NET_CONN_STATE_WAITING_ACK;
|
||||
NET_Conn_Init(conn, addr, protocol);
|
||||
conn->state = NET_CONN_STATE_CONNECTED;
|
||||
}
|
||||
|
||||
// Send a packet to a connection
|
||||
|
|
@ -80,38 +95,6 @@ void NET_Conn_SendPacket(net_connection_t *conn, net_packet_t *packet)
|
|||
NET_SendPacket(conn->addr, packet);
|
||||
}
|
||||
|
||||
// parse an ACK packet from a client
|
||||
|
||||
static void NET_Conn_ParseACK(net_connection_t *conn, net_packet_t *packet)
|
||||
{
|
||||
net_packet_t *reply;
|
||||
|
||||
if (conn->state == NET_CONN_STATE_CONNECTING)
|
||||
{
|
||||
// We are a client
|
||||
|
||||
// received a response from the server to our SYN
|
||||
|
||||
conn->state = NET_CONN_STATE_CONNECTED;
|
||||
|
||||
// We must send an ACK reply to the server's ACK
|
||||
|
||||
reply = NET_NewPacket(10);
|
||||
NET_WriteInt16(reply, NET_PACKET_TYPE_ACK);
|
||||
NET_Conn_SendPacket(conn, reply);
|
||||
NET_FreePacket(reply);
|
||||
}
|
||||
|
||||
if (conn->state == NET_CONN_STATE_WAITING_ACK)
|
||||
{
|
||||
// We are a server
|
||||
|
||||
// Client is connected
|
||||
|
||||
conn->state = NET_CONN_STATE_CONNECTED;
|
||||
}
|
||||
}
|
||||
|
||||
static void NET_Conn_ParseDisconnect(net_connection_t *conn, net_packet_t *packet)
|
||||
{
|
||||
net_packet_t *reply;
|
||||
|
|
@ -151,7 +134,7 @@ static void NET_Conn_ParseReject(net_connection_t *conn, net_packet_t *packet)
|
|||
{
|
||||
char *msg;
|
||||
|
||||
msg = NET_ReadString(packet);
|
||||
msg = NET_ReadSafeString(packet);
|
||||
|
||||
if (msg == NULL)
|
||||
{
|
||||
|
|
@ -165,8 +148,7 @@ static void NET_Conn_ParseReject(net_connection_t *conn, net_packet_t *packet)
|
|||
conn->state = NET_CONN_STATE_DISCONNECTED;
|
||||
conn->disconnect_reason = NET_DISCONNECT_REMOTE;
|
||||
|
||||
printf("Rejected by server: ");
|
||||
NET_SafePuts(msg);
|
||||
printf("Rejected by server: %s\n", msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -282,9 +264,6 @@ boolean NET_Conn_Packet(net_connection_t *conn, net_packet_t *packet,
|
|||
|
||||
switch (*packet_type)
|
||||
{
|
||||
case NET_PACKET_TYPE_ACK:
|
||||
NET_Conn_ParseACK(conn, packet);
|
||||
break;
|
||||
case NET_PACKET_TYPE_DISCONNECT:
|
||||
NET_Conn_ParseDisconnect(conn, packet);
|
||||
break;
|
||||
|
|
@ -370,35 +349,6 @@ void NET_Conn_Run(net_connection_t *conn)
|
|||
conn->reliable_packets->last_send_time = nowtime;
|
||||
}
|
||||
}
|
||||
else if (conn->state == NET_CONN_STATE_WAITING_ACK)
|
||||
{
|
||||
if (conn->last_send_time < 0
|
||||
|| nowtime - conn->last_send_time > 1000)
|
||||
{
|
||||
// it has been a second since the last ACK was sent, and
|
||||
// still no reply.
|
||||
|
||||
if (conn->num_retries < MAX_RETRIES)
|
||||
{
|
||||
// send another ACK
|
||||
|
||||
packet = NET_NewPacket(10);
|
||||
NET_WriteInt16(packet, NET_PACKET_TYPE_ACK);
|
||||
NET_Conn_SendPacket(conn, packet);
|
||||
NET_FreePacket(packet);
|
||||
conn->last_send_time = nowtime;
|
||||
|
||||
++conn->num_retries;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no more retries allowed.
|
||||
|
||||
conn->state = NET_CONN_STATE_DISCONNECTED;
|
||||
conn->disconnect_reason = NET_DISCONNECT_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (conn->state == NET_CONN_STATE_DISCONNECTING)
|
||||
{
|
||||
// Waiting for a reply to our DISCONNECT request.
|
||||
|
|
@ -532,3 +482,106 @@ boolean NET_ValidGameSettings(GameMode_t mode, GameMission_t mission,
|
|||
return true;
|
||||
}
|
||||
|
||||
static net_protocol_t ParseProtocolName(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < arrlen(protocol_names); ++i)
|
||||
{
|
||||
if (!strcmp(protocol_names[i].name, name))
|
||||
{
|
||||
return protocol_names[i].protocol;
|
||||
}
|
||||
}
|
||||
|
||||
return NET_PROTOCOL_UNKNOWN;
|
||||
}
|
||||
|
||||
// NET_ReadProtocol reads a single string-format protocol name from the given
|
||||
// packet, returning NET_PROTOCOL_UNKNOWN if the string describes an unknown
|
||||
// protocol.
|
||||
net_protocol_t NET_ReadProtocol(net_packet_t *packet)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
name = NET_ReadString(packet);
|
||||
if (name == NULL)
|
||||
{
|
||||
return NET_PROTOCOL_UNKNOWN;
|
||||
}
|
||||
|
||||
return ParseProtocolName(name);
|
||||
}
|
||||
|
||||
// NET_WriteProtocol writes a single string-format protocol name to a packet.
|
||||
void NET_WriteProtocol(net_packet_t *packet, net_protocol_t protocol)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < arrlen(protocol_names); ++i)
|
||||
{
|
||||
if (protocol_names[i].protocol == protocol)
|
||||
{
|
||||
NET_WriteString(packet, protocol_names[i].name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If you add an entry to the net_protocol_t enum, a corresponding entry
|
||||
// must be added to the protocol_names list.
|
||||
I_Error("NET_WriteProtocol: protocol %d missing from protocol_names "
|
||||
"list; please add it.", protocol);
|
||||
}
|
||||
|
||||
// NET_ReadProtocolList reads a list of string-format protocol names from
|
||||
// the given packet, returning a single protocol number. The protocol that is
|
||||
// returned is the last protocol in the list that is a supported protocol. If
|
||||
// no recognized protocols are read, NET_PROTOCOL_UNKNOWN is returned.
|
||||
net_protocol_t NET_ReadProtocolList(net_packet_t *packet)
|
||||
{
|
||||
net_protocol_t result;
|
||||
unsigned int num_protocols;
|
||||
int i;
|
||||
|
||||
if (!NET_ReadInt8(packet, &num_protocols))
|
||||
{
|
||||
return NET_PROTOCOL_UNKNOWN;
|
||||
}
|
||||
|
||||
result = NET_PROTOCOL_UNKNOWN;
|
||||
|
||||
for (i = 0; i < num_protocols; ++i)
|
||||
{
|
||||
net_protocol_t p;
|
||||
const char *name;
|
||||
|
||||
name = NET_ReadString(packet);
|
||||
if (name == NULL)
|
||||
{
|
||||
return NET_PROTOCOL_UNKNOWN;
|
||||
}
|
||||
|
||||
p = ParseProtocolName(name);
|
||||
if (p != NET_PROTOCOL_UNKNOWN)
|
||||
{
|
||||
result = p;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// NET_WriteProtocolList writes a list of string-format protocol names into
|
||||
// the given packet, all the supported protocols in the net_protocol_t enum.
|
||||
void NET_WriteProtocolList(net_packet_t *packet)
|
||||
{
|
||||
int i;
|
||||
|
||||
NET_WriteInt8(packet, NET_NUM_PROTOCOLS);
|
||||
|
||||
for (i = 0; i < NET_NUM_PROTOCOLS; ++i)
|
||||
{
|
||||
NET_WriteProtocol(packet, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,28 +21,18 @@
|
|||
#include "net_defs.h"
|
||||
#include "net_packet.h"
|
||||
|
||||
typedef enum
|
||||
typedef enum
|
||||
{
|
||||
// sending syn packets, waiting for an ACK reply
|
||||
// (client side)
|
||||
|
||||
// Client has sent a SYN, is waiting for a SYN in response.
|
||||
NET_CONN_STATE_CONNECTING,
|
||||
|
||||
// received a syn, sent an ack, waiting for an ack reply
|
||||
// (server side)
|
||||
|
||||
NET_CONN_STATE_WAITING_ACK,
|
||||
|
||||
// successfully connected
|
||||
|
||||
// Successfully connected.
|
||||
NET_CONN_STATE_CONNECTED,
|
||||
|
||||
// sent a DISCONNECT packet, waiting for a DISCONNECT_ACK reply
|
||||
|
||||
// Sent a DISCONNECT packet, waiting for a DISCONNECT_ACK reply
|
||||
NET_CONN_STATE_DISCONNECTING,
|
||||
|
||||
// client successfully disconnected
|
||||
|
||||
// Client successfully disconnected
|
||||
NET_CONN_STATE_DISCONNECTED,
|
||||
|
||||
// We are disconnected, but in a sleep state, waiting for several
|
||||
|
|
@ -50,7 +40,6 @@ typedef enum
|
|||
// to arrive, and we need to send another one. We keep this as
|
||||
// a valid connection for a few seconds until we are sure that
|
||||
// the other end has successfully disconnected as well.
|
||||
|
||||
NET_CONN_STATE_DISCONNECTED_SLEEP,
|
||||
|
||||
} net_connstate_t;
|
||||
|
|
@ -82,6 +71,7 @@ typedef struct
|
|||
net_connstate_t state;
|
||||
net_disconnect_reason_t disconnect_reason;
|
||||
net_addr_t *addr;
|
||||
net_protocol_t protocol;
|
||||
int last_send_time;
|
||||
int num_retries;
|
||||
int keepalive_send_time;
|
||||
|
|
@ -93,18 +83,25 @@ typedef struct
|
|||
|
||||
|
||||
void NET_Conn_SendPacket(net_connection_t *conn, net_packet_t *packet);
|
||||
void NET_Conn_InitClient(net_connection_t *conn, net_addr_t *addr);
|
||||
void NET_Conn_InitServer(net_connection_t *conn, net_addr_t *addr);
|
||||
void NET_Conn_InitClient(net_connection_t *conn, net_addr_t *addr,
|
||||
net_protocol_t protocol);
|
||||
void NET_Conn_InitServer(net_connection_t *conn, net_addr_t *addr,
|
||||
net_protocol_t protocol);
|
||||
boolean NET_Conn_Packet(net_connection_t *conn, net_packet_t *packet,
|
||||
unsigned int *packet_type);
|
||||
void NET_Conn_Disconnect(net_connection_t *conn);
|
||||
void NET_Conn_Run(net_connection_t *conn);
|
||||
net_packet_t *NET_Conn_NewReliable(net_connection_t *conn, int packet_type);
|
||||
|
||||
// Other miscellaneous common functions
|
||||
// Protocol list exchange.
|
||||
net_protocol_t NET_ReadProtocol(net_packet_t *packet);
|
||||
void NET_WriteProtocol(net_packet_t *packet, net_protocol_t protocol);
|
||||
net_protocol_t NET_ReadProtocolList(net_packet_t *packet);
|
||||
void NET_WriteProtocolList(net_packet_t *packet);
|
||||
|
||||
// Other miscellaneous common functions
|
||||
unsigned int NET_ExpandTicNum(unsigned int relative, unsigned int b);
|
||||
boolean NET_ValidGameSettings(GameMode_t mode, GameMission_t mission,
|
||||
boolean NET_ValidGameSettings(GameMode_t mode, GameMission_t mission,
|
||||
net_gamesettings_t *settings);
|
||||
|
||||
#endif /* #ifndef NET_COMMON_H */
|
||||
|
|
|
|||
|
|
@ -98,20 +98,43 @@ struct _net_addr_s
|
|||
void *handle;
|
||||
};
|
||||
|
||||
// magic number sent when connecting to check this is a valid client
|
||||
// Magic number sent when connecting to check this is a valid client
|
||||
#define NET_MAGIC_NUMBER 1454104972U
|
||||
|
||||
#define NET_MAGIC_NUMBER 3436803284U
|
||||
// Old magic number used by Chocolate Doom versions before v3.0:
|
||||
#define NET_OLD_MAGIC_NUMBER 3436803284U
|
||||
|
||||
// header field value indicating that the packet is a reliable packet
|
||||
|
||||
#define NET_RELIABLE_PACKET (1 << 15)
|
||||
|
||||
// Supported protocols. If you're developing a fork of Chocolate
|
||||
// Doom, you can add your own entry to this list while maintaining
|
||||
// compatibility with Chocolate Doom servers. Higher-numbered enum values
|
||||
// will be preferred when negotiating a protocol for the client and server
|
||||
// to use, so the order matters.
|
||||
// NOTE: The values in this enum do not have any special value outside of
|
||||
// the program they're compiled in. What matters is the string representation.
|
||||
typedef enum
|
||||
{
|
||||
// Protocol introduced with Chocolate Doom v3.0. Each compatibility-
|
||||
// breaking change to the network protocol will produce a new protocol
|
||||
// number in this enum.
|
||||
NET_PROTOCOL_CHOCOLATE_DOOM_0,
|
||||
|
||||
// Add your own protocol here; be sure to add a name for it to the list
|
||||
// in net_common.c too.
|
||||
|
||||
NET_NUM_PROTOCOLS,
|
||||
NET_PROTOCOL_UNKNOWN,
|
||||
} net_protocol_t;
|
||||
|
||||
// packet types
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NET_PACKET_TYPE_SYN,
|
||||
NET_PACKET_TYPE_ACK,
|
||||
NET_PACKET_TYPE_ACK, // deprecated
|
||||
NET_PACKET_TYPE_REJECTED,
|
||||
NET_PACKET_TYPE_KEEPALIVE,
|
||||
NET_PACKET_TYPE_WAITING_DATA,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
// Network packet manipulation (net_packet_t)
|
||||
//
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include "m_misc.h"
|
||||
#include "net_packet.h"
|
||||
|
|
@ -202,6 +203,37 @@ char *NET_ReadString(net_packet_t *packet)
|
|||
return start;
|
||||
}
|
||||
|
||||
// Read a string from the packet, but (potentially) modify it to strip
|
||||
// out any unprintable characters which could be malicious control codes.
|
||||
// Note that this may modify the original packet contents.
|
||||
char *NET_ReadSafeString(net_packet_t *packet)
|
||||
{
|
||||
char *r, *w, *result;
|
||||
|
||||
result = NET_ReadString(packet);
|
||||
if (result == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// w is always <= r, so we never produce a longer string than the original.
|
||||
w = result;
|
||||
for (r = result; *r != '\0'; ++r)
|
||||
{
|
||||
// TODO: This is a very naive way of producing a safe string; only
|
||||
// ASCII characters are allowed. Probably this should really support
|
||||
// UTF-8 characters as well.
|
||||
if (isprint(*r) || *r == '\n')
|
||||
{
|
||||
*w = *r;
|
||||
++w;
|
||||
}
|
||||
}
|
||||
*w = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Dynamically increases the size of a packet
|
||||
|
||||
static void NET_IncreasePacket(net_packet_t *packet)
|
||||
|
|
@ -270,7 +302,7 @@ void NET_WriteInt32(net_packet_t *packet, unsigned int i)
|
|||
packet->len += 4;
|
||||
}
|
||||
|
||||
void NET_WriteString(net_packet_t *packet, char *string)
|
||||
void NET_WriteString(net_packet_t *packet, const char *string)
|
||||
{
|
||||
byte *p;
|
||||
size_t string_size;
|
||||
|
|
|
|||
|
|
@ -33,12 +33,13 @@ boolean NET_ReadSInt16(net_packet_t *packet, signed int *data);
|
|||
boolean NET_ReadSInt32(net_packet_t *packet, signed int *data);
|
||||
|
||||
char *NET_ReadString(net_packet_t *packet);
|
||||
char *NET_ReadSafeString(net_packet_t *packet);
|
||||
|
||||
void NET_WriteInt8(net_packet_t *packet, unsigned int i);
|
||||
void NET_WriteInt16(net_packet_t *packet, unsigned int i);
|
||||
void NET_WriteInt32(net_packet_t *packet, unsigned int i);
|
||||
|
||||
void NET_WriteString(net_packet_t *packet, char *string);
|
||||
void NET_WriteString(net_packet_t *packet, const char *string);
|
||||
|
||||
#endif /* #ifndef NET_PACKET_H */
|
||||
|
||||
|
|
|
|||
|
|
@ -738,7 +738,7 @@ static void NET_QueryPrintCallback(net_addr_t *addr,
|
|||
printf("(game running) ");
|
||||
}
|
||||
|
||||
NET_SafePuts(data->description);
|
||||
printf("%s\n", data->description);
|
||||
}
|
||||
|
||||
void NET_LANQuery(void)
|
||||
|
|
|
|||
211
src/net_server.c
211
src/net_server.c
|
|
@ -213,7 +213,7 @@ static void NET_SV_BroadcastMessage(char *s, ...)
|
|||
va_start(args, s);
|
||||
M_vsnprintf(buf, sizeof(buf), s, args);
|
||||
va_end(args);
|
||||
|
||||
|
||||
for (i=0; i<MAXNETNODES; ++i)
|
||||
{
|
||||
if (ClientConnected(&clients[i]))
|
||||
|
|
@ -222,7 +222,7 @@ static void NET_SV_BroadcastMessage(char *s, ...)
|
|||
}
|
||||
}
|
||||
|
||||
NET_SafePuts(buf);
|
||||
printf("%s\n", buf);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -555,16 +555,14 @@ static void NET_SV_SendReject(net_addr_t *addr, char *msg)
|
|||
NET_FreePacket(packet);
|
||||
}
|
||||
|
||||
static void NET_SV_InitNewClient(net_client_t *client,
|
||||
net_addr_t *addr,
|
||||
char *player_name)
|
||||
static void NET_SV_InitNewClient(net_client_t *client, net_addr_t *addr,
|
||||
net_protocol_t protocol)
|
||||
{
|
||||
client->active = true;
|
||||
client->connect_time = I_GetTimeMS();
|
||||
NET_Conn_InitServer(&client->connection, addr);
|
||||
NET_Conn_InitServer(&client->connection, addr, protocol);
|
||||
client->addr = addr;
|
||||
client->last_send_time = -1;
|
||||
client->name = M_StringDuplicate(player_name);
|
||||
|
||||
// init the ticcmd send queue
|
||||
|
||||
|
|
@ -580,99 +578,121 @@ static void NET_SV_InitNewClient(net_client_t *client,
|
|||
|
||||
// parse a SYN from a client(initiating a connection)
|
||||
|
||||
static void NET_SV_ParseSYN(net_packet_t *packet,
|
||||
net_client_t *client,
|
||||
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;
|
||||
|
||||
// read the magic number
|
||||
|
||||
// Read the magic number and check it is the expected one.
|
||||
if (!NET_ReadInt32(packet, &magic))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (magic != NET_MAGIC_NUMBER)
|
||||
switch (magic)
|
||||
{
|
||||
// invalid magic number
|
||||
case NET_MAGIC_NUMBER:
|
||||
break;
|
||||
|
||||
return;
|
||||
case NET_OLD_MAGIC_NUMBER:
|
||||
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:
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the client version is the same as the server
|
||||
|
||||
// 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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(client_version, PACKAGE_STRING) != 0)
|
||||
// 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)
|
||||
{
|
||||
//!
|
||||
// @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.
|
||||
//
|
||||
char reject_msg[256];
|
||||
|
||||
if (M_CheckParm("-ignoreversion") == 0)
|
||||
{
|
||||
NET_SV_SendReject(addr,
|
||||
"Different " PACKAGE_NAME " versions cannot play a net game!\n"
|
||||
"Version mismatch: server version is: " PACKAGE_STRING);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
// read the game mode and mission
|
||||
|
||||
if (!NET_ReadConnectData(packet, &data))
|
||||
// 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)
|
||||
|| !D_ValidGameMode(data.gamemission, data.gamemode)
|
||||
|| data.max_players > NET_MAXPLAYERS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!D_ValidGameMode(data.gamemission, data.gamemode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check max_players value. This must be in a sensible range.
|
||||
|
||||
if (data.max_players > NET_MAXPLAYERS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// read the player's name
|
||||
|
||||
// Read the player's name
|
||||
player_name = NET_ReadString(packet);
|
||||
|
||||
if (player_name == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// received a valid SYN
|
||||
|
||||
// not accepting new connections?
|
||||
// At this point we have received a valid SYN.
|
||||
|
||||
// Not accepting new connections?
|
||||
if (server_state != SERVER_WAITING_LAUNCH)
|
||||
{
|
||||
NET_SV_SendReject(addr, "Server is not currently accepting connections");
|
||||
NET_SV_SendReject(addr,
|
||||
"Server is not currently accepting connections");
|
||||
return;
|
||||
}
|
||||
|
||||
// allocate a client slot if there isn't one already
|
||||
// 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_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;
|
||||
}
|
||||
|
||||
// Check the connecting client is playing the same game as all
|
||||
// the other clients
|
||||
if (data.gamemode != sv_gamemode || data.gamemission != sv_gamemission)
|
||||
{
|
||||
NET_SV_SendReject(addr, "You are playing the wrong game!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate a client slot if there isn't one already
|
||||
if (client == NULL)
|
||||
{
|
||||
// find a slot, or return if none found
|
||||
|
|
@ -702,67 +722,30 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
|
|||
}
|
||||
}
|
||||
|
||||
// New client?
|
||||
|
||||
if (!client->active)
|
||||
// Client already connected?
|
||||
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 ((!data.drone && num_players >= NET_SV_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 && !data.drone)
|
||||
{
|
||||
sv_gamemode = data.gamemode;
|
||||
sv_gamemission = data.gamemission;
|
||||
}
|
||||
|
||||
// Save the SHA1 checksums
|
||||
|
||||
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;
|
||||
|
||||
// Check the connecting client is playing the same game as all
|
||||
// the other clients
|
||||
|
||||
if (data.gamemode != sv_gamemode || data.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 = data.lowres_turn;
|
||||
client->drone = data.drone;
|
||||
client->player_class = data.player_class;
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->connection.state == NET_CONN_STATE_WAITING_ACK)
|
||||
{
|
||||
// force an acknowledgement
|
||||
client->connection.last_send_time = -1;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Parse a launch packet. This is sent by the key player when the "start"
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "m_misc.h"
|
||||
|
|
@ -122,7 +121,7 @@ boolean NET_ReadQueryData(net_packet_t *packet, net_querydata_t *query)
|
|||
{
|
||||
boolean result;
|
||||
|
||||
query->version = NET_ReadString(packet);
|
||||
query->version = NET_ReadSafeString(packet);
|
||||
|
||||
result = query->version != NULL
|
||||
&& NET_ReadInt8(packet, (unsigned int *) &query->server_state)
|
||||
|
|
@ -133,7 +132,7 @@ boolean NET_ReadQueryData(net_packet_t *packet, net_querydata_t *query)
|
|||
|
||||
if (result)
|
||||
{
|
||||
query->description = NET_ReadString(packet);
|
||||
query->description = NET_ReadSafeString(packet);
|
||||
|
||||
return query->description != NULL;
|
||||
}
|
||||
|
|
@ -551,22 +550,3 @@ void NET_WritePRNGSeed(net_packet_t *packet, prng_seed_t seed)
|
|||
NET_WriteBlob(packet, seed, sizeof(prng_seed_t));
|
||||
}
|
||||
|
||||
// "Safe" version of puts, for displaying messages received from the
|
||||
// network.
|
||||
|
||||
void NET_SafePuts(char *s)
|
||||
{
|
||||
char *p;
|
||||
|
||||
// Do not do a straight "puts" of the string, as this could be
|
||||
// dangerous (sending control codes to terminals can do all
|
||||
// kinds of things)
|
||||
|
||||
for (p=s; *p; ++p)
|
||||
{
|
||||
if (isprint(*p) || *p == '\n')
|
||||
putchar(*p);
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,8 +43,6 @@ void NET_WriteSHA1Sum(net_packet_t *packet, sha1_digest_t digest);
|
|||
void NET_WriteWaitData(net_packet_t *packet, net_waitdata_t *data);
|
||||
boolean NET_ReadWaitData(net_packet_t *packet, net_waitdata_t *data);
|
||||
|
||||
void NET_SafePuts(char *msg);
|
||||
|
||||
boolean NET_ReadPRNGSeed(net_packet_t *packet, prng_seed_t seed);
|
||||
void NET_WritePRNGSeed(net_packet_t *packet, prng_seed_t seed);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue