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