diff -r d4d00a16ef26 -r bfa6074e2833 network/core/udp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/network/core/udp.c Wed Jan 03 08:32:17 2007 +0000 @@ -0,0 +1,277 @@ +/* $Id$ */ + +#ifdef ENABLE_NETWORK + +#include "../../stdafx.h" +#include "../../date.h" +#include "../../debug.h" +#include "../../macros.h" +#include "../../newgrf_config.h" + +#include "os_abstraction.h" +#include "config.h" +#include "game.h" +#include "packet.h" +#include "udp.h" + +/** + * @file udp.c Basic functions to receive and send UDP packets. + */ + +/** + * Send a packet over UDP + * @param udp the socket to send over + * @param p the packet to send + * @param recv the receiver (target) of the packet + */ +void NetworkSendUDP_Packet(SOCKET udp, Packet *p, struct sockaddr_in *recv) +{ + int res; + + NetworkSend_FillPacketSize(p); + + /* Send the buffer */ + res = sendto(udp, p->buffer, p->size, 0, (struct sockaddr *)recv, sizeof(*recv)); + + /* Check for any errors, but ignore it otherwise */ + if (res == -1) DEBUG(net, 1, "[udp] sendto failed with: %i", GET_LAST_ERROR()); +} + +/** + * Start listening on the given host and port. + * @param udp the place where the (references to the) UDP are stored + * @param host the host (ip) to listen on + * @param port the port to listen on + * @param broadcast whether to allow broadcast sending/receiving + * @return true if the listening succeeded + */ +bool NetworkUDPListen(SOCKET *udp, uint32 host, uint16 port, bool broadcast) +{ + struct sockaddr_in sin; + + /* Make sure socket is closed */ + closesocket(*udp); + + *udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (*udp == INVALID_SOCKET) { + DEBUG(net, 0, "[udp] failed to start UDP listener"); + return false; + } + + /* set nonblocking mode for socket */ + { + unsigned long blocking = 1; +#ifndef BEOS_NET_SERVER + ioctlsocket(*udp, FIONBIO, &blocking); +#else + setsockopt(*udp, SOL_SOCKET, SO_NONBLOCK, &blocking, NULL); +#endif + } + + sin.sin_family = AF_INET; + /* Listen on all IPs */ + sin.sin_addr.s_addr = host; + sin.sin_port = htons(port); + + if (bind(*udp, (struct sockaddr*)&sin, sizeof(sin)) != 0) { + DEBUG(net, 0, "[udp] bind failed on %s:%i", inet_ntoa(*(struct in_addr *)&host), port); + return false; + } + + if (broadcast) { + /* Enable broadcast */ + unsigned long val = 1; +#ifndef BEOS_NET_SERVER // will work around this, some day; maybe. + setsockopt(*udp, SOL_SOCKET, SO_BROADCAST, (char *) &val , sizeof(val)); +#endif + } + + DEBUG(net, 1, "[udp] listening on port %s:%d", inet_ntoa(*(struct in_addr *)&host), port); + + return true; +} + +/** + * Receive a packet at UDP level + * @param udp the socket to receive the packet on + */ +void NetworkUDPReceive(SOCKET udp) +{ + struct sockaddr_in client_addr; + socklen_t client_len; + int nbytes; + Packet p; + int packet_len; + + packet_len = sizeof(p.buffer); + client_len = sizeof(client_addr); + + /* Try to receive anything */ + nbytes = recvfrom(udp, p.buffer, packet_len, 0, (struct sockaddr *)&client_addr, &client_len); + + /* We got some bytes for the base header of the packet. + * Assume we received the whole packet. */ + if (nbytes > 2) { + NetworkRecv_ReadPacketSize(&p); + + /* Put the position on the right place */ + p.pos = 2; + p.next = NULL; + + /* Handle the packet */ + NetworkHandleUDPPacket(&p, &client_addr); + } +} + + +/** + * 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 + */ +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 cs the client state (for closing connect on out-of-bounds reading etc) + * @param p the packet to read the data from + * @param c the configuration to write the GRF ID and MD5 checksum to + */ +void NetworkRecv_GRFIdentifier(NetworkClientState *cs, Packet *p, GRFConfig *c) +{ + uint j; + c->grfid = NetworkRecv_uint32(cs, p); + for (j = 0; j < sizeof(c->md5sum); j++) { + c->md5sum[j] = NetworkRecv_uint8(cs, p); + } +} + + +/** + * Serializes the NetworkGameInfo struct to the packet + * @param p the packet to write the data to + * @param info the NetworkGameInfo struct to serialize + */ +void NetworkSend_NetworkGameInfo(Packet *p, const NetworkGameInfo *info) +{ + NetworkSend_uint8 (p, NETWORK_GAME_INFO_VERSION); + + /* + * Please observe the order. + * The parts must be read in the same order as they are sent! + */ + + + /* 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 count = 0; + + /* Count number of GRFs to send information about */ + for (c = info->grfconfig; c != NULL; c = c->next) { + if (!HASBIT(c->flags, GCF_STATIC)) count++; + } + NetworkSend_uint8 (p, count); // Send number of GRFs + + /* Send actual GRF Identifications */ + for (c = info->grfconfig; c != NULL; c = c->next) { + if (!HASBIT(c->flags, GCF_STATIC)) NetworkSend_GRFIdentifier(p, c); + } + } + + /* NETWORK_GAME_INFO_VERSION = 3 */ + NetworkSend_uint32(p, info->game_date); + NetworkSend_uint32(p, info->start_date); + + /* NETWORK_GAME_INFO_VERSION = 2 */ + NetworkSend_uint8 (p, info->companies_max); + NetworkSend_uint8 (p, info->companies_on); + NetworkSend_uint8 (p, info->spectators_max); + + /* NETWORK_GAME_INFO_VERSION = 1 */ + NetworkSend_string(p, info->server_name); + NetworkSend_string(p, info->server_revision); + NetworkSend_uint8 (p, info->server_lang); + NetworkSend_uint8 (p, info->use_password); + NetworkSend_uint8 (p, info->clients_max); + NetworkSend_uint8 (p, info->clients_on); + NetworkSend_uint8 (p, info->spectators_on); + NetworkSend_string(p, info->map_name); + NetworkSend_uint16(p, info->map_width); + NetworkSend_uint16(p, info->map_height); + NetworkSend_uint8 (p, info->map_set); + NetworkSend_uint8 (p, info->dedicated); +} + +/** + * Deserializes the NetworkGameInfo struct from the packet + * @param cs the client state (for closing connect on out-of-bounds reading etc) + * @param p the packet to read the data from + * @param info the NetworkGameInfo to deserialize into + */ +void NetworkRecv_NetworkGameInfo(NetworkClientState *cs, Packet *p, NetworkGameInfo *info) +{ + info->game_info_version = NetworkRecv_uint8(cs, p); + + /* + * Please observe the order. + * The parts must be read in the same order as they are sent! + */ + + switch (info->game_info_version) { + case 4: { + GRFConfig *c, **dst = &info->grfconfig; + uint i; + uint num_grfs = NetworkRecv_uint8(cs, p); + + for (i = 0; i < num_grfs; i++) { + c = calloc(1, sizeof(*c)); + NetworkRecv_GRFIdentifier(cs, p, c); + HandleIncomingNetworkGameInfoGRFConfig(c); + + /* Append GRFConfig to the list */ + *dst = c; + dst = &c->next; + } + } /* Fallthrough */ + case 3: + info->game_date = NetworkRecv_uint32(cs, p); + info->start_date = NetworkRecv_uint32(cs, p); + /* Fallthrough */ + case 2: + info->companies_max = NetworkRecv_uint8 (cs, p); + info->companies_on = NetworkRecv_uint8 (cs, p); + info->spectators_max = NetworkRecv_uint8 (cs, p); + /* Fallthrough */ + case 1: + NetworkRecv_string(cs, p, info->server_name, sizeof(info->server_name)); + NetworkRecv_string(cs, p, info->server_revision, sizeof(info->server_revision)); + info->server_lang = NetworkRecv_uint8 (cs, p); + info->use_password = NetworkRecv_uint8 (cs, p); + info->clients_max = NetworkRecv_uint8 (cs, p); + info->clients_on = NetworkRecv_uint8 (cs, p); + info->spectators_on = NetworkRecv_uint8 (cs, p); + if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier + info->game_date = NetworkRecv_uint16(cs, p) + DAYS_TILL_ORIGINAL_BASE_YEAR; + info->start_date = NetworkRecv_uint16(cs, p) + DAYS_TILL_ORIGINAL_BASE_YEAR; + } + NetworkRecv_string(cs, p, info->map_name, sizeof(info->map_name)); + info->map_width = NetworkRecv_uint16(cs, p); + info->map_height = NetworkRecv_uint16(cs, p); + info->map_set = NetworkRecv_uint8 (cs, p); + info->dedicated = NetworkRecv_uint8 (cs, p); + } +} + +#endif /* ENABLE_NETWORK */