summaryrefslogtreecommitdiff
path: root/src/network/network_udp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/network_udp.cpp')
-rw-r--r--src/network/network_udp.cpp655
1 files changed, 655 insertions, 0 deletions
diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp
new file mode 100644
index 000000000..5caec3b42
--- /dev/null
+++ b/src/network/network_udp.cpp
@@ -0,0 +1,655 @@
+/* $Id$ */
+
+#ifdef ENABLE_NETWORK
+
+#include "../stdafx.h"
+#include "../debug.h"
+#include "../string.h"
+#include "network_data.h"
+#include "../date.h"
+#include "../map.h"
+#include "network_gamelist.h"
+#include "network_udp.h"
+#include "../variables.h"
+#include "../newgrf_config.h"
+
+#include "core/udp.h"
+
+/**
+ * @file network_udp.c This file handles the UDP related communication.
+ *
+ * This is the GameServer <-> MasterServer and GameServer <-> GameClient
+ * communication before the game is being joined.
+ */
+
+enum {
+ ADVERTISE_NORMAL_INTERVAL = 30000, // interval between advertising in ticks (15 minutes)
+ ADVERTISE_RETRY_INTERVAL = 300, // readvertise when no response after this many ticks (9 seconds)
+ ADVERTISE_RETRY_TIMES = 3 // give up readvertising after this much failed retries
+};
+
+#define DEF_UDP_RECEIVE_COMMAND(type) void NetworkPacketReceive_ ## type ## _command(Packet *p, const struct sockaddr_in *client_addr)
+
+static NetworkClientState _udp_cs;
+
+DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_FIND_SERVER)
+{
+ Packet *packet;
+ // Just a fail-safe.. should never happen
+ if (!_network_udp_server)
+ return;
+
+ packet = NetworkSend_Init(PACKET_UDP_SERVER_RESPONSE);
+
+ // Update some game_info
+ _network_game_info.game_date = _date;
+ _network_game_info.map_width = MapSizeX();
+ _network_game_info.map_height = MapSizeY();
+ _network_game_info.map_set = _opt.landscape;
+ _network_game_info.companies_on = ActivePlayerCount();
+ _network_game_info.spectators_on = NetworkSpectatorCount();
+ _network_game_info.grfconfig = _grfconfig;
+
+ NetworkSend_NetworkGameInfo(packet, &_network_game_info);
+
+ // Let the client know that we are here
+ NetworkSendUDP_Packet(_udp_server_socket, packet, client_addr);
+
+ free(packet);
+
+ DEBUG(net, 2, "[udp] queried from '%s'", inet_ntoa(client_addr->sin_addr));
+}
+
+void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config)
+{
+ /* Find the matching GRF file */
+ const GRFConfig *f = FindGRFConfig(config->grfid, config->md5sum);
+ if (f == NULL) {
+ /* Don't know the GRF, so mark game incompatible and the (possibly)
+ * already resolved name for this GRF (another server has sent the
+ * name of the GRF already */
+ config->name = FindUnknownGRFName(config->grfid, config->md5sum, true);
+ SETBIT(config->flags, GCF_NOT_FOUND);
+ } else {
+ config->filename = f->filename;
+ config->name = f->name;
+ config->info = f->info;
+ }
+ SETBIT(config->flags, GCF_COPY);
+}
+
+DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE)
+{
+ extern const char _openttd_revision[];
+ NetworkGameList *item;
+
+ // Just a fail-safe.. should never happen
+ if (_network_udp_server || _udp_cs.has_quit) return;
+
+ DEBUG(net, 4, "[udp] server response from %s:%d", inet_ntoa(client_addr->sin_addr),ntohs(client_addr->sin_port));
+
+ // Find next item
+ item = NetworkGameListAddItem(inet_addr(inet_ntoa(client_addr->sin_addr)), ntohs(client_addr->sin_port));
+
+ NetworkRecv_NetworkGameInfo(&_udp_cs, p, &item->info);
+
+ item->info.compatible = true;
+ {
+ /* Checks whether there needs to be a request for names of GRFs and makes
+ * the request if necessary. GRFs that need to be requested are the GRFs
+ * that do not exist on the clients system and we do not have the name
+ * resolved of, i.e. the name is still UNKNOWN_GRF_NAME_PLACEHOLDER.
+ * The in_request array and in_request_count are used so there is no need
+ * to do a second loop over the GRF list, which can be relatively expensive
+ * due to the string comparisons. */
+ const GRFConfig *in_request[NETWORK_MAX_GRF_COUNT];
+ const GRFConfig *c;
+ uint in_request_count = 0;
+ struct sockaddr_in out_addr;
+
+ for (c = item->info.grfconfig; c != NULL; c = c->next) {
+ if (HASBIT(c->flags, GCF_NOT_FOUND)) item->info.compatible = false;
+ if (!HASBIT(c->flags, GCF_NOT_FOUND) || strcmp(c->name, UNKNOWN_GRF_NAME_PLACEHOLDER) != 0) continue;
+ in_request[in_request_count] = c;
+ in_request_count++;
+ }
+
+ if (in_request_count > 0) {
+ /* There are 'unknown' GRFs, now send a request for them */
+ uint i;
+ Packet *packet = NetworkSend_Init(PACKET_UDP_CLIENT_GET_NEWGRFS);
+
+ NetworkSend_uint8 (packet, in_request_count);
+ for (i = 0; i < in_request_count; i++) {
+ NetworkSend_GRFIdentifier(packet, in_request[i]);
+ }
+
+ out_addr.sin_family = AF_INET;
+ out_addr.sin_port = htons(item->port);
+ out_addr.sin_addr.s_addr = item->ip;
+ NetworkSendUDP_Packet(_udp_client_socket, packet, &out_addr);
+ free(packet);
+ }
+ }
+
+ if (item->info.server_lang >= NETWORK_NUM_LANGUAGES) item->info.server_lang = 0;
+ if (item->info.map_set >= NUM_LANDSCAPE ) item->info.map_set = 0;
+
+ if (item->info.hostname[0] == '\0')
+ snprintf(item->info.hostname, sizeof(item->info.hostname), "%s", inet_ntoa(client_addr->sin_addr));
+
+ /* Check if we are allowed on this server based on the revision-match */
+ item->info.version_compatible =
+ strcmp(item->info.server_revision, _openttd_revision) == 0 ||
+ strcmp(item->info.server_revision, NOREV_STRING) == 0;
+ item->info.compatible &= item->info.version_compatible; // Already contains match for GRFs
+
+ item->online = true;
+
+ UpdateNetworkGameWindow(false);
+}
+
+DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_DETAIL_INFO)
+{
+ NetworkClientState *cs;
+ NetworkClientInfo *ci;
+ Packet *packet;
+ Player *player;
+ byte current = 0;
+ int i;
+
+ // Just a fail-safe.. should never happen
+ if (!_network_udp_server) return;
+
+ packet = NetworkSend_Init(PACKET_UDP_SERVER_DETAIL_INFO);
+
+ /* Send the amount of active companies */
+ NetworkSend_uint8 (packet, NETWORK_COMPANY_INFO_VERSION);
+ NetworkSend_uint8 (packet, ActivePlayerCount());
+
+ /* Fetch the latest version of everything */
+ NetworkPopulateCompanyInfo();
+
+ /* Go through all the players */
+ FOR_ALL_PLAYERS(player) {
+ /* Skip non-active players */
+ if (!player->is_active) continue;
+
+ current++;
+
+ /* Send the information */
+ NetworkSend_uint8(packet, current);
+
+ NetworkSend_string(packet, _network_player_info[player->index].company_name);
+ NetworkSend_uint32(packet, _network_player_info[player->index].inaugurated_year);
+ NetworkSend_uint64(packet, _network_player_info[player->index].company_value);
+ NetworkSend_uint64(packet, _network_player_info[player->index].money);
+ NetworkSend_uint64(packet, _network_player_info[player->index].income);
+ NetworkSend_uint16(packet, _network_player_info[player->index].performance);
+
+ /* Send 1 if there is a passord for the company else send 0 */
+ if (_network_player_info[player->index].password[0] != '\0') {
+ NetworkSend_uint8(packet, 1);
+ } else {
+ NetworkSend_uint8(packet, 0);
+ }
+
+ for (i = 0; i < NETWORK_VEHICLE_TYPES; i++)
+ NetworkSend_uint16(packet, _network_player_info[player->index].num_vehicle[i]);
+
+ for (i = 0; i < NETWORK_STATION_TYPES; i++)
+ NetworkSend_uint16(packet, _network_player_info[player->index].num_station[i]);
+
+ /* Find the clients that are connected to this player */
+ FOR_ALL_CLIENTS(cs) {
+ ci = DEREF_CLIENT_INFO(cs);
+ if (ci->client_playas == player->index) {
+ /* The uint8 == 1 indicates that a client is following */
+ NetworkSend_uint8(packet, 1);
+ NetworkSend_string(packet, ci->client_name);
+ NetworkSend_string(packet, ci->unique_id);
+ NetworkSend_uint32(packet, ci->join_date);
+ }
+ }
+ /* Also check for the server itself */
+ ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
+ if (ci->client_playas == player->index) {
+ /* The uint8 == 1 indicates that a client is following */
+ NetworkSend_uint8(packet, 1);
+ NetworkSend_string(packet, ci->client_name);
+ NetworkSend_string(packet, ci->unique_id);
+ NetworkSend_uint32(packet, ci->join_date);
+ }
+
+ /* Indicates end of client list */
+ NetworkSend_uint8(packet, 0);
+ }
+
+ /* And check if we have any spectators */
+ FOR_ALL_CLIENTS(cs) {
+ ci = DEREF_CLIENT_INFO(cs);
+ if (!IsValidPlayer(ci->client_playas)) {
+ /* The uint8 == 1 indicates that a client is following */
+ NetworkSend_uint8(packet, 1);
+ NetworkSend_string(packet, ci->client_name);
+ NetworkSend_string(packet, ci->unique_id);
+ NetworkSend_uint32(packet, ci->join_date);
+ }
+ }
+
+ /* Also check for the server itself */
+ ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
+ if (!IsValidPlayer(ci->client_playas)) {
+ /* The uint8 == 1 indicates that a client is following */
+ NetworkSend_uint8(packet, 1);
+ NetworkSend_string(packet, ci->client_name);
+ NetworkSend_string(packet, ci->unique_id);
+ NetworkSend_uint32(packet, ci->join_date);
+ }
+
+ /* Indicates end of client list */
+ NetworkSend_uint8(packet, 0);
+
+ NetworkSendUDP_Packet(_udp_server_socket, packet, client_addr);
+
+ free(packet);
+}
+
+DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_MASTER_RESPONSE_LIST)
+{
+ int i;
+ struct in_addr ip;
+ uint16 port;
+ uint8 ver;
+
+ /* packet begins with the protocol version (uint8)
+ * then an uint16 which indicates how many
+ * ip:port pairs are in this packet, after that
+ * an uint32 (ip) and an uint16 (port) for each pair
+ */
+
+ ver = NetworkRecv_uint8(&_udp_cs, p);
+
+ if (_udp_cs.has_quit) return;
+
+ if (ver == 1) {
+ for (i = NetworkRecv_uint16(&_udp_cs, p); i != 0 ; i--) {
+ ip.s_addr = TO_LE32(NetworkRecv_uint32(&_udp_cs, p));
+ port = NetworkRecv_uint16(&_udp_cs, p);
+ NetworkUDPQueryServer(inet_ntoa(ip), port);
+ }
+ }
+}
+
+DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_MASTER_ACK_REGISTER)
+{
+ _network_advertise_retries = 0;
+ DEBUG(net, 2, "[udp] advertising on master server successfull");
+
+ /* We are advertised, but we don't want to! */
+ if (!_network_advertise) NetworkUDPRemoveAdvertise();
+}
+
+/**
+ * A client has requested the names of some NewGRFs.
+ *
+ * Replying this can be tricky as we have a limit of SEND_MTU bytes
+ * in the reply packet and we can send up to 100 bytes per NewGRF
+ * (GRF ID, MD5sum and NETWORK_GRF_NAME_LENGTH bytes for the name).
+ * As SEND_MTU is _much_ less than 100 * NETWORK_MAX_GRF_COUNT, it
+ * could be that a packet overflows. To stop this we only reply
+ * with the first N NewGRFs so that if the first N + 1 NewGRFs
+ * would be sent, the packet overflows.
+ * in_reply and in_reply_count are used to keep a list of GRFs to
+ * send in the reply.
+ */
+DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_GET_NEWGRFS)
+{
+ uint8 num_grfs;
+ uint i;
+
+ const GRFConfig *in_reply[NETWORK_MAX_GRF_COUNT];
+ Packet *packet;
+ uint8 in_reply_count = 0;
+ uint packet_len = 0;
+
+ /* Just a fail-safe.. should never happen */
+ if (_udp_cs.has_quit) return;
+
+ DEBUG(net, 6, "[udp] newgrf data request from %s:%d", inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port));
+
+ num_grfs = NetworkRecv_uint8 (&_udp_cs, p);
+ if (num_grfs > NETWORK_MAX_GRF_COUNT) return;
+
+ for (i = 0; i < num_grfs; i++) {
+ GRFConfig c;
+ const GRFConfig *f;
+
+ NetworkRecv_GRFIdentifier(&_udp_cs, p, &c);
+
+ /* Find the matching GRF file */
+ f = FindGRFConfig(c.grfid, c.md5sum);
+ if (f == NULL) continue; // The GRF is unknown to this server
+
+ /* If the reply might exceed the size of the packet, only reply
+ * the current list and do not send the other data.
+ * The name could be an empty string, if so take the filename. */
+ packet_len += sizeof(c.grfid) + sizeof(c.md5sum) +
+ min(strlen((f->name != NULL && strlen(f->name) > 0) ? f->name : f->filename) + 1, NETWORK_GRF_NAME_LENGTH);
+ if (packet_len > SEND_MTU - 4) { // 4 is 3 byte header + grf count in reply
+ break;
+ }
+ in_reply[in_reply_count] = f;
+ in_reply_count++;
+ }
+
+ if (in_reply_count == 0) return;
+
+ packet = NetworkSend_Init(PACKET_UDP_SERVER_NEWGRFS);
+ NetworkSend_uint8 (packet, in_reply_count);
+ for (i = 0; i < in_reply_count; i++) {
+ char name[NETWORK_GRF_NAME_LENGTH];
+
+ /* The name could be an empty string, if so take the filename */
+ ttd_strlcpy(name, (in_reply[i]->name != NULL && strlen(in_reply[i]->name) > 0) ?
+ in_reply[i]->name : in_reply[i]->filename, sizeof(name));
+ NetworkSend_GRFIdentifier(packet, in_reply[i]);
+ NetworkSend_string(packet, name);
+ }
+
+ NetworkSendUDP_Packet(_udp_server_socket, packet, client_addr);
+ free(packet);
+}
+
+/** The return of the client's request of the names of some NewGRFs */
+DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_NEWGRFS)
+{
+ uint8 num_grfs;
+ uint i;
+
+ /* Just a fail-safe.. should never happen */
+ if (_udp_cs.has_quit) return;
+
+ DEBUG(net, 6, "[udp] newgrf data reply from %s:%d", inet_ntoa(client_addr->sin_addr),ntohs(client_addr->sin_port));
+
+ num_grfs = NetworkRecv_uint8 (&_udp_cs, p);
+ if (num_grfs > NETWORK_MAX_GRF_COUNT) return;
+
+ for (i = 0; i < num_grfs; i++) {
+ char *unknown_name;
+ char name[NETWORK_GRF_NAME_LENGTH];
+ GRFConfig c;
+
+ NetworkRecv_GRFIdentifier(&_udp_cs, p, &c);
+ NetworkRecv_string(&_udp_cs, p, name, sizeof(name));
+
+ /* An empty name is not possible under normal circumstances
+ * and causes problems when showing the NewGRF list. */
+ if (strlen(name) == 0) continue;
+
+ /* Finds the fake GRFConfig for the just read GRF ID and MD5sum tuple.
+ * If it exists and not resolved yet, then name of the fake GRF is
+ * overwritten with the name from the reply. */
+ unknown_name = FindUnknownGRFName(c.grfid, c.md5sum, false);
+ if (unknown_name != NULL && strcmp(unknown_name, UNKNOWN_GRF_NAME_PLACEHOLDER) == 0) {
+ ttd_strlcpy(unknown_name, name, NETWORK_GRF_NAME_LENGTH);
+ }
+ }
+}
+
+/**
+ * Every type of UDP packet should only be received by a single socket;
+ * The socket communicating with the masterserver should receive the
+ * game information of some 'random' host.
+ */
+typedef struct NetworkUDPPacketAndSocket {
+ void (*callback)(Packet *p, const struct sockaddr_in *client_addr);
+ SOCKET *incoming_socket;
+} NetworkUPDPacketAndSocket;
+
+static const NetworkUPDPacketAndSocket _network_udp_packet[PACKET_UDP_END] = {
+ { RECEIVE_COMMAND(PACKET_UDP_CLIENT_FIND_SERVER), &_udp_server_socket },
+ { RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE), &_udp_client_socket },
+ { RECEIVE_COMMAND(PACKET_UDP_CLIENT_DETAIL_INFO), &_udp_server_socket },
+ { NULL, NULL },
+ { NULL, NULL },
+ { RECEIVE_COMMAND(PACKET_UDP_MASTER_ACK_REGISTER), &_udp_master_socket },
+ { NULL, NULL },
+ { RECEIVE_COMMAND(PACKET_UDP_MASTER_RESPONSE_LIST), &_udp_client_socket },
+ { NULL, NULL },
+ { RECEIVE_COMMAND(PACKET_UDP_CLIENT_GET_NEWGRFS), &_udp_server_socket },
+ { RECEIVE_COMMAND(PACKET_UDP_SERVER_NEWGRFS), &_udp_client_socket },
+};
+
+void NetworkHandleUDPPacket(const SOCKET udp, Packet *p, const struct sockaddr_in *client_addr)
+{
+ byte type;
+
+ /* Fake a client, so we can see when there is an illegal packet */
+ _udp_cs.socket = INVALID_SOCKET;
+ _udp_cs.has_quit = false;
+
+ type = NetworkRecv_uint8(&_udp_cs, p);
+
+ if (type < PACKET_UDP_END && *_network_udp_packet[type].incoming_socket == udp && !_udp_cs.has_quit) {
+ _network_udp_packet[type].callback(p, client_addr);
+ } else {
+ if (*_network_udp_packet[type].incoming_socket != udp) {
+ DEBUG(net, 0, "[udp] received packet on wrong port from %s:%d", inet_ntoa(client_addr->sin_addr),ntohs(client_addr->sin_port));
+ } else if (!_udp_cs.has_quit) {
+ DEBUG(net, 0, "[udp] received invalid packet type %d from %s:%d", type, inet_ntoa(client_addr->sin_addr),ntohs(client_addr->sin_port));
+ } else {
+ DEBUG(net, 0, "[udp] received illegal packet from %s:%d", inet_ntoa(client_addr->sin_addr),ntohs(client_addr->sin_port));
+ }
+ }
+}
+
+
+// Close UDP connection
+void NetworkUDPStop(void)
+{
+ DEBUG(net, 1, "[udp] closed listeners");
+
+ if (_network_udp_server) {
+ NetworkUDPClose(&_udp_server_socket);
+ NetworkUDPClose(&_udp_master_socket);
+ } else {
+ NetworkUDPClose(&_udp_client_socket);
+ }
+
+ _network_udp_server = false;
+ _network_udp_broadcast = 0;
+}
+
+// Broadcast to all ips
+static void NetworkUDPBroadCast(SOCKET udp)
+{
+ Packet* p = NetworkSend_Init(PACKET_UDP_CLIENT_FIND_SERVER);
+ uint i;
+
+ for (i = 0; _broadcast_list[i] != 0; i++) {
+ struct sockaddr_in out_addr;
+
+ out_addr.sin_family = AF_INET;
+ out_addr.sin_port = htons(_network_server_port);
+ out_addr.sin_addr.s_addr = _broadcast_list[i];
+
+ DEBUG(net, 4, "[udp] broadcasting to %s", inet_ntoa(out_addr.sin_addr));
+
+ NetworkSendUDP_Packet(udp, p, &out_addr);
+ }
+
+ free(p);
+}
+
+
+// Request the the server-list from the master server
+void NetworkUDPQueryMasterServer(void)
+{
+ struct sockaddr_in out_addr;
+ Packet *p;
+
+ if (_udp_client_socket == INVALID_SOCKET)
+ if (!NetworkUDPListen(&_udp_client_socket, 0, 0, true))
+ return;
+
+ p = NetworkSend_Init(PACKET_UDP_CLIENT_GET_LIST);
+
+ out_addr.sin_family = AF_INET;
+ out_addr.sin_port = htons(NETWORK_MASTER_SERVER_PORT);
+ out_addr.sin_addr.s_addr = NetworkResolveHost(NETWORK_MASTER_SERVER_HOST);
+
+ // packet only contains protocol version
+ NetworkSend_uint8(p, NETWORK_MASTER_SERVER_VERSION);
+
+ NetworkSendUDP_Packet(_udp_client_socket, p, &out_addr);
+
+ DEBUG(net, 2, "[udp] master server queried at %s:%d", inet_ntoa(out_addr.sin_addr),ntohs(out_addr.sin_port));
+
+ free(p);
+}
+
+// Find all servers
+void NetworkUDPSearchGame(void)
+{
+ // We are still searching..
+ if (_network_udp_broadcast > 0) return;
+
+ // No UDP-socket yet..
+ if (_udp_client_socket == INVALID_SOCKET)
+ if (!NetworkUDPListen(&_udp_client_socket, 0, 0, true))
+ return;
+
+ DEBUG(net, 0, "[udp] searching server");
+
+ NetworkUDPBroadCast(_udp_client_socket);
+ _network_udp_broadcast = 300; // Stay searching for 300 ticks
+}
+
+NetworkGameList *NetworkUDPQueryServer(const char* host, unsigned short port)
+{
+ struct sockaddr_in out_addr;
+ Packet *p;
+ NetworkGameList *item;
+
+ // No UDP-socket yet..
+ if (_udp_client_socket == INVALID_SOCKET)
+ if (!NetworkUDPListen(&_udp_client_socket, 0, 0, true))
+ return NULL;
+
+ out_addr.sin_family = AF_INET;
+ out_addr.sin_port = htons(port);
+ out_addr.sin_addr.s_addr = NetworkResolveHost(host);
+
+ // Clear item in gamelist
+ item = NetworkGameListAddItem(inet_addr(inet_ntoa(out_addr.sin_addr)), ntohs(out_addr.sin_port));
+ memset(&item->info, 0, sizeof(item->info));
+ ttd_strlcpy(item->info.server_name, host, lengthof(item->info.server_name));
+ ttd_strlcpy(item->info.hostname, host, lengthof(item->info.hostname));
+ item->online = false;
+
+ // Init the packet
+ p = NetworkSend_Init(PACKET_UDP_CLIENT_FIND_SERVER);
+
+ NetworkSendUDP_Packet(_udp_client_socket, p, &out_addr);
+
+ free(p);
+
+ UpdateNetworkGameWindow(false);
+ return item;
+}
+
+/* Remove our advertise from the master-server */
+void NetworkUDPRemoveAdvertise(void)
+{
+ struct sockaddr_in out_addr;
+ Packet *p;
+
+ /* Check if we are advertising */
+ if (!_networking || !_network_server || !_network_udp_server) return;
+
+ /* check for socket */
+ if (_udp_master_socket == INVALID_SOCKET)
+ if (!NetworkUDPListen(&_udp_master_socket, _network_server_bind_ip, 0, false))
+ return;
+
+ DEBUG(net, 1, "[udp] removing advertise from master server");
+
+ /* Find somewhere to send */
+ out_addr.sin_family = AF_INET;
+ out_addr.sin_port = htons(NETWORK_MASTER_SERVER_PORT);
+ out_addr.sin_addr.s_addr = NetworkResolveHost(NETWORK_MASTER_SERVER_HOST);
+
+ /* Send the packet */
+ p = NetworkSend_Init(PACKET_UDP_SERVER_UNREGISTER);
+ /* Packet is: Version, server_port */
+ NetworkSend_uint8(p, NETWORK_MASTER_SERVER_VERSION);
+ NetworkSend_uint16(p, _network_server_port);
+ NetworkSendUDP_Packet(_udp_master_socket, p, &out_addr);
+
+ free(p);
+}
+
+/* Register us to the master server
+ This function checks if it needs to send an advertise */
+void NetworkUDPAdvertise(void)
+{
+ struct sockaddr_in out_addr;
+ Packet *p;
+
+ /* Check if we should send an advertise */
+ if (!_networking || !_network_server || !_network_udp_server || !_network_advertise)
+ return;
+
+ /* check for socket */
+ if (_udp_master_socket == INVALID_SOCKET)
+ if (!NetworkUDPListen(&_udp_master_socket, _network_server_bind_ip, 0, false))
+ return;
+
+ if (_network_need_advertise) {
+ _network_need_advertise = false;
+ _network_advertise_retries = ADVERTISE_RETRY_TIMES;
+ } else {
+ /* Only send once every ADVERTISE_NORMAL_INTERVAL ticks */
+ if (_network_advertise_retries == 0) {
+ if ((_network_last_advertise_frame + ADVERTISE_NORMAL_INTERVAL) > _frame_counter)
+ return;
+ _network_advertise_retries = ADVERTISE_RETRY_TIMES;
+ }
+
+ if ((_network_last_advertise_frame + ADVERTISE_RETRY_INTERVAL) > _frame_counter)
+ return;
+ }
+
+ _network_advertise_retries--;
+ _network_last_advertise_frame = _frame_counter;
+
+ /* Find somewhere to send */
+ out_addr.sin_family = AF_INET;
+ out_addr.sin_port = htons(NETWORK_MASTER_SERVER_PORT);
+ out_addr.sin_addr.s_addr = NetworkResolveHost(NETWORK_MASTER_SERVER_HOST);
+
+ DEBUG(net, 1, "[udp] advertising to master server");
+
+ /* Send the packet */
+ p = NetworkSend_Init(PACKET_UDP_SERVER_REGISTER);
+ /* Packet is: WELCOME_MESSAGE, Version, server_port */
+ NetworkSend_string(p, NETWORK_MASTER_SERVER_WELCOME_MESSAGE);
+ NetworkSend_uint8(p, NETWORK_MASTER_SERVER_VERSION);
+ NetworkSend_uint16(p, _network_server_port);
+ NetworkSendUDP_Packet(_udp_master_socket, p, &out_addr);
+
+ free(p);
+}
+
+void NetworkUDPInitialize(void)
+{
+ _udp_client_socket = INVALID_SOCKET;
+ _udp_server_socket = INVALID_SOCKET;
+ _udp_master_socket = INVALID_SOCKET;
+
+ _network_udp_server = false;
+ _network_udp_broadcast = 0;
+}
+
+#endif /* ENABLE_NETWORK */