Index: dataobj/environment.cc =================================================================== --- dataobj/environment.cc (revision 8346) +++ dataobj/environment.cc (working copy) @@ -46,9 +46,9 @@ bool env_t::reload_and_save_on_quit = true; sint32 env_t::server_frames_ahead = 4; -sint32 env_t::additional_client_frames_behind = 0; +sint32 env_t::additional_client_frames_behind = 4; sint32 env_t::network_frames_per_step = 4; -uint32 env_t::server_sync_steps_between_checks = 256; +uint32 env_t::server_sync_steps_between_checks = 24; bool env_t::pause_server_no_clients = false; std::string env_t::nickname = ""; Index: network/network_cmd.h =================================================================== --- network/network_cmd.h (revision 8346) +++ network/network_cmd.h (working copy) @@ -30,6 +30,7 @@ NWC_CHG_PLAYER, NWC_SCENARIO, NWC_SCENARIO_RULES, + NWC_STEP, NWC_COUNT }; Index: network/network_cmd_ingame.cc =================================================================== --- network/network_cmd_ingame.cc (revision 8346) +++ network/network_cmd_ingame.cc (working copy) @@ -50,6 +50,7 @@ case NWC_SCENARIO: nwc = new nwc_scenario_t(); break; case NWC_SCENARIO_RULES: nwc = new nwc_scenario_rules_t(); break; + case NWC_STEP: nwc = new nwc_step_t(); break; default: dbg->warning("network_command_t::read_from_socket", "received unknown packet id %d", p->get_id()); } Index: network/network_cmd_ingame.h =================================================================== --- network/network_cmd_ingame.h (revision 8346) +++ network/network_cmd_ingame.h (working copy) @@ -356,4 +356,18 @@ static bool cmp_default_param(const char *d1, const char *d2); }; +/** +* nwc_step_t +* @from-server: +* @data contains the current sync_steps of the server +* defining the maximum sync_steps a client can advance to. +*/ +class nwc_step_t : public network_world_command_t { +public: + nwc_step_t() : network_world_command_t(NWC_STEP, 0, 0) { } + nwc_step_t(uint32 sync_steps, uint32 map_counter) : network_world_command_t(NWC_STEP, sync_steps, map_counter) {}; + virtual bool execute(karte_t *) { return true;} + virtual const char* get_name() { return "nwc_step_t"; } +}; + #endif Index: simutrans/config/simuconf.tab =================================================================== --- simutrans/config/simuconf.tab (revision 8346) +++ simutrans/config/simuconf.tab (working copy) @@ -645,9 +645,9 @@ #server_frames_ahead = 4 # How much extra delay in command execution on the client side, on top of server_frames_ahead. -# A larger number can compensate for larger fluctuations in communication latency. +# A larger number can compensate for larger fluctuations in communication latency to smooth out play. # This is set by the client side. -#additional_client_frames_behind = 0 +#additional_client_frames_behind = 4 # In network mode, there will be a fixed number of screen updates before a step. # Reasonable values should result in 2-5 steps per second. @@ -656,7 +656,7 @@ # The server sends after a fixed number of steps some information to the clients. # Large values here means: reduced server communication (if that is of importance...) # Small values should improve the timing of the clients. -#server_frames_between_checks = 256 +#server_frames_between_checks = 24 # Automatically announce server on the central server directory (http://servers.simutrans.org/) # 0 (default) = off, 1 = on Index: simutrans/history.txt =================================================================== --- simutrans/history.txt (revision 8346) +++ simutrans/history.txt (working copy) @@ -14,6 +14,8 @@ FIX: removed Windows builds file search resource leak FIX: improved Windows build clipboard Unicode parsing ADD: sorting in depot (HyperSim) + ADD: sync step barrier for multiplayer clients to prevent them running ahead of the server in the case of an unreliable connection + CHG: server_frames_ahead now also controls how often a server advances the sync step barrier for multiplayer clients Release of 120.2.2: (r8163 on 31-3-2017): ADD: Name filter in depot window Index: simworld.cc =================================================================== --- simworld.cc (revision 8346) +++ simworld.cc (working copy) @@ -1247,6 +1247,7 @@ steps = 0; network_frame_count = 0; sync_steps = 0; + sync_steps_barrier = sync_steps; map_counter = 0; recalc_average_speed(); // resets timeline koord::locality_factor = settings.get_locality_factor( last_year ); @@ -2001,6 +2002,7 @@ idle_time = 0; network_frame_count = 0; sync_steps = 0; + sync_steps_barrier = sync_steps; for( uint i=0; iwarning("karte_t::network_game_set_pause", "steps=%d sync_steps=%d pause=%d", steps, sync_steps, pause_); @@ -6346,15 +6350,16 @@ // process the received command while( nwc ) { // check timing - if( nwc->get_id() == NWC_CHECK ) { - // checking for synchronisation - nwc_check_t* nwcheck = (nwc_check_t*)nwc; + uint16 const nwcid = nwc->get_id(); + if( nwcid == NWC_CHECK || nwcid == NWC_STEP ) { + // pull out server sync step + const uint32 server_sync_step = nwcid == NWC_CHECK ? dynamic_cast(nwc)->server_sync_step : dynamic_cast(nwc)->get_sync_step(); // are we on time? *ms_difference = 0; const uint32 timems = dr_time(); const sint32 time_to_next = (sint32)next_step_time - (sint32)timems; // +'ve - still waiting for next, -'ve - lagging - const sint64 frame_timediff = ((sint64)nwcheck->server_sync_step - sync_steps - settings.get_server_frames_ahead() - env_t::additional_client_frames_behind) * fix_ratio_frame_time; // +'ve - server is ahead, -'ve - client is ahead + const sint64 frame_timediff = ((sint64)server_sync_step - sync_steps - settings.get_server_frames_ahead() - env_t::additional_client_frames_behind) * fix_ratio_frame_time; // +'ve - server is ahead, -'ve - client is ahead const sint64 timediff = time_to_next + frame_timediff; dbg->warning("NWC_CHECK", "time difference to server %lli", frame_timediff ); @@ -6368,7 +6373,7 @@ // already waiting longer than how far we're ahead, so set wait time shorter to the time ahead. next_step_time = (sint64)timems - frame_timediff; } - else { + else if( nwcid == NWC_CHECK ) { // gentle slowing down *ms_difference = timediff; } @@ -6380,15 +6385,19 @@ next_step_time = timems; *ms_difference = frame_timediff; } - else { + else if( nwcid == NWC_CHECK ) { // gentle catching up *ms_difference = timediff; } } + + if( sync_steps_barrier < server_sync_step ) { + sync_steps_barrier = server_sync_step; + } } // check random number generator states - if( env_t::server && nwc->get_id()==NWC_TOOL ) { + if( env_t::server && nwcid == NWC_TOOL ) { nwc_tool_t *nwt = dynamic_cast(nwc); if( nwt->is_from_initiator() ) { if( nwt->last_sync_step>sync_steps ) { @@ -6525,6 +6534,7 @@ finish_loop = false; sync_steps = 0; + sync_steps_barrier = sync_steps; network_frame_count = 0; vector_tplhashes_ok; // bit set: this client can do something with this player @@ -6612,6 +6622,10 @@ sync_step( 0, false, true ); idle_time = 100; } + else if( env_t::networkmode && !env_t::server && sync_steps >= sync_steps_barrier ) { + sync_step( 0, false, true ); + next_step_time = time + fix_ratio_frame_time; + } else { if( step_mode==FAST_FORWARD ) { sync_step( 100, true, false ); @@ -6658,6 +6672,11 @@ nwc_check_t* nwc = new nwc_check_t(sync_steps + 1, map_counter, LCHKLST(sync_steps), sync_steps); network_send_all(nwc, true); } + else { + // broadcast sync_step + nwc_step_t* nwcstep = new nwc_step_t(sync_steps, map_counter); + network_send_all(nwcstep, true); + } } #if DEBUG>4 if( env_t::networkmode && (sync_steps & 7)==0 && env_t::verbose_debug>4 ) { Index: simworld.h =================================================================== --- simworld.h (revision 8346) +++ simworld.h (working copy) @@ -488,6 +488,9 @@ /// @note variable used in interactive() uint32 sync_steps; + + // The maximum sync_steps that a client can safely advance to. + uint32 sync_steps_barrier; #define LAST_CHECKLISTS_COUNT 64 /// @note variable used in interactive() checklist_t last_checklists[LAST_CHECKLISTS_COUNT];