network_data.c
branchcustombridgeheads
changeset 5642 bfa6074e2833
parent 5641 d4d00a16ef26
child 5643 3778051e8095
equal deleted inserted replaced
5641:d4d00a16ef26 5642:bfa6074e2833
     1 /* $Id$ */
       
     2 
       
     3 #ifdef ENABLE_NETWORK
       
     4 
       
     5 #include "stdafx.h"
       
     6 #include "debug.h"
       
     7 #include "network_data.h"
       
     8 #include "functions.h"
       
     9 #include "string.h"
       
    10 #include "table/strings.h"
       
    11 #include "network_client.h"
       
    12 #include "command.h"
       
    13 #include "callback_table.h"
       
    14 #include "variables.h"
       
    15 
       
    16 // This files handles the send/receive of all packets
       
    17 
       
    18 // Create a packet for sending
       
    19 Packet *NetworkSend_Init(PacketType type)
       
    20 {
       
    21 	Packet *packet = malloc(sizeof(Packet));
       
    22 	// An error is inplace here, because it simply means we ran out of memory.
       
    23 	if (packet == NULL) error("Failed to allocate Packet");
       
    24 
       
    25 	// Skip the size so we can write that in before sending the packet
       
    26 	packet->size = sizeof(packet->size);
       
    27 	packet->buffer[packet->size++] = type;
       
    28 	packet->pos = 0;
       
    29 
       
    30 	return packet;
       
    31 }
       
    32 
       
    33 // The next couple of functions make sure we can send
       
    34 //  uint8, uint16, uint32 and uint64 endian-safe
       
    35 //  over the network. The order it uses is:
       
    36 //
       
    37 //  1 2 3 4
       
    38 //
       
    39 
       
    40 void NetworkSend_uint8(Packet *packet, uint8 data)
       
    41 {
       
    42 	assert(packet->size < sizeof(packet->buffer) - sizeof(data));
       
    43 	packet->buffer[packet->size++] = data;
       
    44 }
       
    45 
       
    46 void NetworkSend_uint16(Packet *packet, uint16 data)
       
    47 {
       
    48 	assert(packet->size < sizeof(packet->buffer) - sizeof(data));
       
    49 	packet->buffer[packet->size++] = GB(data, 0, 8);
       
    50 	packet->buffer[packet->size++] = GB(data, 8, 8);
       
    51 }
       
    52 
       
    53 void NetworkSend_uint32(Packet *packet, uint32 data)
       
    54 {
       
    55 	assert(packet->size < sizeof(packet->buffer) - sizeof(data));
       
    56 	packet->buffer[packet->size++] = GB(data,  0, 8);
       
    57 	packet->buffer[packet->size++] = GB(data,  8, 8);
       
    58 	packet->buffer[packet->size++] = GB(data, 16, 8);
       
    59 	packet->buffer[packet->size++] = GB(data, 24, 8);
       
    60 }
       
    61 
       
    62 void NetworkSend_uint64(Packet *packet, uint64 data)
       
    63 {
       
    64 	assert(packet->size < sizeof(packet->buffer) - sizeof(data));
       
    65 	packet->buffer[packet->size++] = GB(data,  0, 8);
       
    66 	packet->buffer[packet->size++] = GB(data,  8, 8);
       
    67 	packet->buffer[packet->size++] = GB(data, 16, 8);
       
    68 	packet->buffer[packet->size++] = GB(data, 24, 8);
       
    69 	packet->buffer[packet->size++] = GB(data, 32, 8);
       
    70 	packet->buffer[packet->size++] = GB(data, 40, 8);
       
    71 	packet->buffer[packet->size++] = GB(data, 48, 8);
       
    72 	packet->buffer[packet->size++] = GB(data, 56, 8);
       
    73 }
       
    74 
       
    75 // Sends a string over the network. It sends out
       
    76 //  the string + '\0'. No size-byte or something.
       
    77 void NetworkSend_string(Packet *packet, const char* data)
       
    78 {
       
    79 	assert(data != NULL);
       
    80 	assert(packet->size < sizeof(packet->buffer) - strlen(data) - 1);
       
    81 	while ((packet->buffer[packet->size++] = *data++) != '\0') {}
       
    82 }
       
    83 
       
    84 // If PacketSize changes of size, you have to change the 2 packet->size
       
    85 //   lines below matching the size of packet->size/PacketSize!
       
    86 // (line 'packet->buffer[0] = packet->size & 0xFF;'  and below)
       
    87 assert_compile(sizeof(PacketSize) == 2);
       
    88 
       
    89 // This function puts the packet in the send-queue and it is send
       
    90 //  as soon as possible
       
    91 // (that is: the next tick, or maybe one tick later if the
       
    92 //   OS-network-buffer is full)
       
    93 void NetworkSend_Packet(Packet *packet, NetworkClientState *cs)
       
    94 {
       
    95 	Packet *p;
       
    96 	assert(packet != NULL);
       
    97 
       
    98 	packet->pos = 0;
       
    99 	packet->next = NULL;
       
   100 
       
   101 	packet->buffer[0] = GB(packet->size, 0, 8);
       
   102 	packet->buffer[1] = GB(packet->size, 8, 8);
       
   103 
       
   104 	// Locate last packet buffered for the client
       
   105 	p = cs->packet_queue;
       
   106 	if (p == NULL) {
       
   107 		// No packets yet
       
   108 		cs->packet_queue = packet;
       
   109 	} else {
       
   110 		// Skip to the last packet
       
   111 		while (p->next != NULL) p = p->next;
       
   112 		p->next = packet;
       
   113 	}
       
   114 }
       
   115 
       
   116 // Functions to help NetworkRecv_Packet/NetworkSend_Packet a bit
       
   117 //  A socket can make errors. When that happens
       
   118 //  this handles what to do.
       
   119 // For clients: close connection and drop back to main-menu
       
   120 // For servers: close connection and that is it
       
   121 static NetworkRecvStatus CloseConnection(NetworkClientState *cs)
       
   122 {
       
   123 	NetworkCloseClient(cs);
       
   124 
       
   125 	// Clients drop back to the main menu
       
   126 	if (!_network_server && _networking) {
       
   127 		_switch_mode = SM_MENU;
       
   128 		_networking = false;
       
   129 		_switch_mode_errorstr = STR_NETWORK_ERR_LOSTCONNECTION;
       
   130 
       
   131 		return NETWORK_RECV_STATUS_CONN_LOST;
       
   132 	}
       
   133 
       
   134 	return NETWORK_RECV_STATUS_OKAY;
       
   135 }
       
   136 
       
   137 // Sends all the buffered packets out for this client
       
   138 //  it stops when:
       
   139 //   1) all packets are send (queue is empty)
       
   140 //   2) the OS reports back that it can not send any more
       
   141 //        data right now (full network-buffer, it happens ;))
       
   142 //   3) sending took too long
       
   143 bool NetworkSend_Packets(NetworkClientState *cs)
       
   144 {
       
   145 	ssize_t res;
       
   146 	Packet *p;
       
   147 
       
   148 	// We can not write to this socket!!
       
   149 	if (!cs->writable) return false;
       
   150 	if (cs->socket == INVALID_SOCKET) return false;
       
   151 
       
   152 	p = cs->packet_queue;
       
   153 	while (p != NULL) {
       
   154 		res = send(cs->socket, p->buffer + p->pos, p->size - p->pos, 0);
       
   155 		if (res == -1) {
       
   156 			int err = GET_LAST_ERROR();
       
   157 			if (err != EWOULDBLOCK) { // Something went wrong.. close client!
       
   158 				DEBUG(net, 0, "send failed with error %d", err);
       
   159 				CloseConnection(cs);
       
   160 				return false;
       
   161 			}
       
   162 			return true;
       
   163 		}
       
   164 		if (res == 0) {
       
   165 			// Client/server has left us :(
       
   166 			CloseConnection(cs);
       
   167 			return false;
       
   168 		}
       
   169 
       
   170 		p->pos += res;
       
   171 
       
   172 		// Is this packet sent?
       
   173 		if (p->pos == p->size) {
       
   174 			// Go to the next packet
       
   175 			cs->packet_queue = p->next;
       
   176 			free(p);
       
   177 			p = cs->packet_queue;
       
   178 		} else {
       
   179 			return true;
       
   180 		}
       
   181 	}
       
   182 
       
   183 	return true;
       
   184 }
       
   185 
       
   186 
       
   187 // Receiving commands
       
   188 // Again, the next couple of functions are endian-safe
       
   189 //  see the comment around NetworkSend_uint8 for more info.
       
   190 uint8 NetworkRecv_uint8(NetworkClientState *cs, Packet *packet)
       
   191 {
       
   192 	/* Don't allow reading from a closed socket */
       
   193 	if (cs->has_quit) return 0;
       
   194 
       
   195 	/* Check if variable is within packet-size */
       
   196 	if (packet->pos + 1 > packet->size) {
       
   197 		CloseConnection(cs);
       
   198 		return 0;
       
   199 	}
       
   200 
       
   201 	return packet->buffer[packet->pos++];
       
   202 }
       
   203 
       
   204 uint16 NetworkRecv_uint16(NetworkClientState *cs, Packet *packet)
       
   205 {
       
   206 	uint16 n;
       
   207 
       
   208 	/* Don't allow reading from a closed socket */
       
   209 	if (cs->has_quit) return 0;
       
   210 
       
   211 	/* Check if variable is within packet-size */
       
   212 	if (packet->pos + 2 > packet->size) {
       
   213 		CloseConnection(cs);
       
   214 		return 0;
       
   215 	}
       
   216 
       
   217 	n  = (uint16)packet->buffer[packet->pos++];
       
   218 	n += (uint16)packet->buffer[packet->pos++] << 8;
       
   219 	return n;
       
   220 }
       
   221 
       
   222 uint32 NetworkRecv_uint32(NetworkClientState *cs, Packet *packet)
       
   223 {
       
   224 	uint32 n;
       
   225 
       
   226 	/* Don't allow reading from a closed socket */
       
   227 	if (cs->has_quit) return 0;
       
   228 
       
   229 	/* Check if variable is within packet-size */
       
   230 	if (packet->pos + 4 > packet->size) {
       
   231 		CloseConnection(cs);
       
   232 		return 0;
       
   233 	}
       
   234 
       
   235 	n  = (uint32)packet->buffer[packet->pos++];
       
   236 	n += (uint32)packet->buffer[packet->pos++] << 8;
       
   237 	n += (uint32)packet->buffer[packet->pos++] << 16;
       
   238 	n += (uint32)packet->buffer[packet->pos++] << 24;
       
   239 	return n;
       
   240 }
       
   241 
       
   242 uint64 NetworkRecv_uint64(NetworkClientState *cs, Packet *packet)
       
   243 {
       
   244 	uint64 n;
       
   245 
       
   246 	/* Don't allow reading from a closed socket */
       
   247 	if (cs->has_quit) return 0;
       
   248 
       
   249 	/* Check if variable is within packet-size */
       
   250 	if (packet->pos + 8 > packet->size) {
       
   251 		CloseConnection(cs);
       
   252 		return 0;
       
   253 	}
       
   254 
       
   255 	n  = (uint64)packet->buffer[packet->pos++];
       
   256 	n += (uint64)packet->buffer[packet->pos++] << 8;
       
   257 	n += (uint64)packet->buffer[packet->pos++] << 16;
       
   258 	n += (uint64)packet->buffer[packet->pos++] << 24;
       
   259 	n += (uint64)packet->buffer[packet->pos++] << 32;
       
   260 	n += (uint64)packet->buffer[packet->pos++] << 40;
       
   261 	n += (uint64)packet->buffer[packet->pos++] << 48;
       
   262 	n += (uint64)packet->buffer[packet->pos++] << 56;
       
   263 	return n;
       
   264 }
       
   265 
       
   266 // Reads a string till it finds a '\0' in the stream
       
   267 void NetworkRecv_string(NetworkClientState *cs, Packet *p, char *buffer, size_t size)
       
   268 {
       
   269 	PacketSize pos;
       
   270 	char *bufp = buffer;
       
   271 
       
   272 	/* Don't allow reading from a closed socket */
       
   273 	if (cs->has_quit) return;
       
   274 
       
   275 	pos = p->pos;
       
   276 	while (--size > 0 && pos < p->size && (*buffer++ = p->buffer[pos++]) != '\0') {}
       
   277 
       
   278 	if (size == 0 || pos == p->size) {
       
   279 		*buffer = '\0';
       
   280 		// If size was sooner to zero then the string in the stream
       
   281 		//  skip till the \0, so the packet can be read out correctly for the rest
       
   282 		while (pos < p->size && p->buffer[pos] != '\0') pos++;
       
   283 		pos++;
       
   284 	}
       
   285 	p->pos = pos;
       
   286 
       
   287 	str_validate(bufp);
       
   288 }
       
   289 
       
   290 // If PacketSize changes of size, you have to change the 2 packet->size
       
   291 //   lines below matching the size of packet->size/PacketSize!
       
   292 // (the line: 'p->size = (uint16)p->buffer[0];' and below)
       
   293 assert_compile(sizeof(PacketSize) == 2);
       
   294 
       
   295 Packet *NetworkRecv_Packet(NetworkClientState *cs, NetworkRecvStatus *status)
       
   296 {
       
   297 	ssize_t res;
       
   298 	Packet *p;
       
   299 
       
   300 	*status = NETWORK_RECV_STATUS_OKAY;
       
   301 
       
   302 	if (cs->socket == INVALID_SOCKET) return NULL;
       
   303 
       
   304 	if (cs->packet_recv == NULL) {
       
   305 		cs->packet_recv = malloc(sizeof(Packet));
       
   306 		if (cs->packet_recv == NULL) error("Failed to allocate packet");
       
   307 		// Set pos to zero!
       
   308 		cs->packet_recv->pos = 0;
       
   309 		cs->packet_recv->size = 0; // Can be ommited, just for safety reasons
       
   310 	}
       
   311 
       
   312 	p = cs->packet_recv;
       
   313 
       
   314 	// Read packet size
       
   315 	if (p->pos < sizeof(PacketSize)) {
       
   316 		while (p->pos < sizeof(PacketSize)) {
       
   317 			// Read the size of the packet
       
   318 			res = recv(cs->socket, p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0);
       
   319 			if (res == -1) {
       
   320 				int err = GET_LAST_ERROR();
       
   321 				if (err != EWOULDBLOCK) {
       
   322 					/* Something went wrong... (104 is connection reset by peer) */
       
   323 					if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
       
   324 					*status = CloseConnection(cs);
       
   325 					return NULL;
       
   326 				}
       
   327 				// Connection would block, so stop for now
       
   328 				return NULL;
       
   329 			}
       
   330 			if (res == 0) {
       
   331 				// Client/server has left
       
   332 				*status = CloseConnection(cs);
       
   333 				return NULL;
       
   334 			}
       
   335 			p->pos += res;
       
   336 		}
       
   337 
       
   338 		p->size = (uint16)p->buffer[0];
       
   339 		p->size += (uint16)p->buffer[1] << 8;
       
   340 
       
   341 		if (p->size > SEND_MTU) {
       
   342 			*status = CloseConnection(cs);
       
   343 			return NULL;
       
   344 		}
       
   345 	}
       
   346 
       
   347 	// Read rest of packet
       
   348 	while (p->pos < p->size) {
       
   349 		res = recv(cs->socket, p->buffer + p->pos, p->size - p->pos, 0);
       
   350 		if (res == -1) {
       
   351 			int err = GET_LAST_ERROR();
       
   352 			if (err != EWOULDBLOCK) {
       
   353 				/* Something went wrong... (104 is connection reset by peer) */
       
   354 				if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
       
   355 				*status = CloseConnection(cs);
       
   356 				return NULL;
       
   357 			}
       
   358 			// Connection would block
       
   359 			return NULL;
       
   360 		}
       
   361 		if (res == 0) {
       
   362 			// Client/server has left
       
   363 			*status = CloseConnection(cs);
       
   364 			return NULL;
       
   365 		}
       
   366 
       
   367 		p->pos += res;
       
   368 	}
       
   369 
       
   370 	// We have a complete packet, return it!
       
   371 	p->pos = 2;
       
   372 	p->next = NULL; // Should not be needed, but who knows...
       
   373 
       
   374 	// Prepare for receiving a new packet
       
   375 	cs->packet_recv = NULL;
       
   376 
       
   377 	return p;
       
   378 }
       
   379 
       
   380 // Add a command to the local command queue
       
   381 void NetworkAddCommandQueue(NetworkClientState *cs, CommandPacket *cp)
       
   382 {
       
   383 	CommandPacket* new_cp = malloc(sizeof(*new_cp));
       
   384 
       
   385 	*new_cp = *cp;
       
   386 
       
   387 	if (cs->command_queue == NULL) {
       
   388 		cs->command_queue = new_cp;
       
   389 	} else {
       
   390 		CommandPacket *c = cs->command_queue;
       
   391 		while (c->next != NULL) c = c->next;
       
   392 		c->next = new_cp;
       
   393 	}
       
   394 }
       
   395 
       
   396 // Prepare a DoCommand to be send over the network
       
   397 void NetworkSend_Command(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback)
       
   398 {
       
   399 	CommandPacket *c = malloc(sizeof(CommandPacket));
       
   400 	byte temp_callback;
       
   401 
       
   402 	c->player = _local_player;
       
   403 	c->next = NULL;
       
   404 	c->tile = tile;
       
   405 	c->p1 = p1;
       
   406 	c->p2 = p2;
       
   407 	c->cmd = cmd;
       
   408 	c->callback = 0;
       
   409 
       
   410 	temp_callback = 0;
       
   411 
       
   412 	while (temp_callback < _callback_table_count && _callback_table[temp_callback] != callback)
       
   413 		temp_callback++;
       
   414 	if (temp_callback == _callback_table_count) {
       
   415 		DEBUG(net, 0, "Unknown callback. (Pointer: %p) No callback sent", callback);
       
   416 		temp_callback = 0; /* _callback_table[0] == NULL */
       
   417 	}
       
   418 
       
   419 	if (_network_server) {
       
   420 		// We are the server, so set the command to be executed next possible frame
       
   421 		c->frame = _frame_counter_max + 1;
       
   422 	} else {
       
   423 		c->frame = 0; // The client can't tell which frame, so just make it 0
       
   424 	}
       
   425 
       
   426 	ttd_strlcpy(c->text, (_cmd_text != NULL) ? _cmd_text : "", lengthof(c->text));
       
   427 
       
   428 	if (_network_server) {
       
   429 		// If we are the server, we queue the command in our 'special' queue.
       
   430 		//   In theory, we could execute the command right away, but then the
       
   431 		//   client on the server can do everything 1 tick faster than others.
       
   432 		//   So to keep the game fair, we delay the command with 1 tick
       
   433 		//   which gives about the same speed as most clients.
       
   434 		NetworkClientState *cs;
       
   435 
       
   436 		// And we queue it for delivery to the clients
       
   437 		FOR_ALL_CLIENTS(cs) {
       
   438 			if (cs->status > STATUS_AUTH) NetworkAddCommandQueue(cs, c);
       
   439 		}
       
   440 
       
   441 		// Only the server gets the callback, because clients should not get them
       
   442 		c->callback = temp_callback;
       
   443 		if (_local_command_queue == NULL) {
       
   444 			_local_command_queue = c;
       
   445 		} else {
       
   446 			// Find last packet
       
   447 			CommandPacket *cp = _local_command_queue;
       
   448 			while (cp->next != NULL) cp = cp->next;
       
   449 			cp->next = c;
       
   450 		}
       
   451 
       
   452 		return;
       
   453 	}
       
   454 
       
   455 	// Clients send their command to the server and forget all about the packet
       
   456 	c->callback = temp_callback;
       
   457 	SEND_COMMAND(PACKET_CLIENT_COMMAND)(c);
       
   458 }
       
   459 
       
   460 // Execute a DoCommand we received from the network
       
   461 void NetworkExecuteCommand(CommandPacket *cp)
       
   462 {
       
   463 	_current_player = cp->player;
       
   464 	_cmd_text = cp->text;
       
   465 	/* cp->callback is unsigned. so we don't need to do lower bounds checking. */
       
   466 	if (cp->callback > _callback_table_count) {
       
   467 		DEBUG(net, 0, "Received out-of-bounds callback (%d)", cp->callback);
       
   468 		cp->callback = 0;
       
   469 	}
       
   470 	DoCommandP(cp->tile, cp->p1, cp->p2, _callback_table[cp->callback], cp->cmd | CMD_NETWORK_COMMAND);
       
   471 }
       
   472 
       
   473 #endif /* ENABLE_NETWORK */