TimeSystem.cpp
Go to the documentation of this file.
1 #include "TimeSystem.hpp"
2 #include "Watch.hpp"
3 #include "FixedUpdateClient.hpp"
4 
5 #include <limits>
6 
7 namespace iv
8 {
9 
11  System( sc ),
12  frame_time_ms( 0 ),
13  frame_length_ms( 0 ),
14  frame_update_queued( false ),
15  frame_clients_iterator( frame_clients.end() )
16 {
17  this->time_data.resize( TimeId::ids_count() );
18  this->timeout_queues.resize( TimeId::ids_count() );
19  this->fixed.resize( TimeId::ids_count() );
20  TimeId::lock();
21 }
22 
23 void TimeSystem::addTime( int delta_ms )
24 {
25  this->frame_length_ms += delta_ms;
26 }
27 
29 {
30  this->frame_update_queued = true;
31 
32  for( TimeData & time : this->time_data )
33  {
34  double add = double( this->frame_time_ms ) * time.speed_current + time.frame_start_time_remainder;
35  int add_int = int( add );
36 
37  time.frame_start_time_remainder = add - double( add_int );
38  time.frame_start_time_ms += add_int;
39  time.speed_current = time.speed_target;
40  }
41 
42  this->frame_length_ms -= this->frame_time_ms;
43  this->frame_time_ms = 0;
44 }
45 
47 {
48  this->frame_clients.insert( client );
49 }
50 
52 {
53  if( *this->frame_clients_iterator == client )
54  ++this->frame_clients_iterator;
55 
56  this->frame_clients.erase( client );
57 }
58 
60 {
61  TimeData & time = this->time_data[ time_type.runtime_value() ];
62  return time.frame_start_time_ms + int( time.speed_current * double( this->frame_time_ms ) );
63 }
64 
66 {
67  TimeData & time = this->time_data[ time_type.runtime_value() ];
68  return int( time.speed_current * double( this->frame_length_ms - this->frame_time_ms ) );
69 }
70 
71 void TimeSystem::speed( TimeId time_type, double speed )
72 {
73  TimeData & time = this->time_data[ time_type.runtime_value() ];
74  time.speed_target = speed;
75 }
76 
77 double TimeSystem::speed( TimeId time_type )
78 {
79  TimeData & time = this->time_data[ time_type.runtime_value() ];
80  return time.speed_target;
81 }
82 
83 
84 void TimeSystem::game_update_period( TimeId time_type, int period_ms )
85 {
86  TimeData & time = this->time_data[ time_type.runtime_value() ];
87  time.game_update_period_ms = period_ms;
88 }
89 
91 {
92  TimeData & time = this->time_data[ time_type.runtime_value() ];
93  return time.game_update_period_ms;
94 }
95 
96 bool TimeSystem::is_valid( TimeId time_type )
97 {
98  return time_type.runtime_value() >= 0 && (size_t)time_type.runtime_value() < this->time_data.size();
99 }
100 
101 TimeSystem::timeout_iterator TimeSystem::timeout( Watch * watch, TimeId time_type, int time_abs_ms, std::function< void() > const & fun )
102 {
103  return this->timeout_queues[ time_type.runtime_value() ].insert( std::make_pair( time_abs_ms, UpdateItem( watch->instance(), fun ) ) );
104 }
105 
107 {
108  this->timeout_queues[ time_type.runtime_value() ].erase( it );
109 }
110 
111 void TimeSystem::delay( Watch * watch, std::function< void() > const & fun )
112 {
113  this->delayed.push( UpdateItem( watch->instance(), fun ) );
114 }
115 
117 {
118  this->fixed[ time_type.runtime_value() ].clients.insert( client );
119 }
120 
122 {
123  auto & data = this->fixed[ time_type.runtime_value() ];
124 
125  if( *data.iterator == client )
126  ++data.iterator;
127 
128  data.clients.erase( client );
129 }
130 
132 {
133  bool something_was_called = false;
134 
135  //---------- frame updates ------------------------
136  if( this->frame_update_queued )
137  {
138  something_was_called = true;
139  this->frame_update_queued = false;
140 
141  // update frame clients
142  this->frame_clients_iterator = this->frame_clients.begin();
143  while( this->frame_clients_iterator != this->frame_clients.end() )
144  {
145  FrameUpdateClient * client = *this->frame_clients_iterator;
146  ++this->frame_clients_iterator;
147  client->frame_update();
148  }
149  }
150 
151  //----------- fixed step updates -----------------------
152  static const constexpr int FixedStepMs = 16;
153 
154  for( size_t i = 0; i < this->time_data.size(); i++ )
155  {
156  FixedData & data = this->fixed[ i ];
157  int time_ms = this->time_data[ i ].frame_start_time_ms;
158 
159  if( data.last_update == 0 )
160  {
161  data.last_update = time_ms;
162  continue;
163  }
164 
165  int steps = ( time_ms - data.last_update ) / FixedStepMs;
166  if( steps <= 0 )
167  continue;
168 
169  something_was_called = true;
170 
171  data.last_update += steps * FixedStepMs;
172 
173  data.iterator = data.clients.begin();
174  while( data.iterator != data.clients.end() )
175  {
176  FixedUpdateClient * client = *data.iterator;
177  ++data.iterator;
178  client->fixed_update( TimeId( i ), FixedStepMs, steps );
179  }
180  }
181 
182  //----------- update timeouts -----------------
183  std::vector< double > real_frame_times_for_next_update;
184  real_frame_times_for_next_update.resize( this->time_data.size() );
185  for( size_t i=0; i < this->time_data.size(); i++ )
186  {
187  if( this->timeout_queues[ i ].empty() )
188  {
189  real_frame_times_for_next_update[ i ] = std::numeric_limits< double >::infinity();
190  continue;
191  }
192 
193  TimeData & time_data = this->time_data[ i ];
194  int const & first_item_time = this->timeout_queues[ i ].begin()->first;
195 
196  real_frame_times_for_next_update[ i ] = double( first_item_time - time_data.frame_start_time_ms ) / time_data.speed_current;
197  }
198 
199  while( true )
200  {
201  // find clock that need to be updated fastest
202  int nearest_clock = -1;
203  double nearest_clock_value = std::numeric_limits< double >::infinity();
204  for( size_t i=0; i<TimeId::ids_count(); i++ )
205  {
206  double frame_proximity = real_frame_times_for_next_update[ i ];
207  if( frame_proximity < nearest_clock_value )
208  {
209  nearest_clock_value = frame_proximity;
210  nearest_clock = i;
211  }
212  }
213 
214  // nothing to update was found
215  if( nearest_clock == -1 )
216  {
217  this->frame_time_ms = this->frame_length_ms;
218  break;
219  }
220 
221  // there is something in update queue, but it should be updated in future
222  if( int( nearest_clock_value ) >= this->frame_length_ms )
223  {
224  this->frame_time_ms = this->frame_length_ms;
225  break;
226  }
227 
228  // something will be updated
229  something_was_called = true;
230 
231  //
232  this->frame_time_ms = int( nearest_clock_value );
233 
234  // retrieve the nearest update item
235  std::multimap< int, UpdateItem > & queue = this->timeout_queues[ nearest_clock ];
236  UpdateItem & update_item = queue.begin()->second;
237  std::function< void() > update_fun;
238  if( update_item.watch )
239  update_fun = update_item.fun;
240  queue.erase( queue.begin() );
241 
242  // update real_frame_times_for_next_update array
243  if( this->timeout_queues[ nearest_clock ].empty() )
244  {
245  real_frame_times_for_next_update[ nearest_clock ] = std::numeric_limits< double >::infinity();
246  }
247  else
248  {
249  TimeData & time_data = this->time_data[ nearest_clock ];
250  int const & first_item_time = this->timeout_queues[ nearest_clock ].begin()->first;
251  real_frame_times_for_next_update[ nearest_clock ] = double( first_item_time - time_data.frame_start_time_ms ) / time_data.speed_current;
252  }
253 
254  // call update
255  if( update_fun )
256  update_fun();
257  }
258 
259  //--------- call delayed code --------------
260  if( !this->delayed.empty() )
261  something_was_called = true;
262 
263  while( !this->delayed.empty() )
264  {
265  UpdateItem & item = this->delayed.front();
266  std::function< void() > fun;
267  if( item.watch )
268  fun = item.fun;
269  this->delayed.pop();
270  if( fun )
271  fun();
272  }
273 
274  //------- return ---------------------
275  return something_was_called;
276 }
277 
278 }