Merge lp:~widelands-dev/widelands/lock_game_logic into lp:widelands

Proposed by SirVer
Status: Merged
Merged at revision: 7829
Proposed branch: lp:~widelands-dev/widelands/lock_game_logic
Merge into: lp:widelands
Diff against target: 136 lines (+54/-40)
3 files modified
src/economy/request.cc (+1/-1)
src/ui_basic/panel.cc (+46/-32)
src/wui/interactive_base.cc (+7/-7)
To merge this branch: bzr merge lp:~widelands-dev/widelands/lock_game_logic
Reviewer Review Type Date Requested Status
GunChleoc code Approve
Review via email: mp+285980@code.launchpad.net

Commit message

Decouples UI update frequency from game update frequency (which is now 15 times per second).

Description of the change

This is a hacky-hack. The game logic should not be driven by the UI, but that is such a fundamental design in Widelands that I am not sure if we can ever pull that apart.

For now, this should fix desyncs, I could not reproduce them on my system anymore.

To post a comment you must log in.
Revision history for this message
GunChleoc (gunchleoc) wrote :

Not tested, but code LGTM.

Maybe add a comment to the code to explain why we have the 2 separate intervals?

review: Approve (code)
Revision history for this message
kaputtnik (franku) wrote :

Is there something specific to look at? I tested a short 2Player game against AI and found nothing strange. Does this affect calculation of statistics f.e. the productivity shown on each building? Is a game with 8 players better to test this changes?

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Hello Kaputtnick:

I just review that code, you wrote:

> Is there something specific to look at?

I think we should do tests in the „real world“ with different FPS seconds and a real
Network jitter. SirVers approach feels correct to me, but I am not sure is this
may have adverse effects, as the game logic now will run at 15 thinks()/s compared
to 25/s (with default settings) before.

Worst secario would we a slow computer in australia on ad 1024k Modem (Windows)
vs. a Workstation on Linux/OSX on 1 fibre net. But I tend to exaggerate such issues :-)

> I tested a short 2Player game against AI and found nothing strange.

This should not affect any local game, AFAI can imagine

> Does this affect calculation of statistics f.e. the productivity shown on each building?

No idea, good question,

> Is a game with 8 players better to test this changes?

Yes as many players as spoonable with different FPS.
But not in a LAN-Party :-)

Revision history for this message
GunChleoc (gunchleoc) wrote :

This is how SirVer triggered the bug:

run widelands with --maxfps=10 and host a game, run a second one with --maxfps=60 and join that game -> immediate desyncs.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 685. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/109052981.
Appveyor build 537. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_lock_game_logic-537.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Hello SirVer:

Id propose a small change, mostly comments only

=== modified file 'src/ui_basic/panel.cc'
--- src/ui_basic/panel.cc 2016-02-13 19:17:06 +0000
+++ src/ui_basic/panel.cc 2016-02-14 11:20:39 +0000
@@ -148,9 +148,10 @@
  // Panel-specific startup code. This might call end_modal()!
  start();

- // think() is called at most 15 times per second.
+ // think() is called at most 15 times per second, thats every 66ms
  const uint32_t kGameLogicDelay = 1000 / 15;

+ // with a default of 30 FPS the gemay will be drawn every 33ms
  const uint32_t draw_delay =
     1000 / std::max(5, g_options.pull_section("global").get_int("maxfps", 30));

@@ -163,8 +164,9 @@
   Panel::ui_mousewheel
  };

- uint32_t next_think_time = SDL_GetTicks() + kGameLogicDelay;
- uint32_t next_draw_time = SDL_GetTicks() + draw_delay;
+ uint32_t sdl_ticks = SDL_GetTicks();
+ uint32_t next_think_time = sdl_ticks + kGameLogicDelay;
+ uint32_t next_draw_time = sdl_ticks + draw_delay;
  while (_running) {
   const uint32_t start_time = SDL_GetTicks();

I would like to test this „in the wild“ for a while.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

A test with TinoM palying on Impact with an AI, failed, going to reprodcu this

Revision history for this message
SirVer (sirver) wrote :

I applied all code review comments now.

Apparently, my analyze of the problem was either flawed or incomplete, since this has been reproduced again even with this patch.

However, I still think it is useful to get this in, because the game logic should not depend on the refresh rate of the graphics. Next steps for the sync bug is to enable syncstream dumps through a option (instead of for debug or release builds) so that we can get debug data more easily. I'll prepare a branch for that soonish.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Sounds good to me :)

@bunnybot merge

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/economy/request.cc'
2--- src/economy/request.cc 2016-02-07 06:10:47 +0000
3+++ src/economy/request.cc 2016-02-14 19:35:24 +0000
4@@ -351,7 +351,7 @@
5 economy_->remove_request(*this);
6 economy_ = e;
7 if (economy_ && is_open())
8- economy_-> add_request(*this);
9+ economy_->add_request(*this);
10 }
11 }
12
13
14=== modified file 'src/ui_basic/panel.cc'
15--- src/ui_basic/panel.cc 2016-02-07 16:31:06 +0000
16+++ src/ui_basic/panel.cc 2016-02-14 19:35:24 +0000
17@@ -148,41 +148,55 @@
18 // Panel-specific startup code. This might call end_modal()!
19 start();
20
21- const uint32_t minimum_frame_time =
22+ // think() is called at most 15 times per second, that is roughly ever 66ms.
23+ const uint32_t kGameLogicDelay = 1000 / 15;
24+
25+ // With the default of 33FPS, the game will be drawn every 33ms.
26+ const uint32_t draw_delay =
27 1000 / std::max(5, g_options.pull_section("global").get_int("maxfps", 30));
28
29+ static InputCallback input_callback = {
30+ Panel::ui_mousepress,
31+ Panel::ui_mouserelease,
32+ Panel::ui_mousemove,
33+ Panel::ui_key,
34+ Panel::ui_textinput,
35+ Panel::ui_mousewheel
36+ };
37+
38+ const uint32_t initial_ticks = SDL_GetTicks();
39+ uint32_t next_think_time = initial_ticks + kGameLogicDelay;
40+ uint32_t next_draw_time = initial_ticks + draw_delay;
41 while (_running) {
42- const uint32_t startTime = SDL_GetTicks();
43-
44- static InputCallback icb = {
45- Panel::ui_mousepress,
46- Panel::ui_mouserelease,
47- Panel::ui_mousemove,
48- Panel::ui_key,
49- Panel::ui_textinput,
50- Panel::ui_mousewheel
51- };
52-
53- app->handle_input(&icb);
54- if (app->should_die())
55- end_modal<Returncodes>(Returncodes::kBack);
56-
57- do_think();
58-
59- RenderTarget& rt = *g_gr->get_render_target();
60- forefather->do_draw(rt);
61- rt.blit(app->get_mouse_position() - Point(3, 7),
62- WLApplication::get()->is_mouse_pressed() ? s_default_cursor_click : s_default_cursor);
63- forefather->do_tooltip();
64- g_gr->refresh();
65-
66- if (_flags & pf_child_die)
67- check_child_death();
68-
69- // Wait until 1second/maxfps are over.
70- const uint32_t frame_time = SDL_GetTicks() - startTime;
71- if (frame_time < minimum_frame_time) {
72- SDL_Delay(minimum_frame_time - frame_time);
73+ const uint32_t start_time = SDL_GetTicks();
74+
75+ app->handle_input(&input_callback);
76+
77+ if (start_time >= next_think_time) {
78+ if (app->should_die())
79+ end_modal<Returncodes>(Returncodes::kBack);
80+
81+ do_think();
82+
83+ if (_flags & pf_child_die)
84+ check_child_death();
85+ next_think_time = start_time + kGameLogicDelay;
86+ }
87+
88+ if (start_time >= next_draw_time) {
89+ RenderTarget& rt = *g_gr->get_render_target();
90+ forefather->do_draw(rt);
91+ rt.blit(app->get_mouse_position() - Point(3, 7), WLApplication::get()->is_mouse_pressed() ?
92+ s_default_cursor_click :
93+ s_default_cursor);
94+ forefather->do_tooltip();
95+ g_gr->refresh();
96+ next_draw_time = start_time + draw_delay;
97+ }
98+
99+ int32_t delay = std::min<int32_t>(next_draw_time, next_think_time) - SDL_GetTicks();
100+ if (delay > 0) {
101+ SDL_Delay(delay);
102 }
103 }
104 end();
105
106=== modified file 'src/wui/interactive_base.cc'
107--- src/wui/interactive_base.cc 2016-02-01 11:46:58 +0000
108+++ src/wui/interactive_base.cc 2016-02-14 19:35:24 +0000
109@@ -307,13 +307,6 @@
110 */
111 void InteractiveBase::think()
112 {
113- // Timing
114- uint32_t curframe = SDL_GetTicks();
115-
116- frametime_ = curframe - lastframe_;
117- avg_usframetime_ = ((avg_usframetime_ * 15) + (frametime_ * 1000)) / 16;
118- lastframe_ = curframe;
119-
120 // If one of the arrow keys is pressed, scroll here
121 const uint32_t scrollval = 10;
122
123@@ -347,6 +340,13 @@
124 ===============
125 */
126 void InteractiveBase::draw_overlay(RenderTarget& dst) {
127+ // Timing
128+ uint32_t curframe = SDL_GetTicks();
129+
130+ frametime_ = curframe - lastframe_;
131+ avg_usframetime_ = ((avg_usframetime_ * 15) + (frametime_ * 1000)) / 16;
132+ lastframe_ = curframe;
133+
134
135 const Map & map = egbase().map();
136 const bool is_game = dynamic_cast<const Game*>(&egbase());

Subscribers

People subscribed via source and target branches

to status/vote changes: