diff options
author | signde <signde@openttd.org> | 2004-09-11 19:34:11 +0000 |
---|---|---|
committer | signde <signde@openttd.org> | 2004-09-11 19:34:11 +0000 |
commit | 1fb915df69aaa77ec8be39bb96650e5ec568e245 (patch) | |
tree | a7f24d905e8470f6331b816f08ef922fb43b8532 /network.c | |
parent | d03afadad23fced4bbb11df12a01f26cdbaf6844 (diff) | |
download | openttd-1fb915df69aaa77ec8be39bb96650e5ec568e245.tar.xz |
(svn r207) -Codechange: randomizer handling
-Fix: desync problem fixes
-Fix: server doesnt hang anymore when a client timed out
-Feature: low latency connection enhancements [*net_sync_freq, *net_ready_ahead]
Diffstat (limited to 'network.c')
-rw-r--r-- | network.c | 279 |
1 files changed, 176 insertions, 103 deletions
@@ -79,6 +79,15 @@ struct timerequest *TimerRequest = NULL; #if defined(ENABLE_NETWORK) +enum { + PACKET_TYPE_WELCOME = 0, + PACKET_TYPE_READY, + PACKET_TYPE_ACK, + PACKET_TYPE_SYNC, + PACKET_TYPE_XMIT, + PACKET_TYPE_COMMAND, +}; + // sent from client -> server whenever the client wants to exec a command. // send from server -> client when another player execs a command. typedef struct CommandPacket { @@ -92,8 +101,6 @@ typedef struct CommandPacket { uint32 dp[8]; } CommandPacket; -assert_compile(sizeof(CommandPacket) == 16 + 32); - #define COMMAND_PACKET_BASE_SIZE (sizeof(CommandPacket) - 8 * sizeof(uint32)) // sent from server -> client periodically to tell the client about the current tick in the server @@ -107,13 +114,12 @@ typedef struct SyncPacket { uint32 random_seed_2; } SyncPacket; -assert_compile(sizeof(SyncPacket) == 12); - // sent from server -> client as an acknowledgement that the server received the command. // the command will be executed at the current value of "max". typedef struct AckPacket { byte packet_length; byte packet_type; + byte when; } AckPacket; typedef struct ReadyPacket { @@ -124,16 +130,16 @@ typedef struct ReadyPacket { typedef struct FilePacketHdr { byte packet_length; byte packet_type; - byte unused[2]; } FilePacketHdr; -assert_compile(sizeof(FilePacketHdr) == 4); - // sent from server to client when the client has joined. typedef struct WelcomePacket { byte packet_length; byte packet_type; - byte unused[2]; + uint32 player_seeds[MAX_PLAYERS][2]; + uint32 frames_max; + uint32 frames_srv; + uint32 frames_cnt; } WelcomePacket; typedef struct Packet Packet; @@ -155,19 +161,17 @@ typedef struct ClientState { Packet *head, **last; uint buflen; // receive buffer len - byte buf[256]; // receive buffer + byte buf[1024]; // receive buffer } ClientState; -static uint _not_packet; - typedef struct QueuedCommand QueuedCommand; struct QueuedCommand { QueuedCommand *next; CommandPacket cp; CommandCallback *callback; uint32 cmd; - int32 frame; + uint32 frame; }; typedef struct CommandQueue CommandQueue; @@ -195,7 +199,7 @@ static uint16 _network_ready_ahead = 1; static uint16 _network_client_timeout; typedef struct FutureSeeds { - int32 frame; + uint32 frame; uint32 seed[2]; } FutureSeeds; @@ -223,6 +227,8 @@ enum { }; void NetworkUDPSend(bool client, struct sockaddr_in recv,struct UDPPacket packet); +static void CloseClient(ClientState *cs); +void NetworkSendWelcome(ClientState *cs, bool direct); uint32 _network_ip_list[10]; // network ip list @@ -340,6 +346,16 @@ static QueuedCommand *AllocQueuedCommand(CommandQueue *nq) return qp; } +static void QueueClear(CommandQueue *nq) { + QueuedCommand *qp; + while ((qp=nq->head)) { + // unlink it. + if (!(nq->head = qp->next)) nq->last = &nq->head; + free(qp); + } + nq->last = &nq->head; +} + // go through the player queues for each player and see if there are any pending commands // that should be executed this frame. if there are, execute them. void NetworkProcessCommands() @@ -416,6 +432,20 @@ static void SendBytes(ClientState *cs, void *bytes, uint len) } while (len -= n); } +// send data direct to a client +static void SendDirectBytes(ClientState *cs, void *bytes, uint len) +{ + char *buf = (char*)bytes; + uint n; + + n = send(cs->socket, buf, len, 0); + if (n == -1) { + int err = GET_LAST_ERROR(); + DEBUG(net, 0) ("[NET] send() failed with error %d", err); + CloseClient(cs); + } +} + // client: // add it to the client's ack queue, and send the command to the server // server: @@ -427,7 +457,7 @@ void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, Comman ClientState *cs; qp = AllocQueuedCommand(_networking_server ? &_command_queue : &_ack_queue); - qp->cp.packet_type = 0; + qp->cp.packet_type = PACKET_TYPE_COMMAND; qp->cp.tile = tile; qp->cp.p1 = p1; qp->cp.p2 = p2; @@ -438,7 +468,7 @@ void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, Comman qp->callback = callback; // so the server knows when to execute it. - qp->frame = _frame_counter_max; + qp->frame = _frame_counter + 5; // calculate the amount of extra bytes. nump = 8; @@ -480,6 +510,7 @@ static void HandleCommandPacket(ClientState *cs, CommandPacket *np) QueuedCommand *qp; ClientState *c; AckPacket ap; + int i; DEBUG(net, 2) ("[NET] cmd size %d", np->packet_length); @@ -489,21 +520,28 @@ static void HandleCommandPacket(ClientState *cs, CommandPacket *np) qp = AllocQueuedCommand(&_command_queue); qp->cp = *np; - qp->frame = _frame_counter_max; + i = _frame_counter_max - (_frame_counter + 3); + + if (i<0) { + qp->frame = _frame_counter_max ; + } else { + qp->frame = _frame_counter + 3; + } qp->callback = NULL; // extra params memcpy(&qp->cp.dp, np->dp, np->packet_length - COMMAND_PACKET_BASE_SIZE); - ap.packet_type = 2; - ap.packet_length = 2; + ap.packet_type = PACKET_TYPE_ACK; + ap.when = _frame_counter_max-(qp->frame); + ap.packet_length = sizeof(AckPacket); // send it to the peers if (_networking_server) { for(c=_clients; c->socket != INVALID_SOCKET; c++) { if (c == cs) { - SendBytes(c, &ap, 2); + SendDirectBytes(c, &ap, ap.packet_length); } else { if (!cs->inactive) SendBytes(c, &qp->cp, qp->cp.packet_length); } @@ -540,7 +578,7 @@ static void HandleSyncPacket(SyncPacket *sp) s1 = TO_LE32(sp->random_seed_1); s2 = TO_LE32(sp->random_seed_2); - DEBUG(net, 3) ("[NET] sync seeds: [1]=%i rnd[2]=%i", sp->random_seed_1, sp->random_seed_2); + DEBUG(net, 3) ("[NET] sync seeds: frame=%i 1=%i 2=%i",_frame_counter, sp->random_seed_1, sp->random_seed_2); if (_frame_counter_srv <= _frame_counter) { // we are ahead of the server check if the seed is in our list. @@ -561,7 +599,7 @@ static void HandleSyncPacket(SyncPacket *sp) // sent from server -> client as an acknowledgement that the server received the command. // the command will be executed at the current value of "max". -static void HandleAckPacket() +static void HandleAckPacket(AckPacket * ap) { QueuedCommand *q; // move a packet from the ack queue to the end of this player's queue. @@ -569,12 +607,12 @@ static void HandleAckPacket() assert(q); if (!(_ack_queue.head = q->next)) _ack_queue.last = &_ack_queue.head; q->next = NULL; - q->frame = _frame_counter_max; + q->frame = (_frame_counter_max - (ap->when)); *_command_queue.last = q; _command_queue.last = &q->next; - DEBUG(net, 2) ("[NET] ack"); + DEBUG(net, 2) ("[NET] ack [frame=%i]",q->frame); } static void HandleFilePacket(FilePacketHdr *fp) @@ -619,6 +657,28 @@ static void HandleFilePacket(FilePacketHdr *fp) } } +static void HandleWelcomePacket(WelcomePacket *wp) { + int i; + for (i=0; i<MAX_PLAYERS; i++) { + _player_seeds[i][0]=wp->player_seeds[i][0]; + _player_seeds[i][1]=wp->player_seeds[i][1]; + } + if (wp->frames_srv != 0) { + _frame_counter_max = wp->frames_max; + _frame_counter_srv = wp->frames_srv; + } + if (wp->frames_cnt != 0) { + _frame_counter = wp->frames_cnt; + } +} + +static void HandleReadyPacket(ReadyPacket *rp, ClientState *cs) +{ + cs->ready=true; + cs->timeout=_network_client_timeout; +} + + static void CloseClient(ClientState *cs) { Packet *p, *next; @@ -680,24 +740,26 @@ static bool ReadPackets(ClientState *cs) size -= packet[0]; pos += packet[0]; switch(packet[1]) { - case 0: + case PACKET_TYPE_WELCOME: + HandleWelcomePacket((WelcomePacket *)packet); + break; + case PACKET_TYPE_COMMAND: HandleCommandPacket(cs, (CommandPacket*)packet); break; - case 1: + case PACKET_TYPE_SYNC: assert(_networking_sync || _networking_queuing); assert(!_networking_server); HandleSyncPacket((SyncPacket*)packet); break; - case 2: + case PACKET_TYPE_ACK: assert(!_networking_server); - HandleAckPacket(); + HandleAckPacket((AckPacket*)packet); break; - case 3: + case PACKET_TYPE_XMIT: HandleFilePacket((FilePacketHdr*)packet); break; - case 5: - cs->ready=true; - cs->timeout=_network_client_timeout; + case PACKET_TYPE_READY: + HandleReadyPacket((ReadyPacket*)packet, cs); break; default: DEBUG (net,0) ("net: unknown packet type"); @@ -783,8 +845,7 @@ static void SendXmit(ClientState *cs) n = minu(_transmit_file_size - pos, 248); hdr.packet_length = n + sizeof(hdr); - hdr.packet_type = 3; - hdr.unused[0] = hdr.unused[1] = 0; + hdr.packet_type = PACKET_TYPE_XMIT; SendBytes(cs, &hdr, sizeof(hdr)); if (n == 0) { @@ -797,6 +858,10 @@ static void SendXmit(ClientState *cs) cs->xmitpos = pos + 1; + if (cs->xmitpos == 0) { + NetworkSendWelcome(cs,false); + } + DEBUG(net, 2) ("[NET] client xmit at %d", pos + 1); } @@ -819,17 +884,65 @@ static ClientState *AllocClient(SOCKET s) void NetworkSendReadyPacket() { - if (!_network_ready_sent) { + if ((!_network_ready_sent) && (_frame_counter + _network_ready_ahead >= _frame_counter_max)) { ReadyPacket *rp = malloc(sizeof(rp)); ClientState *c = _clients; - rp->packet_type = 5; + rp->packet_type = PACKET_TYPE_READY; rp->packet_length = sizeof(rp); SendBytes(c, rp, sizeof(rp)); _network_ready_sent = true; } } +void NetworkSendSyncPackets() +{ + ClientState *cs; + uint32 new_max; + SyncPacket sp; + + new_max = _frame_counter + (int)_network_sync_freq; + + DEBUG(net,3) ("net: serv: sync frame=%i,max=%i, seed1=%i, seed2=%i",new_max,_sync_seed_1,_sync_seed_2); + + sp.packet_length = sizeof(sp); + sp.packet_type = PACKET_TYPE_SYNC; + sp.frames = new_max - _frame_counter_max; + sp.server = _frame_counter_max - _frame_counter; + sp.random_seed_1 = TO_LE32(_sync_seed_1); + sp.random_seed_2 = TO_LE32(_sync_seed_2); + _frame_counter_max = new_max; + + // send it to all the clients and mark them unready + for(cs=_clients;cs->socket != INVALID_SOCKET; cs++) { + cs->ready=false; + SendBytes(cs, &sp, sizeof(sp)); + } + +} + +void NetworkSendWelcome(ClientState *cs, bool direct) { + WelcomePacket wp; + int i; + wp.packet_type = PACKET_TYPE_WELCOME; + wp.packet_length = sizeof(WelcomePacket); + for (i=0; i<MAX_PLAYERS; i++) { + wp.player_seeds[i][0]=_player_seeds[i][0]; + wp.player_seeds[i][1]=_player_seeds[i][1]; + } + if (direct) { + wp.frames_max=0; + wp.frames_srv=0; + wp.frames_cnt=_frame_counter; + SendDirectBytes(cs,(void *)&wp,wp.packet_length); + } else { + wp.frames_max=_frame_counter_max; + wp.frames_srv=_frame_counter_srv; + wp.frames_cnt=0; + SendBytes(cs,(void *)&wp,wp.packet_length); + } +} + static void NetworkAcceptClients() { struct sockaddr_in sin; @@ -888,12 +1001,12 @@ static void SendQueuedCommandsToNewClient(ClientState *cs) // send the commands in the server queue to the new client. QueuedCommand *qp; SyncPacket sp; - int32 frame; + uint32 frame; DEBUG(net, 2) ("[NET] sending queued commands to client"); sp.packet_length = sizeof(sp); - sp.packet_type = 1; + sp.packet_type = PACKET_TYPE_SYNC; sp.random_seed_1 = sp.random_seed_2 = 0; sp.server = 0; @@ -918,6 +1031,25 @@ static void SendQueuedCommandsToNewClient(ClientState *cs) } + +bool NetworkCheckClientReady() { + bool ready_all = true; + uint16 count = 0; + ClientState *cs; + + for(cs=_clients;cs->socket != INVALID_SOCKET; cs++) { + count++; + ready_all = ready_all && (cs->ready || cs->inactive || (cs->xmitpos>0)); + if (!cs->ready) cs->timeout-=1; + if (cs->timeout == 0) { + SET_DPARAM16(0,count); + ShowErrorMessage(-1,STR_NETWORK_ERR_TIMEOUT,0,0); + CloseClient(cs); + } + } + return ready_all; +} + // ************************** // // * TCP Networking * // // ************************** // @@ -1077,6 +1209,8 @@ void NetworkReceive() // send queue of commands to client. SendQueuedCommandsToNewClient(cs); + + NetworkSendWelcome(cs, true); } } } @@ -1086,61 +1220,6 @@ void NetworkSend() { ClientState *cs; void *free_xmit; - uint16 count; - bool ready_all; - - // send sync packets? - if (_networking_server && _networking_sync && !_pause) { - - if (++_not_packet >= _network_sync_freq) { - SyncPacket sp; - uint new_max; - - _network_ahead_frames = _network_sync_freq + 1; - - ready_all=false; - - while (!ready_all) { - // check wether all clients are ready - ready_all=true; - count=0; - for(cs=_clients;cs->socket != INVALID_SOCKET; cs++) { - count++; - ready_all = ready_all && (cs->ready || cs->inactive || (cs->xmitpos>0)); - if (!cs->ready) cs->timeout-=5; - if (cs->timeout == 0) { - SET_DPARAM16(0,count); - ShowErrorMessage(-1,STR_NETWORK_ERR_TIMEOUT,0,0); - CloseClient(cs); - } - } - if (!ready_all) { - NetworkReceive(); - CSleep(5); - } - } - - _not_packet = 0; - - new_max = max(_frame_counter + (int)_network_ahead_frames, _frame_counter_max); - - DEBUG(net,3) ("net: serv: sync max=%i, seed1=%i, seed2=%i",new_max,_sync_seed_1,_sync_seed_2); - - sp.packet_length = sizeof(sp); - sp.packet_type = 1; - sp.frames = new_max - _frame_counter_max; - sp.server = _frame_counter_max - _frame_counter; - sp.random_seed_1 = TO_LE32(_sync_seed_1); - sp.random_seed_2 = TO_LE32(_sync_seed_2); - _frame_counter_max = new_max; - - // send it to all the clients and mark them unready - for(cs=_clients;cs->socket != INVALID_SOCKET; cs++) { - cs->ready=false; - SendBytes(cs, &sp, sizeof(sp)); - } - } - } free_xmit = _transmit_file; @@ -1168,8 +1247,9 @@ void NetworkInitialize() { ClientState *cs; + QueueClear(&_command_queue); + QueueClear(&_ack_queue); _command_queue.last = &_command_queue.head; - _ack_queue.last = &_ack_queue.head; // invalidate all clients for(cs=_clients; cs != &_clients[MAX_CLIENTS]; cs++) @@ -1455,7 +1535,7 @@ void NetworkCoreInit() { DEBUG(net, 3) ("[NET][Core] init()"); _network_available=true; -_network_client_timeout=3000; +_network_client_timeout=300; // [win32] winsock startup @@ -1631,19 +1711,12 @@ if (incomming) { if (_networking) { NetworkReceive(); - NetworkProcessCommands(); // to check if we got any new commands belonging to the current frame before we increase it. } } else { - // outgoing - - if ((_networking) && (!_networking_server) && (_frame_counter+_network_ready_ahead >= _frame_counter_max)) { - // send the "i" am ready message to the server - // [_network_ready_ahead] frames before "i" reach the frame-limit - NetworkSendReadyPacket(); - } - + if ( _udp_client_socket != INVALID_SOCKET ) NetworkUDPReceive(true); + if ( _udp_server_socket != INVALID_SOCKET ) NetworkUDPReceive(false); if (_networking) { NetworkSend(); |