_ticker.py (9370B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\destiny\net\client\_ticker.py 2 import logging 3 import sys 4 from destiny.net.client._baseticker import BaseTicker 5 from destiny.net.client._util import merge_state_into_history 6 import blue 7 logger = logging.getLogger(__name__) 8 LOCAL_ACTIONS = {'AddBallsToPark', 'RemoveBall', 'RemoveBalls'} 9 10 class Ticker(BaseTicker): 11 12 def __init__(self, error_handler, client_ticker_interface): 13 super(Ticker, self).__init__(error_handler) 14 self._should_rebase = False 15 self._last_stamp = -1 16 self._states = [] 17 self._state_is_valid = False 18 self._client_ticker_interface = client_ticker_interface 19 20 def set_ballpark(self, ballpark): 21 super(Ticker, self).set_ballpark(ballpark) 22 self._client_ticker_interface.set_ballpark(ballpark) 23 24 def _flush_state(self, state, wait_for_bubble): 25 logger.info('Server Update with %s event(s) added to history', len(state)) 26 if len(state) == 0: 27 logger.warn('Empty state received from remote ballpark') 28 return 29 if state[0][1][0] == 'SetState': 30 self._latest_set_state_time = state[0][0] 31 logger.info('Michelle received a SetState at time %s. Clearing out-of-date entries...', self._latest_set_state_time) 32 self._history = [ history_entry for history_entry in self._history if history_entry[0][0][0] >= self._latest_set_state_time ] 33 self._dump_history() 34 else: 35 entry_time = state[0][0] 36 if entry_time < self._latest_set_state_time: 37 logger.warn('Michelle discarded a state that was too old %s < %s', entry_time, self._latest_set_state_time) 38 self._dump_history() 39 return 40 merge_state_into_history(state, self._history, wait_for_bubble) 41 self._dump_history() 42 43 def do_pre_tick(self): 44 while len(self._history) > 0: 45 state, wait_for_bubble = self._history[0] 46 if wait_for_bubble: 47 return 48 event_stamp = state[0][0] 49 if event_stamp > self._current_time and event_stamp - self._current_time < 3: 50 break 51 self._real_flush_state(state) 52 del self._history[0] 53 if self._state_is_valid and self._should_rebase: 54 self.store_state(mid_tick=True) 55 if len(self._history) > 1: 56 if self._history[0][1]: 57 return 58 self._ballpark._parent_Evolve() 59 60 def _set_state(self, state, ego_ball_id): 61 self._client_ticker_interface.on_set_state() 62 self._ballpark.ClearAll() 63 ms = blue.MemStream() 64 ms.Write(state) 65 self._ballpark.ReadFullStateFromStream(ms) 66 self._ballpark.ego = long(ego_ball_id) 67 self._ballpark.Start() 68 self._state_is_valid = True 69 self.flush_simulation_history() 70 71 def _real_flush_state(self, state): 72 logger.info('Handling Server Update with %s event(s)', len(state)) 73 if len(state) == 0: 74 logger.warn('Empty state received from remote ballpark') 75 return 76 event_stamp, event = state[0] 77 func_name, args = event 78 if func_name == 'SetState': 79 if self._client_ticker_interface.should_log_actions(): 80 logger.info('Action: %12.12s %s - current: %s %s', func_name, event_stamp, self._current_time, args) 81 self._set_state(*args) 82 if self._state_is_valid: 83 self._should_rebase = False 84 synchronised = False 85 for action in state: 86 event_stamp, event = action 87 func_name, args = event 88 if func_name == 'SetState': 89 continue 90 if self._client_ticker_interface.should_log_actions(): 91 logger.info('Action: %12.12s %s - current: %s %s', func_name, event_stamp, self._current_time, args) 92 try: 93 if not synchronised: 94 synchronised = self.synchronize_to_simulation_time(event_stamp) 95 if not synchronised: 96 logger.warn('Failed to synchronize to %s', event_stamp) 97 self._error_handler.on_recoverable_desync() 98 return 99 self._should_rebase = True 100 if func_name in LOCAL_ACTIONS: 101 apply(getattr(self, func_name), args) 102 else: 103 apply(getattr(self._ballpark, '_parent_' + func_name), args) 104 self._client_ticker_interface.on_ballpark_local_action(func_name, args) 105 if func_name == 'CloakBall': 106 event_ball_id = args[0] 107 if self._ballpark.ego and self._ballpark.ego != event_ball_id: 108 self.RemoveBall(event_ball_id) 109 except Exception: 110 message = '{} failed.'.format(func_name) 111 logger.error(message, exc_info=1) 112 sys.exc_clear() 113 continue 114 115 else: 116 logger.info('Events ignored') 117 118 def do_post_tick(self, stamp): 119 if self._should_rebase: 120 self.flush_simulation_history() 121 self._should_rebase = False 122 elif stamp > self._last_stamp + 10: 123 self.store_state() 124 125 def clear_states(self): 126 self._states = [] 127 128 def store_state(self, mid_tick = False): 129 if not self._ballpark.isRunning: 130 return 131 ms = blue.MemStream() 132 self._ballpark.WriteFullStateToStream(ms) 133 self._states.append([ms, self._current_time, mid_tick]) 134 logger.info('store_state: %s mid_tick: %s', self._current_time, mid_tick) 135 if len(self._states) > 10: 136 self._states = self._states[:1] + self._states[3:3] + self._states[-5:] 137 self._last_stamp = self._current_time 138 139 def synchronize_to_simulation_time(self, stamp): 140 logger.info('SynchroniseToSimulationTime looking for: %s - current: %s', stamp, self._current_time) 141 if stamp < self._current_time: 142 last_stamp = 0 143 last_state = None 144 for item in self._states: 145 if item[1] <= stamp: 146 last_stamp = item[1] 147 last_state = item[0] 148 else: 149 continue 150 151 if not last_state: 152 logger.warn('SynchroniseToSimulationTime: Did not find any state') 153 return 0 154 self._ballpark._parent_ReadFullStateFromStream(last_state, 1) 155 else: 156 last_stamp = self._current_time 157 for i in range(stamp - last_stamp): 158 self._ballpark._parent_Evolve() 159 160 logger.info('SynchroniseToSimulationTime found: %s', self._current_time) 161 return 1 162 163 def flush_simulation_history(self, new_base_snapshot = True): 164 logger.info('State history rebased at %s newBaseSnapshot %s', self._current_time, new_base_snapshot) 165 last_mid_state = None 166 if new_base_snapshot and len(self._states): 167 last_mid_state = self._states[-1] 168 if not last_mid_state[2] or last_mid_state[1] != self._current_time - 1: 169 last_mid_state = None 170 self.clear_states() 171 if new_base_snapshot: 172 if last_mid_state: 173 self._states.append(last_mid_state) 174 self.store_state() 175 for state in self._states: 176 logger.info('State entry %s', state) 177 178 def AddBallsToPark(self, state): 179 ms = blue.MemStream() 180 ms.Write(state) 181 self._ballpark.ReadFullStateFromStream(ms, 2) 182 183 def RemoveBall(self, ball_id, terminal = False): 184 blue.statistics.EnterZone('RemoveBall') 185 try: 186 if terminal: 187 logger.info('Removing ball %s (terminal)') 188 else: 189 logger.info('Removing ball %s') 190 ball = self._ballpark.balls.get(ball_id, None) 191 delay = self._client_ticker_interface.get_ball_destruction_delay(ball, is_terminal=terminal) 192 self._ballpark._parent_RemoveBall(ball_id, delay) 193 self._client_ticker_interface.clean_up_after_ball_removal(ball_id, ball, is_terminal=terminal) 194 finally: 195 blue.statistics.LeaveZone() 196 197 def RemoveBalls(self, ball_ids, is_release = False): 198 blue.statistics.EnterZone('RemoveBalls') 199 try: 200 self._client_ticker_interface.clean_up_after_multiple_ball_removal(ball_ids, is_release=is_release) 201 removal_delays = self._client_ticker_interface.get_ball_destruction_delays(ball_ids, is_release=is_release) 202 for ball_id, delay in removal_delays.iteritems(): 203 self._ballpark._parent_RemoveBall(ball_id, delay) 204 205 finally: 206 blue.statistics.LeaveZone() 207 208 def invalidate_state(self): 209 self._state_is_valid = False 210 211 def release(self): 212 self.RemoveBalls(self._ballpark.balls.iterkeys(), is_release=True) 213 self._ballpark.ClearAll() 214 self.flush_simulation_history(new_base_snapshot=False) 215 self._history = [] 216 self._latest_set_state_time = 0 217 self._state_is_valid = False