def RecvSync(self, timeout): '''The receiving end of Sync(). Return value: 0 on success and -1 on failure. ''' end_time = time.time() + timeout reply = TPMessage() reply.method = TPMessage.METHOD_SYNC while time.time() < end_time: try: (ready, _, _) = select.select([self], [], [], max(0, end_time - time.time())) if ready == []: continue msg = self.ReadEvent() recv_time = time.time() reply.timestamp = int(recv_time * 1000) if msg.event_type != EventType.HANDSHAKE: break if msg.method != TPMessage.METHOD_SYNC: break reply.ack = msg.seq (_, ready, _) = select.select([], [self], [], max(0, end_time - time.time())) if ready == []: continue self.WriteEvent(reply) logger.debug('Sent timestamp {0}.'.format(reply.timestamp)) except Exception as e: logger.exception(e) return -1 return 0
def test_ReadAndWriteEvent_Handshake_2(self): evt = TPMessage() evt.method = 4 evt.timestamp = 40000000 self.template_ReadAndWriteEvent(evt)
def test_SerializeAndDeserialize_2(self): msg = TPMessage() msg.timestamp = 10 self.template_SerializeAndDeserialize(msg)
def Sync(self, timeout, sync_rate): '''Measure the latency and clock-difference to the peer. This UDPSocket must be 'connected' to a peer. self.latency will be set to half the average round-trip-time in msec. self.delta will be set to the estimated clock-difference in msec. We use Cristian's algorithm to estimate the clock difference. We assume clock drift is negligible over the course of the game. Arguments: timeout -- The time allocated to this method. sync_rate -- The maximum number of messages to send per second. Return value: 0 on success and -1 on error. ''' assert timeout >= 0 assert sync_rate > 0 initial_seq = self.sock.seq time_between_send = 1.0 / sync_rate last_send = 0.0 end_time = time.time() + timeout n = 0 average_rtt = 0 min_rtt = 1000000 while time.time() < end_time: if time.time() - last_send < time_between_send: continue # Expect up to time_between_send * sync_rate + 1 messages assert self.sock.seq - initial_seq <= timeout * sync_rate expected_ack = self.sock.seq msg = TPMessage() msg.method = TPMessage.METHOD_SYNC msg.seq = self.sock.seq try: (_, ready, _) = select.select([], [self], [], max(0, end_time - time.time())) if ready == []: continue logger.debug('Sending seq {0}.'.format(msg.seq)) start_trip = time.time() last_send = start_trip self.WriteEvent(msg) seq_end_time = start_trip + time_between_send # Wait and read until we find the expected response or timeout. while True: reply = None remaining = seq_end_time - time.time() (ready, _, _) = select.select([self], [], [], remaining) if ready == []: break reply = self.ReadEvent() end_trip = time.time() if reply.event_type != EventType.HANDSHAKE: continue if reply.method != TPMessage.METHOD_SYNC: continue if reply.ack != expected_ack: logger.info('Reply was out of order') continue break if reply == None: continue logger.info('Received sync response.') rtt = int((end_trip - start_trip) * 1000) delta = reply.timestamp - int(start_trip * 1000) if rtt < min_rtt: min_rtt = rtt self.delta = int(delta - (min_rtt // 2)) average_rtt = (average_rtt * n + rtt) / (n + 1) logger.info(\ 'timestamp={0}, start={1}, delta={2}, end={3}'.format( \ reply.timestamp, start_trip, delta, end_trip)) n += 1 except Exception as e: logger.exception(e) return -1 self.latency = int(average_rtt // 2) if n == 0: logger.info('Failed to get any sync data') return -1 return 0
def Handshake(self, svr, resend, timeout): '''Perform a handshake with the server. This must be done prior to starting the game. This method sets self.conf to the game configuration provided by the server. self.conf.start_time is sent separately and set when the start of game is confirmed. Argument: svr -- A UDPEventSocket connected to the server. resend -- Number of duplicate messages to send. timeout -- Timeout for the handshake. Return value: True if the handshake succeeded and False otherwise. ''' start_time = time.time() end_time = start_time + timeout logger.info('Waiting for server to initiate handshake.') did_receive_invitation = False while time.time() < end_time: try: (ready, _, _) = select.select([svr], [], [], end_time - time.time()) if ready == []: continue msg = svr.ReadEvent() except Exception as e: logger.exception(e) continue if msg.event_type != EventType.CONFIGURE: continue self.conf = msg did_receive_invitation = True break if not did_receive_invitation: logger.info('Handshake timed out. Failing.') return False logger.info('Sending confirmation.') reply = TPMessage() reply.method = TPMessage.METHOD_CONFIRM try: status = svr.WriteEvent(reply, 0.5, resend) except Exception as e: logger.exception(e) return False if status != 0: logger.error('Failed to send confirmation.') return False logger.info('Waiting for start of game.') did_receive_start = False end_time = time.time() + 0.5 while time.time() < end_time: try: (ready, _, _) = select.select([svr], [], [], end_time - time.time()) if ready == []: continue msg = svr.ReadEvent() except Exception as e: logger.exception(e) continue if msg.event_type != EventType.HANDSHAKE: logger.debug('Incorrect message type received.') continue if msg.method == TPMessage.METHOD_STARTGAME: did_receive_start = True self.conf.start_time = msg.timestamp break if not did_receive_start: logger.info('Handshake timed out.') return False logger.info('Handshake succeeded.') return True