Files
zwift-offline/protobuf/udp-node-msgs.proto
2025-04-22 08:24:34 -03:00

314 lines
12 KiB
Protocol Buffer

syntax = "proto2";
import "profile.proto"; //enums PlayerType and Sport
import "per-session-info.proto"; //TcpConfig
enum ZofflineConstants {
RealmID = 1; // hardcoded in ZwiftApp=1 (g_CurrentServerRealmID, 0: never connected; -1: disconnected)
}
enum WA_TYPE {
WAT_LEAVE = 2; //proto::PlayerLeftWorld
WAT_RELOGIN = 3; //proto::PlayerLeftWorld
WAT_RIDE_ON = 4; //proto::RideOn
WAT_SPA = 5; //proto::SocialPlayerAction (chat message)
WAT_EVENT = 6; //proto::Event
WAT_JOIN_E = 7; //proto::PlayerJoinedEvent
WAT_LEFT_E = 8; //proto::PlayerLeftEvent
WAT_RQ_PROF = 9; //proto::RequestProfileFromServer
WAT_INV_W = 10; //proto::ReceiveInvitationWorldAttribute
WAT_KICKED = 11; //no payload
WAT_WTIME = 100; //g_WorldTime := payload (GOD message, may be ignored if ZWIFT\CONFIG\IGNOREGODMESSAGES=1)
WAT_RTIME = 101; //BlimpEntity::SetRoadTime(payload), also GOD message
WAT_B_ACT = 102; //BikeEntity::PerformAction(payload)
WAT_GRP_M = 103; //GroupMessage (may be ignored if ZWIFT\CONFIG\SHOWGROUPMSGS=1)
WAT_PRI_M = 104; //PrivateMessage
WAT_SR = 105; //proto::SegmentResult
WAT_FLAG = 106; //Leaderboards::FlagSandbagger / Leaderboards::FlagCheater
WAT_NONE = 107; //does nothing
WAT_RLA = 108; //ZNETWORK_BroadcastRideLeaderAction
WAT_GE = 109; //GroupEvents::UserSignedup / GroupEvents::UserRegistered
WAT_NM = 110; //notable moment + Play_Magic_Whoosh_Deep_Sparkle
WAT_LATE = 111; //ZNETWORK_INTERNAL_HandleLateJoinRequest
WAT_RH = 112; //ZNETWORK_INTERNAL_HandleRouteHashRequest
WAT_STATS = 113; //GLOBAL_MESSAGE_TYPE_RIDER_FENCE_STATS
WAT_FENCE = 114; //GLOBAL_MESSAGE_TYPE_GRFENCE_CONFIG
WAT_BN_GE = 115; //ZNETWORK_BroadcastBibNumberForGroupEvent
WAT_PPI = 116; //ZNETWORK_INTERNAL_HandlePacePartnerInfo
}
enum UserBikeAction {
UBA_ELBOW = 0;
UBA_WAVE = 1;
UBA_02 = 2; //BikeEntity::UpdateAnimation
UBA_RIDEON = 3;
UBA_HAMMER = 4;
UBA_NICE = 5;
UBA_BRING_IT = 6;
UBA_TOAST = 7;
UBA_BELL = 8;
UBA_HOLIDAY_WAVE = 9; //not sure
}
message WorldAttribute {
optional int64 wa_f1 = 1; //not r/w by game? 587645624533328784, later 5876456 85771834256
optional int64 server_realm = 2;
optional WA_TYPE wa_type = 3;
optional bytes payload = 4; //not only protobuf
optional int64 world_time_born = 5;
optional int64 x = 6; //stored as int32
optional int64 y_altitude = 7; //stored as int32
optional int64 z = 8; //stored as int32
optional int64 world_time_expire = 9;
optional int64 rel_id = 10; //WAT_PPI: pace partner smth; WAT_SPA: to_player_id; WAT_RH: route_id? ...
optional int32 importance = 11; //not read by game??? WAT_B_ACT:1000; WAT_NM:50000; WAT_RH:5000000; ... 75000 ?
optional int64 wa_f12 = 12; //not r/w by game? Not in package when testing
optional int32 wa_f13 = 13; //not r/w by game?
optional int64 timestamp = 14; //not written by game? (from server) looks like "The Current Epoch Unix Timestamp" in Microseconds
optional int32 wa_f15 = 15; //6, not r/w by game?
optional int64 wa_f16 = 16; //not r/w by game? stored as bool
}
message PlayerLeftWorld {
required int64 player_id = 1;
optional int64 world_time = 2; // not sure
optional bool anotherLogin = 3; // user profile logged in twice -> one of them should be logged out
optional bool plw_f4 = 4; //true
repeated sint64 plw_f5 = 5; // [1056322864]
}
/*message WorldAttributes {
repeated WorldAttribute world_attributes = 1;
required int64 world_time = 2;
}
message World { //zwift.protobuf.World
required uint64 id = 1;
required string name = 2;
required uint64 w_f3 = 3;
optional bool w_f4 = 4;
required uint64 w_f5 = 5;
required uint64 world_time = 6;
required uint64 real_time = 7;
repeated Player w_f8 = 8;
}
message Player {
optional PlayerProfile player_profile = 1;
optional PlayerState player_state = 2;
}*/
enum POWERUP_TYPE {
LIGHTNESS = 0;
DRAFTBOOST = 1;
BONUS_XP_LIGHT = 2;
BONUS_XP = 3;
UNDRAFTABLE = 4;
AERO = 5;
NINJA = 6;
STEAMROLLER = 7;
ANVIL = 8;
POWERUP_CNT = 9;
POWERUP_NONE = 15;
}
message PlayerState {
optional int64 id = 1;
optional int64 worldTime = 2; // milliseconds
optional int32 distance = 3; // meters
optional int32 roadTime = 4; // 1/100 sec
optional int32 laps = 5;
optional uint32 speed = 6; // millimeters per hour
optional uint32 ps_f7 = 7;
optional int32 roadPosition = 8;
optional int32 cadenceUHz = 9; // =(cad / 60) * 1000000
optional int32 ps_f10 = 10; // BikeEntity.field_B58; 0 - ETA related (something around speed)
optional int32 heartrate = 11;
optional int32 power = 12;
optional int64 heading = 13;
optional int64 lean = 14;
optional int32 climbing = 15; // meters
optional int32 time = 16; // seconds
optional int32 ps_f17 = 17;
optional uint32 frameHue = 18; // BikeEntity::DrawBike m_frameHue * 255.0
//field 19:
//byte[0].bits[0,1]: HasPowerMeter, HasPhoneConnected
//byte[0].bits[2,3]: RoadDirectionForward, ??? !BikeEntity.field_DCC || BikeEntity.disSteer
//byte[0].bits[4]: read in BikeEntity::ProcessNewPacket, steering-related
//byte[1]: =0 ???
//byte[2]: fallback course/getMapRevisionId
//byte[3]: realRideons (not counted yet in BikeEntity::m_rideons) @ BikeEntity::UpdateRideOns, see also BikeEntity::Update
optional uint32 f19 = 19;
//ZNETWORK_SerializeAux3: low 4 bits=POWERUP_TYPE; next 4 bits: BikeEntity.field_2B14+1; next 8 bits: road_id
// BikeEntity.field_2b16: true-> |= 0x2000000, false-> |= 0x1000000 (bits 24-25)
// bit 28: EbikeBoost::GetActiveBoostOption
optional uint32 aux3 = 20;
optional uint32 progress = 21; // WorkoutMode = progress & 0xF, up to 7 including (workoutPhaseType+1 or 0)
optional int64 customizationId = 22;
optional bool justWatching = 23;
optional int32 calories = 24;
optional float x = 25;
optional float y_altitude = 26;
optional float z = 27;
optional int64 watchingRiderId = 28;
optional int64 groupId = 29;
// 30 absent at least in Android Game
optional Sport sport = 31;
optional float ps_f32 = 32;
optional uint32 ps_f33 = 33;
optional float dist_lat = 34; //= BikeEntity.field_F00 (=219.56387 and incr if moving: actual distance moved included lateral movement)
optional int32 world = 35;
optional uint32 ps_f36 = 36; // = f(BikeEntity.field_2a28) BikeEntity::CreateNewPacket
optional uint32 ps_f37 = 37; // = f(BikeEntity.field_2a28) BikeEntity::CreateNewPacket
optional bool canSteer = 38; // = BikeEntity.m_canSteer
optional int32 route = 39;
optional int32 pacerBotGroupSize = 40;
optional bool activeSteer = 41;
optional bool portal = 43;
optional int32 portalGradientScale = 44; // 0 = 50%, 1 = 75%, 2 = 100%, 3 = 125%
optional int32 portalElevationScale = 45; // 50, 75, 100 or 125
optional int32 boostPad = 46;
optional int32 hazardPad = 47;
optional int32 timeBonus = 48;
optional int32 rideonBomb = 49; // always seems to be x5 with value of 9
}
message ClientToServer {
required int64 server_realm = 1; //UdpClient::sendDisconnectedClientToServer: -1. Otherwise g_CurrentServerRealmID (RealmID or 0 if not connected yet)
required int64 player_id = 2;
optional int64 world_time = 3;
optional uint32 seqno = 4;
optional uint32 cts_f5 = 5;
optional int64 cts_f6 = 6;
required PlayerState state = 7;
optional bool cts_f8 = 8;
optional bool cts_f9 = 9;
required int64 last_update = 10;
optional bool cts_f11 = 11;
required int64 last_player_update = 12;
optional int64 larg_wa_time = 13; //TcpClient::sayHello: LargestWorldAttributeTimestamp
optional bool cts_f14 = 14;
repeated int64 subsSegments = 15; //subscribed segment ids? TcpClient::sayHello, TcpClient::sendSubscribeToSegment
repeated int64 unsSegments = 16; //unsubscribed segment ids? TcpClient::processSegmentUnsubscription
}
message PlayerSummary {
optional int32 plsu_f1 = 1;
optional int32 plsu_f2 = 2;
optional int32 plsu_f3 = 3;
optional int32 plsu_f4 = 4;
}
message PlayerSummaries {
optional sint64 plsus_f1 = 1; //stored as int32
optional sint64 plsus_f2 = 2; //stored as int32
optional sint32 plsus_f3 = 3;
optional sint32 plsus_f4 = 4;
optional int32 plsus_f5 = 5;
optional int32 plsus_f6 = 6;
optional int32 plsus_f7 = 7;
repeated PlayerSummary player_summaries = 8;
}
message RelayAddress {
optional int32 lb_realm = 1; // load balancing cluster: server realm or 0 (generic)
optional int32 lb_course = 2; // load balancing cluster: course id
optional string ip = 3;
optional int32 port = 4;
optional float ra_f5 = 5; //used when appropriate server selection occurs (RelayAddressService::getAddress)
optional float ra_f6 = 6; //used when appropriate server selection occurs (RelayAddressService::getAddress)
}
message UdpConfig {
repeated RelayAddress relay_addresses = 1;
optional int32 uc_f2 = 2; //=10?
optional int32 uc_f3 = 3; //=30?
optional int32 uc_f4 = 4; //=3?
}
message RelayAddressesVOD {
optional int32 lb_realm = 1; // load balancing cluster: server realm or 0 (generic)
optional int32 lb_course = 2; // load balancing cluster: course id
repeated RelayAddress relay_addresses = 3;
optional bool rav_f4 = 4; //server selection method: true->higher (ra_f5, ra_f6), false->nearest (ra_f5, ra_f6)
}
message UdpConfigVOD {
repeated RelayAddressesVOD relay_addresses_vod = 1;
optional int32 port = 2;
optional int64 ucv_f3 = 3;
optional int64 ucv_f4 = 4;
optional float ucv_f5 = 5;
optional float ucv_f6 = 6;
}
message PlayerRouteDistance {
optional int32 player_id = 1; //BikeManager::FindBikeWithNetworkID
optional float distance_covered = 2; //not sure
optional int32 millisec_to_leader = 3; // ZNETWORK_INTERNAL_ProcessPlayerPackets
}
message EventSubgroupPlacements {
optional int32 position = 1; //UdpStatistics::registerFanViewLatestPlayerStateInfo
repeated PlayerRouteDistance player_rd1 = 2;
repeated PlayerRouteDistance player_rd2 = 3;
repeated PlayerRouteDistance eventRiderPosition = 4;
repeated PlayerRouteDistance player_rd4 = 5;
optional int32 eventTotalRiders = 6;
optional int32 bikeNetworkId = 7;
optional int32 millisec_to_leader = 8; //ZNETWORK_INTERNAL_ProcessPlayerPackets
optional float esp_f9 = 9; //or fixed
}
enum IPProtocol {
UDP = 1;
TCP = 2;
}
enum ExpungeReason {
NOT_EXPUNGED = 0;
WORLD_FULL = 1;
ROADS_CLOSED = 2;
}
message ServerToClient {
optional int64 server_realm = 1;
optional int64 player_id = 2;
optional int64 world_time = 3;
optional int32 seqno = 4;
optional int32 stc_f5 = 5; //low-priority world time sync algo (not investigated yet, maybe deprecated) in WorldClockService::calculateOneLegLatency
// 6,7: absent
repeated PlayerState states = 8;
repeated WorldAttribute updates = 9;
repeated int64 stc_f10 = 10;
optional bool stc_f11 = 11; //=true???
optional string zc_local_ip = 12;
optional int64 stc_f13 = 13;
optional int32 zwifters = 14;
optional int32 zc_local_port = 15;
optional IPProtocol zc_protocol = 16;
optional int64 cts_latency = 17; //high-priority world time sync algo in WorldClockService::calculateOneLegLatency
optional int32 num_msgs = 18;
optional int32 msgnum = 19;
optional bool hasSimultLogin = 20; //UdpClient::disconnectionRequested due to simultaneous login (1); OR simultaneous login ceased (0)
optional PlayerSummaries player_summaries = 21; //tag426
// 22 absent
optional EventSubgroupPlacements ev_subgroup_ps = 23; //tag442
optional UdpConfig udp_config = 24; //tag450
optional UdpConfigVOD udp_config_vod_1 = 25; //tag458
optional ExpungeReason expungeReason = 26; //tag464 UdpClient::receivedExpungeReason
optional UdpConfigVOD udp_config_vod_2 = 27; //tag474
repeated PlayerState player_states = 28; //tag482
optional TcpConfig tcp_config = 29; //tag490
repeated int64 ackSubsSegm = 30; //tag496 TcpClient::processSubscribedSegment
optional uint32 stc_f31 = 31; //tag508
optional bytes zc_key = 32; //tag642
}
message Ghost { //not from the Zwift game, zoffline-specific!
required int32 player_id = 1;
repeated PlayerState states = 2;
}
message RideOn {
required int64 player_id = 1;
required int64 to_player_id = 2;
required string firstName = 3;
required string lastName = 4;
required int32 countryCode = 5;
}