def handle(self): self.data = self.request.recv(1024) hello = tcp_node_msgs_pb2.TCPHello() try: hello.ParseFromString(self.data[3:-4]) except: return # send packet containing UDP server (127.0.0.1) # (very little investigation done into this packet while creating # protobuf structures hence the excessive "details" usage) msg = tcp_node_msgs_pb2.TCPServerInfo() msg.player_id = hello.player_id msg.f3 = 0 servers = msg.servers.add() if self.request.getpeername( )[0] == '127.0.0.1': # to avoid needing hairpinning udp_node_ip = "127.0.0.1" elif os.path.exists(SERVER_IP_FILE): with open(SERVER_IP_FILE, 'r') as f: udp_node_ip = f.read().rstrip('\r\n') else: udp_node_ip = "127.0.0.1" details1 = servers.details.add() details1.f1 = 1 details1.f2 = 6 details1.ip = udp_node_ip details1.port = 3022 details2 = servers.details.add() details2.f1 = 0 details2.f2 = 0 details2.ip = udp_node_ip details2.port = 3022 servers.f2 = 10 servers.f3 = 30 servers.f4 = 3 other_servers = msg.other_servers.add() wdetails1 = other_servers.details_wrapper.add() wdetails1.f1 = 1 wdetails1.f2 = 6 details3 = wdetails1.details.add() details3.CopyFrom(details1) wdetails2 = other_servers.details_wrapper.add() wdetails2.f1 = 0 wdetails2.f2 = 0 details4 = wdetails2.details.add() details4.CopyFrom(details2) other_servers.port = 3022 payload = msg.SerializeToString() # Send size of payload as 2 bytes self.request.sendall(struct.pack('!h', len(payload))) self.request.sendall(payload) player_id = hello.player_id msg = tcp_node_msgs_pb2.RecurringTCPResponse() msg.player_id = player_id msg.f3 = 0 msg.f11 = 1 payload = msg.SerializeToString() last_alive_check = int(zwift_offline.get_utc_time()) while True: #Check every 5 seconds for new updates tcpthreadevent.wait(timeout=5) try: message = udp_node_msgs_pb2.ServerToClient() message.f1 = 1 message.player_id = player_id message.world_time = zwift_offline.world_time() #PlayerUpdate if player_id in player_update_queue and len( player_update_queue[player_id] ) > 0 and player_id in online: added_player_updates = list() for player_update_proto in player_update_queue[player_id]: player_update = message.updates.add() player_update.ParseFromString(player_update_proto) #Send if 10 updates has already been added and start a new message if len(message.updates) > 9: message_payload = message.SerializeToString() self.request.sendall( struct.pack('!h', len(message_payload))) self.request.sendall(message_payload) message = udp_node_msgs_pb2.ServerToClient() message.f1 = 1 message.player_id = player_id message.world_time = zwift_offline.world_time() added_player_updates.append(player_update_proto) for player_update_proto in added_player_updates: player_update_queue[player_id].remove( player_update_proto) t = int(zwift_offline.get_utc_time()) #Check if any updates are added and should be sent to client, otherwise just keep alive every 25 seconds if len(message.updates) > 0: last_alive_check = t message_payload = message.SerializeToString() self.request.sendall( struct.pack('!h', len(message_payload))) self.request.sendall(message_payload) elif last_alive_check < t - 25: last_alive_check = t self.request.sendall(struct.pack('!h', len(payload))) self.request.sendall(payload) except: # Zwift client 1.0.64913 no longer calls '/api/users/logout' or '/api/profiles/0' zwift_offline.logout(player_id) break
def handle(self): self.data = self.request.recv(1024) if len(self.data) > 3 and self.data[3] != 0: print("TCPHandler hello(0) expected, got %s" % self.data[3]) return #print("TCPHandler hello: %s" % self.data.hex()) hello = udp_node_msgs_pb2.ClientToServer() try: hello.ParseFromString( self.data[4:-4] ) #2 bytes: payload length, 1 byte: =0x1 (TcpClient::sendClientToServer) 1 byte: type; payload; 4 bytes: hash #type: TcpClient::sayHello(=0x0), TcpClient::sendSubscribeToSegment(=0x1), TcpClient::processSegmentUnsubscription(=0x1) except Exception as exc: print('TCPHandler ParseFromString exception: %s' % repr(exc)) return # send packet containing UDP server (127.0.0.1) # (very little investigation done into this packet while creating # protobuf structures hence the excessive "details" usage) msg = udp_node_msgs_pb2.ServerToClient() msg.player_id = hello.player_id msg.world_time = 0 if self.request.getpeername( )[0] == '127.0.0.1': # to avoid needing hairpinning udp_node_ip = "127.0.0.1" elif os.path.exists(SERVER_IP_FILE): with open(SERVER_IP_FILE, 'r') as f: udp_node_ip = f.read().rstrip('\r\n') else: udp_node_ip = "127.0.0.1" details1 = msg.udp_config.relay_addresses.add() details1.lb_realm = udp_node_msgs_pb2.ZofflineConstants.RealmID details1.lb_course = 6 # watopia crowd details1.ip = udp_node_ip details1.port = 3022 details2 = msg.udp_config.relay_addresses.add() details2.lb_realm = 0 #generic load balancing realm details2.lb_course = 0 #generic load balancing course details2.ip = udp_node_ip details2.port = 3022 msg.udp_config.uc_f2 = 10 msg.udp_config.uc_f3 = 30 msg.udp_config.uc_f4 = 3 wdetails1 = msg.udp_config_vod_1.relay_addresses_vod.add() wdetails1.lb_realm = udp_node_msgs_pb2.ZofflineConstants.RealmID wdetails1.lb_course = 6 # watopia crowd details3 = wdetails1.relay_addresses.add() details3.CopyFrom(details1) wdetails2 = msg.udp_config_vod_1.relay_addresses_vod.add() wdetails2.lb_realm = 0 #generic load balancing realm wdetails2.lb_course = 0 #generic load balancing course details4 = wdetails2.relay_addresses.add() details4.CopyFrom(details2) msg.udp_config_vod_1.port = 3022 payload = msg.SerializeToString() # Send size of payload as 2 bytes self.request.sendall(struct.pack('!h', len(payload))) self.request.sendall(payload) player_id = hello.player_id #print("TCPHandler for %d" % player_id) msg = udp_node_msgs_pb2.ServerToClient() msg.player_id = player_id msg.world_time = 0 msg.stc_f11 = True payload = msg.SerializeToString() last_alive_check = int(zo.get_utc_time()) self.request.settimeout(1) #make recv non-blocking while True: self.data = b'' try: self.data = self.request.recv(1024) #print(self.data.hex()) i = 0 while i < len(self.data): size = int.from_bytes(self.data[i:i + 2], "big") packet = self.data[i:i + size + 2] #print(packet.hex()) if len(packet) == size + 2 and packet[3] == 1: subscr = udp_node_msgs_pb2.ClientToServer() try: subscr.ParseFromString(packet[4:-4]) #print(subscr) except Exception as exc: print('TCPHandler ParseFromString exception: %s' % repr(exc)) if subscr.subsSegments: msg1 = udp_node_msgs_pb2.ServerToClient() msg1.server_realm = udp_node_msgs_pb2.ZofflineConstants.RealmID msg1.player_id = subscr.player_id msg1.world_time = zo.world_time() msg1.ackSubsSegm.extend(subscr.subsSegments) payload1 = msg1.SerializeToString() self.request.sendall( struct.pack('!h', len(payload1))) self.request.sendall(payload1) #print('TCPHandler subscr: %s' % msg1.ackSubsSegm) i += size + 2 except Exception as exc: #print('TCPHandler exception: %s' % repr(exc)) #timeout is ok here pass #Check every 5 seconds for new updates #tcpthreadevent.wait(timeout=5) # no more, we will use the request timeout now try: t = int(zo.get_utc_time()) #if ZC need to be registered if player_id in zo.zc_connect_queue: # and player_id in online: zc_params = udp_node_msgs_pb2.ServerToClient() zc_params.player_id = player_id zc_params.world_time = 0 zc_params.zc_local_ip = zo.zc_connect_queue[player_id][0] zc_params.zc_local_port = zo.zc_connect_queue[player_id][ 1] #21587 zc_params.zc_protocol = udp_node_msgs_pb2.IPProtocol.TCP #=2 zc_params_payload = zc_params.SerializeToString() last_alive_check = t self.request.sendall( struct.pack('!h', len(zc_params_payload))) self.request.sendall(zc_params_payload) #print("TCPHandler register_zc %d %s" % (player_id, zc_params_payload.hex())) zo.zc_connect_queue.pop(player_id) message = udp_node_msgs_pb2.ServerToClient() message.server_realm = udp_node_msgs_pb2.ZofflineConstants.RealmID message.player_id = player_id message.world_time = zo.world_time() #PlayerUpdate if player_id in player_update_queue and len( player_update_queue[player_id] ) > 0 and player_id in online: added_player_updates = list() for player_update_proto in player_update_queue[player_id]: player_update = message.updates.add() player_update.ParseFromString(player_update_proto) #Send if 10 updates has already been added and start a new message if len(message.updates) > 9: message_payload = message.SerializeToString() self.request.sendall( struct.pack('!h', len(message_payload))) self.request.sendall(message_payload) message = udp_node_msgs_pb2.ServerToClient() message.server_realm = udp_node_msgs_pb2.ZofflineConstants.RealmID message.player_id = player_id message.world_time = zo.world_time() added_player_updates.append(player_update_proto) for player_update_proto in added_player_updates: player_update_queue[player_id].remove( player_update_proto) #Check if any updates are added and should be sent to client, otherwise just keep alive every 25 seconds if len(message.updates) > 0: last_alive_check = t message_payload = message.SerializeToString() self.request.sendall( struct.pack('!h', len(message_payload))) self.request.sendall(message_payload) elif last_alive_check < t - 25: last_alive_check = t self.request.sendall(struct.pack('!h', len(payload))) self.request.sendall(payload) except Exception as exc: print('TCPHandler loop exception: %s' % repr(exc)) break
def handle(self): data = self.request[0] socket = self.request[1] recv = udp_node_msgs_pb2.ClientToServer() try: recv.ParseFromString(data[:-4]) except: try: #If no sensors connected, first byte must be skipped recv.ParseFromString(data[1:-4]) except Exception as exc: print('UDPHandler ParseFromString exception: %s' % repr(exc)) return client_address = self.client_address player_id = recv.player_id state = recv.state #Add last updates for player if missing if not player_id in last_pp_updates.keys(): last_pp_updates[player_id] = 0 if not player_id in last_bot_updates.keys(): last_bot_updates[player_id] = 0 t = int(zo.get_utc_time()) #Update player online state if state.roadTime: if player_id in online.keys(): if online[player_id].worldTime > state.worldTime: return #udp is unordered -> drop old state elif time.time() > start_time + 10: discord.change_presence(len(online) + 1) online[player_id] = state #Add handling of ghosts for player if it's missing if not player_id in global_ghosts.keys(): global_ghosts[player_id] = GhostsVariables() ghosts = global_ghosts[player_id] ghosts.last_package_time = t if recv.seqno == 1: ghosts.rec = None organize_ghosts(player_id) #Changed course if zo.get_course(state) and ghosts.course != zo.get_course(state): ghosts.rec = None ghosts.course = zo.get_course(state) if ghosts.rec == None: ghosts.rec = udp_node_msgs_pb2.Ghost() ghosts.play = udp_node_msgs_pb2.Ghosts() ghosts.last_rt = 0 ghosts.play_count = 0 ghosts.loaded = False ghosts.started = False ghosts.rec.player_id = player_id if player_id in ghosts_enabled and ghosts_enabled[player_id]: #Load ghosts for current course if not ghosts.loaded and zo.get_course(state): ghosts.loaded = True load_ghosts(player_id, state, ghosts) #Save player state as ghost if moving if state.roadTime and ghosts.last_rt and state.roadTime != ghosts.last_rt: if t >= ghosts.last_rec + ghost_update_freq: s = ghosts.rec.states.add() s.CopyFrom(state) ghosts.last_rec = t #Start loaded ghosts if not ghosts.started and ghosts.play.ghosts and zo.road_id( state) == ghosts.start_road: if zo.is_forward(state): if state.roadTime > ghosts.start_rt and abs( state.roadTime - ghosts.start_rt) < 500000: ghosts.started = True else: if state.roadTime < ghosts.start_rt and abs( state.roadTime - ghosts.start_rt) < 500000: ghosts.started = True #Uncomment to print player state when stopped (to find new start lines) #else: print('course', zo.get_course(state), 'road', zo.road_id(state), 'isForward', zo.is_forward(state), 'roadTime', state.roadTime) ghosts.last_rt = state.roadTime #Set state of player being watched watching_state = None if state.watchingRiderId == player_id: watching_state = state elif state.watchingRiderId in online.keys(): watching_state = online[state.watchingRiderId] elif state.watchingRiderId in global_pace_partners.keys(): pp = global_pace_partners[state.watchingRiderId] watching_state = pp.route.states[pp.position] elif state.watchingRiderId in global_bots.keys(): bot = global_bots[state.watchingRiderId] watching_state = bot.route.states[bot.position] elif state.watchingRiderId > 10000000: ghost = ghosts.play.ghosts[math.floor(state.watchingRiderId / 10000000) - 1] if len(ghost.states) > ghosts.play_count: watching_state = ghost.states[ghosts.play_count] #Check if online players, pace partners, bots and ghosts are nearby nearby = list() for p_id in online.keys(): player = online[p_id] if player.id != player_id and zo.is_nearby( watching_state, player) and is_state_new_for( player, player_id): nearby.append(p_id) if t >= last_pp_updates[player_id] + pacer_update_freq: last_pp_updates[player_id] = t for p_id in global_pace_partners.keys(): pace_partner_variables = global_pace_partners[p_id] pace_partner = pace_partner_variables.route.states[ pace_partner_variables.position] if zo.is_nearby(watching_state, pace_partner): nearby.append(p_id) if t >= last_bot_updates[player_id] + bot_update_freq: last_bot_updates[player_id] = t for p_id in global_bots.keys(): bot_variables = global_bots[p_id] bot = bot_variables.route.states[bot_variables.position] if zo.is_nearby(watching_state, bot): nearby.append(p_id) if ghosts.started and t >= ghosts.last_play + ghost_update_freq: ghosts.last_play = t ghost_id = 1 for g in ghosts.play.ghosts: if len(g.states) > ghosts.play_count and zo.is_nearby( watching_state, g.states[ghosts.play_count]): nearby.append(player_id + ghost_id * 10000000) ghost_id += 1 ghosts.play_count += 1 #Send nearby riders states or empty message message = get_empty_message(player_id) if nearby: message.num_msgs = math.ceil(len(nearby) / 10) for p_id in nearby: player = None if p_id in online.keys(): player = online[p_id] elif p_id in global_pace_partners.keys(): pace_partner_variables = global_pace_partners[p_id] player = pace_partner_variables.route.states[ pace_partner_variables.position] elif p_id in global_bots.keys(): bot_variables = global_bots[p_id] player = bot_variables.route.states[bot_variables.position] elif p_id > 10000000: player = udp_node_msgs_pb2.PlayerState() player.CopyFrom( ghosts.play.ghosts[math.floor(p_id / 10000000) - 1].states[ghosts.play_count - 1]) player.id = p_id player.worldTime = zo.world_time() if player != None: if len(message.states) > 9: message.world_time = zo.world_time() message.cts_latency = message.world_time - recv.world_time socket.sendto(message.SerializeToString(), client_address) message.msgnum += 1 del message.states[:] s = message.states.add() s.CopyFrom(player) else: message.num_msgs = 1 message.world_time = zo.world_time() message.cts_latency = message.world_time - recv.world_time socket.sendto(message.SerializeToString(), client_address)
def handle(self): data = self.request[0] socket = self.request[1] recv = udp_node_msgs_pb2.ClientToServer() try: recv.ParseFromString(data[:-4]) except: try: recv.ParseFromString(data[3:-4]) except: return client_address = self.client_address player_id = recv.player_id state = recv.state nearby_state = state if state.watchingRiderId in online.keys(): nearby_state = online[state.watchingRiderId] elif state.watchingRiderId in global_pace_partners.keys(): pp = global_pace_partners[state.watchingRiderId] nearby_state = pp.route.states[pp.position] #Add handling of ghosts for player if it's missing if not player_id in global_ghosts.keys(): global_ghosts[player_id] = GhostsVariables() ghosts = global_ghosts[player_id] #Add pace partner last update for player if it's missing if not player_id in last_pp_updates.keys(): last_pp_updates[player_id] = 0 last_pp_update = last_pp_updates[player_id] #Add bot last update for player if it's missing if not player_id in last_bot_updates.keys(): last_bot_updates[player_id] = 0 last_bot_update = last_bot_updates[player_id] if recv.seqno == 1 or ghosts.rec == None: ghosts.rec = udp_node_msgs_pb2.Ghost() ghosts.play = udp_node_msgs_pb2.Ghosts() ghosts.last_rt = 0 ghosts.play_count = 0 ghosts.loaded = False ghosts.started = False ghosts.rec.player_id = player_id organize_ghosts(player_id) t = int(zwift_offline.get_utc_time()) ghosts.last_package_time = t if player_id in ghosts_enabled and ghosts_enabled[player_id]: if not ghosts.loaded and get_course(state): ghosts.loaded = True load_ghosts(player_id, state, ghosts) if state.roadTime and ghosts.last_rt and state.roadTime != ghosts.last_rt: if t >= ghosts.last_rec + ghost_update_freq: s = ghosts.rec.states.add() s.CopyFrom(state) ghosts.last_rec = t if not ghosts.started and ghosts.play.ghosts and road_id(state) == ghosts.start_road: if is_forward(state): if state.roadTime > ghosts.start_rt and abs(state.roadTime - ghosts.start_rt) < 500000: ghosts.started = True else: if state.roadTime < ghosts.start_rt and abs(state.roadTime - ghosts.start_rt) < 500000: ghosts.started = True # else: print('course', get_course(state), 'road', road_id(state), 'isForward', is_forward(state), 'roadTime', state.roadTime) ghosts.last_rt = state.roadTime keys = online.keys() remove_players = list() for p_id in keys: if zwift_offline.world_time() > online[p_id].worldTime + 10000: remove_players.insert(0, p_id) for p_id in remove_players: online.pop(p_id) if state.roadTime: if player_id in online.keys(): online[player_id] = state else: online[player_id] = state discord.send_message('%s riders online' % len(online)) #Remove ghosts entries for inactive players (disconnected?) keys = global_ghosts.keys() remove_players = list() for p_id in keys: if global_ghosts[p_id].last_package_time < t - 10: remove_players.insert(0, p_id) for p_id in remove_players: global_ghosts.pop(p_id) if ghosts.started and t >= ghosts.last_play + ghost_update_freq: message = get_empty_message(player_id) active_ghosts = 0 for g in ghosts.play.ghosts: if len(g.states) > ghosts.play_count: active_ghosts += 1 if active_ghosts: message.num_msgs = active_ghosts // 10 if active_ghosts % 10: message.num_msgs += 1 ghost_id = 1 for g in ghosts.play.ghosts: if len(g.states) > ghosts.play_count: if len(message.states) < 10: state = message.states.add() state.CopyFrom(g.states[ghosts.play_count]) state.id = player_id + ghost_id * 10000000 state.worldTime = zwift_offline.world_time() else: message.world_time = zwift_offline.world_time() socket.sendto(message.SerializeToString(), client_address) message.msgnum += 1 del message.states[:] state = message.states.add() state.CopyFrom(g.states[ghosts.play_count]) state.id = player_id + ghost_id * 10000000 state.worldTime = zwift_offline.world_time() ghost_id += 1 else: message.num_msgs = 1 message.world_time = zwift_offline.world_time() socket.sendto(message.SerializeToString(), client_address) ghosts.play_count += 1 ghosts.last_play = t message = get_empty_message(player_id) nearby = list() for p_id in online.keys(): player = online[p_id] if player.id != player_id: #Check if players are close in world if zwift_offline.is_nearby(nearby_state, player): nearby.append(p_id) if t >= last_pp_update + pacer_update_freq: last_pp_updates[player_id] = t for p_id in global_pace_partners.keys(): pace_partner_variables = global_pace_partners[p_id] pace_partner = pace_partner_variables.route.states[pace_partner_variables.position] #Check if pacepartner is close to player in world if zwift_offline.is_nearby(nearby_state, pace_partner): nearby.append(p_id) if t >= last_bot_update + bot_update_freq: last_bot_updates[player_id] = t for p_id in global_bots.keys(): bot_variables = global_bots[p_id] bot = bot_variables.route.states[bot_variables.position] #Check if bot is close to player in world if zwift_offline.is_nearby(nearby_state, bot): nearby.append(p_id) players = len(nearby) message.num_msgs = players // 10 if players % 10: message.num_msgs += 1 for p_id in nearby: player = None if p_id in online.keys(): player = online[p_id] elif p_id in global_pace_partners.keys(): pace_partner_variables = global_pace_partners[p_id] player = pace_partner_variables.route.states[pace_partner_variables.position] elif p_id in global_bots.keys(): bot_variables = global_bots[p_id] player = bot_variables.route.states[bot_variables.position] if player != None: if len(message.states) < 10: state = message.states.add() state.CopyFrom(player) else: message.world_time = zwift_offline.world_time() socket.sendto(message.SerializeToString(), client_address) message.msgnum += 1 del message.states[:] state = message.states.add() state.CopyFrom(player) message.world_time = zwift_offline.world_time() socket.sendto(message.SerializeToString(), client_address)