From 1fb915df69aaa77ec8be39bb96650e5ec568e245 Mon Sep 17 00:00:00 2001 From: signde Date: Sat, 11 Sep 2004 19:34:11 +0000 Subject: (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] --- economy.c | 2 + functions.h | 4 + intro_gui.c | 8 +- main_gui.c | 4 +- misc.c | 40 ++++++--- network.c | 279 ++++++++++++++++++++++++++++++++++++---------------------- network_gui.c | 22 ++++- oldloader.c | 4 +- players.c | 5 +- ttd.c | 34 +++++-- unix.c | 2 +- variables.h | 10 ++- win32.c | 8 +- 13 files changed, 278 insertions(+), 144 deletions(-) diff --git a/economy.c b/economy.c index a9e18e0a7..578230688 100644 --- a/economy.c +++ b/economy.c @@ -1305,6 +1305,8 @@ void PlayersMonthlyLoop() if (_patches.inflation) AddInflation(); PlayersPayInterest(); + // Reset the _current_player flag + _current_player = OWNER_NONE; HandleEconomyFluctuations(); SubsidyMonthlyHandler(); } diff --git a/functions.h b/functions.h index 90f99b530..778b57517 100644 --- a/functions.h +++ b/functions.h @@ -95,6 +95,8 @@ void memswap(void *a, void *b, size_t size); uint32 Random(); uint RandomRange(uint max); +void InitPlayerRandoms(); + uint32 InteractiveRandom(); /* Used for random sequences that are not the same on the other end of the multiplayer link */ void SetDate(uint date); /* facedraw.c */ @@ -131,6 +133,8 @@ void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, Comman void NetworkStartSync(bool fcreset); void NetworkClose(bool client); void NetworkSendReadyPacket(); +void NetworkSendSyncPackets(); +bool NetworkCheckClientReady(); void NetworkIPListInit(); diff --git a/intro_gui.c b/intro_gui.c index 00ea3f105..deb42cf37 100644 --- a/intro_gui.c +++ b/intro_gui.c @@ -125,8 +125,8 @@ int32 CmdGenRandomNewGame(int x, int y, uint32 flags, uint32 p1, uint32 p2) // this forces stuff into test mode. _docommand_recursive = 0; - _random_seed_1 = p1; - _random_seed_2 = p2; + _random_seeds[0][0] = p1; + _random_seeds[0][1] = p2; if (_networking) { NetworkStartSync(true); } @@ -166,8 +166,8 @@ int32 CmdStartScenario(int x, int y, uint32 flags, uint32 p1, uint32 p2) // this forces stuff into test mode. _docommand_recursive = 0; - _random_seed_1 = p1; - _random_seed_2 = p2; + _random_seeds[0][0] = p1; + _random_seeds[0][1] = p2; if (_networking) { NetworkStartSync(true); } diff --git a/main_gui.c b/main_gui.c index 9f9933e6e..3465681c3 100644 --- a/main_gui.c +++ b/main_gui.c @@ -922,8 +922,8 @@ void ZoomInOrOutToCursorWindow(bool in, Window *w) void ResetLandscape() { - _random_seed_1 = InteractiveRandom(); - _random_seed_2 = InteractiveRandom(); + _random_seeds[0][0] = InteractiveRandom(); + _random_seeds[0][1] = InteractiveRandom(); GenerateWorld(1); MarkWholeScreenDirty(); diff --git a/misc.c b/misc.c index b15ca7e1b..dcaf494db 100644 --- a/misc.c +++ b/misc.c @@ -8,8 +8,6 @@ extern void StartupEconomy(); extern void InitNewsItemStructs(); -static uint32 _random_seed_3, _random_seed_4; - byte _name_array[512][32]; static INLINE uint32 ROR(uint32 x, int n) @@ -20,10 +18,17 @@ static INLINE uint32 ROR(uint32 x, int n) uint32 Random() { - uint32 t = _random_seed_2; - uint32 s = _random_seed_1; - _random_seed_1 = s + ROR(t ^ 0x1234567F, 7); - return _random_seed_2 = ROR(s, 3); + if (_current_player>=MAX_PLAYERS) { + uint32 s = _random_seeds[0][0]; + uint32 t = _random_seeds[0][1]; + _random_seeds[0][0] = s + ROR(t ^ 0x1234567F, 7); + return _random_seeds[0][1] = ROR(s, 3); + } else { + uint32 s = _player_seeds[_current_player][0]; + uint32 t = _player_seeds[_current_player][1]; + _player_seeds[_current_player][0] = s + ROR(t ^ 0x1234567F, 7); + return _player_seeds[_current_player][1] = ROR(s, 3); + } } uint RandomRange(uint max) @@ -33,10 +38,19 @@ uint RandomRange(uint max) uint32 InteractiveRandom() { - uint32 t = _random_seed_4; - uint32 s = _random_seed_3; - _random_seed_3 = s + ROR(t ^ 0x1234567F, 7); - return _random_seed_4 = ROR(s, 3); + uint32 t = _random_seeds[1][1]; + uint32 s = _random_seeds[1][0]; + _random_seeds[1][0] = s + ROR(t ^ 0x1234567F, 7); + return _random_seeds[1][1] = ROR(s, 3); +} + +void InitPlayerRandoms() +{ + int i; + for (i=0; i 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; iplayer_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; isocket != 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(); diff --git a/network_gui.c b/network_gui.c index 31e26dd6d..200c924b1 100644 --- a/network_gui.c +++ b/network_gui.c @@ -31,10 +31,17 @@ static byte _players_max; * we'll just use some dummy here */ static byte _network_connection; +static uint16 _network_game_count_last; static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) { switch(e->event) { + case WE_TICK: { + if (_network_game_count_last != _network_game_count) { + SetWindowDirty(w); + } + } + break; case WE_PAINT: { SET_DPARAM16(0, 0x00); @@ -86,8 +93,18 @@ static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */ _network_connection = e->dropdown.index; - - SetWindowDirty(w); + switch (_network_connection) { + case 0: + NetworkGameListFromLAN(); + _network_game_count_last = _network_game_count; + SetWindowDirty(w); + break; + case 1: + NetworkGameListFromInternet(); + _network_game_count_last = _network_game_count; + SetWindowDirty(w); + break; + } break; case WE_MOUSELOOP: @@ -164,6 +181,7 @@ void ShowNetworkGameWindow() w = AllocateWindowDesc(&_network_game_window_desc); strcpy(_edit_str_buf, "Your name"); + _network_game_count_last = _network_game_count; WP(w,querystr_d).caret = 1; WP(w,querystr_d).maxlen = MAX_QUERYSTR_LEN; diff --git a/oldloader.c b/oldloader.c index f15dc4f33..b4aa43c22 100644 --- a/oldloader.c +++ b/oldloader.c @@ -1124,8 +1124,8 @@ bool LoadOldSaveGame(const char *file) _cur_tileloop_tile = m->cur_tileloop_tile; _disaster_delay = m->disaster_delay; _station_tick_ctr = m->station_tick_ctr; - _random_seed_1 = m->seed_1; - _random_seed_2 = m->seed_2; + _random_seeds[0][0] = m->seed_1; + _random_seeds[0][1] = m->seed_2; _cur_town_ctr = REMAP_TOWN_IDX(m->cur_town_ptr); _cur_player_tick_index = m->cur_player_tick_index; _next_competitor_start = m->next_competitor_start; diff --git a/players.c b/players.c index 1101d1a70..3c72f625b 100644 --- a/players.c +++ b/players.c @@ -548,7 +548,7 @@ void RunOtherPlayersLoop() _is_ai_player = true; FOR_ALL_PLAYERS(p) { - if (p->is_active) { + if (p->is_active && p->is_ai) { _current_player = p->index; if (_patches.ainew_active) AiNewDoGameLoop(p); @@ -558,8 +558,7 @@ void RunOtherPlayersLoop() } _is_ai_player = false; -// XXX: is this needed? - _current_player = 0; + _current_player = OWNER_NONE; } // index is the next parameter in _decode_parameters to set up diff --git a/ttd.c b/ttd.c index a5ef2271f..036cbb868 100644 --- a/ttd.c +++ b/ttd.c @@ -533,7 +533,7 @@ int ttd_main(int argc, char* argv[]) _switch_mode = SM_NEWGAME; break; case 'G': - _random_seed_1 = atoi(mgo.opt); + _random_seeds[0][0] = atoi(mgo.opt); break; case 'p': { int i = atoi(mgo.opt); @@ -604,6 +604,7 @@ int ttd_main(int argc, char* argv[]) // initialize the ingame console IConsoleInit(); + InitPlayerRandoms(); while (_video_driver->main_loop() == ML_SWITCHDRIVER) {} @@ -881,8 +882,8 @@ void StateGameLoop() _frame_counter++; // store the random seed to be able to detect out of sync errors - _sync_seed_1 = _random_seed_1; - _sync_seed_2 = _random_seed_2; + _sync_seed_1 = _random_seeds[0][0]; + _sync_seed_2 = _random_seeds[0][1]; if (_networking) disable_computer=true; if (_savedump_path[0] && (uint)_frame_counter >= _savedump_first && (uint)(_frame_counter -_savedump_first) % _savedump_freq == 0 ) { @@ -899,6 +900,11 @@ void StateGameLoop() CallWindowTickEvent(); NewsLoop(); } else { + // All these actions has to be done from OWNER_NONE + // for multiplayer compatibility + uint p = _current_player; + _current_player = OWNER_NONE; + AnimateAnimatedTiles(); IncreaseDate(); RunTileLoop(); @@ -910,6 +916,7 @@ void StateGameLoop() CallWindowTickEvent(); NewsLoop(); + _current_player = p; } _in_state_game_loop = false; } @@ -985,21 +992,36 @@ void GameLoop() NetworkCoreLoop(true); if (_networking_sync) { - // make sure client's time is synched to the server by running frames quickly up to where the server is. + // client: make sure client's time is synched to the server by running frames quickly up to where the server is. if (!_networking_server) { while (_frame_counter < _frame_counter_srv) { StateGameLoop(); NetworkProcessCommands(); // need to process queue to make sure that packets get executed. } - } - // don't exceed the max count told by the server + // client: don't exceed the max count told by the server if (_frame_counter < _frame_counter_max) { StateGameLoop(); NetworkProcessCommands(); } + // client: send the ready packet + NetworkSendReadyPacket(); + } else { + // server: work on to the frame max + if (_frame_counter < _frame_counter_max) { + StateGameLoop(); + NetworkProcessCommands(); // to check if we got any new commands belonging to the current frame before we increase it. + } + // server: wait until all clients were ready for going on + if (_frame_counter == _frame_counter_max) { + if (NetworkCheckClientReady()) NetworkSendSyncPackets(); + } + } } else { + // server/client/standalone: not synced --> state game loop if (!_pause) StateGameLoop(); + // server/client: process queued network commands + if (_networking) NetworkProcessCommands(); } if (!_pause && _display_opt&DO_FULL_ANIMATION) diff --git a/unix.c b/unix.c index 361d7d511..f6feb99ce 100644 --- a/unix.c +++ b/unix.c @@ -420,7 +420,7 @@ int CDECL main(int argc, char* argv[]) ChangeWorkingDirectory(argv[0]); #endif - _random_seed_2 = _random_seed_1 = time(NULL); + _random_seeds[0][1] = _random_seeds[0][0] = time(NULL); return ttd_main(argc, argv); diff --git a/variables.h b/variables.h index 9441fce88..26b133e8b 100644 --- a/variables.h +++ b/variables.h @@ -62,7 +62,9 @@ VARDEF uint16 _disaster_delay; // tick handler. VARDEF uint16 _station_tick_ctr; -VARDEF uint32 _random_seed_1, _random_seed_2; +VARDEF uint32 _random_seeds[2][2]; +VARDEF uint32 _player_seeds[MAX_PLAYERS][2]; + // Iterator through all towns in OnTick_Town VARDEF byte _cur_town_ctr; @@ -214,10 +216,10 @@ VARDEF byte _cur_month; VARDEF byte _player_colors[MAX_PLAYERS]; VARDEF bool _in_state_game_loop; -VARDEF int32 _frame_counter; +VARDEF uint32 _frame_counter; -VARDEF int32 _frame_counter_max; // for networking, this is the frame that we are not allowed to execute yet. -VARDEF int32 _frame_counter_srv; // for networking, this is the last known framecounter of the server. it is always less than frame_counter_max. +VARDEF uint32 _frame_counter_max; // for networking, this is the frame that we are not allowed to execute yet. +VARDEF uint32 _frame_counter_srv; // for networking, this is the last known framecounter of the server. it is always less than frame_counter_max. // networking settings VARDEF bool _network_available; // is network mode available? diff --git a/win32.c b/win32.c index 65638e808..45f009332 100644 --- a/win32.c +++ b/win32.c @@ -1951,12 +1951,12 @@ int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLin #if defined(_MSC_VER) { uint64 seed = rdtsc(); - _random_seed_1 = ((uint32*)&seed)[0]; - _random_seed_2 = ((uint32*)&seed)[1]; + _random_seeds[0][0] = ((uint32*)&seed)[0]; + _random_seeds[0][1] = ((uint32*)&seed)[1]; } #else - _random_seed_1 = GetTickCount(); - _random_seed_2 = _random_seed_1 * 0x1234567; + _random_seeds[0][0] = GetTickCount(); + _random_seeds[0][1] = _random_seeds[0][0] * 0x1234567; #endif argc = ParseCommandLine(GetCommandLine(), argv, lengthof(argv)); -- cgit v1.2.3-54-g00ecf