--- a/network_gui.c Mon Dec 18 10:46:06 2006 +0000
+++ b/network_gui.c Mon Dec 18 12:26:55 2006 +0000
@@ -26,6 +26,7 @@
#include "settings.h"
#include "string.h"
#include "town.h"
+#include "newgrf.h"
#define BGC 5
#define BTC 15
@@ -241,6 +242,10 @@
sel->info.clients_on >= sel->info.clients_max || // Server full
!sel->info.compatible); // Revision mismatch
+ SetWindowWidgetHiddenState(w, 18, sel == NULL ||
+ !sel->online ||
+ sel->info.grfconfig == NULL);
+
SetDParam(0, 0x00);
SetDParam(7, _lan_internet_types_dropdown[_network_lan_internet]);
DrawWindowWidgets(w);
@@ -288,7 +293,7 @@
if (cur_item->info.use_password) DrawSprite(SPR_LOCK, w->widget[8].left + 5, y - 1);
// draw red or green icon, depending on compatibility with server.
- DrawSprite(SPR_BLOT | (cur_item->info.compatible ? PALETTE_TO_GREEN : PALETTE_TO_RED), w->widget[8].left + 15, y);
+ DrawSprite(SPR_BLOT | (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), w->widget[8].left + 15, y);
// draw flag according to server language
DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, w->widget[8].left + 25, y);
@@ -362,7 +367,7 @@
y += 2;
if (!sel->info.compatible) {
- DrawStringCentered(425, y, STR_NETWORK_VERSION_MISMATCH, 0); // server mismatch
+ DrawStringCentered(425, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, 0); // server mismatch
} else if (sel->info.clients_on == sel->info.clients_max) {
// Show: server full, when clients_on == clients_max
DrawStringCentered(425, y, STR_NETWORK_SERVER_FULL, 0); // server full
@@ -436,6 +441,9 @@
if (nd->server != NULL)
NetworkQueryServer(nd->server->info.hostname, nd->server->port, true);
break;
+ case 18: // NewGRF Settings
+ if (nd->server != NULL) ShowNewGRFSettings(false, false, &nd->server->info.grfconfig);
+ break;
} break;
@@ -496,7 +504,7 @@
static const Widget _network_game_window_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, BGC, 11, 549, 0, 13, STR_NETWORK_MULTIPLAYER, STR_NULL},
-{ WWT_PANEL, RESIZE_NONE, BGC, 0, 549, 14, 249, 0x0, STR_NULL},
+{ WWT_PANEL, RESIZE_NONE, BGC, 0, 549, 14, 261, 0x0, STR_NULL},
/* LEFT SIDE */
{ WWT_PANEL, RESIZE_NONE, BGC, 310, 461, 22, 33, 0x0, STR_NETWORK_ENTER_NAME_TIP},
@@ -508,26 +516,28 @@
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 171, 250, 42, 53, STR_NETWORK_CLIENTS_CAPTION, STR_NETWORK_CLIENTS_CAPTION_TIP},
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 251, 290, 42, 53, STR_EMPTY, STR_NETWORK_INFO_ICONS_TIP},
-{ WWT_MATRIX, RESIZE_NONE, BGC, 10, 290, 54, 222, (12 << 8) + 1, STR_NETWORK_CLICK_GAME_TO_SELECT},
-{ WWT_SCROLLBAR, RESIZE_NONE, BGC, 291, 302, 42, 222, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST},
+{ WWT_MATRIX, RESIZE_NONE, BGC, 10, 290, 54, 234, (12 << 8) + 1, STR_NETWORK_CLICK_GAME_TO_SELECT},
+{ WWT_SCROLLBAR, RESIZE_NONE, BGC, 291, 302, 42, 234, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 30, 130, 232, 243, STR_NETWORK_FIND_SERVER, STR_NETWORK_FIND_SERVER_TIP},
-{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 180, 280, 232, 243, STR_NETWORK_ADD_SERVER, STR_NETWORK_ADD_SERVER_TIP},
+{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 30, 130, 244, 255, STR_NETWORK_FIND_SERVER, STR_NETWORK_FIND_SERVER_TIP},
+{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 180, 280, 244, 255, STR_NETWORK_ADD_SERVER, STR_NETWORK_ADD_SERVER_TIP},
/* RIGHT SIDE */
-{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 315, 415, 232, 243, STR_NETWORK_START_SERVER, STR_NETWORK_START_SERVER_TIP},
-{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 430, 535, 232, 243, STR_012E_CANCEL, STR_NULL},
+{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 315, 415, 244, 255, STR_NETWORK_START_SERVER, STR_NETWORK_START_SERVER_TIP},
+{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 430, 535, 244, 255, STR_012E_CANCEL, STR_NULL},
-{ WWT_PANEL, RESIZE_NONE, BGC, 310, 540, 42, 222, 0x0, STR_NULL},
+{ WWT_PANEL, RESIZE_NONE, BGC, 310, 540, 42, 234, 0x0, STR_NULL},
-{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 315, 415, 201, 212, STR_NETWORK_JOIN_GAME, STR_NULL},
-{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 430, 535, 201, 212, STR_NETWORK_REFRESH, STR_NETWORK_REFRESH_TIP},
+{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 315, 415, 213, 224, STR_NETWORK_JOIN_GAME, STR_NULL},
+{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 430, 535, 213, 224, STR_NETWORK_REFRESH, STR_NETWORK_REFRESH_TIP},
+
+{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 430, 535, 195, 206, STR_NEWGRF_SETTINGS_BUTTON, STR_NULL},
{ WIDGETS_END},
};
static const WindowDesc _network_game_window_desc = {
- WDP_CENTER, WDP_CENTER, 550, 250,
+ WDP_CENTER, WDP_CENTER, 550, 262,
WC_NETWORK_WINDOW,0,
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
_network_game_window_widgets,
--- a/network_udp.c Mon Dec 18 10:46:06 2006 +0000
+++ b/network_udp.c Mon Dec 18 12:26:55 2006 +0000
@@ -11,6 +11,7 @@
#include "network_gamelist.h"
#include "network_udp.h"
#include "variables.h"
+#include "newgrf_config.h"
//
// This file handles all the LAN-stuff
@@ -28,6 +29,8 @@
PACKET_UDP_CLIENT_GET_LIST, // Request for serverlist from master server
PACKET_UDP_MASTER_RESPONSE_LIST, // Response from master server with server ip's + port's
PACKET_UDP_SERVER_UNREGISTER, // Request to be removed from the server-list
+ PACKET_UDP_CLIENT_GET_NEWGRFS, // Requests the name for a list of GRFs (GRF_ID and MD5)
+ PACKET_UDP_SERVER_NEWGRFS, // Sends the list of NewGRF's requested.
PACKET_UDP_END
} PacketUDPType;
@@ -42,6 +45,34 @@
static NetworkClientState _udp_cs;
+/**
+ * Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet
+ * @param p the packet to write the data to
+ * @param c the configuration to write the GRF ID and MD5 checksum from
+ */
+static void NetworkSend_GRFIdentifier(Packet *p, const GRFConfig *c)
+{
+ uint j;
+ NetworkSend_uint32(p, c->grfid);
+ for (j = 0; j < sizeof(c->md5sum); j++) {
+ NetworkSend_uint8 (p, c->md5sum[j]);
+ }
+}
+
+/**
+ * Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet
+ * @param p the packet to read the data from
+ * @param c the configuration to write the GRF ID and MD5 checksum to
+ */
+static void NetworkRecv_GRFIdentifier(Packet *p, GRFConfig *c)
+{
+ uint j;
+ c->grfid = NetworkRecv_uint32(&_udp_cs, p);
+ for (j = 0; j < sizeof(c->md5sum); j++) {
+ c->md5sum[j] = NetworkRecv_uint8(&_udp_cs, p);
+ }
+}
+
DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_FIND_SERVER)
{
Packet *packet;
@@ -59,6 +90,27 @@
NetworkSend_uint8 (packet, NETWORK_GAME_INFO_VERSION);
+ /* NETWORK_GAME_INFO_VERSION = 4 */
+ {
+ /* Only send the GRF Identification (GRF_ID and MD5 checksum) of
+ * the GRFs that are needed, i.e. the ones that the server has
+ * selected in the NewGRF GUI and not the ones that are used due
+ * to the fact that they are in [newgrf-static] in openttd.cfg */
+ const GRFConfig *c;
+ uint i = 0;
+
+ /* Count number of GRFs to send information about */
+ for (c = _grfconfig; c != NULL; c = c->next) {
+ if (!HASBIT(c->flags, GCF_STATIC)) i++;
+ }
+ NetworkSend_uint8 (packet, i); // Send number of GRFs
+
+ /* Send actual GRF Identifications */
+ for (c = _grfconfig; c != NULL; c = c->next) {
+ if (!HASBIT(c->flags, GCF_STATIC)) NetworkSend_GRFIdentifier(packet, c);
+ }
+ }
+
/* NETWORK_GAME_INFO_VERSION = 3 */
NetworkSend_uint32(packet, _network_game_info.game_date);
NetworkSend_uint32(packet, _network_game_info.start_date);
@@ -109,9 +161,41 @@
// Find next item
item = NetworkGameListAddItem(inet_addr(inet_ntoa(client_addr->sin_addr)), ntohs(client_addr->sin_port));
+ item->info.compatible = true;
/* Please observer the order. In the order in which packets are sent
* they are to be received */
switch (game_info_version) {
+ case 4: {
+ GRFConfig *c, **dst = &item->info.grfconfig;
+ const GRFConfig *f;
+ uint i;
+ uint num_grfs = NetworkRecv_uint8(&_udp_cs, p);
+
+ for (i = 0; i < num_grfs; i++) {
+ c = calloc(1, sizeof(*c));
+ NetworkRecv_GRFIdentifier(p, c);
+
+ /* Find the matching GRF file */
+ f = FindGRFConfig(c->grfid, c->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 */
+ item->info.compatible = false;
+ c->name = FindUnknownGRFName(c->grfid, c->md5sum, true);
+ SETBIT(c->flags, GCF_NOT_FOUND);
+ } else {
+ c->filename = f->filename;
+ c->name = f->name;
+ c->info = f->info;
+ }
+ SETBIT(c->flags, GCF_COPY);
+
+ /* Append GRFConfig to the list */
+ *dst = c;
+ dst = &c->next;
+ }
+ } /* Fallthrough */
case 3:
item->info.game_date = NetworkRecv_uint32(&_udp_cs, p);
item->info.start_date = NetworkRecv_uint32(&_udp_cs, p);
@@ -146,12 +230,50 @@
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.compatible =
+ 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
break;
}
+ {
+ /* 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) || 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);
+ }
+ }
+
item->online = true;
UpdateNetworkGameWindow(false);
@@ -300,6 +422,104 @@
}
}
+/**
+ * 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)("[NET][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(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 */
+ packet_len += sizeof(c.grfid) + sizeof(c.md5sum) + min(strlen(f->name) + 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];
+ ttd_strlcpy(name, in_reply[i]->name, 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)("[NET][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(p, &c);
+ NetworkRecv_string(&_udp_cs, p, name, sizeof(name));
+
+ /* 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);
+ }
+ }
+}
+
// The layout for the receive-functions by UDP
typedef void NetworkUDPPacket(Packet *p, struct sockaddr_in *client_addr);
@@ -313,7 +533,9 @@
RECEIVE_COMMAND(PACKET_UDP_MASTER_ACK_REGISTER),
NULL,
RECEIVE_COMMAND(PACKET_UDP_MASTER_RESPONSE_LIST),
- NULL
+ NULL,
+ RECEIVE_COMMAND(PACKET_UDP_CLIENT_GET_NEWGRFS),
+ RECEIVE_COMMAND(PACKET_UDP_SERVER_NEWGRFS),
};