From 1067106af5ff693cd500acd20000e763ea93a758 Mon Sep 17 00:00:00 2001 From: VG-Prog Date: Fri, 22 May 2026 21:45:30 +0200 Subject: [PATCH 01/11] feat(Cluster/Contracts): Extend cluster service contracts Add the shared protobuf, generated runtime code, events, GUID helpers, auth identity helpers, and configuration contracts used by clustered gateway, registry, group, guild, matchmaking, and sidecar flows. Tests, local tooling, and broad documentation are intentionally excluded from this upstream-focused scope. --- Makefile | 2 +- api/proto/v1/characters/characters.proto | 474 +- api/proto/v1/chat/chat.proto | 13 + api/proto/v1/group/group.proto | 256 +- api/proto/v1/guilds/guilds.proto | 339 + api/proto/v1/matchmaking/matchmaking.proto | 221 +- api/proto/v1/servers-registry/registry.proto | 22 +- api/proto/v1/worldserver/worldserver.proto | 250 +- config.yml.example | 38 +- gen/characters/pb/characters.pb.go | 8335 ++++++++++++++---- gen/characters/pb/characters_grpc.pb.go | 1037 ++- gen/chat/gen/chat/pb/chat.pb.go | 1494 ++-- gen/chat/gen/chat/pb/chat_grpc.pb.go | 2 +- gen/chat/pb/chat.pb.go | 1153 +-- gen/chat/pb/chat_grpc.pb.go | 2 +- gen/group/pb/group.pb.go | 5305 ++++++++--- gen/group/pb/group_grpc.pb.go | 511 +- gen/guid/pb/guid.pb.go | 2 +- gen/guid/pb/guid_grpc.pb.go | 2 +- gen/guilds/pb/guilds.pb.go | 4964 +++++++++-- gen/guilds/pb/guilds_grpc.pb.go | 626 +- gen/mail/pb/mail.pb.go | 2 +- gen/mail/pb/mail_grpc.pb.go | 2 +- gen/matchmaking/pb/matchmaking.pb.go | 3044 ++++++- gen/matchmaking/pb/matchmaking_grpc.pb.go | 261 +- gen/servers-registry/pb/registry.pb.go | 1256 ++- gen/servers-registry/pb/registry_grpc.pb.go | 110 +- gen/worldserver/pb/worldserver.pb.go | 3493 +++++++- gen/worldserver/pb/worldserver_grpc.pb.go | 302 +- go.mod | 3 +- go.sum | 3 + shared/authidentity/identity.go | 116 + shared/events/consumer-gateway.go | 96 +- shared/events/consumer-group.go | 231 + shared/events/events-characters.go | 36 +- shared/events/events-chat.go | 71 +- shared/events/events-friends.go | 2 +- shared/events/events-gateway.go | 87 +- shared/events/events-group.go | 176 +- shared/events/events-guild.go | 48 +- shared/events/events-matchmaking.go | 103 + shared/events/events-serversregistry.go | 34 +- shared/events/producer-characters.go | 14 + shared/events/producer-gateway.go | 27 + shared/events/producer-group.go | 53 + shared/events/producer-guild.go | 11 + shared/events/producer-matchmaking.go | 10 + shared/events/producer-serversregistry.go | 14 + shared/groupstatetrace/trace.go | 137 + shared/repo/charactersdb.go | 13 + shared/wow/arena/team_id.go | 24 + shared/wow/guid/player.go | 67 + shared/wow/power.go | 50 + 53 files changed, 29154 insertions(+), 5790 deletions(-) create mode 100644 shared/authidentity/identity.go create mode 100644 shared/groupstatetrace/trace.go create mode 100644 shared/wow/arena/team_id.go create mode 100644 shared/wow/power.go diff --git a/Makefile b/Makefile index 45523ca..03bd083 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ build-groupserver: go build -o $(INSTALL_PATH)/groupserver apps/groupserver/cmd/groupserver/main.go build-perun: - cd apps/perun && go build -o ../../$(INSTALL_PATH)/perun cmd/perun/main.go && cd ../.. + cd apps/perun && go build -o $(abspath $(INSTALL_PATH))/perun cmd/perun/main.go && cd ../.. build-mysqlreverseproxy: go build -o $(INSTALL_PATH)/mysqlreverseproxy apps/mysqlreverseproxy/cmd/mysqlreverseproxy/main.go diff --git a/api/proto/v1/characters/characters.proto b/api/proto/v1/characters/characters.proto index 29c1373..2408eff 100644 --- a/api/proto/v1/characters/characters.proto +++ b/api/proto/v1/characters/characters.proto @@ -9,20 +9,45 @@ service CharactersService { rpc CharactersToLoginByGUID(CharactersToLoginByGUIDRequest) returns (CharactersToLoginByGUIDResponse); rpc AccountDataForAccount(AccountDataForAccountRequest) returns (AccountDataForAccountResponse); + rpc UpdateAccountDataForAccount(UpdateAccountDataForAccountRequest) returns (UpdateAccountDataForAccountResponse); rpc WhoQuery(WhoQueryRequest) returns (WhoQueryResponse); rpc CharacterOnlineByName(CharacterOnlineByNameRequest) returns (CharacterOnlineByNameResponse); rpc CharacterByName(CharacterByNameRequest) returns (CharacterByNameResponse); + rpc CharacterByGUID(CharacterByGUIDRequest) returns (CharacterByGUIDResponse); rpc ShortOnlineCharactersDataByGUIDs(ShortCharactersDataByGUIDsRequest) returns (ShortCharactersDataByGUIDsResponse); + rpc ArenaTeamQueueDataForRatedArena(ArenaTeamQueueDataForRatedArenaRequest) returns (ArenaTeamQueueDataForRatedArenaResponse); + rpc GetArenaTeam(GetArenaTeamRequest) returns (GetArenaTeamResponse); + rpc GetArenaTeamPetition(GetArenaTeamPetitionRequest) returns (GetArenaTeamPetitionResponse); + rpc CreateArenaTeamFromPetition(CreateArenaTeamFromPetitionRequest) returns (CreateArenaTeamFromPetitionResponse); + rpc InviteArenaTeamMember(InviteArenaTeamMemberRequest) returns (InviteArenaTeamMemberResponse); + rpc AcceptArenaTeamInvite(AcceptArenaTeamInviteRequest) returns (AcceptArenaTeamInviteResponse); + rpc DeclineArenaTeamInvite(DeclineArenaTeamInviteRequest) returns (ArenaTeamMutationResponse); + rpc AddArenaTeamMember(AddArenaTeamMemberRequest) returns (ArenaTeamMutationResponse); + rpc RemoveArenaTeamMember(RemoveArenaTeamMemberRequest) returns (ArenaTeamMutationResponse); + rpc DisbandArenaTeam(DisbandArenaTeamRequest) returns (ArenaTeamMutationResponse); + rpc SetArenaTeamCaptain(SetArenaTeamCaptainRequest) returns (ArenaTeamMutationResponse); + rpc SetArenaTeamName(SetArenaTeamNameRequest) returns (ArenaTeamMutationResponse); + rpc SaveArenaTeamStats(SaveArenaTeamStatsRequest) returns (ArenaTeamMutationResponse); + rpc DeleteAllArenaTeams(DeleteAllArenaTeamsRequest) returns (ArenaTeamMutationResponse); + rpc ValidateArenaTeamCharacterDelete(ValidateArenaTeamCharacterDeleteRequest) returns (ArenaTeamMutationResponse); + rpc RemovePlayerFromArenaTeams(RemovePlayerFromArenaTeamsRequest) returns (ArenaTeamMutationResponse); // Would effect only offline player. rpc SavePlayerPosition(SavePlayerPositionRequest) returns (SavePlayerPositionResponse); + rpc RecordLfgDungeonRoute(RecordLfgDungeonRouteRequest) returns (RecordLfgDungeonRouteResponse); + rpc ConfirmLfgDungeonRouteEntered(ConfirmLfgDungeonRouteEnteredRequest) returns (ConfirmLfgDungeonRouteEnteredResponse); + rpc ClearUnboundLfgDungeonRoute(ClearUnboundLfgDungeonRouteRequest) returns (ClearUnboundLfgDungeonRouteResponse); + rpc GetLfgDungeonRoute(GetLfgDungeonRouteRequest) returns (GetLfgDungeonRouteResponse); // Friends and ignore list management rpc GetFriendsList(GetFriendsListRequest) returns (GetFriendsListResponse); rpc AddFriend(AddFriendRequest) returns (AddFriendResponse); + rpc AddRealIDFriendByEmail(AddRealIDFriendByEmailRequest) returns (AddRealIDFriendByEmailResponse); + rpc AcceptRealIDFriend(AcceptRealIDFriendRequest) returns (AcceptRealIDFriendResponse); + rpc AreRealIDFriends(AreRealIDFriendsRequest) returns (AreRealIDFriendsResponse); rpc RemoveFriend(RemoveFriendRequest) returns (RemoveFriendResponse); rpc SetFriendNote(SetFriendNoteRequest) returns (SetFriendNoteResponse); rpc AddIgnore(AddIgnoreRequest) returns (AddIgnoreResponse); @@ -60,6 +85,7 @@ message LogInCharacter { bool banned = 23; repeated EquipmentDisplay equipments = 24; uint32 accountID = 25; + uint32 extraFlags = 26; } message EquipmentDisplay { @@ -88,6 +114,7 @@ message CharactersToLoginByGUIDRequest{ uint64 characterGUID = 2; uint32 realmID = 3; + uint32 accountID = 4; } // Response that contains character to login @@ -117,6 +144,20 @@ message AccountDataForAccountResponse{ repeated AccountData accountData = 2; } +message UpdateAccountDataForAccountRequest{ + string api = 1; + + uint32 accountID = 2; + uint32 realmID = 3; + uint32 type = 4; + int64 time = 5; + string data = 6; +} + +message UpdateAccountDataForAccountResponse{ + string api = 1; +} + message WhoQueryRequest{ string api = 1; @@ -208,6 +249,19 @@ message CharacterByNameResponse { Char character = 2; } +message CharacterByGUIDRequest { + string api = 1; + + uint32 realmID = 2; + uint64 characterGUID = 3; +} + +message CharacterByGUIDResponse { + string api = 1; + + CharacterByNameResponse.Char character = 2; +} + message ShortCharactersDataByGUIDsRequest { string api = 1; @@ -237,6 +291,306 @@ message ShortCharactersDataByGUIDsResponse { repeated ShortCharData characters = 2; } +message ArenaTeamQueueDataForRatedArenaRequest { + string api = 1; + + uint32 realmID = 2; + uint64 leaderGUID = 3; + repeated uint64 playerGUIDs = 4; + uint32 arenaType = 5; + uint32 startMatchmakerRating = 6; +} + +message ArenaTeamQueueDataForRatedArenaResponse { + string api = 1; + + enum Status { + Ok = 0; + NotFound = 1; + MemberMismatch = 2; + InvalidPartySize = 3; + Failed = 4; + InvalidType = 5; + } + + Status status = 2; + uint32 arenaTeamID = 3; + uint32 teamRating = 4; + uint32 matchmakerRating = 5; + uint32 previousOpponentsTeamID = 6; +} + +message CreateArenaTeamFromPetitionRequest { + string api = 1; + + uint32 realmID = 2; + uint64 captainGUID = 3; + uint64 petitionGUID = 4; + uint32 arenaType = 5; + uint32 backgroundColor = 6; + uint32 emblemStyle = 7; + uint32 emblemColor = 8; + uint32 borderStyle = 9; + uint32 borderColor = 10; + uint32 startRating = 11; + uint32 startPersonalRating = 12; + uint32 startMatchmakerRating = 13; +} + +message CreateArenaTeamFromPetitionResponse { + string api = 1; + + enum Status { + Ok = 0; + NotFound = 1; + NotOwner = 2; + InvalidType = 3; + NameExists = 4; + AlreadyInTeam = 5; + NotEnoughSignatures = 6; + Failed = 7; + InvalidName = 8; + } + + Status status = 2; + uint32 arenaTeamID = 3; +} + +enum ArenaTeamMutationStatus { + ARENA_TEAM_MUTATION_OK = 0; + ARENA_TEAM_MUTATION_NOT_FOUND = 1; + ARENA_TEAM_MUTATION_MEMBER_MISMATCH = 2; + ARENA_TEAM_MUTATION_INVALID_TYPE = 3; + ARENA_TEAM_MUTATION_NAME_EXISTS = 4; + ARENA_TEAM_MUTATION_ALREADY_IN_TEAM = 5; + ARENA_TEAM_MUTATION_ROSTER_FULL = 6; + ARENA_TEAM_MUTATION_FAILED = 7; + ARENA_TEAM_MUTATION_INVALID_NAME = 8; + ARENA_TEAM_MUTATION_PERMISSION_DENIED = 9; + ARENA_TEAM_MUTATION_LEADER_LEAVE = 10; + ARENA_TEAM_MUTATION_ALREADY_INVITED = 11; + ARENA_TEAM_MUTATION_IGNORING_YOU = 12; +} + +message ArenaTeamMemberData { + uint64 playerGUID = 1; + string name = 2; + bool online = 3; + uint32 level = 4; + uint32 class = 5; + uint32 weekGames = 6; + uint32 weekWins = 7; + uint32 seasonGames = 8; + uint32 seasonWins = 9; + uint32 personalRating = 10; + uint32 matchmakerRating = 11; + uint32 maxMMR = 12; +} + +message ArenaTeamData { + uint32 arenaTeamID = 1; + string name = 2; + uint64 captainGUID = 3; + uint32 type = 4; + uint32 rating = 5; + uint32 weekGames = 6; + uint32 weekWins = 7; + uint32 seasonGames = 8; + uint32 seasonWins = 9; + uint32 rank = 10; + uint32 backgroundColor = 11; + uint32 emblemStyle = 12; + uint32 emblemColor = 13; + uint32 borderStyle = 14; + uint32 borderColor = 15; + repeated ArenaTeamMemberData members = 16; +} + +message GetArenaTeamRequest { + string api = 1; + + uint32 realmID = 2; + uint32 arenaTeamID = 3; +} + +message GetArenaTeamResponse { + string api = 1; + + enum Status { + Ok = 0; + NotFound = 1; + Failed = 2; + } + + Status status = 2; + ArenaTeamData team = 3; +} + +message ArenaTeamPetitionData { + uint64 petitionGUID = 1; + uint32 petitionID = 2; + uint64 ownerGUID = 3; + string name = 4; + uint32 arenaType = 5; + uint32 signatures = 6; +} + +message GetArenaTeamPetitionRequest { + string api = 1; + + uint32 realmID = 2; + uint64 petitionGUID = 3; +} + +message GetArenaTeamPetitionResponse { + string api = 1; + + enum Status { + Ok = 0; + NotFound = 1; + NotArena = 2; + Failed = 3; + } + + Status status = 2; + ArenaTeamPetitionData petition = 3; +} + +message InviteArenaTeamMemberRequest { + string api = 1; + + uint32 realmID = 2; + uint32 arenaTeamID = 3; + uint64 inviterGUID = 4; + string inviterName = 5; + uint64 targetGUID = 6; + string targetName = 7; +} + +message InviteArenaTeamMemberResponse { + string api = 1; + ArenaTeamMutationStatus status = 2; + ArenaTeamData team = 3; +} + +message AcceptArenaTeamInviteRequest { + string api = 1; + + uint32 realmID = 2; + uint64 playerGUID = 3; + string playerName = 4; + uint32 personalRating = 5; +} + +message AcceptArenaTeamInviteResponse { + string api = 1; + ArenaTeamMutationStatus status = 2; + ArenaTeamData team = 3; +} + +message DeclineArenaTeamInviteRequest { + string api = 1; + + uint32 realmID = 2; + uint64 playerGUID = 3; +} + +message AddArenaTeamMemberRequest { + string api = 1; + + uint32 realmID = 2; + uint32 arenaTeamID = 3; + uint64 playerGUID = 4; + uint32 personalRating = 5; +} + +message RemoveArenaTeamMemberRequest { + string api = 1; + + uint32 realmID = 2; + uint32 arenaTeamID = 3; + uint64 playerGUID = 4; + uint64 actorGUID = 5; +} + +message DisbandArenaTeamRequest { + string api = 1; + + uint32 realmID = 2; + uint32 arenaTeamID = 3; + uint64 actorGUID = 4; +} + +message SetArenaTeamCaptainRequest { + string api = 1; + + uint32 realmID = 2; + uint32 arenaTeamID = 3; + uint64 captainGUID = 4; + uint64 actorGUID = 5; +} + +message SetArenaTeamNameRequest { + string api = 1; + + uint32 realmID = 2; + uint32 arenaTeamID = 3; + string name = 4; + uint64 actorGUID = 5; +} + +message ArenaTeamStatsMember { + uint64 playerGUID = 1; + uint32 personalRating = 2; + uint32 weekGames = 3; + uint32 weekWins = 4; + uint32 seasonGames = 5; + uint32 seasonWins = 6; + uint32 matchmakerRating = 7; + uint32 maxMMR = 8; + bool saveArenaStats = 9; +} + +message SaveArenaTeamStatsRequest { + string api = 1; + + uint32 realmID = 2; + uint32 arenaTeamID = 3; + uint32 rating = 4; + uint32 weekGames = 5; + uint32 weekWins = 6; + uint32 seasonGames = 7; + uint32 seasonWins = 8; + uint32 rank = 9; + uint32 slot = 10; + repeated ArenaTeamStatsMember members = 11; +} + +message DeleteAllArenaTeamsRequest { + string api = 1; + + uint32 realmID = 2; +} + +message ValidateArenaTeamCharacterDeleteRequest { + string api = 1; + + uint32 realmID = 2; + uint64 playerGUID = 3; +} + +message RemovePlayerFromArenaTeamsRequest { + string api = 1; + + uint32 realmID = 2; + uint64 playerGUID = 3; +} + +message ArenaTeamMutationResponse { + string api = 1; + ArenaTeamMutationStatus status = 2; +} + message SavePlayerPositionRequest { string api = 1; @@ -254,6 +608,67 @@ message SavePlayerPositionResponse { string api = 1; } +message LfgDungeonRoute { + uint32 realmID = 1; + uint64 playerGUID = 2; + uint32 dungeonEntry = 3; + uint32 mapID = 4; + uint32 difficulty = 5; + uint32 ownerRealmID = 6; + bool isCrossRealm = 7; + bool requiresBoundInstance = 8; + uint32 instanceID = 9; + uint32 boundInstanceID = 10; +} + +message RecordLfgDungeonRouteRequest { + string api = 1; + LfgDungeonRoute route = 2; +} + +message RecordLfgDungeonRouteResponse { + string api = 1; +} + +message ConfirmLfgDungeonRouteEnteredRequest { + string api = 1; + uint32 realmID = 2; + uint64 playerGUID = 3; + uint32 mapID = 4; + uint32 difficulty = 5; + uint32 instanceID = 6; +} + +message ConfirmLfgDungeonRouteEnteredResponse { + string api = 1; + LfgDungeonRoute route = 2; +} + +message ClearUnboundLfgDungeonRouteRequest { + string api = 1; + uint32 realmID = 2; + uint64 playerGUID = 3; + uint32 mapID = 4; +} + +message ClearUnboundLfgDungeonRouteResponse { + string api = 1; +} + +message GetLfgDungeonRouteRequest { + string api = 1; + uint32 realmID = 2; + uint64 playerGUID = 3; + uint32 mapID = 4; +} + +message GetLfgDungeonRouteResponse { + string api = 1; + bool found = 2; + bool available = 3; + LfgDungeonRoute route = 4; +} + // Friends list requests and responses message GetFriendsListRequest { @@ -272,6 +687,7 @@ message GetFriendsListResponse { uint32 area = 4; // zone ID if online uint32 level = 5; uint32 classID = 6; + uint32 realmID = 7; } message IgnoredPlayer { @@ -298,6 +714,62 @@ message AddFriendResponse { uint32 area = 4; uint32 level = 5; uint32 classID = 6; + uint64 friendGUID = 7; + bool pending = 8; + bool accepted = 9; +} + +message AddRealIDFriendByEmailRequest { + string api = 1; + uint32 realmID = 2; + uint64 playerGUID = 3; + uint32 accountID = 4; + string email = 5; + string note = 6; +} + +message AddRealIDFriendByEmailResponse { + string api = 1; + uint32 result = 2; // FriendsResult enum value + uint32 status = 3; // 0=offline, 1=online + uint32 area = 4; + uint32 level = 5; + uint32 classID = 6; + uint64 friendGUID = 7; + bool pending = 8; + bool accepted = 9; +} + +message AcceptRealIDFriendRequest { + string api = 1; + uint32 realmID = 2; + uint64 playerGUID = 3; + uint32 accountID = 4; + uint32 requesterAccountID = 5; + string note = 6; +} + +message AcceptRealIDFriendResponse { + string api = 1; + uint32 result = 2; // FriendsResult enum value + uint32 status = 3; // 0=offline, 1=online + uint32 area = 4; + uint32 level = 5; + uint32 classID = 6; + uint64 friendGUID = 7; + bool pending = 8; + bool accepted = 9; +} + +message AreRealIDFriendsRequest { + string api = 1; + uint32 accountID = 2; + uint32 friendAccountID = 3; +} + +message AreRealIDFriendsResponse { + string api = 1; + bool accepted = 2; } message RemoveFriendRequest { @@ -369,4 +841,4 @@ message GetOnlineCharactersResponse { string api = 1; repeated uint64 characterGUIDs = 2; uint32 totalCount = 3; -} \ No newline at end of file +} diff --git a/api/proto/v1/chat/chat.proto b/api/proto/v1/chat/chat.proto index 2a4c1c1..c66cb83 100644 --- a/api/proto/v1/chat/chat.proto +++ b/api/proto/v1/chat/chat.proto @@ -45,6 +45,12 @@ message SendWhisperMessageRequest { uint32 language = 6; string receiverName = 7; string msg = 8; + uint32 receiverRealmID = 9; + uint32 senderClass = 10; + uint32 senderGender = 11; + uint32 senderAccountID = 12; + bool gatewayValidatedGameplayCrossrealmWhisper = 13; + uint32 senderChatTag = 14; } message SendWhisperMessageResponse { @@ -53,10 +59,16 @@ message SendWhisperMessageResponse { enum Status { Ok = 0; CharacterNotFound = 2; + CharacterAmbiguous = 3; } Status status = 2; uint64 receiverGUID = 3; + uint32 receiverRealmID = 4; + string receiverName = 5; + uint32 receiverRace = 6; + uint32 receiverClass = 7; + uint32 receiverGender = 8; } // Channel messages @@ -117,6 +129,7 @@ message SendChannelMessageRequest { uint32 language = 6; string message = 7; TeamID teamID = 8; + uint32 senderChatTag = 9; } message SendChannelMessageResponse { diff --git a/api/proto/v1/group/group.proto b/api/proto/v1/group/group.proto index aed41ac..e535b78 100644 --- a/api/proto/v1/group/group.proto +++ b/api/proto/v1/group/group.proto @@ -12,10 +12,12 @@ service GroupService { rpc ChangeLeader(ChangeLeaderParams) returns (ChangeLeaderResponse); rpc AcceptInvite(AcceptInviteParams) returns (AcceptInviteResponse); + rpc DeclineInvite(DeclineInviteParams) returns (DeclineInviteResponse); rpc GetGroup(GetGroupRequest) returns (GetGroupResponse); rpc GetGroupByMember(GetGroupByMemberRequest) returns (GetGroupResponse); rpc GetGroupIDByPlayer(GetGroupIDByPlayerRequest) returns (GetGroupIDByPlayerResponse); + rpc GetMemberPlacements(GetMemberPlacementsRequest) returns (GetMemberPlacementsResponse); rpc SetGroupTargetIcon(SetGroupTargetIconRequest) returns (SetGroupTargetIconResponse); rpc SetLootMethod(SetLootMethodRequest) returns (SetLootMethodResponse); @@ -24,6 +26,18 @@ service GroupService { rpc SetRaidDifficulty(SetRaidDifficultyRequest) returns (SetRaidDifficultyResponse); rpc SendMessage(SendGroupMessageParams) returns (SendGroupMessageResponse); + + rpc StartReadyCheck(StartReadyCheckRequest) returns (StartReadyCheckResponse); + rpc SetReadyCheckMemberState(SetReadyCheckMemberStateRequest) returns (SetReadyCheckMemberStateResponse); + rpc FinishReadyCheck(FinishReadyCheckRequest) returns (FinishReadyCheckResponse); + rpc ChangeMemberSubGroup(ChangeMemberSubGroupRequest) returns (ChangeMemberSubGroupResponse); + rpc SetMemberFlags(SetMemberFlagsRequest) returns (SetMemberFlagsResponse); + rpc RegisterAcceptedLfgGroup(RegisterAcceptedLfgGroupRequest) returns (RegisterAcceptedLfgGroupResponse); + rpc RegisterMaterializedLfgGroup(RegisterMaterializedLfgGroupRequest) returns (RegisterMaterializedLfgGroupResponse); + rpc UpdateMemberState(UpdateMemberStateRequest) returns (UpdateMemberStateResponse); + rpc BulkUpdateMemberStates(BulkUpdateMemberStatesRequest) returns (BulkUpdateMemberStatesResponse); + rpc ResetInstance(ResetInstanceRequest) returns (ResetInstanceResponse); + rpc SetInstanceBindExtension(SetInstanceBindExtensionRequest) returns (SetInstanceBindExtensionResponse); } message InviteParams { @@ -68,6 +82,25 @@ message AcceptInviteResponse { Status status = 2; } +message DeclineInviteParams { + string api = 1; + + uint32 realmID = 2; + uint64 player = 3; +} + +message DeclineInviteResponse { + enum Status { + Ok = 0; + InviteNotFound = 1; + Error = 2; + } + + string api = 1; + + Status status = 2; +} + message GetGroupRequest { string api = 1; @@ -85,6 +118,7 @@ message GetGroupResponse { bool isOnline = 4; uint32 subGroup = 5; uint32 roles = 6; + uint32 realmID = 7; } message Group { @@ -99,6 +133,7 @@ message GetGroupResponse { uint64 masterLooter = 9; repeated uint64 targetIconsList = 10; repeated GroupMember members = 11; + uint32 realmID = 12; } Group group = 2; @@ -122,6 +157,33 @@ message GetGroupIDByPlayerResponse { string api = 1; uint32 groupID = 2; + uint32 groupRealmID = 3; +} + +message GetMemberPlacementsRequest { + string api = 1; + + uint32 realmID = 2; + repeated uint64 memberGUIDs = 3; +} + +message MemberPlacement { + uint64 memberGUID = 1; + bool online = 2; + bool fresh = 3; + string gatewayID = 4; + string worldserverID = 5; + uint32 mapID = 6; + uint32 instanceID = 7; + bool instanceKnown = 8; + uint64 timestampMs = 9; + uint64 updatedAtMs = 10; +} + +message GetMemberPlacementsResponse { + string api = 1; + + repeated MemberPlacement placements = 2; } message UninviteParams { @@ -185,6 +247,7 @@ message SendGroupMessageParams { uint32 messageType = 4; string message = 5; uint32 language = 6; + uint32 senderChatTag = 7; } message SendGroupMessageResponse { @@ -264,4 +327,195 @@ message SetRaidDifficultyResponse { } Status status = 2; -} \ No newline at end of file +} + +message StartReadyCheckRequest { + string api = 1; + uint32 realmID = 2; + uint64 leaderGUID = 3; + uint32 durationMs = 4; +} + +message StartReadyCheckResponse { + string api = 1; +} + +message SetReadyCheckMemberStateRequest { + string api = 1; + uint32 realmID = 2; + uint64 memberGUID = 3; + uint32 state = 4; +} + +message SetReadyCheckMemberStateResponse { + string api = 1; +} + +message FinishReadyCheckRequest { + string api = 1; + uint32 realmID = 2; + uint64 playerGUID = 3; +} + +message FinishReadyCheckResponse { + string api = 1; +} + +message ChangeMemberSubGroupRequest { + string api = 1; + uint32 realmID = 2; + uint64 updaterGUID = 3; + uint64 memberGUID = 4; + uint32 subGroup = 5; +} + +message ChangeMemberSubGroupResponse { + string api = 1; +} + +message SetMemberFlagsRequest { + string api = 1; + uint32 realmID = 2; + uint64 updaterGUID = 3; + uint64 memberGUID = 4; + uint32 flags = 5; + uint32 roles = 6; +} + +message SetMemberFlagsResponse { + string api = 1; +} + +message AcceptedLfgGroupMember { + uint32 realmID = 1; + uint64 playerGUID = 2; + uint32 selectedRoles = 3; + uint32 assignedRole = 4; + uint32 queueLeaderRealmID = 5; + uint64 queueLeaderGUID = 6; +} + +message RegisterAcceptedLfgGroupRequest { + string api = 1; + uint32 realmID = 2; + uint32 proposalID = 3; + uint32 dungeonEntry = 4; + uint32 leaderRealmID = 5; + uint64 leaderGUID = 6; + bool crossRealm = 7; + repeated AcceptedLfgGroupMember members = 8; +} + +message RegisterAcceptedLfgGroupResponse { + string api = 1; + uint32 groupID = 2; +} + +message MaterializedLfgGroupMember { + uint32 realmID = 1; + uint64 playerGUID = 2; + string name = 3; + bool isOnline = 4; + uint32 flags = 5; + uint32 roles = 6; + uint32 subGroup = 7; +} + +message RegisterMaterializedLfgGroupRequest { + string api = 1; + uint32 realmID = 2; + uint32 groupID = 3; + uint64 leaderGUID = 4; + uint32 groupType = 5; + uint32 difficulty = 6; + uint32 raidDifficulty = 7; + repeated MaterializedLfgGroupMember members = 8; +} + +message RegisterMaterializedLfgGroupResponse { + string api = 1; +} + +message UpdateMemberStateRequest { + string api = 1; + uint32 realmID = 2; + uint64 memberGUID = 3; + bool online = 4; + uint32 level = 5; + uint32 classID = 6; + uint32 zoneID = 7; + uint32 mapID = 8; + uint32 health = 9; + uint32 maxHealth = 10; + uint32 powerType = 11; + uint32 power = 12; + uint32 maxPower = 13; + optional uint32 instanceID = 14; +} + +message UpdateMemberStateResponse { + string api = 1; +} + +message PlayerStateSnapshot { + uint64 memberGUID = 1; + bool online = 2; + uint32 level = 3; + uint32 classID = 4; + uint32 zoneID = 5; + uint32 mapID = 6; + uint32 health = 7; + uint32 maxHealth = 8; + uint32 powerType = 9; + uint32 power = 10; + uint32 maxPower = 11; + uint64 timestampMs = 12; + bool aurasKnown = 13; + repeated PlayerAuraSnapshot auras = 14; + optional uint32 instanceID = 15; + optional bool dead = 16; + optional bool ghost = 17; +} + +message PlayerAuraSnapshot { + uint32 slot = 1; + uint32 spellID = 2; + uint32 flags = 3; +} + +message BulkUpdateMemberStatesRequest { + string api = 1; + uint32 realmID = 2; + string sourceGatewayID = 3; + string sourceWorldserverID = 4; + repeated PlayerStateSnapshot snapshots = 5; +} + +message BulkUpdateMemberStatesResponse { + string api = 1; +} + +message ResetInstanceRequest { + string api = 1; + uint32 realmID = 2; + uint64 playerGUID = 3; + uint32 mapID = 4; + uint32 difficulty = 5; +} + +message ResetInstanceResponse { + string api = 1; +} + +message SetInstanceBindExtensionRequest { + string api = 1; + uint32 realmID = 2; + uint64 playerGUID = 3; + uint32 mapID = 4; + uint32 difficulty = 5; + bool extended = 6; +} + +message SetInstanceBindExtensionResponse { + string api = 1; +} diff --git a/api/proto/v1/guilds/guilds.proto b/api/proto/v1/guilds/guilds.proto index c4525f0..fe09e55 100644 --- a/api/proto/v1/guilds/guilds.proto +++ b/api/proto/v1/guilds/guilds.proto @@ -28,6 +28,24 @@ service GuildService { rpc DemoteMember(PromoteDemoteParams) returns (PromoteDemoteResponse); rpc SendGuildMessage(SendGuildMessageParams) returns (SendGuildMessageResponse); + + rpc GetGuildPetition(GetGuildPetitionParams) returns (GetGuildPetitionResponse); + rpc OfferGuildPetition(OfferGuildPetitionParams) returns (OfferGuildPetitionResponse); + rpc SignGuildPetition(SignGuildPetitionParams) returns (SignGuildPetitionResponse); + + rpc GetGuildBank(GetGuildBankParams) returns (GetGuildBankResponse); + rpc GetGuildBankLog(GetGuildBankLogParams) returns (GetGuildBankLogResponse); + rpc GetGuildBankTabText(GetGuildBankTabTextParams) returns (GetGuildBankTabTextResponse); + rpc UpdateGuildBankTab(UpdateGuildBankTabParams) returns (GuildBankActionResponse); + rpc SetGuildBankTabText(SetGuildBankTabTextParams) returns (GuildBankActionResponse); + rpc BuyGuildBankTab(BuyGuildBankTabParams) returns (BuyGuildBankTabResponse); + rpc DepositGuildBankMoney(DepositGuildBankMoneyParams) returns (GuildBankActionResponse); + rpc WithdrawGuildBankMoney(WithdrawGuildBankMoneyParams) returns (GuildBankActionResponse); + rpc RollbackGuildBankMoneyWithdraw(RollbackGuildBankMoneyWithdrawParams) returns (GuildBankActionResponse); + rpc DepositGuildBankItem(DepositGuildBankItemParams) returns (GuildBankItemMutationResponse); + rpc WithdrawGuildBankItem(WithdrawGuildBankItemParams) returns (GuildBankItemMutationResponse); + rpc RollbackGuildBankItemWithdraw(RollbackGuildBankItemWithdrawParams) returns (GuildBankItemMutationResponse); + rpc MoveGuildBankItem(MoveGuildBankItemParams) returns (GuildBankItemMutationResponse); } message GetInfoParams { @@ -73,12 +91,21 @@ message GetRosterInfoResponse { int64 logoutTime = 9; string note = 10; string officerNote = 11; + repeated uint32 bankWithdraw = 12; } message Rank { uint32 id = 1; uint32 flags = 2; uint32 goldLimit = 3; + + message BankTabRight { + uint32 tabID = 1; + uint32 flags = 2; + uint32 withdrawItemLimit = 3; + } + + repeated BankTabRight bankTabRights = 4; } message Guild { @@ -88,6 +115,7 @@ message GetRosterInfoResponse { repeated Member members = 4; repeated Rank ranks = 5; + uint32 purchasedBankTabs = 6; }; Guild guild = 2; @@ -100,6 +128,8 @@ message InviteMemberParams { uint64 inviter = 3; uint64 invitee = 4; string inviteeName = 5; + uint32 inviteeRace = 6; + bool allowCrossFaction = 7; } message InviteMemberResponse { @@ -128,6 +158,7 @@ message InviteAcceptedParams { } Character character = 3; + bool allowCrossFaction = 4; } message InviteAcceptedResponse { @@ -198,6 +229,14 @@ message RankUpdateParams { string rankName = 5; uint32 rights = 6; uint32 moneyPerDay = 7; + + message BankTabRight { + uint32 tabID = 1; + uint32 flags = 2; + uint32 withdrawItemLimit = 3; + } + + repeated BankTabRight bankTabRights = 8; } message RankUpdateResponse { @@ -243,8 +282,308 @@ message SendGuildMessageParams { bool isOfficerMessage = 4; string message = 5; uint32 language = 6; + uint32 senderChatTag = 7; } message SendGuildMessageResponse { string api = 1; } + +message GuildPetitionSignature { + uint64 playerGUID = 1; + uint32 playerAccount = 2; +} + +message GuildPetition { + uint64 petitionGUID = 1; + uint32 petitionID = 2; + uint64 ownerGUID = 3; + string name = 4; + uint32 type = 5; + repeated GuildPetitionSignature signatures = 6; +} + +message GetGuildPetitionParams { + string api = 1; + uint32 realmID = 2; + uint64 petitionGUID = 3; +} + +message GetGuildPetitionResponse { + string api = 1; + GuildPetition petition = 2; +} + +message OfferGuildPetitionParams { + string api = 1; + uint32 realmID = 2; + uint64 ownerGUID = 3; + uint64 targetGUID = 4; + string targetName = 5; + uint64 petitionGUID = 6; +} + +message OfferGuildPetitionResponse { + string api = 1; + + enum Status { + Ok = 0; + NotFound = 1; + NotOwner = 2; + TargetNotFound = 3; + TargetAlreadyInGuild = 4; + TargetAlreadyInvited = 5; + Failed = 6; + } + + Status status = 2; +} + +message SignGuildPetitionParams { + string api = 1; + uint32 realmID = 2; + uint64 signerGUID = 3; + string signerName = 4; + uint32 signerAccountID = 5; + uint32 signerGuildID = 6; + uint64 petitionGUID = 7; +} + +message SignGuildPetitionResponse { + string api = 1; + + enum Status { + Ok = 0; + AlreadySigned = 1; + AlreadyInGuild = 2; + CantSignOwn = 3; + NotServer = 4; + NotFound = 5; + Full = 6; + Failed = 7; + AlreadyInvited = 8; + } + + Status status = 2; +} + +message GuildBankSocketEnchant { + uint32 socketIndex = 1; + uint32 socketEnchantID = 2; +} + +message GuildBankItem { + uint64 itemGUID = 1; + uint32 entry = 2; + uint32 slot = 3; + uint32 count = 4; + uint32 flags = 5; + int32 randomPropertyID = 6; + int32 randomPropertySeed = 7; + uint32 durability = 8; + uint32 enchantmentID = 9; + repeated GuildBankSocketEnchant socketEnchants = 10; + uint32 charges = 11; + string text = 12; +} + +message GuildBankTab { + uint32 tabID = 1; + string name = 2; + string icon = 3; + string text = 4; + repeated GuildBankItem items = 5; +} + +message GuildBankStatus { + enum Status { + Ok = 0; + Failed = 1; + GuildNotFound = 2; + NotInGuild = 3; + NotEnoughRights = 4; + InvalidTab = 5; + InvalidSlot = 6; + NotEnoughMoney = 7; + BankFull = 8; + WithdrawLimit = 9; + ItemNotFound = 10; + } +} + +message GetGuildBankParams { + string api = 1; + uint32 realmID = 2; + uint64 guildID = 3; + uint64 memberGUID = 4; + uint32 tabID = 5; + bool fullUpdate = 6; +} + +message GetGuildBankResponse { + string api = 1; + GuildBankStatus.Status status = 2; + uint64 money = 3; + uint32 tabID = 4; + int32 withdrawalsRemaining = 5; + bool fullUpdate = 6; + repeated GuildBankTab tabs = 7; + repeated GuildBankItem items = 8; +} + +message GetGuildBankLogParams { + string api = 1; + uint32 realmID = 2; + uint64 guildID = 3; + uint64 memberGUID = 4; + uint32 tabID = 5; +} + +message GuildBankLogEntry { + uint64 playerGUID = 1; + uint32 timeOffset = 2; + int32 entryType = 3; + uint32 money = 4; + int32 itemID = 5; + int32 count = 6; + int32 otherTab = 7; +} + +message GetGuildBankLogResponse { + string api = 1; + GuildBankStatus.Status status = 2; + uint32 tabID = 3; + repeated GuildBankLogEntry entries = 4; +} + +message GetGuildBankTabTextParams { + string api = 1; + uint32 realmID = 2; + uint64 guildID = 3; + uint64 memberGUID = 4; + uint32 tabID = 5; +} + +message GetGuildBankTabTextResponse { + string api = 1; + GuildBankStatus.Status status = 2; + uint32 tabID = 3; + string text = 4; +} + +message UpdateGuildBankTabParams { + string api = 1; + uint32 realmID = 2; + uint64 guildID = 3; + uint64 memberGUID = 4; + uint32 tabID = 5; + string name = 6; + string icon = 7; +} + +message SetGuildBankTabTextParams { + string api = 1; + uint32 realmID = 2; + uint64 guildID = 3; + uint64 memberGUID = 4; + uint32 tabID = 5; + string text = 6; +} + +message BuyGuildBankTabParams { + string api = 1; + uint32 realmID = 2; + uint64 guildID = 3; + uint64 memberGUID = 4; + uint32 tabID = 5; +} + +message BuyGuildBankTabResponse { + string api = 1; + GuildBankStatus.Status status = 2; + uint32 tabCost = 3; +} + +message DepositGuildBankMoneyParams { + string api = 1; + uint32 realmID = 2; + uint64 guildID = 3; + uint64 memberGUID = 4; + uint32 amount = 5; +} + +message WithdrawGuildBankMoneyParams { + string api = 1; + uint32 realmID = 2; + uint64 guildID = 3; + uint64 memberGUID = 4; + uint32 amount = 5; + bool repair = 6; +} + +message RollbackGuildBankMoneyWithdrawParams { + string api = 1; + uint32 realmID = 2; + uint64 guildID = 3; + uint64 memberGUID = 4; + uint32 amount = 5; + bool repair = 6; + uint32 logGUID = 7; +} + +message DepositGuildBankItemParams { + string api = 1; + uint32 realmID = 2; + uint64 guildID = 3; + uint64 memberGUID = 4; + uint32 tabID = 5; + uint32 slotID = 6; + GuildBankItem item = 7; +} + +message WithdrawGuildBankItemParams { + string api = 1; + uint32 realmID = 2; + uint64 guildID = 3; + uint64 memberGUID = 4; + uint32 tabID = 5; + uint32 slotID = 6; + uint32 count = 7; +} + +message RollbackGuildBankItemWithdrawParams { + string api = 1; + uint32 realmID = 2; + uint64 guildID = 3; + uint64 memberGUID = 4; + uint32 tabID = 5; + uint32 slotID = 6; + GuildBankItem item = 7; + uint32 logGUID = 8; +} + +message MoveGuildBankItemParams { + string api = 1; + uint32 realmID = 2; + uint64 guildID = 3; + uint64 memberGUID = 4; + uint32 sourceTabID = 5; + uint32 sourceSlotID = 6; + uint32 destinationTabID = 7; + uint32 destinationSlotID = 8; + uint32 count = 9; +} + +message GuildBankActionResponse { + string api = 1; + GuildBankStatus.Status status = 2; + uint32 logGUID = 3; +} + +message GuildBankItemMutationResponse { + string api = 1; + GuildBankStatus.Status status = 2; + GuildBankItem item = 3; + repeated uint32 changedSlots = 4; + uint32 logGUID = 5; +} diff --git a/api/proto/v1/matchmaking/matchmaking.proto b/api/proto/v1/matchmaking/matchmaking.proto index a7e8c6f..4cc142e 100644 --- a/api/proto/v1/matchmaking/matchmaking.proto +++ b/api/proto/v1/matchmaking/matchmaking.proto @@ -12,6 +12,14 @@ service MatchmakingService { rpc PlayerLeftBattleground(PlayerLeftBattlegroundRequest) returns (PlayerLeftBattlegroundResponse); rpc PlayerJoinedBattleground(PlayerJoinedBattlegroundRequest) returns (PlayerJoinedBattlegroundResponse); rpc BattlegroundStatusChanged(BattlegroundStatusChangedRequest) returns (BattlegroundStatusChangedResponse); + rpc FinishRatedArenaMatch(FinishRatedArenaMatchRequest) returns (FinishRatedArenaMatchResponse); + + rpc JoinLfg(JoinLfgRequest) returns (JoinLfgResponse); + rpc LeaveLfg(LeaveLfgRequest) returns (LeaveLfgResponse); + rpc SetLfgRoles(SetLfgRolesRequest) returns (SetLfgRolesResponse); + rpc AnswerLfgProposal(AnswerLfgProposalRequest) returns (AnswerLfgProposalResponse); + rpc LfgStatus(LfgStatusRequest) returns (LfgStatusResponse); + rpc CompleteLfgDungeon(CompleteLfgDungeonRequest) returns (CompleteLfgDungeonResponse); } enum PVPTeamID { @@ -38,6 +46,8 @@ message EnqueueToBattlegroundRequest { uint32 bgTypeID = 6; PVPTeamID teamID = 7; + uint32 arenaType = 8; + bool isRated = 9; } message EnqueueToBattlegroundResponse { @@ -72,6 +82,7 @@ message BattlegroundQueueDataForPlayerResponse { uint32 battlegroupID = 3; string gameserverAddress = 4; string gameserverGRPCAddress = 5; + PVPTeamID assignedTeamID = 6; } message QueueSlot { @@ -129,4 +140,212 @@ message BattlegroundStatusChangedRequest { message BattlegroundStatusChangedResponse { string api = 1; -} \ No newline at end of file +} + +enum MatchmakingArenaTeamMutationStatus { + MATCHMAKING_ARENA_TEAM_MUTATION_OK = 0; + MATCHMAKING_ARENA_TEAM_MUTATION_NOT_FOUND = 1; + MATCHMAKING_ARENA_TEAM_MUTATION_MEMBER_MISMATCH = 2; + MATCHMAKING_ARENA_TEAM_MUTATION_INVALID_TYPE = 3; + MATCHMAKING_ARENA_TEAM_MUTATION_NAME_EXISTS = 4; + MATCHMAKING_ARENA_TEAM_MUTATION_ALREADY_IN_TEAM = 5; + MATCHMAKING_ARENA_TEAM_MUTATION_ROSTER_FULL = 6; + MATCHMAKING_ARENA_TEAM_MUTATION_FAILED = 7; + MATCHMAKING_ARENA_TEAM_MUTATION_INVALID_NAME = 8; +} + +message RatedArenaParticipant { + PVPTeamID team = 1; + uint64 playerGUID = 2; +} + +message RatedArenaTeamScore { + uint32 realmID = 1; + uint32 arenaTeamID = 2; + string teamName = 3; + int32 ratingChange = 4; + uint32 matchmakerRating = 5; +} + +message RatedArenaMemberResult { + PVPTeamID team = 1; + uint64 playerGUID = 2; + uint32 personalRating = 3; + uint32 weekGames = 4; + uint32 seasonGames = 5; + uint32 weekWins = 6; + uint32 seasonWins = 7; + uint32 matchmakerRating = 8; +} + +message FinishRatedArenaMatchRequest { + string api = 1; + + uint32 ownerRealmID = 2; + bool isCrossRealm = 3; + uint32 instanceID = 4; + uint32 arenaType = 5; + PVPTeamID winnerTeam = 6; + bool validArena = 7; + + uint32 allianceArenaTeamID = 8; + uint32 hordeArenaTeamID = 9; + uint32 allianceArenaMatchmakerRating = 10; + uint32 hordeArenaMatchmakerRating = 11; + repeated RatedArenaParticipant participants = 12; +} + +message FinishRatedArenaMatchResponse { + string api = 1; + MatchmakingArenaTeamMutationStatus status = 2; + RatedArenaTeamScore allianceScore = 3; + RatedArenaTeamScore hordeScore = 4; + repeated RatedArenaMemberResult memberResults = 5; +} + +enum LfgState { + LFG_STATE_NONE = 0; + LFG_STATE_ROLECHECK = 1; + LFG_STATE_QUEUED = 2; + LFG_STATE_PROPOSAL = 3; + LFG_STATE_BOOT = 4; + LFG_STATE_DUNGEON = 5; + LFG_STATE_FINISHED_DUNGEON = 6; + LFG_STATE_RAIDBROWSER = 7; +} + +enum LfgProposalState { + LFG_PROPOSAL_INITIATING = 0; + LFG_PROPOSAL_FAILED = 1; + LFG_PROPOSAL_SUCCESS = 2; +} + +enum LfgJoinResult { + LFG_JOIN_OK = 0; + LFG_JOIN_FAILED = 1; + LFG_JOIN_GROUP_FULL = 2; + LFG_JOIN_INTERNAL_ERROR = 4; + LFG_JOIN_NOT_MEET_REQS = 5; + LFG_JOIN_PARTY_NOT_MEET_REQS = 6; + LFG_JOIN_MIXED_RAID_DUNGEON = 7; + LFG_JOIN_MULTI_REALM = 8; + LFG_JOIN_DISCONNECTED = 9; + LFG_JOIN_PARTY_INFO_FAILED = 10; + LFG_JOIN_DUNGEON_INVALID = 11; + LFG_JOIN_DESERTER = 12; + LFG_JOIN_PARTY_DESERTER = 13; + LFG_JOIN_RANDOM_COOLDOWN = 14; + LFG_JOIN_PARTY_RANDOM_COOLDOWN = 15; + LFG_JOIN_TOO_MANY_MEMBERS = 16; + LFG_JOIN_USING_BG_SYSTEM = 17; +} + +message LfgMember { + uint64 playerGUID = 1; + uint32 roles = 2; + bool leader = 3; + string worldserverID = 4; + uint32 realmID = 5; + uint32 queueLeaderRealmID = 6; + uint64 queueLeaderGUID = 7; +} + +message LfgProposalMember { + uint64 playerGUID = 1; + uint32 selectedRoles = 2; + uint32 assignedRole = 3; + bool answered = 4; + bool accepted = 5; + uint32 realmID = 6; +} + +message LfgStatusData { + LfgState state = 1; + uint32 proposalID = 2; + LfgProposalState proposalState = 3; + uint32 dungeonEntry = 4; + repeated uint32 selectedDungeons = 5; + repeated LfgMember queuedMembers = 6; + repeated LfgProposalMember proposalMembers = 7; + uint32 queuedTimeMilliseconds = 8; + uint32 tanksNeeded = 9; + uint32 healersNeeded = 10; + uint32 damageNeeded = 11; +} + +message JoinLfgRequest { + string api = 1; + uint32 realmID = 2; + uint64 leaderGUID = 3; + repeated LfgMember members = 4; + repeated uint32 dungeonEntries = 5; + string comment = 6; +} + +message JoinLfgResponse { + string api = 1; + LfgJoinResult result = 2; + LfgStatusData status = 3; +} + +message LeaveLfgRequest { + string api = 1; + uint32 realmID = 2; + uint64 playerGUID = 3; +} + +message LeaveLfgResponse { + string api = 1; +} + +message SetLfgRolesRequest { + string api = 1; + uint32 realmID = 2; + uint64 playerGUID = 3; + uint32 roles = 4; +} + +message SetLfgRolesResponse { + string api = 1; + LfgStatusData status = 2; +} + +message AnswerLfgProposalRequest { + string api = 1; + uint32 realmID = 2; + uint64 playerGUID = 3; + uint32 proposalID = 4; + bool accept = 5; +} + +message AnswerLfgProposalResponse { + string api = 1; + LfgStatusData status = 2; +} + +message LfgStatusRequest { + string api = 1; + uint32 realmID = 2; + uint64 playerGUID = 3; +} + +message LfgStatusResponse { + string api = 1; + LfgStatusData status = 2; +} + +message CompleteLfgDungeonPlayer { + uint32 realmID = 1; + uint64 playerGUID = 2; +} + +message CompleteLfgDungeonRequest { + string api = 1; + uint32 completedDungeonEntry = 2; + uint32 selectedDungeonEntry = 3; + repeated CompleteLfgDungeonPlayer players = 4; +} + +message CompleteLfgDungeonResponse { + string api = 1; +} diff --git a/api/proto/v1/servers-registry/registry.proto b/api/proto/v1/servers-registry/registry.proto index 3157821..b8e4ca2 100644 --- a/api/proto/v1/servers-registry/registry.proto +++ b/api/proto/v1/servers-registry/registry.proto @@ -14,6 +14,8 @@ service ServersRegistryService { rpc RegisterGateway(RegisterGatewayRequest) returns (RegisterGatewayResponse); rpc GatewaysForRealms(GatewaysForRealmsRequest) returns (GatewaysForRealmsResponse); rpc ListGatewaysForRealm(ListGatewaysForRealmRequest) returns (ListGatewaysForRealmResponse); + + rpc RegisterMatchmakingServer(RegisterMatchmakingServerRequest) returns (RegisterMatchmakingServerResponse); } // @@ -185,6 +187,24 @@ message ListGatewaysForRealmResponse { repeated GatewayServerDetailed gateways = 2; } +// +// RegisterMatchmakingServer +// +message RegisterMatchmakingServerRequest { + string api = 1; + + uint32 servicePort = 2; + uint32 healthPort = 3; + string preferredHostName = 4; + int64 startedAtUnixMs = 5; +} + +message RegisterMatchmakingServerResponse { + string api = 1; + + string id = 2; +} + // // Shared // @@ -193,5 +213,5 @@ message Server { uint32 realmID = 2; bool isCrossRealm = 3; string grpcAddress = 4; + string id = 5; } - diff --git a/api/proto/v1/worldserver/worldserver.proto b/api/proto/v1/worldserver/worldserver.proto index d58e80d..028fa27 100644 --- a/api/proto/v1/worldserver/worldserver.proto +++ b/api/proto/v1/worldserver/worldserver.proto @@ -8,6 +8,7 @@ option go_package = "gen/worldserver/pb"; service WorldServerService { // Items rpc GetPlayerItemsByGuids(GetPlayerItemsByGuidsRequest) returns (GetPlayerItemsByGuidsResponse); + rpc TakePlayerItemByPos(TakePlayerItemByPosRequest) returns (TakePlayerItemByPosResponse); rpc RemoveItemsWithGuidsFromPlayer(RemoveItemsWithGuidsFromPlayerRequest) returns (RemoveItemsWithGuidsFromPlayerResponse); rpc AddExistingItemToPlayer(AddExistingItemToPlayerRequest) returns (AddExistingItemToPlayerResponse); @@ -24,6 +25,17 @@ service WorldServerService { rpc AddPlayersToBattleground(AddPlayersToBattlegroundRequest) returns (AddPlayersToBattlegroundResponse); rpc CanPlayerJoinBattlegroundQueue(CanPlayerJoinBattlegroundQueueRequest) returns (CanPlayerJoinBattlegroundQueueResponse); rpc CanPlayerTeleportToBattleground(CanPlayerTeleportToBattlegroundRequest) returns (CanPlayerTeleportToBattlegroundResponse); + + // LFG + rpc GetLfgPlayerLockInfo(GetLfgPlayerLockInfoRequest) returns (GetLfgPlayerLockInfoResponse); + rpc GetLfgPlayerInfo(GetLfgPlayerInfoRequest) returns (GetLfgPlayerInfoResponse); + rpc GetLfgDungeonInfo(GetLfgDungeonInfoRequest) returns (GetLfgDungeonInfoResponse); + rpc TeleportLfgPlayer(TeleportLfgPlayerRequest) returns (TeleportLfgPlayerResponse); + rpc SetLfgBootVote(SetLfgBootVoteRequest) returns (SetLfgBootVoteResponse); + rpc MaterializeLfgProposal(MaterializeLfgProposalRequest) returns (MaterializeLfgProposalResponse); + + // Guilds + rpc CreateGuild(CreateGuildRequest) returns (CreateGuildResponse); } // GetPlayerItemsByGuids @@ -47,12 +59,53 @@ message GetPlayerItemsByGuidsResponse { uint32 count = 7; uint32 flags = 8; uint32 durability = 9; - uint32 randomPropertyID = 10; + int32 randomPropertyID = 10; string text = 11; }; repeated Item items = 2; } +// TakePlayerItemByPos +message TakePlayerItemByPosRequest { + string api = 1; + + uint64 playerGuid = 2; + uint32 bagSlot = 3; + uint32 slot = 4; + uint32 count = 5; + + uint64 assignToPlayerGuid = 6; +} + +message TakePlayerItemByPosResponse { + string api = 1; + + enum Status { + Success = 0; + PlayerNotFound = 1; + ItemNotFound = 2; + ItemNotTradable = 3; + Failed = 4; + } + + message Item { + uint64 guid = 1; + uint32 entry = 2; + uint64 owner = 3; + uint32 bagSlot = 4; + uint32 slot = 5; + bool isTradable = 6; + uint32 count = 7; + uint32 flags = 8; + uint32 durability = 9; + int32 randomPropertyID = 10; + string text = 11; + }; + + Status status = 2; + Item item = 3; +} + // RemoveItemsWithGuidsFromPlayer message RemoveItemsWithGuidsFromPlayerRequest { string api = 1; @@ -79,12 +132,15 @@ message AddExistingItemToPlayerRequest { uint32 count = 7; uint32 flags = 8; uint32 durability = 9; - uint32 randomPropertyID = 10; + int32 randomPropertyID = 10; string text = 11; } uint64 playerGuid = 2; Item item = 3; + bool storeAtPos = 4; + uint32 bagSlot = 5; + uint32 slot = 6; } message AddExistingItemToPlayerResponse { @@ -177,13 +233,18 @@ message StartBattlegroundRequest { string api = 1; BattlegroundType battlegroundTypeID = 2; - uint32 arenaType = 3; // Arenas not supported yet. - bool isRated = 4; // Arenas not supported yet. + uint32 arenaType = 3; + bool isRated = 4; uint32 mapID = 5; uint32 bracketLvl = 6; repeated uint64 playersToAddAlliance = 7; repeated uint64 playersToAddHorde = 8; + + uint32 allianceArenaTeamID = 9; + uint32 hordeArenaTeamID = 10; + uint32 allianceArenaMatchmakerRating = 11; + uint32 hordeArenaMatchmakerRating = 12; } message StartBattlegroundResponse { @@ -241,3 +302,184 @@ message CanPlayerTeleportToBattlegroundResponse { Status status = 2; } + +message LfgDungeonLock { + uint32 dungeonEntry = 1; + uint32 lockStatus = 2; +} + +message GetLfgPlayerLockInfoRequest { + string api = 1; + uint64 playerGUID = 2; + repeated uint32 dungeonEntries = 3; +} + +message GetLfgPlayerLockInfoResponse { + string api = 1; + + enum Status { + Success = 0; + PlayerNotFound = 1; + InternalError = 2; + } + + Status status = 2; + repeated LfgDungeonLock locks = 3; + // AzerothCore LfgJoinResult for player-wide join restrictions not represented + // as per-dungeon lock rows, such as deserter or random dungeon cooldown. + uint32 joinResult = 4; + // AzerothCore DBC-filtered and canonicalized dungeon entries accepted from + // the client selection. Gateway must queue these instead of raw client slots. + repeated uint32 validDungeonEntries = 5; +} + +message LfgRewardItem { + uint32 itemID = 1; + uint32 displayID = 2; + uint32 count = 3; +} + +message LfgRandomDungeonInfo { + uint32 dungeonEntry = 1; + bool done = 2; + uint32 rewardMoney = 3; + uint32 rewardXP = 4; + uint32 rewardUnknown1 = 5; + uint32 rewardUnknown2 = 6; + repeated LfgRewardItem rewardItems = 7; +} + +message GetLfgPlayerInfoRequest { + string api = 1; + uint64 playerGUID = 2; +} + +message GetLfgPlayerInfoResponse { + string api = 1; + + enum Status { + Success = 0; + NoHandler = 1; + PlayerNotFound = 2; + InternalError = 3; + } + + Status status = 2; + repeated LfgRandomDungeonInfo randomDungeons = 3; + repeated LfgDungeonLock locks = 4; +} + +message GetLfgDungeonInfoRequest { + string api = 1; + uint32 dungeonEntry = 2; +} + +message GetLfgDungeonInfoResponse { + string api = 1; + + enum Status { + Success = 0; + NoHandler = 1; + DungeonNotFound = 2; + InternalError = 3; + } + + Status status = 2; + uint32 dungeonEntry = 3; + uint32 dungeonID = 4; + uint32 mapID = 5; + uint32 typeID = 6; + uint32 difficulty = 7; +} + +message TeleportLfgPlayerRequest { + string api = 1; + uint64 playerGUID = 2; + bool out = 3; + uint32 dungeonEntry = 4; +} + +message TeleportLfgPlayerResponse { + string api = 1; + + enum Status { + Success = 0; + NoHandler = 1; + PlayerNotFound = 2; + InternalError = 3; + } + + Status status = 2; +} + +message SetLfgBootVoteRequest { + string api = 1; + uint64 playerGUID = 2; + bool agree = 3; +} + +message SetLfgBootVoteResponse { + string api = 1; + + enum Status { + Success = 0; + NoHandler = 1; + PlayerNotFound = 2; + InternalError = 3; + } + + Status status = 2; +} + +// MaterializeLfgProposal +message MaterializeLfgProposalRequest { + string api = 1; + + message Member { + uint64 playerGUID = 1; + uint32 selectedRoles = 2; + uint32 assignedRole = 3; + uint64 queueLeaderGUID = 4; + } + + uint32 realmID = 2; + uint32 proposalID = 3; + uint32 dungeonEntry = 4; + uint64 leaderGUID = 5; + repeated Member members = 6; +} + +message MaterializeLfgProposalResponse { + string api = 1; + + enum Status { + Success = 0; + NoHandler = 1; + DungeonNotFound = 2; + NoLocalPlayer = 3; + InternalError = 4; + } + + Status status = 2; +} + +message CreateGuildRequest { + string api = 1; + uint64 leaderGuid = 2; + string guildName = 3; +} + +message CreateGuildResponse { + string api = 1; + + enum Status { + Success = 0; + NameExists = 1; + InvalidName = 2; + LeaderNotFound = 3; + InternalError = 4; + } + + Status status = 2; + uint64 guildId = 3; +} diff --git a/config.yml.example b/config.yml.example index 8d81dc3..ce2ab46 100644 --- a/config.yml.example +++ b/config.yml.example @@ -9,7 +9,8 @@ db: schemaType: &defaultSchemaType "ac" nats: &defaultNatsUrl "nats://localhost:4222" -redis: &defaultRedisUrl "redis://:@localhost:6379/0" +# Redis-compatible store URL. Valkey is the recommended local Debian/Linux backend. +redis: &defaultRedisUrl "redis://:@127.0.0.1:6379/0" logging: &defaultLogging # Available options: @@ -34,8 +35,10 @@ auth: characters: port: 8991 + authDB: *defaultAuthDB charactersDB: *defaultCharactersDB worldDB: *defaultWorldDB + realIDCrossFaction: true natsUrl: *defaultNatsUrl serversRegistryServiceAddress: localhost:8999 logging: *defaultLogging @@ -61,7 +64,20 @@ gateway: groupServiceAddress: "localhost:8998" matchmakingServiceAddress: "localhost:8994" packetProcessTimeoutSecs: 20 + worldAuthAttemptTimeoutMs: 5000 + worldAuthSessionReadyDelayMs: 300 + worldserverConnectRetryWaitMs: 200 + worldserverConnectRetryMaxWaitMs: 2000 showGameserverConnChangeToClient: true + allowTwoSideInteractionGuild: false + allowTwoSideInteractionChannel: false + allowTwoSideInteractionArena: false + maxPlayerLevel: 80 + arenaCurrentSeason: 8 + legacyArenaStartRating: 1500 + arenaStartRating: 0 + arenaStartPersonalRating: 0 + arenaStartMatchmakerRating: 1500 natsUrl: *defaultNatsUrl logging: *defaultLogging @@ -75,6 +91,8 @@ guild: port: 8995 natsUrl: *defaultNatsUrl charactersDB: *defaultCharactersDB + worldDB: *defaultWorldDB + guidProviderServiceAddress: "localhost:8996" logging: *defaultLogging mail: @@ -96,26 +114,42 @@ servers-registry: port: 8999 redisUrl: *defaultRedisUrl natsUrl: *defaultNatsUrl + matchmakingServiceAddress: "localhost:8994" + matchmakingServiceHealthCheckAddress: "localhost:8904" + matchmakingServiceHealthCheckIntervalMs: 4000 + matchmakingServiceHealthCheckTimeoutMs: 15000 logging: *defaultLogging realmsID: - 1 matchmakingserver: port: 8994 + healthCheckPort: 8904 natsUrl: *defaultNatsUrl logging: *defaultLogging worldDB: *defaultWorldDB charactersDB: *defaultCharactersDB + groupServiceAddress: "localhost:8998" + charactersServiceAddress: "localhost:8991" battleGroups: 1: "1" + arenaStartMatchmakerRating: 1500 + arenaWinRatingModifier1: 48 + arenaWinRatingModifier2: 24 + arenaLoseRatingModifier: 24 + arenaMatchmakerRatingModifier: 24 + maxAllowedMMRDrop: 500 gameserver: grpcPort: 9501 healthCheckPort: 8901 preferredHostname: "" + loopbackHostOverride: "auto" serversRegistryServiceAddress: "localhost:8999" guidProviderServiceAddress: "localhost:8996" + groupServiceAddress: "localhost:8998" matchmakingServiceAddress: "localhost:8994" + charactersServiceAddress: "localhost:8991" characterGuidsBufferSize: 50 itemGuidsBufferSize: 200 instanceGuidsBufferSize: 10 @@ -192,4 +226,4 @@ perun: - "glb" - "gamelb" binary: "./gateway" - startupTimeoutSecs: 10 \ No newline at end of file + startupTimeoutSecs: 10 diff --git a/gen/characters/pb/characters.pb.go b/gen/characters/pb/characters.pb.go index b93605b..2a816a0 100644 --- a/gen/characters/pb/characters.pb.go +++ b/gen/characters/pb/characters.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v3.20.3 +// protoc v3.21.12 // source: characters.proto package pb @@ -20,6 +20,311 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type ArenaTeamMutationStatus int32 + +const ( + ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK ArenaTeamMutationStatus = 0 + ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_NOT_FOUND ArenaTeamMutationStatus = 1 + ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_MEMBER_MISMATCH ArenaTeamMutationStatus = 2 + ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_INVALID_TYPE ArenaTeamMutationStatus = 3 + ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_NAME_EXISTS ArenaTeamMutationStatus = 4 + ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_ALREADY_IN_TEAM ArenaTeamMutationStatus = 5 + ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_ROSTER_FULL ArenaTeamMutationStatus = 6 + ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_FAILED ArenaTeamMutationStatus = 7 + ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_INVALID_NAME ArenaTeamMutationStatus = 8 + ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_PERMISSION_DENIED ArenaTeamMutationStatus = 9 + ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_LEADER_LEAVE ArenaTeamMutationStatus = 10 + ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_ALREADY_INVITED ArenaTeamMutationStatus = 11 + ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_IGNORING_YOU ArenaTeamMutationStatus = 12 +) + +// Enum value maps for ArenaTeamMutationStatus. +var ( + ArenaTeamMutationStatus_name = map[int32]string{ + 0: "ARENA_TEAM_MUTATION_OK", + 1: "ARENA_TEAM_MUTATION_NOT_FOUND", + 2: "ARENA_TEAM_MUTATION_MEMBER_MISMATCH", + 3: "ARENA_TEAM_MUTATION_INVALID_TYPE", + 4: "ARENA_TEAM_MUTATION_NAME_EXISTS", + 5: "ARENA_TEAM_MUTATION_ALREADY_IN_TEAM", + 6: "ARENA_TEAM_MUTATION_ROSTER_FULL", + 7: "ARENA_TEAM_MUTATION_FAILED", + 8: "ARENA_TEAM_MUTATION_INVALID_NAME", + 9: "ARENA_TEAM_MUTATION_PERMISSION_DENIED", + 10: "ARENA_TEAM_MUTATION_LEADER_LEAVE", + 11: "ARENA_TEAM_MUTATION_ALREADY_INVITED", + 12: "ARENA_TEAM_MUTATION_IGNORING_YOU", + } + ArenaTeamMutationStatus_value = map[string]int32{ + "ARENA_TEAM_MUTATION_OK": 0, + "ARENA_TEAM_MUTATION_NOT_FOUND": 1, + "ARENA_TEAM_MUTATION_MEMBER_MISMATCH": 2, + "ARENA_TEAM_MUTATION_INVALID_TYPE": 3, + "ARENA_TEAM_MUTATION_NAME_EXISTS": 4, + "ARENA_TEAM_MUTATION_ALREADY_IN_TEAM": 5, + "ARENA_TEAM_MUTATION_ROSTER_FULL": 6, + "ARENA_TEAM_MUTATION_FAILED": 7, + "ARENA_TEAM_MUTATION_INVALID_NAME": 8, + "ARENA_TEAM_MUTATION_PERMISSION_DENIED": 9, + "ARENA_TEAM_MUTATION_LEADER_LEAVE": 10, + "ARENA_TEAM_MUTATION_ALREADY_INVITED": 11, + "ARENA_TEAM_MUTATION_IGNORING_YOU": 12, + } +) + +func (x ArenaTeamMutationStatus) Enum() *ArenaTeamMutationStatus { + p := new(ArenaTeamMutationStatus) + *p = x + return p +} + +func (x ArenaTeamMutationStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ArenaTeamMutationStatus) Descriptor() protoreflect.EnumDescriptor { + return file_characters_proto_enumTypes[0].Descriptor() +} + +func (ArenaTeamMutationStatus) Type() protoreflect.EnumType { + return &file_characters_proto_enumTypes[0] +} + +func (x ArenaTeamMutationStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ArenaTeamMutationStatus.Descriptor instead. +func (ArenaTeamMutationStatus) EnumDescriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{0} +} + +type ArenaTeamQueueDataForRatedArenaResponse_Status int32 + +const ( + ArenaTeamQueueDataForRatedArenaResponse_Ok ArenaTeamQueueDataForRatedArenaResponse_Status = 0 + ArenaTeamQueueDataForRatedArenaResponse_NotFound ArenaTeamQueueDataForRatedArenaResponse_Status = 1 + ArenaTeamQueueDataForRatedArenaResponse_MemberMismatch ArenaTeamQueueDataForRatedArenaResponse_Status = 2 + ArenaTeamQueueDataForRatedArenaResponse_InvalidPartySize ArenaTeamQueueDataForRatedArenaResponse_Status = 3 + ArenaTeamQueueDataForRatedArenaResponse_Failed ArenaTeamQueueDataForRatedArenaResponse_Status = 4 + ArenaTeamQueueDataForRatedArenaResponse_InvalidType ArenaTeamQueueDataForRatedArenaResponse_Status = 5 +) + +// Enum value maps for ArenaTeamQueueDataForRatedArenaResponse_Status. +var ( + ArenaTeamQueueDataForRatedArenaResponse_Status_name = map[int32]string{ + 0: "Ok", + 1: "NotFound", + 2: "MemberMismatch", + 3: "InvalidPartySize", + 4: "Failed", + 5: "InvalidType", + } + ArenaTeamQueueDataForRatedArenaResponse_Status_value = map[string]int32{ + "Ok": 0, + "NotFound": 1, + "MemberMismatch": 2, + "InvalidPartySize": 3, + "Failed": 4, + "InvalidType": 5, + } +) + +func (x ArenaTeamQueueDataForRatedArenaResponse_Status) Enum() *ArenaTeamQueueDataForRatedArenaResponse_Status { + p := new(ArenaTeamQueueDataForRatedArenaResponse_Status) + *p = x + return p +} + +func (x ArenaTeamQueueDataForRatedArenaResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ArenaTeamQueueDataForRatedArenaResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_characters_proto_enumTypes[1].Descriptor() +} + +func (ArenaTeamQueueDataForRatedArenaResponse_Status) Type() protoreflect.EnumType { + return &file_characters_proto_enumTypes[1] +} + +func (x ArenaTeamQueueDataForRatedArenaResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ArenaTeamQueueDataForRatedArenaResponse_Status.Descriptor instead. +func (ArenaTeamQueueDataForRatedArenaResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{22, 0} +} + +type CreateArenaTeamFromPetitionResponse_Status int32 + +const ( + CreateArenaTeamFromPetitionResponse_Ok CreateArenaTeamFromPetitionResponse_Status = 0 + CreateArenaTeamFromPetitionResponse_NotFound CreateArenaTeamFromPetitionResponse_Status = 1 + CreateArenaTeamFromPetitionResponse_NotOwner CreateArenaTeamFromPetitionResponse_Status = 2 + CreateArenaTeamFromPetitionResponse_InvalidType CreateArenaTeamFromPetitionResponse_Status = 3 + CreateArenaTeamFromPetitionResponse_NameExists CreateArenaTeamFromPetitionResponse_Status = 4 + CreateArenaTeamFromPetitionResponse_AlreadyInTeam CreateArenaTeamFromPetitionResponse_Status = 5 + CreateArenaTeamFromPetitionResponse_NotEnoughSignatures CreateArenaTeamFromPetitionResponse_Status = 6 + CreateArenaTeamFromPetitionResponse_Failed CreateArenaTeamFromPetitionResponse_Status = 7 + CreateArenaTeamFromPetitionResponse_InvalidName CreateArenaTeamFromPetitionResponse_Status = 8 +) + +// Enum value maps for CreateArenaTeamFromPetitionResponse_Status. +var ( + CreateArenaTeamFromPetitionResponse_Status_name = map[int32]string{ + 0: "Ok", + 1: "NotFound", + 2: "NotOwner", + 3: "InvalidType", + 4: "NameExists", + 5: "AlreadyInTeam", + 6: "NotEnoughSignatures", + 7: "Failed", + 8: "InvalidName", + } + CreateArenaTeamFromPetitionResponse_Status_value = map[string]int32{ + "Ok": 0, + "NotFound": 1, + "NotOwner": 2, + "InvalidType": 3, + "NameExists": 4, + "AlreadyInTeam": 5, + "NotEnoughSignatures": 6, + "Failed": 7, + "InvalidName": 8, + } +) + +func (x CreateArenaTeamFromPetitionResponse_Status) Enum() *CreateArenaTeamFromPetitionResponse_Status { + p := new(CreateArenaTeamFromPetitionResponse_Status) + *p = x + return p +} + +func (x CreateArenaTeamFromPetitionResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (CreateArenaTeamFromPetitionResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_characters_proto_enumTypes[2].Descriptor() +} + +func (CreateArenaTeamFromPetitionResponse_Status) Type() protoreflect.EnumType { + return &file_characters_proto_enumTypes[2] +} + +func (x CreateArenaTeamFromPetitionResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use CreateArenaTeamFromPetitionResponse_Status.Descriptor instead. +func (CreateArenaTeamFromPetitionResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{24, 0} +} + +type GetArenaTeamResponse_Status int32 + +const ( + GetArenaTeamResponse_Ok GetArenaTeamResponse_Status = 0 + GetArenaTeamResponse_NotFound GetArenaTeamResponse_Status = 1 + GetArenaTeamResponse_Failed GetArenaTeamResponse_Status = 2 +) + +// Enum value maps for GetArenaTeamResponse_Status. +var ( + GetArenaTeamResponse_Status_name = map[int32]string{ + 0: "Ok", + 1: "NotFound", + 2: "Failed", + } + GetArenaTeamResponse_Status_value = map[string]int32{ + "Ok": 0, + "NotFound": 1, + "Failed": 2, + } +) + +func (x GetArenaTeamResponse_Status) Enum() *GetArenaTeamResponse_Status { + p := new(GetArenaTeamResponse_Status) + *p = x + return p +} + +func (x GetArenaTeamResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (GetArenaTeamResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_characters_proto_enumTypes[3].Descriptor() +} + +func (GetArenaTeamResponse_Status) Type() protoreflect.EnumType { + return &file_characters_proto_enumTypes[3] +} + +func (x GetArenaTeamResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use GetArenaTeamResponse_Status.Descriptor instead. +func (GetArenaTeamResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{28, 0} +} + +type GetArenaTeamPetitionResponse_Status int32 + +const ( + GetArenaTeamPetitionResponse_Ok GetArenaTeamPetitionResponse_Status = 0 + GetArenaTeamPetitionResponse_NotFound GetArenaTeamPetitionResponse_Status = 1 + GetArenaTeamPetitionResponse_NotArena GetArenaTeamPetitionResponse_Status = 2 + GetArenaTeamPetitionResponse_Failed GetArenaTeamPetitionResponse_Status = 3 +) + +// Enum value maps for GetArenaTeamPetitionResponse_Status. +var ( + GetArenaTeamPetitionResponse_Status_name = map[int32]string{ + 0: "Ok", + 1: "NotFound", + 2: "NotArena", + 3: "Failed", + } + GetArenaTeamPetitionResponse_Status_value = map[string]int32{ + "Ok": 0, + "NotFound": 1, + "NotArena": 2, + "Failed": 3, + } +) + +func (x GetArenaTeamPetitionResponse_Status) Enum() *GetArenaTeamPetitionResponse_Status { + p := new(GetArenaTeamPetitionResponse_Status) + *p = x + return p +} + +func (x GetArenaTeamPetitionResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (GetArenaTeamPetitionResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_characters_proto_enumTypes[4].Descriptor() +} + +func (GetArenaTeamPetitionResponse_Status) Type() protoreflect.EnumType { + return &file_characters_proto_enumTypes[4] +} + +func (x GetArenaTeamPetitionResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use GetArenaTeamPetitionResponse_Status.Descriptor instead. +func (GetArenaTeamPetitionResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{31, 0} +} + // LogInCharacter is character that should be displayed on log in screen type LogInCharacter struct { state protoimpl.MessageState @@ -51,6 +356,7 @@ type LogInCharacter struct { Banned bool `protobuf:"varint,23,opt,name=banned,proto3" json:"banned,omitempty"` Equipments []*EquipmentDisplay `protobuf:"bytes,24,rep,name=equipments,proto3" json:"equipments,omitempty"` AccountID uint32 `protobuf:"varint,25,opt,name=accountID,proto3" json:"accountID,omitempty"` + ExtraFlags uint32 `protobuf:"varint,26,opt,name=extraFlags,proto3" json:"extraFlags,omitempty"` } func (x *LogInCharacter) Reset() { @@ -260,6 +566,13 @@ func (x *LogInCharacter) GetAccountID() uint32 { return 0 } +func (x *LogInCharacter) GetExtraFlags() uint32 { + if x != nil { + return x.ExtraFlags + } + return 0 +} + type EquipmentDisplay struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -450,6 +763,7 @@ type CharactersToLoginByGUIDRequest struct { Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` CharacterGUID uint64 `protobuf:"varint,2,opt,name=characterGUID,proto3" json:"characterGUID,omitempty"` RealmID uint32 `protobuf:"varint,3,opt,name=realmID,proto3" json:"realmID,omitempty"` + AccountID uint32 `protobuf:"varint,4,opt,name=accountID,proto3" json:"accountID,omitempty"` } func (x *CharactersToLoginByGUIDRequest) Reset() { @@ -505,6 +819,13 @@ func (x *CharactersToLoginByGUIDRequest) GetRealmID() uint32 { return 0 } +func (x *CharactersToLoginByGUIDRequest) GetAccountID() uint32 { + if x != nil { + return x.AccountID + } + return 0 +} + // Response that contains character to login type CharactersToLoginByGUIDResponse struct { state protoimpl.MessageState @@ -742,6 +1063,140 @@ func (x *AccountDataForAccountResponse) GetAccountData() []*AccountData { return nil } +type UpdateAccountDataForAccountRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + AccountID uint32 `protobuf:"varint,2,opt,name=accountID,proto3" json:"accountID,omitempty"` + RealmID uint32 `protobuf:"varint,3,opt,name=realmID,proto3" json:"realmID,omitempty"` + Type uint32 `protobuf:"varint,4,opt,name=type,proto3" json:"type,omitempty"` + Time int64 `protobuf:"varint,5,opt,name=time,proto3" json:"time,omitempty"` + Data string `protobuf:"bytes,6,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *UpdateAccountDataForAccountRequest) Reset() { + *x = UpdateAccountDataForAccountRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateAccountDataForAccountRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAccountDataForAccountRequest) ProtoMessage() {} + +func (x *UpdateAccountDataForAccountRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateAccountDataForAccountRequest.ProtoReflect.Descriptor instead. +func (*UpdateAccountDataForAccountRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{9} +} + +func (x *UpdateAccountDataForAccountRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *UpdateAccountDataForAccountRequest) GetAccountID() uint32 { + if x != nil { + return x.AccountID + } + return 0 +} + +func (x *UpdateAccountDataForAccountRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *UpdateAccountDataForAccountRequest) GetType() uint32 { + if x != nil { + return x.Type + } + return 0 +} + +func (x *UpdateAccountDataForAccountRequest) GetTime() int64 { + if x != nil { + return x.Time + } + return 0 +} + +func (x *UpdateAccountDataForAccountRequest) GetData() string { + if x != nil { + return x.Data + } + return "" +} + +type UpdateAccountDataForAccountResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *UpdateAccountDataForAccountResponse) Reset() { + *x = UpdateAccountDataForAccountResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateAccountDataForAccountResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAccountDataForAccountResponse) ProtoMessage() {} + +func (x *UpdateAccountDataForAccountResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateAccountDataForAccountResponse.ProtoReflect.Descriptor instead. +func (*UpdateAccountDataForAccountResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{10} +} + +func (x *UpdateAccountDataForAccountResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + type WhoQueryRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -763,7 +1218,7 @@ type WhoQueryRequest struct { func (x *WhoQueryRequest) Reset() { *x = WhoQueryRequest{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[9] + mi := &file_characters_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -776,7 +1231,7 @@ func (x *WhoQueryRequest) String() string { func (*WhoQueryRequest) ProtoMessage() {} func (x *WhoQueryRequest) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[9] + mi := &file_characters_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -789,7 +1244,7 @@ func (x *WhoQueryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use WhoQueryRequest.ProtoReflect.Descriptor instead. func (*WhoQueryRequest) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{9} + return file_characters_proto_rawDescGZIP(), []int{11} } func (x *WhoQueryRequest) GetApi() string { @@ -882,7 +1337,7 @@ type WhoQueryResponse struct { func (x *WhoQueryResponse) Reset() { *x = WhoQueryResponse{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[10] + mi := &file_characters_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -895,7 +1350,7 @@ func (x *WhoQueryResponse) String() string { func (*WhoQueryResponse) ProtoMessage() {} func (x *WhoQueryResponse) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[10] + mi := &file_characters_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -908,7 +1363,7 @@ func (x *WhoQueryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use WhoQueryResponse.ProtoReflect.Descriptor instead. func (*WhoQueryResponse) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{10} + return file_characters_proto_rawDescGZIP(), []int{12} } func (x *WhoQueryResponse) GetApi() string { @@ -945,7 +1400,7 @@ type CharacterOnlineByNameRequest struct { func (x *CharacterOnlineByNameRequest) Reset() { *x = CharacterOnlineByNameRequest{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[11] + mi := &file_characters_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -958,7 +1413,7 @@ func (x *CharacterOnlineByNameRequest) String() string { func (*CharacterOnlineByNameRequest) ProtoMessage() {} func (x *CharacterOnlineByNameRequest) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[11] + mi := &file_characters_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -971,7 +1426,7 @@ func (x *CharacterOnlineByNameRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CharacterOnlineByNameRequest.ProtoReflect.Descriptor instead. func (*CharacterOnlineByNameRequest) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{11} + return file_characters_proto_rawDescGZIP(), []int{13} } func (x *CharacterOnlineByNameRequest) GetApi() string { @@ -1007,7 +1462,7 @@ type CharacterOnlineByNameResponse struct { func (x *CharacterOnlineByNameResponse) Reset() { *x = CharacterOnlineByNameResponse{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[12] + mi := &file_characters_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1020,7 +1475,7 @@ func (x *CharacterOnlineByNameResponse) String() string { func (*CharacterOnlineByNameResponse) ProtoMessage() {} func (x *CharacterOnlineByNameResponse) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[12] + mi := &file_characters_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1033,7 +1488,7 @@ func (x *CharacterOnlineByNameResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CharacterOnlineByNameResponse.ProtoReflect.Descriptor instead. func (*CharacterOnlineByNameResponse) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{12} + return file_characters_proto_rawDescGZIP(), []int{14} } func (x *CharacterOnlineByNameResponse) GetApi() string { @@ -1063,7 +1518,7 @@ type CharacterByNameRequest struct { func (x *CharacterByNameRequest) Reset() { *x = CharacterByNameRequest{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[13] + mi := &file_characters_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1076,7 +1531,7 @@ func (x *CharacterByNameRequest) String() string { func (*CharacterByNameRequest) ProtoMessage() {} func (x *CharacterByNameRequest) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[13] + mi := &file_characters_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1089,7 +1544,7 @@ func (x *CharacterByNameRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CharacterByNameRequest.ProtoReflect.Descriptor instead. func (*CharacterByNameRequest) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{13} + return file_characters_proto_rawDescGZIP(), []int{15} } func (x *CharacterByNameRequest) GetApi() string { @@ -1125,7 +1580,7 @@ type CharacterByNameResponse struct { func (x *CharacterByNameResponse) Reset() { *x = CharacterByNameResponse{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[14] + mi := &file_characters_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1138,7 +1593,7 @@ func (x *CharacterByNameResponse) String() string { func (*CharacterByNameResponse) ProtoMessage() {} func (x *CharacterByNameResponse) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[14] + mi := &file_characters_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1151,7 +1606,7 @@ func (x *CharacterByNameResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CharacterByNameResponse.ProtoReflect.Descriptor instead. func (*CharacterByNameResponse) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{14} + return file_characters_proto_rawDescGZIP(), []int{16} } func (x *CharacterByNameResponse) GetApi() string { @@ -1168,33 +1623,33 @@ func (x *CharacterByNameResponse) GetCharacter() *CharacterByNameResponse_Char { return nil } -type ShortCharactersDataByGUIDsRequest struct { +type CharacterByGUIDRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - GUIDs []uint64 `protobuf:"varint,3,rep,packed,name=GUIDs,proto3" json:"GUIDs,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + CharacterGUID uint64 `protobuf:"varint,3,opt,name=characterGUID,proto3" json:"characterGUID,omitempty"` } -func (x *ShortCharactersDataByGUIDsRequest) Reset() { - *x = ShortCharactersDataByGUIDsRequest{} +func (x *CharacterByGUIDRequest) Reset() { + *x = CharacterByGUIDRequest{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[15] + mi := &file_characters_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ShortCharactersDataByGUIDsRequest) String() string { +func (x *CharacterByGUIDRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ShortCharactersDataByGUIDsRequest) ProtoMessage() {} +func (*CharacterByGUIDRequest) ProtoMessage() {} -func (x *ShortCharactersDataByGUIDsRequest) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[15] +func (x *CharacterByGUIDRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1205,58 +1660,58 @@ func (x *ShortCharactersDataByGUIDsRequest) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use ShortCharactersDataByGUIDsRequest.ProtoReflect.Descriptor instead. -func (*ShortCharactersDataByGUIDsRequest) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{15} +// Deprecated: Use CharacterByGUIDRequest.ProtoReflect.Descriptor instead. +func (*CharacterByGUIDRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{17} } -func (x *ShortCharactersDataByGUIDsRequest) GetApi() string { +func (x *CharacterByGUIDRequest) GetApi() string { if x != nil { return x.Api } return "" } -func (x *ShortCharactersDataByGUIDsRequest) GetRealmID() uint32 { +func (x *CharacterByGUIDRequest) GetRealmID() uint32 { if x != nil { return x.RealmID } return 0 } -func (x *ShortCharactersDataByGUIDsRequest) GetGUIDs() []uint64 { +func (x *CharacterByGUIDRequest) GetCharacterGUID() uint64 { if x != nil { - return x.GUIDs + return x.CharacterGUID } - return nil + return 0 } -type ShortCharactersDataByGUIDsResponse struct { +type CharacterByGUIDResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - Characters []*ShortCharactersDataByGUIDsResponse_ShortCharData `protobuf:"bytes,2,rep,name=characters,proto3" json:"characters,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Character *CharacterByNameResponse_Char `protobuf:"bytes,2,opt,name=character,proto3" json:"character,omitempty"` } -func (x *ShortCharactersDataByGUIDsResponse) Reset() { - *x = ShortCharactersDataByGUIDsResponse{} +func (x *CharacterByGUIDResponse) Reset() { + *x = CharacterByGUIDResponse{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[16] + mi := &file_characters_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ShortCharactersDataByGUIDsResponse) String() string { +func (x *CharacterByGUIDResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ShortCharactersDataByGUIDsResponse) ProtoMessage() {} +func (*CharacterByGUIDResponse) ProtoMessage() {} -func (x *ShortCharactersDataByGUIDsResponse) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[16] +func (x *CharacterByGUIDResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1267,57 +1722,52 @@ func (x *ShortCharactersDataByGUIDsResponse) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use ShortCharactersDataByGUIDsResponse.ProtoReflect.Descriptor instead. -func (*ShortCharactersDataByGUIDsResponse) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{16} +// Deprecated: Use CharacterByGUIDResponse.ProtoReflect.Descriptor instead. +func (*CharacterByGUIDResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{18} } -func (x *ShortCharactersDataByGUIDsResponse) GetApi() string { +func (x *CharacterByGUIDResponse) GetApi() string { if x != nil { return x.Api } return "" } -func (x *ShortCharactersDataByGUIDsResponse) GetCharacters() []*ShortCharactersDataByGUIDsResponse_ShortCharData { +func (x *CharacterByGUIDResponse) GetCharacter() *CharacterByNameResponse_Char { if x != nil { - return x.Characters + return x.Character } return nil } -type SavePlayerPositionRequest struct { +type ShortCharactersDataByGUIDsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - CharGUID uint64 `protobuf:"varint,3,opt,name=charGUID,proto3" json:"charGUID,omitempty"` - MapID uint32 `protobuf:"varint,4,opt,name=mapID,proto3" json:"mapID,omitempty"` - X float32 `protobuf:"fixed32,5,opt,name=x,proto3" json:"x,omitempty"` - Y float32 `protobuf:"fixed32,6,opt,name=y,proto3" json:"y,omitempty"` - Z float32 `protobuf:"fixed32,7,opt,name=z,proto3" json:"z,omitempty"` - O float32 `protobuf:"fixed32,8,opt,name=o,proto3" json:"o,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GUIDs []uint64 `protobuf:"varint,3,rep,packed,name=GUIDs,proto3" json:"GUIDs,omitempty"` } -func (x *SavePlayerPositionRequest) Reset() { - *x = SavePlayerPositionRequest{} +func (x *ShortCharactersDataByGUIDsRequest) Reset() { + *x = ShortCharactersDataByGUIDsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[17] + mi := &file_characters_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SavePlayerPositionRequest) String() string { +func (x *ShortCharactersDataByGUIDsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SavePlayerPositionRequest) ProtoMessage() {} +func (*ShortCharactersDataByGUIDsRequest) ProtoMessage() {} -func (x *SavePlayerPositionRequest) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[17] +func (x *ShortCharactersDataByGUIDsRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1328,92 +1778,58 @@ func (x *SavePlayerPositionRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SavePlayerPositionRequest.ProtoReflect.Descriptor instead. -func (*SavePlayerPositionRequest) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{17} +// Deprecated: Use ShortCharactersDataByGUIDsRequest.ProtoReflect.Descriptor instead. +func (*ShortCharactersDataByGUIDsRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{19} } -func (x *SavePlayerPositionRequest) GetApi() string { +func (x *ShortCharactersDataByGUIDsRequest) GetApi() string { if x != nil { return x.Api } return "" } -func (x *SavePlayerPositionRequest) GetRealmID() uint32 { +func (x *ShortCharactersDataByGUIDsRequest) GetRealmID() uint32 { if x != nil { return x.RealmID } return 0 } -func (x *SavePlayerPositionRequest) GetCharGUID() uint64 { - if x != nil { - return x.CharGUID - } - return 0 -} - -func (x *SavePlayerPositionRequest) GetMapID() uint32 { - if x != nil { - return x.MapID - } - return 0 -} - -func (x *SavePlayerPositionRequest) GetX() float32 { - if x != nil { - return x.X - } - return 0 -} - -func (x *SavePlayerPositionRequest) GetY() float32 { - if x != nil { - return x.Y - } - return 0 -} - -func (x *SavePlayerPositionRequest) GetZ() float32 { - if x != nil { - return x.Z - } - return 0 -} - -func (x *SavePlayerPositionRequest) GetO() float32 { +func (x *ShortCharactersDataByGUIDsRequest) GetGUIDs() []uint64 { if x != nil { - return x.O + return x.GUIDs } - return 0 + return nil } -type SavePlayerPositionResponse struct { +type ShortCharactersDataByGUIDsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Characters []*ShortCharactersDataByGUIDsResponse_ShortCharData `protobuf:"bytes,2,rep,name=characters,proto3" json:"characters,omitempty"` } -func (x *SavePlayerPositionResponse) Reset() { - *x = SavePlayerPositionResponse{} +func (x *ShortCharactersDataByGUIDsResponse) Reset() { + *x = ShortCharactersDataByGUIDsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[18] + mi := &file_characters_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SavePlayerPositionResponse) String() string { +func (x *ShortCharactersDataByGUIDsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SavePlayerPositionResponse) ProtoMessage() {} +func (*ShortCharactersDataByGUIDsResponse) ProtoMessage() {} -func (x *SavePlayerPositionResponse) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[18] +func (x *ShortCharactersDataByGUIDsResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1424,45 +1840,55 @@ func (x *SavePlayerPositionResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SavePlayerPositionResponse.ProtoReflect.Descriptor instead. -func (*SavePlayerPositionResponse) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{18} +// Deprecated: Use ShortCharactersDataByGUIDsResponse.ProtoReflect.Descriptor instead. +func (*ShortCharactersDataByGUIDsResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{20} } -func (x *SavePlayerPositionResponse) GetApi() string { +func (x *ShortCharactersDataByGUIDsResponse) GetApi() string { if x != nil { return x.Api } return "" } -type GetFriendsListRequest struct { +func (x *ShortCharactersDataByGUIDsResponse) GetCharacters() []*ShortCharactersDataByGUIDsResponse_ShortCharData { + if x != nil { + return x.Characters + } + return nil +} + +type ArenaTeamQueueDataForRatedArenaRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + LeaderGUID uint64 `protobuf:"varint,3,opt,name=leaderGUID,proto3" json:"leaderGUID,omitempty"` + PlayerGUIDs []uint64 `protobuf:"varint,4,rep,packed,name=playerGUIDs,proto3" json:"playerGUIDs,omitempty"` + ArenaType uint32 `protobuf:"varint,5,opt,name=arenaType,proto3" json:"arenaType,omitempty"` + StartMatchmakerRating uint32 `protobuf:"varint,6,opt,name=startMatchmakerRating,proto3" json:"startMatchmakerRating,omitempty"` } -func (x *GetFriendsListRequest) Reset() { - *x = GetFriendsListRequest{} +func (x *ArenaTeamQueueDataForRatedArenaRequest) Reset() { + *x = ArenaTeamQueueDataForRatedArenaRequest{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[19] + mi := &file_characters_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetFriendsListRequest) String() string { +func (x *ArenaTeamQueueDataForRatedArenaRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetFriendsListRequest) ProtoMessage() {} +func (*ArenaTeamQueueDataForRatedArenaRequest) ProtoMessage() {} -func (x *GetFriendsListRequest) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[19] +func (x *ArenaTeamQueueDataForRatedArenaRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1473,59 +1899,83 @@ func (x *GetFriendsListRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetFriendsListRequest.ProtoReflect.Descriptor instead. -func (*GetFriendsListRequest) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{19} +// Deprecated: Use ArenaTeamQueueDataForRatedArenaRequest.ProtoReflect.Descriptor instead. +func (*ArenaTeamQueueDataForRatedArenaRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{21} } -func (x *GetFriendsListRequest) GetApi() string { +func (x *ArenaTeamQueueDataForRatedArenaRequest) GetApi() string { if x != nil { return x.Api } return "" } -func (x *GetFriendsListRequest) GetRealmID() uint32 { +func (x *ArenaTeamQueueDataForRatedArenaRequest) GetRealmID() uint32 { if x != nil { return x.RealmID } return 0 } -func (x *GetFriendsListRequest) GetPlayerGUID() uint64 { +func (x *ArenaTeamQueueDataForRatedArenaRequest) GetLeaderGUID() uint64 { if x != nil { - return x.PlayerGUID + return x.LeaderGUID } return 0 } -type GetFriendsListResponse struct { +func (x *ArenaTeamQueueDataForRatedArenaRequest) GetPlayerGUIDs() []uint64 { + if x != nil { + return x.PlayerGUIDs + } + return nil +} + +func (x *ArenaTeamQueueDataForRatedArenaRequest) GetArenaType() uint32 { + if x != nil { + return x.ArenaType + } + return 0 +} + +func (x *ArenaTeamQueueDataForRatedArenaRequest) GetStartMatchmakerRating() uint32 { + if x != nil { + return x.StartMatchmakerRating + } + return 0 +} + +type ArenaTeamQueueDataForRatedArenaResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - Friends []*GetFriendsListResponse_Friend `protobuf:"bytes,2,rep,name=friends,proto3" json:"friends,omitempty"` - Ignored []*GetFriendsListResponse_IgnoredPlayer `protobuf:"bytes,3,rep,name=ignored,proto3" json:"ignored,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status ArenaTeamQueueDataForRatedArenaResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.ArenaTeamQueueDataForRatedArenaResponse_Status" json:"status,omitempty"` + ArenaTeamID uint32 `protobuf:"varint,3,opt,name=arenaTeamID,proto3" json:"arenaTeamID,omitempty"` + TeamRating uint32 `protobuf:"varint,4,opt,name=teamRating,proto3" json:"teamRating,omitempty"` + MatchmakerRating uint32 `protobuf:"varint,5,opt,name=matchmakerRating,proto3" json:"matchmakerRating,omitempty"` + PreviousOpponentsTeamID uint32 `protobuf:"varint,6,opt,name=previousOpponentsTeamID,proto3" json:"previousOpponentsTeamID,omitempty"` } -func (x *GetFriendsListResponse) Reset() { - *x = GetFriendsListResponse{} +func (x *ArenaTeamQueueDataForRatedArenaResponse) Reset() { + *x = ArenaTeamQueueDataForRatedArenaResponse{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[20] + mi := &file_characters_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetFriendsListResponse) String() string { +func (x *ArenaTeamQueueDataForRatedArenaResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetFriendsListResponse) ProtoMessage() {} +func (*ArenaTeamQueueDataForRatedArenaResponse) ProtoMessage() {} -func (x *GetFriendsListResponse) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[20] +func (x *ArenaTeamQueueDataForRatedArenaResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1536,62 +1986,90 @@ func (x *GetFriendsListResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetFriendsListResponse.ProtoReflect.Descriptor instead. -func (*GetFriendsListResponse) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{20} +// Deprecated: Use ArenaTeamQueueDataForRatedArenaResponse.ProtoReflect.Descriptor instead. +func (*ArenaTeamQueueDataForRatedArenaResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{22} } -func (x *GetFriendsListResponse) GetApi() string { +func (x *ArenaTeamQueueDataForRatedArenaResponse) GetApi() string { if x != nil { return x.Api } return "" } -func (x *GetFriendsListResponse) GetFriends() []*GetFriendsListResponse_Friend { +func (x *ArenaTeamQueueDataForRatedArenaResponse) GetStatus() ArenaTeamQueueDataForRatedArenaResponse_Status { if x != nil { - return x.Friends + return x.Status } - return nil + return ArenaTeamQueueDataForRatedArenaResponse_Ok } -func (x *GetFriendsListResponse) GetIgnored() []*GetFriendsListResponse_IgnoredPlayer { +func (x *ArenaTeamQueueDataForRatedArenaResponse) GetArenaTeamID() uint32 { if x != nil { - return x.Ignored + return x.ArenaTeamID } - return nil + return 0 } -type AddFriendRequest struct { +func (x *ArenaTeamQueueDataForRatedArenaResponse) GetTeamRating() uint32 { + if x != nil { + return x.TeamRating + } + return 0 +} + +func (x *ArenaTeamQueueDataForRatedArenaResponse) GetMatchmakerRating() uint32 { + if x != nil { + return x.MatchmakerRating + } + return 0 +} + +func (x *ArenaTeamQueueDataForRatedArenaResponse) GetPreviousOpponentsTeamID() uint32 { + if x != nil { + return x.PreviousOpponentsTeamID + } + return 0 +} + +type CreateArenaTeamFromPetitionRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` - FriendGUID uint64 `protobuf:"varint,4,opt,name=friendGUID,proto3" json:"friendGUID,omitempty"` - FriendName string `protobuf:"bytes,5,opt,name=friendName,proto3" json:"friendName,omitempty"` - Note string `protobuf:"bytes,6,opt,name=note,proto3" json:"note,omitempty"` -} - -func (x *AddFriendRequest) Reset() { - *x = AddFriendRequest{} + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + CaptainGUID uint64 `protobuf:"varint,3,opt,name=captainGUID,proto3" json:"captainGUID,omitempty"` + PetitionGUID uint64 `protobuf:"varint,4,opt,name=petitionGUID,proto3" json:"petitionGUID,omitempty"` + ArenaType uint32 `protobuf:"varint,5,opt,name=arenaType,proto3" json:"arenaType,omitempty"` + BackgroundColor uint32 `protobuf:"varint,6,opt,name=backgroundColor,proto3" json:"backgroundColor,omitempty"` + EmblemStyle uint32 `protobuf:"varint,7,opt,name=emblemStyle,proto3" json:"emblemStyle,omitempty"` + EmblemColor uint32 `protobuf:"varint,8,opt,name=emblemColor,proto3" json:"emblemColor,omitempty"` + BorderStyle uint32 `protobuf:"varint,9,opt,name=borderStyle,proto3" json:"borderStyle,omitempty"` + BorderColor uint32 `protobuf:"varint,10,opt,name=borderColor,proto3" json:"borderColor,omitempty"` + StartRating uint32 `protobuf:"varint,11,opt,name=startRating,proto3" json:"startRating,omitempty"` + StartPersonalRating uint32 `protobuf:"varint,12,opt,name=startPersonalRating,proto3" json:"startPersonalRating,omitempty"` + StartMatchmakerRating uint32 `protobuf:"varint,13,opt,name=startMatchmakerRating,proto3" json:"startMatchmakerRating,omitempty"` +} + +func (x *CreateArenaTeamFromPetitionRequest) Reset() { + *x = CreateArenaTeamFromPetitionRequest{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[21] + mi := &file_characters_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *AddFriendRequest) String() string { +func (x *CreateArenaTeamFromPetitionRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*AddFriendRequest) ProtoMessage() {} +func (*CreateArenaTeamFromPetitionRequest) ProtoMessage() {} -func (x *AddFriendRequest) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[21] +func (x *CreateArenaTeamFromPetitionRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1602,168 +2080,129 @@ func (x *AddFriendRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use AddFriendRequest.ProtoReflect.Descriptor instead. -func (*AddFriendRequest) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{21} +// Deprecated: Use CreateArenaTeamFromPetitionRequest.ProtoReflect.Descriptor instead. +func (*CreateArenaTeamFromPetitionRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{23} } -func (x *AddFriendRequest) GetApi() string { +func (x *CreateArenaTeamFromPetitionRequest) GetApi() string { if x != nil { return x.Api } return "" } -func (x *AddFriendRequest) GetRealmID() uint32 { +func (x *CreateArenaTeamFromPetitionRequest) GetRealmID() uint32 { if x != nil { return x.RealmID } return 0 } -func (x *AddFriendRequest) GetPlayerGUID() uint64 { +func (x *CreateArenaTeamFromPetitionRequest) GetCaptainGUID() uint64 { if x != nil { - return x.PlayerGUID + return x.CaptainGUID } return 0 } -func (x *AddFriendRequest) GetFriendGUID() uint64 { +func (x *CreateArenaTeamFromPetitionRequest) GetPetitionGUID() uint64 { if x != nil { - return x.FriendGUID + return x.PetitionGUID } return 0 } -func (x *AddFriendRequest) GetFriendName() string { +func (x *CreateArenaTeamFromPetitionRequest) GetArenaType() uint32 { if x != nil { - return x.FriendName + return x.ArenaType } - return "" + return 0 } -func (x *AddFriendRequest) GetNote() string { +func (x *CreateArenaTeamFromPetitionRequest) GetBackgroundColor() uint32 { if x != nil { - return x.Note - } - return "" -} - -type AddFriendResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - Result uint32 `protobuf:"varint,2,opt,name=result,proto3" json:"result,omitempty"` // FriendsResult enum value - Status uint32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"` // 0=offline, 1=online - Area uint32 `protobuf:"varint,4,opt,name=area,proto3" json:"area,omitempty"` - Level uint32 `protobuf:"varint,5,opt,name=level,proto3" json:"level,omitempty"` - ClassID uint32 `protobuf:"varint,6,opt,name=classID,proto3" json:"classID,omitempty"` -} - -func (x *AddFriendResponse) Reset() { - *x = AddFriendResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[22] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) + return x.BackgroundColor } + return 0 } -func (x *AddFriendResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddFriendResponse) ProtoMessage() {} - -func (x *AddFriendResponse) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[22] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *CreateArenaTeamFromPetitionRequest) GetEmblemStyle() uint32 { + if x != nil { + return x.EmblemStyle } - return mi.MessageOf(x) -} - -// Deprecated: Use AddFriendResponse.ProtoReflect.Descriptor instead. -func (*AddFriendResponse) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{22} + return 0 } -func (x *AddFriendResponse) GetApi() string { +func (x *CreateArenaTeamFromPetitionRequest) GetEmblemColor() uint32 { if x != nil { - return x.Api + return x.EmblemColor } - return "" + return 0 } -func (x *AddFriendResponse) GetResult() uint32 { +func (x *CreateArenaTeamFromPetitionRequest) GetBorderStyle() uint32 { if x != nil { - return x.Result + return x.BorderStyle } return 0 } -func (x *AddFriendResponse) GetStatus() uint32 { +func (x *CreateArenaTeamFromPetitionRequest) GetBorderColor() uint32 { if x != nil { - return x.Status + return x.BorderColor } return 0 } -func (x *AddFriendResponse) GetArea() uint32 { +func (x *CreateArenaTeamFromPetitionRequest) GetStartRating() uint32 { if x != nil { - return x.Area + return x.StartRating } return 0 } -func (x *AddFriendResponse) GetLevel() uint32 { +func (x *CreateArenaTeamFromPetitionRequest) GetStartPersonalRating() uint32 { if x != nil { - return x.Level + return x.StartPersonalRating } return 0 } -func (x *AddFriendResponse) GetClassID() uint32 { +func (x *CreateArenaTeamFromPetitionRequest) GetStartMatchmakerRating() uint32 { if x != nil { - return x.ClassID + return x.StartMatchmakerRating } return 0 } -type RemoveFriendRequest struct { +type CreateArenaTeamFromPetitionResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` - FriendGUID uint64 `protobuf:"varint,4,opt,name=friendGUID,proto3" json:"friendGUID,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status CreateArenaTeamFromPetitionResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.CreateArenaTeamFromPetitionResponse_Status" json:"status,omitempty"` + ArenaTeamID uint32 `protobuf:"varint,3,opt,name=arenaTeamID,proto3" json:"arenaTeamID,omitempty"` } -func (x *RemoveFriendRequest) Reset() { - *x = RemoveFriendRequest{} +func (x *CreateArenaTeamFromPetitionResponse) Reset() { + *x = CreateArenaTeamFromPetitionResponse{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[23] + mi := &file_characters_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *RemoveFriendRequest) String() string { +func (x *CreateArenaTeamFromPetitionResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RemoveFriendRequest) ProtoMessage() {} +func (*CreateArenaTeamFromPetitionResponse) ProtoMessage() {} -func (x *RemoveFriendRequest) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[23] +func (x *CreateArenaTeamFromPetitionResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1774,64 +2213,68 @@ func (x *RemoveFriendRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RemoveFriendRequest.ProtoReflect.Descriptor instead. -func (*RemoveFriendRequest) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{23} +// Deprecated: Use CreateArenaTeamFromPetitionResponse.ProtoReflect.Descriptor instead. +func (*CreateArenaTeamFromPetitionResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{24} } -func (x *RemoveFriendRequest) GetApi() string { +func (x *CreateArenaTeamFromPetitionResponse) GetApi() string { if x != nil { return x.Api } return "" } -func (x *RemoveFriendRequest) GetRealmID() uint32 { - if x != nil { - return x.RealmID - } - return 0 -} - -func (x *RemoveFriendRequest) GetPlayerGUID() uint64 { +func (x *CreateArenaTeamFromPetitionResponse) GetStatus() CreateArenaTeamFromPetitionResponse_Status { if x != nil { - return x.PlayerGUID + return x.Status } - return 0 + return CreateArenaTeamFromPetitionResponse_Ok } -func (x *RemoveFriendRequest) GetFriendGUID() uint64 { +func (x *CreateArenaTeamFromPetitionResponse) GetArenaTeamID() uint32 { if x != nil { - return x.FriendGUID + return x.ArenaTeamID } return 0 } -type RemoveFriendResponse struct { +type ArenaTeamMemberData struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` -} - -func (x *RemoveFriendResponse) Reset() { - *x = RemoveFriendResponse{} + PlayerGUID uint64 `protobuf:"varint,1,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Online bool `protobuf:"varint,3,opt,name=online,proto3" json:"online,omitempty"` + Level uint32 `protobuf:"varint,4,opt,name=level,proto3" json:"level,omitempty"` + Class uint32 `protobuf:"varint,5,opt,name=class,proto3" json:"class,omitempty"` + WeekGames uint32 `protobuf:"varint,6,opt,name=weekGames,proto3" json:"weekGames,omitempty"` + WeekWins uint32 `protobuf:"varint,7,opt,name=weekWins,proto3" json:"weekWins,omitempty"` + SeasonGames uint32 `protobuf:"varint,8,opt,name=seasonGames,proto3" json:"seasonGames,omitempty"` + SeasonWins uint32 `protobuf:"varint,9,opt,name=seasonWins,proto3" json:"seasonWins,omitempty"` + PersonalRating uint32 `protobuf:"varint,10,opt,name=personalRating,proto3" json:"personalRating,omitempty"` + MatchmakerRating uint32 `protobuf:"varint,11,opt,name=matchmakerRating,proto3" json:"matchmakerRating,omitempty"` + MaxMMR uint32 `protobuf:"varint,12,opt,name=maxMMR,proto3" json:"maxMMR,omitempty"` +} + +func (x *ArenaTeamMemberData) Reset() { + *x = ArenaTeamMemberData{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[24] + mi := &file_characters_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *RemoveFriendResponse) String() string { +func (x *ArenaTeamMemberData) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RemoveFriendResponse) ProtoMessage() {} +func (*ArenaTeamMemberData) ProtoMessage() {} -func (x *RemoveFriendResponse) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[24] +func (x *ArenaTeamMemberData) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1842,107 +2285,120 @@ func (x *RemoveFriendResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RemoveFriendResponse.ProtoReflect.Descriptor instead. -func (*RemoveFriendResponse) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{24} +// Deprecated: Use ArenaTeamMemberData.ProtoReflect.Descriptor instead. +func (*ArenaTeamMemberData) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{25} } -func (x *RemoveFriendResponse) GetApi() string { +func (x *ArenaTeamMemberData) GetPlayerGUID() uint64 { if x != nil { - return x.Api + return x.PlayerGUID } - return "" + return 0 } -type SetFriendNoteRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` - FriendGUID uint64 `protobuf:"varint,4,opt,name=friendGUID,proto3" json:"friendGUID,omitempty"` - Note string `protobuf:"bytes,5,opt,name=note,proto3" json:"note,omitempty"` +func (x *ArenaTeamMemberData) GetName() string { + if x != nil { + return x.Name + } + return "" } -func (x *SetFriendNoteRequest) Reset() { - *x = SetFriendNoteRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[25] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *ArenaTeamMemberData) GetOnline() bool { + if x != nil { + return x.Online } + return false } -func (x *SetFriendNoteRequest) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *ArenaTeamMemberData) GetLevel() uint32 { + if x != nil { + return x.Level + } + return 0 } -func (*SetFriendNoteRequest) ProtoMessage() {} +func (x *ArenaTeamMemberData) GetClass() uint32 { + if x != nil { + return x.Class + } + return 0 +} -func (x *SetFriendNoteRequest) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[25] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *ArenaTeamMemberData) GetWeekGames() uint32 { + if x != nil { + return x.WeekGames } - return mi.MessageOf(x) + return 0 } -// Deprecated: Use SetFriendNoteRequest.ProtoReflect.Descriptor instead. -func (*SetFriendNoteRequest) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{25} +func (x *ArenaTeamMemberData) GetWeekWins() uint32 { + if x != nil { + return x.WeekWins + } + return 0 } -func (x *SetFriendNoteRequest) GetApi() string { +func (x *ArenaTeamMemberData) GetSeasonGames() uint32 { if x != nil { - return x.Api + return x.SeasonGames } - return "" + return 0 } -func (x *SetFriendNoteRequest) GetRealmID() uint32 { +func (x *ArenaTeamMemberData) GetSeasonWins() uint32 { if x != nil { - return x.RealmID + return x.SeasonWins } return 0 } -func (x *SetFriendNoteRequest) GetPlayerGUID() uint64 { +func (x *ArenaTeamMemberData) GetPersonalRating() uint32 { if x != nil { - return x.PlayerGUID + return x.PersonalRating } return 0 } -func (x *SetFriendNoteRequest) GetFriendGUID() uint64 { +func (x *ArenaTeamMemberData) GetMatchmakerRating() uint32 { if x != nil { - return x.FriendGUID + return x.MatchmakerRating } return 0 } -func (x *SetFriendNoteRequest) GetNote() string { +func (x *ArenaTeamMemberData) GetMaxMMR() uint32 { if x != nil { - return x.Note + return x.MaxMMR } - return "" + return 0 } -type SetFriendNoteResponse struct { +type ArenaTeamData struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` -} - -func (x *SetFriendNoteResponse) Reset() { - *x = SetFriendNoteResponse{} + ArenaTeamID uint32 `protobuf:"varint,1,opt,name=arenaTeamID,proto3" json:"arenaTeamID,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + CaptainGUID uint64 `protobuf:"varint,3,opt,name=captainGUID,proto3" json:"captainGUID,omitempty"` + Type uint32 `protobuf:"varint,4,opt,name=type,proto3" json:"type,omitempty"` + Rating uint32 `protobuf:"varint,5,opt,name=rating,proto3" json:"rating,omitempty"` + WeekGames uint32 `protobuf:"varint,6,opt,name=weekGames,proto3" json:"weekGames,omitempty"` + WeekWins uint32 `protobuf:"varint,7,opt,name=weekWins,proto3" json:"weekWins,omitempty"` + SeasonGames uint32 `protobuf:"varint,8,opt,name=seasonGames,proto3" json:"seasonGames,omitempty"` + SeasonWins uint32 `protobuf:"varint,9,opt,name=seasonWins,proto3" json:"seasonWins,omitempty"` + Rank uint32 `protobuf:"varint,10,opt,name=rank,proto3" json:"rank,omitempty"` + BackgroundColor uint32 `protobuf:"varint,11,opt,name=backgroundColor,proto3" json:"backgroundColor,omitempty"` + EmblemStyle uint32 `protobuf:"varint,12,opt,name=emblemStyle,proto3" json:"emblemStyle,omitempty"` + EmblemColor uint32 `protobuf:"varint,13,opt,name=emblemColor,proto3" json:"emblemColor,omitempty"` + BorderStyle uint32 `protobuf:"varint,14,opt,name=borderStyle,proto3" json:"borderStyle,omitempty"` + BorderColor uint32 `protobuf:"varint,15,opt,name=borderColor,proto3" json:"borderColor,omitempty"` + Members []*ArenaTeamMemberData `protobuf:"bytes,16,rep,name=members,proto3" json:"members,omitempty"` +} + +func (x *ArenaTeamData) Reset() { + *x = ArenaTeamData{} if protoimpl.UnsafeEnabled { mi := &file_characters_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1950,13 +2406,13 @@ func (x *SetFriendNoteResponse) Reset() { } } -func (x *SetFriendNoteResponse) String() string { +func (x *ArenaTeamData) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetFriendNoteResponse) ProtoMessage() {} +func (*ArenaTeamData) ProtoMessage() {} -func (x *SetFriendNoteResponse) ProtoReflect() protoreflect.Message { +func (x *ArenaTeamData) ProtoReflect() protoreflect.Message { mi := &file_characters_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1968,172 +2424,150 @@ func (x *SetFriendNoteResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetFriendNoteResponse.ProtoReflect.Descriptor instead. -func (*SetFriendNoteResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ArenaTeamData.ProtoReflect.Descriptor instead. +func (*ArenaTeamData) Descriptor() ([]byte, []int) { return file_characters_proto_rawDescGZIP(), []int{26} } -func (x *SetFriendNoteResponse) GetApi() string { +func (x *ArenaTeamData) GetArenaTeamID() uint32 { if x != nil { - return x.Api + return x.ArenaTeamID } - return "" -} - -type AddIgnoreRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` - IgnoredGUID uint64 `protobuf:"varint,4,opt,name=ignoredGUID,proto3" json:"ignoredGUID,omitempty"` + return 0 } -func (x *AddIgnoreRequest) Reset() { - *x = AddIgnoreRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[27] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *ArenaTeamData) GetName() string { + if x != nil { + return x.Name } + return "" } -func (x *AddIgnoreRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddIgnoreRequest) ProtoMessage() {} - -func (x *AddIgnoreRequest) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[27] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *ArenaTeamData) GetCaptainGUID() uint64 { + if x != nil { + return x.CaptainGUID } - return mi.MessageOf(x) -} - -// Deprecated: Use AddIgnoreRequest.ProtoReflect.Descriptor instead. -func (*AddIgnoreRequest) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{27} + return 0 } -func (x *AddIgnoreRequest) GetApi() string { +func (x *ArenaTeamData) GetType() uint32 { if x != nil { - return x.Api + return x.Type } - return "" + return 0 } -func (x *AddIgnoreRequest) GetRealmID() uint32 { +func (x *ArenaTeamData) GetRating() uint32 { if x != nil { - return x.RealmID + return x.Rating } return 0 } -func (x *AddIgnoreRequest) GetPlayerGUID() uint64 { +func (x *ArenaTeamData) GetWeekGames() uint32 { if x != nil { - return x.PlayerGUID + return x.WeekGames } return 0 } -func (x *AddIgnoreRequest) GetIgnoredGUID() uint64 { +func (x *ArenaTeamData) GetWeekWins() uint32 { if x != nil { - return x.IgnoredGUID + return x.WeekWins } return 0 } -type AddIgnoreResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - Result uint32 `protobuf:"varint,2,opt,name=result,proto3" json:"result,omitempty"` // FriendsResult enum value +func (x *ArenaTeamData) GetSeasonGames() uint32 { + if x != nil { + return x.SeasonGames + } + return 0 } -func (x *AddIgnoreResponse) Reset() { - *x = AddIgnoreResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[28] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *ArenaTeamData) GetSeasonWins() uint32 { + if x != nil { + return x.SeasonWins } + return 0 } -func (x *AddIgnoreResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *ArenaTeamData) GetRank() uint32 { + if x != nil { + return x.Rank + } + return 0 } -func (*AddIgnoreResponse) ProtoMessage() {} +func (x *ArenaTeamData) GetBackgroundColor() uint32 { + if x != nil { + return x.BackgroundColor + } + return 0 +} -func (x *AddIgnoreResponse) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[28] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *ArenaTeamData) GetEmblemStyle() uint32 { + if x != nil { + return x.EmblemStyle } - return mi.MessageOf(x) + return 0 } -// Deprecated: Use AddIgnoreResponse.ProtoReflect.Descriptor instead. -func (*AddIgnoreResponse) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{28} +func (x *ArenaTeamData) GetEmblemColor() uint32 { + if x != nil { + return x.EmblemColor + } + return 0 } -func (x *AddIgnoreResponse) GetApi() string { +func (x *ArenaTeamData) GetBorderStyle() uint32 { if x != nil { - return x.Api + return x.BorderStyle } - return "" + return 0 } -func (x *AddIgnoreResponse) GetResult() uint32 { +func (x *ArenaTeamData) GetBorderColor() uint32 { if x != nil { - return x.Result + return x.BorderColor } return 0 } -type RemoveIgnoreRequest struct { +func (x *ArenaTeamData) GetMembers() []*ArenaTeamMemberData { + if x != nil { + return x.Members + } + return nil +} + +type GetArenaTeamRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` - IgnoredGUID uint64 `protobuf:"varint,4,opt,name=ignoredGUID,proto3" json:"ignoredGUID,omitempty"` + ArenaTeamID uint32 `protobuf:"varint,3,opt,name=arenaTeamID,proto3" json:"arenaTeamID,omitempty"` } -func (x *RemoveIgnoreRequest) Reset() { - *x = RemoveIgnoreRequest{} +func (x *GetArenaTeamRequest) Reset() { + *x = GetArenaTeamRequest{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[29] + mi := &file_characters_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *RemoveIgnoreRequest) String() string { +func (x *GetArenaTeamRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RemoveIgnoreRequest) ProtoMessage() {} +func (*GetArenaTeamRequest) ProtoMessage() {} -func (x *RemoveIgnoreRequest) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[29] +func (x *GetArenaTeamRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2144,64 +2578,59 @@ func (x *RemoveIgnoreRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RemoveIgnoreRequest.ProtoReflect.Descriptor instead. -func (*RemoveIgnoreRequest) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{29} +// Deprecated: Use GetArenaTeamRequest.ProtoReflect.Descriptor instead. +func (*GetArenaTeamRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{27} } -func (x *RemoveIgnoreRequest) GetApi() string { +func (x *GetArenaTeamRequest) GetApi() string { if x != nil { return x.Api } return "" } -func (x *RemoveIgnoreRequest) GetRealmID() uint32 { +func (x *GetArenaTeamRequest) GetRealmID() uint32 { if x != nil { return x.RealmID } return 0 } -func (x *RemoveIgnoreRequest) GetPlayerGUID() uint64 { - if x != nil { - return x.PlayerGUID - } - return 0 -} - -func (x *RemoveIgnoreRequest) GetIgnoredGUID() uint64 { +func (x *GetArenaTeamRequest) GetArenaTeamID() uint32 { if x != nil { - return x.IgnoredGUID + return x.ArenaTeamID } return 0 } -type RemoveIgnoreResponse struct { +type GetArenaTeamResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status GetArenaTeamResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.GetArenaTeamResponse_Status" json:"status,omitempty"` + Team *ArenaTeamData `protobuf:"bytes,3,opt,name=team,proto3" json:"team,omitempty"` } -func (x *RemoveIgnoreResponse) Reset() { - *x = RemoveIgnoreResponse{} +func (x *GetArenaTeamResponse) Reset() { + *x = GetArenaTeamResponse{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[30] + mi := &file_characters_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *RemoveIgnoreResponse) String() string { +func (x *GetArenaTeamResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RemoveIgnoreResponse) ProtoMessage() {} +func (*GetArenaTeamResponse) ProtoMessage() {} -func (x *RemoveIgnoreResponse) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[30] +func (x *GetArenaTeamResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2212,49 +2641,62 @@ func (x *RemoveIgnoreResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RemoveIgnoreResponse.ProtoReflect.Descriptor instead. -func (*RemoveIgnoreResponse) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{30} +// Deprecated: Use GetArenaTeamResponse.ProtoReflect.Descriptor instead. +func (*GetArenaTeamResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{28} } -func (x *RemoveIgnoreResponse) GetApi() string { +func (x *GetArenaTeamResponse) GetApi() string { if x != nil { return x.Api } return "" } -type NotifyStatusChangeRequest struct { +func (x *GetArenaTeamResponse) GetStatus() GetArenaTeamResponse_Status { + if x != nil { + return x.Status + } + return GetArenaTeamResponse_Ok +} + +func (x *GetArenaTeamResponse) GetTeam() *ArenaTeamData { + if x != nil { + return x.Team + } + return nil +} + +type ArenaTeamPetitionData struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` - Status uint32 `protobuf:"varint,4,opt,name=status,proto3" json:"status,omitempty"` // 0=offline, 1=online - Area uint32 `protobuf:"varint,5,opt,name=area,proto3" json:"area,omitempty"` - Level uint32 `protobuf:"varint,6,opt,name=level,proto3" json:"level,omitempty"` - ClassID uint32 `protobuf:"varint,7,opt,name=classID,proto3" json:"classID,omitempty"` + PetitionGUID uint64 `protobuf:"varint,1,opt,name=petitionGUID,proto3" json:"petitionGUID,omitempty"` + PetitionID uint32 `protobuf:"varint,2,opt,name=petitionID,proto3" json:"petitionID,omitempty"` + OwnerGUID uint64 `protobuf:"varint,3,opt,name=ownerGUID,proto3" json:"ownerGUID,omitempty"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` + ArenaType uint32 `protobuf:"varint,5,opt,name=arenaType,proto3" json:"arenaType,omitempty"` + Signatures uint32 `protobuf:"varint,6,opt,name=signatures,proto3" json:"signatures,omitempty"` } -func (x *NotifyStatusChangeRequest) Reset() { - *x = NotifyStatusChangeRequest{} +func (x *ArenaTeamPetitionData) Reset() { + *x = ArenaTeamPetitionData{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[31] + mi := &file_characters_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *NotifyStatusChangeRequest) String() string { +func (x *ArenaTeamPetitionData) String() string { return protoimpl.X.MessageStringOf(x) } -func (*NotifyStatusChangeRequest) ProtoMessage() {} +func (*ArenaTeamPetitionData) ProtoMessage() {} -func (x *NotifyStatusChangeRequest) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[31] +func (x *ArenaTeamPetitionData) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2265,85 +2707,80 @@ func (x *NotifyStatusChangeRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use NotifyStatusChangeRequest.ProtoReflect.Descriptor instead. -func (*NotifyStatusChangeRequest) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{31} -} - -func (x *NotifyStatusChangeRequest) GetApi() string { - if x != nil { - return x.Api - } - return "" +// Deprecated: Use ArenaTeamPetitionData.ProtoReflect.Descriptor instead. +func (*ArenaTeamPetitionData) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{29} } -func (x *NotifyStatusChangeRequest) GetRealmID() uint32 { +func (x *ArenaTeamPetitionData) GetPetitionGUID() uint64 { if x != nil { - return x.RealmID + return x.PetitionGUID } return 0 } -func (x *NotifyStatusChangeRequest) GetPlayerGUID() uint64 { +func (x *ArenaTeamPetitionData) GetPetitionID() uint32 { if x != nil { - return x.PlayerGUID + return x.PetitionID } return 0 } -func (x *NotifyStatusChangeRequest) GetStatus() uint32 { +func (x *ArenaTeamPetitionData) GetOwnerGUID() uint64 { if x != nil { - return x.Status + return x.OwnerGUID } return 0 } -func (x *NotifyStatusChangeRequest) GetArea() uint32 { +func (x *ArenaTeamPetitionData) GetName() string { if x != nil { - return x.Area + return x.Name } - return 0 + return "" } -func (x *NotifyStatusChangeRequest) GetLevel() uint32 { +func (x *ArenaTeamPetitionData) GetArenaType() uint32 { if x != nil { - return x.Level + return x.ArenaType } return 0 } -func (x *NotifyStatusChangeRequest) GetClassID() uint32 { +func (x *ArenaTeamPetitionData) GetSignatures() uint32 { if x != nil { - return x.ClassID + return x.Signatures } return 0 } -type NotifyStatusChangeResponse struct { +type GetArenaTeamPetitionRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PetitionGUID uint64 `protobuf:"varint,3,opt,name=petitionGUID,proto3" json:"petitionGUID,omitempty"` } -func (x *NotifyStatusChangeResponse) Reset() { - *x = NotifyStatusChangeResponse{} +func (x *GetArenaTeamPetitionRequest) Reset() { + *x = GetArenaTeamPetitionRequest{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[32] + mi := &file_characters_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *NotifyStatusChangeResponse) String() string { +func (x *GetArenaTeamPetitionRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*NotifyStatusChangeResponse) ProtoMessage() {} +func (*GetArenaTeamPetitionRequest) ProtoMessage() {} -func (x *NotifyStatusChangeResponse) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[32] +func (x *GetArenaTeamPetitionRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2354,44 +2791,59 @@ func (x *NotifyStatusChangeResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use NotifyStatusChangeResponse.ProtoReflect.Descriptor instead. -func (*NotifyStatusChangeResponse) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{32} +// Deprecated: Use GetArenaTeamPetitionRequest.ProtoReflect.Descriptor instead. +func (*GetArenaTeamPetitionRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{30} } -func (x *NotifyStatusChangeResponse) GetApi() string { +func (x *GetArenaTeamPetitionRequest) GetApi() string { if x != nil { return x.Api } return "" } -type GetOnlineCharactersRequest struct { +func (x *GetArenaTeamPetitionRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *GetArenaTeamPetitionRequest) GetPetitionGUID() uint64 { + if x != nil { + return x.PetitionGUID + } + return 0 +} + +type GetArenaTeamPetitionResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status GetArenaTeamPetitionResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.GetArenaTeamPetitionResponse_Status" json:"status,omitempty"` + Petition *ArenaTeamPetitionData `protobuf:"bytes,3,opt,name=petition,proto3" json:"petition,omitempty"` } -func (x *GetOnlineCharactersRequest) Reset() { - *x = GetOnlineCharactersRequest{} +func (x *GetArenaTeamPetitionResponse) Reset() { + *x = GetArenaTeamPetitionResponse{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[33] + mi := &file_characters_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetOnlineCharactersRequest) String() string { +func (x *GetArenaTeamPetitionResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetOnlineCharactersRequest) ProtoMessage() {} +func (*GetArenaTeamPetitionResponse) ProtoMessage() {} -func (x *GetOnlineCharactersRequest) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[33] +func (x *GetArenaTeamPetitionResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2402,52 +2854,63 @@ func (x *GetOnlineCharactersRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetOnlineCharactersRequest.ProtoReflect.Descriptor instead. -func (*GetOnlineCharactersRequest) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{33} +// Deprecated: Use GetArenaTeamPetitionResponse.ProtoReflect.Descriptor instead. +func (*GetArenaTeamPetitionResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{31} } -func (x *GetOnlineCharactersRequest) GetApi() string { +func (x *GetArenaTeamPetitionResponse) GetApi() string { if x != nil { return x.Api } return "" } -func (x *GetOnlineCharactersRequest) GetRealmID() uint32 { +func (x *GetArenaTeamPetitionResponse) GetStatus() GetArenaTeamPetitionResponse_Status { if x != nil { - return x.RealmID + return x.Status } - return 0 + return GetArenaTeamPetitionResponse_Ok } -type GetOnlineCharactersResponse struct { +func (x *GetArenaTeamPetitionResponse) GetPetition() *ArenaTeamPetitionData { + if x != nil { + return x.Petition + } + return nil +} + +type InviteArenaTeamMemberRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - CharacterGUIDs []uint64 `protobuf:"varint,2,rep,packed,name=characterGUIDs,proto3" json:"characterGUIDs,omitempty"` - TotalCount uint32 `protobuf:"varint,3,opt,name=totalCount,proto3" json:"totalCount,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + ArenaTeamID uint32 `protobuf:"varint,3,opt,name=arenaTeamID,proto3" json:"arenaTeamID,omitempty"` + InviterGUID uint64 `protobuf:"varint,4,opt,name=inviterGUID,proto3" json:"inviterGUID,omitempty"` + InviterName string `protobuf:"bytes,5,opt,name=inviterName,proto3" json:"inviterName,omitempty"` + TargetGUID uint64 `protobuf:"varint,6,opt,name=targetGUID,proto3" json:"targetGUID,omitempty"` + TargetName string `protobuf:"bytes,7,opt,name=targetName,proto3" json:"targetName,omitempty"` } -func (x *GetOnlineCharactersResponse) Reset() { - *x = GetOnlineCharactersResponse{} +func (x *InviteArenaTeamMemberRequest) Reset() { + *x = InviteArenaTeamMemberRequest{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[34] + mi := &file_characters_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetOnlineCharactersResponse) String() string { +func (x *InviteArenaTeamMemberRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetOnlineCharactersResponse) ProtoMessage() {} +func (*InviteArenaTeamMemberRequest) ProtoMessage() {} -func (x *GetOnlineCharactersResponse) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[34] +func (x *InviteArenaTeamMemberRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2458,64 +2921,87 @@ func (x *GetOnlineCharactersResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetOnlineCharactersResponse.ProtoReflect.Descriptor instead. -func (*GetOnlineCharactersResponse) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{34} +// Deprecated: Use InviteArenaTeamMemberRequest.ProtoReflect.Descriptor instead. +func (*InviteArenaTeamMemberRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{32} } -func (x *GetOnlineCharactersResponse) GetApi() string { +func (x *InviteArenaTeamMemberRequest) GetApi() string { if x != nil { return x.Api } return "" } -func (x *GetOnlineCharactersResponse) GetCharacterGUIDs() []uint64 { +func (x *InviteArenaTeamMemberRequest) GetRealmID() uint32 { if x != nil { - return x.CharacterGUIDs + return x.RealmID } - return nil + return 0 } -func (x *GetOnlineCharactersResponse) GetTotalCount() uint32 { +func (x *InviteArenaTeamMemberRequest) GetArenaTeamID() uint32 { if x != nil { - return x.TotalCount + return x.ArenaTeamID } return 0 } -type WhoQueryResponse_WhoItem struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields +func (x *InviteArenaTeamMemberRequest) GetInviterGUID() uint64 { + if x != nil { + return x.InviterGUID + } + return 0 +} - Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Guild string `protobuf:"bytes,3,opt,name=guild,proto3" json:"guild,omitempty"` - Lvl uint32 `protobuf:"varint,4,opt,name=lvl,proto3" json:"lvl,omitempty"` - Class uint32 `protobuf:"varint,5,opt,name=class,proto3" json:"class,omitempty"` - Race uint32 `protobuf:"varint,6,opt,name=race,proto3" json:"race,omitempty"` - Gender uint32 `protobuf:"varint,7,opt,name=gender,proto3" json:"gender,omitempty"` - ZoneID uint32 `protobuf:"varint,8,opt,name=zoneID,proto3" json:"zoneID,omitempty"` +func (x *InviteArenaTeamMemberRequest) GetInviterName() string { + if x != nil { + return x.InviterName + } + return "" } -func (x *WhoQueryResponse_WhoItem) Reset() { - *x = WhoQueryResponse_WhoItem{} +func (x *InviteArenaTeamMemberRequest) GetTargetGUID() uint64 { + if x != nil { + return x.TargetGUID + } + return 0 +} + +func (x *InviteArenaTeamMemberRequest) GetTargetName() string { + if x != nil { + return x.TargetName + } + return "" +} + +type InviteArenaTeamMemberResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status ArenaTeamMutationStatus `protobuf:"varint,2,opt,name=status,proto3,enum=v1.ArenaTeamMutationStatus" json:"status,omitempty"` + Team *ArenaTeamData `protobuf:"bytes,3,opt,name=team,proto3" json:"team,omitempty"` +} + +func (x *InviteArenaTeamMemberResponse) Reset() { + *x = InviteArenaTeamMemberResponse{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[35] + mi := &file_characters_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *WhoQueryResponse_WhoItem) String() string { +func (x *InviteArenaTeamMemberResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*WhoQueryResponse_WhoItem) ProtoMessage() {} +func (*InviteArenaTeamMemberResponse) ProtoMessage() {} -func (x *WhoQueryResponse_WhoItem) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[35] +func (x *InviteArenaTeamMemberResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2526,103 +3012,138 @@ func (x *WhoQueryResponse_WhoItem) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use WhoQueryResponse_WhoItem.ProtoReflect.Descriptor instead. -func (*WhoQueryResponse_WhoItem) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{10, 0} +// Deprecated: Use InviteArenaTeamMemberResponse.ProtoReflect.Descriptor instead. +func (*InviteArenaTeamMemberResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{33} } -func (x *WhoQueryResponse_WhoItem) GetGuid() uint64 { +func (x *InviteArenaTeamMemberResponse) GetApi() string { if x != nil { - return x.Guid + return x.Api } - return 0 + return "" } -func (x *WhoQueryResponse_WhoItem) GetName() string { +func (x *InviteArenaTeamMemberResponse) GetStatus() ArenaTeamMutationStatus { if x != nil { - return x.Name + return x.Status } - return "" + return ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK } -func (x *WhoQueryResponse_WhoItem) GetGuild() string { +func (x *InviteArenaTeamMemberResponse) GetTeam() *ArenaTeamData { if x != nil { - return x.Guild + return x.Team } - return "" + return nil } -func (x *WhoQueryResponse_WhoItem) GetLvl() uint32 { +type AcceptArenaTeamInviteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + PlayerName string `protobuf:"bytes,4,opt,name=playerName,proto3" json:"playerName,omitempty"` + PersonalRating uint32 `protobuf:"varint,5,opt,name=personalRating,proto3" json:"personalRating,omitempty"` +} + +func (x *AcceptArenaTeamInviteRequest) Reset() { + *x = AcceptArenaTeamInviteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AcceptArenaTeamInviteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AcceptArenaTeamInviteRequest) ProtoMessage() {} + +func (x *AcceptArenaTeamInviteRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AcceptArenaTeamInviteRequest.ProtoReflect.Descriptor instead. +func (*AcceptArenaTeamInviteRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{34} +} + +func (x *AcceptArenaTeamInviteRequest) GetApi() string { if x != nil { - return x.Lvl + return x.Api } - return 0 + return "" } -func (x *WhoQueryResponse_WhoItem) GetClass() uint32 { +func (x *AcceptArenaTeamInviteRequest) GetRealmID() uint32 { if x != nil { - return x.Class + return x.RealmID } return 0 } -func (x *WhoQueryResponse_WhoItem) GetRace() uint32 { +func (x *AcceptArenaTeamInviteRequest) GetPlayerGUID() uint64 { if x != nil { - return x.Race + return x.PlayerGUID } return 0 } -func (x *WhoQueryResponse_WhoItem) GetGender() uint32 { +func (x *AcceptArenaTeamInviteRequest) GetPlayerName() string { if x != nil { - return x.Gender + return x.PlayerName } - return 0 + return "" } -func (x *WhoQueryResponse_WhoItem) GetZoneID() uint32 { +func (x *AcceptArenaTeamInviteRequest) GetPersonalRating() uint32 { if x != nil { - return x.ZoneID + return x.PersonalRating } return 0 } -type CharacterOnlineByNameResponse_Char struct { +type AcceptArenaTeamInviteResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RealmID uint32 `protobuf:"varint,1,opt,name=realmID,proto3" json:"realmID,omitempty"` - GatewayID string `protobuf:"bytes,2,opt,name=gatewayID,proto3" json:"gatewayID,omitempty"` - CharGUID uint64 `protobuf:"varint,3,opt,name=charGUID,proto3" json:"charGUID,omitempty"` - CharName string `protobuf:"bytes,4,opt,name=charName,proto3" json:"charName,omitempty"` - CharRace uint32 `protobuf:"varint,5,opt,name=charRace,proto3" json:"charRace,omitempty"` - CharClass uint32 `protobuf:"varint,6,opt,name=charClass,proto3" json:"charClass,omitempty"` - CharGender uint32 `protobuf:"varint,7,opt,name=charGender,proto3" json:"charGender,omitempty"` - CharLvl uint32 `protobuf:"varint,8,opt,name=charLvl,proto3" json:"charLvl,omitempty"` - CharZone uint32 `protobuf:"varint,9,opt,name=charZone,proto3" json:"charZone,omitempty"` - CharMap uint32 `protobuf:"varint,10,opt,name=charMap,proto3" json:"charMap,omitempty"` - CharGuildID uint64 `protobuf:"varint,11,opt,name=charGuildID,proto3" json:"charGuildID,omitempty"` - AccountID uint32 `protobuf:"varint,12,opt,name=accountID,proto3" json:"accountID,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status ArenaTeamMutationStatus `protobuf:"varint,2,opt,name=status,proto3,enum=v1.ArenaTeamMutationStatus" json:"status,omitempty"` + Team *ArenaTeamData `protobuf:"bytes,3,opt,name=team,proto3" json:"team,omitempty"` } -func (x *CharacterOnlineByNameResponse_Char) Reset() { - *x = CharacterOnlineByNameResponse_Char{} +func (x *AcceptArenaTeamInviteResponse) Reset() { + *x = AcceptArenaTeamInviteResponse{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[36] + mi := &file_characters_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *CharacterOnlineByNameResponse_Char) String() string { +func (x *AcceptArenaTeamInviteResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CharacterOnlineByNameResponse_Char) ProtoMessage() {} +func (*AcceptArenaTeamInviteResponse) ProtoMessage() {} -func (x *CharacterOnlineByNameResponse_Char) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[36] +func (x *AcceptArenaTeamInviteResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2633,117 +3154,109 @@ func (x *CharacterOnlineByNameResponse_Char) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use CharacterOnlineByNameResponse_Char.ProtoReflect.Descriptor instead. -func (*CharacterOnlineByNameResponse_Char) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{12, 0} -} - -func (x *CharacterOnlineByNameResponse_Char) GetRealmID() uint32 { - if x != nil { - return x.RealmID - } - return 0 +// Deprecated: Use AcceptArenaTeamInviteResponse.ProtoReflect.Descriptor instead. +func (*AcceptArenaTeamInviteResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{35} } -func (x *CharacterOnlineByNameResponse_Char) GetGatewayID() string { +func (x *AcceptArenaTeamInviteResponse) GetApi() string { if x != nil { - return x.GatewayID + return x.Api } return "" } -func (x *CharacterOnlineByNameResponse_Char) GetCharGUID() uint64 { +func (x *AcceptArenaTeamInviteResponse) GetStatus() ArenaTeamMutationStatus { if x != nil { - return x.CharGUID + return x.Status } - return 0 + return ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK } -func (x *CharacterOnlineByNameResponse_Char) GetCharName() string { +func (x *AcceptArenaTeamInviteResponse) GetTeam() *ArenaTeamData { if x != nil { - return x.CharName + return x.Team } - return "" + return nil } -func (x *CharacterOnlineByNameResponse_Char) GetCharRace() uint32 { - if x != nil { - return x.CharRace - } - return 0 +type DeclineArenaTeamInviteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` } -func (x *CharacterOnlineByNameResponse_Char) GetCharClass() uint32 { - if x != nil { - return x.CharClass +func (x *DeclineArenaTeamInviteRequest) Reset() { + *x = DeclineArenaTeamInviteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return 0 } -func (x *CharacterOnlineByNameResponse_Char) GetCharGender() uint32 { - if x != nil { - return x.CharGender - } - return 0 +func (x *DeclineArenaTeamInviteRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (x *CharacterOnlineByNameResponse_Char) GetCharLvl() uint32 { - if x != nil { - return x.CharLvl +func (*DeclineArenaTeamInviteRequest) ProtoMessage() {} + +func (x *DeclineArenaTeamInviteRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[36] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return 0 + return mi.MessageOf(x) } -func (x *CharacterOnlineByNameResponse_Char) GetCharZone() uint32 { - if x != nil { - return x.CharZone - } - return 0 +// Deprecated: Use DeclineArenaTeamInviteRequest.ProtoReflect.Descriptor instead. +func (*DeclineArenaTeamInviteRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{36} } -func (x *CharacterOnlineByNameResponse_Char) GetCharMap() uint32 { +func (x *DeclineArenaTeamInviteRequest) GetApi() string { if x != nil { - return x.CharMap + return x.Api } - return 0 + return "" } -func (x *CharacterOnlineByNameResponse_Char) GetCharGuildID() uint64 { +func (x *DeclineArenaTeamInviteRequest) GetRealmID() uint32 { if x != nil { - return x.CharGuildID + return x.RealmID } return 0 } -func (x *CharacterOnlineByNameResponse_Char) GetAccountID() uint32 { +func (x *DeclineArenaTeamInviteRequest) GetPlayerGUID() uint64 { if x != nil { - return x.AccountID + return x.PlayerGUID } return 0 } -type CharacterByNameResponse_Char struct { +type AddArenaTeamMemberRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RealmID uint32 `protobuf:"varint,1,opt,name=realmID,proto3" json:"realmID,omitempty"` - IsOnline bool `protobuf:"varint,2,opt,name=isOnline,proto3" json:"isOnline,omitempty"` - GatewayID string `protobuf:"bytes,3,opt,name=gatewayID,proto3" json:"gatewayID,omitempty"` - CharGUID uint64 `protobuf:"varint,4,opt,name=charGUID,proto3" json:"charGUID,omitempty"` - CharName string `protobuf:"bytes,5,opt,name=charName,proto3" json:"charName,omitempty"` - CharRace uint32 `protobuf:"varint,6,opt,name=charRace,proto3" json:"charRace,omitempty"` - CharClass uint32 `protobuf:"varint,7,opt,name=charClass,proto3" json:"charClass,omitempty"` - CharGender uint32 `protobuf:"varint,8,opt,name=charGender,proto3" json:"charGender,omitempty"` - CharLvl uint32 `protobuf:"varint,9,opt,name=charLvl,proto3" json:"charLvl,omitempty"` - CharZone uint32 `protobuf:"varint,10,opt,name=charZone,proto3" json:"charZone,omitempty"` - CharMap uint32 `protobuf:"varint,11,opt,name=charMap,proto3" json:"charMap,omitempty"` - CharGuildID uint64 `protobuf:"varint,12,opt,name=charGuildID,proto3" json:"charGuildID,omitempty"` - AccountID uint32 `protobuf:"varint,13,opt,name=accountID,proto3" json:"accountID,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + ArenaTeamID uint32 `protobuf:"varint,3,opt,name=arenaTeamID,proto3" json:"arenaTeamID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,4,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + PersonalRating uint32 `protobuf:"varint,5,opt,name=personalRating,proto3" json:"personalRating,omitempty"` } -func (x *CharacterByNameResponse_Char) Reset() { - *x = CharacterByNameResponse_Char{} +func (x *AddArenaTeamMemberRequest) Reset() { + *x = AddArenaTeamMemberRequest{} if protoimpl.UnsafeEnabled { mi := &file_characters_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2751,13 +3264,13 @@ func (x *CharacterByNameResponse_Char) Reset() { } } -func (x *CharacterByNameResponse_Char) String() string { +func (x *AddArenaTeamMemberRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CharacterByNameResponse_Char) ProtoMessage() {} +func (*AddArenaTeamMemberRequest) ProtoMessage() {} -func (x *CharacterByNameResponse_Char) ProtoReflect() protoreflect.Message { +func (x *AddArenaTeamMemberRequest) ProtoReflect() protoreflect.Message { mi := &file_characters_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2769,139 +3282,153 @@ func (x *CharacterByNameResponse_Char) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CharacterByNameResponse_Char.ProtoReflect.Descriptor instead. -func (*CharacterByNameResponse_Char) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{14, 0} +// Deprecated: Use AddArenaTeamMemberRequest.ProtoReflect.Descriptor instead. +func (*AddArenaTeamMemberRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{37} } -func (x *CharacterByNameResponse_Char) GetRealmID() uint32 { +func (x *AddArenaTeamMemberRequest) GetApi() string { if x != nil { - return x.RealmID + return x.Api } - return 0 + return "" } -func (x *CharacterByNameResponse_Char) GetIsOnline() bool { +func (x *AddArenaTeamMemberRequest) GetRealmID() uint32 { if x != nil { - return x.IsOnline + return x.RealmID } - return false + return 0 } -func (x *CharacterByNameResponse_Char) GetGatewayID() string { +func (x *AddArenaTeamMemberRequest) GetArenaTeamID() uint32 { if x != nil { - return x.GatewayID + return x.ArenaTeamID } - return "" + return 0 } -func (x *CharacterByNameResponse_Char) GetCharGUID() uint64 { +func (x *AddArenaTeamMemberRequest) GetPlayerGUID() uint64 { if x != nil { - return x.CharGUID + return x.PlayerGUID } return 0 } -func (x *CharacterByNameResponse_Char) GetCharName() string { +func (x *AddArenaTeamMemberRequest) GetPersonalRating() uint32 { if x != nil { - return x.CharName + return x.PersonalRating } - return "" + return 0 } -func (x *CharacterByNameResponse_Char) GetCharRace() uint32 { - if x != nil { - return x.CharRace - } - return 0 +type RemoveArenaTeamMemberRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + ArenaTeamID uint32 `protobuf:"varint,3,opt,name=arenaTeamID,proto3" json:"arenaTeamID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,4,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + ActorGUID uint64 `protobuf:"varint,5,opt,name=actorGUID,proto3" json:"actorGUID,omitempty"` } -func (x *CharacterByNameResponse_Char) GetCharClass() uint32 { - if x != nil { - return x.CharClass +func (x *RemoveArenaTeamMemberRequest) Reset() { + *x = RemoveArenaTeamMemberRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return 0 } -func (x *CharacterByNameResponse_Char) GetCharGender() uint32 { - if x != nil { - return x.CharGender - } - return 0 +func (x *RemoveArenaTeamMemberRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (x *CharacterByNameResponse_Char) GetCharLvl() uint32 { +func (*RemoveArenaTeamMemberRequest) ProtoMessage() {} + +func (x *RemoveArenaTeamMemberRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveArenaTeamMemberRequest.ProtoReflect.Descriptor instead. +func (*RemoveArenaTeamMemberRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{38} +} + +func (x *RemoveArenaTeamMemberRequest) GetApi() string { if x != nil { - return x.CharLvl + return x.Api } - return 0 + return "" } -func (x *CharacterByNameResponse_Char) GetCharZone() uint32 { +func (x *RemoveArenaTeamMemberRequest) GetRealmID() uint32 { if x != nil { - return x.CharZone + return x.RealmID } return 0 } -func (x *CharacterByNameResponse_Char) GetCharMap() uint32 { +func (x *RemoveArenaTeamMemberRequest) GetArenaTeamID() uint32 { if x != nil { - return x.CharMap + return x.ArenaTeamID } return 0 } -func (x *CharacterByNameResponse_Char) GetCharGuildID() uint64 { +func (x *RemoveArenaTeamMemberRequest) GetPlayerGUID() uint64 { if x != nil { - return x.CharGuildID + return x.PlayerGUID } return 0 } -func (x *CharacterByNameResponse_Char) GetAccountID() uint32 { +func (x *RemoveArenaTeamMemberRequest) GetActorGUID() uint64 { if x != nil { - return x.AccountID + return x.ActorGUID } return 0 } -type ShortCharactersDataByGUIDsResponse_ShortCharData struct { +type DisbandArenaTeamRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RealmID uint32 `protobuf:"varint,1,opt,name=realmID,proto3" json:"realmID,omitempty"` - IsOnline bool `protobuf:"varint,2,opt,name=isOnline,proto3" json:"isOnline,omitempty"` - GatewayID string `protobuf:"bytes,3,opt,name=gatewayID,proto3" json:"gatewayID,omitempty"` - CharGUID uint64 `protobuf:"varint,4,opt,name=charGUID,proto3" json:"charGUID,omitempty"` - CharName string `protobuf:"bytes,5,opt,name=charName,proto3" json:"charName,omitempty"` - CharRace uint32 `protobuf:"varint,6,opt,name=charRace,proto3" json:"charRace,omitempty"` - CharClass uint32 `protobuf:"varint,7,opt,name=charClass,proto3" json:"charClass,omitempty"` - CharGender uint32 `protobuf:"varint,8,opt,name=charGender,proto3" json:"charGender,omitempty"` - CharLvl uint32 `protobuf:"varint,9,opt,name=charLvl,proto3" json:"charLvl,omitempty"` - CharZone uint32 `protobuf:"varint,10,opt,name=charZone,proto3" json:"charZone,omitempty"` - CharMap uint32 `protobuf:"varint,11,opt,name=charMap,proto3" json:"charMap,omitempty"` - CharGuildID uint64 `protobuf:"varint,12,opt,name=charGuildID,proto3" json:"charGuildID,omitempty"` - AccountID uint32 `protobuf:"varint,13,opt,name=accountID,proto3" json:"accountID,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + ArenaTeamID uint32 `protobuf:"varint,3,opt,name=arenaTeamID,proto3" json:"arenaTeamID,omitempty"` + ActorGUID uint64 `protobuf:"varint,4,opt,name=actorGUID,proto3" json:"actorGUID,omitempty"` } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) Reset() { - *x = ShortCharactersDataByGUIDsResponse_ShortCharData{} +func (x *DisbandArenaTeamRequest) Reset() { + *x = DisbandArenaTeamRequest{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[38] + mi := &file_characters_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) String() string { +func (x *DisbandArenaTeamRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ShortCharactersDataByGUIDsResponse_ShortCharData) ProtoMessage() {} +func (*DisbandArenaTeamRequest) ProtoMessage() {} -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[38] +func (x *DisbandArenaTeamRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2912,132 +3439,230 @@ func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) ProtoReflect() protor return mi.MessageOf(x) } -// Deprecated: Use ShortCharactersDataByGUIDsResponse_ShortCharData.ProtoReflect.Descriptor instead. -func (*ShortCharactersDataByGUIDsResponse_ShortCharData) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{16, 0} +// Deprecated: Use DisbandArenaTeamRequest.ProtoReflect.Descriptor instead. +func (*DisbandArenaTeamRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{39} } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetRealmID() uint32 { +func (x *DisbandArenaTeamRequest) GetApi() string { if x != nil { - return x.RealmID + return x.Api } - return 0 + return "" } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetIsOnline() bool { +func (x *DisbandArenaTeamRequest) GetRealmID() uint32 { if x != nil { - return x.IsOnline + return x.RealmID } - return false + return 0 } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetGatewayID() string { +func (x *DisbandArenaTeamRequest) GetArenaTeamID() uint32 { if x != nil { - return x.GatewayID + return x.ArenaTeamID } - return "" + return 0 } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharGUID() uint64 { +func (x *DisbandArenaTeamRequest) GetActorGUID() uint64 { if x != nil { - return x.CharGUID + return x.ActorGUID } return 0 } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharName() string { +type SetArenaTeamCaptainRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + ArenaTeamID uint32 `protobuf:"varint,3,opt,name=arenaTeamID,proto3" json:"arenaTeamID,omitempty"` + CaptainGUID uint64 `protobuf:"varint,4,opt,name=captainGUID,proto3" json:"captainGUID,omitempty"` + ActorGUID uint64 `protobuf:"varint,5,opt,name=actorGUID,proto3" json:"actorGUID,omitempty"` +} + +func (x *SetArenaTeamCaptainRequest) Reset() { + *x = SetArenaTeamCaptainRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetArenaTeamCaptainRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetArenaTeamCaptainRequest) ProtoMessage() {} + +func (x *SetArenaTeamCaptainRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[40] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetArenaTeamCaptainRequest.ProtoReflect.Descriptor instead. +func (*SetArenaTeamCaptainRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{40} +} + +func (x *SetArenaTeamCaptainRequest) GetApi() string { if x != nil { - return x.CharName + return x.Api } return "" } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharRace() uint32 { +func (x *SetArenaTeamCaptainRequest) GetRealmID() uint32 { if x != nil { - return x.CharRace + return x.RealmID } return 0 } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharClass() uint32 { +func (x *SetArenaTeamCaptainRequest) GetArenaTeamID() uint32 { if x != nil { - return x.CharClass + return x.ArenaTeamID } return 0 } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharGender() uint32 { +func (x *SetArenaTeamCaptainRequest) GetCaptainGUID() uint64 { if x != nil { - return x.CharGender + return x.CaptainGUID } return 0 } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharLvl() uint32 { +func (x *SetArenaTeamCaptainRequest) GetActorGUID() uint64 { if x != nil { - return x.CharLvl + return x.ActorGUID } return 0 } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharZone() uint32 { +type SetArenaTeamNameRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + ArenaTeamID uint32 `protobuf:"varint,3,opt,name=arenaTeamID,proto3" json:"arenaTeamID,omitempty"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` + ActorGUID uint64 `protobuf:"varint,5,opt,name=actorGUID,proto3" json:"actorGUID,omitempty"` +} + +func (x *SetArenaTeamNameRequest) Reset() { + *x = SetArenaTeamNameRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetArenaTeamNameRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetArenaTeamNameRequest) ProtoMessage() {} + +func (x *SetArenaTeamNameRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[41] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetArenaTeamNameRequest.ProtoReflect.Descriptor instead. +func (*SetArenaTeamNameRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{41} +} + +func (x *SetArenaTeamNameRequest) GetApi() string { if x != nil { - return x.CharZone + return x.Api } - return 0 + return "" } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharMap() uint32 { +func (x *SetArenaTeamNameRequest) GetRealmID() uint32 { if x != nil { - return x.CharMap + return x.RealmID } return 0 } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharGuildID() uint64 { +func (x *SetArenaTeamNameRequest) GetArenaTeamID() uint32 { if x != nil { - return x.CharGuildID + return x.ArenaTeamID } return 0 } -func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetAccountID() uint32 { +func (x *SetArenaTeamNameRequest) GetName() string { if x != nil { - return x.AccountID + return x.Name + } + return "" +} + +func (x *SetArenaTeamNameRequest) GetActorGUID() uint64 { + if x != nil { + return x.ActorGUID } return 0 } -type GetFriendsListResponse_Friend struct { +type ArenaTeamStatsMember struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` - Note string `protobuf:"bytes,2,opt,name=note,proto3" json:"note,omitempty"` - Status uint32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"` // 0 = offline, 1 = online - Area uint32 `protobuf:"varint,4,opt,name=area,proto3" json:"area,omitempty"` // zone ID if online - Level uint32 `protobuf:"varint,5,opt,name=level,proto3" json:"level,omitempty"` - ClassID uint32 `protobuf:"varint,6,opt,name=classID,proto3" json:"classID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,1,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + PersonalRating uint32 `protobuf:"varint,2,opt,name=personalRating,proto3" json:"personalRating,omitempty"` + WeekGames uint32 `protobuf:"varint,3,opt,name=weekGames,proto3" json:"weekGames,omitempty"` + WeekWins uint32 `protobuf:"varint,4,opt,name=weekWins,proto3" json:"weekWins,omitempty"` + SeasonGames uint32 `protobuf:"varint,5,opt,name=seasonGames,proto3" json:"seasonGames,omitempty"` + SeasonWins uint32 `protobuf:"varint,6,opt,name=seasonWins,proto3" json:"seasonWins,omitempty"` + MatchmakerRating uint32 `protobuf:"varint,7,opt,name=matchmakerRating,proto3" json:"matchmakerRating,omitempty"` + MaxMMR uint32 `protobuf:"varint,8,opt,name=maxMMR,proto3" json:"maxMMR,omitempty"` + SaveArenaStats bool `protobuf:"varint,9,opt,name=saveArenaStats,proto3" json:"saveArenaStats,omitempty"` } -func (x *GetFriendsListResponse_Friend) Reset() { - *x = GetFriendsListResponse_Friend{} +func (x *ArenaTeamStatsMember) Reset() { + *x = ArenaTeamStatsMember{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[39] + mi := &file_characters_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetFriendsListResponse_Friend) String() string { +func (x *ArenaTeamStatsMember) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetFriendsListResponse_Friend) ProtoMessage() {} +func (*ArenaTeamStatsMember) ProtoMessage() {} -func (x *GetFriendsListResponse_Friend) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[39] +func (x *ArenaTeamStatsMember) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3048,78 +3673,109 @@ func (x *GetFriendsListResponse_Friend) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetFriendsListResponse_Friend.ProtoReflect.Descriptor instead. -func (*GetFriendsListResponse_Friend) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{20, 0} +// Deprecated: Use ArenaTeamStatsMember.ProtoReflect.Descriptor instead. +func (*ArenaTeamStatsMember) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{42} } -func (x *GetFriendsListResponse_Friend) GetGuid() uint64 { +func (x *ArenaTeamStatsMember) GetPlayerGUID() uint64 { if x != nil { - return x.Guid + return x.PlayerGUID } return 0 } -func (x *GetFriendsListResponse_Friend) GetNote() string { +func (x *ArenaTeamStatsMember) GetPersonalRating() uint32 { if x != nil { - return x.Note + return x.PersonalRating } - return "" + return 0 } -func (x *GetFriendsListResponse_Friend) GetStatus() uint32 { +func (x *ArenaTeamStatsMember) GetWeekGames() uint32 { if x != nil { - return x.Status + return x.WeekGames } return 0 } -func (x *GetFriendsListResponse_Friend) GetArea() uint32 { +func (x *ArenaTeamStatsMember) GetWeekWins() uint32 { if x != nil { - return x.Area + return x.WeekWins } return 0 } -func (x *GetFriendsListResponse_Friend) GetLevel() uint32 { +func (x *ArenaTeamStatsMember) GetSeasonGames() uint32 { if x != nil { - return x.Level + return x.SeasonGames } return 0 } -func (x *GetFriendsListResponse_Friend) GetClassID() uint32 { +func (x *ArenaTeamStatsMember) GetSeasonWins() uint32 { if x != nil { - return x.ClassID + return x.SeasonWins } return 0 } -type GetFriendsListResponse_IgnoredPlayer struct { +func (x *ArenaTeamStatsMember) GetMatchmakerRating() uint32 { + if x != nil { + return x.MatchmakerRating + } + return 0 +} + +func (x *ArenaTeamStatsMember) GetMaxMMR() uint32 { + if x != nil { + return x.MaxMMR + } + return 0 +} + +func (x *ArenaTeamStatsMember) GetSaveArenaStats() bool { + if x != nil { + return x.SaveArenaStats + } + return false +} + +type SaveArenaTeamStatsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` -} - -func (x *GetFriendsListResponse_IgnoredPlayer) Reset() { - *x = GetFriendsListResponse_IgnoredPlayer{} + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + ArenaTeamID uint32 `protobuf:"varint,3,opt,name=arenaTeamID,proto3" json:"arenaTeamID,omitempty"` + Rating uint32 `protobuf:"varint,4,opt,name=rating,proto3" json:"rating,omitempty"` + WeekGames uint32 `protobuf:"varint,5,opt,name=weekGames,proto3" json:"weekGames,omitempty"` + WeekWins uint32 `protobuf:"varint,6,opt,name=weekWins,proto3" json:"weekWins,omitempty"` + SeasonGames uint32 `protobuf:"varint,7,opt,name=seasonGames,proto3" json:"seasonGames,omitempty"` + SeasonWins uint32 `protobuf:"varint,8,opt,name=seasonWins,proto3" json:"seasonWins,omitempty"` + Rank uint32 `protobuf:"varint,9,opt,name=rank,proto3" json:"rank,omitempty"` + Slot uint32 `protobuf:"varint,10,opt,name=slot,proto3" json:"slot,omitempty"` + Members []*ArenaTeamStatsMember `protobuf:"bytes,11,rep,name=members,proto3" json:"members,omitempty"` +} + +func (x *SaveArenaTeamStatsRequest) Reset() { + *x = SaveArenaTeamStatsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_characters_proto_msgTypes[40] + mi := &file_characters_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetFriendsListResponse_IgnoredPlayer) String() string { +func (x *SaveArenaTeamStatsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetFriendsListResponse_IgnoredPlayer) ProtoMessage() {} +func (*SaveArenaTeamStatsRequest) ProtoMessage() {} -func (x *GetFriendsListResponse_IgnoredPlayer) ProtoReflect() protoreflect.Message { - mi := &file_characters_proto_msgTypes[40] +func (x *SaveArenaTeamStatsRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3130,612 +3786,5132 @@ func (x *GetFriendsListResponse_IgnoredPlayer) ProtoReflect() protoreflect.Messa return mi.MessageOf(x) } -// Deprecated: Use GetFriendsListResponse_IgnoredPlayer.ProtoReflect.Descriptor instead. -func (*GetFriendsListResponse_IgnoredPlayer) Descriptor() ([]byte, []int) { - return file_characters_proto_rawDescGZIP(), []int{20, 1} +// Deprecated: Use SaveArenaTeamStatsRequest.ProtoReflect.Descriptor instead. +func (*SaveArenaTeamStatsRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{43} } -func (x *GetFriendsListResponse_IgnoredPlayer) GetGuid() uint64 { +func (x *SaveArenaTeamStatsRequest) GetApi() string { if x != nil { - return x.Guid + return x.Api + } + return "" +} + +func (x *SaveArenaTeamStatsRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID } return 0 } -var File_characters_proto protoreflect.FileDescriptor +func (x *SaveArenaTeamStatsRequest) GetArenaTeamID() uint32 { + if x != nil { + return x.ArenaTeamID + } + return 0 +} -var file_characters_proto_rawDesc = []byte{ - 0x0a, 0x10, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, 0x22, 0xb0, 0x05, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x49, 0x6e, - 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x47, 0x55, 0x49, - 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x47, 0x55, 0x49, 0x44, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x04, 0x72, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x67, - 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x67, 0x65, 0x6e, - 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6b, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x04, 0x73, 0x6b, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x61, 0x63, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x66, 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x68, - 0x61, 0x69, 0x72, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, - 0x68, 0x61, 0x69, 0x72, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x68, 0x61, 0x69, - 0x72, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x68, 0x61, - 0x69, 0x72, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x61, 0x63, 0x69, 0x61, - 0x6c, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x66, 0x61, - 0x63, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, - 0x65, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, - 0x12, 0x0a, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x7a, - 0x6f, 0x6e, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x70, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x03, 0x6d, 0x61, 0x70, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x58, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x58, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x59, - 0x18, 0x0f, 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x59, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5a, 0x18, 0x10, - 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5a, 0x12, - 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x61, - 0x74, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x61, 0x74, - 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x44, 0x18, - 0x15, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x70, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x49, - 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x65, 0x74, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x16, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x65, 0x74, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x16, 0x0a, - 0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x62, - 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x0a, 0x65, 0x71, 0x75, 0x69, 0x70, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x18, 0x18, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x45, - 0x71, 0x75, 0x69, 0x70, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, - 0x0a, 0x65, 0x71, 0x75, 0x69, 0x70, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x22, 0x84, 0x01, 0x0a, 0x10, 0x45, 0x71, - 0x75, 0x69, 0x70, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x24, - 0x0a, 0x0d, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x49, 0x44, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x49, 0x6e, - 0x66, 0x6f, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, - 0x79, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x49, 0x6e, 0x76, - 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x45, 0x6e, - 0x63, 0x68, 0x61, 0x6e, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0d, 0x45, 0x6e, 0x63, 0x68, 0x61, 0x6e, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, - 0x22, 0x6e, 0x0a, 0x22, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, - 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, - 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, - 0x22, 0x6b, 0x0a, 0x23, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, - 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, - 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x49, 0x6e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, - 0x72, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x22, 0x72, 0x0a, - 0x1e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x4c, 0x6f, 0x67, - 0x69, 0x6e, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, - 0x69, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x47, 0x55, - 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, - 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, - 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, - 0x44, 0x22, 0x65, 0x0a, 0x1f, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, - 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x30, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, - 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x6f, 0x67, 0x49, 0x6e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x52, 0x09, 0x63, - 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x22, 0x68, 0x0a, 0x1c, 0x41, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, - 0x6d, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, - 0x49, 0x44, 0x22, 0x49, 0x0a, 0x0b, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, - 0x61, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x64, 0x0a, - 0x1d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x41, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, - 0x12, 0x31, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x22, 0xbb, 0x02, 0x0a, 0x0f, 0x57, 0x68, 0x6f, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x68, 0x61, - 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, - 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x76, 0x6c, - 0x4d, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6c, 0x76, 0x6c, 0x4d, 0x69, - 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x76, 0x6c, 0x4d, 0x61, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x06, 0x6c, 0x76, 0x6c, 0x4d, 0x61, 0x78, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x75, 0x69, - 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x75, - 0x69, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x61, 0x63, 0x65, 0x4d, - 0x61, 0x73, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x72, 0x61, 0x63, 0x65, 0x4d, - 0x61, 0x73, 0x6b, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4d, 0x61, 0x73, 0x6b, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4d, 0x61, 0x73, - 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0d, - 0x52, 0x05, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x73, 0x22, 0xc0, 0x02, 0x0a, 0x10, 0x57, 0x68, 0x6f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x44, 0x0a, 0x0e, 0x69, 0x74, 0x65, 0x6d, - 0x73, 0x54, 0x6f, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x68, 0x6f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x57, 0x68, 0x6f, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x0e, - 0x69, 0x74, 0x65, 0x6d, 0x73, 0x54, 0x6f, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x1a, 0xb3, - 0x01, 0x0a, 0x07, 0x57, 0x68, 0x6f, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x75, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, 0x75, 0x69, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6c, 0x76, 0x6c, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6c, 0x76, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, - 0x61, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, - 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, - 0x72, 0x61, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, - 0x7a, 0x6f, 0x6e, 0x65, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x7a, 0x6f, - 0x6e, 0x65, 0x49, 0x44, 0x22, 0x70, 0x0a, 0x1c, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, - 0x72, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, - 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, - 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, - 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xda, 0x03, 0x0a, 0x1d, 0x43, 0x68, 0x61, 0x72, 0x61, - 0x63, 0x74, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x44, 0x0a, 0x09, 0x63, 0x68, - 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x2e, 0x43, 0x68, 0x61, 0x72, 0x52, 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, - 0x1a, 0xe0, 0x02, 0x0a, 0x04, 0x43, 0x68, 0x61, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, - 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, - 0x6d, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x44, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, - 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1a, 0x0a, - 0x08, 0x63, 0x68, 0x61, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x63, 0x68, 0x61, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, - 0x72, 0x52, 0x61, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x68, 0x61, - 0x72, 0x52, 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x43, 0x6c, 0x61, - 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x68, 0x61, 0x72, 0x43, 0x6c, - 0x61, 0x73, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, - 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x72, 0x47, 0x65, 0x6e, - 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x72, 0x4c, 0x76, 0x6c, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, 0x61, 0x72, 0x4c, 0x76, 0x6c, 0x12, 0x1a, 0x0a, - 0x08, 0x63, 0x68, 0x61, 0x72, 0x5a, 0x6f, 0x6e, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x08, 0x63, 0x68, 0x61, 0x72, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, - 0x72, 0x4d, 0x61, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, 0x61, 0x72, - 0x4d, 0x61, 0x70, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x72, 0x47, 0x75, 0x69, 0x6c, 0x64, - 0x49, 0x44, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x72, 0x47, 0x75, - 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x49, 0x44, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x49, 0x44, 0x22, 0x6a, 0x0a, 0x16, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, - 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, - 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, - 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x68, 0x61, - 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, - 0xea, 0x03, 0x0a, 0x17, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x42, 0x79, 0x4e, - 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3e, 0x0a, - 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x42, - 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x68, - 0x61, 0x72, 0x52, 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x1a, 0xfc, 0x02, - 0x0a, 0x04, 0x43, 0x68, 0x61, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, - 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, - 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1c, 0x0a, 0x09, - 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, - 0x61, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x63, 0x68, - 0x61, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x4e, 0x61, - 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x52, 0x61, 0x63, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x52, 0x61, 0x63, 0x65, 0x12, 0x1c, - 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x09, 0x63, 0x68, 0x61, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x1e, 0x0a, 0x0a, - 0x63, 0x68, 0x61, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0a, 0x63, 0x68, 0x61, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, - 0x63, 0x68, 0x61, 0x72, 0x4c, 0x76, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, - 0x68, 0x61, 0x72, 0x4c, 0x76, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x5a, 0x6f, - 0x6e, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x5a, 0x6f, - 0x6e, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x72, 0x4d, 0x61, 0x70, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, 0x61, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x20, 0x0a, 0x0b, - 0x63, 0x68, 0x61, 0x72, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x0c, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x72, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x1c, - 0x0a, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x0d, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x22, 0x65, 0x0a, 0x21, - 0x53, 0x68, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x44, - 0x61, 0x74, 0x61, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x14, 0x0a, - 0x05, 0x47, 0x55, 0x49, 0x44, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x52, 0x05, 0x47, 0x55, - 0x49, 0x44, 0x73, 0x22, 0x94, 0x04, 0x0a, 0x22, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, - 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x44, 0x61, 0x74, 0x61, 0x42, 0x79, 0x47, 0x55, 0x49, - 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, - 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x54, 0x0a, 0x0a, - 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x34, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x72, 0x61, - 0x63, 0x74, 0x65, 0x72, 0x73, 0x44, 0x61, 0x74, 0x61, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x43, 0x68, - 0x61, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, - 0x72, 0x73, 0x1a, 0x85, 0x03, 0x0a, 0x0d, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x72, - 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1a, - 0x0a, 0x08, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x08, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, - 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, - 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, - 0x47, 0x55, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x4e, 0x61, 0x6d, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x52, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x52, 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, - 0x63, 0x68, 0x61, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x09, 0x63, 0x68, 0x61, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x68, - 0x61, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, - 0x63, 0x68, 0x61, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, - 0x61, 0x72, 0x4c, 0x76, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, 0x61, - 0x72, 0x4c, 0x76, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x5a, 0x6f, 0x6e, 0x65, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x5a, 0x6f, 0x6e, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x72, 0x4d, 0x61, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x07, 0x63, 0x68, 0x61, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, - 0x61, 0x72, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0b, 0x63, 0x68, 0x61, 0x72, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x22, 0xb1, 0x01, 0x0a, 0x19, 0x53, - 0x61, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, - 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, - 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x47, 0x55, 0x49, 0x44, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x47, 0x55, 0x49, 0x44, - 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x02, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, - 0x01, 0x79, 0x12, 0x0c, 0x0a, 0x01, 0x7a, 0x18, 0x07, 0x20, 0x01, 0x28, 0x02, 0x52, 0x01, 0x7a, - 0x12, 0x0c, 0x0a, 0x01, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x02, 0x52, 0x01, 0x6f, 0x22, 0x2e, - 0x0a, 0x1a, 0x53, 0x61, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, - 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x63, - 0x0a, 0x15, 0x47, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, - 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, - 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, - 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, - 0x55, 0x49, 0x44, 0x22, 0xdf, 0x02, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, - 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, - 0x12, 0x3b, 0x0a, 0x07, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, - 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x72, - 0x69, 0x65, 0x6e, 0x64, 0x52, 0x07, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x12, 0x42, 0x0a, - 0x07, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x4c, 0x69, - 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x67, 0x6e, 0x6f, 0x72, - 0x65, 0x64, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x07, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, - 0x64, 0x1a, 0x8c, 0x01, 0x0a, 0x06, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, - 0x67, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, 0x75, 0x69, 0x64, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x6f, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, - 0x61, 0x72, 0x65, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x61, 0x72, 0x65, 0x61, - 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, - 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, - 0x1a, 0x23, 0x0a, 0x0d, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x50, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x04, 0x67, 0x75, 0x69, 0x64, 0x22, 0xb2, 0x01, 0x0a, 0x10, 0x41, 0x64, 0x64, 0x46, 0x72, 0x69, - 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, - 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, - 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, - 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, - 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x66, 0x72, 0x69, 0x65, - 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, - 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x72, 0x69, 0x65, - 0x6e, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x22, 0x99, 0x01, 0x0a, 0x11, 0x41, - 0x64, 0x64, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x65, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x04, 0x61, 0x72, 0x65, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, - 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, - 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, 0x22, 0x81, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, - 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, - 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x72, - 0x69, 0x65, 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, - 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x22, 0x28, 0x0a, 0x14, 0x52, 0x65, - 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x61, 0x70, 0x69, 0x22, 0x96, 0x01, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, - 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, - 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, - 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x72, 0x69, - 0x65, 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x66, - 0x72, 0x69, 0x65, 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x22, 0x29, 0x0a, - 0x15, 0x53, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x80, 0x01, 0x0a, 0x10, 0x41, 0x64, 0x64, - 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, - 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, - 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x67, 0x6e, - 0x6f, 0x72, 0x65, 0x64, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, - 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x47, 0x55, 0x49, 0x44, 0x22, 0x3d, 0x0a, 0x11, 0x41, - 0x64, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x83, 0x01, 0x0a, 0x13, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, - 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, - 0x0a, 0x0b, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x47, 0x55, 0x49, 0x44, - 0x22, 0x28, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0xc3, 0x01, 0x0a, 0x19, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, - 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, - 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, - 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x47, 0x55, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, - 0x61, 0x72, 0x65, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x61, 0x72, 0x65, 0x61, - 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, - 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, - 0x22, 0x2e, 0x0a, 0x1a, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, - 0x22, 0x48, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x68, 0x61, - 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, - 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, - 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x22, 0x77, 0x0a, 0x1b, 0x47, 0x65, - 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x26, 0x0a, 0x0e, 0x63, - 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x04, 0x52, 0x0e, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x47, 0x55, - 0x49, 0x44, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x32, 0xa4, 0x0a, 0x0a, 0x11, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, - 0x72, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6e, 0x0a, 0x1b, 0x43, 0x68, 0x61, - 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x46, 0x6f, - 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, - 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x46, - 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x27, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, - 0x54, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x17, 0x43, 0x68, 0x61, - 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x79, - 0x47, 0x55, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, - 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x79, 0x47, 0x55, 0x49, - 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, - 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, - 0x79, 0x47, 0x55, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, - 0x15, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x41, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x57, - 0x68, 0x6f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x13, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x68, 0x6f, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x76, - 0x31, 0x2e, 0x57, 0x68, 0x6f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4f, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4a, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x42, 0x79, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, - 0x65, 0x72, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x42, 0x79, - 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x71, 0x0a, 0x20, - 0x53, 0x68, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, - 0x63, 0x74, 0x65, 0x72, 0x73, 0x44, 0x61, 0x74, 0x61, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x73, - 0x12, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x72, 0x61, - 0x63, 0x74, 0x65, 0x72, 0x73, 0x44, 0x61, 0x74, 0x61, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x6f, - 0x72, 0x74, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x44, 0x61, 0x74, 0x61, - 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x53, 0x0a, 0x12, 0x53, 0x61, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x6f, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x50, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x50, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, - 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, - 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, - 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, - 0x09, 0x41, 0x64, 0x64, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x2e, 0x76, 0x31, 0x2e, - 0x41, 0x64, 0x64, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x15, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x72, 0x69, 0x65, - 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0d, 0x53, 0x65, - 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x65, 0x12, 0x18, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x46, 0x72, - 0x69, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x38, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x12, 0x14, 0x2e, - 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x67, 0x6e, 0x6f, - 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x52, 0x65, - 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, - 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, - 0x12, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x56, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, - 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x13, 0x5a, 0x11, 0x67, 0x65, - 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x2f, 0x70, 0x62, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +func (x *SaveArenaTeamStatsRequest) GetRating() uint32 { + if x != nil { + return x.Rating + } + return 0 } -var ( - file_characters_proto_rawDescOnce sync.Once - file_characters_proto_rawDescData = file_characters_proto_rawDesc -) +func (x *SaveArenaTeamStatsRequest) GetWeekGames() uint32 { + if x != nil { + return x.WeekGames + } + return 0 +} -func file_characters_proto_rawDescGZIP() []byte { - file_characters_proto_rawDescOnce.Do(func() { - file_characters_proto_rawDescData = protoimpl.X.CompressGZIP(file_characters_proto_rawDescData) - }) - return file_characters_proto_rawDescData +func (x *SaveArenaTeamStatsRequest) GetWeekWins() uint32 { + if x != nil { + return x.WeekWins + } + return 0 } -var file_characters_proto_msgTypes = make([]protoimpl.MessageInfo, 41) -var file_characters_proto_goTypes = []interface{}{ - (*LogInCharacter)(nil), // 0: v1.LogInCharacter - (*EquipmentDisplay)(nil), // 1: v1.EquipmentDisplay - (*CharactersToLoginForAccountRequest)(nil), // 2: v1.CharactersToLoginForAccountRequest - (*CharactersToLoginForAccountResponse)(nil), // 3: v1.CharactersToLoginForAccountResponse - (*CharactersToLoginByGUIDRequest)(nil), // 4: v1.CharactersToLoginByGUIDRequest - (*CharactersToLoginByGUIDResponse)(nil), // 5: v1.CharactersToLoginByGUIDResponse - (*AccountDataForAccountRequest)(nil), // 6: v1.AccountDataForAccountRequest - (*AccountData)(nil), // 7: v1.AccountData - (*AccountDataForAccountResponse)(nil), // 8: v1.AccountDataForAccountResponse - (*WhoQueryRequest)(nil), // 9: v1.WhoQueryRequest - (*WhoQueryResponse)(nil), // 10: v1.WhoQueryResponse - (*CharacterOnlineByNameRequest)(nil), // 11: v1.CharacterOnlineByNameRequest - (*CharacterOnlineByNameResponse)(nil), // 12: v1.CharacterOnlineByNameResponse - (*CharacterByNameRequest)(nil), // 13: v1.CharacterByNameRequest - (*CharacterByNameResponse)(nil), // 14: v1.CharacterByNameResponse - (*ShortCharactersDataByGUIDsRequest)(nil), // 15: v1.ShortCharactersDataByGUIDsRequest - (*ShortCharactersDataByGUIDsResponse)(nil), // 16: v1.ShortCharactersDataByGUIDsResponse - (*SavePlayerPositionRequest)(nil), // 17: v1.SavePlayerPositionRequest - (*SavePlayerPositionResponse)(nil), // 18: v1.SavePlayerPositionResponse - (*GetFriendsListRequest)(nil), // 19: v1.GetFriendsListRequest - (*GetFriendsListResponse)(nil), // 20: v1.GetFriendsListResponse - (*AddFriendRequest)(nil), // 21: v1.AddFriendRequest - (*AddFriendResponse)(nil), // 22: v1.AddFriendResponse - (*RemoveFriendRequest)(nil), // 23: v1.RemoveFriendRequest - (*RemoveFriendResponse)(nil), // 24: v1.RemoveFriendResponse - (*SetFriendNoteRequest)(nil), // 25: v1.SetFriendNoteRequest - (*SetFriendNoteResponse)(nil), // 26: v1.SetFriendNoteResponse - (*AddIgnoreRequest)(nil), // 27: v1.AddIgnoreRequest - (*AddIgnoreResponse)(nil), // 28: v1.AddIgnoreResponse - (*RemoveIgnoreRequest)(nil), // 29: v1.RemoveIgnoreRequest - (*RemoveIgnoreResponse)(nil), // 30: v1.RemoveIgnoreResponse - (*NotifyStatusChangeRequest)(nil), // 31: v1.NotifyStatusChangeRequest - (*NotifyStatusChangeResponse)(nil), // 32: v1.NotifyStatusChangeResponse - (*GetOnlineCharactersRequest)(nil), // 33: v1.GetOnlineCharactersRequest - (*GetOnlineCharactersResponse)(nil), // 34: v1.GetOnlineCharactersResponse - (*WhoQueryResponse_WhoItem)(nil), // 35: v1.WhoQueryResponse.WhoItem - (*CharacterOnlineByNameResponse_Char)(nil), // 36: v1.CharacterOnlineByNameResponse.Char - (*CharacterByNameResponse_Char)(nil), // 37: v1.CharacterByNameResponse.Char - (*ShortCharactersDataByGUIDsResponse_ShortCharData)(nil), // 38: v1.ShortCharactersDataByGUIDsResponse.ShortCharData - (*GetFriendsListResponse_Friend)(nil), // 39: v1.GetFriendsListResponse.Friend - (*GetFriendsListResponse_IgnoredPlayer)(nil), // 40: v1.GetFriendsListResponse.IgnoredPlayer +func (x *SaveArenaTeamStatsRequest) GetSeasonGames() uint32 { + if x != nil { + return x.SeasonGames + } + return 0 } -var file_characters_proto_depIdxs = []int32{ - 1, // 0: v1.LogInCharacter.equipments:type_name -> v1.EquipmentDisplay - 0, // 1: v1.CharactersToLoginForAccountResponse.characters:type_name -> v1.LogInCharacter - 0, // 2: v1.CharactersToLoginByGUIDResponse.character:type_name -> v1.LogInCharacter - 7, // 3: v1.AccountDataForAccountResponse.accountData:type_name -> v1.AccountData - 35, // 4: v1.WhoQueryResponse.itemsToDisplay:type_name -> v1.WhoQueryResponse.WhoItem - 36, // 5: v1.CharacterOnlineByNameResponse.character:type_name -> v1.CharacterOnlineByNameResponse.Char - 37, // 6: v1.CharacterByNameResponse.character:type_name -> v1.CharacterByNameResponse.Char - 38, // 7: v1.ShortCharactersDataByGUIDsResponse.characters:type_name -> v1.ShortCharactersDataByGUIDsResponse.ShortCharData - 39, // 8: v1.GetFriendsListResponse.friends:type_name -> v1.GetFriendsListResponse.Friend - 40, // 9: v1.GetFriendsListResponse.ignored:type_name -> v1.GetFriendsListResponse.IgnoredPlayer - 2, // 10: v1.CharactersService.CharactersToLoginForAccount:input_type -> v1.CharactersToLoginForAccountRequest - 4, // 11: v1.CharactersService.CharactersToLoginByGUID:input_type -> v1.CharactersToLoginByGUIDRequest - 6, // 12: v1.CharactersService.AccountDataForAccount:input_type -> v1.AccountDataForAccountRequest - 9, // 13: v1.CharactersService.WhoQuery:input_type -> v1.WhoQueryRequest - 11, // 14: v1.CharactersService.CharacterOnlineByName:input_type -> v1.CharacterOnlineByNameRequest - 13, // 15: v1.CharactersService.CharacterByName:input_type -> v1.CharacterByNameRequest - 15, // 16: v1.CharactersService.ShortOnlineCharactersDataByGUIDs:input_type -> v1.ShortCharactersDataByGUIDsRequest - 17, // 17: v1.CharactersService.SavePlayerPosition:input_type -> v1.SavePlayerPositionRequest - 19, // 18: v1.CharactersService.GetFriendsList:input_type -> v1.GetFriendsListRequest - 21, // 19: v1.CharactersService.AddFriend:input_type -> v1.AddFriendRequest - 23, // 20: v1.CharactersService.RemoveFriend:input_type -> v1.RemoveFriendRequest - 25, // 21: v1.CharactersService.SetFriendNote:input_type -> v1.SetFriendNoteRequest - 27, // 22: v1.CharactersService.AddIgnore:input_type -> v1.AddIgnoreRequest - 29, // 23: v1.CharactersService.RemoveIgnore:input_type -> v1.RemoveIgnoreRequest - 31, // 24: v1.CharactersService.NotifyStatusChange:input_type -> v1.NotifyStatusChangeRequest - 33, // 25: v1.CharactersService.GetOnlineCharacters:input_type -> v1.GetOnlineCharactersRequest - 3, // 26: v1.CharactersService.CharactersToLoginForAccount:output_type -> v1.CharactersToLoginForAccountResponse - 5, // 27: v1.CharactersService.CharactersToLoginByGUID:output_type -> v1.CharactersToLoginByGUIDResponse - 8, // 28: v1.CharactersService.AccountDataForAccount:output_type -> v1.AccountDataForAccountResponse - 10, // 29: v1.CharactersService.WhoQuery:output_type -> v1.WhoQueryResponse - 12, // 30: v1.CharactersService.CharacterOnlineByName:output_type -> v1.CharacterOnlineByNameResponse - 14, // 31: v1.CharactersService.CharacterByName:output_type -> v1.CharacterByNameResponse - 16, // 32: v1.CharactersService.ShortOnlineCharactersDataByGUIDs:output_type -> v1.ShortCharactersDataByGUIDsResponse - 18, // 33: v1.CharactersService.SavePlayerPosition:output_type -> v1.SavePlayerPositionResponse - 20, // 34: v1.CharactersService.GetFriendsList:output_type -> v1.GetFriendsListResponse - 22, // 35: v1.CharactersService.AddFriend:output_type -> v1.AddFriendResponse - 24, // 36: v1.CharactersService.RemoveFriend:output_type -> v1.RemoveFriendResponse - 26, // 37: v1.CharactersService.SetFriendNote:output_type -> v1.SetFriendNoteResponse - 28, // 38: v1.CharactersService.AddIgnore:output_type -> v1.AddIgnoreResponse - 30, // 39: v1.CharactersService.RemoveIgnore:output_type -> v1.RemoveIgnoreResponse - 32, // 40: v1.CharactersService.NotifyStatusChange:output_type -> v1.NotifyStatusChangeResponse - 34, // 41: v1.CharactersService.GetOnlineCharacters:output_type -> v1.GetOnlineCharactersResponse - 26, // [26:42] is the sub-list for method output_type - 10, // [10:26] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name + +func (x *SaveArenaTeamStatsRequest) GetSeasonWins() uint32 { + if x != nil { + return x.SeasonWins + } + return 0 } -func init() { file_characters_proto_init() } -func file_characters_proto_init() { - if File_characters_proto != nil { - return +func (x *SaveArenaTeamStatsRequest) GetRank() uint32 { + if x != nil { + return x.Rank } - if !protoimpl.UnsafeEnabled { - file_characters_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LogInCharacter); i { + return 0 +} + +func (x *SaveArenaTeamStatsRequest) GetSlot() uint32 { + if x != nil { + return x.Slot + } + return 0 +} + +func (x *SaveArenaTeamStatsRequest) GetMembers() []*ArenaTeamStatsMember { + if x != nil { + return x.Members + } + return nil +} + +type DeleteAllArenaTeamsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` +} + +func (x *DeleteAllArenaTeamsRequest) Reset() { + *x = DeleteAllArenaTeamsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteAllArenaTeamsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteAllArenaTeamsRequest) ProtoMessage() {} + +func (x *DeleteAllArenaTeamsRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[44] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteAllArenaTeamsRequest.ProtoReflect.Descriptor instead. +func (*DeleteAllArenaTeamsRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{44} +} + +func (x *DeleteAllArenaTeamsRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *DeleteAllArenaTeamsRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +type ValidateArenaTeamCharacterDeleteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` +} + +func (x *ValidateArenaTeamCharacterDeleteRequest) Reset() { + *x = ValidateArenaTeamCharacterDeleteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateArenaTeamCharacterDeleteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateArenaTeamCharacterDeleteRequest) ProtoMessage() {} + +func (x *ValidateArenaTeamCharacterDeleteRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[45] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidateArenaTeamCharacterDeleteRequest.ProtoReflect.Descriptor instead. +func (*ValidateArenaTeamCharacterDeleteRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{45} +} + +func (x *ValidateArenaTeamCharacterDeleteRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *ValidateArenaTeamCharacterDeleteRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *ValidateArenaTeamCharacterDeleteRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +type RemovePlayerFromArenaTeamsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` +} + +func (x *RemovePlayerFromArenaTeamsRequest) Reset() { + *x = RemovePlayerFromArenaTeamsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemovePlayerFromArenaTeamsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemovePlayerFromArenaTeamsRequest) ProtoMessage() {} + +func (x *RemovePlayerFromArenaTeamsRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[46] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemovePlayerFromArenaTeamsRequest.ProtoReflect.Descriptor instead. +func (*RemovePlayerFromArenaTeamsRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{46} +} + +func (x *RemovePlayerFromArenaTeamsRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *RemovePlayerFromArenaTeamsRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *RemovePlayerFromArenaTeamsRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +type ArenaTeamMutationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status ArenaTeamMutationStatus `protobuf:"varint,2,opt,name=status,proto3,enum=v1.ArenaTeamMutationStatus" json:"status,omitempty"` +} + +func (x *ArenaTeamMutationResponse) Reset() { + *x = ArenaTeamMutationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ArenaTeamMutationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ArenaTeamMutationResponse) ProtoMessage() {} + +func (x *ArenaTeamMutationResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[47] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ArenaTeamMutationResponse.ProtoReflect.Descriptor instead. +func (*ArenaTeamMutationResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{47} +} + +func (x *ArenaTeamMutationResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *ArenaTeamMutationResponse) GetStatus() ArenaTeamMutationStatus { + if x != nil { + return x.Status + } + return ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK +} + +type SavePlayerPositionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + CharGUID uint64 `protobuf:"varint,3,opt,name=charGUID,proto3" json:"charGUID,omitempty"` + MapID uint32 `protobuf:"varint,4,opt,name=mapID,proto3" json:"mapID,omitempty"` + X float32 `protobuf:"fixed32,5,opt,name=x,proto3" json:"x,omitempty"` + Y float32 `protobuf:"fixed32,6,opt,name=y,proto3" json:"y,omitempty"` + Z float32 `protobuf:"fixed32,7,opt,name=z,proto3" json:"z,omitempty"` + O float32 `protobuf:"fixed32,8,opt,name=o,proto3" json:"o,omitempty"` +} + +func (x *SavePlayerPositionRequest) Reset() { + *x = SavePlayerPositionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SavePlayerPositionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SavePlayerPositionRequest) ProtoMessage() {} + +func (x *SavePlayerPositionRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[48] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SavePlayerPositionRequest.ProtoReflect.Descriptor instead. +func (*SavePlayerPositionRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{48} +} + +func (x *SavePlayerPositionRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SavePlayerPositionRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *SavePlayerPositionRequest) GetCharGUID() uint64 { + if x != nil { + return x.CharGUID + } + return 0 +} + +func (x *SavePlayerPositionRequest) GetMapID() uint32 { + if x != nil { + return x.MapID + } + return 0 +} + +func (x *SavePlayerPositionRequest) GetX() float32 { + if x != nil { + return x.X + } + return 0 +} + +func (x *SavePlayerPositionRequest) GetY() float32 { + if x != nil { + return x.Y + } + return 0 +} + +func (x *SavePlayerPositionRequest) GetZ() float32 { + if x != nil { + return x.Z + } + return 0 +} + +func (x *SavePlayerPositionRequest) GetO() float32 { + if x != nil { + return x.O + } + return 0 +} + +type SavePlayerPositionResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *SavePlayerPositionResponse) Reset() { + *x = SavePlayerPositionResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[49] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SavePlayerPositionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SavePlayerPositionResponse) ProtoMessage() {} + +func (x *SavePlayerPositionResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[49] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SavePlayerPositionResponse.ProtoReflect.Descriptor instead. +func (*SavePlayerPositionResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{49} +} + +func (x *SavePlayerPositionResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type LfgDungeonRoute struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RealmID uint32 `protobuf:"varint,1,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,2,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + DungeonEntry uint32 `protobuf:"varint,3,opt,name=dungeonEntry,proto3" json:"dungeonEntry,omitempty"` + MapID uint32 `protobuf:"varint,4,opt,name=mapID,proto3" json:"mapID,omitempty"` + Difficulty uint32 `protobuf:"varint,5,opt,name=difficulty,proto3" json:"difficulty,omitempty"` + OwnerRealmID uint32 `protobuf:"varint,6,opt,name=ownerRealmID,proto3" json:"ownerRealmID,omitempty"` + IsCrossRealm bool `protobuf:"varint,7,opt,name=isCrossRealm,proto3" json:"isCrossRealm,omitempty"` + RequiresBoundInstance bool `protobuf:"varint,8,opt,name=requiresBoundInstance,proto3" json:"requiresBoundInstance,omitempty"` + InstanceID uint32 `protobuf:"varint,9,opt,name=instanceID,proto3" json:"instanceID,omitempty"` + BoundInstanceID uint32 `protobuf:"varint,10,opt,name=boundInstanceID,proto3" json:"boundInstanceID,omitempty"` +} + +func (x *LfgDungeonRoute) Reset() { + *x = LfgDungeonRoute{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[50] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LfgDungeonRoute) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LfgDungeonRoute) ProtoMessage() {} + +func (x *LfgDungeonRoute) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[50] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LfgDungeonRoute.ProtoReflect.Descriptor instead. +func (*LfgDungeonRoute) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{50} +} + +func (x *LfgDungeonRoute) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *LfgDungeonRoute) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *LfgDungeonRoute) GetDungeonEntry() uint32 { + if x != nil { + return x.DungeonEntry + } + return 0 +} + +func (x *LfgDungeonRoute) GetMapID() uint32 { + if x != nil { + return x.MapID + } + return 0 +} + +func (x *LfgDungeonRoute) GetDifficulty() uint32 { + if x != nil { + return x.Difficulty + } + return 0 +} + +func (x *LfgDungeonRoute) GetOwnerRealmID() uint32 { + if x != nil { + return x.OwnerRealmID + } + return 0 +} + +func (x *LfgDungeonRoute) GetIsCrossRealm() bool { + if x != nil { + return x.IsCrossRealm + } + return false +} + +func (x *LfgDungeonRoute) GetRequiresBoundInstance() bool { + if x != nil { + return x.RequiresBoundInstance + } + return false +} + +func (x *LfgDungeonRoute) GetInstanceID() uint32 { + if x != nil { + return x.InstanceID + } + return 0 +} + +func (x *LfgDungeonRoute) GetBoundInstanceID() uint32 { + if x != nil { + return x.BoundInstanceID + } + return 0 +} + +type RecordLfgDungeonRouteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Route *LfgDungeonRoute `protobuf:"bytes,2,opt,name=route,proto3" json:"route,omitempty"` +} + +func (x *RecordLfgDungeonRouteRequest) Reset() { + *x = RecordLfgDungeonRouteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[51] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RecordLfgDungeonRouteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RecordLfgDungeonRouteRequest) ProtoMessage() {} + +func (x *RecordLfgDungeonRouteRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[51] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RecordLfgDungeonRouteRequest.ProtoReflect.Descriptor instead. +func (*RecordLfgDungeonRouteRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{51} +} + +func (x *RecordLfgDungeonRouteRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *RecordLfgDungeonRouteRequest) GetRoute() *LfgDungeonRoute { + if x != nil { + return x.Route + } + return nil +} + +type RecordLfgDungeonRouteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *RecordLfgDungeonRouteResponse) Reset() { + *x = RecordLfgDungeonRouteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[52] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RecordLfgDungeonRouteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RecordLfgDungeonRouteResponse) ProtoMessage() {} + +func (x *RecordLfgDungeonRouteResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[52] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RecordLfgDungeonRouteResponse.ProtoReflect.Descriptor instead. +func (*RecordLfgDungeonRouteResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{52} +} + +func (x *RecordLfgDungeonRouteResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type ConfirmLfgDungeonRouteEnteredRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + MapID uint32 `protobuf:"varint,4,opt,name=mapID,proto3" json:"mapID,omitempty"` + Difficulty uint32 `protobuf:"varint,5,opt,name=difficulty,proto3" json:"difficulty,omitempty"` + InstanceID uint32 `protobuf:"varint,6,opt,name=instanceID,proto3" json:"instanceID,omitempty"` +} + +func (x *ConfirmLfgDungeonRouteEnteredRequest) Reset() { + *x = ConfirmLfgDungeonRouteEnteredRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[53] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConfirmLfgDungeonRouteEnteredRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfirmLfgDungeonRouteEnteredRequest) ProtoMessage() {} + +func (x *ConfirmLfgDungeonRouteEnteredRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[53] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConfirmLfgDungeonRouteEnteredRequest.ProtoReflect.Descriptor instead. +func (*ConfirmLfgDungeonRouteEnteredRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{53} +} + +func (x *ConfirmLfgDungeonRouteEnteredRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *ConfirmLfgDungeonRouteEnteredRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *ConfirmLfgDungeonRouteEnteredRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *ConfirmLfgDungeonRouteEnteredRequest) GetMapID() uint32 { + if x != nil { + return x.MapID + } + return 0 +} + +func (x *ConfirmLfgDungeonRouteEnteredRequest) GetDifficulty() uint32 { + if x != nil { + return x.Difficulty + } + return 0 +} + +func (x *ConfirmLfgDungeonRouteEnteredRequest) GetInstanceID() uint32 { + if x != nil { + return x.InstanceID + } + return 0 +} + +type ConfirmLfgDungeonRouteEnteredResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Route *LfgDungeonRoute `protobuf:"bytes,2,opt,name=route,proto3" json:"route,omitempty"` +} + +func (x *ConfirmLfgDungeonRouteEnteredResponse) Reset() { + *x = ConfirmLfgDungeonRouteEnteredResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[54] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConfirmLfgDungeonRouteEnteredResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfirmLfgDungeonRouteEnteredResponse) ProtoMessage() {} + +func (x *ConfirmLfgDungeonRouteEnteredResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[54] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConfirmLfgDungeonRouteEnteredResponse.ProtoReflect.Descriptor instead. +func (*ConfirmLfgDungeonRouteEnteredResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{54} +} + +func (x *ConfirmLfgDungeonRouteEnteredResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *ConfirmLfgDungeonRouteEnteredResponse) GetRoute() *LfgDungeonRoute { + if x != nil { + return x.Route + } + return nil +} + +type ClearUnboundLfgDungeonRouteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + MapID uint32 `protobuf:"varint,4,opt,name=mapID,proto3" json:"mapID,omitempty"` +} + +func (x *ClearUnboundLfgDungeonRouteRequest) Reset() { + *x = ClearUnboundLfgDungeonRouteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[55] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClearUnboundLfgDungeonRouteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClearUnboundLfgDungeonRouteRequest) ProtoMessage() {} + +func (x *ClearUnboundLfgDungeonRouteRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[55] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClearUnboundLfgDungeonRouteRequest.ProtoReflect.Descriptor instead. +func (*ClearUnboundLfgDungeonRouteRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{55} +} + +func (x *ClearUnboundLfgDungeonRouteRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *ClearUnboundLfgDungeonRouteRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *ClearUnboundLfgDungeonRouteRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *ClearUnboundLfgDungeonRouteRequest) GetMapID() uint32 { + if x != nil { + return x.MapID + } + return 0 +} + +type ClearUnboundLfgDungeonRouteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *ClearUnboundLfgDungeonRouteResponse) Reset() { + *x = ClearUnboundLfgDungeonRouteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[56] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClearUnboundLfgDungeonRouteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClearUnboundLfgDungeonRouteResponse) ProtoMessage() {} + +func (x *ClearUnboundLfgDungeonRouteResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[56] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClearUnboundLfgDungeonRouteResponse.ProtoReflect.Descriptor instead. +func (*ClearUnboundLfgDungeonRouteResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{56} +} + +func (x *ClearUnboundLfgDungeonRouteResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type GetLfgDungeonRouteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + MapID uint32 `protobuf:"varint,4,opt,name=mapID,proto3" json:"mapID,omitempty"` +} + +func (x *GetLfgDungeonRouteRequest) Reset() { + *x = GetLfgDungeonRouteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[57] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetLfgDungeonRouteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLfgDungeonRouteRequest) ProtoMessage() {} + +func (x *GetLfgDungeonRouteRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[57] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetLfgDungeonRouteRequest.ProtoReflect.Descriptor instead. +func (*GetLfgDungeonRouteRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{57} +} + +func (x *GetLfgDungeonRouteRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetLfgDungeonRouteRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *GetLfgDungeonRouteRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *GetLfgDungeonRouteRequest) GetMapID() uint32 { + if x != nil { + return x.MapID + } + return 0 +} + +type GetLfgDungeonRouteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Found bool `protobuf:"varint,2,opt,name=found,proto3" json:"found,omitempty"` + Available bool `protobuf:"varint,3,opt,name=available,proto3" json:"available,omitempty"` + Route *LfgDungeonRoute `protobuf:"bytes,4,opt,name=route,proto3" json:"route,omitempty"` +} + +func (x *GetLfgDungeonRouteResponse) Reset() { + *x = GetLfgDungeonRouteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[58] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetLfgDungeonRouteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLfgDungeonRouteResponse) ProtoMessage() {} + +func (x *GetLfgDungeonRouteResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[58] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetLfgDungeonRouteResponse.ProtoReflect.Descriptor instead. +func (*GetLfgDungeonRouteResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{58} +} + +func (x *GetLfgDungeonRouteResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetLfgDungeonRouteResponse) GetFound() bool { + if x != nil { + return x.Found + } + return false +} + +func (x *GetLfgDungeonRouteResponse) GetAvailable() bool { + if x != nil { + return x.Available + } + return false +} + +func (x *GetLfgDungeonRouteResponse) GetRoute() *LfgDungeonRoute { + if x != nil { + return x.Route + } + return nil +} + +type GetFriendsListRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` +} + +func (x *GetFriendsListRequest) Reset() { + *x = GetFriendsListRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[59] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetFriendsListRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetFriendsListRequest) ProtoMessage() {} + +func (x *GetFriendsListRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[59] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetFriendsListRequest.ProtoReflect.Descriptor instead. +func (*GetFriendsListRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{59} +} + +func (x *GetFriendsListRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetFriendsListRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *GetFriendsListRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +type GetFriendsListResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Friends []*GetFriendsListResponse_Friend `protobuf:"bytes,2,rep,name=friends,proto3" json:"friends,omitempty"` + Ignored []*GetFriendsListResponse_IgnoredPlayer `protobuf:"bytes,3,rep,name=ignored,proto3" json:"ignored,omitempty"` +} + +func (x *GetFriendsListResponse) Reset() { + *x = GetFriendsListResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[60] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetFriendsListResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetFriendsListResponse) ProtoMessage() {} + +func (x *GetFriendsListResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[60] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetFriendsListResponse.ProtoReflect.Descriptor instead. +func (*GetFriendsListResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{60} +} + +func (x *GetFriendsListResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetFriendsListResponse) GetFriends() []*GetFriendsListResponse_Friend { + if x != nil { + return x.Friends + } + return nil +} + +func (x *GetFriendsListResponse) GetIgnored() []*GetFriendsListResponse_IgnoredPlayer { + if x != nil { + return x.Ignored + } + return nil +} + +type AddFriendRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + FriendGUID uint64 `protobuf:"varint,4,opt,name=friendGUID,proto3" json:"friendGUID,omitempty"` + FriendName string `protobuf:"bytes,5,opt,name=friendName,proto3" json:"friendName,omitempty"` + Note string `protobuf:"bytes,6,opt,name=note,proto3" json:"note,omitempty"` +} + +func (x *AddFriendRequest) Reset() { + *x = AddFriendRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[61] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddFriendRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddFriendRequest) ProtoMessage() {} + +func (x *AddFriendRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[61] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddFriendRequest.ProtoReflect.Descriptor instead. +func (*AddFriendRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{61} +} + +func (x *AddFriendRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *AddFriendRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *AddFriendRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *AddFriendRequest) GetFriendGUID() uint64 { + if x != nil { + return x.FriendGUID + } + return 0 +} + +func (x *AddFriendRequest) GetFriendName() string { + if x != nil { + return x.FriendName + } + return "" +} + +func (x *AddFriendRequest) GetNote() string { + if x != nil { + return x.Note + } + return "" +} + +type AddFriendResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Result uint32 `protobuf:"varint,2,opt,name=result,proto3" json:"result,omitempty"` // FriendsResult enum value + Status uint32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"` // 0=offline, 1=online + Area uint32 `protobuf:"varint,4,opt,name=area,proto3" json:"area,omitempty"` + Level uint32 `protobuf:"varint,5,opt,name=level,proto3" json:"level,omitempty"` + ClassID uint32 `protobuf:"varint,6,opt,name=classID,proto3" json:"classID,omitempty"` + FriendGUID uint64 `protobuf:"varint,7,opt,name=friendGUID,proto3" json:"friendGUID,omitempty"` + Pending bool `protobuf:"varint,8,opt,name=pending,proto3" json:"pending,omitempty"` + Accepted bool `protobuf:"varint,9,opt,name=accepted,proto3" json:"accepted,omitempty"` +} + +func (x *AddFriendResponse) Reset() { + *x = AddFriendResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[62] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddFriendResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddFriendResponse) ProtoMessage() {} + +func (x *AddFriendResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[62] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddFriendResponse.ProtoReflect.Descriptor instead. +func (*AddFriendResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{62} +} + +func (x *AddFriendResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *AddFriendResponse) GetResult() uint32 { + if x != nil { + return x.Result + } + return 0 +} + +func (x *AddFriendResponse) GetStatus() uint32 { + if x != nil { + return x.Status + } + return 0 +} + +func (x *AddFriendResponse) GetArea() uint32 { + if x != nil { + return x.Area + } + return 0 +} + +func (x *AddFriendResponse) GetLevel() uint32 { + if x != nil { + return x.Level + } + return 0 +} + +func (x *AddFriendResponse) GetClassID() uint32 { + if x != nil { + return x.ClassID + } + return 0 +} + +func (x *AddFriendResponse) GetFriendGUID() uint64 { + if x != nil { + return x.FriendGUID + } + return 0 +} + +func (x *AddFriendResponse) GetPending() bool { + if x != nil { + return x.Pending + } + return false +} + +func (x *AddFriendResponse) GetAccepted() bool { + if x != nil { + return x.Accepted + } + return false +} + +type AddRealIDFriendByEmailRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + AccountID uint32 `protobuf:"varint,4,opt,name=accountID,proto3" json:"accountID,omitempty"` + Email string `protobuf:"bytes,5,opt,name=email,proto3" json:"email,omitempty"` + Note string `protobuf:"bytes,6,opt,name=note,proto3" json:"note,omitempty"` +} + +func (x *AddRealIDFriendByEmailRequest) Reset() { + *x = AddRealIDFriendByEmailRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[63] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddRealIDFriendByEmailRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddRealIDFriendByEmailRequest) ProtoMessage() {} + +func (x *AddRealIDFriendByEmailRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[63] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddRealIDFriendByEmailRequest.ProtoReflect.Descriptor instead. +func (*AddRealIDFriendByEmailRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{63} +} + +func (x *AddRealIDFriendByEmailRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *AddRealIDFriendByEmailRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *AddRealIDFriendByEmailRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *AddRealIDFriendByEmailRequest) GetAccountID() uint32 { + if x != nil { + return x.AccountID + } + return 0 +} + +func (x *AddRealIDFriendByEmailRequest) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *AddRealIDFriendByEmailRequest) GetNote() string { + if x != nil { + return x.Note + } + return "" +} + +type AddRealIDFriendByEmailResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Result uint32 `protobuf:"varint,2,opt,name=result,proto3" json:"result,omitempty"` // FriendsResult enum value + Status uint32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"` // 0=offline, 1=online + Area uint32 `protobuf:"varint,4,opt,name=area,proto3" json:"area,omitempty"` + Level uint32 `protobuf:"varint,5,opt,name=level,proto3" json:"level,omitempty"` + ClassID uint32 `protobuf:"varint,6,opt,name=classID,proto3" json:"classID,omitempty"` + FriendGUID uint64 `protobuf:"varint,7,opt,name=friendGUID,proto3" json:"friendGUID,omitempty"` + Pending bool `protobuf:"varint,8,opt,name=pending,proto3" json:"pending,omitempty"` + Accepted bool `protobuf:"varint,9,opt,name=accepted,proto3" json:"accepted,omitempty"` +} + +func (x *AddRealIDFriendByEmailResponse) Reset() { + *x = AddRealIDFriendByEmailResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[64] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddRealIDFriendByEmailResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddRealIDFriendByEmailResponse) ProtoMessage() {} + +func (x *AddRealIDFriendByEmailResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[64] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddRealIDFriendByEmailResponse.ProtoReflect.Descriptor instead. +func (*AddRealIDFriendByEmailResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{64} +} + +func (x *AddRealIDFriendByEmailResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *AddRealIDFriendByEmailResponse) GetResult() uint32 { + if x != nil { + return x.Result + } + return 0 +} + +func (x *AddRealIDFriendByEmailResponse) GetStatus() uint32 { + if x != nil { + return x.Status + } + return 0 +} + +func (x *AddRealIDFriendByEmailResponse) GetArea() uint32 { + if x != nil { + return x.Area + } + return 0 +} + +func (x *AddRealIDFriendByEmailResponse) GetLevel() uint32 { + if x != nil { + return x.Level + } + return 0 +} + +func (x *AddRealIDFriendByEmailResponse) GetClassID() uint32 { + if x != nil { + return x.ClassID + } + return 0 +} + +func (x *AddRealIDFriendByEmailResponse) GetFriendGUID() uint64 { + if x != nil { + return x.FriendGUID + } + return 0 +} + +func (x *AddRealIDFriendByEmailResponse) GetPending() bool { + if x != nil { + return x.Pending + } + return false +} + +func (x *AddRealIDFriendByEmailResponse) GetAccepted() bool { + if x != nil { + return x.Accepted + } + return false +} + +type AcceptRealIDFriendRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + AccountID uint32 `protobuf:"varint,4,opt,name=accountID,proto3" json:"accountID,omitempty"` + RequesterAccountID uint32 `protobuf:"varint,5,opt,name=requesterAccountID,proto3" json:"requesterAccountID,omitempty"` + Note string `protobuf:"bytes,6,opt,name=note,proto3" json:"note,omitempty"` +} + +func (x *AcceptRealIDFriendRequest) Reset() { + *x = AcceptRealIDFriendRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[65] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AcceptRealIDFriendRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AcceptRealIDFriendRequest) ProtoMessage() {} + +func (x *AcceptRealIDFriendRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[65] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AcceptRealIDFriendRequest.ProtoReflect.Descriptor instead. +func (*AcceptRealIDFriendRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{65} +} + +func (x *AcceptRealIDFriendRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *AcceptRealIDFriendRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *AcceptRealIDFriendRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *AcceptRealIDFriendRequest) GetAccountID() uint32 { + if x != nil { + return x.AccountID + } + return 0 +} + +func (x *AcceptRealIDFriendRequest) GetRequesterAccountID() uint32 { + if x != nil { + return x.RequesterAccountID + } + return 0 +} + +func (x *AcceptRealIDFriendRequest) GetNote() string { + if x != nil { + return x.Note + } + return "" +} + +type AcceptRealIDFriendResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Result uint32 `protobuf:"varint,2,opt,name=result,proto3" json:"result,omitempty"` // FriendsResult enum value + Status uint32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"` // 0=offline, 1=online + Area uint32 `protobuf:"varint,4,opt,name=area,proto3" json:"area,omitempty"` + Level uint32 `protobuf:"varint,5,opt,name=level,proto3" json:"level,omitempty"` + ClassID uint32 `protobuf:"varint,6,opt,name=classID,proto3" json:"classID,omitempty"` + FriendGUID uint64 `protobuf:"varint,7,opt,name=friendGUID,proto3" json:"friendGUID,omitempty"` + Pending bool `protobuf:"varint,8,opt,name=pending,proto3" json:"pending,omitempty"` + Accepted bool `protobuf:"varint,9,opt,name=accepted,proto3" json:"accepted,omitempty"` +} + +func (x *AcceptRealIDFriendResponse) Reset() { + *x = AcceptRealIDFriendResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[66] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AcceptRealIDFriendResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AcceptRealIDFriendResponse) ProtoMessage() {} + +func (x *AcceptRealIDFriendResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[66] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AcceptRealIDFriendResponse.ProtoReflect.Descriptor instead. +func (*AcceptRealIDFriendResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{66} +} + +func (x *AcceptRealIDFriendResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *AcceptRealIDFriendResponse) GetResult() uint32 { + if x != nil { + return x.Result + } + return 0 +} + +func (x *AcceptRealIDFriendResponse) GetStatus() uint32 { + if x != nil { + return x.Status + } + return 0 +} + +func (x *AcceptRealIDFriendResponse) GetArea() uint32 { + if x != nil { + return x.Area + } + return 0 +} + +func (x *AcceptRealIDFriendResponse) GetLevel() uint32 { + if x != nil { + return x.Level + } + return 0 +} + +func (x *AcceptRealIDFriendResponse) GetClassID() uint32 { + if x != nil { + return x.ClassID + } + return 0 +} + +func (x *AcceptRealIDFriendResponse) GetFriendGUID() uint64 { + if x != nil { + return x.FriendGUID + } + return 0 +} + +func (x *AcceptRealIDFriendResponse) GetPending() bool { + if x != nil { + return x.Pending + } + return false +} + +func (x *AcceptRealIDFriendResponse) GetAccepted() bool { + if x != nil { + return x.Accepted + } + return false +} + +type AreRealIDFriendsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + AccountID uint32 `protobuf:"varint,2,opt,name=accountID,proto3" json:"accountID,omitempty"` + FriendAccountID uint32 `protobuf:"varint,3,opt,name=friendAccountID,proto3" json:"friendAccountID,omitempty"` +} + +func (x *AreRealIDFriendsRequest) Reset() { + *x = AreRealIDFriendsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[67] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AreRealIDFriendsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AreRealIDFriendsRequest) ProtoMessage() {} + +func (x *AreRealIDFriendsRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[67] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AreRealIDFriendsRequest.ProtoReflect.Descriptor instead. +func (*AreRealIDFriendsRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{67} +} + +func (x *AreRealIDFriendsRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *AreRealIDFriendsRequest) GetAccountID() uint32 { + if x != nil { + return x.AccountID + } + return 0 +} + +func (x *AreRealIDFriendsRequest) GetFriendAccountID() uint32 { + if x != nil { + return x.FriendAccountID + } + return 0 +} + +type AreRealIDFriendsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Accepted bool `protobuf:"varint,2,opt,name=accepted,proto3" json:"accepted,omitempty"` +} + +func (x *AreRealIDFriendsResponse) Reset() { + *x = AreRealIDFriendsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[68] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AreRealIDFriendsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AreRealIDFriendsResponse) ProtoMessage() {} + +func (x *AreRealIDFriendsResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[68] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AreRealIDFriendsResponse.ProtoReflect.Descriptor instead. +func (*AreRealIDFriendsResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{68} +} + +func (x *AreRealIDFriendsResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *AreRealIDFriendsResponse) GetAccepted() bool { + if x != nil { + return x.Accepted + } + return false +} + +type RemoveFriendRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + FriendGUID uint64 `protobuf:"varint,4,opt,name=friendGUID,proto3" json:"friendGUID,omitempty"` +} + +func (x *RemoveFriendRequest) Reset() { + *x = RemoveFriendRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[69] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoveFriendRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveFriendRequest) ProtoMessage() {} + +func (x *RemoveFriendRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[69] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveFriendRequest.ProtoReflect.Descriptor instead. +func (*RemoveFriendRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{69} +} + +func (x *RemoveFriendRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *RemoveFriendRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *RemoveFriendRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *RemoveFriendRequest) GetFriendGUID() uint64 { + if x != nil { + return x.FriendGUID + } + return 0 +} + +type RemoveFriendResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *RemoveFriendResponse) Reset() { + *x = RemoveFriendResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[70] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoveFriendResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveFriendResponse) ProtoMessage() {} + +func (x *RemoveFriendResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[70] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveFriendResponse.ProtoReflect.Descriptor instead. +func (*RemoveFriendResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{70} +} + +func (x *RemoveFriendResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type SetFriendNoteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + FriendGUID uint64 `protobuf:"varint,4,opt,name=friendGUID,proto3" json:"friendGUID,omitempty"` + Note string `protobuf:"bytes,5,opt,name=note,proto3" json:"note,omitempty"` +} + +func (x *SetFriendNoteRequest) Reset() { + *x = SetFriendNoteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[71] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetFriendNoteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetFriendNoteRequest) ProtoMessage() {} + +func (x *SetFriendNoteRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[71] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetFriendNoteRequest.ProtoReflect.Descriptor instead. +func (*SetFriendNoteRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{71} +} + +func (x *SetFriendNoteRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SetFriendNoteRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *SetFriendNoteRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *SetFriendNoteRequest) GetFriendGUID() uint64 { + if x != nil { + return x.FriendGUID + } + return 0 +} + +func (x *SetFriendNoteRequest) GetNote() string { + if x != nil { + return x.Note + } + return "" +} + +type SetFriendNoteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *SetFriendNoteResponse) Reset() { + *x = SetFriendNoteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[72] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetFriendNoteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetFriendNoteResponse) ProtoMessage() {} + +func (x *SetFriendNoteResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[72] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetFriendNoteResponse.ProtoReflect.Descriptor instead. +func (*SetFriendNoteResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{72} +} + +func (x *SetFriendNoteResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type AddIgnoreRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + IgnoredGUID uint64 `protobuf:"varint,4,opt,name=ignoredGUID,proto3" json:"ignoredGUID,omitempty"` +} + +func (x *AddIgnoreRequest) Reset() { + *x = AddIgnoreRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[73] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddIgnoreRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddIgnoreRequest) ProtoMessage() {} + +func (x *AddIgnoreRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[73] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddIgnoreRequest.ProtoReflect.Descriptor instead. +func (*AddIgnoreRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{73} +} + +func (x *AddIgnoreRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *AddIgnoreRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *AddIgnoreRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *AddIgnoreRequest) GetIgnoredGUID() uint64 { + if x != nil { + return x.IgnoredGUID + } + return 0 +} + +type AddIgnoreResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Result uint32 `protobuf:"varint,2,opt,name=result,proto3" json:"result,omitempty"` // FriendsResult enum value +} + +func (x *AddIgnoreResponse) Reset() { + *x = AddIgnoreResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[74] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddIgnoreResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddIgnoreResponse) ProtoMessage() {} + +func (x *AddIgnoreResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[74] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddIgnoreResponse.ProtoReflect.Descriptor instead. +func (*AddIgnoreResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{74} +} + +func (x *AddIgnoreResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *AddIgnoreResponse) GetResult() uint32 { + if x != nil { + return x.Result + } + return 0 +} + +type RemoveIgnoreRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + IgnoredGUID uint64 `protobuf:"varint,4,opt,name=ignoredGUID,proto3" json:"ignoredGUID,omitempty"` +} + +func (x *RemoveIgnoreRequest) Reset() { + *x = RemoveIgnoreRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[75] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoveIgnoreRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveIgnoreRequest) ProtoMessage() {} + +func (x *RemoveIgnoreRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[75] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveIgnoreRequest.ProtoReflect.Descriptor instead. +func (*RemoveIgnoreRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{75} +} + +func (x *RemoveIgnoreRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *RemoveIgnoreRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *RemoveIgnoreRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *RemoveIgnoreRequest) GetIgnoredGUID() uint64 { + if x != nil { + return x.IgnoredGUID + } + return 0 +} + +type RemoveIgnoreResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *RemoveIgnoreResponse) Reset() { + *x = RemoveIgnoreResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[76] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoveIgnoreResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveIgnoreResponse) ProtoMessage() {} + +func (x *RemoveIgnoreResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[76] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveIgnoreResponse.ProtoReflect.Descriptor instead. +func (*RemoveIgnoreResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{76} +} + +func (x *RemoveIgnoreResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type NotifyStatusChangeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + Status uint32 `protobuf:"varint,4,opt,name=status,proto3" json:"status,omitempty"` // 0=offline, 1=online + Area uint32 `protobuf:"varint,5,opt,name=area,proto3" json:"area,omitempty"` + Level uint32 `protobuf:"varint,6,opt,name=level,proto3" json:"level,omitempty"` + ClassID uint32 `protobuf:"varint,7,opt,name=classID,proto3" json:"classID,omitempty"` +} + +func (x *NotifyStatusChangeRequest) Reset() { + *x = NotifyStatusChangeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[77] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NotifyStatusChangeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NotifyStatusChangeRequest) ProtoMessage() {} + +func (x *NotifyStatusChangeRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[77] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NotifyStatusChangeRequest.ProtoReflect.Descriptor instead. +func (*NotifyStatusChangeRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{77} +} + +func (x *NotifyStatusChangeRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *NotifyStatusChangeRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *NotifyStatusChangeRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *NotifyStatusChangeRequest) GetStatus() uint32 { + if x != nil { + return x.Status + } + return 0 +} + +func (x *NotifyStatusChangeRequest) GetArea() uint32 { + if x != nil { + return x.Area + } + return 0 +} + +func (x *NotifyStatusChangeRequest) GetLevel() uint32 { + if x != nil { + return x.Level + } + return 0 +} + +func (x *NotifyStatusChangeRequest) GetClassID() uint32 { + if x != nil { + return x.ClassID + } + return 0 +} + +type NotifyStatusChangeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *NotifyStatusChangeResponse) Reset() { + *x = NotifyStatusChangeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[78] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NotifyStatusChangeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NotifyStatusChangeResponse) ProtoMessage() {} + +func (x *NotifyStatusChangeResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[78] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NotifyStatusChangeResponse.ProtoReflect.Descriptor instead. +func (*NotifyStatusChangeResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{78} +} + +func (x *NotifyStatusChangeResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type GetOnlineCharactersRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` +} + +func (x *GetOnlineCharactersRequest) Reset() { + *x = GetOnlineCharactersRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[79] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetOnlineCharactersRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetOnlineCharactersRequest) ProtoMessage() {} + +func (x *GetOnlineCharactersRequest) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[79] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetOnlineCharactersRequest.ProtoReflect.Descriptor instead. +func (*GetOnlineCharactersRequest) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{79} +} + +func (x *GetOnlineCharactersRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetOnlineCharactersRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +type GetOnlineCharactersResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + CharacterGUIDs []uint64 `protobuf:"varint,2,rep,packed,name=characterGUIDs,proto3" json:"characterGUIDs,omitempty"` + TotalCount uint32 `protobuf:"varint,3,opt,name=totalCount,proto3" json:"totalCount,omitempty"` +} + +func (x *GetOnlineCharactersResponse) Reset() { + *x = GetOnlineCharactersResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[80] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetOnlineCharactersResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetOnlineCharactersResponse) ProtoMessage() {} + +func (x *GetOnlineCharactersResponse) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[80] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetOnlineCharactersResponse.ProtoReflect.Descriptor instead. +func (*GetOnlineCharactersResponse) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{80} +} + +func (x *GetOnlineCharactersResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetOnlineCharactersResponse) GetCharacterGUIDs() []uint64 { + if x != nil { + return x.CharacterGUIDs + } + return nil +} + +func (x *GetOnlineCharactersResponse) GetTotalCount() uint32 { + if x != nil { + return x.TotalCount + } + return 0 +} + +type WhoQueryResponse_WhoItem struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Guild string `protobuf:"bytes,3,opt,name=guild,proto3" json:"guild,omitempty"` + Lvl uint32 `protobuf:"varint,4,opt,name=lvl,proto3" json:"lvl,omitempty"` + Class uint32 `protobuf:"varint,5,opt,name=class,proto3" json:"class,omitempty"` + Race uint32 `protobuf:"varint,6,opt,name=race,proto3" json:"race,omitempty"` + Gender uint32 `protobuf:"varint,7,opt,name=gender,proto3" json:"gender,omitempty"` + ZoneID uint32 `protobuf:"varint,8,opt,name=zoneID,proto3" json:"zoneID,omitempty"` +} + +func (x *WhoQueryResponse_WhoItem) Reset() { + *x = WhoQueryResponse_WhoItem{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[81] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WhoQueryResponse_WhoItem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WhoQueryResponse_WhoItem) ProtoMessage() {} + +func (x *WhoQueryResponse_WhoItem) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[81] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WhoQueryResponse_WhoItem.ProtoReflect.Descriptor instead. +func (*WhoQueryResponse_WhoItem) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{12, 0} +} + +func (x *WhoQueryResponse_WhoItem) GetGuid() uint64 { + if x != nil { + return x.Guid + } + return 0 +} + +func (x *WhoQueryResponse_WhoItem) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *WhoQueryResponse_WhoItem) GetGuild() string { + if x != nil { + return x.Guild + } + return "" +} + +func (x *WhoQueryResponse_WhoItem) GetLvl() uint32 { + if x != nil { + return x.Lvl + } + return 0 +} + +func (x *WhoQueryResponse_WhoItem) GetClass() uint32 { + if x != nil { + return x.Class + } + return 0 +} + +func (x *WhoQueryResponse_WhoItem) GetRace() uint32 { + if x != nil { + return x.Race + } + return 0 +} + +func (x *WhoQueryResponse_WhoItem) GetGender() uint32 { + if x != nil { + return x.Gender + } + return 0 +} + +func (x *WhoQueryResponse_WhoItem) GetZoneID() uint32 { + if x != nil { + return x.ZoneID + } + return 0 +} + +type CharacterOnlineByNameResponse_Char struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RealmID uint32 `protobuf:"varint,1,opt,name=realmID,proto3" json:"realmID,omitempty"` + GatewayID string `protobuf:"bytes,2,opt,name=gatewayID,proto3" json:"gatewayID,omitempty"` + CharGUID uint64 `protobuf:"varint,3,opt,name=charGUID,proto3" json:"charGUID,omitempty"` + CharName string `protobuf:"bytes,4,opt,name=charName,proto3" json:"charName,omitempty"` + CharRace uint32 `protobuf:"varint,5,opt,name=charRace,proto3" json:"charRace,omitempty"` + CharClass uint32 `protobuf:"varint,6,opt,name=charClass,proto3" json:"charClass,omitempty"` + CharGender uint32 `protobuf:"varint,7,opt,name=charGender,proto3" json:"charGender,omitempty"` + CharLvl uint32 `protobuf:"varint,8,opt,name=charLvl,proto3" json:"charLvl,omitempty"` + CharZone uint32 `protobuf:"varint,9,opt,name=charZone,proto3" json:"charZone,omitempty"` + CharMap uint32 `protobuf:"varint,10,opt,name=charMap,proto3" json:"charMap,omitempty"` + CharGuildID uint64 `protobuf:"varint,11,opt,name=charGuildID,proto3" json:"charGuildID,omitempty"` + AccountID uint32 `protobuf:"varint,12,opt,name=accountID,proto3" json:"accountID,omitempty"` +} + +func (x *CharacterOnlineByNameResponse_Char) Reset() { + *x = CharacterOnlineByNameResponse_Char{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[82] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CharacterOnlineByNameResponse_Char) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CharacterOnlineByNameResponse_Char) ProtoMessage() {} + +func (x *CharacterOnlineByNameResponse_Char) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[82] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CharacterOnlineByNameResponse_Char.ProtoReflect.Descriptor instead. +func (*CharacterOnlineByNameResponse_Char) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{14, 0} +} + +func (x *CharacterOnlineByNameResponse_Char) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *CharacterOnlineByNameResponse_Char) GetGatewayID() string { + if x != nil { + return x.GatewayID + } + return "" +} + +func (x *CharacterOnlineByNameResponse_Char) GetCharGUID() uint64 { + if x != nil { + return x.CharGUID + } + return 0 +} + +func (x *CharacterOnlineByNameResponse_Char) GetCharName() string { + if x != nil { + return x.CharName + } + return "" +} + +func (x *CharacterOnlineByNameResponse_Char) GetCharRace() uint32 { + if x != nil { + return x.CharRace + } + return 0 +} + +func (x *CharacterOnlineByNameResponse_Char) GetCharClass() uint32 { + if x != nil { + return x.CharClass + } + return 0 +} + +func (x *CharacterOnlineByNameResponse_Char) GetCharGender() uint32 { + if x != nil { + return x.CharGender + } + return 0 +} + +func (x *CharacterOnlineByNameResponse_Char) GetCharLvl() uint32 { + if x != nil { + return x.CharLvl + } + return 0 +} + +func (x *CharacterOnlineByNameResponse_Char) GetCharZone() uint32 { + if x != nil { + return x.CharZone + } + return 0 +} + +func (x *CharacterOnlineByNameResponse_Char) GetCharMap() uint32 { + if x != nil { + return x.CharMap + } + return 0 +} + +func (x *CharacterOnlineByNameResponse_Char) GetCharGuildID() uint64 { + if x != nil { + return x.CharGuildID + } + return 0 +} + +func (x *CharacterOnlineByNameResponse_Char) GetAccountID() uint32 { + if x != nil { + return x.AccountID + } + return 0 +} + +type CharacterByNameResponse_Char struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RealmID uint32 `protobuf:"varint,1,opt,name=realmID,proto3" json:"realmID,omitempty"` + IsOnline bool `protobuf:"varint,2,opt,name=isOnline,proto3" json:"isOnline,omitempty"` + GatewayID string `protobuf:"bytes,3,opt,name=gatewayID,proto3" json:"gatewayID,omitempty"` + CharGUID uint64 `protobuf:"varint,4,opt,name=charGUID,proto3" json:"charGUID,omitempty"` + CharName string `protobuf:"bytes,5,opt,name=charName,proto3" json:"charName,omitempty"` + CharRace uint32 `protobuf:"varint,6,opt,name=charRace,proto3" json:"charRace,omitempty"` + CharClass uint32 `protobuf:"varint,7,opt,name=charClass,proto3" json:"charClass,omitempty"` + CharGender uint32 `protobuf:"varint,8,opt,name=charGender,proto3" json:"charGender,omitempty"` + CharLvl uint32 `protobuf:"varint,9,opt,name=charLvl,proto3" json:"charLvl,omitempty"` + CharZone uint32 `protobuf:"varint,10,opt,name=charZone,proto3" json:"charZone,omitempty"` + CharMap uint32 `protobuf:"varint,11,opt,name=charMap,proto3" json:"charMap,omitempty"` + CharGuildID uint64 `protobuf:"varint,12,opt,name=charGuildID,proto3" json:"charGuildID,omitempty"` + AccountID uint32 `protobuf:"varint,13,opt,name=accountID,proto3" json:"accountID,omitempty"` +} + +func (x *CharacterByNameResponse_Char) Reset() { + *x = CharacterByNameResponse_Char{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[83] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CharacterByNameResponse_Char) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CharacterByNameResponse_Char) ProtoMessage() {} + +func (x *CharacterByNameResponse_Char) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[83] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CharacterByNameResponse_Char.ProtoReflect.Descriptor instead. +func (*CharacterByNameResponse_Char) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{16, 0} +} + +func (x *CharacterByNameResponse_Char) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *CharacterByNameResponse_Char) GetIsOnline() bool { + if x != nil { + return x.IsOnline + } + return false +} + +func (x *CharacterByNameResponse_Char) GetGatewayID() string { + if x != nil { + return x.GatewayID + } + return "" +} + +func (x *CharacterByNameResponse_Char) GetCharGUID() uint64 { + if x != nil { + return x.CharGUID + } + return 0 +} + +func (x *CharacterByNameResponse_Char) GetCharName() string { + if x != nil { + return x.CharName + } + return "" +} + +func (x *CharacterByNameResponse_Char) GetCharRace() uint32 { + if x != nil { + return x.CharRace + } + return 0 +} + +func (x *CharacterByNameResponse_Char) GetCharClass() uint32 { + if x != nil { + return x.CharClass + } + return 0 +} + +func (x *CharacterByNameResponse_Char) GetCharGender() uint32 { + if x != nil { + return x.CharGender + } + return 0 +} + +func (x *CharacterByNameResponse_Char) GetCharLvl() uint32 { + if x != nil { + return x.CharLvl + } + return 0 +} + +func (x *CharacterByNameResponse_Char) GetCharZone() uint32 { + if x != nil { + return x.CharZone + } + return 0 +} + +func (x *CharacterByNameResponse_Char) GetCharMap() uint32 { + if x != nil { + return x.CharMap + } + return 0 +} + +func (x *CharacterByNameResponse_Char) GetCharGuildID() uint64 { + if x != nil { + return x.CharGuildID + } + return 0 +} + +func (x *CharacterByNameResponse_Char) GetAccountID() uint32 { + if x != nil { + return x.AccountID + } + return 0 +} + +type ShortCharactersDataByGUIDsResponse_ShortCharData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RealmID uint32 `protobuf:"varint,1,opt,name=realmID,proto3" json:"realmID,omitempty"` + IsOnline bool `protobuf:"varint,2,opt,name=isOnline,proto3" json:"isOnline,omitempty"` + GatewayID string `protobuf:"bytes,3,opt,name=gatewayID,proto3" json:"gatewayID,omitempty"` + CharGUID uint64 `protobuf:"varint,4,opt,name=charGUID,proto3" json:"charGUID,omitempty"` + CharName string `protobuf:"bytes,5,opt,name=charName,proto3" json:"charName,omitempty"` + CharRace uint32 `protobuf:"varint,6,opt,name=charRace,proto3" json:"charRace,omitempty"` + CharClass uint32 `protobuf:"varint,7,opt,name=charClass,proto3" json:"charClass,omitempty"` + CharGender uint32 `protobuf:"varint,8,opt,name=charGender,proto3" json:"charGender,omitempty"` + CharLvl uint32 `protobuf:"varint,9,opt,name=charLvl,proto3" json:"charLvl,omitempty"` + CharZone uint32 `protobuf:"varint,10,opt,name=charZone,proto3" json:"charZone,omitempty"` + CharMap uint32 `protobuf:"varint,11,opt,name=charMap,proto3" json:"charMap,omitempty"` + CharGuildID uint64 `protobuf:"varint,12,opt,name=charGuildID,proto3" json:"charGuildID,omitempty"` + AccountID uint32 `protobuf:"varint,13,opt,name=accountID,proto3" json:"accountID,omitempty"` +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) Reset() { + *x = ShortCharactersDataByGUIDsResponse_ShortCharData{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[84] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ShortCharactersDataByGUIDsResponse_ShortCharData) ProtoMessage() {} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[84] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ShortCharactersDataByGUIDsResponse_ShortCharData.ProtoReflect.Descriptor instead. +func (*ShortCharactersDataByGUIDsResponse_ShortCharData) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{20, 0} +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetIsOnline() bool { + if x != nil { + return x.IsOnline + } + return false +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetGatewayID() string { + if x != nil { + return x.GatewayID + } + return "" +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharGUID() uint64 { + if x != nil { + return x.CharGUID + } + return 0 +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharName() string { + if x != nil { + return x.CharName + } + return "" +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharRace() uint32 { + if x != nil { + return x.CharRace + } + return 0 +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharClass() uint32 { + if x != nil { + return x.CharClass + } + return 0 +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharGender() uint32 { + if x != nil { + return x.CharGender + } + return 0 +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharLvl() uint32 { + if x != nil { + return x.CharLvl + } + return 0 +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharZone() uint32 { + if x != nil { + return x.CharZone + } + return 0 +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharMap() uint32 { + if x != nil { + return x.CharMap + } + return 0 +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetCharGuildID() uint64 { + if x != nil { + return x.CharGuildID + } + return 0 +} + +func (x *ShortCharactersDataByGUIDsResponse_ShortCharData) GetAccountID() uint32 { + if x != nil { + return x.AccountID + } + return 0 +} + +type GetFriendsListResponse_Friend struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` + Note string `protobuf:"bytes,2,opt,name=note,proto3" json:"note,omitempty"` + Status uint32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"` // 0 = offline, 1 = online + Area uint32 `protobuf:"varint,4,opt,name=area,proto3" json:"area,omitempty"` // zone ID if online + Level uint32 `protobuf:"varint,5,opt,name=level,proto3" json:"level,omitempty"` + ClassID uint32 `protobuf:"varint,6,opt,name=classID,proto3" json:"classID,omitempty"` + RealmID uint32 `protobuf:"varint,7,opt,name=realmID,proto3" json:"realmID,omitempty"` +} + +func (x *GetFriendsListResponse_Friend) Reset() { + *x = GetFriendsListResponse_Friend{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[85] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetFriendsListResponse_Friend) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetFriendsListResponse_Friend) ProtoMessage() {} + +func (x *GetFriendsListResponse_Friend) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[85] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetFriendsListResponse_Friend.ProtoReflect.Descriptor instead. +func (*GetFriendsListResponse_Friend) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{60, 0} +} + +func (x *GetFriendsListResponse_Friend) GetGuid() uint64 { + if x != nil { + return x.Guid + } + return 0 +} + +func (x *GetFriendsListResponse_Friend) GetNote() string { + if x != nil { + return x.Note + } + return "" +} + +func (x *GetFriendsListResponse_Friend) GetStatus() uint32 { + if x != nil { + return x.Status + } + return 0 +} + +func (x *GetFriendsListResponse_Friend) GetArea() uint32 { + if x != nil { + return x.Area + } + return 0 +} + +func (x *GetFriendsListResponse_Friend) GetLevel() uint32 { + if x != nil { + return x.Level + } + return 0 +} + +func (x *GetFriendsListResponse_Friend) GetClassID() uint32 { + if x != nil { + return x.ClassID + } + return 0 +} + +func (x *GetFriendsListResponse_Friend) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +type GetFriendsListResponse_IgnoredPlayer struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` +} + +func (x *GetFriendsListResponse_IgnoredPlayer) Reset() { + *x = GetFriendsListResponse_IgnoredPlayer{} + if protoimpl.UnsafeEnabled { + mi := &file_characters_proto_msgTypes[86] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetFriendsListResponse_IgnoredPlayer) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetFriendsListResponse_IgnoredPlayer) ProtoMessage() {} + +func (x *GetFriendsListResponse_IgnoredPlayer) ProtoReflect() protoreflect.Message { + mi := &file_characters_proto_msgTypes[86] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetFriendsListResponse_IgnoredPlayer.ProtoReflect.Descriptor instead. +func (*GetFriendsListResponse_IgnoredPlayer) Descriptor() ([]byte, []int) { + return file_characters_proto_rawDescGZIP(), []int{60, 1} +} + +func (x *GetFriendsListResponse_IgnoredPlayer) GetGuid() uint64 { + if x != nil { + return x.Guid + } + return 0 +} + +var File_characters_proto protoreflect.FileDescriptor + +var file_characters_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, 0x22, 0xd0, 0x05, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x49, 0x6e, + 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x47, 0x55, 0x49, + 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x47, 0x55, 0x49, 0x44, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x04, 0x72, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x67, + 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x67, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6b, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x04, 0x73, 0x6b, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x61, 0x63, 0x65, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x66, 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x68, + 0x61, 0x69, 0x72, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, + 0x68, 0x61, 0x69, 0x72, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x68, 0x61, 0x69, + 0x72, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x68, 0x61, + 0x69, 0x72, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x61, 0x63, 0x69, 0x61, + 0x6c, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x66, 0x61, + 0x63, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, + 0x65, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, + 0x12, 0x0a, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x7a, + 0x6f, 0x6e, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x70, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x03, 0x6d, 0x61, 0x70, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x58, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x58, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x59, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x59, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5a, 0x18, 0x10, + 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5a, 0x12, + 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, + 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x61, + 0x74, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x61, 0x74, + 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x44, 0x18, + 0x15, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x70, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x49, + 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x65, 0x74, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x16, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x65, 0x74, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x16, 0x0a, + 0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x62, + 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x0a, 0x65, 0x71, 0x75, 0x69, 0x70, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x18, 0x18, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x45, + 0x71, 0x75, 0x69, 0x70, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, + 0x0a, 0x65, 0x71, 0x75, 0x69, 0x70, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, + 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x74, + 0x72, 0x61, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x65, + 0x78, 0x74, 0x72, 0x61, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x10, 0x45, 0x71, + 0x75, 0x69, 0x70, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x24, + 0x0a, 0x0d, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x49, 0x6e, + 0x66, 0x6f, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, + 0x79, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x49, 0x6e, 0x76, + 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x45, 0x6e, + 0x63, 0x68, 0x61, 0x6e, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0d, 0x45, 0x6e, 0x63, 0x68, 0x61, 0x6e, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, + 0x22, 0x6e, 0x0a, 0x22, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, + 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x22, 0x6b, 0x0a, 0x23, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, + 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, + 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x49, 0x6e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, + 0x72, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x22, 0x90, 0x01, + 0x0a, 0x1e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x4c, 0x6f, + 0x67, 0x69, 0x6e, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x61, + 0x63, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, + 0x22, 0x65, 0x0a, 0x1f, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, + 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x30, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, + 0x67, 0x49, 0x6e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x52, 0x09, 0x63, 0x68, + 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x22, 0x68, 0x0a, 0x1c, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x22, 0x49, 0x0a, 0x0b, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x64, 0x0a, 0x1d, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x31, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x22, 0xaa, 0x01, 0x0a, 0x22, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1c, 0x0a, 0x09, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, + 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, + 0x37, 0x0a, 0x23, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0xbb, 0x02, 0x0a, 0x0f, 0x57, 0x68, 0x6f, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x24, + 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, + 0x0a, 0x06, 0x6c, 0x76, 0x6c, 0x4d, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, + 0x6c, 0x76, 0x6c, 0x4d, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x76, 0x6c, 0x4d, 0x61, 0x78, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6c, 0x76, 0x6c, 0x4d, 0x61, 0x78, 0x12, 0x1e, + 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, + 0x0a, 0x09, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x72, 0x61, 0x63, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, + 0x72, 0x61, 0x63, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6c, 0x61, 0x73, + 0x73, 0x4d, 0x61, 0x73, 0x6b, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x6c, 0x61, + 0x73, 0x73, 0x4d, 0x61, 0x73, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x18, + 0x0a, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x05, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, + 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x22, 0xc0, 0x02, 0x0a, 0x10, 0x57, 0x68, 0x6f, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, + 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1e, 0x0a, + 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x44, 0x0a, + 0x0e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x54, 0x6f, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x68, 0x6f, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x57, 0x68, 0x6f, 0x49, + 0x74, 0x65, 0x6d, 0x52, 0x0e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x54, 0x6f, 0x44, 0x69, 0x73, 0x70, + 0x6c, 0x61, 0x79, 0x1a, 0xb3, 0x01, 0x0a, 0x07, 0x57, 0x68, 0x6f, 0x49, 0x74, 0x65, 0x6d, 0x12, + 0x12, 0x0a, 0x04, 0x67, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, + 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x75, 0x69, 0x6c, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x10, 0x0a, + 0x03, 0x6c, 0x76, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6c, 0x76, 0x6c, 0x12, + 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, + 0x63, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x61, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x12, 0x16, 0x0a, 0x06, 0x7a, 0x6f, 0x6e, 0x65, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x06, 0x7a, 0x6f, 0x6e, 0x65, 0x49, 0x44, 0x22, 0x70, 0x0a, 0x1c, 0x43, 0x68, 0x61, + 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x79, 0x4e, 0x61, + 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x68, + 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xda, 0x03, 0x0a, 0x1d, + 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, + 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x44, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, + 0x72, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x52, 0x09, 0x63, 0x68, 0x61, 0x72, + 0x61, 0x63, 0x74, 0x65, 0x72, 0x1a, 0xe0, 0x02, 0x0a, 0x04, 0x43, 0x68, 0x61, 0x72, 0x12, 0x18, + 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x52, 0x61, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x52, 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, + 0x61, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, + 0x68, 0x61, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x72, + 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x68, + 0x61, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x72, + 0x4c, 0x76, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, 0x61, 0x72, 0x4c, + 0x76, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x5a, 0x6f, 0x6e, 0x65, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x63, 0x68, 0x61, 0x72, 0x4d, 0x61, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x63, 0x68, 0x61, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x72, + 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, + 0x68, 0x61, 0x72, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x22, 0x6a, 0x0a, 0x16, 0x43, 0x68, 0x61, 0x72, + 0x61, 0x63, 0x74, 0x65, 0x72, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x24, + 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, + 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xea, 0x03, 0x0a, 0x17, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, + 0x65, 0x72, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x3e, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, + 0x63, 0x74, 0x65, 0x72, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x52, 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, + 0x65, 0x72, 0x1a, 0xfc, 0x02, 0x0a, 0x04, 0x43, 0x68, 0x61, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x44, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x44, 0x12, + 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x63, + 0x68, 0x61, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, + 0x68, 0x61, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x52, + 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x52, + 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x68, 0x61, 0x72, 0x43, 0x6c, 0x61, 0x73, + 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x72, 0x4c, 0x76, 0x6c, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, 0x61, 0x72, 0x4c, 0x76, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x63, + 0x68, 0x61, 0x72, 0x5a, 0x6f, 0x6e, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, + 0x68, 0x61, 0x72, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x72, 0x4d, + 0x61, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, 0x61, 0x72, 0x4d, 0x61, + 0x70, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x72, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x72, 0x47, 0x75, 0x69, 0x6c, + 0x64, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, + 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, + 0x44, 0x22, 0x6a, 0x0a, 0x16, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x42, 0x79, + 0x47, 0x55, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, + 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, + 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x61, + 0x63, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, + 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0x6b, 0x0a, + 0x17, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3e, 0x0a, 0x09, 0x63, 0x68, + 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x42, 0x79, 0x4e, 0x61, + 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x52, + 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x22, 0x65, 0x0a, 0x21, 0x53, 0x68, + 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x44, 0x61, 0x74, + 0x61, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, + 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x47, + 0x55, 0x49, 0x44, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x52, 0x05, 0x47, 0x55, 0x49, 0x44, + 0x73, 0x22, 0x94, 0x04, 0x0a, 0x22, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x72, 0x61, + 0x63, 0x74, 0x65, 0x72, 0x73, 0x44, 0x61, 0x74, 0x61, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x54, 0x0a, 0x0a, 0x63, 0x68, + 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, + 0x65, 0x72, 0x73, 0x44, 0x61, 0x74, 0x61, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x72, + 0x44, 0x61, 0x74, 0x61, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, + 0x1a, 0x85, 0x03, 0x0a, 0x0d, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x72, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, + 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, + 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x52, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x52, 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, + 0x61, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, + 0x68, 0x61, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x72, + 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x68, + 0x61, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x72, + 0x4c, 0x76, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, 0x61, 0x72, 0x4c, + 0x76, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x72, 0x5a, 0x6f, 0x6e, 0x65, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x63, 0x68, 0x61, 0x72, 0x4d, 0x61, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x63, 0x68, 0x61, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x72, + 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, + 0x68, 0x61, 0x72, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x22, 0xea, 0x01, 0x0a, 0x26, 0x41, 0x72, 0x65, + 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x46, + 0x6f, 0x72, 0x52, 0x61, 0x74, 0x65, 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, + 0x1e, 0x0a, 0x0a, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, + 0x20, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x04, 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x79, 0x70, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x34, 0x0a, 0x15, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, + 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x65, 0x72, 0x52, + 0x61, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x96, 0x03, 0x0a, 0x27, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, + 0x65, 0x61, 0x6d, 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x52, + 0x61, 0x74, 0x65, 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x4a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, + 0x61, 0x6d, 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x52, 0x61, + 0x74, 0x65, 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x20, 0x0a, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, + 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x65, 0x61, 0x6d, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x65, 0x61, 0x6d, 0x52, 0x61, 0x74, 0x69, 0x6e, + 0x67, 0x12, 0x2a, 0x0a, 0x10, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x65, 0x72, 0x52, + 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x6d, 0x61, 0x74, + 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x38, 0x0a, + 0x17, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x4f, 0x70, 0x70, 0x6f, 0x6e, 0x65, 0x6e, + 0x74, 0x73, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x17, + 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x4f, 0x70, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, + 0x73, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0x65, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, + 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x4d, 0x69, 0x73, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x10, 0x02, 0x12, 0x14, 0x0a, 0x10, 0x49, + 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x50, 0x61, 0x72, 0x74, 0x79, 0x53, 0x69, 0x7a, 0x65, 0x10, + 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x04, 0x12, 0x0f, 0x0a, + 0x0b, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x79, 0x70, 0x65, 0x10, 0x05, 0x22, 0xf0, + 0x03, 0x0a, 0x22, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, + 0x61, 0x6d, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x47, 0x55, 0x49, 0x44, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x47, + 0x55, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x47, + 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x65, 0x74, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x72, 0x65, 0x6e, 0x61, + 0x54, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x72, 0x65, 0x6e, + 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, + 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x12, + 0x20, 0x0a, 0x0b, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x6d, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x6d, 0x53, 0x74, 0x79, 0x6c, + 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x6d, 0x43, 0x6f, + 0x6c, 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x53, 0x74, 0x79, + 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x43, + 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x62, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x30, 0x0a, 0x13, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x65, 0x72, + 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x15, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x65, 0x72, 0x52, 0x61, + 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, + 0x67, 0x22, 0xba, 0x02, 0x0a, 0x23, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x72, 0x65, 0x6e, + 0x61, 0x54, 0x65, 0x61, 0x6d, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x46, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2e, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, + 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, + 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, + 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0x96, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x46, + 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x4f, 0x77, 0x6e, + 0x65, 0x72, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x54, + 0x79, 0x70, 0x65, 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x45, 0x78, 0x69, + 0x73, 0x74, 0x73, 0x10, 0x04, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, + 0x49, 0x6e, 0x54, 0x65, 0x61, 0x6d, 0x10, 0x05, 0x12, 0x17, 0x0a, 0x13, 0x4e, 0x6f, 0x74, 0x45, + 0x6e, 0x6f, 0x75, 0x67, 0x68, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x10, + 0x06, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x07, 0x12, 0x0f, 0x0a, + 0x0b, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x10, 0x08, 0x22, 0xf5, + 0x02, 0x0a, 0x13, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6f, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x61, 0x73, + 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x1c, + 0x0a, 0x09, 0x77, 0x65, 0x65, 0x6b, 0x47, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x09, 0x77, 0x65, 0x65, 0x6b, 0x47, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, + 0x77, 0x65, 0x65, 0x6b, 0x57, 0x69, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, + 0x77, 0x65, 0x65, 0x6b, 0x57, 0x69, 0x6e, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x47, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x47, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x57, 0x69, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, + 0x73, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x57, 0x69, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x70, 0x65, + 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0e, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x61, 0x74, 0x69, + 0x6e, 0x67, 0x12, 0x2a, 0x0a, 0x10, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x65, 0x72, + 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x16, + 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x4d, 0x4d, 0x52, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, + 0x6d, 0x61, 0x78, 0x4d, 0x4d, 0x52, 0x22, 0x88, 0x04, 0x0a, 0x0d, 0x41, 0x72, 0x65, 0x6e, 0x61, + 0x54, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x72, 0x65, 0x6e, + 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x61, + 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, + 0x0a, 0x0b, 0x63, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x47, 0x55, 0x49, 0x44, + 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1c, 0x0a, 0x09, + 0x77, 0x65, 0x65, 0x6b, 0x47, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x09, 0x77, 0x65, 0x65, 0x6b, 0x47, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x65, + 0x65, 0x6b, 0x57, 0x69, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x77, 0x65, + 0x65, 0x6b, 0x57, 0x69, 0x6e, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x47, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x47, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x57, 0x69, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x57, 0x69, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x6e, 0x6b, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x61, 0x6e, 0x6b, 0x12, 0x28, 0x0a, 0x0f, + 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x6d, + 0x53, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x65, 0x6d, 0x62, + 0x6c, 0x65, 0x6d, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x6d, 0x62, 0x6c, + 0x65, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x65, + 0x6d, 0x62, 0x6c, 0x65, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0b, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, + 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0b, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x31, + 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x73, 0x22, 0x63, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, + 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, + 0x6d, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, + 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb4, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x72, + 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, + 0x69, 0x12, 0x37, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, + 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x0a, 0x04, 0x74, 0x65, + 0x61, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, + 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x74, 0x65, 0x61, + 0x6d, 0x22, 0x2a, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, + 0x6b, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, + 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x02, 0x22, 0xcb, 0x01, + 0x0a, 0x15, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x50, 0x65, 0x74, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x65, 0x74, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x47, 0x55, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, + 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, + 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0a, 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x6f, + 0x77, 0x6e, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, + 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, + 0x09, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x09, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x6d, 0x0a, 0x1b, 0x47, + 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x50, 0x65, 0x74, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, + 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, + 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x65, + 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x55, 0x49, 0x44, 0x22, 0xe2, 0x01, 0x0a, 0x1c, 0x47, + 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x50, 0x65, 0x74, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, + 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3f, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x50, + 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x35, + 0x0a, 0x08, 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x50, + 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x70, 0x65, 0x74, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x38, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x46, 0x6f, + 0x75, 0x6e, 0x64, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x41, 0x72, 0x65, 0x6e, + 0x61, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x03, 0x22, + 0xf0, 0x01, 0x0a, 0x1c, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, + 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, + 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x20, + 0x0a, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x55, 0x49, 0x44, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x55, + 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x22, 0x8d, 0x01, 0x0a, 0x1d, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x41, 0x72, 0x65, + 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x6e, + 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x0a, 0x04, 0x74, + 0x65, 0x61, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x74, 0x65, + 0x61, 0x6d, 0x22, 0xb2, 0x01, 0x0a, 0x1c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x41, 0x72, 0x65, + 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, + 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, + 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x26, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x61, 0x74, 0x69, 0x6e, + 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, + 0x6c, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x8d, 0x01, 0x0a, 0x1d, 0x41, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x76, 0x69, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x33, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x76, 0x31, + 0x2e, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x25, 0x0a, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, + 0x61, 0x52, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x22, 0x6b, 0x0a, 0x1d, 0x44, 0x65, 0x63, 0x6c, 0x69, + 0x6e, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x76, 0x69, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x22, 0xb1, 0x01, 0x0a, 0x19, 0x41, 0x64, 0x64, 0x41, 0x72, 0x65, 0x6e, + 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, + 0x0a, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, + 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x12, 0x26, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x61, 0x74, 0x69, + 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, + 0x61, 0x6c, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x22, 0xaa, 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, + 0x61, 0x6d, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x61, 0x72, 0x65, 0x6e, + 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0x85, 0x01, 0x0a, 0x17, 0x44, 0x69, 0x73, 0x62, 0x61, 0x6e, + 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, + 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, + 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x09, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0xaa, 0x01, + 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x43, 0x61, + 0x70, 0x74, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, + 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x72, 0x65, 0x6e, + 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x61, + 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x61, + 0x70, 0x74, 0x61, 0x69, 0x6e, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0b, 0x63, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, + 0x61, 0x63, 0x74, 0x6f, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x09, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0x99, 0x01, 0x0a, 0x17, 0x53, + 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, + 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, + 0x61, 0x6d, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x63, 0x74, + 0x6f, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0xc6, 0x02, 0x0a, 0x14, 0x41, 0x72, 0x65, 0x6e, 0x61, + 0x54, 0x65, 0x61, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, + 0x26, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x61, 0x74, 0x69, 0x6e, + 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, + 0x6c, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x65, 0x65, 0x6b, 0x47, + 0x61, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x77, 0x65, 0x65, 0x6b, + 0x47, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x65, 0x65, 0x6b, 0x57, 0x69, 0x6e, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x77, 0x65, 0x65, 0x6b, 0x57, 0x69, 0x6e, + 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x47, 0x61, 0x6d, 0x65, 0x73, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x47, 0x61, + 0x6d, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x57, 0x69, 0x6e, + 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x57, + 0x69, 0x6e, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x65, + 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x6d, + 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, + 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x4d, 0x4d, 0x52, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x6d, 0x61, 0x78, 0x4d, 0x4d, 0x52, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x61, 0x76, 0x65, 0x41, + 0x72, 0x65, 0x6e, 0x61, 0x53, 0x74, 0x61, 0x74, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0e, 0x73, 0x61, 0x76, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x53, 0x74, 0x61, 0x74, 0x73, 0x22, + 0xd9, 0x02, 0x0a, 0x19, 0x53, 0x61, 0x76, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, + 0x6d, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x72, 0x65, + 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, + 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x72, + 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x72, 0x61, 0x74, + 0x69, 0x6e, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x65, 0x65, 0x6b, 0x47, 0x61, 0x6d, 0x65, 0x73, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x77, 0x65, 0x65, 0x6b, 0x47, 0x61, 0x6d, 0x65, + 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x65, 0x65, 0x6b, 0x57, 0x69, 0x6e, 0x73, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x08, 0x77, 0x65, 0x65, 0x6b, 0x57, 0x69, 0x6e, 0x73, 0x12, 0x20, 0x0a, + 0x0b, 0x73, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x47, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x47, 0x61, 0x6d, 0x65, 0x73, 0x12, + 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x57, 0x69, 0x6e, 0x73, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x57, 0x69, 0x6e, 0x73, 0x12, + 0x12, 0x0a, 0x04, 0x72, 0x61, 0x6e, 0x6b, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, + 0x61, 0x6e, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x32, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, + 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0x48, 0x0a, 0x1a, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, + 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x22, 0x75, 0x0a, 0x27, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, + 0x74, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, + 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0x6f, 0x0a, 0x21, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, + 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0x62, 0x0a, + 0x19, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, + 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x33, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x76, + 0x31, 0x2e, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x22, 0xb1, 0x01, 0x0a, 0x19, 0x53, 0x61, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, + 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x63, + 0x68, 0x61, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x63, + 0x68, 0x61, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x12, 0x0c, 0x0a, + 0x01, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x02, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x01, 0x79, 0x12, 0x0c, 0x0a, 0x01, 0x7a, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x02, 0x52, 0x01, 0x7a, 0x12, 0x0c, 0x0a, 0x01, 0x6f, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x02, 0x52, 0x01, 0x6f, 0x22, 0x2e, 0x0a, 0x1a, 0x53, 0x61, 0x76, 0x65, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0xed, 0x02, 0x0a, 0x0f, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, + 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, + 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x22, 0x0a, + 0x0c, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, + 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, + 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x12, 0x34, 0x0a, 0x15, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x73, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x42, 0x6f, + 0x75, 0x6e, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x28, 0x0a, 0x0f, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x49, 0x44, 0x22, 0x5b, 0x0a, 0x1c, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, + 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x29, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x66, 0x67, 0x44, + 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x05, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x22, 0x31, 0x0a, 0x1d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x66, 0x67, 0x44, + 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0xc8, 0x01, 0x0a, 0x24, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, + 0x6d, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x45, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, + 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, + 0x70, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, + 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, + 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, + 0x22, 0x64, 0x0a, 0x25, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x4c, 0x66, 0x67, 0x44, 0x75, + 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x65, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x29, 0x0a, 0x05, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, + 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x22, 0x86, 0x01, 0x0a, 0x22, 0x43, 0x6c, 0x65, 0x61, 0x72, + 0x55, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, + 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x70, + 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x22, + 0x37, 0x0a, 0x23, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x55, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x4c, + 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x7d, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4c, + 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x22, 0x8d, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4c, + 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x1c, + 0x0a, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x29, 0x0a, 0x05, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x52, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x22, 0x63, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x46, 0x72, + 0x69, 0x65, 0x6e, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, + 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0xf9, 0x02, 0x0a, + 0x16, 0x47, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3b, 0x0a, 0x07, 0x66, 0x72, 0x69, + 0x65, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x07, 0x66, + 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x12, 0x42, 0x0a, 0x07, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, + 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x50, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x52, 0x07, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x1a, 0xa6, 0x01, 0x0a, 0x06, 0x46, + 0x72, 0x69, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x65, 0x61, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x04, 0x61, 0x72, 0x65, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, + 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, + 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x1a, 0x23, 0x0a, 0x0d, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x04, 0x67, 0x75, 0x69, 0x64, 0x22, 0xb2, 0x01, 0x0a, 0x10, 0x41, 0x64, 0x64, + 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x72, 0x69, + 0x65, 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x66, + 0x72, 0x69, 0x65, 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x72, 0x69, + 0x65, 0x6e, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, + 0x72, 0x69, 0x65, 0x6e, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x22, 0xef, 0x01, + 0x0a, 0x11, 0x41, 0x64, 0x64, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x65, 0x61, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x04, 0x61, 0x72, 0x65, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, + 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, + 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x72, 0x69, + 0x65, 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x66, + 0x72, 0x69, 0x65, 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x22, + 0xb3, 0x01, 0x0a, 0x1d, 0x41, 0x64, 0x64, 0x52, 0x65, 0x61, 0x6c, 0x49, 0x44, 0x46, 0x72, 0x69, + 0x65, 0x6e, 0x64, 0x42, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1c, 0x0a, + 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x65, + 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, + 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x22, 0xfc, 0x01, 0x0a, 0x1e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x61, + 0x6c, 0x49, 0x44, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x42, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, + 0x65, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x61, 0x72, 0x65, 0x61, 0x12, 0x14, + 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, + 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, 0x12, 0x1e, + 0x0a, 0x0a, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0a, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x12, 0x18, + 0x0a, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x65, 0x64, 0x22, 0xc9, 0x01, 0x0a, 0x19, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, + 0x65, 0x61, 0x6c, 0x49, 0x44, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, + 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1c, + 0x0a, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x2e, 0x0a, 0x12, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, + 0x22, 0xf8, 0x01, 0x0a, 0x1a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x61, 0x6c, 0x49, + 0x44, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, + 0x69, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x65, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x04, 0x61, 0x72, 0x65, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x63, + 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6c, + 0x61, 0x73, 0x73, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x47, + 0x55, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x66, 0x72, 0x69, 0x65, 0x6e, + 0x64, 0x47, 0x55, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, + 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x22, 0x73, 0x0a, 0x17, 0x41, + 0x72, 0x65, 0x52, 0x65, 0x61, 0x6c, 0x49, 0x44, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x28, 0x0a, 0x0f, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0f, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, + 0x22, 0x48, 0x0a, 0x18, 0x41, 0x72, 0x65, 0x52, 0x65, 0x61, 0x6c, 0x49, 0x44, 0x46, 0x72, 0x69, + 0x65, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1a, + 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x22, 0x81, 0x01, 0x0a, 0x13, 0x52, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, + 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, + 0x0a, 0x0a, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0a, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x22, 0x28, + 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x96, 0x01, 0x0a, 0x14, 0x53, 0x65, 0x74, + 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x47, 0x55, 0x49, 0x44, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, + 0x65, 0x22, 0x29, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x4e, 0x6f, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, + 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x80, 0x01, 0x0a, + 0x10, 0x41, 0x64, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, + 0x0b, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0b, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x47, 0x55, 0x49, 0x44, 0x22, + 0x3d, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x83, + 0x01, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x47, 0x55, 0x49, + 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, + 0x47, 0x55, 0x49, 0x44, 0x22, 0x28, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x67, + 0x6e, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0xc3, + 0x01, 0x0a, 0x19, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, + 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x65, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, + 0x61, 0x72, 0x65, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, + 0x61, 0x73, 0x73, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6c, 0x61, + 0x73, 0x73, 0x49, 0x44, 0x22, 0x2e, 0x0a, 0x1a, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x22, 0x48, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x22, 0x77, + 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, + 0x63, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x26, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0e, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, + 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x2a, 0x80, 0x04, 0x0a, 0x17, 0x41, 0x72, 0x65, 0x6e, + 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, 0x41, + 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4f, 0x4b, 0x10, 0x00, 0x12, + 0x21, 0x0a, 0x1d, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x55, + 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, + 0x10, 0x01, 0x12, 0x27, 0x0a, 0x23, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, 0x41, 0x4d, + 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, + 0x5f, 0x4d, 0x49, 0x53, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x10, 0x02, 0x12, 0x24, 0x0a, 0x20, 0x41, + 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, + 0x03, 0x12, 0x23, 0x0a, 0x1f, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x5f, + 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x5f, 0x45, 0x58, + 0x49, 0x53, 0x54, 0x53, 0x10, 0x04, 0x12, 0x27, 0x0a, 0x23, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, + 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x4c, + 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, 0x49, 0x4e, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x10, 0x05, 0x12, + 0x23, 0x0a, 0x1f, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x55, + 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x4f, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x46, 0x55, + 0x4c, 0x4c, 0x10, 0x06, 0x12, 0x1e, 0x0a, 0x1a, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, + 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, + 0x45, 0x44, 0x10, 0x07, 0x12, 0x24, 0x0a, 0x20, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, + 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x56, 0x41, + 0x4c, 0x49, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x10, 0x08, 0x12, 0x29, 0x0a, 0x25, 0x41, 0x52, + 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, + 0x4e, 0x5f, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4e, + 0x49, 0x45, 0x44, 0x10, 0x09, 0x12, 0x24, 0x0a, 0x20, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, + 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4c, 0x45, 0x41, + 0x44, 0x45, 0x52, 0x5f, 0x4c, 0x45, 0x41, 0x56, 0x45, 0x10, 0x0a, 0x12, 0x27, 0x0a, 0x23, 0x41, + 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x41, 0x4c, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, 0x49, 0x4e, 0x56, 0x49, 0x54, + 0x45, 0x44, 0x10, 0x0b, 0x12, 0x24, 0x0a, 0x20, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, + 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x47, 0x4e, 0x4f, + 0x52, 0x49, 0x4e, 0x47, 0x5f, 0x59, 0x4f, 0x55, 0x10, 0x0c, 0x32, 0xc2, 0x1c, 0x0a, 0x11, 0x43, + 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x12, 0x6e, 0x0a, 0x1b, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, + 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, + 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, + 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, + 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x46, 0x6f, + 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x62, 0x0a, 0x17, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, + 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x4c, 0x6f, 0x67, + 0x69, 0x6e, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, + 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x15, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x20, 0x2e, + 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, + 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x76, 0x31, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x46, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x57, 0x68, 0x6f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x13, + 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x68, 0x6f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x68, 0x6f, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x15, 0x43, 0x68, 0x61, + 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x79, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, + 0x72, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, + 0x74, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x72, 0x61, + 0x63, 0x74, 0x65, 0x72, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, + 0x61, 0x63, 0x74, 0x65, 0x72, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, + 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, + 0x61, 0x63, 0x74, 0x65, 0x72, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, + 0x72, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x71, 0x0a, 0x20, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x68, + 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x44, 0x61, 0x74, 0x61, 0x42, 0x79, 0x47, 0x55, + 0x49, 0x44, 0x73, 0x12, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x43, 0x68, + 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x44, 0x61, 0x74, 0x61, 0x42, 0x79, 0x47, 0x55, + 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x68, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x44, + 0x61, 0x74, 0x61, 0x42, 0x79, 0x47, 0x55, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x7a, 0x0a, 0x1f, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x51, + 0x75, 0x65, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x52, 0x61, 0x74, 0x65, 0x64, + 0x41, 0x72, 0x65, 0x6e, 0x61, 0x12, 0x2a, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x6e, 0x61, + 0x54, 0x65, 0x61, 0x6d, 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, + 0x52, 0x61, 0x74, 0x65, 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, + 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x52, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, + 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x12, 0x17, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x59, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, + 0x6d, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x50, 0x65, 0x74, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x50, 0x65, 0x74, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x1b, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x46, + 0x72, 0x6f, 0x6d, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, + 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, + 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x65, 0x74, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x15, + 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, + 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, + 0x69, 0x74, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x15, 0x41, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x76, + 0x69, 0x74, 0x65, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x41, + 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x16, 0x44, 0x65, 0x63, 0x6c, + 0x69, 0x6e, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x76, 0x69, + 0x74, 0x65, 0x12, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x6c, 0x69, 0x6e, 0x65, 0x41, + 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x6e, 0x61, + 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, + 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, + 0x41, 0x64, 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x15, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x72, 0x65, + 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, + 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x62, 0x61, 0x6e, 0x64, 0x41, 0x72, 0x65, + 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x62, + 0x61, 0x6e, 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, + 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x54, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, + 0x61, 0x6d, 0x43, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x12, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x43, 0x61, 0x70, 0x74, 0x61, + 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x41, + 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4e, 0x61, + 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x12, 0x53, 0x61, 0x76, 0x65, + 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x1d, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, + 0x6d, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, + 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x13, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, + 0x61, 0x6d, 0x73, 0x12, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, + 0x6c, 0x6c, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, + 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x41, 0x72, + 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x43, 0x68, 0x61, + 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, + 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x62, 0x0a, 0x1a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x73, + 0x12, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, + 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x12, 0x53, 0x61, 0x76, 0x65, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x61, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x15, 0x52, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x74, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x72, 0x6d, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x28, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, + 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, + 0x6d, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x45, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x6e, 0x0a, 0x1b, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x55, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x4c, + 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x26, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x55, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, + 0x72, 0x55, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, + 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x53, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x66, + 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, + 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, + 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, + 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, + 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, + 0x09, 0x41, 0x64, 0x64, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x2e, 0x76, 0x31, 0x2e, + 0x41, 0x64, 0x64, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x15, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x16, 0x41, 0x64, 0x64, 0x52, 0x65, + 0x61, 0x6c, 0x49, 0x44, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x42, 0x79, 0x45, 0x6d, 0x61, 0x69, + 0x6c, 0x12, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x61, 0x6c, 0x49, 0x44, + 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x42, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x61, + 0x6c, 0x49, 0x44, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x42, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x12, 0x41, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x52, 0x65, 0x61, 0x6c, 0x49, 0x44, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x12, 0x1d, + 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x61, 0x6c, 0x49, 0x44, + 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x61, 0x6c, 0x49, 0x44, 0x46, + 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, + 0x10, 0x41, 0x72, 0x65, 0x52, 0x65, 0x61, 0x6c, 0x49, 0x44, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, + 0x73, 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x52, 0x65, 0x61, 0x6c, 0x49, 0x44, + 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, + 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x65, 0x52, 0x65, 0x61, 0x6c, 0x49, 0x44, 0x46, 0x72, 0x69, + 0x65, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0c, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x44, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x65, + 0x12, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x4e, + 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x74, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x49, 0x67, 0x6e, 0x6f, + 0x72, 0x65, 0x12, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, + 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x41, 0x0a, 0x0c, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x12, + 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x67, 0x6e, 0x6f, 0x72, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x53, 0x0a, 0x12, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, + 0x69, 0x66, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4f, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1e, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x68, 0x61, + 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x68, 0x61, + 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, + 0x13, 0x5a, 0x11, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, + 0x73, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_characters_proto_rawDescOnce sync.Once + file_characters_proto_rawDescData = file_characters_proto_rawDesc +) + +func file_characters_proto_rawDescGZIP() []byte { + file_characters_proto_rawDescOnce.Do(func() { + file_characters_proto_rawDescData = protoimpl.X.CompressGZIP(file_characters_proto_rawDescData) + }) + return file_characters_proto_rawDescData +} + +var file_characters_proto_enumTypes = make([]protoimpl.EnumInfo, 5) +var file_characters_proto_msgTypes = make([]protoimpl.MessageInfo, 87) +var file_characters_proto_goTypes = []interface{}{ + (ArenaTeamMutationStatus)(0), // 0: v1.ArenaTeamMutationStatus + (ArenaTeamQueueDataForRatedArenaResponse_Status)(0), // 1: v1.ArenaTeamQueueDataForRatedArenaResponse.Status + (CreateArenaTeamFromPetitionResponse_Status)(0), // 2: v1.CreateArenaTeamFromPetitionResponse.Status + (GetArenaTeamResponse_Status)(0), // 3: v1.GetArenaTeamResponse.Status + (GetArenaTeamPetitionResponse_Status)(0), // 4: v1.GetArenaTeamPetitionResponse.Status + (*LogInCharacter)(nil), // 5: v1.LogInCharacter + (*EquipmentDisplay)(nil), // 6: v1.EquipmentDisplay + (*CharactersToLoginForAccountRequest)(nil), // 7: v1.CharactersToLoginForAccountRequest + (*CharactersToLoginForAccountResponse)(nil), // 8: v1.CharactersToLoginForAccountResponse + (*CharactersToLoginByGUIDRequest)(nil), // 9: v1.CharactersToLoginByGUIDRequest + (*CharactersToLoginByGUIDResponse)(nil), // 10: v1.CharactersToLoginByGUIDResponse + (*AccountDataForAccountRequest)(nil), // 11: v1.AccountDataForAccountRequest + (*AccountData)(nil), // 12: v1.AccountData + (*AccountDataForAccountResponse)(nil), // 13: v1.AccountDataForAccountResponse + (*UpdateAccountDataForAccountRequest)(nil), // 14: v1.UpdateAccountDataForAccountRequest + (*UpdateAccountDataForAccountResponse)(nil), // 15: v1.UpdateAccountDataForAccountResponse + (*WhoQueryRequest)(nil), // 16: v1.WhoQueryRequest + (*WhoQueryResponse)(nil), // 17: v1.WhoQueryResponse + (*CharacterOnlineByNameRequest)(nil), // 18: v1.CharacterOnlineByNameRequest + (*CharacterOnlineByNameResponse)(nil), // 19: v1.CharacterOnlineByNameResponse + (*CharacterByNameRequest)(nil), // 20: v1.CharacterByNameRequest + (*CharacterByNameResponse)(nil), // 21: v1.CharacterByNameResponse + (*CharacterByGUIDRequest)(nil), // 22: v1.CharacterByGUIDRequest + (*CharacterByGUIDResponse)(nil), // 23: v1.CharacterByGUIDResponse + (*ShortCharactersDataByGUIDsRequest)(nil), // 24: v1.ShortCharactersDataByGUIDsRequest + (*ShortCharactersDataByGUIDsResponse)(nil), // 25: v1.ShortCharactersDataByGUIDsResponse + (*ArenaTeamQueueDataForRatedArenaRequest)(nil), // 26: v1.ArenaTeamQueueDataForRatedArenaRequest + (*ArenaTeamQueueDataForRatedArenaResponse)(nil), // 27: v1.ArenaTeamQueueDataForRatedArenaResponse + (*CreateArenaTeamFromPetitionRequest)(nil), // 28: v1.CreateArenaTeamFromPetitionRequest + (*CreateArenaTeamFromPetitionResponse)(nil), // 29: v1.CreateArenaTeamFromPetitionResponse + (*ArenaTeamMemberData)(nil), // 30: v1.ArenaTeamMemberData + (*ArenaTeamData)(nil), // 31: v1.ArenaTeamData + (*GetArenaTeamRequest)(nil), // 32: v1.GetArenaTeamRequest + (*GetArenaTeamResponse)(nil), // 33: v1.GetArenaTeamResponse + (*ArenaTeamPetitionData)(nil), // 34: v1.ArenaTeamPetitionData + (*GetArenaTeamPetitionRequest)(nil), // 35: v1.GetArenaTeamPetitionRequest + (*GetArenaTeamPetitionResponse)(nil), // 36: v1.GetArenaTeamPetitionResponse + (*InviteArenaTeamMemberRequest)(nil), // 37: v1.InviteArenaTeamMemberRequest + (*InviteArenaTeamMemberResponse)(nil), // 38: v1.InviteArenaTeamMemberResponse + (*AcceptArenaTeamInviteRequest)(nil), // 39: v1.AcceptArenaTeamInviteRequest + (*AcceptArenaTeamInviteResponse)(nil), // 40: v1.AcceptArenaTeamInviteResponse + (*DeclineArenaTeamInviteRequest)(nil), // 41: v1.DeclineArenaTeamInviteRequest + (*AddArenaTeamMemberRequest)(nil), // 42: v1.AddArenaTeamMemberRequest + (*RemoveArenaTeamMemberRequest)(nil), // 43: v1.RemoveArenaTeamMemberRequest + (*DisbandArenaTeamRequest)(nil), // 44: v1.DisbandArenaTeamRequest + (*SetArenaTeamCaptainRequest)(nil), // 45: v1.SetArenaTeamCaptainRequest + (*SetArenaTeamNameRequest)(nil), // 46: v1.SetArenaTeamNameRequest + (*ArenaTeamStatsMember)(nil), // 47: v1.ArenaTeamStatsMember + (*SaveArenaTeamStatsRequest)(nil), // 48: v1.SaveArenaTeamStatsRequest + (*DeleteAllArenaTeamsRequest)(nil), // 49: v1.DeleteAllArenaTeamsRequest + (*ValidateArenaTeamCharacterDeleteRequest)(nil), // 50: v1.ValidateArenaTeamCharacterDeleteRequest + (*RemovePlayerFromArenaTeamsRequest)(nil), // 51: v1.RemovePlayerFromArenaTeamsRequest + (*ArenaTeamMutationResponse)(nil), // 52: v1.ArenaTeamMutationResponse + (*SavePlayerPositionRequest)(nil), // 53: v1.SavePlayerPositionRequest + (*SavePlayerPositionResponse)(nil), // 54: v1.SavePlayerPositionResponse + (*LfgDungeonRoute)(nil), // 55: v1.LfgDungeonRoute + (*RecordLfgDungeonRouteRequest)(nil), // 56: v1.RecordLfgDungeonRouteRequest + (*RecordLfgDungeonRouteResponse)(nil), // 57: v1.RecordLfgDungeonRouteResponse + (*ConfirmLfgDungeonRouteEnteredRequest)(nil), // 58: v1.ConfirmLfgDungeonRouteEnteredRequest + (*ConfirmLfgDungeonRouteEnteredResponse)(nil), // 59: v1.ConfirmLfgDungeonRouteEnteredResponse + (*ClearUnboundLfgDungeonRouteRequest)(nil), // 60: v1.ClearUnboundLfgDungeonRouteRequest + (*ClearUnboundLfgDungeonRouteResponse)(nil), // 61: v1.ClearUnboundLfgDungeonRouteResponse + (*GetLfgDungeonRouteRequest)(nil), // 62: v1.GetLfgDungeonRouteRequest + (*GetLfgDungeonRouteResponse)(nil), // 63: v1.GetLfgDungeonRouteResponse + (*GetFriendsListRequest)(nil), // 64: v1.GetFriendsListRequest + (*GetFriendsListResponse)(nil), // 65: v1.GetFriendsListResponse + (*AddFriendRequest)(nil), // 66: v1.AddFriendRequest + (*AddFriendResponse)(nil), // 67: v1.AddFriendResponse + (*AddRealIDFriendByEmailRequest)(nil), // 68: v1.AddRealIDFriendByEmailRequest + (*AddRealIDFriendByEmailResponse)(nil), // 69: v1.AddRealIDFriendByEmailResponse + (*AcceptRealIDFriendRequest)(nil), // 70: v1.AcceptRealIDFriendRequest + (*AcceptRealIDFriendResponse)(nil), // 71: v1.AcceptRealIDFriendResponse + (*AreRealIDFriendsRequest)(nil), // 72: v1.AreRealIDFriendsRequest + (*AreRealIDFriendsResponse)(nil), // 73: v1.AreRealIDFriendsResponse + (*RemoveFriendRequest)(nil), // 74: v1.RemoveFriendRequest + (*RemoveFriendResponse)(nil), // 75: v1.RemoveFriendResponse + (*SetFriendNoteRequest)(nil), // 76: v1.SetFriendNoteRequest + (*SetFriendNoteResponse)(nil), // 77: v1.SetFriendNoteResponse + (*AddIgnoreRequest)(nil), // 78: v1.AddIgnoreRequest + (*AddIgnoreResponse)(nil), // 79: v1.AddIgnoreResponse + (*RemoveIgnoreRequest)(nil), // 80: v1.RemoveIgnoreRequest + (*RemoveIgnoreResponse)(nil), // 81: v1.RemoveIgnoreResponse + (*NotifyStatusChangeRequest)(nil), // 82: v1.NotifyStatusChangeRequest + (*NotifyStatusChangeResponse)(nil), // 83: v1.NotifyStatusChangeResponse + (*GetOnlineCharactersRequest)(nil), // 84: v1.GetOnlineCharactersRequest + (*GetOnlineCharactersResponse)(nil), // 85: v1.GetOnlineCharactersResponse + (*WhoQueryResponse_WhoItem)(nil), // 86: v1.WhoQueryResponse.WhoItem + (*CharacterOnlineByNameResponse_Char)(nil), // 87: v1.CharacterOnlineByNameResponse.Char + (*CharacterByNameResponse_Char)(nil), // 88: v1.CharacterByNameResponse.Char + (*ShortCharactersDataByGUIDsResponse_ShortCharData)(nil), // 89: v1.ShortCharactersDataByGUIDsResponse.ShortCharData + (*GetFriendsListResponse_Friend)(nil), // 90: v1.GetFriendsListResponse.Friend + (*GetFriendsListResponse_IgnoredPlayer)(nil), // 91: v1.GetFriendsListResponse.IgnoredPlayer +} +var file_characters_proto_depIdxs = []int32{ + 6, // 0: v1.LogInCharacter.equipments:type_name -> v1.EquipmentDisplay + 5, // 1: v1.CharactersToLoginForAccountResponse.characters:type_name -> v1.LogInCharacter + 5, // 2: v1.CharactersToLoginByGUIDResponse.character:type_name -> v1.LogInCharacter + 12, // 3: v1.AccountDataForAccountResponse.accountData:type_name -> v1.AccountData + 86, // 4: v1.WhoQueryResponse.itemsToDisplay:type_name -> v1.WhoQueryResponse.WhoItem + 87, // 5: v1.CharacterOnlineByNameResponse.character:type_name -> v1.CharacterOnlineByNameResponse.Char + 88, // 6: v1.CharacterByNameResponse.character:type_name -> v1.CharacterByNameResponse.Char + 88, // 7: v1.CharacterByGUIDResponse.character:type_name -> v1.CharacterByNameResponse.Char + 89, // 8: v1.ShortCharactersDataByGUIDsResponse.characters:type_name -> v1.ShortCharactersDataByGUIDsResponse.ShortCharData + 1, // 9: v1.ArenaTeamQueueDataForRatedArenaResponse.status:type_name -> v1.ArenaTeamQueueDataForRatedArenaResponse.Status + 2, // 10: v1.CreateArenaTeamFromPetitionResponse.status:type_name -> v1.CreateArenaTeamFromPetitionResponse.Status + 30, // 11: v1.ArenaTeamData.members:type_name -> v1.ArenaTeamMemberData + 3, // 12: v1.GetArenaTeamResponse.status:type_name -> v1.GetArenaTeamResponse.Status + 31, // 13: v1.GetArenaTeamResponse.team:type_name -> v1.ArenaTeamData + 4, // 14: v1.GetArenaTeamPetitionResponse.status:type_name -> v1.GetArenaTeamPetitionResponse.Status + 34, // 15: v1.GetArenaTeamPetitionResponse.petition:type_name -> v1.ArenaTeamPetitionData + 0, // 16: v1.InviteArenaTeamMemberResponse.status:type_name -> v1.ArenaTeamMutationStatus + 31, // 17: v1.InviteArenaTeamMemberResponse.team:type_name -> v1.ArenaTeamData + 0, // 18: v1.AcceptArenaTeamInviteResponse.status:type_name -> v1.ArenaTeamMutationStatus + 31, // 19: v1.AcceptArenaTeamInviteResponse.team:type_name -> v1.ArenaTeamData + 47, // 20: v1.SaveArenaTeamStatsRequest.members:type_name -> v1.ArenaTeamStatsMember + 0, // 21: v1.ArenaTeamMutationResponse.status:type_name -> v1.ArenaTeamMutationStatus + 55, // 22: v1.RecordLfgDungeonRouteRequest.route:type_name -> v1.LfgDungeonRoute + 55, // 23: v1.ConfirmLfgDungeonRouteEnteredResponse.route:type_name -> v1.LfgDungeonRoute + 55, // 24: v1.GetLfgDungeonRouteResponse.route:type_name -> v1.LfgDungeonRoute + 90, // 25: v1.GetFriendsListResponse.friends:type_name -> v1.GetFriendsListResponse.Friend + 91, // 26: v1.GetFriendsListResponse.ignored:type_name -> v1.GetFriendsListResponse.IgnoredPlayer + 7, // 27: v1.CharactersService.CharactersToLoginForAccount:input_type -> v1.CharactersToLoginForAccountRequest + 9, // 28: v1.CharactersService.CharactersToLoginByGUID:input_type -> v1.CharactersToLoginByGUIDRequest + 11, // 29: v1.CharactersService.AccountDataForAccount:input_type -> v1.AccountDataForAccountRequest + 14, // 30: v1.CharactersService.UpdateAccountDataForAccount:input_type -> v1.UpdateAccountDataForAccountRequest + 16, // 31: v1.CharactersService.WhoQuery:input_type -> v1.WhoQueryRequest + 18, // 32: v1.CharactersService.CharacterOnlineByName:input_type -> v1.CharacterOnlineByNameRequest + 20, // 33: v1.CharactersService.CharacterByName:input_type -> v1.CharacterByNameRequest + 22, // 34: v1.CharactersService.CharacterByGUID:input_type -> v1.CharacterByGUIDRequest + 24, // 35: v1.CharactersService.ShortOnlineCharactersDataByGUIDs:input_type -> v1.ShortCharactersDataByGUIDsRequest + 26, // 36: v1.CharactersService.ArenaTeamQueueDataForRatedArena:input_type -> v1.ArenaTeamQueueDataForRatedArenaRequest + 32, // 37: v1.CharactersService.GetArenaTeam:input_type -> v1.GetArenaTeamRequest + 35, // 38: v1.CharactersService.GetArenaTeamPetition:input_type -> v1.GetArenaTeamPetitionRequest + 28, // 39: v1.CharactersService.CreateArenaTeamFromPetition:input_type -> v1.CreateArenaTeamFromPetitionRequest + 37, // 40: v1.CharactersService.InviteArenaTeamMember:input_type -> v1.InviteArenaTeamMemberRequest + 39, // 41: v1.CharactersService.AcceptArenaTeamInvite:input_type -> v1.AcceptArenaTeamInviteRequest + 41, // 42: v1.CharactersService.DeclineArenaTeamInvite:input_type -> v1.DeclineArenaTeamInviteRequest + 42, // 43: v1.CharactersService.AddArenaTeamMember:input_type -> v1.AddArenaTeamMemberRequest + 43, // 44: v1.CharactersService.RemoveArenaTeamMember:input_type -> v1.RemoveArenaTeamMemberRequest + 44, // 45: v1.CharactersService.DisbandArenaTeam:input_type -> v1.DisbandArenaTeamRequest + 45, // 46: v1.CharactersService.SetArenaTeamCaptain:input_type -> v1.SetArenaTeamCaptainRequest + 46, // 47: v1.CharactersService.SetArenaTeamName:input_type -> v1.SetArenaTeamNameRequest + 48, // 48: v1.CharactersService.SaveArenaTeamStats:input_type -> v1.SaveArenaTeamStatsRequest + 49, // 49: v1.CharactersService.DeleteAllArenaTeams:input_type -> v1.DeleteAllArenaTeamsRequest + 50, // 50: v1.CharactersService.ValidateArenaTeamCharacterDelete:input_type -> v1.ValidateArenaTeamCharacterDeleteRequest + 51, // 51: v1.CharactersService.RemovePlayerFromArenaTeams:input_type -> v1.RemovePlayerFromArenaTeamsRequest + 53, // 52: v1.CharactersService.SavePlayerPosition:input_type -> v1.SavePlayerPositionRequest + 56, // 53: v1.CharactersService.RecordLfgDungeonRoute:input_type -> v1.RecordLfgDungeonRouteRequest + 58, // 54: v1.CharactersService.ConfirmLfgDungeonRouteEntered:input_type -> v1.ConfirmLfgDungeonRouteEnteredRequest + 60, // 55: v1.CharactersService.ClearUnboundLfgDungeonRoute:input_type -> v1.ClearUnboundLfgDungeonRouteRequest + 62, // 56: v1.CharactersService.GetLfgDungeonRoute:input_type -> v1.GetLfgDungeonRouteRequest + 64, // 57: v1.CharactersService.GetFriendsList:input_type -> v1.GetFriendsListRequest + 66, // 58: v1.CharactersService.AddFriend:input_type -> v1.AddFriendRequest + 68, // 59: v1.CharactersService.AddRealIDFriendByEmail:input_type -> v1.AddRealIDFriendByEmailRequest + 70, // 60: v1.CharactersService.AcceptRealIDFriend:input_type -> v1.AcceptRealIDFriendRequest + 72, // 61: v1.CharactersService.AreRealIDFriends:input_type -> v1.AreRealIDFriendsRequest + 74, // 62: v1.CharactersService.RemoveFriend:input_type -> v1.RemoveFriendRequest + 76, // 63: v1.CharactersService.SetFriendNote:input_type -> v1.SetFriendNoteRequest + 78, // 64: v1.CharactersService.AddIgnore:input_type -> v1.AddIgnoreRequest + 80, // 65: v1.CharactersService.RemoveIgnore:input_type -> v1.RemoveIgnoreRequest + 82, // 66: v1.CharactersService.NotifyStatusChange:input_type -> v1.NotifyStatusChangeRequest + 84, // 67: v1.CharactersService.GetOnlineCharacters:input_type -> v1.GetOnlineCharactersRequest + 8, // 68: v1.CharactersService.CharactersToLoginForAccount:output_type -> v1.CharactersToLoginForAccountResponse + 10, // 69: v1.CharactersService.CharactersToLoginByGUID:output_type -> v1.CharactersToLoginByGUIDResponse + 13, // 70: v1.CharactersService.AccountDataForAccount:output_type -> v1.AccountDataForAccountResponse + 15, // 71: v1.CharactersService.UpdateAccountDataForAccount:output_type -> v1.UpdateAccountDataForAccountResponse + 17, // 72: v1.CharactersService.WhoQuery:output_type -> v1.WhoQueryResponse + 19, // 73: v1.CharactersService.CharacterOnlineByName:output_type -> v1.CharacterOnlineByNameResponse + 21, // 74: v1.CharactersService.CharacterByName:output_type -> v1.CharacterByNameResponse + 23, // 75: v1.CharactersService.CharacterByGUID:output_type -> v1.CharacterByGUIDResponse + 25, // 76: v1.CharactersService.ShortOnlineCharactersDataByGUIDs:output_type -> v1.ShortCharactersDataByGUIDsResponse + 27, // 77: v1.CharactersService.ArenaTeamQueueDataForRatedArena:output_type -> v1.ArenaTeamQueueDataForRatedArenaResponse + 33, // 78: v1.CharactersService.GetArenaTeam:output_type -> v1.GetArenaTeamResponse + 36, // 79: v1.CharactersService.GetArenaTeamPetition:output_type -> v1.GetArenaTeamPetitionResponse + 29, // 80: v1.CharactersService.CreateArenaTeamFromPetition:output_type -> v1.CreateArenaTeamFromPetitionResponse + 38, // 81: v1.CharactersService.InviteArenaTeamMember:output_type -> v1.InviteArenaTeamMemberResponse + 40, // 82: v1.CharactersService.AcceptArenaTeamInvite:output_type -> v1.AcceptArenaTeamInviteResponse + 52, // 83: v1.CharactersService.DeclineArenaTeamInvite:output_type -> v1.ArenaTeamMutationResponse + 52, // 84: v1.CharactersService.AddArenaTeamMember:output_type -> v1.ArenaTeamMutationResponse + 52, // 85: v1.CharactersService.RemoveArenaTeamMember:output_type -> v1.ArenaTeamMutationResponse + 52, // 86: v1.CharactersService.DisbandArenaTeam:output_type -> v1.ArenaTeamMutationResponse + 52, // 87: v1.CharactersService.SetArenaTeamCaptain:output_type -> v1.ArenaTeamMutationResponse + 52, // 88: v1.CharactersService.SetArenaTeamName:output_type -> v1.ArenaTeamMutationResponse + 52, // 89: v1.CharactersService.SaveArenaTeamStats:output_type -> v1.ArenaTeamMutationResponse + 52, // 90: v1.CharactersService.DeleteAllArenaTeams:output_type -> v1.ArenaTeamMutationResponse + 52, // 91: v1.CharactersService.ValidateArenaTeamCharacterDelete:output_type -> v1.ArenaTeamMutationResponse + 52, // 92: v1.CharactersService.RemovePlayerFromArenaTeams:output_type -> v1.ArenaTeamMutationResponse + 54, // 93: v1.CharactersService.SavePlayerPosition:output_type -> v1.SavePlayerPositionResponse + 57, // 94: v1.CharactersService.RecordLfgDungeonRoute:output_type -> v1.RecordLfgDungeonRouteResponse + 59, // 95: v1.CharactersService.ConfirmLfgDungeonRouteEntered:output_type -> v1.ConfirmLfgDungeonRouteEnteredResponse + 61, // 96: v1.CharactersService.ClearUnboundLfgDungeonRoute:output_type -> v1.ClearUnboundLfgDungeonRouteResponse + 63, // 97: v1.CharactersService.GetLfgDungeonRoute:output_type -> v1.GetLfgDungeonRouteResponse + 65, // 98: v1.CharactersService.GetFriendsList:output_type -> v1.GetFriendsListResponse + 67, // 99: v1.CharactersService.AddFriend:output_type -> v1.AddFriendResponse + 69, // 100: v1.CharactersService.AddRealIDFriendByEmail:output_type -> v1.AddRealIDFriendByEmailResponse + 71, // 101: v1.CharactersService.AcceptRealIDFriend:output_type -> v1.AcceptRealIDFriendResponse + 73, // 102: v1.CharactersService.AreRealIDFriends:output_type -> v1.AreRealIDFriendsResponse + 75, // 103: v1.CharactersService.RemoveFriend:output_type -> v1.RemoveFriendResponse + 77, // 104: v1.CharactersService.SetFriendNote:output_type -> v1.SetFriendNoteResponse + 79, // 105: v1.CharactersService.AddIgnore:output_type -> v1.AddIgnoreResponse + 81, // 106: v1.CharactersService.RemoveIgnore:output_type -> v1.RemoveIgnoreResponse + 83, // 107: v1.CharactersService.NotifyStatusChange:output_type -> v1.NotifyStatusChangeResponse + 85, // 108: v1.CharactersService.GetOnlineCharacters:output_type -> v1.GetOnlineCharactersResponse + 68, // [68:109] is the sub-list for method output_type + 27, // [27:68] is the sub-list for method input_type + 27, // [27:27] is the sub-list for extension type_name + 27, // [27:27] is the sub-list for extension extendee + 0, // [0:27] is the sub-list for field type_name +} + +func init() { file_characters_proto_init() } +func file_characters_proto_init() { + if File_characters_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_characters_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LogInCharacter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EquipmentDisplay); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CharactersToLoginForAccountRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CharactersToLoginForAccountResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CharactersToLoginByGUIDRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CharactersToLoginByGUIDResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AccountDataForAccountRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AccountData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AccountDataForAccountResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateAccountDataForAccountRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateAccountDataForAccountResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WhoQueryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WhoQueryResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CharacterOnlineByNameRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CharacterOnlineByNameResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CharacterByNameRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CharacterByNameResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CharacterByGUIDRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CharacterByGUIDResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ShortCharactersDataByGUIDsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ShortCharactersDataByGUIDsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ArenaTeamQueueDataForRatedArenaRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ArenaTeamQueueDataForRatedArenaResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateArenaTeamFromPetitionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateArenaTeamFromPetitionResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ArenaTeamMemberData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ArenaTeamData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetArenaTeamRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetArenaTeamResponse); i { case 0: return &v.state case 1: @@ -3746,8 +8922,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EquipmentDisplay); i { + file_characters_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ArenaTeamPetitionData); i { case 0: return &v.state case 1: @@ -3758,8 +8934,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CharactersToLoginForAccountRequest); i { + file_characters_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetArenaTeamPetitionRequest); i { case 0: return &v.state case 1: @@ -3770,8 +8946,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CharactersToLoginForAccountResponse); i { + file_characters_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetArenaTeamPetitionResponse); i { case 0: return &v.state case 1: @@ -3782,8 +8958,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CharactersToLoginByGUIDRequest); i { + file_characters_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InviteArenaTeamMemberRequest); i { case 0: return &v.state case 1: @@ -3794,8 +8970,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CharactersToLoginByGUIDResponse); i { + file_characters_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InviteArenaTeamMemberResponse); i { case 0: return &v.state case 1: @@ -3806,8 +8982,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AccountDataForAccountRequest); i { + file_characters_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AcceptArenaTeamInviteRequest); i { case 0: return &v.state case 1: @@ -3818,8 +8994,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AccountData); i { + file_characters_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AcceptArenaTeamInviteResponse); i { case 0: return &v.state case 1: @@ -3830,8 +9006,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AccountDataForAccountResponse); i { + file_characters_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeclineArenaTeamInviteRequest); i { case 0: return &v.state case 1: @@ -3842,8 +9018,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WhoQueryRequest); i { + file_characters_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddArenaTeamMemberRequest); i { case 0: return &v.state case 1: @@ -3854,8 +9030,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WhoQueryResponse); i { + file_characters_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveArenaTeamMemberRequest); i { case 0: return &v.state case 1: @@ -3866,8 +9042,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CharacterOnlineByNameRequest); i { + file_characters_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DisbandArenaTeamRequest); i { case 0: return &v.state case 1: @@ -3878,8 +9054,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CharacterOnlineByNameResponse); i { + file_characters_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetArenaTeamCaptainRequest); i { case 0: return &v.state case 1: @@ -3890,8 +9066,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CharacterByNameRequest); i { + file_characters_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetArenaTeamNameRequest); i { case 0: return &v.state case 1: @@ -3902,8 +9078,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CharacterByNameResponse); i { + file_characters_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ArenaTeamStatsMember); i { case 0: return &v.state case 1: @@ -3914,8 +9090,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ShortCharactersDataByGUIDsRequest); i { + file_characters_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SaveArenaTeamStatsRequest); i { case 0: return &v.state case 1: @@ -3926,8 +9102,8 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ShortCharactersDataByGUIDsResponse); i { + file_characters_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteAllArenaTeamsRequest); i { case 0: return &v.state case 1: @@ -3938,7 +9114,43 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateArenaTeamCharacterDeleteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemovePlayerFromArenaTeamsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ArenaTeamMutationResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SavePlayerPositionRequest); i { case 0: return &v.state @@ -3950,7 +9162,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SavePlayerPositionResponse); i { case 0: return &v.state @@ -3962,7 +9174,115 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LfgDungeonRoute); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RecordLfgDungeonRouteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RecordLfgDungeonRouteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConfirmLfgDungeonRouteEnteredRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConfirmLfgDungeonRouteEnteredResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClearUnboundLfgDungeonRouteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClearUnboundLfgDungeonRouteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLfgDungeonRouteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLfgDungeonRouteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetFriendsListRequest); i { case 0: return &v.state @@ -3974,7 +9294,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetFriendsListResponse); i { case 0: return &v.state @@ -3986,7 +9306,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AddFriendRequest); i { case 0: return &v.state @@ -3998,7 +9318,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AddFriendResponse); i { case 0: return &v.state @@ -4010,7 +9330,79 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddRealIDFriendByEmailRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddRealIDFriendByEmailResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AcceptRealIDFriendRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AcceptRealIDFriendResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AreRealIDFriendsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AreRealIDFriendsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_characters_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RemoveFriendRequest); i { case 0: return &v.state @@ -4022,7 +9414,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RemoveFriendResponse); i { case 0: return &v.state @@ -4034,7 +9426,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetFriendNoteRequest); i { case 0: return &v.state @@ -4046,7 +9438,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetFriendNoteResponse); i { case 0: return &v.state @@ -4058,7 +9450,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AddIgnoreRequest); i { case 0: return &v.state @@ -4070,7 +9462,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AddIgnoreResponse); i { case 0: return &v.state @@ -4082,7 +9474,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RemoveIgnoreRequest); i { case 0: return &v.state @@ -4094,7 +9486,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RemoveIgnoreResponse); i { case 0: return &v.state @@ -4106,7 +9498,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NotifyStatusChangeRequest); i { case 0: return &v.state @@ -4118,7 +9510,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NotifyStatusChangeResponse); i { case 0: return &v.state @@ -4130,7 +9522,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[79].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetOnlineCharactersRequest); i { case 0: return &v.state @@ -4142,7 +9534,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[80].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetOnlineCharactersResponse); i { case 0: return &v.state @@ -4154,7 +9546,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[81].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WhoQueryResponse_WhoItem); i { case 0: return &v.state @@ -4166,7 +9558,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[82].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CharacterOnlineByNameResponse_Char); i { case 0: return &v.state @@ -4178,7 +9570,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CharacterByNameResponse_Char); i { case 0: return &v.state @@ -4190,7 +9582,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ShortCharactersDataByGUIDsResponse_ShortCharData); i { case 0: return &v.state @@ -4202,7 +9594,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[85].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetFriendsListResponse_Friend); i { case 0: return &v.state @@ -4214,7 +9606,7 @@ func file_characters_proto_init() { return nil } } - file_characters_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + file_characters_proto_msgTypes[86].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetFriendsListResponse_IgnoredPlayer); i { case 0: return &v.state @@ -4232,13 +9624,14 @@ func file_characters_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_characters_proto_rawDesc, - NumEnums: 0, - NumMessages: 41, + NumEnums: 5, + NumMessages: 87, NumExtensions: 0, NumServices: 1, }, GoTypes: file_characters_proto_goTypes, DependencyIndexes: file_characters_proto_depIdxs, + EnumInfos: file_characters_proto_enumTypes, MessageInfos: file_characters_proto_msgTypes, }.Build() File_characters_proto = out.File diff --git a/gen/characters/pb/characters_grpc.pb.go b/gen/characters/pb/characters_grpc.pb.go index a70982b..94efda8 100644 --- a/gen/characters/pb/characters_grpc.pb.go +++ b/gen/characters/pb/characters_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v3.20.3 +// - protoc v3.21.12 // source: characters.proto package pb @@ -22,13 +22,38 @@ const ( CharactersService_CharactersToLoginForAccount_FullMethodName = "/v1.CharactersService/CharactersToLoginForAccount" CharactersService_CharactersToLoginByGUID_FullMethodName = "/v1.CharactersService/CharactersToLoginByGUID" CharactersService_AccountDataForAccount_FullMethodName = "/v1.CharactersService/AccountDataForAccount" + CharactersService_UpdateAccountDataForAccount_FullMethodName = "/v1.CharactersService/UpdateAccountDataForAccount" CharactersService_WhoQuery_FullMethodName = "/v1.CharactersService/WhoQuery" CharactersService_CharacterOnlineByName_FullMethodName = "/v1.CharactersService/CharacterOnlineByName" CharactersService_CharacterByName_FullMethodName = "/v1.CharactersService/CharacterByName" + CharactersService_CharacterByGUID_FullMethodName = "/v1.CharactersService/CharacterByGUID" CharactersService_ShortOnlineCharactersDataByGUIDs_FullMethodName = "/v1.CharactersService/ShortOnlineCharactersDataByGUIDs" + CharactersService_ArenaTeamQueueDataForRatedArena_FullMethodName = "/v1.CharactersService/ArenaTeamQueueDataForRatedArena" + CharactersService_GetArenaTeam_FullMethodName = "/v1.CharactersService/GetArenaTeam" + CharactersService_GetArenaTeamPetition_FullMethodName = "/v1.CharactersService/GetArenaTeamPetition" + CharactersService_CreateArenaTeamFromPetition_FullMethodName = "/v1.CharactersService/CreateArenaTeamFromPetition" + CharactersService_InviteArenaTeamMember_FullMethodName = "/v1.CharactersService/InviteArenaTeamMember" + CharactersService_AcceptArenaTeamInvite_FullMethodName = "/v1.CharactersService/AcceptArenaTeamInvite" + CharactersService_DeclineArenaTeamInvite_FullMethodName = "/v1.CharactersService/DeclineArenaTeamInvite" + CharactersService_AddArenaTeamMember_FullMethodName = "/v1.CharactersService/AddArenaTeamMember" + CharactersService_RemoveArenaTeamMember_FullMethodName = "/v1.CharactersService/RemoveArenaTeamMember" + CharactersService_DisbandArenaTeam_FullMethodName = "/v1.CharactersService/DisbandArenaTeam" + CharactersService_SetArenaTeamCaptain_FullMethodName = "/v1.CharactersService/SetArenaTeamCaptain" + CharactersService_SetArenaTeamName_FullMethodName = "/v1.CharactersService/SetArenaTeamName" + CharactersService_SaveArenaTeamStats_FullMethodName = "/v1.CharactersService/SaveArenaTeamStats" + CharactersService_DeleteAllArenaTeams_FullMethodName = "/v1.CharactersService/DeleteAllArenaTeams" + CharactersService_ValidateArenaTeamCharacterDelete_FullMethodName = "/v1.CharactersService/ValidateArenaTeamCharacterDelete" + CharactersService_RemovePlayerFromArenaTeams_FullMethodName = "/v1.CharactersService/RemovePlayerFromArenaTeams" CharactersService_SavePlayerPosition_FullMethodName = "/v1.CharactersService/SavePlayerPosition" + CharactersService_RecordLfgDungeonRoute_FullMethodName = "/v1.CharactersService/RecordLfgDungeonRoute" + CharactersService_ConfirmLfgDungeonRouteEntered_FullMethodName = "/v1.CharactersService/ConfirmLfgDungeonRouteEntered" + CharactersService_ClearUnboundLfgDungeonRoute_FullMethodName = "/v1.CharactersService/ClearUnboundLfgDungeonRoute" + CharactersService_GetLfgDungeonRoute_FullMethodName = "/v1.CharactersService/GetLfgDungeonRoute" CharactersService_GetFriendsList_FullMethodName = "/v1.CharactersService/GetFriendsList" CharactersService_AddFriend_FullMethodName = "/v1.CharactersService/AddFriend" + CharactersService_AddRealIDFriendByEmail_FullMethodName = "/v1.CharactersService/AddRealIDFriendByEmail" + CharactersService_AcceptRealIDFriend_FullMethodName = "/v1.CharactersService/AcceptRealIDFriend" + CharactersService_AreRealIDFriends_FullMethodName = "/v1.CharactersService/AreRealIDFriends" CharactersService_RemoveFriend_FullMethodName = "/v1.CharactersService/RemoveFriend" CharactersService_SetFriendNote_FullMethodName = "/v1.CharactersService/SetFriendNote" CharactersService_AddIgnore_FullMethodName = "/v1.CharactersService/AddIgnore" @@ -44,15 +69,40 @@ type CharactersServiceClient interface { CharactersToLoginForAccount(ctx context.Context, in *CharactersToLoginForAccountRequest, opts ...grpc.CallOption) (*CharactersToLoginForAccountResponse, error) CharactersToLoginByGUID(ctx context.Context, in *CharactersToLoginByGUIDRequest, opts ...grpc.CallOption) (*CharactersToLoginByGUIDResponse, error) AccountDataForAccount(ctx context.Context, in *AccountDataForAccountRequest, opts ...grpc.CallOption) (*AccountDataForAccountResponse, error) + UpdateAccountDataForAccount(ctx context.Context, in *UpdateAccountDataForAccountRequest, opts ...grpc.CallOption) (*UpdateAccountDataForAccountResponse, error) WhoQuery(ctx context.Context, in *WhoQueryRequest, opts ...grpc.CallOption) (*WhoQueryResponse, error) CharacterOnlineByName(ctx context.Context, in *CharacterOnlineByNameRequest, opts ...grpc.CallOption) (*CharacterOnlineByNameResponse, error) CharacterByName(ctx context.Context, in *CharacterByNameRequest, opts ...grpc.CallOption) (*CharacterByNameResponse, error) + CharacterByGUID(ctx context.Context, in *CharacterByGUIDRequest, opts ...grpc.CallOption) (*CharacterByGUIDResponse, error) ShortOnlineCharactersDataByGUIDs(ctx context.Context, in *ShortCharactersDataByGUIDsRequest, opts ...grpc.CallOption) (*ShortCharactersDataByGUIDsResponse, error) + ArenaTeamQueueDataForRatedArena(ctx context.Context, in *ArenaTeamQueueDataForRatedArenaRequest, opts ...grpc.CallOption) (*ArenaTeamQueueDataForRatedArenaResponse, error) + GetArenaTeam(ctx context.Context, in *GetArenaTeamRequest, opts ...grpc.CallOption) (*GetArenaTeamResponse, error) + GetArenaTeamPetition(ctx context.Context, in *GetArenaTeamPetitionRequest, opts ...grpc.CallOption) (*GetArenaTeamPetitionResponse, error) + CreateArenaTeamFromPetition(ctx context.Context, in *CreateArenaTeamFromPetitionRequest, opts ...grpc.CallOption) (*CreateArenaTeamFromPetitionResponse, error) + InviteArenaTeamMember(ctx context.Context, in *InviteArenaTeamMemberRequest, opts ...grpc.CallOption) (*InviteArenaTeamMemberResponse, error) + AcceptArenaTeamInvite(ctx context.Context, in *AcceptArenaTeamInviteRequest, opts ...grpc.CallOption) (*AcceptArenaTeamInviteResponse, error) + DeclineArenaTeamInvite(ctx context.Context, in *DeclineArenaTeamInviteRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) + AddArenaTeamMember(ctx context.Context, in *AddArenaTeamMemberRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) + RemoveArenaTeamMember(ctx context.Context, in *RemoveArenaTeamMemberRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) + DisbandArenaTeam(ctx context.Context, in *DisbandArenaTeamRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) + SetArenaTeamCaptain(ctx context.Context, in *SetArenaTeamCaptainRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) + SetArenaTeamName(ctx context.Context, in *SetArenaTeamNameRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) + SaveArenaTeamStats(ctx context.Context, in *SaveArenaTeamStatsRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) + DeleteAllArenaTeams(ctx context.Context, in *DeleteAllArenaTeamsRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) + ValidateArenaTeamCharacterDelete(ctx context.Context, in *ValidateArenaTeamCharacterDeleteRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) + RemovePlayerFromArenaTeams(ctx context.Context, in *RemovePlayerFromArenaTeamsRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) // Would effect only offline player. SavePlayerPosition(ctx context.Context, in *SavePlayerPositionRequest, opts ...grpc.CallOption) (*SavePlayerPositionResponse, error) + RecordLfgDungeonRoute(ctx context.Context, in *RecordLfgDungeonRouteRequest, opts ...grpc.CallOption) (*RecordLfgDungeonRouteResponse, error) + ConfirmLfgDungeonRouteEntered(ctx context.Context, in *ConfirmLfgDungeonRouteEnteredRequest, opts ...grpc.CallOption) (*ConfirmLfgDungeonRouteEnteredResponse, error) + ClearUnboundLfgDungeonRoute(ctx context.Context, in *ClearUnboundLfgDungeonRouteRequest, opts ...grpc.CallOption) (*ClearUnboundLfgDungeonRouteResponse, error) + GetLfgDungeonRoute(ctx context.Context, in *GetLfgDungeonRouteRequest, opts ...grpc.CallOption) (*GetLfgDungeonRouteResponse, error) // Friends and ignore list management GetFriendsList(ctx context.Context, in *GetFriendsListRequest, opts ...grpc.CallOption) (*GetFriendsListResponse, error) AddFriend(ctx context.Context, in *AddFriendRequest, opts ...grpc.CallOption) (*AddFriendResponse, error) + AddRealIDFriendByEmail(ctx context.Context, in *AddRealIDFriendByEmailRequest, opts ...grpc.CallOption) (*AddRealIDFriendByEmailResponse, error) + AcceptRealIDFriend(ctx context.Context, in *AcceptRealIDFriendRequest, opts ...grpc.CallOption) (*AcceptRealIDFriendResponse, error) + AreRealIDFriends(ctx context.Context, in *AreRealIDFriendsRequest, opts ...grpc.CallOption) (*AreRealIDFriendsResponse, error) RemoveFriend(ctx context.Context, in *RemoveFriendRequest, opts ...grpc.CallOption) (*RemoveFriendResponse, error) SetFriendNote(ctx context.Context, in *SetFriendNoteRequest, opts ...grpc.CallOption) (*SetFriendNoteResponse, error) AddIgnore(ctx context.Context, in *AddIgnoreRequest, opts ...grpc.CallOption) (*AddIgnoreResponse, error) @@ -97,6 +147,15 @@ func (c *charactersServiceClient) AccountDataForAccount(ctx context.Context, in return out, nil } +func (c *charactersServiceClient) UpdateAccountDataForAccount(ctx context.Context, in *UpdateAccountDataForAccountRequest, opts ...grpc.CallOption) (*UpdateAccountDataForAccountResponse, error) { + out := new(UpdateAccountDataForAccountResponse) + err := c.cc.Invoke(ctx, CharactersService_UpdateAccountDataForAccount_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *charactersServiceClient) WhoQuery(ctx context.Context, in *WhoQueryRequest, opts ...grpc.CallOption) (*WhoQueryResponse, error) { out := new(WhoQueryResponse) err := c.cc.Invoke(ctx, CharactersService_WhoQuery_FullMethodName, in, out, opts...) @@ -124,6 +183,15 @@ func (c *charactersServiceClient) CharacterByName(ctx context.Context, in *Chara return out, nil } +func (c *charactersServiceClient) CharacterByGUID(ctx context.Context, in *CharacterByGUIDRequest, opts ...grpc.CallOption) (*CharacterByGUIDResponse, error) { + out := new(CharacterByGUIDResponse) + err := c.cc.Invoke(ctx, CharactersService_CharacterByGUID_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *charactersServiceClient) ShortOnlineCharactersDataByGUIDs(ctx context.Context, in *ShortCharactersDataByGUIDsRequest, opts ...grpc.CallOption) (*ShortCharactersDataByGUIDsResponse, error) { out := new(ShortCharactersDataByGUIDsResponse) err := c.cc.Invoke(ctx, CharactersService_ShortOnlineCharactersDataByGUIDs_FullMethodName, in, out, opts...) @@ -133,6 +201,150 @@ func (c *charactersServiceClient) ShortOnlineCharactersDataByGUIDs(ctx context.C return out, nil } +func (c *charactersServiceClient) ArenaTeamQueueDataForRatedArena(ctx context.Context, in *ArenaTeamQueueDataForRatedArenaRequest, opts ...grpc.CallOption) (*ArenaTeamQueueDataForRatedArenaResponse, error) { + out := new(ArenaTeamQueueDataForRatedArenaResponse) + err := c.cc.Invoke(ctx, CharactersService_ArenaTeamQueueDataForRatedArena_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) GetArenaTeam(ctx context.Context, in *GetArenaTeamRequest, opts ...grpc.CallOption) (*GetArenaTeamResponse, error) { + out := new(GetArenaTeamResponse) + err := c.cc.Invoke(ctx, CharactersService_GetArenaTeam_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) GetArenaTeamPetition(ctx context.Context, in *GetArenaTeamPetitionRequest, opts ...grpc.CallOption) (*GetArenaTeamPetitionResponse, error) { + out := new(GetArenaTeamPetitionResponse) + err := c.cc.Invoke(ctx, CharactersService_GetArenaTeamPetition_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) CreateArenaTeamFromPetition(ctx context.Context, in *CreateArenaTeamFromPetitionRequest, opts ...grpc.CallOption) (*CreateArenaTeamFromPetitionResponse, error) { + out := new(CreateArenaTeamFromPetitionResponse) + err := c.cc.Invoke(ctx, CharactersService_CreateArenaTeamFromPetition_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) InviteArenaTeamMember(ctx context.Context, in *InviteArenaTeamMemberRequest, opts ...grpc.CallOption) (*InviteArenaTeamMemberResponse, error) { + out := new(InviteArenaTeamMemberResponse) + err := c.cc.Invoke(ctx, CharactersService_InviteArenaTeamMember_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) AcceptArenaTeamInvite(ctx context.Context, in *AcceptArenaTeamInviteRequest, opts ...grpc.CallOption) (*AcceptArenaTeamInviteResponse, error) { + out := new(AcceptArenaTeamInviteResponse) + err := c.cc.Invoke(ctx, CharactersService_AcceptArenaTeamInvite_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) DeclineArenaTeamInvite(ctx context.Context, in *DeclineArenaTeamInviteRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) { + out := new(ArenaTeamMutationResponse) + err := c.cc.Invoke(ctx, CharactersService_DeclineArenaTeamInvite_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) AddArenaTeamMember(ctx context.Context, in *AddArenaTeamMemberRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) { + out := new(ArenaTeamMutationResponse) + err := c.cc.Invoke(ctx, CharactersService_AddArenaTeamMember_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) RemoveArenaTeamMember(ctx context.Context, in *RemoveArenaTeamMemberRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) { + out := new(ArenaTeamMutationResponse) + err := c.cc.Invoke(ctx, CharactersService_RemoveArenaTeamMember_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) DisbandArenaTeam(ctx context.Context, in *DisbandArenaTeamRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) { + out := new(ArenaTeamMutationResponse) + err := c.cc.Invoke(ctx, CharactersService_DisbandArenaTeam_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) SetArenaTeamCaptain(ctx context.Context, in *SetArenaTeamCaptainRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) { + out := new(ArenaTeamMutationResponse) + err := c.cc.Invoke(ctx, CharactersService_SetArenaTeamCaptain_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) SetArenaTeamName(ctx context.Context, in *SetArenaTeamNameRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) { + out := new(ArenaTeamMutationResponse) + err := c.cc.Invoke(ctx, CharactersService_SetArenaTeamName_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) SaveArenaTeamStats(ctx context.Context, in *SaveArenaTeamStatsRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) { + out := new(ArenaTeamMutationResponse) + err := c.cc.Invoke(ctx, CharactersService_SaveArenaTeamStats_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) DeleteAllArenaTeams(ctx context.Context, in *DeleteAllArenaTeamsRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) { + out := new(ArenaTeamMutationResponse) + err := c.cc.Invoke(ctx, CharactersService_DeleteAllArenaTeams_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) ValidateArenaTeamCharacterDelete(ctx context.Context, in *ValidateArenaTeamCharacterDeleteRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) { + out := new(ArenaTeamMutationResponse) + err := c.cc.Invoke(ctx, CharactersService_ValidateArenaTeamCharacterDelete_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) RemovePlayerFromArenaTeams(ctx context.Context, in *RemovePlayerFromArenaTeamsRequest, opts ...grpc.CallOption) (*ArenaTeamMutationResponse, error) { + out := new(ArenaTeamMutationResponse) + err := c.cc.Invoke(ctx, CharactersService_RemovePlayerFromArenaTeams_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *charactersServiceClient) SavePlayerPosition(ctx context.Context, in *SavePlayerPositionRequest, opts ...grpc.CallOption) (*SavePlayerPositionResponse, error) { out := new(SavePlayerPositionResponse) err := c.cc.Invoke(ctx, CharactersService_SavePlayerPosition_FullMethodName, in, out, opts...) @@ -142,6 +354,42 @@ func (c *charactersServiceClient) SavePlayerPosition(ctx context.Context, in *Sa return out, nil } +func (c *charactersServiceClient) RecordLfgDungeonRoute(ctx context.Context, in *RecordLfgDungeonRouteRequest, opts ...grpc.CallOption) (*RecordLfgDungeonRouteResponse, error) { + out := new(RecordLfgDungeonRouteResponse) + err := c.cc.Invoke(ctx, CharactersService_RecordLfgDungeonRoute_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) ConfirmLfgDungeonRouteEntered(ctx context.Context, in *ConfirmLfgDungeonRouteEnteredRequest, opts ...grpc.CallOption) (*ConfirmLfgDungeonRouteEnteredResponse, error) { + out := new(ConfirmLfgDungeonRouteEnteredResponse) + err := c.cc.Invoke(ctx, CharactersService_ConfirmLfgDungeonRouteEntered_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) ClearUnboundLfgDungeonRoute(ctx context.Context, in *ClearUnboundLfgDungeonRouteRequest, opts ...grpc.CallOption) (*ClearUnboundLfgDungeonRouteResponse, error) { + out := new(ClearUnboundLfgDungeonRouteResponse) + err := c.cc.Invoke(ctx, CharactersService_ClearUnboundLfgDungeonRoute_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) GetLfgDungeonRoute(ctx context.Context, in *GetLfgDungeonRouteRequest, opts ...grpc.CallOption) (*GetLfgDungeonRouteResponse, error) { + out := new(GetLfgDungeonRouteResponse) + err := c.cc.Invoke(ctx, CharactersService_GetLfgDungeonRoute_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *charactersServiceClient) GetFriendsList(ctx context.Context, in *GetFriendsListRequest, opts ...grpc.CallOption) (*GetFriendsListResponse, error) { out := new(GetFriendsListResponse) err := c.cc.Invoke(ctx, CharactersService_GetFriendsList_FullMethodName, in, out, opts...) @@ -160,6 +408,33 @@ func (c *charactersServiceClient) AddFriend(ctx context.Context, in *AddFriendRe return out, nil } +func (c *charactersServiceClient) AddRealIDFriendByEmail(ctx context.Context, in *AddRealIDFriendByEmailRequest, opts ...grpc.CallOption) (*AddRealIDFriendByEmailResponse, error) { + out := new(AddRealIDFriendByEmailResponse) + err := c.cc.Invoke(ctx, CharactersService_AddRealIDFriendByEmail_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) AcceptRealIDFriend(ctx context.Context, in *AcceptRealIDFriendRequest, opts ...grpc.CallOption) (*AcceptRealIDFriendResponse, error) { + out := new(AcceptRealIDFriendResponse) + err := c.cc.Invoke(ctx, CharactersService_AcceptRealIDFriend_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *charactersServiceClient) AreRealIDFriends(ctx context.Context, in *AreRealIDFriendsRequest, opts ...grpc.CallOption) (*AreRealIDFriendsResponse, error) { + out := new(AreRealIDFriendsResponse) + err := c.cc.Invoke(ctx, CharactersService_AreRealIDFriends_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *charactersServiceClient) RemoveFriend(ctx context.Context, in *RemoveFriendRequest, opts ...grpc.CallOption) (*RemoveFriendResponse, error) { out := new(RemoveFriendResponse) err := c.cc.Invoke(ctx, CharactersService_RemoveFriend_FullMethodName, in, out, opts...) @@ -221,15 +496,40 @@ type CharactersServiceServer interface { CharactersToLoginForAccount(context.Context, *CharactersToLoginForAccountRequest) (*CharactersToLoginForAccountResponse, error) CharactersToLoginByGUID(context.Context, *CharactersToLoginByGUIDRequest) (*CharactersToLoginByGUIDResponse, error) AccountDataForAccount(context.Context, *AccountDataForAccountRequest) (*AccountDataForAccountResponse, error) + UpdateAccountDataForAccount(context.Context, *UpdateAccountDataForAccountRequest) (*UpdateAccountDataForAccountResponse, error) WhoQuery(context.Context, *WhoQueryRequest) (*WhoQueryResponse, error) CharacterOnlineByName(context.Context, *CharacterOnlineByNameRequest) (*CharacterOnlineByNameResponse, error) CharacterByName(context.Context, *CharacterByNameRequest) (*CharacterByNameResponse, error) + CharacterByGUID(context.Context, *CharacterByGUIDRequest) (*CharacterByGUIDResponse, error) ShortOnlineCharactersDataByGUIDs(context.Context, *ShortCharactersDataByGUIDsRequest) (*ShortCharactersDataByGUIDsResponse, error) + ArenaTeamQueueDataForRatedArena(context.Context, *ArenaTeamQueueDataForRatedArenaRequest) (*ArenaTeamQueueDataForRatedArenaResponse, error) + GetArenaTeam(context.Context, *GetArenaTeamRequest) (*GetArenaTeamResponse, error) + GetArenaTeamPetition(context.Context, *GetArenaTeamPetitionRequest) (*GetArenaTeamPetitionResponse, error) + CreateArenaTeamFromPetition(context.Context, *CreateArenaTeamFromPetitionRequest) (*CreateArenaTeamFromPetitionResponse, error) + InviteArenaTeamMember(context.Context, *InviteArenaTeamMemberRequest) (*InviteArenaTeamMemberResponse, error) + AcceptArenaTeamInvite(context.Context, *AcceptArenaTeamInviteRequest) (*AcceptArenaTeamInviteResponse, error) + DeclineArenaTeamInvite(context.Context, *DeclineArenaTeamInviteRequest) (*ArenaTeamMutationResponse, error) + AddArenaTeamMember(context.Context, *AddArenaTeamMemberRequest) (*ArenaTeamMutationResponse, error) + RemoveArenaTeamMember(context.Context, *RemoveArenaTeamMemberRequest) (*ArenaTeamMutationResponse, error) + DisbandArenaTeam(context.Context, *DisbandArenaTeamRequest) (*ArenaTeamMutationResponse, error) + SetArenaTeamCaptain(context.Context, *SetArenaTeamCaptainRequest) (*ArenaTeamMutationResponse, error) + SetArenaTeamName(context.Context, *SetArenaTeamNameRequest) (*ArenaTeamMutationResponse, error) + SaveArenaTeamStats(context.Context, *SaveArenaTeamStatsRequest) (*ArenaTeamMutationResponse, error) + DeleteAllArenaTeams(context.Context, *DeleteAllArenaTeamsRequest) (*ArenaTeamMutationResponse, error) + ValidateArenaTeamCharacterDelete(context.Context, *ValidateArenaTeamCharacterDeleteRequest) (*ArenaTeamMutationResponse, error) + RemovePlayerFromArenaTeams(context.Context, *RemovePlayerFromArenaTeamsRequest) (*ArenaTeamMutationResponse, error) // Would effect only offline player. SavePlayerPosition(context.Context, *SavePlayerPositionRequest) (*SavePlayerPositionResponse, error) + RecordLfgDungeonRoute(context.Context, *RecordLfgDungeonRouteRequest) (*RecordLfgDungeonRouteResponse, error) + ConfirmLfgDungeonRouteEntered(context.Context, *ConfirmLfgDungeonRouteEnteredRequest) (*ConfirmLfgDungeonRouteEnteredResponse, error) + ClearUnboundLfgDungeonRoute(context.Context, *ClearUnboundLfgDungeonRouteRequest) (*ClearUnboundLfgDungeonRouteResponse, error) + GetLfgDungeonRoute(context.Context, *GetLfgDungeonRouteRequest) (*GetLfgDungeonRouteResponse, error) // Friends and ignore list management GetFriendsList(context.Context, *GetFriendsListRequest) (*GetFriendsListResponse, error) AddFriend(context.Context, *AddFriendRequest) (*AddFriendResponse, error) + AddRealIDFriendByEmail(context.Context, *AddRealIDFriendByEmailRequest) (*AddRealIDFriendByEmailResponse, error) + AcceptRealIDFriend(context.Context, *AcceptRealIDFriendRequest) (*AcceptRealIDFriendResponse, error) + AreRealIDFriends(context.Context, *AreRealIDFriendsRequest) (*AreRealIDFriendsResponse, error) RemoveFriend(context.Context, *RemoveFriendRequest) (*RemoveFriendResponse, error) SetFriendNote(context.Context, *SetFriendNoteRequest) (*SetFriendNoteResponse, error) AddIgnore(context.Context, *AddIgnoreRequest) (*AddIgnoreResponse, error) @@ -253,6 +553,9 @@ func (UnimplementedCharactersServiceServer) CharactersToLoginByGUID(context.Cont func (UnimplementedCharactersServiceServer) AccountDataForAccount(context.Context, *AccountDataForAccountRequest) (*AccountDataForAccountResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AccountDataForAccount not implemented") } +func (UnimplementedCharactersServiceServer) UpdateAccountDataForAccount(context.Context, *UpdateAccountDataForAccountRequest) (*UpdateAccountDataForAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateAccountDataForAccount not implemented") +} func (UnimplementedCharactersServiceServer) WhoQuery(context.Context, *WhoQueryRequest) (*WhoQueryResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method WhoQuery not implemented") } @@ -262,18 +565,90 @@ func (UnimplementedCharactersServiceServer) CharacterOnlineByName(context.Contex func (UnimplementedCharactersServiceServer) CharacterByName(context.Context, *CharacterByNameRequest) (*CharacterByNameResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CharacterByName not implemented") } +func (UnimplementedCharactersServiceServer) CharacterByGUID(context.Context, *CharacterByGUIDRequest) (*CharacterByGUIDResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CharacterByGUID not implemented") +} func (UnimplementedCharactersServiceServer) ShortOnlineCharactersDataByGUIDs(context.Context, *ShortCharactersDataByGUIDsRequest) (*ShortCharactersDataByGUIDsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ShortOnlineCharactersDataByGUIDs not implemented") } +func (UnimplementedCharactersServiceServer) ArenaTeamQueueDataForRatedArena(context.Context, *ArenaTeamQueueDataForRatedArenaRequest) (*ArenaTeamQueueDataForRatedArenaResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ArenaTeamQueueDataForRatedArena not implemented") +} +func (UnimplementedCharactersServiceServer) GetArenaTeam(context.Context, *GetArenaTeamRequest) (*GetArenaTeamResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetArenaTeam not implemented") +} +func (UnimplementedCharactersServiceServer) GetArenaTeamPetition(context.Context, *GetArenaTeamPetitionRequest) (*GetArenaTeamPetitionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetArenaTeamPetition not implemented") +} +func (UnimplementedCharactersServiceServer) CreateArenaTeamFromPetition(context.Context, *CreateArenaTeamFromPetitionRequest) (*CreateArenaTeamFromPetitionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateArenaTeamFromPetition not implemented") +} +func (UnimplementedCharactersServiceServer) InviteArenaTeamMember(context.Context, *InviteArenaTeamMemberRequest) (*InviteArenaTeamMemberResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method InviteArenaTeamMember not implemented") +} +func (UnimplementedCharactersServiceServer) AcceptArenaTeamInvite(context.Context, *AcceptArenaTeamInviteRequest) (*AcceptArenaTeamInviteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AcceptArenaTeamInvite not implemented") +} +func (UnimplementedCharactersServiceServer) DeclineArenaTeamInvite(context.Context, *DeclineArenaTeamInviteRequest) (*ArenaTeamMutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeclineArenaTeamInvite not implemented") +} +func (UnimplementedCharactersServiceServer) AddArenaTeamMember(context.Context, *AddArenaTeamMemberRequest) (*ArenaTeamMutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddArenaTeamMember not implemented") +} +func (UnimplementedCharactersServiceServer) RemoveArenaTeamMember(context.Context, *RemoveArenaTeamMemberRequest) (*ArenaTeamMutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveArenaTeamMember not implemented") +} +func (UnimplementedCharactersServiceServer) DisbandArenaTeam(context.Context, *DisbandArenaTeamRequest) (*ArenaTeamMutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DisbandArenaTeam not implemented") +} +func (UnimplementedCharactersServiceServer) SetArenaTeamCaptain(context.Context, *SetArenaTeamCaptainRequest) (*ArenaTeamMutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetArenaTeamCaptain not implemented") +} +func (UnimplementedCharactersServiceServer) SetArenaTeamName(context.Context, *SetArenaTeamNameRequest) (*ArenaTeamMutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetArenaTeamName not implemented") +} +func (UnimplementedCharactersServiceServer) SaveArenaTeamStats(context.Context, *SaveArenaTeamStatsRequest) (*ArenaTeamMutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SaveArenaTeamStats not implemented") +} +func (UnimplementedCharactersServiceServer) DeleteAllArenaTeams(context.Context, *DeleteAllArenaTeamsRequest) (*ArenaTeamMutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteAllArenaTeams not implemented") +} +func (UnimplementedCharactersServiceServer) ValidateArenaTeamCharacterDelete(context.Context, *ValidateArenaTeamCharacterDeleteRequest) (*ArenaTeamMutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidateArenaTeamCharacterDelete not implemented") +} +func (UnimplementedCharactersServiceServer) RemovePlayerFromArenaTeams(context.Context, *RemovePlayerFromArenaTeamsRequest) (*ArenaTeamMutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemovePlayerFromArenaTeams not implemented") +} func (UnimplementedCharactersServiceServer) SavePlayerPosition(context.Context, *SavePlayerPositionRequest) (*SavePlayerPositionResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SavePlayerPosition not implemented") } +func (UnimplementedCharactersServiceServer) RecordLfgDungeonRoute(context.Context, *RecordLfgDungeonRouteRequest) (*RecordLfgDungeonRouteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RecordLfgDungeonRoute not implemented") +} +func (UnimplementedCharactersServiceServer) ConfirmLfgDungeonRouteEntered(context.Context, *ConfirmLfgDungeonRouteEnteredRequest) (*ConfirmLfgDungeonRouteEnteredResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConfirmLfgDungeonRouteEntered not implemented") +} +func (UnimplementedCharactersServiceServer) ClearUnboundLfgDungeonRoute(context.Context, *ClearUnboundLfgDungeonRouteRequest) (*ClearUnboundLfgDungeonRouteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ClearUnboundLfgDungeonRoute not implemented") +} +func (UnimplementedCharactersServiceServer) GetLfgDungeonRoute(context.Context, *GetLfgDungeonRouteRequest) (*GetLfgDungeonRouteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetLfgDungeonRoute not implemented") +} func (UnimplementedCharactersServiceServer) GetFriendsList(context.Context, *GetFriendsListRequest) (*GetFriendsListResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetFriendsList not implemented") } func (UnimplementedCharactersServiceServer) AddFriend(context.Context, *AddFriendRequest) (*AddFriendResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddFriend not implemented") } +func (UnimplementedCharactersServiceServer) AddRealIDFriendByEmail(context.Context, *AddRealIDFriendByEmailRequest) (*AddRealIDFriendByEmailResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddRealIDFriendByEmail not implemented") +} +func (UnimplementedCharactersServiceServer) AcceptRealIDFriend(context.Context, *AcceptRealIDFriendRequest) (*AcceptRealIDFriendResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AcceptRealIDFriend not implemented") +} +func (UnimplementedCharactersServiceServer) AreRealIDFriends(context.Context, *AreRealIDFriendsRequest) (*AreRealIDFriendsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AreRealIDFriends not implemented") +} func (UnimplementedCharactersServiceServer) RemoveFriend(context.Context, *RemoveFriendRequest) (*RemoveFriendResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RemoveFriend not implemented") } @@ -359,6 +734,24 @@ func _CharactersService_AccountDataForAccount_Handler(srv interface{}, ctx conte return interceptor(ctx, in, info, handler) } +func _CharactersService_UpdateAccountDataForAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateAccountDataForAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).UpdateAccountDataForAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_UpdateAccountDataForAccount_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).UpdateAccountDataForAccount(ctx, req.(*UpdateAccountDataForAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _CharactersService_WhoQuery_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(WhoQueryRequest) if err := dec(in); err != nil { @@ -413,6 +806,24 @@ func _CharactersService_CharacterByName_Handler(srv interface{}, ctx context.Con return interceptor(ctx, in, info, handler) } +func _CharactersService_CharacterByGUID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CharacterByGUIDRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).CharacterByGUID(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_CharacterByGUID_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).CharacterByGUID(ctx, req.(*CharacterByGUIDRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _CharactersService_ShortOnlineCharactersDataByGUIDs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ShortCharactersDataByGUIDsRequest) if err := dec(in); err != nil { @@ -431,178 +842,592 @@ func _CharactersService_ShortOnlineCharactersDataByGUIDs_Handler(srv interface{} return interceptor(ctx, in, info, handler) } -func _CharactersService_SavePlayerPosition_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SavePlayerPositionRequest) +func _CharactersService_ArenaTeamQueueDataForRatedArena_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ArenaTeamQueueDataForRatedArenaRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(CharactersServiceServer).SavePlayerPosition(ctx, in) + return srv.(CharactersServiceServer).ArenaTeamQueueDataForRatedArena(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: CharactersService_SavePlayerPosition_FullMethodName, + FullMethod: CharactersService_ArenaTeamQueueDataForRatedArena_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CharactersServiceServer).SavePlayerPosition(ctx, req.(*SavePlayerPositionRequest)) + return srv.(CharactersServiceServer).ArenaTeamQueueDataForRatedArena(ctx, req.(*ArenaTeamQueueDataForRatedArenaRequest)) } return interceptor(ctx, in, info, handler) } -func _CharactersService_GetFriendsList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetFriendsListRequest) +func _CharactersService_GetArenaTeam_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetArenaTeamRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(CharactersServiceServer).GetFriendsList(ctx, in) + return srv.(CharactersServiceServer).GetArenaTeam(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: CharactersService_GetFriendsList_FullMethodName, + FullMethod: CharactersService_GetArenaTeam_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CharactersServiceServer).GetFriendsList(ctx, req.(*GetFriendsListRequest)) + return srv.(CharactersServiceServer).GetArenaTeam(ctx, req.(*GetArenaTeamRequest)) } return interceptor(ctx, in, info, handler) } -func _CharactersService_AddFriend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AddFriendRequest) +func _CharactersService_GetArenaTeamPetition_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetArenaTeamPetitionRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(CharactersServiceServer).AddFriend(ctx, in) + return srv.(CharactersServiceServer).GetArenaTeamPetition(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: CharactersService_AddFriend_FullMethodName, + FullMethod: CharactersService_GetArenaTeamPetition_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CharactersServiceServer).AddFriend(ctx, req.(*AddFriendRequest)) + return srv.(CharactersServiceServer).GetArenaTeamPetition(ctx, req.(*GetArenaTeamPetitionRequest)) } return interceptor(ctx, in, info, handler) } -func _CharactersService_RemoveFriend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RemoveFriendRequest) +func _CharactersService_CreateArenaTeamFromPetition_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateArenaTeamFromPetitionRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(CharactersServiceServer).RemoveFriend(ctx, in) + return srv.(CharactersServiceServer).CreateArenaTeamFromPetition(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: CharactersService_RemoveFriend_FullMethodName, + FullMethod: CharactersService_CreateArenaTeamFromPetition_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CharactersServiceServer).RemoveFriend(ctx, req.(*RemoveFriendRequest)) + return srv.(CharactersServiceServer).CreateArenaTeamFromPetition(ctx, req.(*CreateArenaTeamFromPetitionRequest)) } return interceptor(ctx, in, info, handler) } -func _CharactersService_SetFriendNote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetFriendNoteRequest) +func _CharactersService_InviteArenaTeamMember_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InviteArenaTeamMemberRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(CharactersServiceServer).SetFriendNote(ctx, in) + return srv.(CharactersServiceServer).InviteArenaTeamMember(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: CharactersService_SetFriendNote_FullMethodName, + FullMethod: CharactersService_InviteArenaTeamMember_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CharactersServiceServer).SetFriendNote(ctx, req.(*SetFriendNoteRequest)) + return srv.(CharactersServiceServer).InviteArenaTeamMember(ctx, req.(*InviteArenaTeamMemberRequest)) } return interceptor(ctx, in, info, handler) } -func _CharactersService_AddIgnore_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AddIgnoreRequest) +func _CharactersService_AcceptArenaTeamInvite_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AcceptArenaTeamInviteRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(CharactersServiceServer).AddIgnore(ctx, in) + return srv.(CharactersServiceServer).AcceptArenaTeamInvite(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: CharactersService_AddIgnore_FullMethodName, + FullMethod: CharactersService_AcceptArenaTeamInvite_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CharactersServiceServer).AddIgnore(ctx, req.(*AddIgnoreRequest)) + return srv.(CharactersServiceServer).AcceptArenaTeamInvite(ctx, req.(*AcceptArenaTeamInviteRequest)) } return interceptor(ctx, in, info, handler) } -func _CharactersService_RemoveIgnore_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RemoveIgnoreRequest) +func _CharactersService_DeclineArenaTeamInvite_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeclineArenaTeamInviteRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(CharactersServiceServer).RemoveIgnore(ctx, in) + return srv.(CharactersServiceServer).DeclineArenaTeamInvite(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: CharactersService_RemoveIgnore_FullMethodName, + FullMethod: CharactersService_DeclineArenaTeamInvite_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CharactersServiceServer).RemoveIgnore(ctx, req.(*RemoveIgnoreRequest)) + return srv.(CharactersServiceServer).DeclineArenaTeamInvite(ctx, req.(*DeclineArenaTeamInviteRequest)) } return interceptor(ctx, in, info, handler) } -func _CharactersService_NotifyStatusChange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(NotifyStatusChangeRequest) +func _CharactersService_AddArenaTeamMember_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddArenaTeamMemberRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(CharactersServiceServer).NotifyStatusChange(ctx, in) + return srv.(CharactersServiceServer).AddArenaTeamMember(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: CharactersService_NotifyStatusChange_FullMethodName, + FullMethod: CharactersService_AddArenaTeamMember_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CharactersServiceServer).NotifyStatusChange(ctx, req.(*NotifyStatusChangeRequest)) + return srv.(CharactersServiceServer).AddArenaTeamMember(ctx, req.(*AddArenaTeamMemberRequest)) } return interceptor(ctx, in, info, handler) } -func _CharactersService_GetOnlineCharacters_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetOnlineCharactersRequest) +func _CharactersService_RemoveArenaTeamMember_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveArenaTeamMemberRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(CharactersServiceServer).GetOnlineCharacters(ctx, in) + return srv.(CharactersServiceServer).RemoveArenaTeamMember(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: CharactersService_GetOnlineCharacters_FullMethodName, + FullMethod: CharactersService_RemoveArenaTeamMember_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CharactersServiceServer).GetOnlineCharacters(ctx, req.(*GetOnlineCharactersRequest)) + return srv.(CharactersServiceServer).RemoveArenaTeamMember(ctx, req.(*RemoveArenaTeamMemberRequest)) } return interceptor(ctx, in, info, handler) } -// CharactersService_ServiceDesc is the grpc.ServiceDesc for CharactersService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var CharactersService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "v1.CharactersService", - HandlerType: (*CharactersServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "CharactersToLoginForAccount", - Handler: _CharactersService_CharactersToLoginForAccount_Handler, +func _CharactersService_DisbandArenaTeam_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DisbandArenaTeamRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).DisbandArenaTeam(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_DisbandArenaTeam_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).DisbandArenaTeam(ctx, req.(*DisbandArenaTeamRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_SetArenaTeamCaptain_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetArenaTeamCaptainRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).SetArenaTeamCaptain(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_SetArenaTeamCaptain_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).SetArenaTeamCaptain(ctx, req.(*SetArenaTeamCaptainRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_SetArenaTeamName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetArenaTeamNameRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).SetArenaTeamName(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_SetArenaTeamName_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).SetArenaTeamName(ctx, req.(*SetArenaTeamNameRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_SaveArenaTeamStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SaveArenaTeamStatsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).SaveArenaTeamStats(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_SaveArenaTeamStats_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).SaveArenaTeamStats(ctx, req.(*SaveArenaTeamStatsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_DeleteAllArenaTeams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteAllArenaTeamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).DeleteAllArenaTeams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_DeleteAllArenaTeams_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).DeleteAllArenaTeams(ctx, req.(*DeleteAllArenaTeamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_ValidateArenaTeamCharacterDelete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ValidateArenaTeamCharacterDeleteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).ValidateArenaTeamCharacterDelete(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_ValidateArenaTeamCharacterDelete_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).ValidateArenaTeamCharacterDelete(ctx, req.(*ValidateArenaTeamCharacterDeleteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_RemovePlayerFromArenaTeams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemovePlayerFromArenaTeamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).RemovePlayerFromArenaTeams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_RemovePlayerFromArenaTeams_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).RemovePlayerFromArenaTeams(ctx, req.(*RemovePlayerFromArenaTeamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_SavePlayerPosition_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SavePlayerPositionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).SavePlayerPosition(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_SavePlayerPosition_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).SavePlayerPosition(ctx, req.(*SavePlayerPositionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_RecordLfgDungeonRoute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RecordLfgDungeonRouteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).RecordLfgDungeonRoute(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_RecordLfgDungeonRoute_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).RecordLfgDungeonRoute(ctx, req.(*RecordLfgDungeonRouteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_ConfirmLfgDungeonRouteEntered_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConfirmLfgDungeonRouteEnteredRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).ConfirmLfgDungeonRouteEntered(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_ConfirmLfgDungeonRouteEntered_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).ConfirmLfgDungeonRouteEntered(ctx, req.(*ConfirmLfgDungeonRouteEnteredRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_ClearUnboundLfgDungeonRoute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ClearUnboundLfgDungeonRouteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).ClearUnboundLfgDungeonRoute(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_ClearUnboundLfgDungeonRoute_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).ClearUnboundLfgDungeonRoute(ctx, req.(*ClearUnboundLfgDungeonRouteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_GetLfgDungeonRoute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetLfgDungeonRouteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).GetLfgDungeonRoute(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_GetLfgDungeonRoute_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).GetLfgDungeonRoute(ctx, req.(*GetLfgDungeonRouteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_GetFriendsList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetFriendsListRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).GetFriendsList(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_GetFriendsList_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).GetFriendsList(ctx, req.(*GetFriendsListRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_AddFriend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddFriendRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).AddFriend(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_AddFriend_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).AddFriend(ctx, req.(*AddFriendRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_AddRealIDFriendByEmail_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddRealIDFriendByEmailRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).AddRealIDFriendByEmail(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_AddRealIDFriendByEmail_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).AddRealIDFriendByEmail(ctx, req.(*AddRealIDFriendByEmailRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_AcceptRealIDFriend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AcceptRealIDFriendRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).AcceptRealIDFriend(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_AcceptRealIDFriend_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).AcceptRealIDFriend(ctx, req.(*AcceptRealIDFriendRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_AreRealIDFriends_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AreRealIDFriendsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).AreRealIDFriends(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_AreRealIDFriends_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).AreRealIDFriends(ctx, req.(*AreRealIDFriendsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_RemoveFriend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveFriendRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).RemoveFriend(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_RemoveFriend_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).RemoveFriend(ctx, req.(*RemoveFriendRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_SetFriendNote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetFriendNoteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).SetFriendNote(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_SetFriendNote_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).SetFriendNote(ctx, req.(*SetFriendNoteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_AddIgnore_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddIgnoreRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).AddIgnore(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_AddIgnore_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).AddIgnore(ctx, req.(*AddIgnoreRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_RemoveIgnore_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveIgnoreRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).RemoveIgnore(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_RemoveIgnore_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).RemoveIgnore(ctx, req.(*RemoveIgnoreRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_NotifyStatusChange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NotifyStatusChangeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).NotifyStatusChange(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_NotifyStatusChange_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).NotifyStatusChange(ctx, req.(*NotifyStatusChangeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CharactersService_GetOnlineCharacters_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetOnlineCharactersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CharactersServiceServer).GetOnlineCharacters(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CharactersService_GetOnlineCharacters_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CharactersServiceServer).GetOnlineCharacters(ctx, req.(*GetOnlineCharactersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// CharactersService_ServiceDesc is the grpc.ServiceDesc for CharactersService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var CharactersService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "v1.CharactersService", + HandlerType: (*CharactersServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CharactersToLoginForAccount", + Handler: _CharactersService_CharactersToLoginForAccount_Handler, }, { MethodName: "CharactersToLoginByGUID", @@ -612,6 +1437,10 @@ var CharactersService_ServiceDesc = grpc.ServiceDesc{ MethodName: "AccountDataForAccount", Handler: _CharactersService_AccountDataForAccount_Handler, }, + { + MethodName: "UpdateAccountDataForAccount", + Handler: _CharactersService_UpdateAccountDataForAccount_Handler, + }, { MethodName: "WhoQuery", Handler: _CharactersService_WhoQuery_Handler, @@ -624,14 +1453,98 @@ var CharactersService_ServiceDesc = grpc.ServiceDesc{ MethodName: "CharacterByName", Handler: _CharactersService_CharacterByName_Handler, }, + { + MethodName: "CharacterByGUID", + Handler: _CharactersService_CharacterByGUID_Handler, + }, { MethodName: "ShortOnlineCharactersDataByGUIDs", Handler: _CharactersService_ShortOnlineCharactersDataByGUIDs_Handler, }, + { + MethodName: "ArenaTeamQueueDataForRatedArena", + Handler: _CharactersService_ArenaTeamQueueDataForRatedArena_Handler, + }, + { + MethodName: "GetArenaTeam", + Handler: _CharactersService_GetArenaTeam_Handler, + }, + { + MethodName: "GetArenaTeamPetition", + Handler: _CharactersService_GetArenaTeamPetition_Handler, + }, + { + MethodName: "CreateArenaTeamFromPetition", + Handler: _CharactersService_CreateArenaTeamFromPetition_Handler, + }, + { + MethodName: "InviteArenaTeamMember", + Handler: _CharactersService_InviteArenaTeamMember_Handler, + }, + { + MethodName: "AcceptArenaTeamInvite", + Handler: _CharactersService_AcceptArenaTeamInvite_Handler, + }, + { + MethodName: "DeclineArenaTeamInvite", + Handler: _CharactersService_DeclineArenaTeamInvite_Handler, + }, + { + MethodName: "AddArenaTeamMember", + Handler: _CharactersService_AddArenaTeamMember_Handler, + }, + { + MethodName: "RemoveArenaTeamMember", + Handler: _CharactersService_RemoveArenaTeamMember_Handler, + }, + { + MethodName: "DisbandArenaTeam", + Handler: _CharactersService_DisbandArenaTeam_Handler, + }, + { + MethodName: "SetArenaTeamCaptain", + Handler: _CharactersService_SetArenaTeamCaptain_Handler, + }, + { + MethodName: "SetArenaTeamName", + Handler: _CharactersService_SetArenaTeamName_Handler, + }, + { + MethodName: "SaveArenaTeamStats", + Handler: _CharactersService_SaveArenaTeamStats_Handler, + }, + { + MethodName: "DeleteAllArenaTeams", + Handler: _CharactersService_DeleteAllArenaTeams_Handler, + }, + { + MethodName: "ValidateArenaTeamCharacterDelete", + Handler: _CharactersService_ValidateArenaTeamCharacterDelete_Handler, + }, + { + MethodName: "RemovePlayerFromArenaTeams", + Handler: _CharactersService_RemovePlayerFromArenaTeams_Handler, + }, { MethodName: "SavePlayerPosition", Handler: _CharactersService_SavePlayerPosition_Handler, }, + { + MethodName: "RecordLfgDungeonRoute", + Handler: _CharactersService_RecordLfgDungeonRoute_Handler, + }, + { + MethodName: "ConfirmLfgDungeonRouteEntered", + Handler: _CharactersService_ConfirmLfgDungeonRouteEntered_Handler, + }, + { + MethodName: "ClearUnboundLfgDungeonRoute", + Handler: _CharactersService_ClearUnboundLfgDungeonRoute_Handler, + }, + { + MethodName: "GetLfgDungeonRoute", + Handler: _CharactersService_GetLfgDungeonRoute_Handler, + }, { MethodName: "GetFriendsList", Handler: _CharactersService_GetFriendsList_Handler, @@ -640,6 +1553,18 @@ var CharactersService_ServiceDesc = grpc.ServiceDesc{ MethodName: "AddFriend", Handler: _CharactersService_AddFriend_Handler, }, + { + MethodName: "AddRealIDFriendByEmail", + Handler: _CharactersService_AddRealIDFriendByEmail_Handler, + }, + { + MethodName: "AcceptRealIDFriend", + Handler: _CharactersService_AcceptRealIDFriend_Handler, + }, + { + MethodName: "AreRealIDFriends", + Handler: _CharactersService_AreRealIDFriends_Handler, + }, { MethodName: "RemoveFriend", Handler: _CharactersService_RemoveFriend_Handler, diff --git a/gen/chat/gen/chat/pb/chat.pb.go b/gen/chat/gen/chat/pb/chat.pb.go index 5d10c6f..226c5cc 100644 --- a/gen/chat/gen/chat/pb/chat.pb.go +++ b/gen/chat/gen/chat/pb/chat.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v3.20.3 +// protoc v3.21.12 // source: chat.proto package pb @@ -20,11 +20,61 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type TeamID int32 + +const ( + TeamID_TEAM_ALLIANCE TeamID = 0 + TeamID_TEAM_HORDE TeamID = 1 + TeamID_TEAM_NEUTRAL TeamID = 2 +) + +// Enum value maps for TeamID. +var ( + TeamID_name = map[int32]string{ + 0: "TEAM_ALLIANCE", + 1: "TEAM_HORDE", + 2: "TEAM_NEUTRAL", + } + TeamID_value = map[string]int32{ + "TEAM_ALLIANCE": 0, + "TEAM_HORDE": 1, + "TEAM_NEUTRAL": 2, + } +) + +func (x TeamID) Enum() *TeamID { + p := new(TeamID) + *p = x + return p +} + +func (x TeamID) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (TeamID) Descriptor() protoreflect.EnumDescriptor { + return file_chat_proto_enumTypes[0].Descriptor() +} + +func (TeamID) Type() protoreflect.EnumType { + return &file_chat_proto_enumTypes[0] +} + +func (x TeamID) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use TeamID.Descriptor instead. +func (TeamID) EnumDescriptor() ([]byte, []int) { + return file_chat_proto_rawDescGZIP(), []int{0} +} + type SendWhisperMessageResponse_Status int32 const ( - SendWhisperMessageResponse_Ok SendWhisperMessageResponse_Status = 0 - SendWhisperMessageResponse_CharacterNotFound SendWhisperMessageResponse_Status = 2 + SendWhisperMessageResponse_Ok SendWhisperMessageResponse_Status = 0 + SendWhisperMessageResponse_CharacterNotFound SendWhisperMessageResponse_Status = 2 + SendWhisperMessageResponse_CharacterAmbiguous SendWhisperMessageResponse_Status = 3 ) // Enum value maps for SendWhisperMessageResponse_Status. @@ -32,10 +82,12 @@ var ( SendWhisperMessageResponse_Status_name = map[int32]string{ 0: "Ok", 2: "CharacterNotFound", + 3: "CharacterAmbiguous", } SendWhisperMessageResponse_Status_value = map[string]int32{ - "Ok": 0, - "CharacterNotFound": 2, + "Ok": 0, + "CharacterNotFound": 2, + "CharacterAmbiguous": 3, } ) @@ -50,11 +102,11 @@ func (x SendWhisperMessageResponse_Status) String() string { } func (SendWhisperMessageResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[0].Descriptor() + return file_chat_proto_enumTypes[1].Descriptor() } func (SendWhisperMessageResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[0] + return &file_chat_proto_enumTypes[1] } func (x SendWhisperMessageResponse_Status) Number() protoreflect.EnumNumber { @@ -108,11 +160,11 @@ func (x JoinChannelResponse_Status) String() string { } func (JoinChannelResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[1].Descriptor() + return file_chat_proto_enumTypes[2].Descriptor() } func (JoinChannelResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[1] + return &file_chat_proto_enumTypes[2] } func (x JoinChannelResponse_Status) Number() protoreflect.EnumNumber { @@ -154,11 +206,11 @@ func (x LeaveChannelResponse_Status) String() string { } func (LeaveChannelResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[2].Descriptor() + return file_chat_proto_enumTypes[3].Descriptor() } func (LeaveChannelResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[2] + return &file_chat_proto_enumTypes[3] } func (x LeaveChannelResponse_Status) Number() protoreflect.EnumNumber { @@ -206,11 +258,11 @@ func (x SendChannelMessageResponse_Status) String() string { } func (SendChannelMessageResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[3].Descriptor() + return file_chat_proto_enumTypes[4].Descriptor() } func (SendChannelMessageResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[3] + return &file_chat_proto_enumTypes[4] } func (x SendChannelMessageResponse_Status) Number() protoreflect.EnumNumber { @@ -252,11 +304,11 @@ func (x GetChannelListResponse_Status) String() string { } func (GetChannelListResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[4].Descriptor() + return file_chat_proto_enumTypes[5].Descriptor() } func (GetChannelListResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[4] + return &file_chat_proto_enumTypes[5] } func (x GetChannelListResponse_Status) Number() protoreflect.EnumNumber { @@ -304,11 +356,11 @@ func (x KickFromChannelResponse_Status) String() string { } func (KickFromChannelResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[5].Descriptor() + return file_chat_proto_enumTypes[6].Descriptor() } func (KickFromChannelResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[5] + return &file_chat_proto_enumTypes[6] } func (x KickFromChannelResponse_Status) Number() protoreflect.EnumNumber { @@ -356,11 +408,11 @@ func (x BanFromChannelResponse_Status) String() string { } func (BanFromChannelResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[6].Descriptor() + return file_chat_proto_enumTypes[7].Descriptor() } func (BanFromChannelResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[6] + return &file_chat_proto_enumTypes[7] } func (x BanFromChannelResponse_Status) Number() protoreflect.EnumNumber { @@ -408,11 +460,11 @@ func (x UnbanFromChannelResponse_Status) String() string { } func (UnbanFromChannelResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[7].Descriptor() + return file_chat_proto_enumTypes[8].Descriptor() } func (UnbanFromChannelResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[7] + return &file_chat_proto_enumTypes[8] } func (x UnbanFromChannelResponse_Status) Number() protoreflect.EnumNumber { @@ -460,11 +512,11 @@ func (x SetChannelModeratorResponse_Status) String() string { } func (SetChannelModeratorResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[8].Descriptor() + return file_chat_proto_enumTypes[9].Descriptor() } func (SetChannelModeratorResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[8] + return &file_chat_proto_enumTypes[9] } func (x SetChannelModeratorResponse_Status) Number() protoreflect.EnumNumber { @@ -512,11 +564,11 @@ func (x UnsetChannelModeratorResponse_Status) String() string { } func (UnsetChannelModeratorResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[9].Descriptor() + return file_chat_proto_enumTypes[10].Descriptor() } func (UnsetChannelModeratorResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[9] + return &file_chat_proto_enumTypes[10] } func (x UnsetChannelModeratorResponse_Status) Number() protoreflect.EnumNumber { @@ -564,11 +616,11 @@ func (x SetChannelMuteResponse_Status) String() string { } func (SetChannelMuteResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[10].Descriptor() + return file_chat_proto_enumTypes[11].Descriptor() } func (SetChannelMuteResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[10] + return &file_chat_proto_enumTypes[11] } func (x SetChannelMuteResponse_Status) Number() protoreflect.EnumNumber { @@ -616,11 +668,11 @@ func (x UnsetChannelMuteResponse_Status) String() string { } func (UnsetChannelMuteResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[11].Descriptor() + return file_chat_proto_enumTypes[12].Descriptor() } func (UnsetChannelMuteResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[11] + return &file_chat_proto_enumTypes[12] } func (x UnsetChannelMuteResponse_Status) Number() protoreflect.EnumNumber { @@ -668,11 +720,11 @@ func (x SetChannelOwnerResponse_Status) String() string { } func (SetChannelOwnerResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[12].Descriptor() + return file_chat_proto_enumTypes[13].Descriptor() } func (SetChannelOwnerResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[12] + return &file_chat_proto_enumTypes[13] } func (x SetChannelOwnerResponse_Status) Number() protoreflect.EnumNumber { @@ -717,11 +769,11 @@ func (x SetChannelPasswordResponse_Status) String() string { } func (SetChannelPasswordResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[13].Descriptor() + return file_chat_proto_enumTypes[14].Descriptor() } func (SetChannelPasswordResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[13] + return &file_chat_proto_enumTypes[14] } func (x SetChannelPasswordResponse_Status) Number() protoreflect.EnumNumber { @@ -766,11 +818,11 @@ func (x ToggleChannelModerationResponse_Status) String() string { } func (ToggleChannelModerationResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[14].Descriptor() + return file_chat_proto_enumTypes[15].Descriptor() } func (ToggleChannelModerationResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[14] + return &file_chat_proto_enumTypes[15] } func (x ToggleChannelModerationResponse_Status) Number() protoreflect.EnumNumber { @@ -815,11 +867,11 @@ func (x ToggleChannelAnnouncementsResponse_Status) String() string { } func (ToggleChannelAnnouncementsResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[15].Descriptor() + return file_chat_proto_enumTypes[16].Descriptor() } func (ToggleChannelAnnouncementsResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[15] + return &file_chat_proto_enumTypes[16] } func (x ToggleChannelAnnouncementsResponse_Status) Number() protoreflect.EnumNumber { @@ -873,11 +925,11 @@ func (x InviteToChannelResponse_Status) String() string { } func (InviteToChannelResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_chat_proto_enumTypes[16].Descriptor() + return file_chat_proto_enumTypes[17].Descriptor() } func (InviteToChannelResponse_Status) Type() protoreflect.EnumType { - return &file_chat_proto_enumTypes[16] + return &file_chat_proto_enumTypes[17] } func (x InviteToChannelResponse_Status) Number() protoreflect.EnumNumber { @@ -894,14 +946,20 @@ type SendWhisperMessageRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - SenderGUID uint64 `protobuf:"varint,3,opt,name=senderGUID,proto3" json:"senderGUID,omitempty"` - SenderName string `protobuf:"bytes,4,opt,name=senderName,proto3" json:"senderName,omitempty"` - SenderRace uint32 `protobuf:"varint,5,opt,name=senderRace,proto3" json:"senderRace,omitempty"` - Language uint32 `protobuf:"varint,6,opt,name=language,proto3" json:"language,omitempty"` - ReceiverName string `protobuf:"bytes,7,opt,name=receiverName,proto3" json:"receiverName,omitempty"` - Msg string `protobuf:"bytes,8,opt,name=msg,proto3" json:"msg,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + SenderGUID uint64 `protobuf:"varint,3,opt,name=senderGUID,proto3" json:"senderGUID,omitempty"` + SenderName string `protobuf:"bytes,4,opt,name=senderName,proto3" json:"senderName,omitempty"` + SenderRace uint32 `protobuf:"varint,5,opt,name=senderRace,proto3" json:"senderRace,omitempty"` + Language uint32 `protobuf:"varint,6,opt,name=language,proto3" json:"language,omitempty"` + ReceiverName string `protobuf:"bytes,7,opt,name=receiverName,proto3" json:"receiverName,omitempty"` + Msg string `protobuf:"bytes,8,opt,name=msg,proto3" json:"msg,omitempty"` + ReceiverRealmID uint32 `protobuf:"varint,9,opt,name=receiverRealmID,proto3" json:"receiverRealmID,omitempty"` + SenderClass uint32 `protobuf:"varint,10,opt,name=senderClass,proto3" json:"senderClass,omitempty"` + SenderGender uint32 `protobuf:"varint,11,opt,name=senderGender,proto3" json:"senderGender,omitempty"` + SenderAccountID uint32 `protobuf:"varint,12,opt,name=senderAccountID,proto3" json:"senderAccountID,omitempty"` + GatewayValidatedGameplayCrossrealmWhisper bool `protobuf:"varint,13,opt,name=gatewayValidatedGameplayCrossrealmWhisper,proto3" json:"gatewayValidatedGameplayCrossrealmWhisper,omitempty"` + SenderChatTag uint32 `protobuf:"varint,14,opt,name=senderChatTag,proto3" json:"senderChatTag,omitempty"` } func (x *SendWhisperMessageRequest) Reset() { @@ -992,14 +1050,61 @@ func (x *SendWhisperMessageRequest) GetMsg() string { return "" } +func (x *SendWhisperMessageRequest) GetReceiverRealmID() uint32 { + if x != nil { + return x.ReceiverRealmID + } + return 0 +} + +func (x *SendWhisperMessageRequest) GetSenderClass() uint32 { + if x != nil { + return x.SenderClass + } + return 0 +} + +func (x *SendWhisperMessageRequest) GetSenderGender() uint32 { + if x != nil { + return x.SenderGender + } + return 0 +} + +func (x *SendWhisperMessageRequest) GetSenderAccountID() uint32 { + if x != nil { + return x.SenderAccountID + } + return 0 +} + +func (x *SendWhisperMessageRequest) GetGatewayValidatedGameplayCrossrealmWhisper() bool { + if x != nil { + return x.GatewayValidatedGameplayCrossrealmWhisper + } + return false +} + +func (x *SendWhisperMessageRequest) GetSenderChatTag() uint32 { + if x != nil { + return x.SenderChatTag + } + return 0 +} + type SendWhisperMessageResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - Status SendWhisperMessageResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.SendWhisperMessageResponse_Status" json:"status,omitempty"` - ReceiverGUID uint64 `protobuf:"varint,3,opt,name=receiverGUID,proto3" json:"receiverGUID,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status SendWhisperMessageResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.SendWhisperMessageResponse_Status" json:"status,omitempty"` + ReceiverGUID uint64 `protobuf:"varint,3,opt,name=receiverGUID,proto3" json:"receiverGUID,omitempty"` + ReceiverRealmID uint32 `protobuf:"varint,4,opt,name=receiverRealmID,proto3" json:"receiverRealmID,omitempty"` + ReceiverName string `protobuf:"bytes,5,opt,name=receiverName,proto3" json:"receiverName,omitempty"` + ReceiverRace uint32 `protobuf:"varint,6,opt,name=receiverRace,proto3" json:"receiverRace,omitempty"` + ReceiverClass uint32 `protobuf:"varint,7,opt,name=receiverClass,proto3" json:"receiverClass,omitempty"` + ReceiverGender uint32 `protobuf:"varint,8,opt,name=receiverGender,proto3" json:"receiverGender,omitempty"` } func (x *SendWhisperMessageResponse) Reset() { @@ -1055,19 +1160,55 @@ func (x *SendWhisperMessageResponse) GetReceiverGUID() uint64 { return 0 } +func (x *SendWhisperMessageResponse) GetReceiverRealmID() uint32 { + if x != nil { + return x.ReceiverRealmID + } + return 0 +} + +func (x *SendWhisperMessageResponse) GetReceiverName() string { + if x != nil { + return x.ReceiverName + } + return "" +} + +func (x *SendWhisperMessageResponse) GetReceiverRace() uint32 { + if x != nil { + return x.ReceiverRace + } + return 0 +} + +func (x *SendWhisperMessageResponse) GetReceiverClass() uint32 { + if x != nil { + return x.ReceiverClass + } + return 0 +} + +func (x *SendWhisperMessageResponse) GetReceiverGender() uint32 { + if x != nil { + return x.ReceiverGender + } + return 0 +} + type JoinChannelRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` - PlayerName string `protobuf:"bytes,4,opt,name=playerName,proto3" json:"playerName,omitempty"` - TeamID uint32 `protobuf:"varint,5,opt,name=teamID,proto3" json:"teamID,omitempty"` // 0=Alliance, 1=Horde, 2=Neutral - ChannelName string `protobuf:"bytes,6,opt,name=channelName,proto3" json:"channelName,omitempty"` - ChannelID uint32 `protobuf:"varint,7,opt,name=channelID,proto3" json:"channelID,omitempty"` // 0 for custom channels - Password string `protobuf:"bytes,8,opt,name=password,proto3" json:"password,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + PlayerName string `protobuf:"bytes,4,opt,name=playerName,proto3" json:"playerName,omitempty"` + TeamID TeamID `protobuf:"varint,5,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` + ChannelName string `protobuf:"bytes,6,opt,name=channelName,proto3" json:"channelName,omitempty"` + ChannelID uint32 `protobuf:"varint,7,opt,name=channelID,proto3" json:"channelID,omitempty"` // 0 for custom channels, or worldserver's ID for territorial + Password string `protobuf:"bytes,8,opt,name=password,proto3" json:"password,omitempty"` + ChannelFlags uint32 `protobuf:"varint,9,opt,name=channelFlags,proto3" json:"channelFlags,omitempty"` // 0 to auto-detect, or worldserver's flags for territorial } func (x *JoinChannelRequest) Reset() { @@ -1130,11 +1271,11 @@ func (x *JoinChannelRequest) GetPlayerName() string { return "" } -func (x *JoinChannelRequest) GetTeamID() uint32 { +func (x *JoinChannelRequest) GetTeamID() TeamID { if x != nil { return x.TeamID } - return 0 + return TeamID_TEAM_ALLIANCE } func (x *JoinChannelRequest) GetChannelName() string { @@ -1158,6 +1299,13 @@ func (x *JoinChannelRequest) GetPassword() string { return "" } +func (x *JoinChannelRequest) GetChannelFlags() uint32 { + if x != nil { + return x.ChannelFlags + } + return 0 +} + type JoinChannelResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1230,6 +1378,7 @@ type LeaveChannelRequest struct { RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` ChannelName string `protobuf:"bytes,4,opt,name=channelName,proto3" json:"channelName,omitempty"` + TeamID TeamID `protobuf:"varint,5,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` } func (x *LeaveChannelRequest) Reset() { @@ -1292,6 +1441,13 @@ func (x *LeaveChannelRequest) GetChannelName() string { return "" } +func (x *LeaveChannelRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + type LeaveChannelResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1352,13 +1508,15 @@ type SendChannelMessageRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - SenderGUID uint64 `protobuf:"varint,3,opt,name=senderGUID,proto3" json:"senderGUID,omitempty"` - SenderName string `protobuf:"bytes,4,opt,name=senderName,proto3" json:"senderName,omitempty"` - ChannelName string `protobuf:"bytes,5,opt,name=channelName,proto3" json:"channelName,omitempty"` - Language uint32 `protobuf:"varint,6,opt,name=language,proto3" json:"language,omitempty"` - Message string `protobuf:"bytes,7,opt,name=message,proto3" json:"message,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + SenderGUID uint64 `protobuf:"varint,3,opt,name=senderGUID,proto3" json:"senderGUID,omitempty"` + SenderName string `protobuf:"bytes,4,opt,name=senderName,proto3" json:"senderName,omitempty"` + ChannelName string `protobuf:"bytes,5,opt,name=channelName,proto3" json:"channelName,omitempty"` + Language uint32 `protobuf:"varint,6,opt,name=language,proto3" json:"language,omitempty"` + Message string `protobuf:"bytes,7,opt,name=message,proto3" json:"message,omitempty"` + TeamID TeamID `protobuf:"varint,8,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` + SenderChatTag uint32 `protobuf:"varint,9,opt,name=senderChatTag,proto3" json:"senderChatTag,omitempty"` } func (x *SendChannelMessageRequest) Reset() { @@ -1442,6 +1600,20 @@ func (x *SendChannelMessageRequest) GetMessage() string { return "" } +func (x *SendChannelMessageRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + +func (x *SendChannelMessageRequest) GetSenderChatTag() uint32 { + if x != nil { + return x.SenderChatTag + } + return 0 +} + type SendChannelMessageResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1506,6 +1678,7 @@ type GetChannelListRequest struct { RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` ChannelName string `protobuf:"bytes,4,opt,name=channelName,proto3" json:"channelName,omitempty"` + TeamID TeamID `protobuf:"varint,5,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` } func (x *GetChannelListRequest) Reset() { @@ -1568,6 +1741,13 @@ func (x *GetChannelListRequest) GetChannelName() string { return "" } +func (x *GetChannelListRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + type GetChannelListResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1641,6 +1821,7 @@ type KickFromChannelRequest struct { KickerGUID uint64 `protobuf:"varint,3,opt,name=kickerGUID,proto3" json:"kickerGUID,omitempty"` ChannelName string `protobuf:"bytes,4,opt,name=channelName,proto3" json:"channelName,omitempty"` TargetName string `protobuf:"bytes,5,opt,name=targetName,proto3" json:"targetName,omitempty"` + TeamID TeamID `protobuf:"varint,6,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` } func (x *KickFromChannelRequest) Reset() { @@ -1710,6 +1891,13 @@ func (x *KickFromChannelRequest) GetTargetName() string { return "" } +func (x *KickFromChannelRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + type KickFromChannelResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1775,6 +1963,7 @@ type BanFromChannelRequest struct { BannerGUID uint64 `protobuf:"varint,3,opt,name=bannerGUID,proto3" json:"bannerGUID,omitempty"` ChannelName string `protobuf:"bytes,4,opt,name=channelName,proto3" json:"channelName,omitempty"` TargetName string `protobuf:"bytes,5,opt,name=targetName,proto3" json:"targetName,omitempty"` + TeamID TeamID `protobuf:"varint,6,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` } func (x *BanFromChannelRequest) Reset() { @@ -1844,6 +2033,13 @@ func (x *BanFromChannelRequest) GetTargetName() string { return "" } +func (x *BanFromChannelRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + type BanFromChannelResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1909,6 +2105,7 @@ type UnbanFromChannelRequest struct { UnbannerGUID uint64 `protobuf:"varint,3,opt,name=unbannerGUID,proto3" json:"unbannerGUID,omitempty"` ChannelName string `protobuf:"bytes,4,opt,name=channelName,proto3" json:"channelName,omitempty"` TargetName string `protobuf:"bytes,5,opt,name=targetName,proto3" json:"targetName,omitempty"` + TeamID TeamID `protobuf:"varint,6,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` } func (x *UnbanFromChannelRequest) Reset() { @@ -1978,6 +2175,13 @@ func (x *UnbanFromChannelRequest) GetTargetName() string { return "" } +func (x *UnbanFromChannelRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + type UnbanFromChannelResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2043,6 +2247,7 @@ type SetChannelModeratorRequest struct { SetterGUID uint64 `protobuf:"varint,3,opt,name=setterGUID,proto3" json:"setterGUID,omitempty"` ChannelName string `protobuf:"bytes,4,opt,name=channelName,proto3" json:"channelName,omitempty"` TargetName string `protobuf:"bytes,5,opt,name=targetName,proto3" json:"targetName,omitempty"` + TeamID TeamID `protobuf:"varint,6,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` } func (x *SetChannelModeratorRequest) Reset() { @@ -2112,6 +2317,13 @@ func (x *SetChannelModeratorRequest) GetTargetName() string { return "" } +func (x *SetChannelModeratorRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + type SetChannelModeratorResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2177,6 +2389,7 @@ type UnsetChannelModeratorRequest struct { SetterGUID uint64 `protobuf:"varint,3,opt,name=setterGUID,proto3" json:"setterGUID,omitempty"` ChannelName string `protobuf:"bytes,4,opt,name=channelName,proto3" json:"channelName,omitempty"` TargetName string `protobuf:"bytes,5,opt,name=targetName,proto3" json:"targetName,omitempty"` + TeamID TeamID `protobuf:"varint,6,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` } func (x *UnsetChannelModeratorRequest) Reset() { @@ -2246,6 +2459,13 @@ func (x *UnsetChannelModeratorRequest) GetTargetName() string { return "" } +func (x *UnsetChannelModeratorRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + type UnsetChannelModeratorResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2311,6 +2531,7 @@ type SetChannelMuteRequest struct { MuterGUID uint64 `protobuf:"varint,3,opt,name=muterGUID,proto3" json:"muterGUID,omitempty"` ChannelName string `protobuf:"bytes,4,opt,name=channelName,proto3" json:"channelName,omitempty"` TargetName string `protobuf:"bytes,5,opt,name=targetName,proto3" json:"targetName,omitempty"` + TeamID TeamID `protobuf:"varint,6,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` } func (x *SetChannelMuteRequest) Reset() { @@ -2380,6 +2601,13 @@ func (x *SetChannelMuteRequest) GetTargetName() string { return "" } +func (x *SetChannelMuteRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + type SetChannelMuteResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2445,6 +2673,7 @@ type UnsetChannelMuteRequest struct { UnmuterGUID uint64 `protobuf:"varint,3,opt,name=unmuterGUID,proto3" json:"unmuterGUID,omitempty"` ChannelName string `protobuf:"bytes,4,opt,name=channelName,proto3" json:"channelName,omitempty"` TargetName string `protobuf:"bytes,5,opt,name=targetName,proto3" json:"targetName,omitempty"` + TeamID TeamID `protobuf:"varint,6,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` } func (x *UnsetChannelMuteRequest) Reset() { @@ -2514,6 +2743,13 @@ func (x *UnsetChannelMuteRequest) GetTargetName() string { return "" } +func (x *UnsetChannelMuteRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + type UnsetChannelMuteResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2579,6 +2815,7 @@ type SetChannelOwnerRequest struct { SetterGUID uint64 `protobuf:"varint,3,opt,name=setterGUID,proto3" json:"setterGUID,omitempty"` ChannelName string `protobuf:"bytes,4,opt,name=channelName,proto3" json:"channelName,omitempty"` TargetName string `protobuf:"bytes,5,opt,name=targetName,proto3" json:"targetName,omitempty"` + TeamID TeamID `protobuf:"varint,6,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` } func (x *SetChannelOwnerRequest) Reset() { @@ -2648,6 +2885,13 @@ func (x *SetChannelOwnerRequest) GetTargetName() string { return "" } +func (x *SetChannelOwnerRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + type SetChannelOwnerResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2713,6 +2957,7 @@ type SetChannelPasswordRequest struct { SetterGUID uint64 `protobuf:"varint,3,opt,name=setterGUID,proto3" json:"setterGUID,omitempty"` ChannelName string `protobuf:"bytes,4,opt,name=channelName,proto3" json:"channelName,omitempty"` Password string `protobuf:"bytes,5,opt,name=password,proto3" json:"password,omitempty"` + TeamID TeamID `protobuf:"varint,6,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` } func (x *SetChannelPasswordRequest) Reset() { @@ -2782,6 +3027,13 @@ func (x *SetChannelPasswordRequest) GetPassword() string { return "" } +func (x *SetChannelPasswordRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + type SetChannelPasswordResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2846,6 +3098,7 @@ type ToggleChannelModerationRequest struct { RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` TogglerGUID uint64 `protobuf:"varint,3,opt,name=togglerGUID,proto3" json:"togglerGUID,omitempty"` ChannelName string `protobuf:"bytes,4,opt,name=channelName,proto3" json:"channelName,omitempty"` + TeamID TeamID `protobuf:"varint,5,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` } func (x *ToggleChannelModerationRequest) Reset() { @@ -2908,6 +3161,13 @@ func (x *ToggleChannelModerationRequest) GetChannelName() string { return "" } +func (x *ToggleChannelModerationRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + type ToggleChannelModerationResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2980,6 +3240,7 @@ type ToggleChannelAnnouncementsRequest struct { RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` TogglerGUID uint64 `protobuf:"varint,3,opt,name=togglerGUID,proto3" json:"togglerGUID,omitempty"` ChannelName string `protobuf:"bytes,4,opt,name=channelName,proto3" json:"channelName,omitempty"` + TeamID TeamID `protobuf:"varint,5,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` } func (x *ToggleChannelAnnouncementsRequest) Reset() { @@ -3042,6 +3303,13 @@ func (x *ToggleChannelAnnouncementsRequest) GetChannelName() string { return "" } +func (x *ToggleChannelAnnouncementsRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + type ToggleChannelAnnouncementsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3115,6 +3383,7 @@ type InviteToChannelRequest struct { InviterGUID uint64 `protobuf:"varint,3,opt,name=inviterGUID,proto3" json:"inviterGUID,omitempty"` ChannelName string `protobuf:"bytes,4,opt,name=channelName,proto3" json:"channelName,omitempty"` TargetName string `protobuf:"bytes,5,opt,name=targetName,proto3" json:"targetName,omitempty"` + TeamID TeamID `protobuf:"varint,6,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` } func (x *InviteToChannelRequest) Reset() { @@ -3184,6 +3453,13 @@ func (x *InviteToChannelRequest) GetTargetName() string { return "" } +func (x *InviteToChannelRequest) GetTeamID() TeamID { + if x != nil { + return x.TeamID + } + return TeamID_TEAM_ALLIANCE +} + type InviteToChannelResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3385,7 +3661,7 @@ var File_chat_proto protoreflect.FileDescriptor var file_chat_proto_rawDesc = []byte{ 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, - 0x22, 0xf9, 0x01, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, + 0x22, 0x97, 0x04, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, @@ -3400,143 +3676,191 @@ var file_chat_proto_rawDesc = []byte{ 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, - 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0xba, 0x01, 0x0a, - 0x1a, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3d, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x0a, 0x0c, - 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, - 0x22, 0x27, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, - 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4e, - 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x02, 0x22, 0xf4, 0x01, 0x0a, 0x12, 0x4a, 0x6f, - 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x74, 0x65, - 0x61, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, - 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x22, 0xeb, 0x01, 0x0a, 0x13, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x36, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x76, 0x31, 0x2e, - 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x22, 0x5f, 0x0a, - 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, - 0x11, 0x0a, 0x0d, 0x57, 0x72, 0x6f, 0x6e, 0x67, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x10, 0x02, 0x12, 0x10, - 0x0a, 0x0c, 0x57, 0x72, 0x6f, 0x6e, 0x67, 0x46, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x03, - 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x49, 0x6e, 0x41, 0x72, 0x65, 0x61, 0x10, 0x04, 0x12, - 0x0d, 0x0a, 0x09, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x10, 0x05, 0x22, 0x83, - 0x01, 0x0a, 0x13, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, + 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x28, 0x0a, 0x0f, + 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, + 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, + 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x0f, + 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x5c, 0x0a, 0x29, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x47, 0x61, 0x6d, 0x65, 0x70, 0x6c, + 0x61, 0x79, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x57, 0x68, 0x69, 0x73, + 0x70, 0x65, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x29, 0x67, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x47, 0x61, 0x6d, 0x65, 0x70, + 0x6c, 0x61, 0x79, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x57, 0x68, 0x69, + 0x73, 0x70, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x68, + 0x61, 0x74, 0x54, 0x61, 0x67, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x73, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x43, 0x68, 0x61, 0x74, 0x54, 0x61, 0x67, 0x22, 0x92, 0x03, 0x0a, 0x1a, 0x53, + 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3d, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, + 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x28, + 0x0a, 0x0f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, + 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x61, 0x63, 0x65, + 0x12, 0x24, 0x0a, 0x0d, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x43, 0x6c, 0x61, 0x73, + 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, + 0x65, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, + 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x22, 0x3f, + 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, + 0x12, 0x15, 0x0a, 0x11, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x74, + 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x02, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x68, 0x61, 0x72, 0x61, + 0x63, 0x74, 0x65, 0x72, 0x41, 0x6d, 0x62, 0x69, 0x67, 0x75, 0x6f, 0x75, 0x73, 0x10, 0x03, 0x22, + 0xa4, 0x02, 0x0a, 0x12, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, - 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x82, 0x01, 0x0a, 0x14, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, - 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, - 0x37, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x6c, 0x61, + 0x67, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0xeb, 0x01, 0x0a, 0x13, 0x4a, 0x6f, 0x69, 0x6e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x36, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x1f, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, - 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x22, 0xdf, 0x01, 0x0a, 0x19, 0x53, 0x65, - 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x22, 0x5f, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, + 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x57, 0x72, 0x6f, 0x6e, 0x67, 0x50, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x61, 0x6e, 0x6e, + 0x65, 0x64, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x57, 0x72, 0x6f, 0x6e, 0x67, 0x46, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x49, 0x6e, 0x41, + 0x72, 0x65, 0x61, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, + 0x65, 0x64, 0x10, 0x05, 0x22, 0xa7, 0x01, 0x0a, 0x13, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, + 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, + 0x61, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0x82, + 0x01, 0x0a, 0x14, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x37, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x22, 0x1f, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, + 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x10, 0x01, 0x22, 0xa9, 0x02, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, + 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, + 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x43, 0x68, 0x61, 0x74, 0x54, 0x61, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0d, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x68, 0x61, 0x74, 0x54, 0x61, 0x67, 0x22, + 0xa8, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x3d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, + 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, + 0x12, 0x09, 0x0a, 0x05, 0x4d, 0x75, 0x74, 0x65, 0x64, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x54, + 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x10, 0x03, 0x22, 0xa9, 0x01, 0x0a, 0x15, 0x47, + 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb3, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x39, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, + 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0x1f, 0x0a, 0x06, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, + 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x22, 0xca, 0x01, 0x0a, + 0x16, 0x4b, 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, - 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, - 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x47, - 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, - 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xa8, 0x01, 0x0a, 0x1a, - 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, - 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3d, 0x0a, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x39, 0x0a, 0x06, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, - 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, - 0x4d, 0x75, 0x74, 0x65, 0x64, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x68, 0x72, 0x6f, 0x74, - 0x74, 0x6c, 0x65, 0x64, 0x10, 0x03, 0x22, 0x85, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, - 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xb3, - 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x39, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x73, 0x22, 0x1f, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, + 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6b, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6b, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, + 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xae, 0x01, 0x0a, 0x17, 0x4b, 0x69, + 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x69, 0x63, + 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x22, 0x45, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x10, 0x01, 0x22, 0xa6, 0x01, 0x0a, 0x16, 0x4b, 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, - 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, - 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6b, - 0x69, 0x63, 0x6b, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0a, 0x6b, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, - 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xae, 0x01, - 0x0a, 0x17, 0x4b, 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3a, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x76, 0x31, - 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x45, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, - 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xa5, - 0x01, 0x0a, 0x15, 0x42, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, - 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, - 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x47, 0x55, - 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, - 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, - 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xac, 0x01, 0x0a, 0x16, 0x42, 0x61, 0x6e, 0x46, 0x72, + 0x65, 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xc9, 0x01, 0x0a, 0x15, 0x42, + 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xac, 0x01, 0x0a, 0x16, 0x42, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x39, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, @@ -3547,7 +3871,7 @@ var file_chat_proto_rawDesc = []byte{ 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, - 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xab, 0x01, 0x0a, 0x17, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, + 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xcf, 0x01, 0x0a, 0x17, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, @@ -3558,41 +3882,21 @@ var file_chat_proto_rawDesc = []byte{ 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, - 0x61, 0x6d, 0x65, 0x22, 0xb1, 0x01, 0x0a, 0x18, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, 0x72, 0x6f, - 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x3b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, 0x72, 0x6f, - 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, - 0x46, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, - 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, - 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, - 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x42, - 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x10, 0x03, 0x22, 0xaa, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, - 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, - 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, - 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, - 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xb2, 0x01, 0x0a, 0x1b, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, + 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb1, 0x01, 0x0a, 0x18, 0x55, 0x6e, 0x62, 0x61, + 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x41, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x4f, 0x77, - 0x6e, 0x65, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, - 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xac, 0x01, 0x0a, 0x1c, 0x55, 0x6e, - 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x62, 0x61, + 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x22, 0x46, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, + 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x4e, 0x6f, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x10, 0x03, 0x22, 0xce, 0x01, 0x0a, 0x1a, + 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, @@ -3602,29 +3906,58 @@ var file_chat_proto_rawDesc = []byte{ 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xb6, 0x01, 0x0a, 0x1d, 0x55, 0x6e, 0x73, - 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, - 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x40, 0x0a, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x76, - 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, + 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, + 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, + 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb2, 0x01, 0x0a, + 0x1b, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3e, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x41, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, - 0x03, 0x22, 0xa3, 0x01, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, - 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x6d, 0x75, 0x74, 0x65, 0x72, - 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x75, 0x74, 0x65, - 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xac, 0x01, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x43, + 0x03, 0x22, 0xd0, 0x01, 0x0a, 0x1c, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, + 0x0a, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, + 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, + 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb6, 0x01, 0x0a, 0x1d, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x40, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, + 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x41, 0x0a, 0x06, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, + 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, + 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xc7, 0x01, + 0x0a, 0x15, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x6d, 0x75, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x75, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, + 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xac, 0x01, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x39, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, @@ -3635,7 +3968,7 @@ var file_chat_proto_rawDesc = []byte{ 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, - 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xa9, 0x01, 0x0a, 0x17, 0x55, 0x6e, 0x73, 0x65, 0x74, + 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xcd, 0x01, 0x0a, 0x17, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, @@ -3646,69 +3979,78 @@ var file_chat_proto_rawDesc = []byte{ 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, - 0x6d, 0x65, 0x22, 0xb0, 0x01, 0x0a, 0x18, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, - 0x69, 0x12, 0x3b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x45, - 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, - 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, - 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x10, - 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, - 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xa6, 0x01, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, - 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, - 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, - 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xaa, - 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, - 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3a, 0x0a, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x41, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, - 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, - 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xa5, 0x01, 0x0a, 0x19, - 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, - 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, - 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, - 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, - 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, - 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, - 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, - 0x6f, 0x72, 0x64, 0x22, 0x9c, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb0, 0x01, 0x0a, 0x18, 0x55, 0x6e, 0x73, 0x65, 0x74, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x22, 0x45, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, + 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, + 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xca, 0x01, 0x0a, 0x16, 0x53, 0x65, + 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xaa, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x61, 0x70, 0x69, 0x12, 0x3d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x22, 0x2d, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, - 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, - 0x10, 0x02, 0x22, 0x90, 0x01, 0x0a, 0x1e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, - 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, - 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x72, 0x47, - 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, - 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xd8, 0x01, 0x0a, 0x1f, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x22, 0x41, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, + 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, + 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x10, 0x02, 0x12, + 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, + 0x64, 0x10, 0x03, 0x22, 0xc9, 0x01, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, + 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x22, 0x0a, 0x06, 0x74, + 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, + 0x9c, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x3d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0x2d, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, + 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, + 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x10, 0x02, 0x22, 0xb4, + 0x01, 0x0a, 0x1e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, + 0x0b, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0b, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, + 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, + 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xd8, 0x01, 0x0a, 0x1f, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x42, 0x0a, 0x06, 0x73, @@ -3722,7 +4064,7 @@ var file_chat_proto_rawDesc = []byte{ 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x10, 0x02, - 0x22, 0x93, 0x01, 0x0a, 0x21, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x22, 0xb7, 0x01, 0x0a, 0x21, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, @@ -3731,149 +4073,158 @@ var file_chat_proto_rawDesc = []byte{ 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xe4, 0x01, 0x0a, 0x22, 0x54, 0x6f, 0x67, 0x67, 0x6c, - 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, - 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, - 0x45, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x2d, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, - 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x31, 0x0a, 0x06, 0x53, 0x74, + 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, + 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xe4, 0x01, 0x0a, 0x22, 0x54, + 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, + 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x45, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x61, 0x6e, + 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, + 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x31, + 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, + 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, + 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x10, + 0x02, 0x22, 0xcc, 0x01, 0x0a, 0x16, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, + 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x76, 0x69, + 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, + 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, + 0x22, 0xd9, 0x01, 0x0a, 0x17, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3a, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, + 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x70, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, - 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, - 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x10, 0x02, 0x22, 0xa8, 0x01, - 0x0a, 0x16, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, - 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, - 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x47, - 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, - 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xd9, 0x01, 0x0a, 0x17, 0x49, 0x6e, 0x76, - 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, - 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x22, 0x70, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, - 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, - 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x03, - 0x12, 0x10, 0x0a, 0x0c, 0x57, 0x72, 0x6f, 0x6e, 0x67, 0x46, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6e, - 0x65, 0x64, 0x10, 0x05, 0x22, 0x97, 0x01, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x1e, 0x0a, 0x0a, - 0x6e, 0x75, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x20, 0x0a, 0x0b, - 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0b, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x4d, - 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, - 0x12, 0x0a, 0x04, 0x67, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, - 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x32, 0xf3, 0x0a, - 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x53, 0x0a, - 0x12, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, - 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, - 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0b, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x12, 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x4a, - 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x12, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, + 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x50, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x02, 0x12, + 0x17, 0x0a, 0x13, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x57, 0x72, 0x6f, 0x6e, + 0x67, 0x46, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x10, 0x05, 0x22, 0x97, 0x01, 0x0a, + 0x0b, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x12, 0x14, + 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, + 0x6c, 0x61, 0x67, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x46, 0x6c, + 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x4d, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x75, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, + 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2a, 0x3d, 0x0a, 0x06, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, + 0x11, 0x0a, 0x0d, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x41, 0x4c, 0x4c, 0x49, 0x41, 0x4e, 0x43, 0x45, + 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x48, 0x4f, 0x52, 0x44, 0x45, + 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4e, 0x45, 0x55, 0x54, 0x52, + 0x41, 0x4c, 0x10, 0x02, 0x32, 0xf3, 0x0a, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x74, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x53, 0x0a, 0x12, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, + 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0b, 0x4a, 0x6f, 0x69, + 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, + 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x4c, 0x65, 0x61, + 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x12, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x47, 0x65, - 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0f, 0x4b, 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x69, 0x63, 0x6b, - 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, + 0x67, 0x65, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, + 0x69, 0x73, 0x74, 0x12, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0f, 0x4b, 0x69, + 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x2e, + 0x76, 0x31, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x4b, + 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x42, 0x61, 0x6e, 0x46, 0x72, 0x6f, + 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, + 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x47, 0x0a, 0x0e, 0x42, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x12, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x76, - 0x31, 0x2e, 0x42, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x10, 0x55, 0x6e, 0x62, 0x61, - 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1b, 0x2e, 0x76, - 0x31, 0x2e, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x55, - 0x6e, 0x62, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x1e, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, - 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, - 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x5c, 0x0a, 0x15, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, - 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, - 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, - 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, - 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, - 0x0e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x12, - 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, - 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x10, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, - 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, - 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x53, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x17, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, - 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, 0x1a, 0x54, 0x6f, - 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, - 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, - 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, - 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x4d, 0x0a, 0x10, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, 0x72, + 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, + 0x0a, 0x13, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x15, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, + 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, + 0x10, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, + 0x65, 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, + 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0f, + 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, + 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, + 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1d, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, + 0x17, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, + 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, + 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, + 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x6b, 0x0a, 0x1a, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, + 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0f, 0x49, 0x6e, 0x76, 0x69, 0x74, - 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, - 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, - 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x0d, 0x5a, 0x0b, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x74, 0x2f, - 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, + 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, + 0x0a, 0x0f, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0d, 0x5a, 0x0b, 0x67, 0x65, + 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x74, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -3888,122 +4239,139 @@ func file_chat_proto_rawDescGZIP() []byte { return file_chat_proto_rawDescData } -var file_chat_proto_enumTypes = make([]protoimpl.EnumInfo, 17) +var file_chat_proto_enumTypes = make([]protoimpl.EnumInfo, 18) var file_chat_proto_msgTypes = make([]protoimpl.MessageInfo, 36) var file_chat_proto_goTypes = []interface{}{ - (SendWhisperMessageResponse_Status)(0), // 0: v1.SendWhisperMessageResponse.Status - (JoinChannelResponse_Status)(0), // 1: v1.JoinChannelResponse.Status - (LeaveChannelResponse_Status)(0), // 2: v1.LeaveChannelResponse.Status - (SendChannelMessageResponse_Status)(0), // 3: v1.SendChannelMessageResponse.Status - (GetChannelListResponse_Status)(0), // 4: v1.GetChannelListResponse.Status - (KickFromChannelResponse_Status)(0), // 5: v1.KickFromChannelResponse.Status - (BanFromChannelResponse_Status)(0), // 6: v1.BanFromChannelResponse.Status - (UnbanFromChannelResponse_Status)(0), // 7: v1.UnbanFromChannelResponse.Status - (SetChannelModeratorResponse_Status)(0), // 8: v1.SetChannelModeratorResponse.Status - (UnsetChannelModeratorResponse_Status)(0), // 9: v1.UnsetChannelModeratorResponse.Status - (SetChannelMuteResponse_Status)(0), // 10: v1.SetChannelMuteResponse.Status - (UnsetChannelMuteResponse_Status)(0), // 11: v1.UnsetChannelMuteResponse.Status - (SetChannelOwnerResponse_Status)(0), // 12: v1.SetChannelOwnerResponse.Status - (SetChannelPasswordResponse_Status)(0), // 13: v1.SetChannelPasswordResponse.Status - (ToggleChannelModerationResponse_Status)(0), // 14: v1.ToggleChannelModerationResponse.Status - (ToggleChannelAnnouncementsResponse_Status)(0), // 15: v1.ToggleChannelAnnouncementsResponse.Status - (InviteToChannelResponse_Status)(0), // 16: v1.InviteToChannelResponse.Status - (*SendWhisperMessageRequest)(nil), // 17: v1.SendWhisperMessageRequest - (*SendWhisperMessageResponse)(nil), // 18: v1.SendWhisperMessageResponse - (*JoinChannelRequest)(nil), // 19: v1.JoinChannelRequest - (*JoinChannelResponse)(nil), // 20: v1.JoinChannelResponse - (*LeaveChannelRequest)(nil), // 21: v1.LeaveChannelRequest - (*LeaveChannelResponse)(nil), // 22: v1.LeaveChannelResponse - (*SendChannelMessageRequest)(nil), // 23: v1.SendChannelMessageRequest - (*SendChannelMessageResponse)(nil), // 24: v1.SendChannelMessageResponse - (*GetChannelListRequest)(nil), // 25: v1.GetChannelListRequest - (*GetChannelListResponse)(nil), // 26: v1.GetChannelListResponse - (*KickFromChannelRequest)(nil), // 27: v1.KickFromChannelRequest - (*KickFromChannelResponse)(nil), // 28: v1.KickFromChannelResponse - (*BanFromChannelRequest)(nil), // 29: v1.BanFromChannelRequest - (*BanFromChannelResponse)(nil), // 30: v1.BanFromChannelResponse - (*UnbanFromChannelRequest)(nil), // 31: v1.UnbanFromChannelRequest - (*UnbanFromChannelResponse)(nil), // 32: v1.UnbanFromChannelResponse - (*SetChannelModeratorRequest)(nil), // 33: v1.SetChannelModeratorRequest - (*SetChannelModeratorResponse)(nil), // 34: v1.SetChannelModeratorResponse - (*UnsetChannelModeratorRequest)(nil), // 35: v1.UnsetChannelModeratorRequest - (*UnsetChannelModeratorResponse)(nil), // 36: v1.UnsetChannelModeratorResponse - (*SetChannelMuteRequest)(nil), // 37: v1.SetChannelMuteRequest - (*SetChannelMuteResponse)(nil), // 38: v1.SetChannelMuteResponse - (*UnsetChannelMuteRequest)(nil), // 39: v1.UnsetChannelMuteRequest - (*UnsetChannelMuteResponse)(nil), // 40: v1.UnsetChannelMuteResponse - (*SetChannelOwnerRequest)(nil), // 41: v1.SetChannelOwnerRequest - (*SetChannelOwnerResponse)(nil), // 42: v1.SetChannelOwnerResponse - (*SetChannelPasswordRequest)(nil), // 43: v1.SetChannelPasswordRequest - (*SetChannelPasswordResponse)(nil), // 44: v1.SetChannelPasswordResponse - (*ToggleChannelModerationRequest)(nil), // 45: v1.ToggleChannelModerationRequest - (*ToggleChannelModerationResponse)(nil), // 46: v1.ToggleChannelModerationResponse - (*ToggleChannelAnnouncementsRequest)(nil), // 47: v1.ToggleChannelAnnouncementsRequest - (*ToggleChannelAnnouncementsResponse)(nil), // 48: v1.ToggleChannelAnnouncementsResponse - (*InviteToChannelRequest)(nil), // 49: v1.InviteToChannelRequest - (*InviteToChannelResponse)(nil), // 50: v1.InviteToChannelResponse - (*ChannelInfo)(nil), // 51: v1.ChannelInfo - (*ChannelMember)(nil), // 52: v1.ChannelMember + (TeamID)(0), // 0: v1.TeamID + (SendWhisperMessageResponse_Status)(0), // 1: v1.SendWhisperMessageResponse.Status + (JoinChannelResponse_Status)(0), // 2: v1.JoinChannelResponse.Status + (LeaveChannelResponse_Status)(0), // 3: v1.LeaveChannelResponse.Status + (SendChannelMessageResponse_Status)(0), // 4: v1.SendChannelMessageResponse.Status + (GetChannelListResponse_Status)(0), // 5: v1.GetChannelListResponse.Status + (KickFromChannelResponse_Status)(0), // 6: v1.KickFromChannelResponse.Status + (BanFromChannelResponse_Status)(0), // 7: v1.BanFromChannelResponse.Status + (UnbanFromChannelResponse_Status)(0), // 8: v1.UnbanFromChannelResponse.Status + (SetChannelModeratorResponse_Status)(0), // 9: v1.SetChannelModeratorResponse.Status + (UnsetChannelModeratorResponse_Status)(0), // 10: v1.UnsetChannelModeratorResponse.Status + (SetChannelMuteResponse_Status)(0), // 11: v1.SetChannelMuteResponse.Status + (UnsetChannelMuteResponse_Status)(0), // 12: v1.UnsetChannelMuteResponse.Status + (SetChannelOwnerResponse_Status)(0), // 13: v1.SetChannelOwnerResponse.Status + (SetChannelPasswordResponse_Status)(0), // 14: v1.SetChannelPasswordResponse.Status + (ToggleChannelModerationResponse_Status)(0), // 15: v1.ToggleChannelModerationResponse.Status + (ToggleChannelAnnouncementsResponse_Status)(0), // 16: v1.ToggleChannelAnnouncementsResponse.Status + (InviteToChannelResponse_Status)(0), // 17: v1.InviteToChannelResponse.Status + (*SendWhisperMessageRequest)(nil), // 18: v1.SendWhisperMessageRequest + (*SendWhisperMessageResponse)(nil), // 19: v1.SendWhisperMessageResponse + (*JoinChannelRequest)(nil), // 20: v1.JoinChannelRequest + (*JoinChannelResponse)(nil), // 21: v1.JoinChannelResponse + (*LeaveChannelRequest)(nil), // 22: v1.LeaveChannelRequest + (*LeaveChannelResponse)(nil), // 23: v1.LeaveChannelResponse + (*SendChannelMessageRequest)(nil), // 24: v1.SendChannelMessageRequest + (*SendChannelMessageResponse)(nil), // 25: v1.SendChannelMessageResponse + (*GetChannelListRequest)(nil), // 26: v1.GetChannelListRequest + (*GetChannelListResponse)(nil), // 27: v1.GetChannelListResponse + (*KickFromChannelRequest)(nil), // 28: v1.KickFromChannelRequest + (*KickFromChannelResponse)(nil), // 29: v1.KickFromChannelResponse + (*BanFromChannelRequest)(nil), // 30: v1.BanFromChannelRequest + (*BanFromChannelResponse)(nil), // 31: v1.BanFromChannelResponse + (*UnbanFromChannelRequest)(nil), // 32: v1.UnbanFromChannelRequest + (*UnbanFromChannelResponse)(nil), // 33: v1.UnbanFromChannelResponse + (*SetChannelModeratorRequest)(nil), // 34: v1.SetChannelModeratorRequest + (*SetChannelModeratorResponse)(nil), // 35: v1.SetChannelModeratorResponse + (*UnsetChannelModeratorRequest)(nil), // 36: v1.UnsetChannelModeratorRequest + (*UnsetChannelModeratorResponse)(nil), // 37: v1.UnsetChannelModeratorResponse + (*SetChannelMuteRequest)(nil), // 38: v1.SetChannelMuteRequest + (*SetChannelMuteResponse)(nil), // 39: v1.SetChannelMuteResponse + (*UnsetChannelMuteRequest)(nil), // 40: v1.UnsetChannelMuteRequest + (*UnsetChannelMuteResponse)(nil), // 41: v1.UnsetChannelMuteResponse + (*SetChannelOwnerRequest)(nil), // 42: v1.SetChannelOwnerRequest + (*SetChannelOwnerResponse)(nil), // 43: v1.SetChannelOwnerResponse + (*SetChannelPasswordRequest)(nil), // 44: v1.SetChannelPasswordRequest + (*SetChannelPasswordResponse)(nil), // 45: v1.SetChannelPasswordResponse + (*ToggleChannelModerationRequest)(nil), // 46: v1.ToggleChannelModerationRequest + (*ToggleChannelModerationResponse)(nil), // 47: v1.ToggleChannelModerationResponse + (*ToggleChannelAnnouncementsRequest)(nil), // 48: v1.ToggleChannelAnnouncementsRequest + (*ToggleChannelAnnouncementsResponse)(nil), // 49: v1.ToggleChannelAnnouncementsResponse + (*InviteToChannelRequest)(nil), // 50: v1.InviteToChannelRequest + (*InviteToChannelResponse)(nil), // 51: v1.InviteToChannelResponse + (*ChannelInfo)(nil), // 52: v1.ChannelInfo + (*ChannelMember)(nil), // 53: v1.ChannelMember } var file_chat_proto_depIdxs = []int32{ - 0, // 0: v1.SendWhisperMessageResponse.status:type_name -> v1.SendWhisperMessageResponse.Status - 1, // 1: v1.JoinChannelResponse.status:type_name -> v1.JoinChannelResponse.Status - 51, // 2: v1.JoinChannelResponse.channel:type_name -> v1.ChannelInfo - 2, // 3: v1.LeaveChannelResponse.status:type_name -> v1.LeaveChannelResponse.Status - 3, // 4: v1.SendChannelMessageResponse.status:type_name -> v1.SendChannelMessageResponse.Status - 4, // 5: v1.GetChannelListResponse.status:type_name -> v1.GetChannelListResponse.Status - 52, // 6: v1.GetChannelListResponse.members:type_name -> v1.ChannelMember - 5, // 7: v1.KickFromChannelResponse.status:type_name -> v1.KickFromChannelResponse.Status - 6, // 8: v1.BanFromChannelResponse.status:type_name -> v1.BanFromChannelResponse.Status - 7, // 9: v1.UnbanFromChannelResponse.status:type_name -> v1.UnbanFromChannelResponse.Status - 8, // 10: v1.SetChannelModeratorResponse.status:type_name -> v1.SetChannelModeratorResponse.Status - 9, // 11: v1.UnsetChannelModeratorResponse.status:type_name -> v1.UnsetChannelModeratorResponse.Status - 10, // 12: v1.SetChannelMuteResponse.status:type_name -> v1.SetChannelMuteResponse.Status - 11, // 13: v1.UnsetChannelMuteResponse.status:type_name -> v1.UnsetChannelMuteResponse.Status - 12, // 14: v1.SetChannelOwnerResponse.status:type_name -> v1.SetChannelOwnerResponse.Status - 13, // 15: v1.SetChannelPasswordResponse.status:type_name -> v1.SetChannelPasswordResponse.Status - 14, // 16: v1.ToggleChannelModerationResponse.status:type_name -> v1.ToggleChannelModerationResponse.Status - 15, // 17: v1.ToggleChannelAnnouncementsResponse.status:type_name -> v1.ToggleChannelAnnouncementsResponse.Status - 16, // 18: v1.InviteToChannelResponse.status:type_name -> v1.InviteToChannelResponse.Status - 17, // 19: v1.ChatService.SendWhisperMessage:input_type -> v1.SendWhisperMessageRequest - 19, // 20: v1.ChatService.JoinChannel:input_type -> v1.JoinChannelRequest - 21, // 21: v1.ChatService.LeaveChannel:input_type -> v1.LeaveChannelRequest - 23, // 22: v1.ChatService.SendChannelMessage:input_type -> v1.SendChannelMessageRequest - 25, // 23: v1.ChatService.GetChannelList:input_type -> v1.GetChannelListRequest - 27, // 24: v1.ChatService.KickFromChannel:input_type -> v1.KickFromChannelRequest - 29, // 25: v1.ChatService.BanFromChannel:input_type -> v1.BanFromChannelRequest - 31, // 26: v1.ChatService.UnbanFromChannel:input_type -> v1.UnbanFromChannelRequest - 33, // 27: v1.ChatService.SetChannelModerator:input_type -> v1.SetChannelModeratorRequest - 35, // 28: v1.ChatService.UnsetChannelModerator:input_type -> v1.UnsetChannelModeratorRequest - 37, // 29: v1.ChatService.SetChannelMute:input_type -> v1.SetChannelMuteRequest - 39, // 30: v1.ChatService.UnsetChannelMute:input_type -> v1.UnsetChannelMuteRequest - 41, // 31: v1.ChatService.SetChannelOwner:input_type -> v1.SetChannelOwnerRequest - 43, // 32: v1.ChatService.SetChannelPassword:input_type -> v1.SetChannelPasswordRequest - 45, // 33: v1.ChatService.ToggleChannelModeration:input_type -> v1.ToggleChannelModerationRequest - 47, // 34: v1.ChatService.ToggleChannelAnnouncements:input_type -> v1.ToggleChannelAnnouncementsRequest - 49, // 35: v1.ChatService.InviteToChannel:input_type -> v1.InviteToChannelRequest - 18, // 36: v1.ChatService.SendWhisperMessage:output_type -> v1.SendWhisperMessageResponse - 20, // 37: v1.ChatService.JoinChannel:output_type -> v1.JoinChannelResponse - 22, // 38: v1.ChatService.LeaveChannel:output_type -> v1.LeaveChannelResponse - 24, // 39: v1.ChatService.SendChannelMessage:output_type -> v1.SendChannelMessageResponse - 26, // 40: v1.ChatService.GetChannelList:output_type -> v1.GetChannelListResponse - 28, // 41: v1.ChatService.KickFromChannel:output_type -> v1.KickFromChannelResponse - 30, // 42: v1.ChatService.BanFromChannel:output_type -> v1.BanFromChannelResponse - 32, // 43: v1.ChatService.UnbanFromChannel:output_type -> v1.UnbanFromChannelResponse - 34, // 44: v1.ChatService.SetChannelModerator:output_type -> v1.SetChannelModeratorResponse - 36, // 45: v1.ChatService.UnsetChannelModerator:output_type -> v1.UnsetChannelModeratorResponse - 38, // 46: v1.ChatService.SetChannelMute:output_type -> v1.SetChannelMuteResponse - 40, // 47: v1.ChatService.UnsetChannelMute:output_type -> v1.UnsetChannelMuteResponse - 42, // 48: v1.ChatService.SetChannelOwner:output_type -> v1.SetChannelOwnerResponse - 44, // 49: v1.ChatService.SetChannelPassword:output_type -> v1.SetChannelPasswordResponse - 46, // 50: v1.ChatService.ToggleChannelModeration:output_type -> v1.ToggleChannelModerationResponse - 48, // 51: v1.ChatService.ToggleChannelAnnouncements:output_type -> v1.ToggleChannelAnnouncementsResponse - 50, // 52: v1.ChatService.InviteToChannel:output_type -> v1.InviteToChannelResponse - 36, // [36:53] is the sub-list for method output_type - 19, // [19:36] is the sub-list for method input_type - 19, // [19:19] is the sub-list for extension type_name - 19, // [19:19] is the sub-list for extension extendee - 0, // [0:19] is the sub-list for field type_name + 1, // 0: v1.SendWhisperMessageResponse.status:type_name -> v1.SendWhisperMessageResponse.Status + 0, // 1: v1.JoinChannelRequest.teamID:type_name -> v1.TeamID + 2, // 2: v1.JoinChannelResponse.status:type_name -> v1.JoinChannelResponse.Status + 52, // 3: v1.JoinChannelResponse.channel:type_name -> v1.ChannelInfo + 0, // 4: v1.LeaveChannelRequest.teamID:type_name -> v1.TeamID + 3, // 5: v1.LeaveChannelResponse.status:type_name -> v1.LeaveChannelResponse.Status + 0, // 6: v1.SendChannelMessageRequest.teamID:type_name -> v1.TeamID + 4, // 7: v1.SendChannelMessageResponse.status:type_name -> v1.SendChannelMessageResponse.Status + 0, // 8: v1.GetChannelListRequest.teamID:type_name -> v1.TeamID + 5, // 9: v1.GetChannelListResponse.status:type_name -> v1.GetChannelListResponse.Status + 53, // 10: v1.GetChannelListResponse.members:type_name -> v1.ChannelMember + 0, // 11: v1.KickFromChannelRequest.teamID:type_name -> v1.TeamID + 6, // 12: v1.KickFromChannelResponse.status:type_name -> v1.KickFromChannelResponse.Status + 0, // 13: v1.BanFromChannelRequest.teamID:type_name -> v1.TeamID + 7, // 14: v1.BanFromChannelResponse.status:type_name -> v1.BanFromChannelResponse.Status + 0, // 15: v1.UnbanFromChannelRequest.teamID:type_name -> v1.TeamID + 8, // 16: v1.UnbanFromChannelResponse.status:type_name -> v1.UnbanFromChannelResponse.Status + 0, // 17: v1.SetChannelModeratorRequest.teamID:type_name -> v1.TeamID + 9, // 18: v1.SetChannelModeratorResponse.status:type_name -> v1.SetChannelModeratorResponse.Status + 0, // 19: v1.UnsetChannelModeratorRequest.teamID:type_name -> v1.TeamID + 10, // 20: v1.UnsetChannelModeratorResponse.status:type_name -> v1.UnsetChannelModeratorResponse.Status + 0, // 21: v1.SetChannelMuteRequest.teamID:type_name -> v1.TeamID + 11, // 22: v1.SetChannelMuteResponse.status:type_name -> v1.SetChannelMuteResponse.Status + 0, // 23: v1.UnsetChannelMuteRequest.teamID:type_name -> v1.TeamID + 12, // 24: v1.UnsetChannelMuteResponse.status:type_name -> v1.UnsetChannelMuteResponse.Status + 0, // 25: v1.SetChannelOwnerRequest.teamID:type_name -> v1.TeamID + 13, // 26: v1.SetChannelOwnerResponse.status:type_name -> v1.SetChannelOwnerResponse.Status + 0, // 27: v1.SetChannelPasswordRequest.teamID:type_name -> v1.TeamID + 14, // 28: v1.SetChannelPasswordResponse.status:type_name -> v1.SetChannelPasswordResponse.Status + 0, // 29: v1.ToggleChannelModerationRequest.teamID:type_name -> v1.TeamID + 15, // 30: v1.ToggleChannelModerationResponse.status:type_name -> v1.ToggleChannelModerationResponse.Status + 0, // 31: v1.ToggleChannelAnnouncementsRequest.teamID:type_name -> v1.TeamID + 16, // 32: v1.ToggleChannelAnnouncementsResponse.status:type_name -> v1.ToggleChannelAnnouncementsResponse.Status + 0, // 33: v1.InviteToChannelRequest.teamID:type_name -> v1.TeamID + 17, // 34: v1.InviteToChannelResponse.status:type_name -> v1.InviteToChannelResponse.Status + 18, // 35: v1.ChatService.SendWhisperMessage:input_type -> v1.SendWhisperMessageRequest + 20, // 36: v1.ChatService.JoinChannel:input_type -> v1.JoinChannelRequest + 22, // 37: v1.ChatService.LeaveChannel:input_type -> v1.LeaveChannelRequest + 24, // 38: v1.ChatService.SendChannelMessage:input_type -> v1.SendChannelMessageRequest + 26, // 39: v1.ChatService.GetChannelList:input_type -> v1.GetChannelListRequest + 28, // 40: v1.ChatService.KickFromChannel:input_type -> v1.KickFromChannelRequest + 30, // 41: v1.ChatService.BanFromChannel:input_type -> v1.BanFromChannelRequest + 32, // 42: v1.ChatService.UnbanFromChannel:input_type -> v1.UnbanFromChannelRequest + 34, // 43: v1.ChatService.SetChannelModerator:input_type -> v1.SetChannelModeratorRequest + 36, // 44: v1.ChatService.UnsetChannelModerator:input_type -> v1.UnsetChannelModeratorRequest + 38, // 45: v1.ChatService.SetChannelMute:input_type -> v1.SetChannelMuteRequest + 40, // 46: v1.ChatService.UnsetChannelMute:input_type -> v1.UnsetChannelMuteRequest + 42, // 47: v1.ChatService.SetChannelOwner:input_type -> v1.SetChannelOwnerRequest + 44, // 48: v1.ChatService.SetChannelPassword:input_type -> v1.SetChannelPasswordRequest + 46, // 49: v1.ChatService.ToggleChannelModeration:input_type -> v1.ToggleChannelModerationRequest + 48, // 50: v1.ChatService.ToggleChannelAnnouncements:input_type -> v1.ToggleChannelAnnouncementsRequest + 50, // 51: v1.ChatService.InviteToChannel:input_type -> v1.InviteToChannelRequest + 19, // 52: v1.ChatService.SendWhisperMessage:output_type -> v1.SendWhisperMessageResponse + 21, // 53: v1.ChatService.JoinChannel:output_type -> v1.JoinChannelResponse + 23, // 54: v1.ChatService.LeaveChannel:output_type -> v1.LeaveChannelResponse + 25, // 55: v1.ChatService.SendChannelMessage:output_type -> v1.SendChannelMessageResponse + 27, // 56: v1.ChatService.GetChannelList:output_type -> v1.GetChannelListResponse + 29, // 57: v1.ChatService.KickFromChannel:output_type -> v1.KickFromChannelResponse + 31, // 58: v1.ChatService.BanFromChannel:output_type -> v1.BanFromChannelResponse + 33, // 59: v1.ChatService.UnbanFromChannel:output_type -> v1.UnbanFromChannelResponse + 35, // 60: v1.ChatService.SetChannelModerator:output_type -> v1.SetChannelModeratorResponse + 37, // 61: v1.ChatService.UnsetChannelModerator:output_type -> v1.UnsetChannelModeratorResponse + 39, // 62: v1.ChatService.SetChannelMute:output_type -> v1.SetChannelMuteResponse + 41, // 63: v1.ChatService.UnsetChannelMute:output_type -> v1.UnsetChannelMuteResponse + 43, // 64: v1.ChatService.SetChannelOwner:output_type -> v1.SetChannelOwnerResponse + 45, // 65: v1.ChatService.SetChannelPassword:output_type -> v1.SetChannelPasswordResponse + 47, // 66: v1.ChatService.ToggleChannelModeration:output_type -> v1.ToggleChannelModerationResponse + 49, // 67: v1.ChatService.ToggleChannelAnnouncements:output_type -> v1.ToggleChannelAnnouncementsResponse + 51, // 68: v1.ChatService.InviteToChannel:output_type -> v1.InviteToChannelResponse + 52, // [52:69] is the sub-list for method output_type + 35, // [35:52] is the sub-list for method input_type + 35, // [35:35] is the sub-list for extension type_name + 35, // [35:35] is the sub-list for extension extendee + 0, // [0:35] is the sub-list for field type_name } func init() { file_chat_proto_init() } @@ -4450,7 +4818,7 @@ func file_chat_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_chat_proto_rawDesc, - NumEnums: 17, + NumEnums: 18, NumMessages: 36, NumExtensions: 0, NumServices: 1, diff --git a/gen/chat/gen/chat/pb/chat_grpc.pb.go b/gen/chat/gen/chat/pb/chat_grpc.pb.go index 4ca578c..27a4311 100644 --- a/gen/chat/gen/chat/pb/chat_grpc.pb.go +++ b/gen/chat/gen/chat/pb/chat_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v3.20.3 +// - protoc v3.21.12 // source: chat.proto package pb diff --git a/gen/chat/pb/chat.pb.go b/gen/chat/pb/chat.pb.go index f9aab7f..226c5cc 100644 --- a/gen/chat/pb/chat.pb.go +++ b/gen/chat/pb/chat.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v3.20.3 +// protoc v3.21.12 // source: chat.proto package pb @@ -72,8 +72,9 @@ func (TeamID) EnumDescriptor() ([]byte, []int) { type SendWhisperMessageResponse_Status int32 const ( - SendWhisperMessageResponse_Ok SendWhisperMessageResponse_Status = 0 - SendWhisperMessageResponse_CharacterNotFound SendWhisperMessageResponse_Status = 2 + SendWhisperMessageResponse_Ok SendWhisperMessageResponse_Status = 0 + SendWhisperMessageResponse_CharacterNotFound SendWhisperMessageResponse_Status = 2 + SendWhisperMessageResponse_CharacterAmbiguous SendWhisperMessageResponse_Status = 3 ) // Enum value maps for SendWhisperMessageResponse_Status. @@ -81,10 +82,12 @@ var ( SendWhisperMessageResponse_Status_name = map[int32]string{ 0: "Ok", 2: "CharacterNotFound", + 3: "CharacterAmbiguous", } SendWhisperMessageResponse_Status_value = map[string]int32{ - "Ok": 0, - "CharacterNotFound": 2, + "Ok": 0, + "CharacterNotFound": 2, + "CharacterAmbiguous": 3, } ) @@ -943,14 +946,20 @@ type SendWhisperMessageRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - SenderGUID uint64 `protobuf:"varint,3,opt,name=senderGUID,proto3" json:"senderGUID,omitempty"` - SenderName string `protobuf:"bytes,4,opt,name=senderName,proto3" json:"senderName,omitempty"` - SenderRace uint32 `protobuf:"varint,5,opt,name=senderRace,proto3" json:"senderRace,omitempty"` - Language uint32 `protobuf:"varint,6,opt,name=language,proto3" json:"language,omitempty"` - ReceiverName string `protobuf:"bytes,7,opt,name=receiverName,proto3" json:"receiverName,omitempty"` - Msg string `protobuf:"bytes,8,opt,name=msg,proto3" json:"msg,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + SenderGUID uint64 `protobuf:"varint,3,opt,name=senderGUID,proto3" json:"senderGUID,omitempty"` + SenderName string `protobuf:"bytes,4,opt,name=senderName,proto3" json:"senderName,omitempty"` + SenderRace uint32 `protobuf:"varint,5,opt,name=senderRace,proto3" json:"senderRace,omitempty"` + Language uint32 `protobuf:"varint,6,opt,name=language,proto3" json:"language,omitempty"` + ReceiverName string `protobuf:"bytes,7,opt,name=receiverName,proto3" json:"receiverName,omitempty"` + Msg string `protobuf:"bytes,8,opt,name=msg,proto3" json:"msg,omitempty"` + ReceiverRealmID uint32 `protobuf:"varint,9,opt,name=receiverRealmID,proto3" json:"receiverRealmID,omitempty"` + SenderClass uint32 `protobuf:"varint,10,opt,name=senderClass,proto3" json:"senderClass,omitempty"` + SenderGender uint32 `protobuf:"varint,11,opt,name=senderGender,proto3" json:"senderGender,omitempty"` + SenderAccountID uint32 `protobuf:"varint,12,opt,name=senderAccountID,proto3" json:"senderAccountID,omitempty"` + GatewayValidatedGameplayCrossrealmWhisper bool `protobuf:"varint,13,opt,name=gatewayValidatedGameplayCrossrealmWhisper,proto3" json:"gatewayValidatedGameplayCrossrealmWhisper,omitempty"` + SenderChatTag uint32 `protobuf:"varint,14,opt,name=senderChatTag,proto3" json:"senderChatTag,omitempty"` } func (x *SendWhisperMessageRequest) Reset() { @@ -1041,14 +1050,61 @@ func (x *SendWhisperMessageRequest) GetMsg() string { return "" } +func (x *SendWhisperMessageRequest) GetReceiverRealmID() uint32 { + if x != nil { + return x.ReceiverRealmID + } + return 0 +} + +func (x *SendWhisperMessageRequest) GetSenderClass() uint32 { + if x != nil { + return x.SenderClass + } + return 0 +} + +func (x *SendWhisperMessageRequest) GetSenderGender() uint32 { + if x != nil { + return x.SenderGender + } + return 0 +} + +func (x *SendWhisperMessageRequest) GetSenderAccountID() uint32 { + if x != nil { + return x.SenderAccountID + } + return 0 +} + +func (x *SendWhisperMessageRequest) GetGatewayValidatedGameplayCrossrealmWhisper() bool { + if x != nil { + return x.GatewayValidatedGameplayCrossrealmWhisper + } + return false +} + +func (x *SendWhisperMessageRequest) GetSenderChatTag() uint32 { + if x != nil { + return x.SenderChatTag + } + return 0 +} + type SendWhisperMessageResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - Status SendWhisperMessageResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.SendWhisperMessageResponse_Status" json:"status,omitempty"` - ReceiverGUID uint64 `protobuf:"varint,3,opt,name=receiverGUID,proto3" json:"receiverGUID,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status SendWhisperMessageResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.SendWhisperMessageResponse_Status" json:"status,omitempty"` + ReceiverGUID uint64 `protobuf:"varint,3,opt,name=receiverGUID,proto3" json:"receiverGUID,omitempty"` + ReceiverRealmID uint32 `protobuf:"varint,4,opt,name=receiverRealmID,proto3" json:"receiverRealmID,omitempty"` + ReceiverName string `protobuf:"bytes,5,opt,name=receiverName,proto3" json:"receiverName,omitempty"` + ReceiverRace uint32 `protobuf:"varint,6,opt,name=receiverRace,proto3" json:"receiverRace,omitempty"` + ReceiverClass uint32 `protobuf:"varint,7,opt,name=receiverClass,proto3" json:"receiverClass,omitempty"` + ReceiverGender uint32 `protobuf:"varint,8,opt,name=receiverGender,proto3" json:"receiverGender,omitempty"` } func (x *SendWhisperMessageResponse) Reset() { @@ -1104,6 +1160,41 @@ func (x *SendWhisperMessageResponse) GetReceiverGUID() uint64 { return 0 } +func (x *SendWhisperMessageResponse) GetReceiverRealmID() uint32 { + if x != nil { + return x.ReceiverRealmID + } + return 0 +} + +func (x *SendWhisperMessageResponse) GetReceiverName() string { + if x != nil { + return x.ReceiverName + } + return "" +} + +func (x *SendWhisperMessageResponse) GetReceiverRace() uint32 { + if x != nil { + return x.ReceiverRace + } + return 0 +} + +func (x *SendWhisperMessageResponse) GetReceiverClass() uint32 { + if x != nil { + return x.ReceiverClass + } + return 0 +} + +func (x *SendWhisperMessageResponse) GetReceiverGender() uint32 { + if x != nil { + return x.ReceiverGender + } + return 0 +} + type JoinChannelRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1417,14 +1508,15 @@ type SendChannelMessageRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - SenderGUID uint64 `protobuf:"varint,3,opt,name=senderGUID,proto3" json:"senderGUID,omitempty"` - SenderName string `protobuf:"bytes,4,opt,name=senderName,proto3" json:"senderName,omitempty"` - ChannelName string `protobuf:"bytes,5,opt,name=channelName,proto3" json:"channelName,omitempty"` - Language uint32 `protobuf:"varint,6,opt,name=language,proto3" json:"language,omitempty"` - Message string `protobuf:"bytes,7,opt,name=message,proto3" json:"message,omitempty"` - TeamID TeamID `protobuf:"varint,8,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + SenderGUID uint64 `protobuf:"varint,3,opt,name=senderGUID,proto3" json:"senderGUID,omitempty"` + SenderName string `protobuf:"bytes,4,opt,name=senderName,proto3" json:"senderName,omitempty"` + ChannelName string `protobuf:"bytes,5,opt,name=channelName,proto3" json:"channelName,omitempty"` + Language uint32 `protobuf:"varint,6,opt,name=language,proto3" json:"language,omitempty"` + Message string `protobuf:"bytes,7,opt,name=message,proto3" json:"message,omitempty"` + TeamID TeamID `protobuf:"varint,8,opt,name=teamID,proto3,enum=v1.TeamID" json:"teamID,omitempty"` + SenderChatTag uint32 `protobuf:"varint,9,opt,name=senderChatTag,proto3" json:"senderChatTag,omitempty"` } func (x *SendChannelMessageRequest) Reset() { @@ -1515,6 +1607,13 @@ func (x *SendChannelMessageRequest) GetTeamID() TeamID { return TeamID_TEAM_ALLIANCE } +func (x *SendChannelMessageRequest) GetSenderChatTag() uint32 { + if x != nil { + return x.SenderChatTag + } + return 0 +} + type SendChannelMessageResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3562,7 +3661,7 @@ var File_chat_proto protoreflect.FileDescriptor var file_chat_proto_rawDesc = []byte{ 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, - 0x22, 0xf9, 0x01, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, + 0x22, 0x97, 0x04, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, @@ -3577,521 +3676,555 @@ var file_chat_proto_rawDesc = []byte{ 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, - 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0xba, 0x01, 0x0a, - 0x1a, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3d, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x0a, 0x0c, - 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, - 0x22, 0x27, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, - 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4e, - 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x02, 0x22, 0xa4, 0x02, 0x0a, 0x12, 0x4a, 0x6f, - 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, - 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, - 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, - 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, - 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x22, 0x0a, 0x0c, - 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, - 0x22, 0xeb, 0x01, 0x0a, 0x13, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x36, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x76, 0x31, 0x2e, - 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x22, 0x5f, 0x0a, - 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, - 0x11, 0x0a, 0x0d, 0x57, 0x72, 0x6f, 0x6e, 0x67, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x10, 0x02, 0x12, 0x10, - 0x0a, 0x0c, 0x57, 0x72, 0x6f, 0x6e, 0x67, 0x46, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x03, - 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x49, 0x6e, 0x41, 0x72, 0x65, 0x61, 0x10, 0x04, 0x12, - 0x0d, 0x0a, 0x09, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x10, 0x05, 0x22, 0xa7, - 0x01, 0x0a, 0x13, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, + 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x28, 0x0a, 0x0f, + 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, + 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, + 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x0f, + 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x5c, 0x0a, 0x29, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x47, 0x61, 0x6d, 0x65, 0x70, 0x6c, + 0x61, 0x79, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x57, 0x68, 0x69, 0x73, + 0x70, 0x65, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x29, 0x67, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x47, 0x61, 0x6d, 0x65, 0x70, + 0x6c, 0x61, 0x79, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x57, 0x68, 0x69, + 0x73, 0x70, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x68, + 0x61, 0x74, 0x54, 0x61, 0x67, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x73, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x43, 0x68, 0x61, 0x74, 0x54, 0x61, 0x67, 0x22, 0x92, 0x03, 0x0a, 0x1a, 0x53, + 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3d, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, + 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x28, + 0x0a, 0x0f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, + 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x61, 0x63, 0x65, + 0x12, 0x24, 0x0a, 0x0d, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x43, 0x6c, 0x61, 0x73, + 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, + 0x65, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, + 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x22, 0x3f, + 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, + 0x12, 0x15, 0x0a, 0x11, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x74, + 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x02, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x68, 0x61, 0x72, 0x61, + 0x63, 0x74, 0x65, 0x72, 0x41, 0x6d, 0x62, 0x69, 0x67, 0x75, 0x6f, 0x75, 0x73, 0x10, 0x03, 0x22, + 0xa4, 0x02, 0x0a, 0x12, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, - 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, - 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0x82, 0x01, 0x0a, 0x14, 0x4c, 0x65, 0x61, - 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x61, 0x70, 0x69, 0x12, 0x37, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x1f, 0x0a, 0x06, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, - 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x22, 0x83, 0x02, - 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, - 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, - 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x6e, - 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, - 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x6e, - 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, - 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x61, 0x6e, - 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, - 0x6d, 0x49, 0x44, 0x22, 0xa8, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x61, 0x70, 0x69, 0x12, 0x3d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x22, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, - 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x4d, 0x75, 0x74, 0x65, 0x64, 0x10, 0x02, 0x12, - 0x0d, 0x0a, 0x09, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x10, 0x03, 0x22, 0xa9, - 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, - 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, - 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, - 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, - 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, - 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb3, 0x01, 0x0a, 0x16, 0x47, - 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x39, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, - 0x1f, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, - 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, - 0x22, 0xca, 0x01, 0x0a, 0x16, 0x4b, 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, - 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6b, 0x69, 0x63, 0x6b, 0x65, - 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6b, 0x69, 0x63, - 0x6b, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, - 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xae, 0x01, - 0x0a, 0x17, 0x4b, 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3a, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x76, 0x31, - 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x45, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, - 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xc9, - 0x01, 0x0a, 0x15, 0x42, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, - 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, - 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x47, 0x55, - 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, - 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, - 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, - 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xac, 0x01, 0x0a, 0x16, 0x42, - 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x39, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x6e, - 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x22, 0x45, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, - 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, - 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xcf, 0x01, 0x0a, 0x17, 0x55, 0x6e, - 0x62, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, - 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, - 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x75, 0x6e, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x47, 0x55, 0x49, - 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x75, 0x6e, 0x62, 0x61, 0x6e, 0x6e, 0x65, - 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, - 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, - 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb1, 0x01, 0x0a, 0x18, - 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3b, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x76, 0x31, 0x2e, - 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x46, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, - 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x10, 0x03, 0x22, - 0xce, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, - 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, + 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x6c, 0x61, + 0x67, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0xeb, 0x01, 0x0a, 0x13, 0x4a, 0x6f, 0x69, 0x6e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, - 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, - 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, - 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, - 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, - 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, - 0x22, 0xb2, 0x01, 0x0a, 0x1b, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, - 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x3e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x12, 0x36, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x22, 0x5f, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, + 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x57, 0x72, 0x6f, 0x6e, 0x67, 0x50, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x61, 0x6e, 0x6e, + 0x65, 0x64, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x57, 0x72, 0x6f, 0x6e, 0x67, 0x46, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x49, 0x6e, 0x41, + 0x72, 0x65, 0x61, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, + 0x65, 0x64, 0x10, 0x05, 0x22, 0xa7, 0x01, 0x0a, 0x13, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, + 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, + 0x61, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0x82, + 0x01, 0x0a, 0x14, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x37, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x22, 0x41, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, + 0x75, 0x73, 0x22, 0x1f, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x10, - 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, - 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xd0, 0x01, 0x0a, 0x1c, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, - 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, - 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, - 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, - 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, - 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb6, 0x01, 0x0a, 0x1d, 0x55, 0x6e, 0x73, - 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, - 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x40, 0x0a, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x76, - 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, - 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x41, - 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, - 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, - 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, - 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, - 0x03, 0x22, 0xc7, 0x01, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, - 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x6d, 0x75, 0x74, 0x65, 0x72, - 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x75, 0x74, 0x65, - 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, - 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, - 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xac, 0x01, 0x0a, 0x16, - 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x39, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x72, 0x10, 0x01, 0x22, 0xa9, 0x02, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, + 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, + 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x43, 0x68, 0x61, 0x74, 0x54, 0x61, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0d, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x68, 0x61, 0x74, 0x54, 0x61, 0x67, 0x22, + 0xa8, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x3d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, + 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, + 0x12, 0x09, 0x0a, 0x05, 0x4d, 0x75, 0x74, 0x65, 0x64, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x54, + 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x10, 0x03, 0x22, 0xa9, 0x01, 0x0a, 0x15, 0x47, + 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb3, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x39, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, + 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0x1f, 0x0a, 0x06, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, + 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x22, 0xca, 0x01, 0x0a, + 0x16, 0x4b, 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6b, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6b, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, + 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xae, 0x01, 0x0a, 0x17, 0x4b, 0x69, + 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x69, 0x63, + 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x45, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xcd, 0x01, 0x0a, 0x17, 0x55, - 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, - 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, - 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x75, 0x6e, 0x6d, 0x75, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, - 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x75, 0x6e, 0x6d, 0x75, 0x74, 0x65, 0x72, - 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, - 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, - 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb0, 0x01, 0x0a, 0x18, 0x55, - 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3b, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x55, - 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x45, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, - 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xca, 0x01, - 0x0a, 0x16, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, - 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, - 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, - 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, - 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, - 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, - 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xaa, 0x01, 0x0a, 0x17, 0x53, - 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x22, 0x41, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, - 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, - 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, - 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xc9, 0x01, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, - 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, - 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xc9, 0x01, 0x0a, 0x15, 0x42, + 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xac, 0x01, 0x0a, 0x16, 0x42, 0x61, 0x6e, 0x46, 0x72, + 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x39, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x45, + 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, + 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, + 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x10, + 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, + 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xcf, 0x01, 0x0a, 0x17, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, + 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x22, 0x0a, + 0x0c, 0x75, 0x6e, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0c, 0x75, 0x6e, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, - 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, - 0x6d, 0x49, 0x44, 0x22, 0x9c, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x61, 0x70, 0x69, 0x12, 0x3d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, + 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb1, 0x01, 0x0a, 0x18, 0x55, 0x6e, 0x62, 0x61, + 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x62, 0x61, + 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x22, 0x2d, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, + 0x74, 0x75, 0x73, 0x22, 0x46, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, - 0x10, 0x02, 0x22, 0xb4, 0x01, 0x0a, 0x1e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, - 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, - 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x72, 0x47, - 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, - 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, - 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xd8, 0x01, 0x0a, 0x1f, 0x54, 0x6f, - 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, - 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, - 0x42, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x2a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x6d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, - 0x6d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x22, 0x31, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, - 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x10, 0x02, 0x22, 0xb7, 0x01, 0x0a, 0x21, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, + 0x65, 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x4e, 0x6f, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x10, 0x03, 0x22, 0xce, 0x01, 0x0a, 0x1a, + 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, - 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, - 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, 0x6f, 0x67, - 0x67, 0x6c, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, - 0x61, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xe4, - 0x01, 0x0a, 0x22, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x45, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, - 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, - 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, - 0x0a, 0x14, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x45, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x6e, - 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x22, 0x31, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, - 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x10, 0x02, 0x22, 0xcc, 0x01, 0x0a, 0x16, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, - 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, - 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, + 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, + 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, + 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb2, 0x01, 0x0a, + 0x1b, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3e, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, + 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x41, + 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, + 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, + 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, + 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, + 0x03, 0x22, 0xd0, 0x01, 0x0a, 0x1c, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, + 0x0a, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, - 0x61, 0x6d, 0x49, 0x44, 0x22, 0xd9, 0x01, 0x0a, 0x17, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, - 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x70, + 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb6, 0x01, 0x0a, 0x1d, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x40, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, + 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x41, 0x0a, 0x06, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, + 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, + 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xc7, 0x01, + 0x0a, 0x15, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x6d, 0x75, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x75, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, + 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xac, 0x01, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x39, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0x45, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, + 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, + 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, + 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xcd, 0x01, 0x0a, 0x17, 0x55, 0x6e, 0x73, 0x65, 0x74, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, + 0x0a, 0x0b, 0x75, 0x6e, 0x6d, 0x75, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0b, 0x75, 0x6e, 0x6d, 0x75, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xb0, 0x01, 0x0a, 0x18, 0x55, 0x6e, 0x73, 0x65, 0x74, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x22, 0x45, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, + 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, + 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x22, 0xca, 0x01, 0x0a, 0x16, 0x53, 0x65, + 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xaa, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x22, 0x41, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, + 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, + 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x10, 0x02, 0x12, + 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, + 0x64, 0x10, 0x03, 0x22, 0xc9, 0x01, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, + 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x22, 0x0a, 0x06, 0x74, + 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, + 0x9c, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x3d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0x2d, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, + 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, + 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x10, 0x02, 0x22, 0xb4, + 0x01, 0x0a, 0x1e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, + 0x0b, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0b, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, + 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, + 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xd8, 0x01, 0x0a, 0x1f, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x42, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, + 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x2c, 0x0a, 0x11, 0x6d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x6d, 0x6f, 0x64, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x31, 0x0a, + 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, + 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x10, + 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x10, 0x02, + 0x22, 0xb7, 0x01, 0x0a, 0x21, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, + 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, + 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0xe4, 0x01, 0x0a, 0x22, 0x54, + 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, + 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x45, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x61, 0x6e, + 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, + 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x31, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, - 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, - 0x64, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x41, 0x6c, 0x72, - 0x65, 0x61, 0x64, 0x79, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, - 0x57, 0x72, 0x6f, 0x6e, 0x67, 0x46, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x04, 0x12, 0x10, - 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x10, 0x05, - 0x22, 0x97, 0x01, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, - 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x4d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, - 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x4d, 0x0a, 0x0d, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x67, - 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, 0x75, 0x69, 0x64, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2a, 0x3d, 0x0a, 0x06, 0x54, 0x65, 0x61, - 0x6d, 0x49, 0x44, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x41, 0x4c, 0x4c, 0x49, - 0x41, 0x4e, 0x43, 0x45, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x48, - 0x4f, 0x52, 0x44, 0x45, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4e, - 0x45, 0x55, 0x54, 0x52, 0x41, 0x4c, 0x10, 0x02, 0x32, 0xf3, 0x0a, 0x0a, 0x0b, 0x43, 0x68, 0x61, - 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x53, 0x0a, 0x12, 0x53, 0x65, 0x6e, 0x64, - 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, - 0x0b, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x16, 0x2e, 0x76, - 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, - 0x0c, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x17, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, - 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x53, 0x0a, 0x12, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, - 0x0a, 0x0f, 0x4b, 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x10, + 0x02, 0x22, 0xcc, 0x01, 0x0a, 0x16, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, + 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x76, 0x69, + 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, + 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, + 0x22, 0xd9, 0x01, 0x0a, 0x17, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3a, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, + 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x70, 0x0a, 0x06, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, + 0x4e, 0x6f, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x50, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x02, 0x12, + 0x17, 0x0a, 0x13, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x57, 0x72, 0x6f, 0x6e, + 0x67, 0x46, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x10, 0x05, 0x22, 0x97, 0x01, 0x0a, + 0x0b, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x12, 0x14, + 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, + 0x6c, 0x61, 0x67, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x46, 0x6c, + 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x4d, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x75, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, + 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2a, 0x3d, 0x0a, 0x06, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, + 0x11, 0x0a, 0x0d, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x41, 0x4c, 0x4c, 0x49, 0x41, 0x4e, 0x43, 0x45, + 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x48, 0x4f, 0x52, 0x44, 0x45, + 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4e, 0x45, 0x55, 0x54, 0x52, + 0x41, 0x4c, 0x10, 0x02, 0x32, 0xf3, 0x0a, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x74, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x53, 0x0a, 0x12, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, + 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x65, 0x6e, 0x64, 0x57, 0x68, 0x69, 0x73, 0x70, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0b, 0x4a, 0x6f, 0x69, + 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, + 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x4c, 0x65, 0x61, + 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x12, + 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, + 0x69, 0x73, 0x74, 0x12, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0f, 0x4b, 0x69, + 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x42, 0x61, - 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x76, - 0x31, 0x2e, 0x42, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x6e, - 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x10, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x62, - 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, - 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x56, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x15, 0x55, 0x6e, - 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, + 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x4b, + 0x69, 0x63, 0x6b, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x42, 0x61, 0x6e, 0x46, 0x72, 0x6f, + 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, + 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x4d, 0x0a, 0x10, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, 0x72, + 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, + 0x0a, 0x13, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x4d, 0x0a, 0x10, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4a, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, - 0x6e, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, - 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x12, - 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, - 0x72, 0x64, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x62, 0x0a, 0x17, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x76, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x15, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, + 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, + 0x10, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, + 0x65, 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, + 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x4d, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0f, + 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, + 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, + 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x77, 0x6e, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1d, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, + 0x17, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, + 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, + 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, - 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, 0x1a, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, - 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0f, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, - 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0d, - 0x5a, 0x0b, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x74, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x6b, 0x0a, 0x1a, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, + 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, + 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, + 0x0a, 0x0f, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0d, 0x5a, 0x0b, 0x67, 0x65, + 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x74, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/gen/chat/pb/chat_grpc.pb.go b/gen/chat/pb/chat_grpc.pb.go index 4ca578c..27a4311 100644 --- a/gen/chat/pb/chat_grpc.pb.go +++ b/gen/chat/pb/chat_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v3.20.3 +// - protoc v3.21.12 // source: chat.proto package pb diff --git a/gen/group/pb/group.pb.go b/gen/group/pb/group.pb.go index 6e20280..22bfd95 100644 --- a/gen/group/pb/group.pb.go +++ b/gen/group/pb/group.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v3.20.3 +// protoc v3.21.12 // source: group.proto package pb @@ -115,6 +115,55 @@ func (AcceptInviteResponse_Status) EnumDescriptor() ([]byte, []int) { return file_group_proto_rawDescGZIP(), []int{3, 0} } +type DeclineInviteResponse_Status int32 + +const ( + DeclineInviteResponse_Ok DeclineInviteResponse_Status = 0 + DeclineInviteResponse_InviteNotFound DeclineInviteResponse_Status = 1 + DeclineInviteResponse_Error DeclineInviteResponse_Status = 2 +) + +// Enum value maps for DeclineInviteResponse_Status. +var ( + DeclineInviteResponse_Status_name = map[int32]string{ + 0: "Ok", + 1: "InviteNotFound", + 2: "Error", + } + DeclineInviteResponse_Status_value = map[string]int32{ + "Ok": 0, + "InviteNotFound": 1, + "Error": 2, + } +) + +func (x DeclineInviteResponse_Status) Enum() *DeclineInviteResponse_Status { + p := new(DeclineInviteResponse_Status) + *p = x + return p +} + +func (x DeclineInviteResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (DeclineInviteResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_group_proto_enumTypes[2].Descriptor() +} + +func (DeclineInviteResponse_Status) Type() protoreflect.EnumType { + return &file_group_proto_enumTypes[2] +} + +func (x DeclineInviteResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use DeclineInviteResponse_Status.Descriptor instead. +func (DeclineInviteResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{5, 0} +} + type UninviteResponse_Status int32 const ( @@ -145,11 +194,11 @@ func (x UninviteResponse_Status) String() string { } func (UninviteResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_group_proto_enumTypes[2].Descriptor() + return file_group_proto_enumTypes[3].Descriptor() } func (UninviteResponse_Status) Type() protoreflect.EnumType { - return &file_group_proto_enumTypes[2] + return &file_group_proto_enumTypes[3] } func (x UninviteResponse_Status) Number() protoreflect.EnumNumber { @@ -158,7 +207,7 @@ func (x UninviteResponse_Status) Number() protoreflect.EnumNumber { // Deprecated: Use UninviteResponse_Status.Descriptor instead. func (UninviteResponse_Status) EnumDescriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{10, 0} + return file_group_proto_rawDescGZIP(), []int{15, 0} } type SetDungeonDifficultyResponse_Status int32 @@ -191,11 +240,11 @@ func (x SetDungeonDifficultyResponse_Status) String() string { } func (SetDungeonDifficultyResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_group_proto_enumTypes[3].Descriptor() + return file_group_proto_enumTypes[4].Descriptor() } func (SetDungeonDifficultyResponse_Status) Type() protoreflect.EnumType { - return &file_group_proto_enumTypes[3] + return &file_group_proto_enumTypes[4] } func (x SetDungeonDifficultyResponse_Status) Number() protoreflect.EnumNumber { @@ -204,7 +253,7 @@ func (x SetDungeonDifficultyResponse_Status) Number() protoreflect.EnumNumber { // Deprecated: Use SetDungeonDifficultyResponse_Status.Descriptor instead. func (SetDungeonDifficultyResponse_Status) EnumDescriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{26, 0} + return file_group_proto_rawDescGZIP(), []int{31, 0} } type SetRaidDifficultyResponse_Status int32 @@ -237,11 +286,11 @@ func (x SetRaidDifficultyResponse_Status) String() string { } func (SetRaidDifficultyResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_group_proto_enumTypes[4].Descriptor() + return file_group_proto_enumTypes[5].Descriptor() } func (SetRaidDifficultyResponse_Status) Type() protoreflect.EnumType { - return &file_group_proto_enumTypes[4] + return &file_group_proto_enumTypes[5] } func (x SetRaidDifficultyResponse_Status) Number() protoreflect.EnumNumber { @@ -250,7 +299,7 @@ func (x SetRaidDifficultyResponse_Status) Number() protoreflect.EnumNumber { // Deprecated: Use SetRaidDifficultyResponse_Status.Descriptor instead. func (SetRaidDifficultyResponse_Status) EnumDescriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{28, 0} + return file_group_proto_rawDescGZIP(), []int{33, 0} } type InviteParams struct { @@ -513,6 +562,124 @@ func (x *AcceptInviteResponse) GetStatus() AcceptInviteResponse_Status { return AcceptInviteResponse_Ok } +type DeclineInviteParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + Player uint64 `protobuf:"varint,3,opt,name=player,proto3" json:"player,omitempty"` +} + +func (x *DeclineInviteParams) Reset() { + *x = DeclineInviteParams{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeclineInviteParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeclineInviteParams) ProtoMessage() {} + +func (x *DeclineInviteParams) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeclineInviteParams.ProtoReflect.Descriptor instead. +func (*DeclineInviteParams) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{4} +} + +func (x *DeclineInviteParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *DeclineInviteParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *DeclineInviteParams) GetPlayer() uint64 { + if x != nil { + return x.Player + } + return 0 +} + +type DeclineInviteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status DeclineInviteResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.DeclineInviteResponse_Status" json:"status,omitempty"` +} + +func (x *DeclineInviteResponse) Reset() { + *x = DeclineInviteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeclineInviteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeclineInviteResponse) ProtoMessage() {} + +func (x *DeclineInviteResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeclineInviteResponse.ProtoReflect.Descriptor instead. +func (*DeclineInviteResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{5} +} + +func (x *DeclineInviteResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *DeclineInviteResponse) GetStatus() DeclineInviteResponse_Status { + if x != nil { + return x.Status + } + return DeclineInviteResponse_Ok +} + type GetGroupRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -526,7 +693,7 @@ type GetGroupRequest struct { func (x *GetGroupRequest) Reset() { *x = GetGroupRequest{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[4] + mi := &file_group_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -539,7 +706,7 @@ func (x *GetGroupRequest) String() string { func (*GetGroupRequest) ProtoMessage() {} func (x *GetGroupRequest) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[4] + mi := &file_group_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -552,7 +719,7 @@ func (x *GetGroupRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetGroupRequest.ProtoReflect.Descriptor instead. func (*GetGroupRequest) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{4} + return file_group_proto_rawDescGZIP(), []int{6} } func (x *GetGroupRequest) GetApi() string { @@ -588,7 +755,7 @@ type GetGroupResponse struct { func (x *GetGroupResponse) Reset() { *x = GetGroupResponse{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[5] + mi := &file_group_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -601,7 +768,7 @@ func (x *GetGroupResponse) String() string { func (*GetGroupResponse) ProtoMessage() {} func (x *GetGroupResponse) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[5] + mi := &file_group_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -614,7 +781,7 @@ func (x *GetGroupResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetGroupResponse.ProtoReflect.Descriptor instead. func (*GetGroupResponse) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{5} + return file_group_proto_rawDescGZIP(), []int{7} } func (x *GetGroupResponse) GetApi() string { @@ -644,7 +811,7 @@ type GetGroupByMemberRequest struct { func (x *GetGroupByMemberRequest) Reset() { *x = GetGroupByMemberRequest{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[6] + mi := &file_group_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -657,7 +824,7 @@ func (x *GetGroupByMemberRequest) String() string { func (*GetGroupByMemberRequest) ProtoMessage() {} func (x *GetGroupByMemberRequest) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[6] + mi := &file_group_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -670,7 +837,7 @@ func (x *GetGroupByMemberRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetGroupByMemberRequest.ProtoReflect.Descriptor instead. func (*GetGroupByMemberRequest) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{6} + return file_group_proto_rawDescGZIP(), []int{8} } func (x *GetGroupByMemberRequest) GetApi() string { @@ -707,7 +874,7 @@ type GetGroupIDByPlayerRequest struct { func (x *GetGroupIDByPlayerRequest) Reset() { *x = GetGroupIDByPlayerRequest{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[7] + mi := &file_group_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -720,7 +887,7 @@ func (x *GetGroupIDByPlayerRequest) String() string { func (*GetGroupIDByPlayerRequest) ProtoMessage() {} func (x *GetGroupIDByPlayerRequest) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[7] + mi := &file_group_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -733,7 +900,7 @@ func (x *GetGroupIDByPlayerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetGroupIDByPlayerRequest.ProtoReflect.Descriptor instead. func (*GetGroupIDByPlayerRequest) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{7} + return file_group_proto_rawDescGZIP(), []int{9} } func (x *GetGroupIDByPlayerRequest) GetApi() string { @@ -762,14 +929,15 @@ type GetGroupIDByPlayerResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - GroupID uint32 `protobuf:"varint,2,opt,name=groupID,proto3" json:"groupID,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + GroupID uint32 `protobuf:"varint,2,opt,name=groupID,proto3" json:"groupID,omitempty"` + GroupRealmID uint32 `protobuf:"varint,3,opt,name=groupRealmID,proto3" json:"groupRealmID,omitempty"` } func (x *GetGroupIDByPlayerResponse) Reset() { *x = GetGroupIDByPlayerResponse{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[8] + mi := &file_group_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -782,7 +950,7 @@ func (x *GetGroupIDByPlayerResponse) String() string { func (*GetGroupIDByPlayerResponse) ProtoMessage() {} func (x *GetGroupIDByPlayerResponse) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[8] + mi := &file_group_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -795,7 +963,7 @@ func (x *GetGroupIDByPlayerResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetGroupIDByPlayerResponse.ProtoReflect.Descriptor instead. func (*GetGroupIDByPlayerResponse) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{8} + return file_group_proto_rawDescGZIP(), []int{10} } func (x *GetGroupIDByPlayerResponse) GetApi() string { @@ -812,35 +980,40 @@ func (x *GetGroupIDByPlayerResponse) GetGroupID() uint32 { return 0 } -type UninviteParams struct { +func (x *GetGroupIDByPlayerResponse) GetGroupRealmID() uint32 { + if x != nil { + return x.GroupRealmID + } + return 0 +} + +type GetMemberPlacementsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - Initiator uint64 `protobuf:"varint,3,opt,name=initiator,proto3" json:"initiator,omitempty"` - Target uint64 `protobuf:"varint,4,opt,name=target,proto3" json:"target,omitempty"` - Reason string `protobuf:"bytes,5,opt,name=reason,proto3" json:"reason,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + MemberGUIDs []uint64 `protobuf:"varint,3,rep,packed,name=memberGUIDs,proto3" json:"memberGUIDs,omitempty"` } -func (x *UninviteParams) Reset() { - *x = UninviteParams{} +func (x *GetMemberPlacementsRequest) Reset() { + *x = GetMemberPlacementsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[9] + mi := &file_group_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *UninviteParams) String() string { +func (x *GetMemberPlacementsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*UninviteParams) ProtoMessage() {} +func (*GetMemberPlacementsRequest) ProtoMessage() {} -func (x *UninviteParams) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[9] +func (x *GetMemberPlacementsRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -851,72 +1024,66 @@ func (x *UninviteParams) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use UninviteParams.ProtoReflect.Descriptor instead. -func (*UninviteParams) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{9} +// Deprecated: Use GetMemberPlacementsRequest.ProtoReflect.Descriptor instead. +func (*GetMemberPlacementsRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{11} } -func (x *UninviteParams) GetApi() string { +func (x *GetMemberPlacementsRequest) GetApi() string { if x != nil { return x.Api } return "" } -func (x *UninviteParams) GetRealmID() uint32 { +func (x *GetMemberPlacementsRequest) GetRealmID() uint32 { if x != nil { return x.RealmID } return 0 } -func (x *UninviteParams) GetInitiator() uint64 { - if x != nil { - return x.Initiator - } - return 0 -} - -func (x *UninviteParams) GetTarget() uint64 { - if x != nil { - return x.Target - } - return 0 -} - -func (x *UninviteParams) GetReason() string { +func (x *GetMemberPlacementsRequest) GetMemberGUIDs() []uint64 { if x != nil { - return x.Reason + return x.MemberGUIDs } - return "" + return nil } -type UninviteResponse struct { +type MemberPlacement struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - Status UninviteResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.UninviteResponse_Status" json:"status,omitempty"` + MemberGUID uint64 `protobuf:"varint,1,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + Online bool `protobuf:"varint,2,opt,name=online,proto3" json:"online,omitempty"` + Fresh bool `protobuf:"varint,3,opt,name=fresh,proto3" json:"fresh,omitempty"` + GatewayID string `protobuf:"bytes,4,opt,name=gatewayID,proto3" json:"gatewayID,omitempty"` + WorldserverID string `protobuf:"bytes,5,opt,name=worldserverID,proto3" json:"worldserverID,omitempty"` + MapID uint32 `protobuf:"varint,6,opt,name=mapID,proto3" json:"mapID,omitempty"` + InstanceID uint32 `protobuf:"varint,7,opt,name=instanceID,proto3" json:"instanceID,omitempty"` + InstanceKnown bool `protobuf:"varint,8,opt,name=instanceKnown,proto3" json:"instanceKnown,omitempty"` + TimestampMs uint64 `protobuf:"varint,9,opt,name=timestampMs,proto3" json:"timestampMs,omitempty"` + UpdatedAtMs uint64 `protobuf:"varint,10,opt,name=updatedAtMs,proto3" json:"updatedAtMs,omitempty"` } -func (x *UninviteResponse) Reset() { - *x = UninviteResponse{} +func (x *MemberPlacement) Reset() { + *x = MemberPlacement{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[10] + mi := &file_group_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *UninviteResponse) String() string { +func (x *MemberPlacement) String() string { return protoimpl.X.MessageStringOf(x) } -func (*UninviteResponse) ProtoMessage() {} +func (*MemberPlacement) ProtoMessage() {} -func (x *UninviteResponse) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[10] +func (x *MemberPlacement) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -927,113 +1094,107 @@ func (x *UninviteResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use UninviteResponse.ProtoReflect.Descriptor instead. -func (*UninviteResponse) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{10} +// Deprecated: Use MemberPlacement.ProtoReflect.Descriptor instead. +func (*MemberPlacement) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{12} } -func (x *UninviteResponse) GetApi() string { +func (x *MemberPlacement) GetMemberGUID() uint64 { if x != nil { - return x.Api + return x.MemberGUID } - return "" + return 0 } -func (x *UninviteResponse) GetStatus() UninviteResponse_Status { +func (x *MemberPlacement) GetOnline() bool { if x != nil { - return x.Status + return x.Online } - return UninviteResponse_Ok -} - -type GroupLeaveParams struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - Player uint64 `protobuf:"varint,3,opt,name=player,proto3" json:"player,omitempty"` + return false } -func (x *GroupLeaveParams) Reset() { - *x = GroupLeaveParams{} - if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *MemberPlacement) GetFresh() bool { + if x != nil { + return x.Fresh } + return false } -func (x *GroupLeaveParams) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *MemberPlacement) GetGatewayID() string { + if x != nil { + return x.GatewayID + } + return "" } -func (*GroupLeaveParams) ProtoMessage() {} +func (x *MemberPlacement) GetWorldserverID() string { + if x != nil { + return x.WorldserverID + } + return "" +} -func (x *GroupLeaveParams) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *MemberPlacement) GetMapID() uint32 { + if x != nil { + return x.MapID } - return mi.MessageOf(x) + return 0 } -// Deprecated: Use GroupLeaveParams.ProtoReflect.Descriptor instead. -func (*GroupLeaveParams) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{11} +func (x *MemberPlacement) GetInstanceID() uint32 { + if x != nil { + return x.InstanceID + } + return 0 } -func (x *GroupLeaveParams) GetApi() string { +func (x *MemberPlacement) GetInstanceKnown() bool { if x != nil { - return x.Api + return x.InstanceKnown } - return "" + return false } -func (x *GroupLeaveParams) GetRealmID() uint32 { +func (x *MemberPlacement) GetTimestampMs() uint64 { if x != nil { - return x.RealmID + return x.TimestampMs } return 0 } -func (x *GroupLeaveParams) GetPlayer() uint64 { +func (x *MemberPlacement) GetUpdatedAtMs() uint64 { if x != nil { - return x.Player + return x.UpdatedAtMs } return 0 } -type GroupLeaveResponse struct { +type GetMemberPlacementsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Placements []*MemberPlacement `protobuf:"bytes,2,rep,name=placements,proto3" json:"placements,omitempty"` } -func (x *GroupLeaveResponse) Reset() { - *x = GroupLeaveResponse{} +func (x *GetMemberPlacementsResponse) Reset() { + *x = GetMemberPlacementsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[12] + mi := &file_group_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GroupLeaveResponse) String() string { +func (x *GetMemberPlacementsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GroupLeaveResponse) ProtoMessage() {} +func (*GetMemberPlacementsResponse) ProtoMessage() {} -func (x *GroupLeaveResponse) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[12] +func (x *GetMemberPlacementsResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1044,45 +1205,54 @@ func (x *GroupLeaveResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GroupLeaveResponse.ProtoReflect.Descriptor instead. -func (*GroupLeaveResponse) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{12} +// Deprecated: Use GetMemberPlacementsResponse.ProtoReflect.Descriptor instead. +func (*GetMemberPlacementsResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{13} } -func (x *GroupLeaveResponse) GetApi() string { +func (x *GetMemberPlacementsResponse) GetApi() string { if x != nil { return x.Api } return "" } -type ConvertToRaidParams struct { +func (x *GetMemberPlacementsResponse) GetPlacements() []*MemberPlacement { + if x != nil { + return x.Placements + } + return nil +} + +type UninviteParams struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - Player uint64 `protobuf:"varint,3,opt,name=player,proto3" json:"player,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + Initiator uint64 `protobuf:"varint,3,opt,name=initiator,proto3" json:"initiator,omitempty"` + Target uint64 `protobuf:"varint,4,opt,name=target,proto3" json:"target,omitempty"` + Reason string `protobuf:"bytes,5,opt,name=reason,proto3" json:"reason,omitempty"` } -func (x *ConvertToRaidParams) Reset() { - *x = ConvertToRaidParams{} +func (x *UninviteParams) Reset() { + *x = UninviteParams{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[13] + mi := &file_group_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ConvertToRaidParams) String() string { +func (x *UninviteParams) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ConvertToRaidParams) ProtoMessage() {} +func (*UninviteParams) ProtoMessage() {} -func (x *ConvertToRaidParams) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[13] +func (x *UninviteParams) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1093,57 +1263,72 @@ func (x *ConvertToRaidParams) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ConvertToRaidParams.ProtoReflect.Descriptor instead. -func (*ConvertToRaidParams) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{13} +// Deprecated: Use UninviteParams.ProtoReflect.Descriptor instead. +func (*UninviteParams) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{14} } -func (x *ConvertToRaidParams) GetApi() string { +func (x *UninviteParams) GetApi() string { if x != nil { return x.Api } return "" } -func (x *ConvertToRaidParams) GetRealmID() uint32 { +func (x *UninviteParams) GetRealmID() uint32 { if x != nil { return x.RealmID } return 0 } -func (x *ConvertToRaidParams) GetPlayer() uint64 { +func (x *UninviteParams) GetInitiator() uint64 { if x != nil { - return x.Player + return x.Initiator } return 0 } -type ConvertToRaidResponse struct { +func (x *UninviteParams) GetTarget() uint64 { + if x != nil { + return x.Target + } + return 0 +} + +func (x *UninviteParams) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +type UninviteResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status UninviteResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.UninviteResponse_Status" json:"status,omitempty"` } -func (x *ConvertToRaidResponse) Reset() { - *x = ConvertToRaidResponse{} +func (x *UninviteResponse) Reset() { + *x = UninviteResponse{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[14] + mi := &file_group_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ConvertToRaidResponse) String() string { +func (x *UninviteResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ConvertToRaidResponse) ProtoMessage() {} +func (*UninviteResponse) ProtoMessage() {} -func (x *ConvertToRaidResponse) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[14] +func (x *UninviteResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1154,46 +1339,52 @@ func (x *ConvertToRaidResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ConvertToRaidResponse.ProtoReflect.Descriptor instead. -func (*ConvertToRaidResponse) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{14} +// Deprecated: Use UninviteResponse.ProtoReflect.Descriptor instead. +func (*UninviteResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{15} } -func (x *ConvertToRaidResponse) GetApi() string { +func (x *UninviteResponse) GetApi() string { if x != nil { return x.Api } return "" } -type ChangeLeaderParams struct { +func (x *UninviteResponse) GetStatus() UninviteResponse_Status { + if x != nil { + return x.Status + } + return UninviteResponse_Ok +} + +type GroupLeaveParams struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - Player uint64 `protobuf:"varint,3,opt,name=player,proto3" json:"player,omitempty"` - NewLeader uint64 `protobuf:"varint,4,opt,name=newLeader,proto3" json:"newLeader,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + Player uint64 `protobuf:"varint,3,opt,name=player,proto3" json:"player,omitempty"` } -func (x *ChangeLeaderParams) Reset() { - *x = ChangeLeaderParams{} +func (x *GroupLeaveParams) Reset() { + *x = GroupLeaveParams{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[15] + mi := &file_group_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ChangeLeaderParams) String() string { +func (x *GroupLeaveParams) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ChangeLeaderParams) ProtoMessage() {} +func (*GroupLeaveParams) ProtoMessage() {} -func (x *ChangeLeaderParams) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[15] +func (x *GroupLeaveParams) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1204,40 +1395,33 @@ func (x *ChangeLeaderParams) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ChangeLeaderParams.ProtoReflect.Descriptor instead. -func (*ChangeLeaderParams) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{15} +// Deprecated: Use GroupLeaveParams.ProtoReflect.Descriptor instead. +func (*GroupLeaveParams) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{16} } -func (x *ChangeLeaderParams) GetApi() string { +func (x *GroupLeaveParams) GetApi() string { if x != nil { return x.Api } return "" } -func (x *ChangeLeaderParams) GetRealmID() uint32 { +func (x *GroupLeaveParams) GetRealmID() uint32 { if x != nil { return x.RealmID } return 0 } -func (x *ChangeLeaderParams) GetPlayer() uint64 { +func (x *GroupLeaveParams) GetPlayer() uint64 { if x != nil { return x.Player } return 0 } -func (x *ChangeLeaderParams) GetNewLeader() uint64 { - if x != nil { - return x.NewLeader - } - return 0 -} - -type ChangeLeaderResponse struct { +type GroupLeaveResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -1245,23 +1429,23 @@ type ChangeLeaderResponse struct { Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` } -func (x *ChangeLeaderResponse) Reset() { - *x = ChangeLeaderResponse{} +func (x *GroupLeaveResponse) Reset() { + *x = GroupLeaveResponse{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[16] + mi := &file_group_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ChangeLeaderResponse) String() string { +func (x *GroupLeaveResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ChangeLeaderResponse) ProtoMessage() {} +func (*GroupLeaveResponse) ProtoMessage() {} -func (x *ChangeLeaderResponse) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[16] +func (x *GroupLeaveResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1272,48 +1456,45 @@ func (x *ChangeLeaderResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ChangeLeaderResponse.ProtoReflect.Descriptor instead. -func (*ChangeLeaderResponse) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{16} +// Deprecated: Use GroupLeaveResponse.ProtoReflect.Descriptor instead. +func (*GroupLeaveResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{17} } -func (x *ChangeLeaderResponse) GetApi() string { +func (x *GroupLeaveResponse) GetApi() string { if x != nil { return x.Api } return "" } -type SendGroupMessageParams struct { +type ConvertToRaidParams struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - SenderGUID uint64 `protobuf:"varint,3,opt,name=senderGUID,proto3" json:"senderGUID,omitempty"` - MessageType uint32 `protobuf:"varint,4,opt,name=messageType,proto3" json:"messageType,omitempty"` - Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` - Language uint32 `protobuf:"varint,6,opt,name=language,proto3" json:"language,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + Player uint64 `protobuf:"varint,3,opt,name=player,proto3" json:"player,omitempty"` } -func (x *SendGroupMessageParams) Reset() { - *x = SendGroupMessageParams{} +func (x *ConvertToRaidParams) Reset() { + *x = ConvertToRaidParams{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[17] + mi := &file_group_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SendGroupMessageParams) String() string { +func (x *ConvertToRaidParams) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SendGroupMessageParams) ProtoMessage() {} +func (*ConvertToRaidParams) ProtoMessage() {} -func (x *SendGroupMessageParams) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[17] +func (x *ConvertToRaidParams) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1324,78 +1505,57 @@ func (x *SendGroupMessageParams) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SendGroupMessageParams.ProtoReflect.Descriptor instead. -func (*SendGroupMessageParams) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{17} +// Deprecated: Use ConvertToRaidParams.ProtoReflect.Descriptor instead. +func (*ConvertToRaidParams) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{18} } -func (x *SendGroupMessageParams) GetApi() string { +func (x *ConvertToRaidParams) GetApi() string { if x != nil { return x.Api } return "" } -func (x *SendGroupMessageParams) GetRealmID() uint32 { +func (x *ConvertToRaidParams) GetRealmID() uint32 { if x != nil { return x.RealmID } return 0 } -func (x *SendGroupMessageParams) GetSenderGUID() uint64 { +func (x *ConvertToRaidParams) GetPlayer() uint64 { if x != nil { - return x.SenderGUID + return x.Player } return 0 } -func (x *SendGroupMessageParams) GetMessageType() uint32 { - if x != nil { - return x.MessageType - } - return 0 +type ConvertToRaidResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` } -func (x *SendGroupMessageParams) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *SendGroupMessageParams) GetLanguage() uint32 { - if x != nil { - return x.Language - } - return 0 -} - -type SendGroupMessageResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` -} - -func (x *SendGroupMessageResponse) Reset() { - *x = SendGroupMessageResponse{} +func (x *ConvertToRaidResponse) Reset() { + *x = ConvertToRaidResponse{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[18] + mi := &file_group_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SendGroupMessageResponse) String() string { +func (x *ConvertToRaidResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SendGroupMessageResponse) ProtoMessage() {} +func (*ConvertToRaidResponse) ProtoMessage() {} -func (x *SendGroupMessageResponse) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[18] +func (x *ConvertToRaidResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1406,47 +1566,46 @@ func (x *SendGroupMessageResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SendGroupMessageResponse.ProtoReflect.Descriptor instead. -func (*SendGroupMessageResponse) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{18} +// Deprecated: Use ConvertToRaidResponse.ProtoReflect.Descriptor instead. +func (*ConvertToRaidResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{19} } -func (x *SendGroupMessageResponse) GetApi() string { +func (x *ConvertToRaidResponse) GetApi() string { if x != nil { return x.Api } return "" } -type SetGroupTargetIconRequest struct { +type ChangeLeaderParams struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - SetterGUID uint64 `protobuf:"varint,3,opt,name=setterGUID,proto3" json:"setterGUID,omitempty"` - IconID uint32 `protobuf:"varint,4,opt,name=iconID,proto3" json:"iconID,omitempty"` - TargetGUID uint64 `protobuf:"varint,5,opt,name=targetGUID,proto3" json:"targetGUID,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + Player uint64 `protobuf:"varint,3,opt,name=player,proto3" json:"player,omitempty"` + NewLeader uint64 `protobuf:"varint,4,opt,name=newLeader,proto3" json:"newLeader,omitempty"` } -func (x *SetGroupTargetIconRequest) Reset() { - *x = SetGroupTargetIconRequest{} +func (x *ChangeLeaderParams) Reset() { + *x = ChangeLeaderParams{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[19] + mi := &file_group_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SetGroupTargetIconRequest) String() string { +func (x *ChangeLeaderParams) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetGroupTargetIconRequest) ProtoMessage() {} +func (*ChangeLeaderParams) ProtoMessage() {} -func (x *SetGroupTargetIconRequest) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[19] +func (x *ChangeLeaderParams) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1457,47 +1616,40 @@ func (x *SetGroupTargetIconRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetGroupTargetIconRequest.ProtoReflect.Descriptor instead. -func (*SetGroupTargetIconRequest) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{19} +// Deprecated: Use ChangeLeaderParams.ProtoReflect.Descriptor instead. +func (*ChangeLeaderParams) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{20} } -func (x *SetGroupTargetIconRequest) GetApi() string { +func (x *ChangeLeaderParams) GetApi() string { if x != nil { return x.Api } return "" } -func (x *SetGroupTargetIconRequest) GetRealmID() uint32 { +func (x *ChangeLeaderParams) GetRealmID() uint32 { if x != nil { return x.RealmID } return 0 } -func (x *SetGroupTargetIconRequest) GetSetterGUID() uint64 { - if x != nil { - return x.SetterGUID - } - return 0 -} - -func (x *SetGroupTargetIconRequest) GetIconID() uint32 { +func (x *ChangeLeaderParams) GetPlayer() uint64 { if x != nil { - return x.IconID + return x.Player } return 0 } -func (x *SetGroupTargetIconRequest) GetTargetGUID() uint64 { +func (x *ChangeLeaderParams) GetNewLeader() uint64 { if x != nil { - return x.TargetGUID + return x.NewLeader } return 0 } -type SetGroupTargetIconResponse struct { +type ChangeLeaderResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -1505,23 +1657,23 @@ type SetGroupTargetIconResponse struct { Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` } -func (x *SetGroupTargetIconResponse) Reset() { - *x = SetGroupTargetIconResponse{} +func (x *ChangeLeaderResponse) Reset() { + *x = ChangeLeaderResponse{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[20] + mi := &file_group_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SetGroupTargetIconResponse) String() string { +func (x *ChangeLeaderResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetGroupTargetIconResponse) ProtoMessage() {} +func (*ChangeLeaderResponse) ProtoMessage() {} -func (x *SetGroupTargetIconResponse) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[20] +func (x *ChangeLeaderResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1532,45 +1684,49 @@ func (x *SetGroupTargetIconResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetGroupTargetIconResponse.ProtoReflect.Descriptor instead. -func (*SetGroupTargetIconResponse) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{20} +// Deprecated: Use ChangeLeaderResponse.ProtoReflect.Descriptor instead. +func (*ChangeLeaderResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{21} } -func (x *SetGroupTargetIconResponse) GetApi() string { +func (x *ChangeLeaderResponse) GetApi() string { if x != nil { return x.Api } return "" } -type GetGroupTargetIconsRequest struct { +type SendGroupMessageParams struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + SenderGUID uint64 `protobuf:"varint,3,opt,name=senderGUID,proto3" json:"senderGUID,omitempty"` + MessageType uint32 `protobuf:"varint,4,opt,name=messageType,proto3" json:"messageType,omitempty"` + Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` + Language uint32 `protobuf:"varint,6,opt,name=language,proto3" json:"language,omitempty"` + SenderChatTag uint32 `protobuf:"varint,7,opt,name=senderChatTag,proto3" json:"senderChatTag,omitempty"` } -func (x *GetGroupTargetIconsRequest) Reset() { - *x = GetGroupTargetIconsRequest{} +func (x *SendGroupMessageParams) Reset() { + *x = SendGroupMessageParams{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[21] + mi := &file_group_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetGroupTargetIconsRequest) String() string { +func (x *SendGroupMessageParams) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetGroupTargetIconsRequest) ProtoMessage() {} +func (*SendGroupMessageParams) ProtoMessage() {} -func (x *GetGroupTargetIconsRequest) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[21] +func (x *SendGroupMessageParams) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1581,58 +1737,85 @@ func (x *GetGroupTargetIconsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetGroupTargetIconsRequest.ProtoReflect.Descriptor instead. -func (*GetGroupTargetIconsRequest) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{21} +// Deprecated: Use SendGroupMessageParams.ProtoReflect.Descriptor instead. +func (*SendGroupMessageParams) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{22} } -func (x *GetGroupTargetIconsRequest) GetApi() string { +func (x *SendGroupMessageParams) GetApi() string { if x != nil { return x.Api } return "" } -func (x *GetGroupTargetIconsRequest) GetRealmID() uint32 { +func (x *SendGroupMessageParams) GetRealmID() uint32 { if x != nil { return x.RealmID } return 0 } -func (x *GetGroupTargetIconsRequest) GetPlayerGUID() uint64 { +func (x *SendGroupMessageParams) GetSenderGUID() uint64 { if x != nil { - return x.PlayerGUID + return x.SenderGUID } return 0 } -type GetGroupTargetIconsResponse struct { +func (x *SendGroupMessageParams) GetMessageType() uint32 { + if x != nil { + return x.MessageType + } + return 0 +} + +func (x *SendGroupMessageParams) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *SendGroupMessageParams) GetLanguage() uint32 { + if x != nil { + return x.Language + } + return 0 +} + +func (x *SendGroupMessageParams) GetSenderChatTag() uint32 { + if x != nil { + return x.SenderChatTag + } + return 0 +} + +type SendGroupMessageResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - Targets []uint64 `protobuf:"varint,2,rep,packed,name=targets,proto3" json:"targets,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` } -func (x *GetGroupTargetIconsResponse) Reset() { - *x = GetGroupTargetIconsResponse{} +func (x *SendGroupMessageResponse) Reset() { + *x = SendGroupMessageResponse{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[22] + mi := &file_group_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetGroupTargetIconsResponse) String() string { +func (x *SendGroupMessageResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetGroupTargetIconsResponse) ProtoMessage() {} +func (*SendGroupMessageResponse) ProtoMessage() {} -func (x *GetGroupTargetIconsResponse) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[22] +func (x *SendGroupMessageResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1643,55 +1826,47 @@ func (x *GetGroupTargetIconsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetGroupTargetIconsResponse.ProtoReflect.Descriptor instead. -func (*GetGroupTargetIconsResponse) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{22} +// Deprecated: Use SendGroupMessageResponse.ProtoReflect.Descriptor instead. +func (*SendGroupMessageResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{23} } -func (x *GetGroupTargetIconsResponse) GetApi() string { +func (x *SendGroupMessageResponse) GetApi() string { if x != nil { return x.Api } return "" } -func (x *GetGroupTargetIconsResponse) GetTargets() []uint64 { - if x != nil { - return x.Targets - } - return nil -} - -type SetLootMethodRequest struct { +type SetGroupTargetIconRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` - Method uint32 `protobuf:"varint,4,opt,name=method,proto3" json:"method,omitempty"` - LootMaster uint64 `protobuf:"varint,5,opt,name=lootMaster,proto3" json:"lootMaster,omitempty"` - LootThreshold uint32 `protobuf:"varint,6,opt,name=lootThreshold,proto3" json:"lootThreshold,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + SetterGUID uint64 `protobuf:"varint,3,opt,name=setterGUID,proto3" json:"setterGUID,omitempty"` + IconID uint32 `protobuf:"varint,4,opt,name=iconID,proto3" json:"iconID,omitempty"` + TargetGUID uint64 `protobuf:"varint,5,opt,name=targetGUID,proto3" json:"targetGUID,omitempty"` } -func (x *SetLootMethodRequest) Reset() { - *x = SetLootMethodRequest{} +func (x *SetGroupTargetIconRequest) Reset() { + *x = SetGroupTargetIconRequest{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[23] + mi := &file_group_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SetLootMethodRequest) String() string { +func (x *SetGroupTargetIconRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetLootMethodRequest) ProtoMessage() {} +func (*SetGroupTargetIconRequest) ProtoMessage() {} -func (x *SetLootMethodRequest) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[23] +func (x *SetGroupTargetIconRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1702,54 +1877,47 @@ func (x *SetLootMethodRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetLootMethodRequest.ProtoReflect.Descriptor instead. -func (*SetLootMethodRequest) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{23} +// Deprecated: Use SetGroupTargetIconRequest.ProtoReflect.Descriptor instead. +func (*SetGroupTargetIconRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{24} } -func (x *SetLootMethodRequest) GetApi() string { +func (x *SetGroupTargetIconRequest) GetApi() string { if x != nil { return x.Api } return "" } -func (x *SetLootMethodRequest) GetRealmID() uint32 { +func (x *SetGroupTargetIconRequest) GetRealmID() uint32 { if x != nil { return x.RealmID } return 0 } -func (x *SetLootMethodRequest) GetPlayerGUID() uint64 { +func (x *SetGroupTargetIconRequest) GetSetterGUID() uint64 { if x != nil { - return x.PlayerGUID + return x.SetterGUID } return 0 } -func (x *SetLootMethodRequest) GetMethod() uint32 { +func (x *SetGroupTargetIconRequest) GetIconID() uint32 { if x != nil { - return x.Method + return x.IconID } return 0 } -func (x *SetLootMethodRequest) GetLootMaster() uint64 { - if x != nil { - return x.LootMaster - } - return 0 -} - -func (x *SetLootMethodRequest) GetLootThreshold() uint32 { +func (x *SetGroupTargetIconRequest) GetTargetGUID() uint64 { if x != nil { - return x.LootThreshold + return x.TargetGUID } return 0 } -type SetLootMethodResponse struct { +type SetGroupTargetIconResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -1757,23 +1925,23 @@ type SetLootMethodResponse struct { Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` } -func (x *SetLootMethodResponse) Reset() { - *x = SetLootMethodResponse{} +func (x *SetGroupTargetIconResponse) Reset() { + *x = SetGroupTargetIconResponse{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[24] + mi := &file_group_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SetLootMethodResponse) String() string { +func (x *SetGroupTargetIconResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetLootMethodResponse) ProtoMessage() {} +func (*SetGroupTargetIconResponse) ProtoMessage() {} -func (x *SetLootMethodResponse) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[24] +func (x *SetGroupTargetIconResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1784,19 +1952,19 @@ func (x *SetLootMethodResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetLootMethodResponse.ProtoReflect.Descriptor instead. -func (*SetLootMethodResponse) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{24} +// Deprecated: Use SetGroupTargetIconResponse.ProtoReflect.Descriptor instead. +func (*SetGroupTargetIconResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{25} } -func (x *SetLootMethodResponse) GetApi() string { +func (x *SetGroupTargetIconResponse) GetApi() string { if x != nil { return x.Api } return "" } -type SetDungeonDifficultyRequest struct { +type GetGroupTargetIconsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -1804,26 +1972,25 @@ type SetDungeonDifficultyRequest struct { Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` - Difficulty uint32 `protobuf:"varint,4,opt,name=difficulty,proto3" json:"difficulty,omitempty"` } -func (x *SetDungeonDifficultyRequest) Reset() { - *x = SetDungeonDifficultyRequest{} +func (x *GetGroupTargetIconsRequest) Reset() { + *x = GetGroupTargetIconsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[25] + mi := &file_group_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SetDungeonDifficultyRequest) String() string { +func (x *GetGroupTargetIconsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetDungeonDifficultyRequest) ProtoMessage() {} +func (*GetGroupTargetIconsRequest) ProtoMessage() {} -func (x *SetDungeonDifficultyRequest) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[25] +func (x *GetGroupTargetIconsRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1834,65 +2001,58 @@ func (x *SetDungeonDifficultyRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetDungeonDifficultyRequest.ProtoReflect.Descriptor instead. -func (*SetDungeonDifficultyRequest) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{25} +// Deprecated: Use GetGroupTargetIconsRequest.ProtoReflect.Descriptor instead. +func (*GetGroupTargetIconsRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{26} } -func (x *SetDungeonDifficultyRequest) GetApi() string { +func (x *GetGroupTargetIconsRequest) GetApi() string { if x != nil { return x.Api } return "" } -func (x *SetDungeonDifficultyRequest) GetRealmID() uint32 { +func (x *GetGroupTargetIconsRequest) GetRealmID() uint32 { if x != nil { return x.RealmID } return 0 } -func (x *SetDungeonDifficultyRequest) GetPlayerGUID() uint64 { +func (x *GetGroupTargetIconsRequest) GetPlayerGUID() uint64 { if x != nil { return x.PlayerGUID } return 0 } -func (x *SetDungeonDifficultyRequest) GetDifficulty() uint32 { - if x != nil { - return x.Difficulty - } - return 0 -} - -type SetDungeonDifficultyResponse struct { +type GetGroupTargetIconsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - Status SetDungeonDifficultyResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.SetDungeonDifficultyResponse_Status" json:"status,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Targets []uint64 `protobuf:"varint,2,rep,packed,name=targets,proto3" json:"targets,omitempty"` } -func (x *SetDungeonDifficultyResponse) Reset() { - *x = SetDungeonDifficultyResponse{} +func (x *GetGroupTargetIconsResponse) Reset() { + *x = GetGroupTargetIconsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[26] + mi := &file_group_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SetDungeonDifficultyResponse) String() string { +func (x *GetGroupTargetIconsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetDungeonDifficultyResponse) ProtoMessage() {} +func (*GetGroupTargetIconsResponse) ProtoMessage() {} -func (x *SetDungeonDifficultyResponse) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[26] +func (x *GetGroupTargetIconsResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1903,53 +2063,55 @@ func (x *SetDungeonDifficultyResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetDungeonDifficultyResponse.ProtoReflect.Descriptor instead. -func (*SetDungeonDifficultyResponse) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{26} +// Deprecated: Use GetGroupTargetIconsResponse.ProtoReflect.Descriptor instead. +func (*GetGroupTargetIconsResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{27} } -func (x *SetDungeonDifficultyResponse) GetApi() string { +func (x *GetGroupTargetIconsResponse) GetApi() string { if x != nil { return x.Api } return "" } -func (x *SetDungeonDifficultyResponse) GetStatus() SetDungeonDifficultyResponse_Status { +func (x *GetGroupTargetIconsResponse) GetTargets() []uint64 { if x != nil { - return x.Status + return x.Targets } - return SetDungeonDifficultyResponse_Ok + return nil } -type SetRaidDifficultyRequest struct { +type SetLootMethodRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` - Difficulty uint32 `protobuf:"varint,4,opt,name=difficulty,proto3" json:"difficulty,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + Method uint32 `protobuf:"varint,4,opt,name=method,proto3" json:"method,omitempty"` + LootMaster uint64 `protobuf:"varint,5,opt,name=lootMaster,proto3" json:"lootMaster,omitempty"` + LootThreshold uint32 `protobuf:"varint,6,opt,name=lootThreshold,proto3" json:"lootThreshold,omitempty"` } -func (x *SetRaidDifficultyRequest) Reset() { - *x = SetRaidDifficultyRequest{} +func (x *SetLootMethodRequest) Reset() { + *x = SetLootMethodRequest{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[27] + mi := &file_group_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SetRaidDifficultyRequest) String() string { +func (x *SetLootMethodRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetRaidDifficultyRequest) ProtoMessage() {} +func (*SetLootMethodRequest) ProtoMessage() {} -func (x *SetRaidDifficultyRequest) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[27] +func (x *SetLootMethodRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1960,65 +2122,78 @@ func (x *SetRaidDifficultyRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetRaidDifficultyRequest.ProtoReflect.Descriptor instead. -func (*SetRaidDifficultyRequest) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{27} +// Deprecated: Use SetLootMethodRequest.ProtoReflect.Descriptor instead. +func (*SetLootMethodRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{28} } -func (x *SetRaidDifficultyRequest) GetApi() string { +func (x *SetLootMethodRequest) GetApi() string { if x != nil { return x.Api } return "" } -func (x *SetRaidDifficultyRequest) GetRealmID() uint32 { +func (x *SetLootMethodRequest) GetRealmID() uint32 { if x != nil { return x.RealmID } return 0 } -func (x *SetRaidDifficultyRequest) GetPlayerGUID() uint64 { +func (x *SetLootMethodRequest) GetPlayerGUID() uint64 { if x != nil { return x.PlayerGUID } return 0 } -func (x *SetRaidDifficultyRequest) GetDifficulty() uint32 { +func (x *SetLootMethodRequest) GetMethod() uint32 { if x != nil { - return x.Difficulty + return x.Method } return 0 } -type SetRaidDifficultyResponse struct { +func (x *SetLootMethodRequest) GetLootMaster() uint64 { + if x != nil { + return x.LootMaster + } + return 0 +} + +func (x *SetLootMethodRequest) GetLootThreshold() uint32 { + if x != nil { + return x.LootThreshold + } + return 0 +} + +type SetLootMethodResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - Status SetRaidDifficultyResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.SetRaidDifficultyResponse_Status" json:"status,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` } -func (x *SetRaidDifficultyResponse) Reset() { - *x = SetRaidDifficultyResponse{} +func (x *SetLootMethodResponse) Reset() { + *x = SetLootMethodResponse{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[28] + mi := &file_group_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SetRaidDifficultyResponse) String() string { +func (x *SetLootMethodResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetRaidDifficultyResponse) ProtoMessage() {} +func (*SetLootMethodResponse) ProtoMessage() {} -func (x *SetRaidDifficultyResponse) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[28] +func (x *SetLootMethodResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2029,55 +2204,46 @@ func (x *SetRaidDifficultyResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetRaidDifficultyResponse.ProtoReflect.Descriptor instead. -func (*SetRaidDifficultyResponse) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{28} +// Deprecated: Use SetLootMethodResponse.ProtoReflect.Descriptor instead. +func (*SetLootMethodResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{29} } -func (x *SetRaidDifficultyResponse) GetApi() string { +func (x *SetLootMethodResponse) GetApi() string { if x != nil { return x.Api } return "" } -func (x *SetRaidDifficultyResponse) GetStatus() SetRaidDifficultyResponse_Status { - if x != nil { - return x.Status - } - return SetRaidDifficultyResponse_Ok -} - -type GetGroupResponse_GroupMember struct { +type SetDungeonDifficultyRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` - Flags uint32 `protobuf:"varint,2,opt,name=flags,proto3" json:"flags,omitempty"` - Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` - IsOnline bool `protobuf:"varint,4,opt,name=isOnline,proto3" json:"isOnline,omitempty"` - SubGroup uint32 `protobuf:"varint,5,opt,name=subGroup,proto3" json:"subGroup,omitempty"` - Roles uint32 `protobuf:"varint,6,opt,name=roles,proto3" json:"roles,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + Difficulty uint32 `protobuf:"varint,4,opt,name=difficulty,proto3" json:"difficulty,omitempty"` } -func (x *GetGroupResponse_GroupMember) Reset() { - *x = GetGroupResponse_GroupMember{} +func (x *SetDungeonDifficultyRequest) Reset() { + *x = SetDungeonDifficultyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[29] + mi := &file_group_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetGroupResponse_GroupMember) String() string { +func (x *SetDungeonDifficultyRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetGroupResponse_GroupMember) ProtoMessage() {} +func (*SetDungeonDifficultyRequest) ProtoMessage() {} -func (x *GetGroupResponse_GroupMember) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[29] +func (x *SetDungeonDifficultyRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2088,581 +2254,3582 @@ func (x *GetGroupResponse_GroupMember) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetGroupResponse_GroupMember.ProtoReflect.Descriptor instead. -func (*GetGroupResponse_GroupMember) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{5, 0} +// Deprecated: Use SetDungeonDifficultyRequest.ProtoReflect.Descriptor instead. +func (*SetDungeonDifficultyRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{30} } -func (x *GetGroupResponse_GroupMember) GetGuid() uint64 { +func (x *SetDungeonDifficultyRequest) GetApi() string { if x != nil { - return x.Guid + return x.Api } - return 0 + return "" } -func (x *GetGroupResponse_GroupMember) GetFlags() uint32 { +func (x *SetDungeonDifficultyRequest) GetRealmID() uint32 { if x != nil { - return x.Flags + return x.RealmID } return 0 } -func (x *GetGroupResponse_GroupMember) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *GetGroupResponse_GroupMember) GetIsOnline() bool { +func (x *SetDungeonDifficultyRequest) GetPlayerGUID() uint64 { if x != nil { - return x.IsOnline + return x.PlayerGUID } - return false + return 0 } -func (x *GetGroupResponse_GroupMember) GetSubGroup() uint32 { +func (x *SetDungeonDifficultyRequest) GetDifficulty() uint32 { if x != nil { - return x.SubGroup + return x.Difficulty } return 0 } -func (x *GetGroupResponse_GroupMember) GetRoles() uint32 { - if x != nil { - return x.Roles - } +type SetDungeonDifficultyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status SetDungeonDifficultyResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.SetDungeonDifficultyResponse_Status" json:"status,omitempty"` +} + +func (x *SetDungeonDifficultyResponse) Reset() { + *x = SetDungeonDifficultyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetDungeonDifficultyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetDungeonDifficultyResponse) ProtoMessage() {} + +func (x *SetDungeonDifficultyResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetDungeonDifficultyResponse.ProtoReflect.Descriptor instead. +func (*SetDungeonDifficultyResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{31} +} + +func (x *SetDungeonDifficultyResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SetDungeonDifficultyResponse) GetStatus() SetDungeonDifficultyResponse_Status { + if x != nil { + return x.Status + } + return SetDungeonDifficultyResponse_Ok +} + +type SetRaidDifficultyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + Difficulty uint32 `protobuf:"varint,4,opt,name=difficulty,proto3" json:"difficulty,omitempty"` +} + +func (x *SetRaidDifficultyRequest) Reset() { + *x = SetRaidDifficultyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetRaidDifficultyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetRaidDifficultyRequest) ProtoMessage() {} + +func (x *SetRaidDifficultyRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetRaidDifficultyRequest.ProtoReflect.Descriptor instead. +func (*SetRaidDifficultyRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{32} +} + +func (x *SetRaidDifficultyRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SetRaidDifficultyRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } return 0 } -type GetGroupResponse_Group struct { +func (x *SetRaidDifficultyRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *SetRaidDifficultyRequest) GetDifficulty() uint32 { + if x != nil { + return x.Difficulty + } + return 0 +} + +type SetRaidDifficultyResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Leader uint64 `protobuf:"varint,2,opt,name=leader,proto3" json:"leader,omitempty"` - LootMethod uint32 `protobuf:"varint,3,opt,name=lootMethod,proto3" json:"lootMethod,omitempty"` - Looter uint64 `protobuf:"varint,4,opt,name=looter,proto3" json:"looter,omitempty"` - LootThreshold uint32 `protobuf:"varint,5,opt,name=lootThreshold,proto3" json:"lootThreshold,omitempty"` - GroupType uint32 `protobuf:"varint,6,opt,name=groupType,proto3" json:"groupType,omitempty"` - Difficulty uint32 `protobuf:"varint,7,opt,name=difficulty,proto3" json:"difficulty,omitempty"` - RaidDifficulty uint32 `protobuf:"varint,8,opt,name=raidDifficulty,proto3" json:"raidDifficulty,omitempty"` - MasterLooter uint64 `protobuf:"varint,9,opt,name=masterLooter,proto3" json:"masterLooter,omitempty"` - TargetIconsList []uint64 `protobuf:"varint,10,rep,packed,name=targetIconsList,proto3" json:"targetIconsList,omitempty"` - Members []*GetGroupResponse_GroupMember `protobuf:"bytes,11,rep,name=members,proto3" json:"members,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status SetRaidDifficultyResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.SetRaidDifficultyResponse_Status" json:"status,omitempty"` } -func (x *GetGroupResponse_Group) Reset() { - *x = GetGroupResponse_Group{} +func (x *SetRaidDifficultyResponse) Reset() { + *x = SetRaidDifficultyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_group_proto_msgTypes[30] + mi := &file_group_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetGroupResponse_Group) String() string { +func (x *SetRaidDifficultyResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetGroupResponse_Group) ProtoMessage() {} +func (*SetRaidDifficultyResponse) ProtoMessage() {} -func (x *GetGroupResponse_Group) ProtoReflect() protoreflect.Message { - mi := &file_group_proto_msgTypes[30] +func (x *SetRaidDifficultyResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetRaidDifficultyResponse.ProtoReflect.Descriptor instead. +func (*SetRaidDifficultyResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{33} +} + +func (x *SetRaidDifficultyResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SetRaidDifficultyResponse) GetStatus() SetRaidDifficultyResponse_Status { + if x != nil { + return x.Status + } + return SetRaidDifficultyResponse_Ok +} + +type StartReadyCheckRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + LeaderGUID uint64 `protobuf:"varint,3,opt,name=leaderGUID,proto3" json:"leaderGUID,omitempty"` + DurationMs uint32 `protobuf:"varint,4,opt,name=durationMs,proto3" json:"durationMs,omitempty"` +} + +func (x *StartReadyCheckRequest) Reset() { + *x = StartReadyCheckRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartReadyCheckRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartReadyCheckRequest) ProtoMessage() {} + +func (x *StartReadyCheckRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartReadyCheckRequest.ProtoReflect.Descriptor instead. +func (*StartReadyCheckRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{34} +} + +func (x *StartReadyCheckRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *StartReadyCheckRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *StartReadyCheckRequest) GetLeaderGUID() uint64 { + if x != nil { + return x.LeaderGUID + } + return 0 +} + +func (x *StartReadyCheckRequest) GetDurationMs() uint32 { + if x != nil { + return x.DurationMs + } + return 0 +} + +type StartReadyCheckResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *StartReadyCheckResponse) Reset() { + *x = StartReadyCheckResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartReadyCheckResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartReadyCheckResponse) ProtoMessage() {} + +func (x *StartReadyCheckResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetGroupResponse_Group.ProtoReflect.Descriptor instead. -func (*GetGroupResponse_Group) Descriptor() ([]byte, []int) { - return file_group_proto_rawDescGZIP(), []int{5, 1} -} - -func (x *GetGroupResponse_Group) GetId() uint32 { - if x != nil { - return x.Id - } - return 0 -} - -func (x *GetGroupResponse_Group) GetLeader() uint64 { - if x != nil { - return x.Leader - } - return 0 -} - -func (x *GetGroupResponse_Group) GetLootMethod() uint32 { - if x != nil { - return x.LootMethod - } - return 0 -} - -func (x *GetGroupResponse_Group) GetLooter() uint64 { - if x != nil { - return x.Looter - } - return 0 -} - -func (x *GetGroupResponse_Group) GetLootThreshold() uint32 { - if x != nil { - return x.LootThreshold - } - return 0 -} - -func (x *GetGroupResponse_Group) GetGroupType() uint32 { - if x != nil { - return x.GroupType - } - return 0 -} - -func (x *GetGroupResponse_Group) GetDifficulty() uint32 { - if x != nil { - return x.Difficulty - } - return 0 -} - -func (x *GetGroupResponse_Group) GetRaidDifficulty() uint32 { - if x != nil { - return x.RaidDifficulty - } - return 0 -} - -func (x *GetGroupResponse_Group) GetMasterLooter() uint64 { - if x != nil { - return x.MasterLooter - } - return 0 -} - -func (x *GetGroupResponse_Group) GetTargetIconsList() []uint64 { - if x != nil { - return x.TargetIconsList - } - return nil -} - -func (x *GetGroupResponse_Group) GetMembers() []*GetGroupResponse_GroupMember { - if x != nil { - return x.Members - } - return nil -} - -var File_group_proto protoreflect.FileDescriptor - -var file_group_proto_rawDesc = []byte{ - 0x0a, 0x0b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, - 0x31, 0x22, 0xb2, 0x01, 0x0a, 0x0c, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, - 0x0a, 0x07, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x07, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x76, 0x69, - 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x69, 0x6e, 0x76, 0x69, 0x74, - 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x64, 0x4e, - 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, - 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x72, 0x0a, 0x0e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x31, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x76, 0x31, 0x2e, - 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x1b, 0x0a, - 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, - 0x09, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x01, 0x22, 0x58, 0x0a, 0x12, 0x41, 0x63, - 0x63, 0x65, 0x70, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x70, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x22, 0x92, 0x01, 0x0a, 0x14, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x49, - 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, - 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, - 0x37, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x2f, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x6e, - 0x76, 0x69, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x01, 0x12, 0x09, - 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x02, 0x22, 0x57, 0x0a, 0x0f, 0x47, 0x65, 0x74, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, - 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, - 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x49, 0x44, 0x22, 0xf2, 0x04, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x30, 0x0a, 0x05, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x99, 0x01, 0x0a, 0x0b, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x67, - 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, 0x75, 0x69, 0x64, 0x12, - 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, - 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x73, 0x4f, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x4f, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x75, 0x62, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x1a, 0xfd, 0x02, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x6f, 0x6f, - 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, - 0x6f, 0x6f, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x6f, - 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6c, 0x6f, 0x6f, 0x74, 0x65, - 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x6c, 0x6f, 0x6f, 0x74, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, - 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6c, 0x6f, 0x6f, 0x74, 0x54, 0x68, - 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x54, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, - 0x6c, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, - 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x61, 0x69, 0x64, 0x44, 0x69, 0x66, - 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x72, - 0x61, 0x69, 0x64, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x22, 0x0a, - 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x74, 0x65, 0x72, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x74, 0x65, - 0x72, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x73, - 0x4c, 0x69, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0f, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x07, 0x6d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x07, - 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0x5d, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x42, 0x79, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, - 0x0a, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x22, 0x5f, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x49, 0x44, 0x42, 0x79, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, - 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x22, 0x48, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x49, 0x44, 0x42, 0x79, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, - 0x44, 0x22, 0x8a, 0x01, 0x0a, 0x0e, 0x55, 0x6e, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, - 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, - 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x16, - 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x76, - 0x0a, 0x10, 0x55, 0x6e, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x61, 0x70, 0x69, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x76, 0x69, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x1b, 0x0a, 0x06, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x10, 0x01, 0x22, 0x56, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, - 0x65, 0x61, 0x76, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, - 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, - 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, - 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x22, 0x26, - 0x0a, 0x12, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x59, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x74, 0x54, 0x6f, 0x52, 0x61, 0x69, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, - 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, - 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x22, 0x29, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x6f, 0x52, 0x61, - 0x69, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, - 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x76, 0x0a, 0x12, - 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, - 0x0a, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x4c, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6e, 0x65, 0x77, 0x4c, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x22, 0x28, 0x0a, 0x14, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, - 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0xbc, - 0x01, 0x0a, 0x16, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, - 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, - 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x47, - 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, - 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x54, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x22, 0x2c, 0x0a, - 0x18, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x9f, 0x01, 0x0a, 0x19, - 0x53, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x63, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, - 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, - 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, - 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, - 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x63, 0x6f, 0x6e, 0x49, 0x44, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x69, 0x63, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1e, 0x0a, - 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x55, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x55, 0x49, 0x44, 0x22, 0x2e, 0x0a, - 0x1a, 0x53, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, - 0x63, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x68, 0x0a, - 0x1a, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, - 0x63, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, - 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0x49, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x74, 0x4d, 0x65, - 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, - 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, - 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, - 0x1e, 0x0a, 0x0a, 0x6c, 0x6f, 0x6f, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6c, 0x6f, 0x6f, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x12, - 0x24, 0x0a, 0x0d, 0x6c, 0x6f, 0x6f, 0x74, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6c, 0x6f, 0x6f, 0x74, 0x54, 0x68, 0x72, 0x65, - 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0x29, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x74, - 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, - 0x22, 0x89, 0x01, 0x0a, 0x1b, 0x53, 0x65, 0x74, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x44, - 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, - 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x22, 0x9a, 0x01, 0x0a, - 0x1c, 0x53, 0x65, 0x74, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x44, 0x69, 0x66, 0x66, 0x69, - 0x63, 0x75, 0x6c, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, - 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, - 0x3f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x27, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x44, - 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x22, 0x27, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, - 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x73, 0x49, 0x6e, - 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x10, 0x01, 0x22, 0x86, 0x01, 0x0a, 0x18, 0x53, 0x65, - 0x74, 0x52, 0x61, 0x69, 0x64, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, - 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, - 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, - 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, - 0x74, 0x79, 0x22, 0x91, 0x01, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x52, 0x61, 0x69, 0x64, 0x44, 0x69, - 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x61, 0x69, 0x64, 0x44, - 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x22, 0x24, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, - 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x73, 0x49, 0x6e, - 0x52, 0x61, 0x69, 0x64, 0x10, 0x01, 0x32, 0xd8, 0x07, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x06, 0x49, 0x6e, 0x76, 0x69, 0x74, - 0x65, 0x12, 0x10, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x1a, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x08, 0x55, 0x6e, 0x69, 0x6e, 0x76, - 0x69, 0x74, 0x65, 0x12, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x76, 0x69, 0x74, - 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x69, - 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, - 0x05, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x16, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, - 0x6f, 0x52, 0x61, 0x69, 0x64, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x74, 0x54, 0x6f, 0x52, 0x61, 0x69, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x19, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x6f, 0x52, 0x61, 0x69, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x43, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x41, - 0x63, 0x63, 0x65, 0x70, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x76, 0x31, - 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x49, - 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, - 0x08, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x13, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x42, 0x79, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x79, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x12, 0x47, - 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x42, 0x79, 0x50, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, - 0x44, 0x42, 0x79, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, - 0x42, 0x79, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x53, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x74, - 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4c, - 0x6f, 0x6f, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x74, 0x4d, 0x65, 0x74, - 0x68, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x14, 0x53, - 0x65, 0x74, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, - 0x6c, 0x74, 0x79, 0x12, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x75, 0x6e, 0x67, - 0x65, 0x6f, 0x6e, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x75, 0x6e, - 0x67, 0x65, 0x6f, 0x6e, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x52, 0x61, 0x69, - 0x64, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x1c, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x74, 0x52, 0x61, 0x69, 0x64, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, - 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x65, 0x74, 0x52, 0x61, 0x69, 0x64, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, - 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x1a, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x42, 0x0e, 0x5a, 0x0c, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2f, 0x70, - 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_group_proto_rawDescOnce sync.Once - file_group_proto_rawDescData = file_group_proto_rawDesc -) - -func file_group_proto_rawDescGZIP() []byte { - file_group_proto_rawDescOnce.Do(func() { - file_group_proto_rawDescData = protoimpl.X.CompressGZIP(file_group_proto_rawDescData) - }) - return file_group_proto_rawDescData -} - -var file_group_proto_enumTypes = make([]protoimpl.EnumInfo, 5) -var file_group_proto_msgTypes = make([]protoimpl.MessageInfo, 31) -var file_group_proto_goTypes = []interface{}{ - (InviteResponse_Status)(0), // 0: v1.InviteResponse.Status - (AcceptInviteResponse_Status)(0), // 1: v1.AcceptInviteResponse.Status - (UninviteResponse_Status)(0), // 2: v1.UninviteResponse.Status - (SetDungeonDifficultyResponse_Status)(0), // 3: v1.SetDungeonDifficultyResponse.Status - (SetRaidDifficultyResponse_Status)(0), // 4: v1.SetRaidDifficultyResponse.Status - (*InviteParams)(nil), // 5: v1.InviteParams - (*InviteResponse)(nil), // 6: v1.InviteResponse - (*AcceptInviteParams)(nil), // 7: v1.AcceptInviteParams - (*AcceptInviteResponse)(nil), // 8: v1.AcceptInviteResponse - (*GetGroupRequest)(nil), // 9: v1.GetGroupRequest - (*GetGroupResponse)(nil), // 10: v1.GetGroupResponse - (*GetGroupByMemberRequest)(nil), // 11: v1.GetGroupByMemberRequest - (*GetGroupIDByPlayerRequest)(nil), // 12: v1.GetGroupIDByPlayerRequest - (*GetGroupIDByPlayerResponse)(nil), // 13: v1.GetGroupIDByPlayerResponse - (*UninviteParams)(nil), // 14: v1.UninviteParams - (*UninviteResponse)(nil), // 15: v1.UninviteResponse - (*GroupLeaveParams)(nil), // 16: v1.GroupLeaveParams - (*GroupLeaveResponse)(nil), // 17: v1.GroupLeaveResponse - (*ConvertToRaidParams)(nil), // 18: v1.ConvertToRaidParams - (*ConvertToRaidResponse)(nil), // 19: v1.ConvertToRaidResponse - (*ChangeLeaderParams)(nil), // 20: v1.ChangeLeaderParams - (*ChangeLeaderResponse)(nil), // 21: v1.ChangeLeaderResponse - (*SendGroupMessageParams)(nil), // 22: v1.SendGroupMessageParams - (*SendGroupMessageResponse)(nil), // 23: v1.SendGroupMessageResponse - (*SetGroupTargetIconRequest)(nil), // 24: v1.SetGroupTargetIconRequest - (*SetGroupTargetIconResponse)(nil), // 25: v1.SetGroupTargetIconResponse - (*GetGroupTargetIconsRequest)(nil), // 26: v1.GetGroupTargetIconsRequest - (*GetGroupTargetIconsResponse)(nil), // 27: v1.GetGroupTargetIconsResponse - (*SetLootMethodRequest)(nil), // 28: v1.SetLootMethodRequest - (*SetLootMethodResponse)(nil), // 29: v1.SetLootMethodResponse - (*SetDungeonDifficultyRequest)(nil), // 30: v1.SetDungeonDifficultyRequest - (*SetDungeonDifficultyResponse)(nil), // 31: v1.SetDungeonDifficultyResponse - (*SetRaidDifficultyRequest)(nil), // 32: v1.SetRaidDifficultyRequest - (*SetRaidDifficultyResponse)(nil), // 33: v1.SetRaidDifficultyResponse - (*GetGroupResponse_GroupMember)(nil), // 34: v1.GetGroupResponse.GroupMember - (*GetGroupResponse_Group)(nil), // 35: v1.GetGroupResponse.Group -} -var file_group_proto_depIdxs = []int32{ - 0, // 0: v1.InviteResponse.status:type_name -> v1.InviteResponse.Status - 1, // 1: v1.AcceptInviteResponse.status:type_name -> v1.AcceptInviteResponse.Status - 35, // 2: v1.GetGroupResponse.group:type_name -> v1.GetGroupResponse.Group - 2, // 3: v1.UninviteResponse.status:type_name -> v1.UninviteResponse.Status - 3, // 4: v1.SetDungeonDifficultyResponse.status:type_name -> v1.SetDungeonDifficultyResponse.Status - 4, // 5: v1.SetRaidDifficultyResponse.status:type_name -> v1.SetRaidDifficultyResponse.Status - 34, // 6: v1.GetGroupResponse.Group.members:type_name -> v1.GetGroupResponse.GroupMember - 5, // 7: v1.GroupService.Invite:input_type -> v1.InviteParams - 14, // 8: v1.GroupService.Uninvite:input_type -> v1.UninviteParams - 16, // 9: v1.GroupService.Leave:input_type -> v1.GroupLeaveParams - 18, // 10: v1.GroupService.ConvertToRaid:input_type -> v1.ConvertToRaidParams - 20, // 11: v1.GroupService.ChangeLeader:input_type -> v1.ChangeLeaderParams - 7, // 12: v1.GroupService.AcceptInvite:input_type -> v1.AcceptInviteParams - 9, // 13: v1.GroupService.GetGroup:input_type -> v1.GetGroupRequest - 11, // 14: v1.GroupService.GetGroupByMember:input_type -> v1.GetGroupByMemberRequest - 12, // 15: v1.GroupService.GetGroupIDByPlayer:input_type -> v1.GetGroupIDByPlayerRequest - 24, // 16: v1.GroupService.SetGroupTargetIcon:input_type -> v1.SetGroupTargetIconRequest - 28, // 17: v1.GroupService.SetLootMethod:input_type -> v1.SetLootMethodRequest - 30, // 18: v1.GroupService.SetDungeonDifficulty:input_type -> v1.SetDungeonDifficultyRequest - 32, // 19: v1.GroupService.SetRaidDifficulty:input_type -> v1.SetRaidDifficultyRequest - 22, // 20: v1.GroupService.SendMessage:input_type -> v1.SendGroupMessageParams - 6, // 21: v1.GroupService.Invite:output_type -> v1.InviteResponse - 15, // 22: v1.GroupService.Uninvite:output_type -> v1.UninviteResponse - 17, // 23: v1.GroupService.Leave:output_type -> v1.GroupLeaveResponse - 19, // 24: v1.GroupService.ConvertToRaid:output_type -> v1.ConvertToRaidResponse - 21, // 25: v1.GroupService.ChangeLeader:output_type -> v1.ChangeLeaderResponse - 8, // 26: v1.GroupService.AcceptInvite:output_type -> v1.AcceptInviteResponse - 10, // 27: v1.GroupService.GetGroup:output_type -> v1.GetGroupResponse - 10, // 28: v1.GroupService.GetGroupByMember:output_type -> v1.GetGroupResponse - 13, // 29: v1.GroupService.GetGroupIDByPlayer:output_type -> v1.GetGroupIDByPlayerResponse - 25, // 30: v1.GroupService.SetGroupTargetIcon:output_type -> v1.SetGroupTargetIconResponse - 29, // 31: v1.GroupService.SetLootMethod:output_type -> v1.SetLootMethodResponse - 31, // 32: v1.GroupService.SetDungeonDifficulty:output_type -> v1.SetDungeonDifficultyResponse - 33, // 33: v1.GroupService.SetRaidDifficulty:output_type -> v1.SetRaidDifficultyResponse - 23, // 34: v1.GroupService.SendMessage:output_type -> v1.SendGroupMessageResponse - 21, // [21:35] is the sub-list for method output_type - 7, // [7:21] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name -} - -func init() { file_group_proto_init() } -func file_group_proto_init() { - if File_group_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_group_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InviteParams); i { + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartReadyCheckResponse.ProtoReflect.Descriptor instead. +func (*StartReadyCheckResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{35} +} + +func (x *StartReadyCheckResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type SetReadyCheckMemberStateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + MemberGUID uint64 `protobuf:"varint,3,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + State uint32 `protobuf:"varint,4,opt,name=state,proto3" json:"state,omitempty"` +} + +func (x *SetReadyCheckMemberStateRequest) Reset() { + *x = SetReadyCheckMemberStateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetReadyCheckMemberStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetReadyCheckMemberStateRequest) ProtoMessage() {} + +func (x *SetReadyCheckMemberStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[36] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetReadyCheckMemberStateRequest.ProtoReflect.Descriptor instead. +func (*SetReadyCheckMemberStateRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{36} +} + +func (x *SetReadyCheckMemberStateRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SetReadyCheckMemberStateRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *SetReadyCheckMemberStateRequest) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *SetReadyCheckMemberStateRequest) GetState() uint32 { + if x != nil { + return x.State + } + return 0 +} + +type SetReadyCheckMemberStateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *SetReadyCheckMemberStateResponse) Reset() { + *x = SetReadyCheckMemberStateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetReadyCheckMemberStateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetReadyCheckMemberStateResponse) ProtoMessage() {} + +func (x *SetReadyCheckMemberStateResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetReadyCheckMemberStateResponse.ProtoReflect.Descriptor instead. +func (*SetReadyCheckMemberStateResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{37} +} + +func (x *SetReadyCheckMemberStateResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type FinishReadyCheckRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` +} + +func (x *FinishReadyCheckRequest) Reset() { + *x = FinishReadyCheckRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FinishReadyCheckRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FinishReadyCheckRequest) ProtoMessage() {} + +func (x *FinishReadyCheckRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FinishReadyCheckRequest.ProtoReflect.Descriptor instead. +func (*FinishReadyCheckRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{38} +} + +func (x *FinishReadyCheckRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *FinishReadyCheckRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *FinishReadyCheckRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +type FinishReadyCheckResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *FinishReadyCheckResponse) Reset() { + *x = FinishReadyCheckResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FinishReadyCheckResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FinishReadyCheckResponse) ProtoMessage() {} + +func (x *FinishReadyCheckResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[39] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FinishReadyCheckResponse.ProtoReflect.Descriptor instead. +func (*FinishReadyCheckResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{39} +} + +func (x *FinishReadyCheckResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type ChangeMemberSubGroupRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + UpdaterGUID uint64 `protobuf:"varint,3,opt,name=updaterGUID,proto3" json:"updaterGUID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + SubGroup uint32 `protobuf:"varint,5,opt,name=subGroup,proto3" json:"subGroup,omitempty"` +} + +func (x *ChangeMemberSubGroupRequest) Reset() { + *x = ChangeMemberSubGroupRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChangeMemberSubGroupRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChangeMemberSubGroupRequest) ProtoMessage() {} + +func (x *ChangeMemberSubGroupRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[40] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChangeMemberSubGroupRequest.ProtoReflect.Descriptor instead. +func (*ChangeMemberSubGroupRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{40} +} + +func (x *ChangeMemberSubGroupRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *ChangeMemberSubGroupRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *ChangeMemberSubGroupRequest) GetUpdaterGUID() uint64 { + if x != nil { + return x.UpdaterGUID + } + return 0 +} + +func (x *ChangeMemberSubGroupRequest) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *ChangeMemberSubGroupRequest) GetSubGroup() uint32 { + if x != nil { + return x.SubGroup + } + return 0 +} + +type ChangeMemberSubGroupResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *ChangeMemberSubGroupResponse) Reset() { + *x = ChangeMemberSubGroupResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChangeMemberSubGroupResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChangeMemberSubGroupResponse) ProtoMessage() {} + +func (x *ChangeMemberSubGroupResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[41] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChangeMemberSubGroupResponse.ProtoReflect.Descriptor instead. +func (*ChangeMemberSubGroupResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{41} +} + +func (x *ChangeMemberSubGroupResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type SetMemberFlagsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + UpdaterGUID uint64 `protobuf:"varint,3,opt,name=updaterGUID,proto3" json:"updaterGUID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + Flags uint32 `protobuf:"varint,5,opt,name=flags,proto3" json:"flags,omitempty"` + Roles uint32 `protobuf:"varint,6,opt,name=roles,proto3" json:"roles,omitempty"` +} + +func (x *SetMemberFlagsRequest) Reset() { + *x = SetMemberFlagsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetMemberFlagsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetMemberFlagsRequest) ProtoMessage() {} + +func (x *SetMemberFlagsRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[42] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetMemberFlagsRequest.ProtoReflect.Descriptor instead. +func (*SetMemberFlagsRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{42} +} + +func (x *SetMemberFlagsRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SetMemberFlagsRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *SetMemberFlagsRequest) GetUpdaterGUID() uint64 { + if x != nil { + return x.UpdaterGUID + } + return 0 +} + +func (x *SetMemberFlagsRequest) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *SetMemberFlagsRequest) GetFlags() uint32 { + if x != nil { + return x.Flags + } + return 0 +} + +func (x *SetMemberFlagsRequest) GetRoles() uint32 { + if x != nil { + return x.Roles + } + return 0 +} + +type SetMemberFlagsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *SetMemberFlagsResponse) Reset() { + *x = SetMemberFlagsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetMemberFlagsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetMemberFlagsResponse) ProtoMessage() {} + +func (x *SetMemberFlagsResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[43] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetMemberFlagsResponse.ProtoReflect.Descriptor instead. +func (*SetMemberFlagsResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{43} +} + +func (x *SetMemberFlagsResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type AcceptedLfgGroupMember struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RealmID uint32 `protobuf:"varint,1,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,2,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + SelectedRoles uint32 `protobuf:"varint,3,opt,name=selectedRoles,proto3" json:"selectedRoles,omitempty"` + AssignedRole uint32 `protobuf:"varint,4,opt,name=assignedRole,proto3" json:"assignedRole,omitempty"` + QueueLeaderRealmID uint32 `protobuf:"varint,5,opt,name=queueLeaderRealmID,proto3" json:"queueLeaderRealmID,omitempty"` + QueueLeaderGUID uint64 `protobuf:"varint,6,opt,name=queueLeaderGUID,proto3" json:"queueLeaderGUID,omitempty"` +} + +func (x *AcceptedLfgGroupMember) Reset() { + *x = AcceptedLfgGroupMember{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AcceptedLfgGroupMember) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AcceptedLfgGroupMember) ProtoMessage() {} + +func (x *AcceptedLfgGroupMember) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[44] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AcceptedLfgGroupMember.ProtoReflect.Descriptor instead. +func (*AcceptedLfgGroupMember) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{44} +} + +func (x *AcceptedLfgGroupMember) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *AcceptedLfgGroupMember) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *AcceptedLfgGroupMember) GetSelectedRoles() uint32 { + if x != nil { + return x.SelectedRoles + } + return 0 +} + +func (x *AcceptedLfgGroupMember) GetAssignedRole() uint32 { + if x != nil { + return x.AssignedRole + } + return 0 +} + +func (x *AcceptedLfgGroupMember) GetQueueLeaderRealmID() uint32 { + if x != nil { + return x.QueueLeaderRealmID + } + return 0 +} + +func (x *AcceptedLfgGroupMember) GetQueueLeaderGUID() uint64 { + if x != nil { + return x.QueueLeaderGUID + } + return 0 +} + +type RegisterAcceptedLfgGroupRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + ProposalID uint32 `protobuf:"varint,3,opt,name=proposalID,proto3" json:"proposalID,omitempty"` + DungeonEntry uint32 `protobuf:"varint,4,opt,name=dungeonEntry,proto3" json:"dungeonEntry,omitempty"` + LeaderRealmID uint32 `protobuf:"varint,5,opt,name=leaderRealmID,proto3" json:"leaderRealmID,omitempty"` + LeaderGUID uint64 `protobuf:"varint,6,opt,name=leaderGUID,proto3" json:"leaderGUID,omitempty"` + CrossRealm bool `protobuf:"varint,7,opt,name=crossRealm,proto3" json:"crossRealm,omitempty"` + Members []*AcceptedLfgGroupMember `protobuf:"bytes,8,rep,name=members,proto3" json:"members,omitempty"` +} + +func (x *RegisterAcceptedLfgGroupRequest) Reset() { + *x = RegisterAcceptedLfgGroupRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterAcceptedLfgGroupRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterAcceptedLfgGroupRequest) ProtoMessage() {} + +func (x *RegisterAcceptedLfgGroupRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[45] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterAcceptedLfgGroupRequest.ProtoReflect.Descriptor instead. +func (*RegisterAcceptedLfgGroupRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{45} +} + +func (x *RegisterAcceptedLfgGroupRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *RegisterAcceptedLfgGroupRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *RegisterAcceptedLfgGroupRequest) GetProposalID() uint32 { + if x != nil { + return x.ProposalID + } + return 0 +} + +func (x *RegisterAcceptedLfgGroupRequest) GetDungeonEntry() uint32 { + if x != nil { + return x.DungeonEntry + } + return 0 +} + +func (x *RegisterAcceptedLfgGroupRequest) GetLeaderRealmID() uint32 { + if x != nil { + return x.LeaderRealmID + } + return 0 +} + +func (x *RegisterAcceptedLfgGroupRequest) GetLeaderGUID() uint64 { + if x != nil { + return x.LeaderGUID + } + return 0 +} + +func (x *RegisterAcceptedLfgGroupRequest) GetCrossRealm() bool { + if x != nil { + return x.CrossRealm + } + return false +} + +func (x *RegisterAcceptedLfgGroupRequest) GetMembers() []*AcceptedLfgGroupMember { + if x != nil { + return x.Members + } + return nil +} + +type RegisterAcceptedLfgGroupResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + GroupID uint32 `protobuf:"varint,2,opt,name=groupID,proto3" json:"groupID,omitempty"` +} + +func (x *RegisterAcceptedLfgGroupResponse) Reset() { + *x = RegisterAcceptedLfgGroupResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterAcceptedLfgGroupResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterAcceptedLfgGroupResponse) ProtoMessage() {} + +func (x *RegisterAcceptedLfgGroupResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[46] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterAcceptedLfgGroupResponse.ProtoReflect.Descriptor instead. +func (*RegisterAcceptedLfgGroupResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{46} +} + +func (x *RegisterAcceptedLfgGroupResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *RegisterAcceptedLfgGroupResponse) GetGroupID() uint32 { + if x != nil { + return x.GroupID + } + return 0 +} + +type MaterializedLfgGroupMember struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RealmID uint32 `protobuf:"varint,1,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,2,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + IsOnline bool `protobuf:"varint,4,opt,name=isOnline,proto3" json:"isOnline,omitempty"` + Flags uint32 `protobuf:"varint,5,opt,name=flags,proto3" json:"flags,omitempty"` + Roles uint32 `protobuf:"varint,6,opt,name=roles,proto3" json:"roles,omitempty"` + SubGroup uint32 `protobuf:"varint,7,opt,name=subGroup,proto3" json:"subGroup,omitempty"` +} + +func (x *MaterializedLfgGroupMember) Reset() { + *x = MaterializedLfgGroupMember{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MaterializedLfgGroupMember) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MaterializedLfgGroupMember) ProtoMessage() {} + +func (x *MaterializedLfgGroupMember) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[47] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MaterializedLfgGroupMember.ProtoReflect.Descriptor instead. +func (*MaterializedLfgGroupMember) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{47} +} + +func (x *MaterializedLfgGroupMember) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *MaterializedLfgGroupMember) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *MaterializedLfgGroupMember) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *MaterializedLfgGroupMember) GetIsOnline() bool { + if x != nil { + return x.IsOnline + } + return false +} + +func (x *MaterializedLfgGroupMember) GetFlags() uint32 { + if x != nil { + return x.Flags + } + return 0 +} + +func (x *MaterializedLfgGroupMember) GetRoles() uint32 { + if x != nil { + return x.Roles + } + return 0 +} + +func (x *MaterializedLfgGroupMember) GetSubGroup() uint32 { + if x != nil { + return x.SubGroup + } + return 0 +} + +type RegisterMaterializedLfgGroupRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GroupID uint32 `protobuf:"varint,3,opt,name=groupID,proto3" json:"groupID,omitempty"` + LeaderGUID uint64 `protobuf:"varint,4,opt,name=leaderGUID,proto3" json:"leaderGUID,omitempty"` + GroupType uint32 `protobuf:"varint,5,opt,name=groupType,proto3" json:"groupType,omitempty"` + Difficulty uint32 `protobuf:"varint,6,opt,name=difficulty,proto3" json:"difficulty,omitempty"` + RaidDifficulty uint32 `protobuf:"varint,7,opt,name=raidDifficulty,proto3" json:"raidDifficulty,omitempty"` + Members []*MaterializedLfgGroupMember `protobuf:"bytes,8,rep,name=members,proto3" json:"members,omitempty"` +} + +func (x *RegisterMaterializedLfgGroupRequest) Reset() { + *x = RegisterMaterializedLfgGroupRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterMaterializedLfgGroupRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterMaterializedLfgGroupRequest) ProtoMessage() {} + +func (x *RegisterMaterializedLfgGroupRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[48] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterMaterializedLfgGroupRequest.ProtoReflect.Descriptor instead. +func (*RegisterMaterializedLfgGroupRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{48} +} + +func (x *RegisterMaterializedLfgGroupRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *RegisterMaterializedLfgGroupRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *RegisterMaterializedLfgGroupRequest) GetGroupID() uint32 { + if x != nil { + return x.GroupID + } + return 0 +} + +func (x *RegisterMaterializedLfgGroupRequest) GetLeaderGUID() uint64 { + if x != nil { + return x.LeaderGUID + } + return 0 +} + +func (x *RegisterMaterializedLfgGroupRequest) GetGroupType() uint32 { + if x != nil { + return x.GroupType + } + return 0 +} + +func (x *RegisterMaterializedLfgGroupRequest) GetDifficulty() uint32 { + if x != nil { + return x.Difficulty + } + return 0 +} + +func (x *RegisterMaterializedLfgGroupRequest) GetRaidDifficulty() uint32 { + if x != nil { + return x.RaidDifficulty + } + return 0 +} + +func (x *RegisterMaterializedLfgGroupRequest) GetMembers() []*MaterializedLfgGroupMember { + if x != nil { + return x.Members + } + return nil +} + +type RegisterMaterializedLfgGroupResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *RegisterMaterializedLfgGroupResponse) Reset() { + *x = RegisterMaterializedLfgGroupResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[49] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterMaterializedLfgGroupResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterMaterializedLfgGroupResponse) ProtoMessage() {} + +func (x *RegisterMaterializedLfgGroupResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[49] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterMaterializedLfgGroupResponse.ProtoReflect.Descriptor instead. +func (*RegisterMaterializedLfgGroupResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{49} +} + +func (x *RegisterMaterializedLfgGroupResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type UpdateMemberStateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + MemberGUID uint64 `protobuf:"varint,3,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + Online bool `protobuf:"varint,4,opt,name=online,proto3" json:"online,omitempty"` + Level uint32 `protobuf:"varint,5,opt,name=level,proto3" json:"level,omitempty"` + ClassID uint32 `protobuf:"varint,6,opt,name=classID,proto3" json:"classID,omitempty"` + ZoneID uint32 `protobuf:"varint,7,opt,name=zoneID,proto3" json:"zoneID,omitempty"` + MapID uint32 `protobuf:"varint,8,opt,name=mapID,proto3" json:"mapID,omitempty"` + Health uint32 `protobuf:"varint,9,opt,name=health,proto3" json:"health,omitempty"` + MaxHealth uint32 `protobuf:"varint,10,opt,name=maxHealth,proto3" json:"maxHealth,omitempty"` + PowerType uint32 `protobuf:"varint,11,opt,name=powerType,proto3" json:"powerType,omitempty"` + Power uint32 `protobuf:"varint,12,opt,name=power,proto3" json:"power,omitempty"` + MaxPower uint32 `protobuf:"varint,13,opt,name=maxPower,proto3" json:"maxPower,omitempty"` + InstanceID *uint32 `protobuf:"varint,14,opt,name=instanceID,proto3,oneof" json:"instanceID,omitempty"` +} + +func (x *UpdateMemberStateRequest) Reset() { + *x = UpdateMemberStateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[50] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateMemberStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateMemberStateRequest) ProtoMessage() {} + +func (x *UpdateMemberStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[50] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateMemberStateRequest.ProtoReflect.Descriptor instead. +func (*UpdateMemberStateRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{50} +} + +func (x *UpdateMemberStateRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *UpdateMemberStateRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *UpdateMemberStateRequest) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *UpdateMemberStateRequest) GetOnline() bool { + if x != nil { + return x.Online + } + return false +} + +func (x *UpdateMemberStateRequest) GetLevel() uint32 { + if x != nil { + return x.Level + } + return 0 +} + +func (x *UpdateMemberStateRequest) GetClassID() uint32 { + if x != nil { + return x.ClassID + } + return 0 +} + +func (x *UpdateMemberStateRequest) GetZoneID() uint32 { + if x != nil { + return x.ZoneID + } + return 0 +} + +func (x *UpdateMemberStateRequest) GetMapID() uint32 { + if x != nil { + return x.MapID + } + return 0 +} + +func (x *UpdateMemberStateRequest) GetHealth() uint32 { + if x != nil { + return x.Health + } + return 0 +} + +func (x *UpdateMemberStateRequest) GetMaxHealth() uint32 { + if x != nil { + return x.MaxHealth + } + return 0 +} + +func (x *UpdateMemberStateRequest) GetPowerType() uint32 { + if x != nil { + return x.PowerType + } + return 0 +} + +func (x *UpdateMemberStateRequest) GetPower() uint32 { + if x != nil { + return x.Power + } + return 0 +} + +func (x *UpdateMemberStateRequest) GetMaxPower() uint32 { + if x != nil { + return x.MaxPower + } + return 0 +} + +func (x *UpdateMemberStateRequest) GetInstanceID() uint32 { + if x != nil && x.InstanceID != nil { + return *x.InstanceID + } + return 0 +} + +type UpdateMemberStateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *UpdateMemberStateResponse) Reset() { + *x = UpdateMemberStateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[51] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateMemberStateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateMemberStateResponse) ProtoMessage() {} + +func (x *UpdateMemberStateResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[51] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateMemberStateResponse.ProtoReflect.Descriptor instead. +func (*UpdateMemberStateResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{51} +} + +func (x *UpdateMemberStateResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type PlayerStateSnapshot struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MemberGUID uint64 `protobuf:"varint,1,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + Online bool `protobuf:"varint,2,opt,name=online,proto3" json:"online,omitempty"` + Level uint32 `protobuf:"varint,3,opt,name=level,proto3" json:"level,omitempty"` + ClassID uint32 `protobuf:"varint,4,opt,name=classID,proto3" json:"classID,omitempty"` + ZoneID uint32 `protobuf:"varint,5,opt,name=zoneID,proto3" json:"zoneID,omitempty"` + MapID uint32 `protobuf:"varint,6,opt,name=mapID,proto3" json:"mapID,omitempty"` + Health uint32 `protobuf:"varint,7,opt,name=health,proto3" json:"health,omitempty"` + MaxHealth uint32 `protobuf:"varint,8,opt,name=maxHealth,proto3" json:"maxHealth,omitempty"` + PowerType uint32 `protobuf:"varint,9,opt,name=powerType,proto3" json:"powerType,omitempty"` + Power uint32 `protobuf:"varint,10,opt,name=power,proto3" json:"power,omitempty"` + MaxPower uint32 `protobuf:"varint,11,opt,name=maxPower,proto3" json:"maxPower,omitempty"` + TimestampMs uint64 `protobuf:"varint,12,opt,name=timestampMs,proto3" json:"timestampMs,omitempty"` + AurasKnown bool `protobuf:"varint,13,opt,name=aurasKnown,proto3" json:"aurasKnown,omitempty"` + Auras []*PlayerAuraSnapshot `protobuf:"bytes,14,rep,name=auras,proto3" json:"auras,omitempty"` + InstanceID *uint32 `protobuf:"varint,15,opt,name=instanceID,proto3,oneof" json:"instanceID,omitempty"` + Dead *bool `protobuf:"varint,16,opt,name=dead,proto3,oneof" json:"dead,omitempty"` + Ghost *bool `protobuf:"varint,17,opt,name=ghost,proto3,oneof" json:"ghost,omitempty"` +} + +func (x *PlayerStateSnapshot) Reset() { + *x = PlayerStateSnapshot{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[52] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PlayerStateSnapshot) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PlayerStateSnapshot) ProtoMessage() {} + +func (x *PlayerStateSnapshot) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[52] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PlayerStateSnapshot.ProtoReflect.Descriptor instead. +func (*PlayerStateSnapshot) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{52} +} + +func (x *PlayerStateSnapshot) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *PlayerStateSnapshot) GetOnline() bool { + if x != nil { + return x.Online + } + return false +} + +func (x *PlayerStateSnapshot) GetLevel() uint32 { + if x != nil { + return x.Level + } + return 0 +} + +func (x *PlayerStateSnapshot) GetClassID() uint32 { + if x != nil { + return x.ClassID + } + return 0 +} + +func (x *PlayerStateSnapshot) GetZoneID() uint32 { + if x != nil { + return x.ZoneID + } + return 0 +} + +func (x *PlayerStateSnapshot) GetMapID() uint32 { + if x != nil { + return x.MapID + } + return 0 +} + +func (x *PlayerStateSnapshot) GetHealth() uint32 { + if x != nil { + return x.Health + } + return 0 +} + +func (x *PlayerStateSnapshot) GetMaxHealth() uint32 { + if x != nil { + return x.MaxHealth + } + return 0 +} + +func (x *PlayerStateSnapshot) GetPowerType() uint32 { + if x != nil { + return x.PowerType + } + return 0 +} + +func (x *PlayerStateSnapshot) GetPower() uint32 { + if x != nil { + return x.Power + } + return 0 +} + +func (x *PlayerStateSnapshot) GetMaxPower() uint32 { + if x != nil { + return x.MaxPower + } + return 0 +} + +func (x *PlayerStateSnapshot) GetTimestampMs() uint64 { + if x != nil { + return x.TimestampMs + } + return 0 +} + +func (x *PlayerStateSnapshot) GetAurasKnown() bool { + if x != nil { + return x.AurasKnown + } + return false +} + +func (x *PlayerStateSnapshot) GetAuras() []*PlayerAuraSnapshot { + if x != nil { + return x.Auras + } + return nil +} + +func (x *PlayerStateSnapshot) GetInstanceID() uint32 { + if x != nil && x.InstanceID != nil { + return *x.InstanceID + } + return 0 +} + +func (x *PlayerStateSnapshot) GetDead() bool { + if x != nil && x.Dead != nil { + return *x.Dead + } + return false +} + +func (x *PlayerStateSnapshot) GetGhost() bool { + if x != nil && x.Ghost != nil { + return *x.Ghost + } + return false +} + +type PlayerAuraSnapshot struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Slot uint32 `protobuf:"varint,1,opt,name=slot,proto3" json:"slot,omitempty"` + SpellID uint32 `protobuf:"varint,2,opt,name=spellID,proto3" json:"spellID,omitempty"` + Flags uint32 `protobuf:"varint,3,opt,name=flags,proto3" json:"flags,omitempty"` +} + +func (x *PlayerAuraSnapshot) Reset() { + *x = PlayerAuraSnapshot{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[53] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PlayerAuraSnapshot) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PlayerAuraSnapshot) ProtoMessage() {} + +func (x *PlayerAuraSnapshot) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[53] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PlayerAuraSnapshot.ProtoReflect.Descriptor instead. +func (*PlayerAuraSnapshot) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{53} +} + +func (x *PlayerAuraSnapshot) GetSlot() uint32 { + if x != nil { + return x.Slot + } + return 0 +} + +func (x *PlayerAuraSnapshot) GetSpellID() uint32 { + if x != nil { + return x.SpellID + } + return 0 +} + +func (x *PlayerAuraSnapshot) GetFlags() uint32 { + if x != nil { + return x.Flags + } + return 0 +} + +type BulkUpdateMemberStatesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + SourceGatewayID string `protobuf:"bytes,3,opt,name=sourceGatewayID,proto3" json:"sourceGatewayID,omitempty"` + SourceWorldserverID string `protobuf:"bytes,4,opt,name=sourceWorldserverID,proto3" json:"sourceWorldserverID,omitempty"` + Snapshots []*PlayerStateSnapshot `protobuf:"bytes,5,rep,name=snapshots,proto3" json:"snapshots,omitempty"` +} + +func (x *BulkUpdateMemberStatesRequest) Reset() { + *x = BulkUpdateMemberStatesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[54] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BulkUpdateMemberStatesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BulkUpdateMemberStatesRequest) ProtoMessage() {} + +func (x *BulkUpdateMemberStatesRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[54] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BulkUpdateMemberStatesRequest.ProtoReflect.Descriptor instead. +func (*BulkUpdateMemberStatesRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{54} +} + +func (x *BulkUpdateMemberStatesRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *BulkUpdateMemberStatesRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *BulkUpdateMemberStatesRequest) GetSourceGatewayID() string { + if x != nil { + return x.SourceGatewayID + } + return "" +} + +func (x *BulkUpdateMemberStatesRequest) GetSourceWorldserverID() string { + if x != nil { + return x.SourceWorldserverID + } + return "" +} + +func (x *BulkUpdateMemberStatesRequest) GetSnapshots() []*PlayerStateSnapshot { + if x != nil { + return x.Snapshots + } + return nil +} + +type BulkUpdateMemberStatesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *BulkUpdateMemberStatesResponse) Reset() { + *x = BulkUpdateMemberStatesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[55] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BulkUpdateMemberStatesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BulkUpdateMemberStatesResponse) ProtoMessage() {} + +func (x *BulkUpdateMemberStatesResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[55] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BulkUpdateMemberStatesResponse.ProtoReflect.Descriptor instead. +func (*BulkUpdateMemberStatesResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{55} +} + +func (x *BulkUpdateMemberStatesResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type ResetInstanceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + MapID uint32 `protobuf:"varint,4,opt,name=mapID,proto3" json:"mapID,omitempty"` + Difficulty uint32 `protobuf:"varint,5,opt,name=difficulty,proto3" json:"difficulty,omitempty"` +} + +func (x *ResetInstanceRequest) Reset() { + *x = ResetInstanceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[56] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResetInstanceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResetInstanceRequest) ProtoMessage() {} + +func (x *ResetInstanceRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[56] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResetInstanceRequest.ProtoReflect.Descriptor instead. +func (*ResetInstanceRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{56} +} + +func (x *ResetInstanceRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *ResetInstanceRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *ResetInstanceRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *ResetInstanceRequest) GetMapID() uint32 { + if x != nil { + return x.MapID + } + return 0 +} + +func (x *ResetInstanceRequest) GetDifficulty() uint32 { + if x != nil { + return x.Difficulty + } + return 0 +} + +type ResetInstanceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *ResetInstanceResponse) Reset() { + *x = ResetInstanceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[57] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResetInstanceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResetInstanceResponse) ProtoMessage() {} + +func (x *ResetInstanceResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[57] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResetInstanceResponse.ProtoReflect.Descriptor instead. +func (*ResetInstanceResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{57} +} + +func (x *ResetInstanceResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type SetInstanceBindExtensionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + MapID uint32 `protobuf:"varint,4,opt,name=mapID,proto3" json:"mapID,omitempty"` + Difficulty uint32 `protobuf:"varint,5,opt,name=difficulty,proto3" json:"difficulty,omitempty"` + Extended bool `protobuf:"varint,6,opt,name=extended,proto3" json:"extended,omitempty"` +} + +func (x *SetInstanceBindExtensionRequest) Reset() { + *x = SetInstanceBindExtensionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[58] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetInstanceBindExtensionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetInstanceBindExtensionRequest) ProtoMessage() {} + +func (x *SetInstanceBindExtensionRequest) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[58] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetInstanceBindExtensionRequest.ProtoReflect.Descriptor instead. +func (*SetInstanceBindExtensionRequest) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{58} +} + +func (x *SetInstanceBindExtensionRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SetInstanceBindExtensionRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *SetInstanceBindExtensionRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *SetInstanceBindExtensionRequest) GetMapID() uint32 { + if x != nil { + return x.MapID + } + return 0 +} + +func (x *SetInstanceBindExtensionRequest) GetDifficulty() uint32 { + if x != nil { + return x.Difficulty + } + return 0 +} + +func (x *SetInstanceBindExtensionRequest) GetExtended() bool { + if x != nil { + return x.Extended + } + return false +} + +type SetInstanceBindExtensionResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *SetInstanceBindExtensionResponse) Reset() { + *x = SetInstanceBindExtensionResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[59] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetInstanceBindExtensionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetInstanceBindExtensionResponse) ProtoMessage() {} + +func (x *SetInstanceBindExtensionResponse) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[59] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetInstanceBindExtensionResponse.ProtoReflect.Descriptor instead. +func (*SetInstanceBindExtensionResponse) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{59} +} + +func (x *SetInstanceBindExtensionResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type GetGroupResponse_GroupMember struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` + Flags uint32 `protobuf:"varint,2,opt,name=flags,proto3" json:"flags,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + IsOnline bool `protobuf:"varint,4,opt,name=isOnline,proto3" json:"isOnline,omitempty"` + SubGroup uint32 `protobuf:"varint,5,opt,name=subGroup,proto3" json:"subGroup,omitempty"` + Roles uint32 `protobuf:"varint,6,opt,name=roles,proto3" json:"roles,omitempty"` + RealmID uint32 `protobuf:"varint,7,opt,name=realmID,proto3" json:"realmID,omitempty"` +} + +func (x *GetGroupResponse_GroupMember) Reset() { + *x = GetGroupResponse_GroupMember{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[60] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetGroupResponse_GroupMember) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetGroupResponse_GroupMember) ProtoMessage() {} + +func (x *GetGroupResponse_GroupMember) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[60] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetGroupResponse_GroupMember.ProtoReflect.Descriptor instead. +func (*GetGroupResponse_GroupMember) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{7, 0} +} + +func (x *GetGroupResponse_GroupMember) GetGuid() uint64 { + if x != nil { + return x.Guid + } + return 0 +} + +func (x *GetGroupResponse_GroupMember) GetFlags() uint32 { + if x != nil { + return x.Flags + } + return 0 +} + +func (x *GetGroupResponse_GroupMember) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *GetGroupResponse_GroupMember) GetIsOnline() bool { + if x != nil { + return x.IsOnline + } + return false +} + +func (x *GetGroupResponse_GroupMember) GetSubGroup() uint32 { + if x != nil { + return x.SubGroup + } + return 0 +} + +func (x *GetGroupResponse_GroupMember) GetRoles() uint32 { + if x != nil { + return x.Roles + } + return 0 +} + +func (x *GetGroupResponse_GroupMember) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +type GetGroupResponse_Group struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Leader uint64 `protobuf:"varint,2,opt,name=leader,proto3" json:"leader,omitempty"` + LootMethod uint32 `protobuf:"varint,3,opt,name=lootMethod,proto3" json:"lootMethod,omitempty"` + Looter uint64 `protobuf:"varint,4,opt,name=looter,proto3" json:"looter,omitempty"` + LootThreshold uint32 `protobuf:"varint,5,opt,name=lootThreshold,proto3" json:"lootThreshold,omitempty"` + GroupType uint32 `protobuf:"varint,6,opt,name=groupType,proto3" json:"groupType,omitempty"` + Difficulty uint32 `protobuf:"varint,7,opt,name=difficulty,proto3" json:"difficulty,omitempty"` + RaidDifficulty uint32 `protobuf:"varint,8,opt,name=raidDifficulty,proto3" json:"raidDifficulty,omitempty"` + MasterLooter uint64 `protobuf:"varint,9,opt,name=masterLooter,proto3" json:"masterLooter,omitempty"` + TargetIconsList []uint64 `protobuf:"varint,10,rep,packed,name=targetIconsList,proto3" json:"targetIconsList,omitempty"` + Members []*GetGroupResponse_GroupMember `protobuf:"bytes,11,rep,name=members,proto3" json:"members,omitempty"` + RealmID uint32 `protobuf:"varint,12,opt,name=realmID,proto3" json:"realmID,omitempty"` +} + +func (x *GetGroupResponse_Group) Reset() { + *x = GetGroupResponse_Group{} + if protoimpl.UnsafeEnabled { + mi := &file_group_proto_msgTypes[61] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetGroupResponse_Group) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetGroupResponse_Group) ProtoMessage() {} + +func (x *GetGroupResponse_Group) ProtoReflect() protoreflect.Message { + mi := &file_group_proto_msgTypes[61] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetGroupResponse_Group.ProtoReflect.Descriptor instead. +func (*GetGroupResponse_Group) Descriptor() ([]byte, []int) { + return file_group_proto_rawDescGZIP(), []int{7, 1} +} + +func (x *GetGroupResponse_Group) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *GetGroupResponse_Group) GetLeader() uint64 { + if x != nil { + return x.Leader + } + return 0 +} + +func (x *GetGroupResponse_Group) GetLootMethod() uint32 { + if x != nil { + return x.LootMethod + } + return 0 +} + +func (x *GetGroupResponse_Group) GetLooter() uint64 { + if x != nil { + return x.Looter + } + return 0 +} + +func (x *GetGroupResponse_Group) GetLootThreshold() uint32 { + if x != nil { + return x.LootThreshold + } + return 0 +} + +func (x *GetGroupResponse_Group) GetGroupType() uint32 { + if x != nil { + return x.GroupType + } + return 0 +} + +func (x *GetGroupResponse_Group) GetDifficulty() uint32 { + if x != nil { + return x.Difficulty + } + return 0 +} + +func (x *GetGroupResponse_Group) GetRaidDifficulty() uint32 { + if x != nil { + return x.RaidDifficulty + } + return 0 +} + +func (x *GetGroupResponse_Group) GetMasterLooter() uint64 { + if x != nil { + return x.MasterLooter + } + return 0 +} + +func (x *GetGroupResponse_Group) GetTargetIconsList() []uint64 { + if x != nil { + return x.TargetIconsList + } + return nil +} + +func (x *GetGroupResponse_Group) GetMembers() []*GetGroupResponse_GroupMember { + if x != nil { + return x.Members + } + return nil +} + +func (x *GetGroupResponse_Group) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +var File_group_proto protoreflect.FileDescriptor + +var file_group_proto_rawDesc = []byte{ + 0x0a, 0x0b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, + 0x31, 0x22, 0xb2, 0x01, 0x0a, 0x0c, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, + 0x0a, 0x07, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x07, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x76, 0x69, + 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x69, 0x6e, 0x76, 0x69, 0x74, + 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x64, 0x4e, + 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, + 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x72, 0x0a, 0x0e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x31, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x76, 0x31, 0x2e, + 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x1b, 0x0a, + 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, + 0x09, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x01, 0x22, 0x58, 0x0a, 0x12, 0x41, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, + 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x70, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x22, 0x92, 0x01, 0x0a, 0x14, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x49, + 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x37, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x2f, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x6e, + 0x76, 0x69, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x01, 0x12, 0x09, + 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x02, 0x22, 0x59, 0x0a, 0x13, 0x44, 0x65, 0x63, + 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, + 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x70, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x22, 0x94, 0x01, 0x0a, 0x15, 0x44, 0x65, 0x63, 0x6c, 0x69, 0x6e, 0x65, + 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x38, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x76, + 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x2f, 0x0a, 0x06, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, + 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x01, + 0x12, 0x09, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x02, 0x22, 0x57, 0x0a, 0x0f, 0x47, + 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x49, 0x44, 0x22, 0xa6, 0x05, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x30, 0x0a, 0x05, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0xb3, 0x01, + 0x0a, 0x0b, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, + 0x04, 0x67, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, 0x75, 0x69, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, + 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, + 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x75, 0x62, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x1a, 0x97, 0x03, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6c, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x6f, 0x6f, 0x74, 0x4d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x6f, 0x6f, 0x74, 0x4d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x6f, 0x74, 0x65, 0x72, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6c, 0x6f, 0x6f, 0x74, 0x65, 0x72, 0x12, 0x24, 0x0a, + 0x0d, 0x6c, 0x6f, 0x6f, 0x74, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6c, 0x6f, 0x6f, 0x74, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, + 0x6f, 0x6c, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, + 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x61, 0x69, 0x64, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, + 0x6c, 0x74, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x72, 0x61, 0x69, 0x64, 0x44, + 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x6d, 0x61, 0x73, + 0x74, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x74, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x74, 0x65, 0x72, 0x12, 0x28, 0x0a, + 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x73, 0x4c, 0x69, 0x73, 0x74, + 0x18, 0x0a, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x63, + 0x6f, 0x6e, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x22, 0x5d, 0x0a, + 0x17, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x79, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x22, 0x5f, 0x0a, 0x19, + 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x42, 0x79, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x22, 0x6c, 0x0a, + 0x1a, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x42, 0x79, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, + 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, + 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x22, 0x6a, 0x0a, 0x1a, 0x47, + 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x73, 0x22, 0xc3, 0x02, 0x0a, 0x0f, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x6f, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6f, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x05, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6c, 0x64, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x44, 0x12, 0x14, 0x0a, + 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x61, + 0x70, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, + 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x4b, + 0x6e, 0x6f, 0x77, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4d, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4d, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x4d, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x4d, 0x73, 0x22, 0x64, 0x0a, + 0x1b, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x33, + 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6c, + 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x22, 0x8a, 0x01, 0x0a, 0x0e, 0x55, 0x6e, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, + 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x22, 0x76, 0x0a, 0x10, 0x55, 0x6e, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x69, 0x6e, + 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x1b, 0x0a, 0x06, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x09, 0x0a, + 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x01, 0x22, 0x56, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, + 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x22, 0x26, 0x0a, 0x12, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x59, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x74, 0x54, 0x6f, 0x52, 0x61, 0x69, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, + 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, + 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x70, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x70, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x22, 0x29, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x6f, + 0x52, 0x61, 0x69, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x76, + 0x0a, 0x12, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x4c, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6e, 0x65, 0x77, + 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x22, 0x28, 0x0a, 0x14, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x22, 0xe2, 0x01, 0x0a, 0x16, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, + 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, + 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, + 0x24, 0x0a, 0x0d, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x68, 0x61, 0x74, 0x54, 0x61, 0x67, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x68, + 0x61, 0x74, 0x54, 0x61, 0x67, 0x22, 0x2c, 0x0a, 0x18, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x22, 0x9f, 0x01, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x16, 0x0a, + 0x06, 0x69, 0x63, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x69, + 0x63, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, + 0x55, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x47, 0x55, 0x49, 0x44, 0x22, 0x2e, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x68, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, + 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, + 0x49, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x18, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x04, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x53, + 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, + 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, + 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x6f, 0x6f, 0x74, 0x4d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6c, 0x6f, 0x6f, + 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x6c, 0x6f, 0x6f, 0x74, 0x54, + 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, + 0x6c, 0x6f, 0x6f, 0x74, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0x29, 0x0a, + 0x15, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x89, 0x01, 0x0a, 0x1b, 0x53, 0x65, 0x74, + 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, + 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, + 0x75, 0x6c, 0x74, 0x79, 0x22, 0x9a, 0x01, 0x0a, 0x1c, 0x53, 0x65, 0x74, 0x44, 0x75, 0x6e, 0x67, + 0x65, 0x6f, 0x6e, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, + 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x27, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x49, 0x73, 0x49, 0x6e, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x10, + 0x01, 0x22, 0x86, 0x01, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x52, 0x61, 0x69, 0x64, 0x44, 0x69, 0x66, + 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, + 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, + 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, + 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x22, 0x91, 0x01, 0x0a, 0x19, 0x53, + 0x65, 0x74, 0x52, 0x61, 0x69, 0x64, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x74, 0x52, 0x61, 0x69, 0x64, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x24, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x49, 0x73, 0x49, 0x6e, 0x52, 0x61, 0x69, 0x64, 0x10, 0x01, 0x22, 0x84, + 0x01, 0x0a, 0x16, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x61, 0x64, 0x79, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6c, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x4d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x22, 0x2b, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, + 0x61, 0x64, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x22, 0x83, 0x01, 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x79, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x34, 0x0a, 0x20, 0x53, 0x65, 0x74, 0x52, + 0x65, 0x61, 0x64, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x65, + 0x0a, 0x17, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x52, 0x65, 0x61, 0x64, 0x79, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0x2c, 0x0a, 0x18, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x52, + 0x65, 0x61, 0x64, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x22, 0xa7, 0x01, 0x0a, 0x1b, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x53, 0x75, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, + 0x20, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x75, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x30, 0x0a, + 0x1c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x75, 0x62, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, + 0xb1, 0x01, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x46, 0x6c, 0x61, + 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x14, 0x0a, + 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x72, 0x6f, + 0x6c, 0x65, 0x73, 0x22, 0x2a, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, + 0xf6, 0x01, 0x0a, 0x16, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x4c, 0x66, 0x67, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x73, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x73, + 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0c, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2e, + 0x0a, 0x12, 0x71, 0x75, 0x65, 0x75, 0x65, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x71, 0x75, 0x65, 0x75, + 0x65, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x28, + 0x0a, 0x0f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x4c, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0xad, 0x02, 0x0a, 0x1f, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x4c, 0x66, 0x67, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, + 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x61, 0x6c, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x70, 0x72, + 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x75, 0x6e, 0x67, + 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, + 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x24, 0x0a, 0x0d, + 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, + 0x6c, 0x6d, 0x12, 0x34, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x08, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x4c, 0x66, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, + 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0x4e, 0x0a, 0x20, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x4c, 0x66, 0x67, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, + 0x0a, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x22, 0xce, 0x01, 0x0a, 0x1a, 0x4d, 0x61, 0x74, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4c, 0x66, 0x67, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, + 0x08, 0x73, 0x75, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x08, 0x73, 0x75, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0xab, 0x02, 0x0a, 0x23, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x64, 0x4c, 0x66, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, + 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6c, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x54, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, + 0x6c, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, + 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x61, 0x69, 0x64, 0x44, 0x69, 0x66, + 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x72, + 0x61, 0x69, 0x64, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x38, 0x0a, + 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, + 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, + 0x4c, 0x66, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x07, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0x38, 0x0a, 0x24, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4c, + 0x66, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, + 0x69, 0x22, 0x96, 0x03, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6f, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73, + 0x73, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, + 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x7a, 0x6f, 0x6e, 0x65, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x06, 0x7a, 0x6f, 0x6e, 0x65, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, + 0x70, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, + 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x48, + 0x65, 0x61, 0x6c, 0x74, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6d, 0x61, 0x78, + 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x54, + 0x79, 0x70, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x6f, 0x77, 0x65, 0x72, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x18, 0x0c, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x61, + 0x78, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6d, 0x61, + 0x78, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x49, 0x44, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x0a, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x22, 0x2d, 0x0a, 0x19, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x9c, 0x04, 0x0a, 0x13, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x06, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, + 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, + 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x7a, 0x6f, 0x6e, + 0x65, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x7a, 0x6f, 0x6e, 0x65, 0x49, + 0x44, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, + 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, + 0x1c, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x1c, 0x0a, + 0x09, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x09, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, + 0x6f, 0x77, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x6f, 0x77, 0x65, + 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x20, 0x0a, + 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4d, 0x73, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4d, 0x73, 0x12, + 0x1e, 0x0a, 0x0a, 0x61, 0x75, 0x72, 0x61, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x18, 0x0d, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, 0x75, 0x72, 0x61, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x12, + 0x2c, 0x0a, 0x05, 0x61, 0x75, 0x72, 0x61, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x41, 0x75, 0x72, 0x61, 0x53, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x05, 0x61, 0x75, 0x72, 0x61, 0x73, 0x12, 0x23, 0x0a, + 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x0f, 0x20, 0x01, 0x28, + 0x0d, 0x48, 0x00, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x88, + 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x64, 0x65, 0x61, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, + 0x48, 0x01, 0x52, 0x04, 0x64, 0x65, 0x61, 0x64, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x67, + 0x68, 0x6f, 0x73, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x05, 0x67, 0x68, + 0x6f, 0x73, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x49, 0x44, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x42, 0x08, + 0x0a, 0x06, 0x5f, 0x67, 0x68, 0x6f, 0x73, 0x74, 0x22, 0x58, 0x0a, 0x12, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x41, 0x75, 0x72, 0x61, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x6c, + 0x6f, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x70, 0x65, 0x6c, 0x6c, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x70, 0x65, 0x6c, 0x6c, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, + 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, + 0x67, 0x73, 0x22, 0xde, 0x01, 0x0a, 0x1d, 0x42, 0x75, 0x6c, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x44, 0x12, 0x30, 0x0a, 0x13, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, + 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x57, + 0x6f, 0x72, 0x6c, 0x64, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x44, 0x12, 0x35, 0x0a, 0x09, + 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x09, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x73, 0x22, 0x32, 0x0a, 0x1e, 0x42, 0x75, 0x6c, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x98, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x65, + 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, + 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, + 0x6d, 0x61, 0x70, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x61, 0x70, + 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, + 0x74, 0x79, 0x22, 0x29, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, + 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0xbf, 0x01, + 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x42, 0x69, 0x6e, + 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, + 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, + 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x61, + 0x70, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, + 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, + 0x6c, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x22, + 0x34, 0x0a, 0x20, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x42, 0x69, + 0x6e, 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x32, 0xd5, 0x10, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x06, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, + 0x12, 0x10, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x1a, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x08, 0x55, 0x6e, 0x69, 0x6e, 0x76, 0x69, + 0x74, 0x65, 0x12, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x69, 0x6e, + 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, + 0x4c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x4c, 0x65, 0x61, 0x76, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x16, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x6f, + 0x52, 0x61, 0x69, 0x64, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x74, 0x54, 0x6f, 0x52, 0x61, 0x69, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x19, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x6f, 0x52, 0x61, 0x69, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x41, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x76, 0x31, 0x2e, + 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x49, 0x6e, + 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x0d, + 0x44, 0x65, 0x63, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x12, 0x17, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x6c, + 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x35, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x13, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x42, 0x79, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1b, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x79, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x53, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x42, 0x79, 0x50, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x49, 0x44, 0x42, 0x79, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x49, 0x44, 0x42, 0x79, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x12, + 0x53, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x63, + 0x6f, 0x6e, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x44, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x74, 0x4d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x12, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x74, 0x4d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x44, 0x75, + 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, + 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x44, + 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, + 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x50, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x52, 0x61, 0x69, 0x64, 0x44, 0x69, 0x66, + 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, + 0x52, 0x61, 0x69, 0x64, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x61, + 0x69, 0x64, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, + 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, + 0x0f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x61, 0x64, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x61, 0x64, 0x79, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x61, 0x64, 0x79, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x18, 0x53, 0x65, 0x74, + 0x52, 0x65, 0x61, 0x64, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, + 0x61, 0x64, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x4d, 0x0a, 0x10, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x52, 0x65, 0x61, 0x64, 0x79, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, + 0x52, 0x65, 0x61, 0x64, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x52, 0x65, 0x61, + 0x64, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x59, 0x0a, 0x14, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, + 0x75, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x75, 0x62, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x75, 0x62, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x53, 0x65, + 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x19, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x18, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x4c, 0x66, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, + 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x65, 0x64, 0x4c, 0x66, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x4c, 0x66, 0x67, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x71, 0x0a, 0x1c, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x64, 0x4c, 0x66, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x27, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x69, 0x7a, 0x65, 0x64, 0x4c, 0x66, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4c, 0x66, 0x67, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, + 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x5f, 0x0a, 0x16, 0x42, 0x75, 0x6c, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x42, + 0x75, 0x6c, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, + 0x31, 0x2e, 0x42, 0x75, 0x6c, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x44, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x12, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, + 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x45, 0x78, 0x74, 0x65, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0e, 0x5a, + 0x0c, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_group_proto_rawDescOnce sync.Once + file_group_proto_rawDescData = file_group_proto_rawDesc +) + +func file_group_proto_rawDescGZIP() []byte { + file_group_proto_rawDescOnce.Do(func() { + file_group_proto_rawDescData = protoimpl.X.CompressGZIP(file_group_proto_rawDescData) + }) + return file_group_proto_rawDescData +} + +var file_group_proto_enumTypes = make([]protoimpl.EnumInfo, 6) +var file_group_proto_msgTypes = make([]protoimpl.MessageInfo, 62) +var file_group_proto_goTypes = []interface{}{ + (InviteResponse_Status)(0), // 0: v1.InviteResponse.Status + (AcceptInviteResponse_Status)(0), // 1: v1.AcceptInviteResponse.Status + (DeclineInviteResponse_Status)(0), // 2: v1.DeclineInviteResponse.Status + (UninviteResponse_Status)(0), // 3: v1.UninviteResponse.Status + (SetDungeonDifficultyResponse_Status)(0), // 4: v1.SetDungeonDifficultyResponse.Status + (SetRaidDifficultyResponse_Status)(0), // 5: v1.SetRaidDifficultyResponse.Status + (*InviteParams)(nil), // 6: v1.InviteParams + (*InviteResponse)(nil), // 7: v1.InviteResponse + (*AcceptInviteParams)(nil), // 8: v1.AcceptInviteParams + (*AcceptInviteResponse)(nil), // 9: v1.AcceptInviteResponse + (*DeclineInviteParams)(nil), // 10: v1.DeclineInviteParams + (*DeclineInviteResponse)(nil), // 11: v1.DeclineInviteResponse + (*GetGroupRequest)(nil), // 12: v1.GetGroupRequest + (*GetGroupResponse)(nil), // 13: v1.GetGroupResponse + (*GetGroupByMemberRequest)(nil), // 14: v1.GetGroupByMemberRequest + (*GetGroupIDByPlayerRequest)(nil), // 15: v1.GetGroupIDByPlayerRequest + (*GetGroupIDByPlayerResponse)(nil), // 16: v1.GetGroupIDByPlayerResponse + (*GetMemberPlacementsRequest)(nil), // 17: v1.GetMemberPlacementsRequest + (*MemberPlacement)(nil), // 18: v1.MemberPlacement + (*GetMemberPlacementsResponse)(nil), // 19: v1.GetMemberPlacementsResponse + (*UninviteParams)(nil), // 20: v1.UninviteParams + (*UninviteResponse)(nil), // 21: v1.UninviteResponse + (*GroupLeaveParams)(nil), // 22: v1.GroupLeaveParams + (*GroupLeaveResponse)(nil), // 23: v1.GroupLeaveResponse + (*ConvertToRaidParams)(nil), // 24: v1.ConvertToRaidParams + (*ConvertToRaidResponse)(nil), // 25: v1.ConvertToRaidResponse + (*ChangeLeaderParams)(nil), // 26: v1.ChangeLeaderParams + (*ChangeLeaderResponse)(nil), // 27: v1.ChangeLeaderResponse + (*SendGroupMessageParams)(nil), // 28: v1.SendGroupMessageParams + (*SendGroupMessageResponse)(nil), // 29: v1.SendGroupMessageResponse + (*SetGroupTargetIconRequest)(nil), // 30: v1.SetGroupTargetIconRequest + (*SetGroupTargetIconResponse)(nil), // 31: v1.SetGroupTargetIconResponse + (*GetGroupTargetIconsRequest)(nil), // 32: v1.GetGroupTargetIconsRequest + (*GetGroupTargetIconsResponse)(nil), // 33: v1.GetGroupTargetIconsResponse + (*SetLootMethodRequest)(nil), // 34: v1.SetLootMethodRequest + (*SetLootMethodResponse)(nil), // 35: v1.SetLootMethodResponse + (*SetDungeonDifficultyRequest)(nil), // 36: v1.SetDungeonDifficultyRequest + (*SetDungeonDifficultyResponse)(nil), // 37: v1.SetDungeonDifficultyResponse + (*SetRaidDifficultyRequest)(nil), // 38: v1.SetRaidDifficultyRequest + (*SetRaidDifficultyResponse)(nil), // 39: v1.SetRaidDifficultyResponse + (*StartReadyCheckRequest)(nil), // 40: v1.StartReadyCheckRequest + (*StartReadyCheckResponse)(nil), // 41: v1.StartReadyCheckResponse + (*SetReadyCheckMemberStateRequest)(nil), // 42: v1.SetReadyCheckMemberStateRequest + (*SetReadyCheckMemberStateResponse)(nil), // 43: v1.SetReadyCheckMemberStateResponse + (*FinishReadyCheckRequest)(nil), // 44: v1.FinishReadyCheckRequest + (*FinishReadyCheckResponse)(nil), // 45: v1.FinishReadyCheckResponse + (*ChangeMemberSubGroupRequest)(nil), // 46: v1.ChangeMemberSubGroupRequest + (*ChangeMemberSubGroupResponse)(nil), // 47: v1.ChangeMemberSubGroupResponse + (*SetMemberFlagsRequest)(nil), // 48: v1.SetMemberFlagsRequest + (*SetMemberFlagsResponse)(nil), // 49: v1.SetMemberFlagsResponse + (*AcceptedLfgGroupMember)(nil), // 50: v1.AcceptedLfgGroupMember + (*RegisterAcceptedLfgGroupRequest)(nil), // 51: v1.RegisterAcceptedLfgGroupRequest + (*RegisterAcceptedLfgGroupResponse)(nil), // 52: v1.RegisterAcceptedLfgGroupResponse + (*MaterializedLfgGroupMember)(nil), // 53: v1.MaterializedLfgGroupMember + (*RegisterMaterializedLfgGroupRequest)(nil), // 54: v1.RegisterMaterializedLfgGroupRequest + (*RegisterMaterializedLfgGroupResponse)(nil), // 55: v1.RegisterMaterializedLfgGroupResponse + (*UpdateMemberStateRequest)(nil), // 56: v1.UpdateMemberStateRequest + (*UpdateMemberStateResponse)(nil), // 57: v1.UpdateMemberStateResponse + (*PlayerStateSnapshot)(nil), // 58: v1.PlayerStateSnapshot + (*PlayerAuraSnapshot)(nil), // 59: v1.PlayerAuraSnapshot + (*BulkUpdateMemberStatesRequest)(nil), // 60: v1.BulkUpdateMemberStatesRequest + (*BulkUpdateMemberStatesResponse)(nil), // 61: v1.BulkUpdateMemberStatesResponse + (*ResetInstanceRequest)(nil), // 62: v1.ResetInstanceRequest + (*ResetInstanceResponse)(nil), // 63: v1.ResetInstanceResponse + (*SetInstanceBindExtensionRequest)(nil), // 64: v1.SetInstanceBindExtensionRequest + (*SetInstanceBindExtensionResponse)(nil), // 65: v1.SetInstanceBindExtensionResponse + (*GetGroupResponse_GroupMember)(nil), // 66: v1.GetGroupResponse.GroupMember + (*GetGroupResponse_Group)(nil), // 67: v1.GetGroupResponse.Group +} +var file_group_proto_depIdxs = []int32{ + 0, // 0: v1.InviteResponse.status:type_name -> v1.InviteResponse.Status + 1, // 1: v1.AcceptInviteResponse.status:type_name -> v1.AcceptInviteResponse.Status + 2, // 2: v1.DeclineInviteResponse.status:type_name -> v1.DeclineInviteResponse.Status + 67, // 3: v1.GetGroupResponse.group:type_name -> v1.GetGroupResponse.Group + 18, // 4: v1.GetMemberPlacementsResponse.placements:type_name -> v1.MemberPlacement + 3, // 5: v1.UninviteResponse.status:type_name -> v1.UninviteResponse.Status + 4, // 6: v1.SetDungeonDifficultyResponse.status:type_name -> v1.SetDungeonDifficultyResponse.Status + 5, // 7: v1.SetRaidDifficultyResponse.status:type_name -> v1.SetRaidDifficultyResponse.Status + 50, // 8: v1.RegisterAcceptedLfgGroupRequest.members:type_name -> v1.AcceptedLfgGroupMember + 53, // 9: v1.RegisterMaterializedLfgGroupRequest.members:type_name -> v1.MaterializedLfgGroupMember + 59, // 10: v1.PlayerStateSnapshot.auras:type_name -> v1.PlayerAuraSnapshot + 58, // 11: v1.BulkUpdateMemberStatesRequest.snapshots:type_name -> v1.PlayerStateSnapshot + 66, // 12: v1.GetGroupResponse.Group.members:type_name -> v1.GetGroupResponse.GroupMember + 6, // 13: v1.GroupService.Invite:input_type -> v1.InviteParams + 20, // 14: v1.GroupService.Uninvite:input_type -> v1.UninviteParams + 22, // 15: v1.GroupService.Leave:input_type -> v1.GroupLeaveParams + 24, // 16: v1.GroupService.ConvertToRaid:input_type -> v1.ConvertToRaidParams + 26, // 17: v1.GroupService.ChangeLeader:input_type -> v1.ChangeLeaderParams + 8, // 18: v1.GroupService.AcceptInvite:input_type -> v1.AcceptInviteParams + 10, // 19: v1.GroupService.DeclineInvite:input_type -> v1.DeclineInviteParams + 12, // 20: v1.GroupService.GetGroup:input_type -> v1.GetGroupRequest + 14, // 21: v1.GroupService.GetGroupByMember:input_type -> v1.GetGroupByMemberRequest + 15, // 22: v1.GroupService.GetGroupIDByPlayer:input_type -> v1.GetGroupIDByPlayerRequest + 17, // 23: v1.GroupService.GetMemberPlacements:input_type -> v1.GetMemberPlacementsRequest + 30, // 24: v1.GroupService.SetGroupTargetIcon:input_type -> v1.SetGroupTargetIconRequest + 34, // 25: v1.GroupService.SetLootMethod:input_type -> v1.SetLootMethodRequest + 36, // 26: v1.GroupService.SetDungeonDifficulty:input_type -> v1.SetDungeonDifficultyRequest + 38, // 27: v1.GroupService.SetRaidDifficulty:input_type -> v1.SetRaidDifficultyRequest + 28, // 28: v1.GroupService.SendMessage:input_type -> v1.SendGroupMessageParams + 40, // 29: v1.GroupService.StartReadyCheck:input_type -> v1.StartReadyCheckRequest + 42, // 30: v1.GroupService.SetReadyCheckMemberState:input_type -> v1.SetReadyCheckMemberStateRequest + 44, // 31: v1.GroupService.FinishReadyCheck:input_type -> v1.FinishReadyCheckRequest + 46, // 32: v1.GroupService.ChangeMemberSubGroup:input_type -> v1.ChangeMemberSubGroupRequest + 48, // 33: v1.GroupService.SetMemberFlags:input_type -> v1.SetMemberFlagsRequest + 51, // 34: v1.GroupService.RegisterAcceptedLfgGroup:input_type -> v1.RegisterAcceptedLfgGroupRequest + 54, // 35: v1.GroupService.RegisterMaterializedLfgGroup:input_type -> v1.RegisterMaterializedLfgGroupRequest + 56, // 36: v1.GroupService.UpdateMemberState:input_type -> v1.UpdateMemberStateRequest + 60, // 37: v1.GroupService.BulkUpdateMemberStates:input_type -> v1.BulkUpdateMemberStatesRequest + 62, // 38: v1.GroupService.ResetInstance:input_type -> v1.ResetInstanceRequest + 64, // 39: v1.GroupService.SetInstanceBindExtension:input_type -> v1.SetInstanceBindExtensionRequest + 7, // 40: v1.GroupService.Invite:output_type -> v1.InviteResponse + 21, // 41: v1.GroupService.Uninvite:output_type -> v1.UninviteResponse + 23, // 42: v1.GroupService.Leave:output_type -> v1.GroupLeaveResponse + 25, // 43: v1.GroupService.ConvertToRaid:output_type -> v1.ConvertToRaidResponse + 27, // 44: v1.GroupService.ChangeLeader:output_type -> v1.ChangeLeaderResponse + 9, // 45: v1.GroupService.AcceptInvite:output_type -> v1.AcceptInviteResponse + 11, // 46: v1.GroupService.DeclineInvite:output_type -> v1.DeclineInviteResponse + 13, // 47: v1.GroupService.GetGroup:output_type -> v1.GetGroupResponse + 13, // 48: v1.GroupService.GetGroupByMember:output_type -> v1.GetGroupResponse + 16, // 49: v1.GroupService.GetGroupIDByPlayer:output_type -> v1.GetGroupIDByPlayerResponse + 19, // 50: v1.GroupService.GetMemberPlacements:output_type -> v1.GetMemberPlacementsResponse + 31, // 51: v1.GroupService.SetGroupTargetIcon:output_type -> v1.SetGroupTargetIconResponse + 35, // 52: v1.GroupService.SetLootMethod:output_type -> v1.SetLootMethodResponse + 37, // 53: v1.GroupService.SetDungeonDifficulty:output_type -> v1.SetDungeonDifficultyResponse + 39, // 54: v1.GroupService.SetRaidDifficulty:output_type -> v1.SetRaidDifficultyResponse + 29, // 55: v1.GroupService.SendMessage:output_type -> v1.SendGroupMessageResponse + 41, // 56: v1.GroupService.StartReadyCheck:output_type -> v1.StartReadyCheckResponse + 43, // 57: v1.GroupService.SetReadyCheckMemberState:output_type -> v1.SetReadyCheckMemberStateResponse + 45, // 58: v1.GroupService.FinishReadyCheck:output_type -> v1.FinishReadyCheckResponse + 47, // 59: v1.GroupService.ChangeMemberSubGroup:output_type -> v1.ChangeMemberSubGroupResponse + 49, // 60: v1.GroupService.SetMemberFlags:output_type -> v1.SetMemberFlagsResponse + 52, // 61: v1.GroupService.RegisterAcceptedLfgGroup:output_type -> v1.RegisterAcceptedLfgGroupResponse + 55, // 62: v1.GroupService.RegisterMaterializedLfgGroup:output_type -> v1.RegisterMaterializedLfgGroupResponse + 57, // 63: v1.GroupService.UpdateMemberState:output_type -> v1.UpdateMemberStateResponse + 61, // 64: v1.GroupService.BulkUpdateMemberStates:output_type -> v1.BulkUpdateMemberStatesResponse + 63, // 65: v1.GroupService.ResetInstance:output_type -> v1.ResetInstanceResponse + 65, // 66: v1.GroupService.SetInstanceBindExtension:output_type -> v1.SetInstanceBindExtensionResponse + 40, // [40:67] is the sub-list for method output_type + 13, // [13:40] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name +} + +func init() { file_group_proto_init() } +func file_group_proto_init() { + if File_group_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_group_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InviteParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InviteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AcceptInviteParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AcceptInviteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeclineInviteParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeclineInviteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGroupRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGroupResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGroupByMemberRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGroupIDByPlayerRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGroupIDByPlayerResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMemberPlacementsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MemberPlacement); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMemberPlacementsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UninviteParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UninviteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupLeaveParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupLeaveResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConvertToRaidParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConvertToRaidResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChangeLeaderParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChangeLeaderResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendGroupMessageParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendGroupMessageResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetGroupTargetIconRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetGroupTargetIconResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGroupTargetIconsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGroupTargetIconsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetLootMethodRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetLootMethodResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetDungeonDifficultyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetDungeonDifficultyResponse); i { case 0: return &v.state case 1: @@ -2673,8 +5840,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InviteResponse); i { + file_group_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetRaidDifficultyRequest); i { case 0: return &v.state case 1: @@ -2685,8 +5852,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AcceptInviteParams); i { + file_group_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetRaidDifficultyResponse); i { case 0: return &v.state case 1: @@ -2697,8 +5864,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AcceptInviteResponse); i { + file_group_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartReadyCheckRequest); i { case 0: return &v.state case 1: @@ -2709,8 +5876,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetGroupRequest); i { + file_group_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartReadyCheckResponse); i { case 0: return &v.state case 1: @@ -2721,8 +5888,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetGroupResponse); i { + file_group_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetReadyCheckMemberStateRequest); i { case 0: return &v.state case 1: @@ -2733,8 +5900,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetGroupByMemberRequest); i { + file_group_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetReadyCheckMemberStateResponse); i { case 0: return &v.state case 1: @@ -2745,8 +5912,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetGroupIDByPlayerRequest); i { + file_group_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FinishReadyCheckRequest); i { case 0: return &v.state case 1: @@ -2757,8 +5924,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetGroupIDByPlayerResponse); i { + file_group_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FinishReadyCheckResponse); i { case 0: return &v.state case 1: @@ -2769,8 +5936,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UninviteParams); i { + file_group_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChangeMemberSubGroupRequest); i { case 0: return &v.state case 1: @@ -2781,8 +5948,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UninviteResponse); i { + file_group_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChangeMemberSubGroupResponse); i { case 0: return &v.state case 1: @@ -2793,8 +5960,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GroupLeaveParams); i { + file_group_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetMemberFlagsRequest); i { case 0: return &v.state case 1: @@ -2805,8 +5972,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GroupLeaveResponse); i { + file_group_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetMemberFlagsResponse); i { case 0: return &v.state case 1: @@ -2817,8 +5984,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConvertToRaidParams); i { + file_group_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AcceptedLfgGroupMember); i { case 0: return &v.state case 1: @@ -2829,8 +5996,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConvertToRaidResponse); i { + file_group_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterAcceptedLfgGroupRequest); i { case 0: return &v.state case 1: @@ -2841,8 +6008,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChangeLeaderParams); i { + file_group_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterAcceptedLfgGroupResponse); i { case 0: return &v.state case 1: @@ -2853,8 +6020,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChangeLeaderResponse); i { + file_group_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MaterializedLfgGroupMember); i { case 0: return &v.state case 1: @@ -2865,8 +6032,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendGroupMessageParams); i { + file_group_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterMaterializedLfgGroupRequest); i { case 0: return &v.state case 1: @@ -2877,8 +6044,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendGroupMessageResponse); i { + file_group_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterMaterializedLfgGroupResponse); i { case 0: return &v.state case 1: @@ -2889,8 +6056,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetGroupTargetIconRequest); i { + file_group_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateMemberStateRequest); i { case 0: return &v.state case 1: @@ -2901,8 +6068,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetGroupTargetIconResponse); i { + file_group_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateMemberStateResponse); i { case 0: return &v.state case 1: @@ -2913,8 +6080,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetGroupTargetIconsRequest); i { + file_group_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PlayerStateSnapshot); i { case 0: return &v.state case 1: @@ -2925,8 +6092,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetGroupTargetIconsResponse); i { + file_group_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PlayerAuraSnapshot); i { case 0: return &v.state case 1: @@ -2937,8 +6104,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetLootMethodRequest); i { + file_group_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BulkUpdateMemberStatesRequest); i { case 0: return &v.state case 1: @@ -2949,8 +6116,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetLootMethodResponse); i { + file_group_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BulkUpdateMemberStatesResponse); i { case 0: return &v.state case 1: @@ -2961,8 +6128,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetDungeonDifficultyRequest); i { + file_group_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResetInstanceRequest); i { case 0: return &v.state case 1: @@ -2973,8 +6140,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetDungeonDifficultyResponse); i { + file_group_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResetInstanceResponse); i { case 0: return &v.state case 1: @@ -2985,8 +6152,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetRaidDifficultyRequest); i { + file_group_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetInstanceBindExtensionRequest); i { case 0: return &v.state case 1: @@ -2997,8 +6164,8 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetRaidDifficultyResponse); i { + file_group_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetInstanceBindExtensionResponse); i { case 0: return &v.state case 1: @@ -3009,7 +6176,7 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_group_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetGroupResponse_GroupMember); i { case 0: return &v.state @@ -3021,7 +6188,7 @@ func file_group_proto_init() { return nil } } - file_group_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_group_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetGroupResponse_Group); i { case 0: return &v.state @@ -3034,13 +6201,15 @@ func file_group_proto_init() { } } } + file_group_proto_msgTypes[50].OneofWrappers = []interface{}{} + file_group_proto_msgTypes[52].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_group_proto_rawDesc, - NumEnums: 5, - NumMessages: 31, + NumEnums: 6, + NumMessages: 62, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/group/pb/group_grpc.pb.go b/gen/group/pb/group_grpc.pb.go index 5142fc3..58a1796 100644 --- a/gen/group/pb/group_grpc.pb.go +++ b/gen/group/pb/group_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v3.20.3 +// - protoc v3.21.12 // source: group.proto package pb @@ -19,20 +19,33 @@ import ( const _ = grpc.SupportPackageIsVersion7 const ( - GroupService_Invite_FullMethodName = "/v1.GroupService/Invite" - GroupService_Uninvite_FullMethodName = "/v1.GroupService/Uninvite" - GroupService_Leave_FullMethodName = "/v1.GroupService/Leave" - GroupService_ConvertToRaid_FullMethodName = "/v1.GroupService/ConvertToRaid" - GroupService_ChangeLeader_FullMethodName = "/v1.GroupService/ChangeLeader" - GroupService_AcceptInvite_FullMethodName = "/v1.GroupService/AcceptInvite" - GroupService_GetGroup_FullMethodName = "/v1.GroupService/GetGroup" - GroupService_GetGroupByMember_FullMethodName = "/v1.GroupService/GetGroupByMember" - GroupService_GetGroupIDByPlayer_FullMethodName = "/v1.GroupService/GetGroupIDByPlayer" - GroupService_SetGroupTargetIcon_FullMethodName = "/v1.GroupService/SetGroupTargetIcon" - GroupService_SetLootMethod_FullMethodName = "/v1.GroupService/SetLootMethod" - GroupService_SetDungeonDifficulty_FullMethodName = "/v1.GroupService/SetDungeonDifficulty" - GroupService_SetRaidDifficulty_FullMethodName = "/v1.GroupService/SetRaidDifficulty" - GroupService_SendMessage_FullMethodName = "/v1.GroupService/SendMessage" + GroupService_Invite_FullMethodName = "/v1.GroupService/Invite" + GroupService_Uninvite_FullMethodName = "/v1.GroupService/Uninvite" + GroupService_Leave_FullMethodName = "/v1.GroupService/Leave" + GroupService_ConvertToRaid_FullMethodName = "/v1.GroupService/ConvertToRaid" + GroupService_ChangeLeader_FullMethodName = "/v1.GroupService/ChangeLeader" + GroupService_AcceptInvite_FullMethodName = "/v1.GroupService/AcceptInvite" + GroupService_DeclineInvite_FullMethodName = "/v1.GroupService/DeclineInvite" + GroupService_GetGroup_FullMethodName = "/v1.GroupService/GetGroup" + GroupService_GetGroupByMember_FullMethodName = "/v1.GroupService/GetGroupByMember" + GroupService_GetGroupIDByPlayer_FullMethodName = "/v1.GroupService/GetGroupIDByPlayer" + GroupService_GetMemberPlacements_FullMethodName = "/v1.GroupService/GetMemberPlacements" + GroupService_SetGroupTargetIcon_FullMethodName = "/v1.GroupService/SetGroupTargetIcon" + GroupService_SetLootMethod_FullMethodName = "/v1.GroupService/SetLootMethod" + GroupService_SetDungeonDifficulty_FullMethodName = "/v1.GroupService/SetDungeonDifficulty" + GroupService_SetRaidDifficulty_FullMethodName = "/v1.GroupService/SetRaidDifficulty" + GroupService_SendMessage_FullMethodName = "/v1.GroupService/SendMessage" + GroupService_StartReadyCheck_FullMethodName = "/v1.GroupService/StartReadyCheck" + GroupService_SetReadyCheckMemberState_FullMethodName = "/v1.GroupService/SetReadyCheckMemberState" + GroupService_FinishReadyCheck_FullMethodName = "/v1.GroupService/FinishReadyCheck" + GroupService_ChangeMemberSubGroup_FullMethodName = "/v1.GroupService/ChangeMemberSubGroup" + GroupService_SetMemberFlags_FullMethodName = "/v1.GroupService/SetMemberFlags" + GroupService_RegisterAcceptedLfgGroup_FullMethodName = "/v1.GroupService/RegisterAcceptedLfgGroup" + GroupService_RegisterMaterializedLfgGroup_FullMethodName = "/v1.GroupService/RegisterMaterializedLfgGroup" + GroupService_UpdateMemberState_FullMethodName = "/v1.GroupService/UpdateMemberState" + GroupService_BulkUpdateMemberStates_FullMethodName = "/v1.GroupService/BulkUpdateMemberStates" + GroupService_ResetInstance_FullMethodName = "/v1.GroupService/ResetInstance" + GroupService_SetInstanceBindExtension_FullMethodName = "/v1.GroupService/SetInstanceBindExtension" ) // GroupServiceClient is the client API for GroupService service. @@ -45,14 +58,27 @@ type GroupServiceClient interface { ConvertToRaid(ctx context.Context, in *ConvertToRaidParams, opts ...grpc.CallOption) (*ConvertToRaidResponse, error) ChangeLeader(ctx context.Context, in *ChangeLeaderParams, opts ...grpc.CallOption) (*ChangeLeaderResponse, error) AcceptInvite(ctx context.Context, in *AcceptInviteParams, opts ...grpc.CallOption) (*AcceptInviteResponse, error) + DeclineInvite(ctx context.Context, in *DeclineInviteParams, opts ...grpc.CallOption) (*DeclineInviteResponse, error) GetGroup(ctx context.Context, in *GetGroupRequest, opts ...grpc.CallOption) (*GetGroupResponse, error) GetGroupByMember(ctx context.Context, in *GetGroupByMemberRequest, opts ...grpc.CallOption) (*GetGroupResponse, error) GetGroupIDByPlayer(ctx context.Context, in *GetGroupIDByPlayerRequest, opts ...grpc.CallOption) (*GetGroupIDByPlayerResponse, error) + GetMemberPlacements(ctx context.Context, in *GetMemberPlacementsRequest, opts ...grpc.CallOption) (*GetMemberPlacementsResponse, error) SetGroupTargetIcon(ctx context.Context, in *SetGroupTargetIconRequest, opts ...grpc.CallOption) (*SetGroupTargetIconResponse, error) SetLootMethod(ctx context.Context, in *SetLootMethodRequest, opts ...grpc.CallOption) (*SetLootMethodResponse, error) SetDungeonDifficulty(ctx context.Context, in *SetDungeonDifficultyRequest, opts ...grpc.CallOption) (*SetDungeonDifficultyResponse, error) SetRaidDifficulty(ctx context.Context, in *SetRaidDifficultyRequest, opts ...grpc.CallOption) (*SetRaidDifficultyResponse, error) SendMessage(ctx context.Context, in *SendGroupMessageParams, opts ...grpc.CallOption) (*SendGroupMessageResponse, error) + StartReadyCheck(ctx context.Context, in *StartReadyCheckRequest, opts ...grpc.CallOption) (*StartReadyCheckResponse, error) + SetReadyCheckMemberState(ctx context.Context, in *SetReadyCheckMemberStateRequest, opts ...grpc.CallOption) (*SetReadyCheckMemberStateResponse, error) + FinishReadyCheck(ctx context.Context, in *FinishReadyCheckRequest, opts ...grpc.CallOption) (*FinishReadyCheckResponse, error) + ChangeMemberSubGroup(ctx context.Context, in *ChangeMemberSubGroupRequest, opts ...grpc.CallOption) (*ChangeMemberSubGroupResponse, error) + SetMemberFlags(ctx context.Context, in *SetMemberFlagsRequest, opts ...grpc.CallOption) (*SetMemberFlagsResponse, error) + RegisterAcceptedLfgGroup(ctx context.Context, in *RegisterAcceptedLfgGroupRequest, opts ...grpc.CallOption) (*RegisterAcceptedLfgGroupResponse, error) + RegisterMaterializedLfgGroup(ctx context.Context, in *RegisterMaterializedLfgGroupRequest, opts ...grpc.CallOption) (*RegisterMaterializedLfgGroupResponse, error) + UpdateMemberState(ctx context.Context, in *UpdateMemberStateRequest, opts ...grpc.CallOption) (*UpdateMemberStateResponse, error) + BulkUpdateMemberStates(ctx context.Context, in *BulkUpdateMemberStatesRequest, opts ...grpc.CallOption) (*BulkUpdateMemberStatesResponse, error) + ResetInstance(ctx context.Context, in *ResetInstanceRequest, opts ...grpc.CallOption) (*ResetInstanceResponse, error) + SetInstanceBindExtension(ctx context.Context, in *SetInstanceBindExtensionRequest, opts ...grpc.CallOption) (*SetInstanceBindExtensionResponse, error) } type groupServiceClient struct { @@ -117,6 +143,15 @@ func (c *groupServiceClient) AcceptInvite(ctx context.Context, in *AcceptInviteP return out, nil } +func (c *groupServiceClient) DeclineInvite(ctx context.Context, in *DeclineInviteParams, opts ...grpc.CallOption) (*DeclineInviteResponse, error) { + out := new(DeclineInviteResponse) + err := c.cc.Invoke(ctx, GroupService_DeclineInvite_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *groupServiceClient) GetGroup(ctx context.Context, in *GetGroupRequest, opts ...grpc.CallOption) (*GetGroupResponse, error) { out := new(GetGroupResponse) err := c.cc.Invoke(ctx, GroupService_GetGroup_FullMethodName, in, out, opts...) @@ -144,6 +179,15 @@ func (c *groupServiceClient) GetGroupIDByPlayer(ctx context.Context, in *GetGrou return out, nil } +func (c *groupServiceClient) GetMemberPlacements(ctx context.Context, in *GetMemberPlacementsRequest, opts ...grpc.CallOption) (*GetMemberPlacementsResponse, error) { + out := new(GetMemberPlacementsResponse) + err := c.cc.Invoke(ctx, GroupService_GetMemberPlacements_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *groupServiceClient) SetGroupTargetIcon(ctx context.Context, in *SetGroupTargetIconRequest, opts ...grpc.CallOption) (*SetGroupTargetIconResponse, error) { out := new(SetGroupTargetIconResponse) err := c.cc.Invoke(ctx, GroupService_SetGroupTargetIcon_FullMethodName, in, out, opts...) @@ -189,6 +233,105 @@ func (c *groupServiceClient) SendMessage(ctx context.Context, in *SendGroupMessa return out, nil } +func (c *groupServiceClient) StartReadyCheck(ctx context.Context, in *StartReadyCheckRequest, opts ...grpc.CallOption) (*StartReadyCheckResponse, error) { + out := new(StartReadyCheckResponse) + err := c.cc.Invoke(ctx, GroupService_StartReadyCheck_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) SetReadyCheckMemberState(ctx context.Context, in *SetReadyCheckMemberStateRequest, opts ...grpc.CallOption) (*SetReadyCheckMemberStateResponse, error) { + out := new(SetReadyCheckMemberStateResponse) + err := c.cc.Invoke(ctx, GroupService_SetReadyCheckMemberState_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) FinishReadyCheck(ctx context.Context, in *FinishReadyCheckRequest, opts ...grpc.CallOption) (*FinishReadyCheckResponse, error) { + out := new(FinishReadyCheckResponse) + err := c.cc.Invoke(ctx, GroupService_FinishReadyCheck_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) ChangeMemberSubGroup(ctx context.Context, in *ChangeMemberSubGroupRequest, opts ...grpc.CallOption) (*ChangeMemberSubGroupResponse, error) { + out := new(ChangeMemberSubGroupResponse) + err := c.cc.Invoke(ctx, GroupService_ChangeMemberSubGroup_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) SetMemberFlags(ctx context.Context, in *SetMemberFlagsRequest, opts ...grpc.CallOption) (*SetMemberFlagsResponse, error) { + out := new(SetMemberFlagsResponse) + err := c.cc.Invoke(ctx, GroupService_SetMemberFlags_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) RegisterAcceptedLfgGroup(ctx context.Context, in *RegisterAcceptedLfgGroupRequest, opts ...grpc.CallOption) (*RegisterAcceptedLfgGroupResponse, error) { + out := new(RegisterAcceptedLfgGroupResponse) + err := c.cc.Invoke(ctx, GroupService_RegisterAcceptedLfgGroup_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) RegisterMaterializedLfgGroup(ctx context.Context, in *RegisterMaterializedLfgGroupRequest, opts ...grpc.CallOption) (*RegisterMaterializedLfgGroupResponse, error) { + out := new(RegisterMaterializedLfgGroupResponse) + err := c.cc.Invoke(ctx, GroupService_RegisterMaterializedLfgGroup_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) UpdateMemberState(ctx context.Context, in *UpdateMemberStateRequest, opts ...grpc.CallOption) (*UpdateMemberStateResponse, error) { + out := new(UpdateMemberStateResponse) + err := c.cc.Invoke(ctx, GroupService_UpdateMemberState_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) BulkUpdateMemberStates(ctx context.Context, in *BulkUpdateMemberStatesRequest, opts ...grpc.CallOption) (*BulkUpdateMemberStatesResponse, error) { + out := new(BulkUpdateMemberStatesResponse) + err := c.cc.Invoke(ctx, GroupService_BulkUpdateMemberStates_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) ResetInstance(ctx context.Context, in *ResetInstanceRequest, opts ...grpc.CallOption) (*ResetInstanceResponse, error) { + out := new(ResetInstanceResponse) + err := c.cc.Invoke(ctx, GroupService_ResetInstance_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) SetInstanceBindExtension(ctx context.Context, in *SetInstanceBindExtensionRequest, opts ...grpc.CallOption) (*SetInstanceBindExtensionResponse, error) { + out := new(SetInstanceBindExtensionResponse) + err := c.cc.Invoke(ctx, GroupService_SetInstanceBindExtension_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // GroupServiceServer is the server API for GroupService service. // All implementations must embed UnimplementedGroupServiceServer // for forward compatibility @@ -199,14 +342,27 @@ type GroupServiceServer interface { ConvertToRaid(context.Context, *ConvertToRaidParams) (*ConvertToRaidResponse, error) ChangeLeader(context.Context, *ChangeLeaderParams) (*ChangeLeaderResponse, error) AcceptInvite(context.Context, *AcceptInviteParams) (*AcceptInviteResponse, error) + DeclineInvite(context.Context, *DeclineInviteParams) (*DeclineInviteResponse, error) GetGroup(context.Context, *GetGroupRequest) (*GetGroupResponse, error) GetGroupByMember(context.Context, *GetGroupByMemberRequest) (*GetGroupResponse, error) GetGroupIDByPlayer(context.Context, *GetGroupIDByPlayerRequest) (*GetGroupIDByPlayerResponse, error) + GetMemberPlacements(context.Context, *GetMemberPlacementsRequest) (*GetMemberPlacementsResponse, error) SetGroupTargetIcon(context.Context, *SetGroupTargetIconRequest) (*SetGroupTargetIconResponse, error) SetLootMethod(context.Context, *SetLootMethodRequest) (*SetLootMethodResponse, error) SetDungeonDifficulty(context.Context, *SetDungeonDifficultyRequest) (*SetDungeonDifficultyResponse, error) SetRaidDifficulty(context.Context, *SetRaidDifficultyRequest) (*SetRaidDifficultyResponse, error) SendMessage(context.Context, *SendGroupMessageParams) (*SendGroupMessageResponse, error) + StartReadyCheck(context.Context, *StartReadyCheckRequest) (*StartReadyCheckResponse, error) + SetReadyCheckMemberState(context.Context, *SetReadyCheckMemberStateRequest) (*SetReadyCheckMemberStateResponse, error) + FinishReadyCheck(context.Context, *FinishReadyCheckRequest) (*FinishReadyCheckResponse, error) + ChangeMemberSubGroup(context.Context, *ChangeMemberSubGroupRequest) (*ChangeMemberSubGroupResponse, error) + SetMemberFlags(context.Context, *SetMemberFlagsRequest) (*SetMemberFlagsResponse, error) + RegisterAcceptedLfgGroup(context.Context, *RegisterAcceptedLfgGroupRequest) (*RegisterAcceptedLfgGroupResponse, error) + RegisterMaterializedLfgGroup(context.Context, *RegisterMaterializedLfgGroupRequest) (*RegisterMaterializedLfgGroupResponse, error) + UpdateMemberState(context.Context, *UpdateMemberStateRequest) (*UpdateMemberStateResponse, error) + BulkUpdateMemberStates(context.Context, *BulkUpdateMemberStatesRequest) (*BulkUpdateMemberStatesResponse, error) + ResetInstance(context.Context, *ResetInstanceRequest) (*ResetInstanceResponse, error) + SetInstanceBindExtension(context.Context, *SetInstanceBindExtensionRequest) (*SetInstanceBindExtensionResponse, error) mustEmbedUnimplementedGroupServiceServer() } @@ -232,6 +388,9 @@ func (UnimplementedGroupServiceServer) ChangeLeader(context.Context, *ChangeLead func (UnimplementedGroupServiceServer) AcceptInvite(context.Context, *AcceptInviteParams) (*AcceptInviteResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AcceptInvite not implemented") } +func (UnimplementedGroupServiceServer) DeclineInvite(context.Context, *DeclineInviteParams) (*DeclineInviteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeclineInvite not implemented") +} func (UnimplementedGroupServiceServer) GetGroup(context.Context, *GetGroupRequest) (*GetGroupResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetGroup not implemented") } @@ -241,6 +400,9 @@ func (UnimplementedGroupServiceServer) GetGroupByMember(context.Context, *GetGro func (UnimplementedGroupServiceServer) GetGroupIDByPlayer(context.Context, *GetGroupIDByPlayerRequest) (*GetGroupIDByPlayerResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetGroupIDByPlayer not implemented") } +func (UnimplementedGroupServiceServer) GetMemberPlacements(context.Context, *GetMemberPlacementsRequest) (*GetMemberPlacementsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetMemberPlacements not implemented") +} func (UnimplementedGroupServiceServer) SetGroupTargetIcon(context.Context, *SetGroupTargetIconRequest) (*SetGroupTargetIconResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SetGroupTargetIcon not implemented") } @@ -256,6 +418,39 @@ func (UnimplementedGroupServiceServer) SetRaidDifficulty(context.Context, *SetRa func (UnimplementedGroupServiceServer) SendMessage(context.Context, *SendGroupMessageParams) (*SendGroupMessageResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SendMessage not implemented") } +func (UnimplementedGroupServiceServer) StartReadyCheck(context.Context, *StartReadyCheckRequest) (*StartReadyCheckResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method StartReadyCheck not implemented") +} +func (UnimplementedGroupServiceServer) SetReadyCheckMemberState(context.Context, *SetReadyCheckMemberStateRequest) (*SetReadyCheckMemberStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetReadyCheckMemberState not implemented") +} +func (UnimplementedGroupServiceServer) FinishReadyCheck(context.Context, *FinishReadyCheckRequest) (*FinishReadyCheckResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FinishReadyCheck not implemented") +} +func (UnimplementedGroupServiceServer) ChangeMemberSubGroup(context.Context, *ChangeMemberSubGroupRequest) (*ChangeMemberSubGroupResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChangeMemberSubGroup not implemented") +} +func (UnimplementedGroupServiceServer) SetMemberFlags(context.Context, *SetMemberFlagsRequest) (*SetMemberFlagsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetMemberFlags not implemented") +} +func (UnimplementedGroupServiceServer) RegisterAcceptedLfgGroup(context.Context, *RegisterAcceptedLfgGroupRequest) (*RegisterAcceptedLfgGroupResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RegisterAcceptedLfgGroup not implemented") +} +func (UnimplementedGroupServiceServer) RegisterMaterializedLfgGroup(context.Context, *RegisterMaterializedLfgGroupRequest) (*RegisterMaterializedLfgGroupResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RegisterMaterializedLfgGroup not implemented") +} +func (UnimplementedGroupServiceServer) UpdateMemberState(context.Context, *UpdateMemberStateRequest) (*UpdateMemberStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateMemberState not implemented") +} +func (UnimplementedGroupServiceServer) BulkUpdateMemberStates(context.Context, *BulkUpdateMemberStatesRequest) (*BulkUpdateMemberStatesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BulkUpdateMemberStates not implemented") +} +func (UnimplementedGroupServiceServer) ResetInstance(context.Context, *ResetInstanceRequest) (*ResetInstanceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ResetInstance not implemented") +} +func (UnimplementedGroupServiceServer) SetInstanceBindExtension(context.Context, *SetInstanceBindExtensionRequest) (*SetInstanceBindExtensionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetInstanceBindExtension not implemented") +} func (UnimplementedGroupServiceServer) mustEmbedUnimplementedGroupServiceServer() {} // UnsafeGroupServiceServer may be embedded to opt out of forward compatibility for this service. @@ -377,6 +572,24 @@ func _GroupService_AcceptInvite_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } +func _GroupService_DeclineInvite_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeclineInviteParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).DeclineInvite(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_DeclineInvite_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).DeclineInvite(ctx, req.(*DeclineInviteParams)) + } + return interceptor(ctx, in, info, handler) +} + func _GroupService_GetGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetGroupRequest) if err := dec(in); err != nil { @@ -431,6 +644,24 @@ func _GroupService_GetGroupIDByPlayer_Handler(srv interface{}, ctx context.Conte return interceptor(ctx, in, info, handler) } +func _GroupService_GetMemberPlacements_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetMemberPlacementsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).GetMemberPlacements(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_GetMemberPlacements_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).GetMemberPlacements(ctx, req.(*GetMemberPlacementsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _GroupService_SetGroupTargetIcon_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(SetGroupTargetIconRequest) if err := dec(in); err != nil { @@ -521,6 +752,204 @@ func _GroupService_SendMessage_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _GroupService_StartReadyCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StartReadyCheckRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).StartReadyCheck(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_StartReadyCheck_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).StartReadyCheck(ctx, req.(*StartReadyCheckRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_SetReadyCheckMemberState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetReadyCheckMemberStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).SetReadyCheckMemberState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_SetReadyCheckMemberState_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).SetReadyCheckMemberState(ctx, req.(*SetReadyCheckMemberStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_FinishReadyCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FinishReadyCheckRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).FinishReadyCheck(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_FinishReadyCheck_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).FinishReadyCheck(ctx, req.(*FinishReadyCheckRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_ChangeMemberSubGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ChangeMemberSubGroupRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).ChangeMemberSubGroup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_ChangeMemberSubGroup_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).ChangeMemberSubGroup(ctx, req.(*ChangeMemberSubGroupRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_SetMemberFlags_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetMemberFlagsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).SetMemberFlags(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_SetMemberFlags_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).SetMemberFlags(ctx, req.(*SetMemberFlagsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_RegisterAcceptedLfgGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegisterAcceptedLfgGroupRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).RegisterAcceptedLfgGroup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_RegisterAcceptedLfgGroup_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).RegisterAcceptedLfgGroup(ctx, req.(*RegisterAcceptedLfgGroupRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_RegisterMaterializedLfgGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegisterMaterializedLfgGroupRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).RegisterMaterializedLfgGroup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_RegisterMaterializedLfgGroup_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).RegisterMaterializedLfgGroup(ctx, req.(*RegisterMaterializedLfgGroupRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_UpdateMemberState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateMemberStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).UpdateMemberState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_UpdateMemberState_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).UpdateMemberState(ctx, req.(*UpdateMemberStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_BulkUpdateMemberStates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BulkUpdateMemberStatesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).BulkUpdateMemberStates(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_BulkUpdateMemberStates_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).BulkUpdateMemberStates(ctx, req.(*BulkUpdateMemberStatesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_ResetInstance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ResetInstanceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).ResetInstance(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_ResetInstance_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).ResetInstance(ctx, req.(*ResetInstanceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_SetInstanceBindExtension_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetInstanceBindExtensionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).SetInstanceBindExtension(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_SetInstanceBindExtension_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).SetInstanceBindExtension(ctx, req.(*SetInstanceBindExtensionRequest)) + } + return interceptor(ctx, in, info, handler) +} + // GroupService_ServiceDesc is the grpc.ServiceDesc for GroupService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -552,6 +981,10 @@ var GroupService_ServiceDesc = grpc.ServiceDesc{ MethodName: "AcceptInvite", Handler: _GroupService_AcceptInvite_Handler, }, + { + MethodName: "DeclineInvite", + Handler: _GroupService_DeclineInvite_Handler, + }, { MethodName: "GetGroup", Handler: _GroupService_GetGroup_Handler, @@ -564,6 +997,10 @@ var GroupService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetGroupIDByPlayer", Handler: _GroupService_GetGroupIDByPlayer_Handler, }, + { + MethodName: "GetMemberPlacements", + Handler: _GroupService_GetMemberPlacements_Handler, + }, { MethodName: "SetGroupTargetIcon", Handler: _GroupService_SetGroupTargetIcon_Handler, @@ -584,6 +1021,50 @@ var GroupService_ServiceDesc = grpc.ServiceDesc{ MethodName: "SendMessage", Handler: _GroupService_SendMessage_Handler, }, + { + MethodName: "StartReadyCheck", + Handler: _GroupService_StartReadyCheck_Handler, + }, + { + MethodName: "SetReadyCheckMemberState", + Handler: _GroupService_SetReadyCheckMemberState_Handler, + }, + { + MethodName: "FinishReadyCheck", + Handler: _GroupService_FinishReadyCheck_Handler, + }, + { + MethodName: "ChangeMemberSubGroup", + Handler: _GroupService_ChangeMemberSubGroup_Handler, + }, + { + MethodName: "SetMemberFlags", + Handler: _GroupService_SetMemberFlags_Handler, + }, + { + MethodName: "RegisterAcceptedLfgGroup", + Handler: _GroupService_RegisterAcceptedLfgGroup_Handler, + }, + { + MethodName: "RegisterMaterializedLfgGroup", + Handler: _GroupService_RegisterMaterializedLfgGroup_Handler, + }, + { + MethodName: "UpdateMemberState", + Handler: _GroupService_UpdateMemberState_Handler, + }, + { + MethodName: "BulkUpdateMemberStates", + Handler: _GroupService_BulkUpdateMemberStates_Handler, + }, + { + MethodName: "ResetInstance", + Handler: _GroupService_ResetInstance_Handler, + }, + { + MethodName: "SetInstanceBindExtension", + Handler: _GroupService_SetInstanceBindExtension_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "group.proto", diff --git a/gen/guid/pb/guid.pb.go b/gen/guid/pb/guid.pb.go index d3d48c1..18512b5 100644 --- a/gen/guid/pb/guid.pb.go +++ b/gen/guid/pb/guid.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v3.20.3 +// protoc v3.21.12 // source: guid.proto package pb diff --git a/gen/guid/pb/guid_grpc.pb.go b/gen/guid/pb/guid_grpc.pb.go index e27c655..7b71658 100644 --- a/gen/guid/pb/guid_grpc.pb.go +++ b/gen/guid/pb/guid_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v3.20.3 +// - protoc v3.21.12 // source: guid.proto package pb diff --git a/gen/guilds/pb/guilds.pb.go b/gen/guilds/pb/guilds.pb.go index 6f96a24..9c456e6 100644 --- a/gen/guilds/pb/guilds.pb.go +++ b/gen/guilds/pb/guilds.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v3.20.3 +// protoc v3.21.12 // source: guilds.proto package pb @@ -66,6 +66,207 @@ func (InviteMemberResponse_Status) EnumDescriptor() ([]byte, []int) { return file_guilds_proto_rawDescGZIP(), []int{5, 0} } +type OfferGuildPetitionResponse_Status int32 + +const ( + OfferGuildPetitionResponse_Ok OfferGuildPetitionResponse_Status = 0 + OfferGuildPetitionResponse_NotFound OfferGuildPetitionResponse_Status = 1 + OfferGuildPetitionResponse_NotOwner OfferGuildPetitionResponse_Status = 2 + OfferGuildPetitionResponse_TargetNotFound OfferGuildPetitionResponse_Status = 3 + OfferGuildPetitionResponse_TargetAlreadyInGuild OfferGuildPetitionResponse_Status = 4 + OfferGuildPetitionResponse_TargetAlreadyInvited OfferGuildPetitionResponse_Status = 5 + OfferGuildPetitionResponse_Failed OfferGuildPetitionResponse_Status = 6 +) + +// Enum value maps for OfferGuildPetitionResponse_Status. +var ( + OfferGuildPetitionResponse_Status_name = map[int32]string{ + 0: "Ok", + 1: "NotFound", + 2: "NotOwner", + 3: "TargetNotFound", + 4: "TargetAlreadyInGuild", + 5: "TargetAlreadyInvited", + 6: "Failed", + } + OfferGuildPetitionResponse_Status_value = map[string]int32{ + "Ok": 0, + "NotFound": 1, + "NotOwner": 2, + "TargetNotFound": 3, + "TargetAlreadyInGuild": 4, + "TargetAlreadyInvited": 5, + "Failed": 6, + } +) + +func (x OfferGuildPetitionResponse_Status) Enum() *OfferGuildPetitionResponse_Status { + p := new(OfferGuildPetitionResponse_Status) + *p = x + return p +} + +func (x OfferGuildPetitionResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (OfferGuildPetitionResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_guilds_proto_enumTypes[1].Descriptor() +} + +func (OfferGuildPetitionResponse_Status) Type() protoreflect.EnumType { + return &file_guilds_proto_enumTypes[1] +} + +func (x OfferGuildPetitionResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use OfferGuildPetitionResponse_Status.Descriptor instead. +func (OfferGuildPetitionResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{33, 0} +} + +type SignGuildPetitionResponse_Status int32 + +const ( + SignGuildPetitionResponse_Ok SignGuildPetitionResponse_Status = 0 + SignGuildPetitionResponse_AlreadySigned SignGuildPetitionResponse_Status = 1 + SignGuildPetitionResponse_AlreadyInGuild SignGuildPetitionResponse_Status = 2 + SignGuildPetitionResponse_CantSignOwn SignGuildPetitionResponse_Status = 3 + SignGuildPetitionResponse_NotServer SignGuildPetitionResponse_Status = 4 + SignGuildPetitionResponse_NotFound SignGuildPetitionResponse_Status = 5 + SignGuildPetitionResponse_Full SignGuildPetitionResponse_Status = 6 + SignGuildPetitionResponse_Failed SignGuildPetitionResponse_Status = 7 + SignGuildPetitionResponse_AlreadyInvited SignGuildPetitionResponse_Status = 8 +) + +// Enum value maps for SignGuildPetitionResponse_Status. +var ( + SignGuildPetitionResponse_Status_name = map[int32]string{ + 0: "Ok", + 1: "AlreadySigned", + 2: "AlreadyInGuild", + 3: "CantSignOwn", + 4: "NotServer", + 5: "NotFound", + 6: "Full", + 7: "Failed", + 8: "AlreadyInvited", + } + SignGuildPetitionResponse_Status_value = map[string]int32{ + "Ok": 0, + "AlreadySigned": 1, + "AlreadyInGuild": 2, + "CantSignOwn": 3, + "NotServer": 4, + "NotFound": 5, + "Full": 6, + "Failed": 7, + "AlreadyInvited": 8, + } +) + +func (x SignGuildPetitionResponse_Status) Enum() *SignGuildPetitionResponse_Status { + p := new(SignGuildPetitionResponse_Status) + *p = x + return p +} + +func (x SignGuildPetitionResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SignGuildPetitionResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_guilds_proto_enumTypes[2].Descriptor() +} + +func (SignGuildPetitionResponse_Status) Type() protoreflect.EnumType { + return &file_guilds_proto_enumTypes[2] +} + +func (x SignGuildPetitionResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SignGuildPetitionResponse_Status.Descriptor instead. +func (SignGuildPetitionResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{35, 0} +} + +type GuildBankStatus_Status int32 + +const ( + GuildBankStatus_Ok GuildBankStatus_Status = 0 + GuildBankStatus_Failed GuildBankStatus_Status = 1 + GuildBankStatus_GuildNotFound GuildBankStatus_Status = 2 + GuildBankStatus_NotInGuild GuildBankStatus_Status = 3 + GuildBankStatus_NotEnoughRights GuildBankStatus_Status = 4 + GuildBankStatus_InvalidTab GuildBankStatus_Status = 5 + GuildBankStatus_InvalidSlot GuildBankStatus_Status = 6 + GuildBankStatus_NotEnoughMoney GuildBankStatus_Status = 7 + GuildBankStatus_BankFull GuildBankStatus_Status = 8 + GuildBankStatus_WithdrawLimit GuildBankStatus_Status = 9 + GuildBankStatus_ItemNotFound GuildBankStatus_Status = 10 +) + +// Enum value maps for GuildBankStatus_Status. +var ( + GuildBankStatus_Status_name = map[int32]string{ + 0: "Ok", + 1: "Failed", + 2: "GuildNotFound", + 3: "NotInGuild", + 4: "NotEnoughRights", + 5: "InvalidTab", + 6: "InvalidSlot", + 7: "NotEnoughMoney", + 8: "BankFull", + 9: "WithdrawLimit", + 10: "ItemNotFound", + } + GuildBankStatus_Status_value = map[string]int32{ + "Ok": 0, + "Failed": 1, + "GuildNotFound": 2, + "NotInGuild": 3, + "NotEnoughRights": 4, + "InvalidTab": 5, + "InvalidSlot": 6, + "NotEnoughMoney": 7, + "BankFull": 8, + "WithdrawLimit": 9, + "ItemNotFound": 10, + } +) + +func (x GuildBankStatus_Status) Enum() *GuildBankStatus_Status { + p := new(GuildBankStatus_Status) + *p = x + return p +} + +func (x GuildBankStatus_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (GuildBankStatus_Status) Descriptor() protoreflect.EnumDescriptor { + return file_guilds_proto_enumTypes[3].Descriptor() +} + +func (GuildBankStatus_Status) Type() protoreflect.EnumType { + return &file_guilds_proto_enumTypes[3] +} + +func (x GuildBankStatus_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use GuildBankStatus_Status.Descriptor instead. +func (GuildBankStatus_Status) EnumDescriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{39, 0} +} + type GetInfoParams struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -363,11 +564,13 @@ type InviteMemberParams struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - Inviter uint64 `protobuf:"varint,3,opt,name=inviter,proto3" json:"inviter,omitempty"` - Invitee uint64 `protobuf:"varint,4,opt,name=invitee,proto3" json:"invitee,omitempty"` - InviteeName string `protobuf:"bytes,5,opt,name=inviteeName,proto3" json:"inviteeName,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + Inviter uint64 `protobuf:"varint,3,opt,name=inviter,proto3" json:"inviter,omitempty"` + Invitee uint64 `protobuf:"varint,4,opt,name=invitee,proto3" json:"invitee,omitempty"` + InviteeName string `protobuf:"bytes,5,opt,name=inviteeName,proto3" json:"inviteeName,omitempty"` + InviteeRace uint32 `protobuf:"varint,6,opt,name=inviteeRace,proto3" json:"inviteeRace,omitempty"` + AllowCrossFaction bool `protobuf:"varint,7,opt,name=allowCrossFaction,proto3" json:"allowCrossFaction,omitempty"` } func (x *InviteMemberParams) Reset() { @@ -437,6 +640,20 @@ func (x *InviteMemberParams) GetInviteeName() string { return "" } +func (x *InviteMemberParams) GetInviteeRace() uint32 { + if x != nil { + return x.InviteeRace + } + return 0 +} + +func (x *InviteMemberParams) GetAllowCrossFaction() bool { + if x != nil { + return x.AllowCrossFaction + } + return false +} + type InviteMemberResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -489,9 +706,10 @@ type InviteAcceptedParams struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - Character *InviteAcceptedParams_Character `protobuf:"bytes,3,opt,name=character,proto3" json:"character,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + Character *InviteAcceptedParams_Character `protobuf:"bytes,3,opt,name=character,proto3" json:"character,omitempty"` + AllowCrossFaction bool `protobuf:"varint,4,opt,name=allowCrossFaction,proto3" json:"allowCrossFaction,omitempty"` } func (x *InviteAcceptedParams) Reset() { @@ -547,6 +765,13 @@ func (x *InviteAcceptedParams) GetCharacter() *InviteAcceptedParams_Character { return nil } +func (x *InviteAcceptedParams) GetAllowCrossFaction() bool { + if x != nil { + return x.AllowCrossFaction + } + return false +} + type InviteAcceptedResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1197,13 +1422,14 @@ type RankUpdateParams struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - ChangerGUID uint64 `protobuf:"varint,3,opt,name=changerGUID,proto3" json:"changerGUID,omitempty"` - Rank uint32 `protobuf:"varint,4,opt,name=rank,proto3" json:"rank,omitempty"` - RankName string `protobuf:"bytes,5,opt,name=rankName,proto3" json:"rankName,omitempty"` - Rights uint32 `protobuf:"varint,6,opt,name=rights,proto3" json:"rights,omitempty"` - MoneyPerDay uint32 `protobuf:"varint,7,opt,name=moneyPerDay,proto3" json:"moneyPerDay,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + ChangerGUID uint64 `protobuf:"varint,3,opt,name=changerGUID,proto3" json:"changerGUID,omitempty"` + Rank uint32 `protobuf:"varint,4,opt,name=rank,proto3" json:"rank,omitempty"` + RankName string `protobuf:"bytes,5,opt,name=rankName,proto3" json:"rankName,omitempty"` + Rights uint32 `protobuf:"varint,6,opt,name=rights,proto3" json:"rights,omitempty"` + MoneyPerDay uint32 `protobuf:"varint,7,opt,name=moneyPerDay,proto3" json:"moneyPerDay,omitempty"` + BankTabRights []*RankUpdateParams_BankTabRight `protobuf:"bytes,8,rep,name=bankTabRights,proto3" json:"bankTabRights,omitempty"` } func (x *RankUpdateParams) Reset() { @@ -1287,6 +1513,13 @@ func (x *RankUpdateParams) GetMoneyPerDay() uint32 { return 0 } +func (x *RankUpdateParams) GetBankTabRights() []*RankUpdateParams_BankTabRight { + if x != nil { + return x.BankTabRights + } + return nil +} + type RankUpdateResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1691,6 +1924,7 @@ type SendGuildMessageParams struct { IsOfficerMessage bool `protobuf:"varint,4,opt,name=isOfficerMessage,proto3" json:"isOfficerMessage,omitempty"` Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` Language uint32 `protobuf:"varint,6,opt,name=language,proto3" json:"language,omitempty"` + SenderChatTag uint32 `protobuf:"varint,7,opt,name=senderChatTag,proto3" json:"senderChatTag,omitempty"` } func (x *SendGuildMessageParams) Reset() { @@ -1767,6 +2001,13 @@ func (x *SendGuildMessageParams) GetLanguage() uint32 { return 0 } +func (x *SendGuildMessageParams) GetSenderChatTag() uint32 { + if x != nil { + return x.SenderChatTag + } + return 0 +} + type SendGuildMessageResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1814,26 +2055,17 @@ func (x *SendGuildMessageResponse) GetApi() string { return "" } -type GetRosterInfoResponse_Member struct { +type GuildPetitionSignature struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Status uint32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"` - RankID uint32 `protobuf:"varint,4,opt,name=rankID,proto3" json:"rankID,omitempty"` - Lvl uint32 `protobuf:"varint,5,opt,name=lvl,proto3" json:"lvl,omitempty"` - ClassID uint32 `protobuf:"varint,6,opt,name=classID,proto3" json:"classID,omitempty"` - Gender uint32 `protobuf:"varint,7,opt,name=gender,proto3" json:"gender,omitempty"` - AreaID uint32 `protobuf:"varint,8,opt,name=areaID,proto3" json:"areaID,omitempty"` - LogoutTime int64 `protobuf:"varint,9,opt,name=logoutTime,proto3" json:"logoutTime,omitempty"` - Note string `protobuf:"bytes,10,opt,name=note,proto3" json:"note,omitempty"` - OfficerNote string `protobuf:"bytes,11,opt,name=officerNote,proto3" json:"officerNote,omitempty"` + PlayerGUID uint64 `protobuf:"varint,1,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + PlayerAccount uint32 `protobuf:"varint,2,opt,name=playerAccount,proto3" json:"playerAccount,omitempty"` } -func (x *GetRosterInfoResponse_Member) Reset() { - *x = GetRosterInfoResponse_Member{} +func (x *GuildPetitionSignature) Reset() { + *x = GuildPetitionSignature{} if protoimpl.UnsafeEnabled { mi := &file_guilds_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1841,13 +2073,13 @@ func (x *GetRosterInfoResponse_Member) Reset() { } } -func (x *GetRosterInfoResponse_Member) String() string { +func (x *GuildPetitionSignature) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetRosterInfoResponse_Member) ProtoMessage() {} +func (*GuildPetitionSignature) ProtoMessage() {} -func (x *GetRosterInfoResponse_Member) ProtoReflect() protoreflect.Message { +func (x *GuildPetitionSignature) ProtoReflect() protoreflect.Message { mi := &file_guilds_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1859,115 +2091,139 @@ func (x *GetRosterInfoResponse_Member) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetRosterInfoResponse_Member.ProtoReflect.Descriptor instead. -func (*GetRosterInfoResponse_Member) Descriptor() ([]byte, []int) { - return file_guilds_proto_rawDescGZIP(), []int{3, 0} +// Deprecated: Use GuildPetitionSignature.ProtoReflect.Descriptor instead. +func (*GuildPetitionSignature) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{28} } -func (x *GetRosterInfoResponse_Member) GetGuid() uint64 { +func (x *GuildPetitionSignature) GetPlayerGUID() uint64 { if x != nil { - return x.Guid + return x.PlayerGUID } return 0 } -func (x *GetRosterInfoResponse_Member) GetName() string { +func (x *GuildPetitionSignature) GetPlayerAccount() uint32 { if x != nil { - return x.Name + return x.PlayerAccount } - return "" + return 0 } -func (x *GetRosterInfoResponse_Member) GetStatus() uint32 { - if x != nil { - return x.Status - } - return 0 +type GuildPetition struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PetitionGUID uint64 `protobuf:"varint,1,opt,name=petitionGUID,proto3" json:"petitionGUID,omitempty"` + PetitionID uint32 `protobuf:"varint,2,opt,name=petitionID,proto3" json:"petitionID,omitempty"` + OwnerGUID uint64 `protobuf:"varint,3,opt,name=ownerGUID,proto3" json:"ownerGUID,omitempty"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` + Type uint32 `protobuf:"varint,5,opt,name=type,proto3" json:"type,omitempty"` + Signatures []*GuildPetitionSignature `protobuf:"bytes,6,rep,name=signatures,proto3" json:"signatures,omitempty"` } -func (x *GetRosterInfoResponse_Member) GetRankID() uint32 { - if x != nil { - return x.RankID +func (x *GuildPetition) Reset() { + *x = GuildPetition{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return 0 } -func (x *GetRosterInfoResponse_Member) GetLvl() uint32 { - if x != nil { - return x.Lvl +func (x *GuildPetition) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GuildPetition) ProtoMessage() {} + +func (x *GuildPetition) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return 0 + return mi.MessageOf(x) } -func (x *GetRosterInfoResponse_Member) GetClassID() uint32 { +// Deprecated: Use GuildPetition.ProtoReflect.Descriptor instead. +func (*GuildPetition) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{29} +} + +func (x *GuildPetition) GetPetitionGUID() uint64 { if x != nil { - return x.ClassID + return x.PetitionGUID } return 0 } -func (x *GetRosterInfoResponse_Member) GetGender() uint32 { +func (x *GuildPetition) GetPetitionID() uint32 { if x != nil { - return x.Gender + return x.PetitionID } return 0 } -func (x *GetRosterInfoResponse_Member) GetAreaID() uint32 { +func (x *GuildPetition) GetOwnerGUID() uint64 { if x != nil { - return x.AreaID + return x.OwnerGUID } return 0 } -func (x *GetRosterInfoResponse_Member) GetLogoutTime() int64 { +func (x *GuildPetition) GetName() string { if x != nil { - return x.LogoutTime + return x.Name } - return 0 + return "" } -func (x *GetRosterInfoResponse_Member) GetNote() string { +func (x *GuildPetition) GetType() uint32 { if x != nil { - return x.Note + return x.Type } - return "" + return 0 } -func (x *GetRosterInfoResponse_Member) GetOfficerNote() string { +func (x *GuildPetition) GetSignatures() []*GuildPetitionSignature { if x != nil { - return x.OfficerNote + return x.Signatures } - return "" + return nil } -type GetRosterInfoResponse_Rank struct { +type GetGuildPetitionParams struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Flags uint32 `protobuf:"varint,2,opt,name=flags,proto3" json:"flags,omitempty"` - GoldLimit uint32 `protobuf:"varint,3,opt,name=goldLimit,proto3" json:"goldLimit,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PetitionGUID uint64 `protobuf:"varint,3,opt,name=petitionGUID,proto3" json:"petitionGUID,omitempty"` } -func (x *GetRosterInfoResponse_Rank) Reset() { - *x = GetRosterInfoResponse_Rank{} +func (x *GetGuildPetitionParams) Reset() { + *x = GetGuildPetitionParams{} if protoimpl.UnsafeEnabled { - mi := &file_guilds_proto_msgTypes[29] + mi := &file_guilds_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetRosterInfoResponse_Rank) String() string { +func (x *GetGuildPetitionParams) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetRosterInfoResponse_Rank) ProtoMessage() {} +func (*GetGuildPetitionParams) ProtoMessage() {} -func (x *GetRosterInfoResponse_Rank) ProtoReflect() protoreflect.Message { - mi := &file_guilds_proto_msgTypes[29] +func (x *GetGuildPetitionParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1978,61 +2234,58 @@ func (x *GetRosterInfoResponse_Rank) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetRosterInfoResponse_Rank.ProtoReflect.Descriptor instead. -func (*GetRosterInfoResponse_Rank) Descriptor() ([]byte, []int) { - return file_guilds_proto_rawDescGZIP(), []int{3, 1} +// Deprecated: Use GetGuildPetitionParams.ProtoReflect.Descriptor instead. +func (*GetGuildPetitionParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{30} } -func (x *GetRosterInfoResponse_Rank) GetId() uint32 { +func (x *GetGuildPetitionParams) GetApi() string { if x != nil { - return x.Id + return x.Api } - return 0 + return "" } -func (x *GetRosterInfoResponse_Rank) GetFlags() uint32 { +func (x *GetGuildPetitionParams) GetRealmID() uint32 { if x != nil { - return x.Flags + return x.RealmID } return 0 } -func (x *GetRosterInfoResponse_Rank) GetGoldLimit() uint32 { +func (x *GetGuildPetitionParams) GetPetitionGUID() uint64 { if x != nil { - return x.GoldLimit + return x.PetitionGUID } return 0 } -type GetRosterInfoResponse_Guild struct { +type GetGuildPetitionResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - WelcomeText string `protobuf:"bytes,2,opt,name=welcomeText,proto3" json:"welcomeText,omitempty"` - InfoText string `protobuf:"bytes,3,opt,name=infoText,proto3" json:"infoText,omitempty"` - Members []*GetRosterInfoResponse_Member `protobuf:"bytes,4,rep,name=members,proto3" json:"members,omitempty"` - Ranks []*GetRosterInfoResponse_Rank `protobuf:"bytes,5,rep,name=ranks,proto3" json:"ranks,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Petition *GuildPetition `protobuf:"bytes,2,opt,name=petition,proto3" json:"petition,omitempty"` } -func (x *GetRosterInfoResponse_Guild) Reset() { - *x = GetRosterInfoResponse_Guild{} +func (x *GetGuildPetitionResponse) Reset() { + *x = GetGuildPetitionResponse{} if protoimpl.UnsafeEnabled { - mi := &file_guilds_proto_msgTypes[30] + mi := &file_guilds_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetRosterInfoResponse_Guild) String() string { +func (x *GetGuildPetitionResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetRosterInfoResponse_Guild) ProtoMessage() {} +func (*GetGuildPetitionResponse) ProtoMessage() {} -func (x *GetRosterInfoResponse_Guild) ProtoReflect() protoreflect.Message { - mi := &file_guilds_proto_msgTypes[30] +func (x *GetGuildPetitionResponse) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2043,78 +2296,55 @@ func (x *GetRosterInfoResponse_Guild) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetRosterInfoResponse_Guild.ProtoReflect.Descriptor instead. -func (*GetRosterInfoResponse_Guild) Descriptor() ([]byte, []int) { - return file_guilds_proto_rawDescGZIP(), []int{3, 2} -} - -func (x *GetRosterInfoResponse_Guild) GetId() uint64 { - if x != nil { - return x.Id - } - return 0 -} - -func (x *GetRosterInfoResponse_Guild) GetWelcomeText() string { - if x != nil { - return x.WelcomeText - } - return "" +// Deprecated: Use GetGuildPetitionResponse.ProtoReflect.Descriptor instead. +func (*GetGuildPetitionResponse) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{31} } -func (x *GetRosterInfoResponse_Guild) GetInfoText() string { +func (x *GetGuildPetitionResponse) GetApi() string { if x != nil { - return x.InfoText + return x.Api } return "" } -func (x *GetRosterInfoResponse_Guild) GetMembers() []*GetRosterInfoResponse_Member { - if x != nil { - return x.Members - } - return nil -} - -func (x *GetRosterInfoResponse_Guild) GetRanks() []*GetRosterInfoResponse_Rank { +func (x *GetGuildPetitionResponse) GetPetition() *GuildPetition { if x != nil { - return x.Ranks + return x.Petition } return nil } -type InviteAcceptedParams_Character struct { +type OfferGuildPetitionParams struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Lvl uint32 `protobuf:"varint,3,opt,name=lvl,proto3" json:"lvl,omitempty"` - Race uint32 `protobuf:"varint,4,opt,name=race,proto3" json:"race,omitempty"` - ClassID uint32 `protobuf:"varint,5,opt,name=classID,proto3" json:"classID,omitempty"` - Gender uint32 `protobuf:"varint,6,opt,name=gender,proto3" json:"gender,omitempty"` - AreaID uint32 `protobuf:"varint,7,opt,name=areaID,proto3" json:"areaID,omitempty"` - AccountID uint64 `protobuf:"varint,8,opt,name=accountID,proto3" json:"accountID,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + OwnerGUID uint64 `protobuf:"varint,3,opt,name=ownerGUID,proto3" json:"ownerGUID,omitempty"` + TargetGUID uint64 `protobuf:"varint,4,opt,name=targetGUID,proto3" json:"targetGUID,omitempty"` + TargetName string `protobuf:"bytes,5,opt,name=targetName,proto3" json:"targetName,omitempty"` + PetitionGUID uint64 `protobuf:"varint,6,opt,name=petitionGUID,proto3" json:"petitionGUID,omitempty"` } -func (x *InviteAcceptedParams_Character) Reset() { - *x = InviteAcceptedParams_Character{} +func (x *OfferGuildPetitionParams) Reset() { + *x = OfferGuildPetitionParams{} if protoimpl.UnsafeEnabled { - mi := &file_guilds_proto_msgTypes[31] + mi := &file_guilds_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *InviteAcceptedParams_Character) String() string { +func (x *OfferGuildPetitionParams) String() string { return protoimpl.X.MessageStringOf(x) } -func (*InviteAcceptedParams_Character) ProtoMessage() {} +func (*OfferGuildPetitionParams) ProtoMessage() {} -func (x *InviteAcceptedParams_Character) ProtoReflect() protoreflect.Message { - mi := &file_guilds_proto_msgTypes[31] +func (x *OfferGuildPetitionParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2125,12 +2355,2685 @@ func (x *InviteAcceptedParams_Character) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use InviteAcceptedParams_Character.ProtoReflect.Descriptor instead. -func (*InviteAcceptedParams_Character) Descriptor() ([]byte, []int) { - return file_guilds_proto_rawDescGZIP(), []int{6, 0} +// Deprecated: Use OfferGuildPetitionParams.ProtoReflect.Descriptor instead. +func (*OfferGuildPetitionParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{32} } -func (x *InviteAcceptedParams_Character) GetGuid() uint64 { +func (x *OfferGuildPetitionParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *OfferGuildPetitionParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *OfferGuildPetitionParams) GetOwnerGUID() uint64 { + if x != nil { + return x.OwnerGUID + } + return 0 +} + +func (x *OfferGuildPetitionParams) GetTargetGUID() uint64 { + if x != nil { + return x.TargetGUID + } + return 0 +} + +func (x *OfferGuildPetitionParams) GetTargetName() string { + if x != nil { + return x.TargetName + } + return "" +} + +func (x *OfferGuildPetitionParams) GetPetitionGUID() uint64 { + if x != nil { + return x.PetitionGUID + } + return 0 +} + +type OfferGuildPetitionResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status OfferGuildPetitionResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.OfferGuildPetitionResponse_Status" json:"status,omitempty"` +} + +func (x *OfferGuildPetitionResponse) Reset() { + *x = OfferGuildPetitionResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OfferGuildPetitionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OfferGuildPetitionResponse) ProtoMessage() {} + +func (x *OfferGuildPetitionResponse) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OfferGuildPetitionResponse.ProtoReflect.Descriptor instead. +func (*OfferGuildPetitionResponse) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{33} +} + +func (x *OfferGuildPetitionResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *OfferGuildPetitionResponse) GetStatus() OfferGuildPetitionResponse_Status { + if x != nil { + return x.Status + } + return OfferGuildPetitionResponse_Ok +} + +type SignGuildPetitionParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + SignerGUID uint64 `protobuf:"varint,3,opt,name=signerGUID,proto3" json:"signerGUID,omitempty"` + SignerName string `protobuf:"bytes,4,opt,name=signerName,proto3" json:"signerName,omitempty"` + SignerAccountID uint32 `protobuf:"varint,5,opt,name=signerAccountID,proto3" json:"signerAccountID,omitempty"` + SignerGuildID uint32 `protobuf:"varint,6,opt,name=signerGuildID,proto3" json:"signerGuildID,omitempty"` + PetitionGUID uint64 `protobuf:"varint,7,opt,name=petitionGUID,proto3" json:"petitionGUID,omitempty"` +} + +func (x *SignGuildPetitionParams) Reset() { + *x = SignGuildPetitionParams{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SignGuildPetitionParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignGuildPetitionParams) ProtoMessage() {} + +func (x *SignGuildPetitionParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignGuildPetitionParams.ProtoReflect.Descriptor instead. +func (*SignGuildPetitionParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{34} +} + +func (x *SignGuildPetitionParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SignGuildPetitionParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *SignGuildPetitionParams) GetSignerGUID() uint64 { + if x != nil { + return x.SignerGUID + } + return 0 +} + +func (x *SignGuildPetitionParams) GetSignerName() string { + if x != nil { + return x.SignerName + } + return "" +} + +func (x *SignGuildPetitionParams) GetSignerAccountID() uint32 { + if x != nil { + return x.SignerAccountID + } + return 0 +} + +func (x *SignGuildPetitionParams) GetSignerGuildID() uint32 { + if x != nil { + return x.SignerGuildID + } + return 0 +} + +func (x *SignGuildPetitionParams) GetPetitionGUID() uint64 { + if x != nil { + return x.PetitionGUID + } + return 0 +} + +type SignGuildPetitionResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status SignGuildPetitionResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.SignGuildPetitionResponse_Status" json:"status,omitempty"` +} + +func (x *SignGuildPetitionResponse) Reset() { + *x = SignGuildPetitionResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SignGuildPetitionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignGuildPetitionResponse) ProtoMessage() {} + +func (x *SignGuildPetitionResponse) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[35] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignGuildPetitionResponse.ProtoReflect.Descriptor instead. +func (*SignGuildPetitionResponse) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{35} +} + +func (x *SignGuildPetitionResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SignGuildPetitionResponse) GetStatus() SignGuildPetitionResponse_Status { + if x != nil { + return x.Status + } + return SignGuildPetitionResponse_Ok +} + +type GuildBankSocketEnchant struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SocketIndex uint32 `protobuf:"varint,1,opt,name=socketIndex,proto3" json:"socketIndex,omitempty"` + SocketEnchantID uint32 `protobuf:"varint,2,opt,name=socketEnchantID,proto3" json:"socketEnchantID,omitempty"` +} + +func (x *GuildBankSocketEnchant) Reset() { + *x = GuildBankSocketEnchant{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GuildBankSocketEnchant) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GuildBankSocketEnchant) ProtoMessage() {} + +func (x *GuildBankSocketEnchant) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[36] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GuildBankSocketEnchant.ProtoReflect.Descriptor instead. +func (*GuildBankSocketEnchant) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{36} +} + +func (x *GuildBankSocketEnchant) GetSocketIndex() uint32 { + if x != nil { + return x.SocketIndex + } + return 0 +} + +func (x *GuildBankSocketEnchant) GetSocketEnchantID() uint32 { + if x != nil { + return x.SocketEnchantID + } + return 0 +} + +type GuildBankItem struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ItemGUID uint64 `protobuf:"varint,1,opt,name=itemGUID,proto3" json:"itemGUID,omitempty"` + Entry uint32 `protobuf:"varint,2,opt,name=entry,proto3" json:"entry,omitempty"` + Slot uint32 `protobuf:"varint,3,opt,name=slot,proto3" json:"slot,omitempty"` + Count uint32 `protobuf:"varint,4,opt,name=count,proto3" json:"count,omitempty"` + Flags uint32 `protobuf:"varint,5,opt,name=flags,proto3" json:"flags,omitempty"` + RandomPropertyID int32 `protobuf:"varint,6,opt,name=randomPropertyID,proto3" json:"randomPropertyID,omitempty"` + RandomPropertySeed int32 `protobuf:"varint,7,opt,name=randomPropertySeed,proto3" json:"randomPropertySeed,omitempty"` + Durability uint32 `protobuf:"varint,8,opt,name=durability,proto3" json:"durability,omitempty"` + EnchantmentID uint32 `protobuf:"varint,9,opt,name=enchantmentID,proto3" json:"enchantmentID,omitempty"` + SocketEnchants []*GuildBankSocketEnchant `protobuf:"bytes,10,rep,name=socketEnchants,proto3" json:"socketEnchants,omitempty"` + Charges uint32 `protobuf:"varint,11,opt,name=charges,proto3" json:"charges,omitempty"` + Text string `protobuf:"bytes,12,opt,name=text,proto3" json:"text,omitempty"` +} + +func (x *GuildBankItem) Reset() { + *x = GuildBankItem{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GuildBankItem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GuildBankItem) ProtoMessage() {} + +func (x *GuildBankItem) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GuildBankItem.ProtoReflect.Descriptor instead. +func (*GuildBankItem) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{37} +} + +func (x *GuildBankItem) GetItemGUID() uint64 { + if x != nil { + return x.ItemGUID + } + return 0 +} + +func (x *GuildBankItem) GetEntry() uint32 { + if x != nil { + return x.Entry + } + return 0 +} + +func (x *GuildBankItem) GetSlot() uint32 { + if x != nil { + return x.Slot + } + return 0 +} + +func (x *GuildBankItem) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *GuildBankItem) GetFlags() uint32 { + if x != nil { + return x.Flags + } + return 0 +} + +func (x *GuildBankItem) GetRandomPropertyID() int32 { + if x != nil { + return x.RandomPropertyID + } + return 0 +} + +func (x *GuildBankItem) GetRandomPropertySeed() int32 { + if x != nil { + return x.RandomPropertySeed + } + return 0 +} + +func (x *GuildBankItem) GetDurability() uint32 { + if x != nil { + return x.Durability + } + return 0 +} + +func (x *GuildBankItem) GetEnchantmentID() uint32 { + if x != nil { + return x.EnchantmentID + } + return 0 +} + +func (x *GuildBankItem) GetSocketEnchants() []*GuildBankSocketEnchant { + if x != nil { + return x.SocketEnchants + } + return nil +} + +func (x *GuildBankItem) GetCharges() uint32 { + if x != nil { + return x.Charges + } + return 0 +} + +func (x *GuildBankItem) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +type GuildBankTab struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TabID uint32 `protobuf:"varint,1,opt,name=tabID,proto3" json:"tabID,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Icon string `protobuf:"bytes,3,opt,name=icon,proto3" json:"icon,omitempty"` + Text string `protobuf:"bytes,4,opt,name=text,proto3" json:"text,omitempty"` + Items []*GuildBankItem `protobuf:"bytes,5,rep,name=items,proto3" json:"items,omitempty"` +} + +func (x *GuildBankTab) Reset() { + *x = GuildBankTab{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GuildBankTab) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GuildBankTab) ProtoMessage() {} + +func (x *GuildBankTab) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GuildBankTab.ProtoReflect.Descriptor instead. +func (*GuildBankTab) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{38} +} + +func (x *GuildBankTab) GetTabID() uint32 { + if x != nil { + return x.TabID + } + return 0 +} + +func (x *GuildBankTab) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *GuildBankTab) GetIcon() string { + if x != nil { + return x.Icon + } + return "" +} + +func (x *GuildBankTab) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +func (x *GuildBankTab) GetItems() []*GuildBankItem { + if x != nil { + return x.Items + } + return nil +} + +type GuildBankStatus struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GuildBankStatus) Reset() { + *x = GuildBankStatus{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GuildBankStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GuildBankStatus) ProtoMessage() {} + +func (x *GuildBankStatus) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[39] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GuildBankStatus.ProtoReflect.Descriptor instead. +func (*GuildBankStatus) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{39} +} + +type GetGuildBankParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GuildID uint64 `protobuf:"varint,3,opt,name=guildID,proto3" json:"guildID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + TabID uint32 `protobuf:"varint,5,opt,name=tabID,proto3" json:"tabID,omitempty"` + FullUpdate bool `protobuf:"varint,6,opt,name=fullUpdate,proto3" json:"fullUpdate,omitempty"` +} + +func (x *GetGuildBankParams) Reset() { + *x = GetGuildBankParams{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetGuildBankParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetGuildBankParams) ProtoMessage() {} + +func (x *GetGuildBankParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[40] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetGuildBankParams.ProtoReflect.Descriptor instead. +func (*GetGuildBankParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{40} +} + +func (x *GetGuildBankParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetGuildBankParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *GetGuildBankParams) GetGuildID() uint64 { + if x != nil { + return x.GuildID + } + return 0 +} + +func (x *GetGuildBankParams) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *GetGuildBankParams) GetTabID() uint32 { + if x != nil { + return x.TabID + } + return 0 +} + +func (x *GetGuildBankParams) GetFullUpdate() bool { + if x != nil { + return x.FullUpdate + } + return false +} + +type GetGuildBankResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status GuildBankStatus_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.GuildBankStatus_Status" json:"status,omitempty"` + Money uint64 `protobuf:"varint,3,opt,name=money,proto3" json:"money,omitempty"` + TabID uint32 `protobuf:"varint,4,opt,name=tabID,proto3" json:"tabID,omitempty"` + WithdrawalsRemaining int32 `protobuf:"varint,5,opt,name=withdrawalsRemaining,proto3" json:"withdrawalsRemaining,omitempty"` + FullUpdate bool `protobuf:"varint,6,opt,name=fullUpdate,proto3" json:"fullUpdate,omitempty"` + Tabs []*GuildBankTab `protobuf:"bytes,7,rep,name=tabs,proto3" json:"tabs,omitempty"` + Items []*GuildBankItem `protobuf:"bytes,8,rep,name=items,proto3" json:"items,omitempty"` +} + +func (x *GetGuildBankResponse) Reset() { + *x = GetGuildBankResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetGuildBankResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetGuildBankResponse) ProtoMessage() {} + +func (x *GetGuildBankResponse) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[41] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetGuildBankResponse.ProtoReflect.Descriptor instead. +func (*GetGuildBankResponse) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{41} +} + +func (x *GetGuildBankResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetGuildBankResponse) GetStatus() GuildBankStatus_Status { + if x != nil { + return x.Status + } + return GuildBankStatus_Ok +} + +func (x *GetGuildBankResponse) GetMoney() uint64 { + if x != nil { + return x.Money + } + return 0 +} + +func (x *GetGuildBankResponse) GetTabID() uint32 { + if x != nil { + return x.TabID + } + return 0 +} + +func (x *GetGuildBankResponse) GetWithdrawalsRemaining() int32 { + if x != nil { + return x.WithdrawalsRemaining + } + return 0 +} + +func (x *GetGuildBankResponse) GetFullUpdate() bool { + if x != nil { + return x.FullUpdate + } + return false +} + +func (x *GetGuildBankResponse) GetTabs() []*GuildBankTab { + if x != nil { + return x.Tabs + } + return nil +} + +func (x *GetGuildBankResponse) GetItems() []*GuildBankItem { + if x != nil { + return x.Items + } + return nil +} + +type GetGuildBankLogParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GuildID uint64 `protobuf:"varint,3,opt,name=guildID,proto3" json:"guildID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + TabID uint32 `protobuf:"varint,5,opt,name=tabID,proto3" json:"tabID,omitempty"` +} + +func (x *GetGuildBankLogParams) Reset() { + *x = GetGuildBankLogParams{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetGuildBankLogParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetGuildBankLogParams) ProtoMessage() {} + +func (x *GetGuildBankLogParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[42] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetGuildBankLogParams.ProtoReflect.Descriptor instead. +func (*GetGuildBankLogParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{42} +} + +func (x *GetGuildBankLogParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetGuildBankLogParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *GetGuildBankLogParams) GetGuildID() uint64 { + if x != nil { + return x.GuildID + } + return 0 +} + +func (x *GetGuildBankLogParams) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *GetGuildBankLogParams) GetTabID() uint32 { + if x != nil { + return x.TabID + } + return 0 +} + +type GuildBankLogEntry struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PlayerGUID uint64 `protobuf:"varint,1,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + TimeOffset uint32 `protobuf:"varint,2,opt,name=timeOffset,proto3" json:"timeOffset,omitempty"` + EntryType int32 `protobuf:"varint,3,opt,name=entryType,proto3" json:"entryType,omitempty"` + Money uint32 `protobuf:"varint,4,opt,name=money,proto3" json:"money,omitempty"` + ItemID int32 `protobuf:"varint,5,opt,name=itemID,proto3" json:"itemID,omitempty"` + Count int32 `protobuf:"varint,6,opt,name=count,proto3" json:"count,omitempty"` + OtherTab int32 `protobuf:"varint,7,opt,name=otherTab,proto3" json:"otherTab,omitempty"` +} + +func (x *GuildBankLogEntry) Reset() { + *x = GuildBankLogEntry{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GuildBankLogEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GuildBankLogEntry) ProtoMessage() {} + +func (x *GuildBankLogEntry) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[43] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GuildBankLogEntry.ProtoReflect.Descriptor instead. +func (*GuildBankLogEntry) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{43} +} + +func (x *GuildBankLogEntry) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *GuildBankLogEntry) GetTimeOffset() uint32 { + if x != nil { + return x.TimeOffset + } + return 0 +} + +func (x *GuildBankLogEntry) GetEntryType() int32 { + if x != nil { + return x.EntryType + } + return 0 +} + +func (x *GuildBankLogEntry) GetMoney() uint32 { + if x != nil { + return x.Money + } + return 0 +} + +func (x *GuildBankLogEntry) GetItemID() int32 { + if x != nil { + return x.ItemID + } + return 0 +} + +func (x *GuildBankLogEntry) GetCount() int32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *GuildBankLogEntry) GetOtherTab() int32 { + if x != nil { + return x.OtherTab + } + return 0 +} + +type GetGuildBankLogResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status GuildBankStatus_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.GuildBankStatus_Status" json:"status,omitempty"` + TabID uint32 `protobuf:"varint,3,opt,name=tabID,proto3" json:"tabID,omitempty"` + Entries []*GuildBankLogEntry `protobuf:"bytes,4,rep,name=entries,proto3" json:"entries,omitempty"` +} + +func (x *GetGuildBankLogResponse) Reset() { + *x = GetGuildBankLogResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetGuildBankLogResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetGuildBankLogResponse) ProtoMessage() {} + +func (x *GetGuildBankLogResponse) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[44] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetGuildBankLogResponse.ProtoReflect.Descriptor instead. +func (*GetGuildBankLogResponse) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{44} +} + +func (x *GetGuildBankLogResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetGuildBankLogResponse) GetStatus() GuildBankStatus_Status { + if x != nil { + return x.Status + } + return GuildBankStatus_Ok +} + +func (x *GetGuildBankLogResponse) GetTabID() uint32 { + if x != nil { + return x.TabID + } + return 0 +} + +func (x *GetGuildBankLogResponse) GetEntries() []*GuildBankLogEntry { + if x != nil { + return x.Entries + } + return nil +} + +type GetGuildBankTabTextParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GuildID uint64 `protobuf:"varint,3,opt,name=guildID,proto3" json:"guildID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + TabID uint32 `protobuf:"varint,5,opt,name=tabID,proto3" json:"tabID,omitempty"` +} + +func (x *GetGuildBankTabTextParams) Reset() { + *x = GetGuildBankTabTextParams{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetGuildBankTabTextParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetGuildBankTabTextParams) ProtoMessage() {} + +func (x *GetGuildBankTabTextParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[45] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetGuildBankTabTextParams.ProtoReflect.Descriptor instead. +func (*GetGuildBankTabTextParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{45} +} + +func (x *GetGuildBankTabTextParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetGuildBankTabTextParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *GetGuildBankTabTextParams) GetGuildID() uint64 { + if x != nil { + return x.GuildID + } + return 0 +} + +func (x *GetGuildBankTabTextParams) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *GetGuildBankTabTextParams) GetTabID() uint32 { + if x != nil { + return x.TabID + } + return 0 +} + +type GetGuildBankTabTextResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status GuildBankStatus_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.GuildBankStatus_Status" json:"status,omitempty"` + TabID uint32 `protobuf:"varint,3,opt,name=tabID,proto3" json:"tabID,omitempty"` + Text string `protobuf:"bytes,4,opt,name=text,proto3" json:"text,omitempty"` +} + +func (x *GetGuildBankTabTextResponse) Reset() { + *x = GetGuildBankTabTextResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetGuildBankTabTextResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetGuildBankTabTextResponse) ProtoMessage() {} + +func (x *GetGuildBankTabTextResponse) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[46] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetGuildBankTabTextResponse.ProtoReflect.Descriptor instead. +func (*GetGuildBankTabTextResponse) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{46} +} + +func (x *GetGuildBankTabTextResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetGuildBankTabTextResponse) GetStatus() GuildBankStatus_Status { + if x != nil { + return x.Status + } + return GuildBankStatus_Ok +} + +func (x *GetGuildBankTabTextResponse) GetTabID() uint32 { + if x != nil { + return x.TabID + } + return 0 +} + +func (x *GetGuildBankTabTextResponse) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +type UpdateGuildBankTabParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GuildID uint64 `protobuf:"varint,3,opt,name=guildID,proto3" json:"guildID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + TabID uint32 `protobuf:"varint,5,opt,name=tabID,proto3" json:"tabID,omitempty"` + Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` + Icon string `protobuf:"bytes,7,opt,name=icon,proto3" json:"icon,omitempty"` +} + +func (x *UpdateGuildBankTabParams) Reset() { + *x = UpdateGuildBankTabParams{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateGuildBankTabParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateGuildBankTabParams) ProtoMessage() {} + +func (x *UpdateGuildBankTabParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[47] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateGuildBankTabParams.ProtoReflect.Descriptor instead. +func (*UpdateGuildBankTabParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{47} +} + +func (x *UpdateGuildBankTabParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *UpdateGuildBankTabParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *UpdateGuildBankTabParams) GetGuildID() uint64 { + if x != nil { + return x.GuildID + } + return 0 +} + +func (x *UpdateGuildBankTabParams) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *UpdateGuildBankTabParams) GetTabID() uint32 { + if x != nil { + return x.TabID + } + return 0 +} + +func (x *UpdateGuildBankTabParams) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *UpdateGuildBankTabParams) GetIcon() string { + if x != nil { + return x.Icon + } + return "" +} + +type SetGuildBankTabTextParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GuildID uint64 `protobuf:"varint,3,opt,name=guildID,proto3" json:"guildID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + TabID uint32 `protobuf:"varint,5,opt,name=tabID,proto3" json:"tabID,omitempty"` + Text string `protobuf:"bytes,6,opt,name=text,proto3" json:"text,omitempty"` +} + +func (x *SetGuildBankTabTextParams) Reset() { + *x = SetGuildBankTabTextParams{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetGuildBankTabTextParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetGuildBankTabTextParams) ProtoMessage() {} + +func (x *SetGuildBankTabTextParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[48] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetGuildBankTabTextParams.ProtoReflect.Descriptor instead. +func (*SetGuildBankTabTextParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{48} +} + +func (x *SetGuildBankTabTextParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SetGuildBankTabTextParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *SetGuildBankTabTextParams) GetGuildID() uint64 { + if x != nil { + return x.GuildID + } + return 0 +} + +func (x *SetGuildBankTabTextParams) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *SetGuildBankTabTextParams) GetTabID() uint32 { + if x != nil { + return x.TabID + } + return 0 +} + +func (x *SetGuildBankTabTextParams) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +type BuyGuildBankTabParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GuildID uint64 `protobuf:"varint,3,opt,name=guildID,proto3" json:"guildID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + TabID uint32 `protobuf:"varint,5,opt,name=tabID,proto3" json:"tabID,omitempty"` +} + +func (x *BuyGuildBankTabParams) Reset() { + *x = BuyGuildBankTabParams{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[49] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BuyGuildBankTabParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BuyGuildBankTabParams) ProtoMessage() {} + +func (x *BuyGuildBankTabParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[49] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BuyGuildBankTabParams.ProtoReflect.Descriptor instead. +func (*BuyGuildBankTabParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{49} +} + +func (x *BuyGuildBankTabParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *BuyGuildBankTabParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *BuyGuildBankTabParams) GetGuildID() uint64 { + if x != nil { + return x.GuildID + } + return 0 +} + +func (x *BuyGuildBankTabParams) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *BuyGuildBankTabParams) GetTabID() uint32 { + if x != nil { + return x.TabID + } + return 0 +} + +type BuyGuildBankTabResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status GuildBankStatus_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.GuildBankStatus_Status" json:"status,omitempty"` + TabCost uint32 `protobuf:"varint,3,opt,name=tabCost,proto3" json:"tabCost,omitempty"` +} + +func (x *BuyGuildBankTabResponse) Reset() { + *x = BuyGuildBankTabResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[50] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BuyGuildBankTabResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BuyGuildBankTabResponse) ProtoMessage() {} + +func (x *BuyGuildBankTabResponse) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[50] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BuyGuildBankTabResponse.ProtoReflect.Descriptor instead. +func (*BuyGuildBankTabResponse) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{50} +} + +func (x *BuyGuildBankTabResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *BuyGuildBankTabResponse) GetStatus() GuildBankStatus_Status { + if x != nil { + return x.Status + } + return GuildBankStatus_Ok +} + +func (x *BuyGuildBankTabResponse) GetTabCost() uint32 { + if x != nil { + return x.TabCost + } + return 0 +} + +type DepositGuildBankMoneyParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GuildID uint64 `protobuf:"varint,3,opt,name=guildID,proto3" json:"guildID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + Amount uint32 `protobuf:"varint,5,opt,name=amount,proto3" json:"amount,omitempty"` +} + +func (x *DepositGuildBankMoneyParams) Reset() { + *x = DepositGuildBankMoneyParams{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[51] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DepositGuildBankMoneyParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DepositGuildBankMoneyParams) ProtoMessage() {} + +func (x *DepositGuildBankMoneyParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[51] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DepositGuildBankMoneyParams.ProtoReflect.Descriptor instead. +func (*DepositGuildBankMoneyParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{51} +} + +func (x *DepositGuildBankMoneyParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *DepositGuildBankMoneyParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *DepositGuildBankMoneyParams) GetGuildID() uint64 { + if x != nil { + return x.GuildID + } + return 0 +} + +func (x *DepositGuildBankMoneyParams) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *DepositGuildBankMoneyParams) GetAmount() uint32 { + if x != nil { + return x.Amount + } + return 0 +} + +type WithdrawGuildBankMoneyParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GuildID uint64 `protobuf:"varint,3,opt,name=guildID,proto3" json:"guildID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + Amount uint32 `protobuf:"varint,5,opt,name=amount,proto3" json:"amount,omitempty"` + Repair bool `protobuf:"varint,6,opt,name=repair,proto3" json:"repair,omitempty"` +} + +func (x *WithdrawGuildBankMoneyParams) Reset() { + *x = WithdrawGuildBankMoneyParams{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[52] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WithdrawGuildBankMoneyParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WithdrawGuildBankMoneyParams) ProtoMessage() {} + +func (x *WithdrawGuildBankMoneyParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[52] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WithdrawGuildBankMoneyParams.ProtoReflect.Descriptor instead. +func (*WithdrawGuildBankMoneyParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{52} +} + +func (x *WithdrawGuildBankMoneyParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *WithdrawGuildBankMoneyParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *WithdrawGuildBankMoneyParams) GetGuildID() uint64 { + if x != nil { + return x.GuildID + } + return 0 +} + +func (x *WithdrawGuildBankMoneyParams) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *WithdrawGuildBankMoneyParams) GetAmount() uint32 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *WithdrawGuildBankMoneyParams) GetRepair() bool { + if x != nil { + return x.Repair + } + return false +} + +type RollbackGuildBankMoneyWithdrawParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GuildID uint64 `protobuf:"varint,3,opt,name=guildID,proto3" json:"guildID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + Amount uint32 `protobuf:"varint,5,opt,name=amount,proto3" json:"amount,omitempty"` + Repair bool `protobuf:"varint,6,opt,name=repair,proto3" json:"repair,omitempty"` + LogGUID uint32 `protobuf:"varint,7,opt,name=logGUID,proto3" json:"logGUID,omitempty"` +} + +func (x *RollbackGuildBankMoneyWithdrawParams) Reset() { + *x = RollbackGuildBankMoneyWithdrawParams{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[53] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RollbackGuildBankMoneyWithdrawParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RollbackGuildBankMoneyWithdrawParams) ProtoMessage() {} + +func (x *RollbackGuildBankMoneyWithdrawParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[53] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RollbackGuildBankMoneyWithdrawParams.ProtoReflect.Descriptor instead. +func (*RollbackGuildBankMoneyWithdrawParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{53} +} + +func (x *RollbackGuildBankMoneyWithdrawParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *RollbackGuildBankMoneyWithdrawParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *RollbackGuildBankMoneyWithdrawParams) GetGuildID() uint64 { + if x != nil { + return x.GuildID + } + return 0 +} + +func (x *RollbackGuildBankMoneyWithdrawParams) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *RollbackGuildBankMoneyWithdrawParams) GetAmount() uint32 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *RollbackGuildBankMoneyWithdrawParams) GetRepair() bool { + if x != nil { + return x.Repair + } + return false +} + +func (x *RollbackGuildBankMoneyWithdrawParams) GetLogGUID() uint32 { + if x != nil { + return x.LogGUID + } + return 0 +} + +type DepositGuildBankItemParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GuildID uint64 `protobuf:"varint,3,opt,name=guildID,proto3" json:"guildID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + TabID uint32 `protobuf:"varint,5,opt,name=tabID,proto3" json:"tabID,omitempty"` + SlotID uint32 `protobuf:"varint,6,opt,name=slotID,proto3" json:"slotID,omitempty"` + Item *GuildBankItem `protobuf:"bytes,7,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *DepositGuildBankItemParams) Reset() { + *x = DepositGuildBankItemParams{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[54] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DepositGuildBankItemParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DepositGuildBankItemParams) ProtoMessage() {} + +func (x *DepositGuildBankItemParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[54] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DepositGuildBankItemParams.ProtoReflect.Descriptor instead. +func (*DepositGuildBankItemParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{54} +} + +func (x *DepositGuildBankItemParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *DepositGuildBankItemParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *DepositGuildBankItemParams) GetGuildID() uint64 { + if x != nil { + return x.GuildID + } + return 0 +} + +func (x *DepositGuildBankItemParams) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *DepositGuildBankItemParams) GetTabID() uint32 { + if x != nil { + return x.TabID + } + return 0 +} + +func (x *DepositGuildBankItemParams) GetSlotID() uint32 { + if x != nil { + return x.SlotID + } + return 0 +} + +func (x *DepositGuildBankItemParams) GetItem() *GuildBankItem { + if x != nil { + return x.Item + } + return nil +} + +type WithdrawGuildBankItemParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GuildID uint64 `protobuf:"varint,3,opt,name=guildID,proto3" json:"guildID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + TabID uint32 `protobuf:"varint,5,opt,name=tabID,proto3" json:"tabID,omitempty"` + SlotID uint32 `protobuf:"varint,6,opt,name=slotID,proto3" json:"slotID,omitempty"` + Count uint32 `protobuf:"varint,7,opt,name=count,proto3" json:"count,omitempty"` +} + +func (x *WithdrawGuildBankItemParams) Reset() { + *x = WithdrawGuildBankItemParams{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[55] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WithdrawGuildBankItemParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WithdrawGuildBankItemParams) ProtoMessage() {} + +func (x *WithdrawGuildBankItemParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[55] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WithdrawGuildBankItemParams.ProtoReflect.Descriptor instead. +func (*WithdrawGuildBankItemParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{55} +} + +func (x *WithdrawGuildBankItemParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *WithdrawGuildBankItemParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *WithdrawGuildBankItemParams) GetGuildID() uint64 { + if x != nil { + return x.GuildID + } + return 0 +} + +func (x *WithdrawGuildBankItemParams) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *WithdrawGuildBankItemParams) GetTabID() uint32 { + if x != nil { + return x.TabID + } + return 0 +} + +func (x *WithdrawGuildBankItemParams) GetSlotID() uint32 { + if x != nil { + return x.SlotID + } + return 0 +} + +func (x *WithdrawGuildBankItemParams) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +type RollbackGuildBankItemWithdrawParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GuildID uint64 `protobuf:"varint,3,opt,name=guildID,proto3" json:"guildID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + TabID uint32 `protobuf:"varint,5,opt,name=tabID,proto3" json:"tabID,omitempty"` + SlotID uint32 `protobuf:"varint,6,opt,name=slotID,proto3" json:"slotID,omitempty"` + Item *GuildBankItem `protobuf:"bytes,7,opt,name=item,proto3" json:"item,omitempty"` + LogGUID uint32 `protobuf:"varint,8,opt,name=logGUID,proto3" json:"logGUID,omitempty"` +} + +func (x *RollbackGuildBankItemWithdrawParams) Reset() { + *x = RollbackGuildBankItemWithdrawParams{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[56] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RollbackGuildBankItemWithdrawParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RollbackGuildBankItemWithdrawParams) ProtoMessage() {} + +func (x *RollbackGuildBankItemWithdrawParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[56] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RollbackGuildBankItemWithdrawParams.ProtoReflect.Descriptor instead. +func (*RollbackGuildBankItemWithdrawParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{56} +} + +func (x *RollbackGuildBankItemWithdrawParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *RollbackGuildBankItemWithdrawParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *RollbackGuildBankItemWithdrawParams) GetGuildID() uint64 { + if x != nil { + return x.GuildID + } + return 0 +} + +func (x *RollbackGuildBankItemWithdrawParams) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *RollbackGuildBankItemWithdrawParams) GetTabID() uint32 { + if x != nil { + return x.TabID + } + return 0 +} + +func (x *RollbackGuildBankItemWithdrawParams) GetSlotID() uint32 { + if x != nil { + return x.SlotID + } + return 0 +} + +func (x *RollbackGuildBankItemWithdrawParams) GetItem() *GuildBankItem { + if x != nil { + return x.Item + } + return nil +} + +func (x *RollbackGuildBankItemWithdrawParams) GetLogGUID() uint32 { + if x != nil { + return x.LogGUID + } + return 0 +} + +type MoveGuildBankItemParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + GuildID uint64 `protobuf:"varint,3,opt,name=guildID,proto3" json:"guildID,omitempty"` + MemberGUID uint64 `protobuf:"varint,4,opt,name=memberGUID,proto3" json:"memberGUID,omitempty"` + SourceTabID uint32 `protobuf:"varint,5,opt,name=sourceTabID,proto3" json:"sourceTabID,omitempty"` + SourceSlotID uint32 `protobuf:"varint,6,opt,name=sourceSlotID,proto3" json:"sourceSlotID,omitempty"` + DestinationTabID uint32 `protobuf:"varint,7,opt,name=destinationTabID,proto3" json:"destinationTabID,omitempty"` + DestinationSlotID uint32 `protobuf:"varint,8,opt,name=destinationSlotID,proto3" json:"destinationSlotID,omitempty"` + Count uint32 `protobuf:"varint,9,opt,name=count,proto3" json:"count,omitempty"` +} + +func (x *MoveGuildBankItemParams) Reset() { + *x = MoveGuildBankItemParams{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[57] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MoveGuildBankItemParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MoveGuildBankItemParams) ProtoMessage() {} + +func (x *MoveGuildBankItemParams) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[57] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MoveGuildBankItemParams.ProtoReflect.Descriptor instead. +func (*MoveGuildBankItemParams) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{57} +} + +func (x *MoveGuildBankItemParams) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *MoveGuildBankItemParams) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *MoveGuildBankItemParams) GetGuildID() uint64 { + if x != nil { + return x.GuildID + } + return 0 +} + +func (x *MoveGuildBankItemParams) GetMemberGUID() uint64 { + if x != nil { + return x.MemberGUID + } + return 0 +} + +func (x *MoveGuildBankItemParams) GetSourceTabID() uint32 { + if x != nil { + return x.SourceTabID + } + return 0 +} + +func (x *MoveGuildBankItemParams) GetSourceSlotID() uint32 { + if x != nil { + return x.SourceSlotID + } + return 0 +} + +func (x *MoveGuildBankItemParams) GetDestinationTabID() uint32 { + if x != nil { + return x.DestinationTabID + } + return 0 +} + +func (x *MoveGuildBankItemParams) GetDestinationSlotID() uint32 { + if x != nil { + return x.DestinationSlotID + } + return 0 +} + +func (x *MoveGuildBankItemParams) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +type GuildBankActionResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status GuildBankStatus_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.GuildBankStatus_Status" json:"status,omitempty"` + LogGUID uint32 `protobuf:"varint,3,opt,name=logGUID,proto3" json:"logGUID,omitempty"` +} + +func (x *GuildBankActionResponse) Reset() { + *x = GuildBankActionResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[58] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GuildBankActionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GuildBankActionResponse) ProtoMessage() {} + +func (x *GuildBankActionResponse) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[58] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GuildBankActionResponse.ProtoReflect.Descriptor instead. +func (*GuildBankActionResponse) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{58} +} + +func (x *GuildBankActionResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GuildBankActionResponse) GetStatus() GuildBankStatus_Status { + if x != nil { + return x.Status + } + return GuildBankStatus_Ok +} + +func (x *GuildBankActionResponse) GetLogGUID() uint32 { + if x != nil { + return x.LogGUID + } + return 0 +} + +type GuildBankItemMutationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status GuildBankStatus_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.GuildBankStatus_Status" json:"status,omitempty"` + Item *GuildBankItem `protobuf:"bytes,3,opt,name=item,proto3" json:"item,omitempty"` + ChangedSlots []uint32 `protobuf:"varint,4,rep,packed,name=changedSlots,proto3" json:"changedSlots,omitempty"` + LogGUID uint32 `protobuf:"varint,5,opt,name=logGUID,proto3" json:"logGUID,omitempty"` +} + +func (x *GuildBankItemMutationResponse) Reset() { + *x = GuildBankItemMutationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[59] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GuildBankItemMutationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GuildBankItemMutationResponse) ProtoMessage() {} + +func (x *GuildBankItemMutationResponse) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[59] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GuildBankItemMutationResponse.ProtoReflect.Descriptor instead. +func (*GuildBankItemMutationResponse) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{59} +} + +func (x *GuildBankItemMutationResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GuildBankItemMutationResponse) GetStatus() GuildBankStatus_Status { + if x != nil { + return x.Status + } + return GuildBankStatus_Ok +} + +func (x *GuildBankItemMutationResponse) GetItem() *GuildBankItem { + if x != nil { + return x.Item + } + return nil +} + +func (x *GuildBankItemMutationResponse) GetChangedSlots() []uint32 { + if x != nil { + return x.ChangedSlots + } + return nil +} + +func (x *GuildBankItemMutationResponse) GetLogGUID() uint32 { + if x != nil { + return x.LogGUID + } + return 0 +} + +type GetRosterInfoResponse_Member struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Status uint32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"` + RankID uint32 `protobuf:"varint,4,opt,name=rankID,proto3" json:"rankID,omitempty"` + Lvl uint32 `protobuf:"varint,5,opt,name=lvl,proto3" json:"lvl,omitempty"` + ClassID uint32 `protobuf:"varint,6,opt,name=classID,proto3" json:"classID,omitempty"` + Gender uint32 `protobuf:"varint,7,opt,name=gender,proto3" json:"gender,omitempty"` + AreaID uint32 `protobuf:"varint,8,opt,name=areaID,proto3" json:"areaID,omitempty"` + LogoutTime int64 `protobuf:"varint,9,opt,name=logoutTime,proto3" json:"logoutTime,omitempty"` + Note string `protobuf:"bytes,10,opt,name=note,proto3" json:"note,omitempty"` + OfficerNote string `protobuf:"bytes,11,opt,name=officerNote,proto3" json:"officerNote,omitempty"` + BankWithdraw []uint32 `protobuf:"varint,12,rep,packed,name=bankWithdraw,proto3" json:"bankWithdraw,omitempty"` +} + +func (x *GetRosterInfoResponse_Member) Reset() { + *x = GetRosterInfoResponse_Member{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[60] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRosterInfoResponse_Member) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRosterInfoResponse_Member) ProtoMessage() {} + +func (x *GetRosterInfoResponse_Member) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[60] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRosterInfoResponse_Member.ProtoReflect.Descriptor instead. +func (*GetRosterInfoResponse_Member) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{3, 0} +} + +func (x *GetRosterInfoResponse_Member) GetGuid() uint64 { + if x != nil { + return x.Guid + } + return 0 +} + +func (x *GetRosterInfoResponse_Member) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *GetRosterInfoResponse_Member) GetStatus() uint32 { + if x != nil { + return x.Status + } + return 0 +} + +func (x *GetRosterInfoResponse_Member) GetRankID() uint32 { + if x != nil { + return x.RankID + } + return 0 +} + +func (x *GetRosterInfoResponse_Member) GetLvl() uint32 { + if x != nil { + return x.Lvl + } + return 0 +} + +func (x *GetRosterInfoResponse_Member) GetClassID() uint32 { + if x != nil { + return x.ClassID + } + return 0 +} + +func (x *GetRosterInfoResponse_Member) GetGender() uint32 { + if x != nil { + return x.Gender + } + return 0 +} + +func (x *GetRosterInfoResponse_Member) GetAreaID() uint32 { + if x != nil { + return x.AreaID + } + return 0 +} + +func (x *GetRosterInfoResponse_Member) GetLogoutTime() int64 { + if x != nil { + return x.LogoutTime + } + return 0 +} + +func (x *GetRosterInfoResponse_Member) GetNote() string { + if x != nil { + return x.Note + } + return "" +} + +func (x *GetRosterInfoResponse_Member) GetOfficerNote() string { + if x != nil { + return x.OfficerNote + } + return "" +} + +func (x *GetRosterInfoResponse_Member) GetBankWithdraw() []uint32 { + if x != nil { + return x.BankWithdraw + } + return nil +} + +type GetRosterInfoResponse_Rank struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Flags uint32 `protobuf:"varint,2,opt,name=flags,proto3" json:"flags,omitempty"` + GoldLimit uint32 `protobuf:"varint,3,opt,name=goldLimit,proto3" json:"goldLimit,omitempty"` + BankTabRights []*GetRosterInfoResponse_Rank_BankTabRight `protobuf:"bytes,4,rep,name=bankTabRights,proto3" json:"bankTabRights,omitempty"` +} + +func (x *GetRosterInfoResponse_Rank) Reset() { + *x = GetRosterInfoResponse_Rank{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[61] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRosterInfoResponse_Rank) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRosterInfoResponse_Rank) ProtoMessage() {} + +func (x *GetRosterInfoResponse_Rank) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[61] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRosterInfoResponse_Rank.ProtoReflect.Descriptor instead. +func (*GetRosterInfoResponse_Rank) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{3, 1} +} + +func (x *GetRosterInfoResponse_Rank) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *GetRosterInfoResponse_Rank) GetFlags() uint32 { + if x != nil { + return x.Flags + } + return 0 +} + +func (x *GetRosterInfoResponse_Rank) GetGoldLimit() uint32 { + if x != nil { + return x.GoldLimit + } + return 0 +} + +func (x *GetRosterInfoResponse_Rank) GetBankTabRights() []*GetRosterInfoResponse_Rank_BankTabRight { + if x != nil { + return x.BankTabRights + } + return nil +} + +type GetRosterInfoResponse_Guild struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + WelcomeText string `protobuf:"bytes,2,opt,name=welcomeText,proto3" json:"welcomeText,omitempty"` + InfoText string `protobuf:"bytes,3,opt,name=infoText,proto3" json:"infoText,omitempty"` + Members []*GetRosterInfoResponse_Member `protobuf:"bytes,4,rep,name=members,proto3" json:"members,omitempty"` + Ranks []*GetRosterInfoResponse_Rank `protobuf:"bytes,5,rep,name=ranks,proto3" json:"ranks,omitempty"` + PurchasedBankTabs uint32 `protobuf:"varint,6,opt,name=purchasedBankTabs,proto3" json:"purchasedBankTabs,omitempty"` +} + +func (x *GetRosterInfoResponse_Guild) Reset() { + *x = GetRosterInfoResponse_Guild{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[62] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRosterInfoResponse_Guild) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRosterInfoResponse_Guild) ProtoMessage() {} + +func (x *GetRosterInfoResponse_Guild) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[62] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRosterInfoResponse_Guild.ProtoReflect.Descriptor instead. +func (*GetRosterInfoResponse_Guild) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{3, 2} +} + +func (x *GetRosterInfoResponse_Guild) GetId() uint64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *GetRosterInfoResponse_Guild) GetWelcomeText() string { + if x != nil { + return x.WelcomeText + } + return "" +} + +func (x *GetRosterInfoResponse_Guild) GetInfoText() string { + if x != nil { + return x.InfoText + } + return "" +} + +func (x *GetRosterInfoResponse_Guild) GetMembers() []*GetRosterInfoResponse_Member { + if x != nil { + return x.Members + } + return nil +} + +func (x *GetRosterInfoResponse_Guild) GetRanks() []*GetRosterInfoResponse_Rank { + if x != nil { + return x.Ranks + } + return nil +} + +func (x *GetRosterInfoResponse_Guild) GetPurchasedBankTabs() uint32 { + if x != nil { + return x.PurchasedBankTabs + } + return 0 +} + +type GetRosterInfoResponse_Rank_BankTabRight struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TabID uint32 `protobuf:"varint,1,opt,name=tabID,proto3" json:"tabID,omitempty"` + Flags uint32 `protobuf:"varint,2,opt,name=flags,proto3" json:"flags,omitempty"` + WithdrawItemLimit uint32 `protobuf:"varint,3,opt,name=withdrawItemLimit,proto3" json:"withdrawItemLimit,omitempty"` +} + +func (x *GetRosterInfoResponse_Rank_BankTabRight) Reset() { + *x = GetRosterInfoResponse_Rank_BankTabRight{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[63] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRosterInfoResponse_Rank_BankTabRight) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRosterInfoResponse_Rank_BankTabRight) ProtoMessage() {} + +func (x *GetRosterInfoResponse_Rank_BankTabRight) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[63] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRosterInfoResponse_Rank_BankTabRight.ProtoReflect.Descriptor instead. +func (*GetRosterInfoResponse_Rank_BankTabRight) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{3, 1, 0} +} + +func (x *GetRosterInfoResponse_Rank_BankTabRight) GetTabID() uint32 { + if x != nil { + return x.TabID + } + return 0 +} + +func (x *GetRosterInfoResponse_Rank_BankTabRight) GetFlags() uint32 { + if x != nil { + return x.Flags + } + return 0 +} + +func (x *GetRosterInfoResponse_Rank_BankTabRight) GetWithdrawItemLimit() uint32 { + if x != nil { + return x.WithdrawItemLimit + } + return 0 +} + +type InviteAcceptedParams_Character struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Lvl uint32 `protobuf:"varint,3,opt,name=lvl,proto3" json:"lvl,omitempty"` + Race uint32 `protobuf:"varint,4,opt,name=race,proto3" json:"race,omitempty"` + ClassID uint32 `protobuf:"varint,5,opt,name=classID,proto3" json:"classID,omitempty"` + Gender uint32 `protobuf:"varint,6,opt,name=gender,proto3" json:"gender,omitempty"` + AreaID uint32 `protobuf:"varint,7,opt,name=areaID,proto3" json:"areaID,omitempty"` + AccountID uint64 `protobuf:"varint,8,opt,name=accountID,proto3" json:"accountID,omitempty"` +} + +func (x *InviteAcceptedParams_Character) Reset() { + *x = InviteAcceptedParams_Character{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[64] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InviteAcceptedParams_Character) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InviteAcceptedParams_Character) ProtoMessage() {} + +func (x *InviteAcceptedParams_Character) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[64] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InviteAcceptedParams_Character.ProtoReflect.Descriptor instead. +func (*InviteAcceptedParams_Character) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{6, 0} +} + +func (x *InviteAcceptedParams_Character) GetGuid() uint64 { if x != nil { return x.Guid } @@ -2167,21 +5070,84 @@ func (x *InviteAcceptedParams_Character) GetClassID() uint32 { func (x *InviteAcceptedParams_Character) GetGender() uint32 { if x != nil { - return x.Gender + return x.Gender + } + return 0 +} + +func (x *InviteAcceptedParams_Character) GetAreaID() uint32 { + if x != nil { + return x.AreaID + } + return 0 +} + +func (x *InviteAcceptedParams_Character) GetAccountID() uint64 { + if x != nil { + return x.AccountID + } + return 0 +} + +type RankUpdateParams_BankTabRight struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TabID uint32 `protobuf:"varint,1,opt,name=tabID,proto3" json:"tabID,omitempty"` + Flags uint32 `protobuf:"varint,2,opt,name=flags,proto3" json:"flags,omitempty"` + WithdrawItemLimit uint32 `protobuf:"varint,3,opt,name=withdrawItemLimit,proto3" json:"withdrawItemLimit,omitempty"` +} + +func (x *RankUpdateParams_BankTabRight) Reset() { + *x = RankUpdateParams_BankTabRight{} + if protoimpl.UnsafeEnabled { + mi := &file_guilds_proto_msgTypes[65] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RankUpdateParams_BankTabRight) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RankUpdateParams_BankTabRight) ProtoMessage() {} + +func (x *RankUpdateParams_BankTabRight) ProtoReflect() protoreflect.Message { + mi := &file_guilds_proto_msgTypes[65] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RankUpdateParams_BankTabRight.ProtoReflect.Descriptor instead. +func (*RankUpdateParams_BankTabRight) Descriptor() ([]byte, []int) { + return file_guilds_proto_rawDescGZIP(), []int{18, 0} +} + +func (x *RankUpdateParams_BankTabRight) GetTabID() uint32 { + if x != nil { + return x.TabID } return 0 } -func (x *InviteAcceptedParams_Character) GetAreaID() uint32 { +func (x *RankUpdateParams_BankTabRight) GetFlags() uint32 { if x != nil { - return x.AreaID + return x.Flags } return 0 } -func (x *InviteAcceptedParams_Character) GetAccountID() uint64 { +func (x *RankUpdateParams_BankTabRight) GetWithdrawItemLimit() uint32 { if x != nil { - return x.AccountID + return x.WithdrawItemLimit } return 0 } @@ -2220,13 +5186,13 @@ var file_guilds_proto_rawDesc = []byte{ 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, - 0x6c, 0x64, 0x49, 0x44, 0x22, 0x8b, 0x05, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x73, 0x74, + 0x6c, 0x64, 0x49, 0x44, 0x22, 0x9b, 0x07, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x35, 0x0a, 0x05, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, - 0x52, 0x05, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x1a, 0x92, 0x02, 0x0a, 0x06, 0x4d, 0x65, 0x6d, 0x62, + 0x52, 0x05, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x1a, 0xb6, 0x02, 0x0a, 0x06, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, @@ -2243,25 +5209,42 @@ var file_guilds_proto_rawDesc = []byte{ 0x75, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x6f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x65, 0x1a, 0x4a, 0x0a, 0x04, - 0x52, 0x61, 0x6e, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x6f, - 0x6c, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x67, - 0x6f, 0x6c, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x1a, 0xc7, 0x01, 0x0a, 0x05, 0x47, 0x75, 0x69, - 0x6c, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x54, 0x65, 0x78, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, - 0x54, 0x65, 0x78, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x66, 0x6f, 0x54, 0x65, 0x78, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x66, 0x6f, 0x54, 0x65, 0x78, 0x74, - 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x34, 0x0a, 0x05, - 0x72, 0x61, 0x6e, 0x6b, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x52, 0x05, 0x72, 0x61, 0x6e, - 0x6b, 0x73, 0x22, 0x96, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x4d, 0x65, 0x6d, + 0x0b, 0x6f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x0c, + 0x62, 0x61, 0x6e, 0x6b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x18, 0x0c, 0x20, 0x03, + 0x28, 0x0d, 0x52, 0x0c, 0x62, 0x61, 0x6e, 0x6b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x1a, 0x87, 0x02, 0x0a, 0x04, 0x52, 0x61, 0x6e, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, + 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, + 0x1c, 0x0a, 0x09, 0x67, 0x6f, 0x6c, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x09, 0x67, 0x6f, 0x6c, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x51, 0x0a, + 0x0d, 0x62, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x73, + 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x52, 0x61, 0x6e, 0x6b, 0x2e, 0x42, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x52, 0x69, 0x67, 0x68, + 0x74, 0x52, 0x0d, 0x62, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, + 0x1a, 0x68, 0x0a, 0x0c, 0x42, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x52, 0x69, 0x67, 0x68, 0x74, + 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x11, + 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x49, 0x74, 0x65, 0x6d, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x49, 0x74, 0x65, 0x6d, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x1a, 0xf5, 0x01, 0x0a, 0x05, 0x47, + 0x75, 0x69, 0x6c, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x54, + 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x65, 0x6c, 0x63, 0x6f, + 0x6d, 0x65, 0x54, 0x65, 0x78, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x66, 0x6f, 0x54, 0x65, + 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x66, 0x6f, 0x54, 0x65, + 0x78, 0x74, 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x73, 0x74, + 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x34, + 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x6b, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x52, 0x05, 0x72, + 0x61, 0x6e, 0x6b, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x70, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, + 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x11, 0x70, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x54, 0x61, + 0x62, 0x73, 0x22, 0xe6, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, @@ -2270,12 +5253,17 @@ var file_guilds_proto_rawDesc = []byte{ 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x46, 0x0a, 0x14, 0x49, + 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x69, + 0x6e, 0x76, 0x69, 0x74, 0x65, 0x65, 0x52, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x65, 0x52, 0x61, 0x63, 0x65, 0x12, 0x2c, 0x0a, + 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x46, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x43, + 0x72, 0x6f, 0x73, 0x73, 0x46, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x46, 0x0a, 0x14, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x1c, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x61, 0x69, 0x6c, 0x65, - 0x64, 0x10, 0x01, 0x22, 0xc8, 0x02, 0x0a, 0x14, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x41, 0x63, + 0x64, 0x10, 0x01, 0x22, 0xf6, 0x02, 0x0a, 0x14, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, @@ -2283,201 +5271,688 @@ var file_guilds_proto_rawDesc = []byte{ 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x52, - 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x1a, 0xc1, 0x01, 0x0a, 0x09, 0x43, - 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x75, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x6c, 0x76, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6c, - 0x76, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x04, 0x72, 0x61, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, - 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, - 0x12, 0x16, 0x0a, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x72, 0x65, 0x61, - 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x61, 0x72, 0x65, 0x61, 0x49, 0x44, - 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x22, 0x44, - 0x0a, 0x16, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, - 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, - 0x6c, 0x64, 0x49, 0x44, 0x22, 0x51, 0x0a, 0x0b, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, - 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x72, 0x22, 0x21, 0x0a, 0x0d, 0x4c, 0x65, 0x61, 0x76, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x68, 0x0a, 0x0a, 0x4b, 0x69, - 0x63, 0x6b, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, - 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, - 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x6b, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6b, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x74, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x22, 0x20, 0x0a, 0x0c, 0x4b, 0x69, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x92, 0x01, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x44, 0x61, 0x79, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, - 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, - 0x44, 0x12, 0x28, 0x0a, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x66, 0x54, 0x68, - 0x65, 0x44, 0x61, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x44, 0x61, 0x79, 0x22, 0x2e, 0x0a, 0x1a, 0x53, - 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x44, 0x61, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x76, 0x0a, 0x12, 0x53, - 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x09, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x11, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x46, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x43, 0x72, 0x6f, 0x73, + 0x73, 0x46, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xc1, 0x01, 0x0a, 0x09, 0x43, 0x68, 0x61, + 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x75, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x6c, 0x76, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6c, 0x76, 0x6c, + 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, + 0x72, 0x61, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x44, 0x12, 0x16, + 0x0a, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, + 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x72, 0x65, 0x61, 0x49, 0x44, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x61, 0x72, 0x65, 0x61, 0x49, 0x44, 0x12, 0x1c, + 0x0a, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x22, 0x44, 0x0a, 0x16, + 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, 0x6c, + 0x64, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, + 0x49, 0x44, 0x22, 0x51, 0x0a, 0x0b, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x16, 0x0a, + 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6c, + 0x65, 0x61, 0x76, 0x65, 0x72, 0x22, 0x21, 0x0a, 0x0d, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x68, 0x0a, 0x0a, 0x4b, 0x69, 0x63, 0x6b, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x6b, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x06, 0x6b, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x22, 0x20, 0x0a, 0x0c, 0x4b, 0x69, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x22, 0x92, 0x01, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x44, 0x61, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, - 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, - 0x6e, 0x66, 0x6f, 0x22, 0x28, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x91, 0x01, - 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, + 0x28, 0x0a, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x44, + 0x61, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x44, 0x61, 0x79, 0x22, 0x2e, 0x0a, 0x1a, 0x53, 0x65, 0x74, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x44, 0x61, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x76, 0x0a, 0x12, 0x53, 0x65, 0x74, + 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, - 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x55, 0x49, 0x44, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, - 0x65, 0x22, 0x23, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0xca, 0x01, 0x0a, 0x10, 0x52, 0x61, 0x6e, 0x6b, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, - 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x6e, - 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x61, 0x6e, 0x6b, 0x12, 0x1a, 0x0a, - 0x08, 0x72, 0x61, 0x6e, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x72, 0x61, 0x6e, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x69, 0x67, - 0x68, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x72, 0x69, 0x67, 0x68, 0x74, - 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x50, 0x65, 0x72, 0x44, 0x61, 0x79, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x50, 0x65, 0x72, - 0x44, 0x61, 0x79, 0x22, 0x26, 0x0a, 0x12, 0x52, 0x61, 0x6e, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x79, 0x0a, 0x0d, 0x41, - 0x64, 0x64, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, - 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, - 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x61, - 0x6e, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x61, - 0x6e, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x23, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x52, 0x61, 0x6e, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x64, 0x0a, 0x14, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, - 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, - 0x44, 0x22, 0x2a, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x52, - 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x83, 0x01, - 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, + 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x12, 0x0a, + 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x66, + 0x6f, 0x22, 0x28, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x91, 0x01, 0x0a, 0x0d, + 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x55, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x6f, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x22, + 0x23, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x22, 0xfd, 0x02, 0x0a, 0x10, 0x52, 0x61, 0x6e, 0x6b, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x6e, 0x6b, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x61, 0x6e, 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x72, + 0x61, 0x6e, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, + 0x61, 0x6e, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x69, 0x67, 0x68, 0x74, + 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, + 0x20, 0x0a, 0x0b, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x50, 0x65, 0x72, 0x44, 0x61, 0x79, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x50, 0x65, 0x72, 0x44, 0x61, + 0x79, 0x12, 0x47, 0x0a, 0x0d, 0x62, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x52, 0x69, 0x67, 0x68, + 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x61, + 0x6e, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x42, + 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x52, 0x69, 0x67, 0x68, 0x74, 0x52, 0x0d, 0x62, 0x61, 0x6e, + 0x6b, 0x54, 0x61, 0x62, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x1a, 0x68, 0x0a, 0x0c, 0x42, 0x61, + 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x52, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, + 0x62, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, + 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x49, 0x74, 0x65, 0x6d, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x11, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x49, 0x74, 0x65, 0x6d, 0x4c, + 0x69, 0x6d, 0x69, 0x74, 0x22, 0x26, 0x0a, 0x12, 0x52, 0x61, 0x6e, 0x6b, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, + 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x79, 0x0a, 0x0d, + 0x41, 0x64, 0x64, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x72, + 0x61, 0x6e, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, + 0x61, 0x6e, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x23, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x52, 0x61, + 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, + 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x64, 0x0a, 0x14, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x22, 0x2a, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x61, 0x73, 0x74, + 0x52, 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x83, + 0x01, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x55, + 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x47, 0x55, 0x49, 0x44, 0x22, 0x29, 0x0a, 0x15, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x44, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, + 0xec, 0x01, 0x0a, 0x16, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, + 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, + 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x73, 0x4f, 0x66, 0x66, 0x69, + 0x63, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x10, 0x69, 0x73, 0x4f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x43, 0x68, 0x61, 0x74, 0x54, 0x61, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0d, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x68, 0x61, 0x74, 0x54, 0x61, 0x67, 0x22, 0x2c, + 0x0a, 0x18, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, + 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x5e, 0x0a, 0x16, + 0x47, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x70, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xd5, 0x01, 0x0a, + 0x0d, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, + 0x0a, 0x0c, 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x55, 0x49, 0x44, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x55, + 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x22, 0x68, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, + 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x65, + 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0c, 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x55, 0x49, 0x44, 0x22, 0x5b, + 0x0a, 0x18, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, + 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x2d, 0x0a, 0x08, + 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x08, 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xc8, 0x01, 0x0a, 0x18, + 0x4f, 0x66, 0x66, 0x65, 0x72, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x55, 0x49, 0x44, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x55, + 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x55, + 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x47, 0x55, 0x49, 0x44, 0x22, 0xf0, 0x01, 0x0a, 0x1a, 0x4f, 0x66, 0x66, 0x65, 0x72, + 0x47, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x66, 0x66, + 0x65, 0x72, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x80, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, + 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x4f, 0x77, + 0x6e, 0x65, 0x72, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, + 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x49, 0x6e, 0x47, 0x75, 0x69, 0x6c, + 0x64, 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x6c, 0x72, + 0x65, 0x61, 0x64, 0x79, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x64, 0x10, 0x05, 0x12, 0x0a, 0x0a, + 0x06, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x06, 0x22, 0xf9, 0x01, 0x0a, 0x17, 0x53, 0x69, + 0x67, 0x6e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, - 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x47, - 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x55, 0x49, - 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, - 0x55, 0x49, 0x44, 0x22, 0x29, 0x0a, 0x15, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x44, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, - 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0xc6, - 0x01, 0x0a, 0x16, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x73, 0x69, 0x67, 0x6e, + 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x73, + 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, + 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x55, 0x49, + 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x47, 0x55, 0x49, 0x44, 0x22, 0xfd, 0x01, 0x0a, 0x19, 0x53, 0x69, 0x67, 0x6e, 0x47, 0x75, + 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x47, + 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x22, 0x8f, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, + 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, + 0x79, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x41, 0x6c, 0x72, + 0x65, 0x61, 0x64, 0x79, 0x49, 0x6e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x10, 0x02, 0x12, 0x0f, 0x0a, + 0x0b, 0x43, 0x61, 0x6e, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x77, 0x6e, 0x10, 0x03, 0x12, 0x0d, + 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x10, 0x04, 0x12, 0x0c, 0x0a, + 0x08, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x46, + 0x75, 0x6c, 0x6c, 0x10, 0x06, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, + 0x07, 0x12, 0x12, 0x0a, 0x0e, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x49, 0x6e, 0x76, 0x69, + 0x74, 0x65, 0x64, 0x10, 0x08, 0x22, 0x64, 0x0a, 0x16, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, + 0x6e, 0x6b, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x68, 0x61, 0x6e, 0x74, 0x12, + 0x20, 0x0a, 0x0b, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x68, 0x61, + 0x6e, 0x74, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x73, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x45, 0x6e, 0x63, 0x68, 0x61, 0x6e, 0x74, 0x49, 0x44, 0x22, 0x95, 0x03, 0x0a, 0x0d, + 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1a, 0x0a, + 0x08, 0x69, 0x74, 0x65, 0x6d, 0x47, 0x55, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x08, 0x69, 0x74, 0x65, 0x6d, 0x47, 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6e, 0x74, + 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, + 0x6c, 0x6f, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, + 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, + 0x2a, 0x0a, 0x10, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, + 0x79, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x72, 0x61, 0x6e, 0x64, 0x6f, + 0x6d, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x49, 0x44, 0x12, 0x2e, 0x0a, 0x12, 0x72, + 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x53, 0x65, 0x65, + 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, + 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x53, 0x65, 0x65, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x64, + 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0a, 0x64, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x24, 0x0a, 0x0d, 0x65, + 0x6e, 0x63, 0x68, 0x61, 0x6e, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0d, 0x65, 0x6e, 0x63, 0x68, 0x61, 0x6e, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x49, + 0x44, 0x12, 0x42, 0x0a, 0x0e, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x68, 0x61, + 0x6e, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x6e, + 0x63, 0x68, 0x61, 0x6e, 0x74, 0x52, 0x0e, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x6e, 0x63, + 0x68, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x73, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x73, 0x12, + 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, + 0x65, 0x78, 0x74, 0x22, 0x89, 0x01, 0x0a, 0x0c, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, + 0x6b, 0x54, 0x61, 0x62, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, + 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x27, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, + 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, + 0xd0, 0x01, 0x0a, 0x0f, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x22, 0xbc, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, + 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, + 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x4e, 0x6f, 0x74, 0x46, 0x6f, + 0x75, 0x6e, 0x64, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x6f, 0x74, 0x49, 0x6e, 0x47, 0x75, + 0x69, 0x6c, 0x64, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x4e, 0x6f, 0x74, 0x45, 0x6e, 0x6f, 0x75, + 0x67, 0x68, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x6e, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x61, 0x62, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x6e, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x6c, 0x6f, 0x74, 0x10, 0x06, 0x12, 0x12, 0x0a, 0x0e, 0x4e, + 0x6f, 0x74, 0x45, 0x6e, 0x6f, 0x75, 0x67, 0x68, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x10, 0x07, 0x12, + 0x0c, 0x0a, 0x08, 0x42, 0x61, 0x6e, 0x6b, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x08, 0x12, 0x11, 0x0a, + 0x0d, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x10, 0x09, + 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x74, 0x65, 0x6d, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, + 0x10, 0x0a, 0x22, 0xb0, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, + 0x61, 0x6e, 0x6b, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, - 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x47, - 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, - 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x73, 0x4f, 0x66, 0x66, 0x69, 0x63, - 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x10, 0x69, 0x73, 0x4f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6c, - 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, - 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x22, 0x2c, 0x0a, 0x18, 0x53, 0x65, 0x6e, 0x64, 0x47, - 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x61, 0x70, 0x69, 0x32, 0x8f, 0x08, 0x0a, 0x0c, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, - 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, - 0x6e, 0x66, 0x6f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x13, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, - 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, - 0x66, 0x6f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x52, 0x6f, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x4d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x12, 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x4d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x18, 0x2e, 0x76, 0x31, - 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x41, - 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x12, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, - 0x69, 0x74, 0x65, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x41, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, - 0x05, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x0f, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, - 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, - 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x4b, 0x69, - 0x63, 0x6b, 0x12, 0x0e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x1a, 0x10, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x44, 0x61, 0x79, 0x12, 0x1c, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x44, - 0x61, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x44, 0x61, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x53, 0x65, 0x74, 0x47, - 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, - 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x13, 0x53, 0x65, - 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4e, 0x6f, 0x74, - 0x65, 0x12, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x13, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x14, 0x53, 0x65, 0x74, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x72, 0x4e, 0x6f, 0x74, - 0x65, 0x12, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x13, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x6b, 0x12, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x61, 0x6e, - 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x16, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x52, 0x61, 0x6e, 0x6b, - 0x12, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x1a, 0x13, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x61, 0x6e, 0x6b, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x52, 0x61, 0x6e, 0x6b, 0x12, 0x18, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x4c, 0x61, 0x73, 0x74, 0x52, 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x43, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x44, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x19, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0c, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, - 0x74, 0x65, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x19, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x44, 0x65, 0x6d, 0x6f, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x10, 0x53, 0x65, 0x6e, - 0x64, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x65, 0x6e, 0x64, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0f, 0x5a, 0x0d, 0x67, 0x65, 0x6e, 0x2f, 0x67, - 0x75, 0x69, 0x6c, 0x64, 0x73, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, + 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, + 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, + 0x74, 0x61, 0x62, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x22, 0xab, 0x02, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, + 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x32, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x05, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, + 0x62, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, + 0x12, 0x32, 0x0a, 0x14, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x73, 0x52, + 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, + 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x6d, 0x61, 0x69, + 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x74, 0x61, 0x62, 0x73, 0x18, 0x07, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, + 0x6b, 0x54, 0x61, 0x62, 0x52, 0x04, 0x74, 0x61, 0x62, 0x73, 0x12, 0x27, 0x0a, 0x05, 0x69, 0x74, + 0x65, 0x6d, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, + 0x65, 0x6d, 0x73, 0x22, 0x93, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, + 0x42, 0x61, 0x6e, 0x6b, 0x4c, 0x6f, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, + 0x6c, 0x64, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, 0x6c, + 0x64, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x22, 0xd1, 0x01, 0x0a, 0x11, 0x47, 0x75, + 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, + 0x1e, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, + 0x1c, 0x0a, 0x09, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x09, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x6f, + 0x6e, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x54, 0x61, 0x62, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x08, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x54, 0x61, 0x62, 0x22, 0xa6, 0x01, + 0x0a, 0x17, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x4c, 0x6f, + 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x32, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, + 0x74, 0x61, 0x62, 0x49, 0x44, 0x12, 0x2f, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, + 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x47, 0x75, + 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x54, 0x65, 0x78, 0x74, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, + 0x62, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, + 0x22, 0x8d, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, + 0x6b, 0x54, 0x61, 0x62, 0x54, 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x32, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, + 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, + 0x22, 0xbe, 0x01, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x75, 0x69, 0x6c, 0x64, + 0x42, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, + 0x6c, 0x64, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, 0x6c, + 0x64, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, + 0x6e, 0x22, 0xab, 0x01, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, + 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x54, 0x65, 0x78, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, + 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, + 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, + 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, + 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x65, 0x78, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, + 0x93, 0x01, 0x0a, 0x15, 0x42, 0x75, 0x79, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, + 0x54, 0x61, 0x62, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, + 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, + 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, + 0x74, 0x61, 0x62, 0x49, 0x44, 0x22, 0x79, 0x0a, 0x17, 0x42, 0x75, 0x79, 0x47, 0x75, 0x69, 0x6c, + 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x32, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, + 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x61, 0x62, 0x43, 0x6f, 0x73, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x74, 0x61, 0x62, 0x43, 0x6f, 0x73, 0x74, + 0x22, 0x9b, 0x01, 0x0a, 0x1b, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x47, 0x75, 0x69, 0x6c, + 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, + 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, + 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xb4, + 0x01, 0x0a, 0x1c, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x47, 0x75, 0x69, 0x6c, 0x64, + 0x42, 0x61, 0x6e, 0x6b, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, + 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, + 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, + 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, + 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x72, 0x65, 0x70, 0x61, 0x69, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, + 0x65, 0x70, 0x61, 0x69, 0x72, 0x22, 0xd6, 0x01, 0x0a, 0x24, 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61, + 0x63, 0x6b, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x4d, 0x6f, 0x6e, 0x65, 0x79, + 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, + 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, + 0x6c, 0x64, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x72, 0x65, 0x70, 0x61, 0x69, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, + 0x70, 0x61, 0x69, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x47, 0x55, 0x49, 0x44, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x47, 0x55, 0x49, 0x44, 0x22, 0xd7, + 0x01, 0x0a, 0x1a, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, + 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, + 0x6c, 0x64, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, 0x6c, + 0x64, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6c, 0x6f, + 0x74, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x6c, 0x6f, 0x74, 0x49, + 0x44, 0x12, 0x25, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, + 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xc7, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, + 0x65, 0x6d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x1e, + 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x14, + 0x0a, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x74, + 0x61, 0x62, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6c, 0x6f, 0x74, 0x49, 0x44, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x6c, 0x6f, 0x74, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x22, 0xfa, 0x01, 0x0a, 0x23, 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x47, + 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x57, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, + 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, + 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, + 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, + 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x05, 0x74, 0x61, 0x62, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6c, 0x6f, 0x74, 0x49, 0x44, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x6c, 0x6f, 0x74, 0x49, 0x44, 0x12, 0x25, + 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x52, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x47, 0x55, 0x49, 0x44, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x47, 0x55, 0x49, 0x44, 0x22, + 0xb5, 0x02, 0x0a, 0x17, 0x4d, 0x6f, 0x76, 0x65, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, + 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, + 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, + 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, + 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, + 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x49, 0x44, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, + 0x62, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x6c, 0x6f, + 0x74, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x49, 0x44, 0x12, 0x2a, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x62, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x61, + 0x62, 0x49, 0x44, 0x12, 0x2c, 0x0a, 0x11, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x53, 0x6c, 0x6f, 0x74, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, + 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6c, 0x6f, 0x74, 0x49, + 0x44, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x79, 0x0a, 0x17, 0x47, 0x75, 0x69, 0x6c, 0x64, + 0x42, 0x61, 0x6e, 0x6b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x32, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, + 0x61, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x47, + 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x47, 0x55, + 0x49, 0x44, 0x22, 0xca, 0x01, 0x0a, 0x1d, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, + 0x49, 0x74, 0x65, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x32, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, + 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x0a, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, + 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x53, 0x6c, 0x6f, 0x74, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, + 0x53, 0x6c, 0x6f, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x47, 0x55, 0x49, 0x44, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x47, 0x55, 0x49, 0x44, 0x32, + 0xe8, 0x12, 0x0a, 0x0c, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x12, 0x36, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x1a, 0x13, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x52, + 0x6f, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x52, 0x6f, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x1a, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x73, 0x74, 0x65, + 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, + 0x0c, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x16, 0x2e, + 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, + 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x46, 0x0a, 0x0e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x12, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x41, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1a, 0x2e, 0x76, 0x31, + 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x4c, 0x65, 0x61, 0x76, 0x65, + 0x12, 0x0f, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x1a, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x4b, 0x69, 0x63, 0x6b, 0x12, 0x0e, 0x2e, 0x76, + 0x31, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x10, 0x2e, 0x76, + 0x31, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, + 0x0a, 0x12, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x66, 0x54, 0x68, + 0x65, 0x44, 0x61, 0x79, 0x12, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x44, 0x61, 0x79, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x44, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x53, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, + 0x49, 0x6e, 0x66, 0x6f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x18, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4e, 0x6f, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x13, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x4f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x13, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, + 0x6b, 0x12, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x61, 0x6e, + 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x31, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x52, 0x61, 0x6e, 0x6b, 0x12, 0x11, 0x2e, 0x76, 0x31, 0x2e, + 0x41, 0x64, 0x64, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x13, 0x2e, + 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x61, 0x73, 0x74, + 0x52, 0x61, 0x6e, 0x6b, 0x12, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x4c, 0x61, 0x73, 0x74, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1a, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x52, 0x61, + 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x50, 0x72, + 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x76, 0x31, + 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, + 0x65, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x42, 0x0a, 0x0c, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x44, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, + 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x75, 0x69, 0x6c, 0x64, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, + 0x64, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x1a, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x75, 0x69, + 0x6c, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x4c, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x75, + 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x1a, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x50, + 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x52, 0x0a, 0x12, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x66, 0x66, 0x65, 0x72, + 0x47, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x47, 0x75, + 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x11, 0x53, 0x69, 0x67, 0x6e, 0x47, 0x75, 0x69, 0x6c, 0x64, + 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, + 0x67, 0x6e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x47, + 0x75, 0x69, 0x6c, 0x64, 0x50, 0x65, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, + 0x42, 0x61, 0x6e, 0x6b, 0x12, 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, + 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x18, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, + 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x4c, 0x6f, 0x67, 0x12, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x4c, 0x6f, 0x67, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, + 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x55, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, + 0x6b, 0x54, 0x61, 0x62, 0x54, 0x65, 0x78, 0x74, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x54, 0x65, 0x78, + 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x54, 0x65, 0x78, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x12, 0x1c, + 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, + 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1b, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x41, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x13, 0x53, 0x65, 0x74, + 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x54, 0x65, 0x78, 0x74, + 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, + 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x54, 0x65, 0x78, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, + 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0f, + 0x42, 0x75, 0x79, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x12, + 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x79, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, + 0x6b, 0x54, 0x61, 0x62, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1b, 0x2e, 0x76, 0x31, 0x2e, + 0x42, 0x75, 0x79, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x15, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x4d, 0x6f, 0x6e, 0x65, 0x79, + 0x12, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x47, 0x75, 0x69, + 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x1a, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, + 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, + 0x61, 0x6e, 0x6b, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x4d, + 0x6f, 0x6e, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1b, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x1e, 0x52, 0x6f, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x4d, 0x6f, 0x6e, 0x65, + 0x79, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x28, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, + 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x1a, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, + 0x6e, 0x6b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x59, 0x0a, 0x14, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, + 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, + 0x65, 0x6d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, + 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x15, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, + 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, + 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, 0x1d, 0x52, 0x6f, 0x6c, 0x6c, + 0x62, 0x61, 0x63, 0x6b, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, + 0x6d, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x27, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, + 0x49, 0x74, 0x65, 0x6d, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, + 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x11, 0x4d, 0x6f, 0x76, 0x65, 0x47, 0x75, 0x69, + 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, + 0x4d, 0x6f, 0x76, 0x65, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, + 0x6d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x69, + 0x6c, 0x64, 0x42, 0x61, 0x6e, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0f, 0x5a, 0x0d, 0x67, 0x65, + 0x6e, 0x2f, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -2492,85 +5967,174 @@ func file_guilds_proto_rawDescGZIP() []byte { return file_guilds_proto_rawDescData } -var file_guilds_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_guilds_proto_msgTypes = make([]protoimpl.MessageInfo, 32) +var file_guilds_proto_enumTypes = make([]protoimpl.EnumInfo, 4) +var file_guilds_proto_msgTypes = make([]protoimpl.MessageInfo, 66) var file_guilds_proto_goTypes = []interface{}{ - (InviteMemberResponse_Status)(0), // 0: v1.InviteMemberResponse.Status - (*GetInfoParams)(nil), // 1: v1.GetInfoParams - (*GetInfoResponse)(nil), // 2: v1.GetInfoResponse - (*GetRosterInfoParams)(nil), // 3: v1.GetRosterInfoParams - (*GetRosterInfoResponse)(nil), // 4: v1.GetRosterInfoResponse - (*InviteMemberParams)(nil), // 5: v1.InviteMemberParams - (*InviteMemberResponse)(nil), // 6: v1.InviteMemberResponse - (*InviteAcceptedParams)(nil), // 7: v1.InviteAcceptedParams - (*InviteAcceptedResponse)(nil), // 8: v1.InviteAcceptedResponse - (*LeaveParams)(nil), // 9: v1.LeaveParams - (*LeaveResponse)(nil), // 10: v1.LeaveResponse - (*KickParams)(nil), // 11: v1.KickParams - (*KickResponse)(nil), // 12: v1.KickResponse - (*SetMessageOfTheDayParams)(nil), // 13: v1.SetMessageOfTheDayParams - (*SetMessageOfTheDayResponse)(nil), // 14: v1.SetMessageOfTheDayResponse - (*SetGuildInfoParams)(nil), // 15: v1.SetGuildInfoParams - (*SetGuildInfoResponse)(nil), // 16: v1.SetGuildInfoResponse - (*SetNoteParams)(nil), // 17: v1.SetNoteParams - (*SetNoteResponse)(nil), // 18: v1.SetNoteResponse - (*RankUpdateParams)(nil), // 19: v1.RankUpdateParams - (*RankUpdateResponse)(nil), // 20: v1.RankUpdateResponse - (*AddRankParams)(nil), // 21: v1.AddRankParams - (*AddRankResponse)(nil), // 22: v1.AddRankResponse - (*DeleteLastRankParams)(nil), // 23: v1.DeleteLastRankParams - (*DeleteLastRankResponse)(nil), // 24: v1.DeleteLastRankResponse - (*PromoteDemoteParams)(nil), // 25: v1.PromoteDemoteParams - (*PromoteDemoteResponse)(nil), // 26: v1.PromoteDemoteResponse - (*SendGuildMessageParams)(nil), // 27: v1.SendGuildMessageParams - (*SendGuildMessageResponse)(nil), // 28: v1.SendGuildMessageResponse - (*GetRosterInfoResponse_Member)(nil), // 29: v1.GetRosterInfoResponse.Member - (*GetRosterInfoResponse_Rank)(nil), // 30: v1.GetRosterInfoResponse.Rank - (*GetRosterInfoResponse_Guild)(nil), // 31: v1.GetRosterInfoResponse.Guild - (*InviteAcceptedParams_Character)(nil), // 32: v1.InviteAcceptedParams.Character + (InviteMemberResponse_Status)(0), // 0: v1.InviteMemberResponse.Status + (OfferGuildPetitionResponse_Status)(0), // 1: v1.OfferGuildPetitionResponse.Status + (SignGuildPetitionResponse_Status)(0), // 2: v1.SignGuildPetitionResponse.Status + (GuildBankStatus_Status)(0), // 3: v1.GuildBankStatus.Status + (*GetInfoParams)(nil), // 4: v1.GetInfoParams + (*GetInfoResponse)(nil), // 5: v1.GetInfoResponse + (*GetRosterInfoParams)(nil), // 6: v1.GetRosterInfoParams + (*GetRosterInfoResponse)(nil), // 7: v1.GetRosterInfoResponse + (*InviteMemberParams)(nil), // 8: v1.InviteMemberParams + (*InviteMemberResponse)(nil), // 9: v1.InviteMemberResponse + (*InviteAcceptedParams)(nil), // 10: v1.InviteAcceptedParams + (*InviteAcceptedResponse)(nil), // 11: v1.InviteAcceptedResponse + (*LeaveParams)(nil), // 12: v1.LeaveParams + (*LeaveResponse)(nil), // 13: v1.LeaveResponse + (*KickParams)(nil), // 14: v1.KickParams + (*KickResponse)(nil), // 15: v1.KickResponse + (*SetMessageOfTheDayParams)(nil), // 16: v1.SetMessageOfTheDayParams + (*SetMessageOfTheDayResponse)(nil), // 17: v1.SetMessageOfTheDayResponse + (*SetGuildInfoParams)(nil), // 18: v1.SetGuildInfoParams + (*SetGuildInfoResponse)(nil), // 19: v1.SetGuildInfoResponse + (*SetNoteParams)(nil), // 20: v1.SetNoteParams + (*SetNoteResponse)(nil), // 21: v1.SetNoteResponse + (*RankUpdateParams)(nil), // 22: v1.RankUpdateParams + (*RankUpdateResponse)(nil), // 23: v1.RankUpdateResponse + (*AddRankParams)(nil), // 24: v1.AddRankParams + (*AddRankResponse)(nil), // 25: v1.AddRankResponse + (*DeleteLastRankParams)(nil), // 26: v1.DeleteLastRankParams + (*DeleteLastRankResponse)(nil), // 27: v1.DeleteLastRankResponse + (*PromoteDemoteParams)(nil), // 28: v1.PromoteDemoteParams + (*PromoteDemoteResponse)(nil), // 29: v1.PromoteDemoteResponse + (*SendGuildMessageParams)(nil), // 30: v1.SendGuildMessageParams + (*SendGuildMessageResponse)(nil), // 31: v1.SendGuildMessageResponse + (*GuildPetitionSignature)(nil), // 32: v1.GuildPetitionSignature + (*GuildPetition)(nil), // 33: v1.GuildPetition + (*GetGuildPetitionParams)(nil), // 34: v1.GetGuildPetitionParams + (*GetGuildPetitionResponse)(nil), // 35: v1.GetGuildPetitionResponse + (*OfferGuildPetitionParams)(nil), // 36: v1.OfferGuildPetitionParams + (*OfferGuildPetitionResponse)(nil), // 37: v1.OfferGuildPetitionResponse + (*SignGuildPetitionParams)(nil), // 38: v1.SignGuildPetitionParams + (*SignGuildPetitionResponse)(nil), // 39: v1.SignGuildPetitionResponse + (*GuildBankSocketEnchant)(nil), // 40: v1.GuildBankSocketEnchant + (*GuildBankItem)(nil), // 41: v1.GuildBankItem + (*GuildBankTab)(nil), // 42: v1.GuildBankTab + (*GuildBankStatus)(nil), // 43: v1.GuildBankStatus + (*GetGuildBankParams)(nil), // 44: v1.GetGuildBankParams + (*GetGuildBankResponse)(nil), // 45: v1.GetGuildBankResponse + (*GetGuildBankLogParams)(nil), // 46: v1.GetGuildBankLogParams + (*GuildBankLogEntry)(nil), // 47: v1.GuildBankLogEntry + (*GetGuildBankLogResponse)(nil), // 48: v1.GetGuildBankLogResponse + (*GetGuildBankTabTextParams)(nil), // 49: v1.GetGuildBankTabTextParams + (*GetGuildBankTabTextResponse)(nil), // 50: v1.GetGuildBankTabTextResponse + (*UpdateGuildBankTabParams)(nil), // 51: v1.UpdateGuildBankTabParams + (*SetGuildBankTabTextParams)(nil), // 52: v1.SetGuildBankTabTextParams + (*BuyGuildBankTabParams)(nil), // 53: v1.BuyGuildBankTabParams + (*BuyGuildBankTabResponse)(nil), // 54: v1.BuyGuildBankTabResponse + (*DepositGuildBankMoneyParams)(nil), // 55: v1.DepositGuildBankMoneyParams + (*WithdrawGuildBankMoneyParams)(nil), // 56: v1.WithdrawGuildBankMoneyParams + (*RollbackGuildBankMoneyWithdrawParams)(nil), // 57: v1.RollbackGuildBankMoneyWithdrawParams + (*DepositGuildBankItemParams)(nil), // 58: v1.DepositGuildBankItemParams + (*WithdrawGuildBankItemParams)(nil), // 59: v1.WithdrawGuildBankItemParams + (*RollbackGuildBankItemWithdrawParams)(nil), // 60: v1.RollbackGuildBankItemWithdrawParams + (*MoveGuildBankItemParams)(nil), // 61: v1.MoveGuildBankItemParams + (*GuildBankActionResponse)(nil), // 62: v1.GuildBankActionResponse + (*GuildBankItemMutationResponse)(nil), // 63: v1.GuildBankItemMutationResponse + (*GetRosterInfoResponse_Member)(nil), // 64: v1.GetRosterInfoResponse.Member + (*GetRosterInfoResponse_Rank)(nil), // 65: v1.GetRosterInfoResponse.Rank + (*GetRosterInfoResponse_Guild)(nil), // 66: v1.GetRosterInfoResponse.Guild + (*GetRosterInfoResponse_Rank_BankTabRight)(nil), // 67: v1.GetRosterInfoResponse.Rank.BankTabRight + (*InviteAcceptedParams_Character)(nil), // 68: v1.InviteAcceptedParams.Character + (*RankUpdateParams_BankTabRight)(nil), // 69: v1.RankUpdateParams.BankTabRight } var file_guilds_proto_depIdxs = []int32{ - 31, // 0: v1.GetRosterInfoResponse.guild:type_name -> v1.GetRosterInfoResponse.Guild - 32, // 1: v1.InviteAcceptedParams.character:type_name -> v1.InviteAcceptedParams.Character - 29, // 2: v1.GetRosterInfoResponse.Guild.members:type_name -> v1.GetRosterInfoResponse.Member - 30, // 3: v1.GetRosterInfoResponse.Guild.ranks:type_name -> v1.GetRosterInfoResponse.Rank - 1, // 4: v1.GuildService.GetGuildInfo:input_type -> v1.GetInfoParams - 3, // 5: v1.GuildService.GetRosterInfo:input_type -> v1.GetRosterInfoParams - 5, // 6: v1.GuildService.InviteMember:input_type -> v1.InviteMemberParams - 7, // 7: v1.GuildService.InviteAccepted:input_type -> v1.InviteAcceptedParams - 9, // 8: v1.GuildService.Leave:input_type -> v1.LeaveParams - 11, // 9: v1.GuildService.Kick:input_type -> v1.KickParams - 13, // 10: v1.GuildService.SetMessageOfTheDay:input_type -> v1.SetMessageOfTheDayParams - 15, // 11: v1.GuildService.SetGuildInfo:input_type -> v1.SetGuildInfoParams - 17, // 12: v1.GuildService.SetMemberPublicNote:input_type -> v1.SetNoteParams - 17, // 13: v1.GuildService.SetMemberOfficerNote:input_type -> v1.SetNoteParams - 19, // 14: v1.GuildService.UpdateRank:input_type -> v1.RankUpdateParams - 21, // 15: v1.GuildService.AddRank:input_type -> v1.AddRankParams - 23, // 16: v1.GuildService.DeleteLastRank:input_type -> v1.DeleteLastRankParams - 25, // 17: v1.GuildService.PromoteMember:input_type -> v1.PromoteDemoteParams - 25, // 18: v1.GuildService.DemoteMember:input_type -> v1.PromoteDemoteParams - 27, // 19: v1.GuildService.SendGuildMessage:input_type -> v1.SendGuildMessageParams - 2, // 20: v1.GuildService.GetGuildInfo:output_type -> v1.GetInfoResponse - 4, // 21: v1.GuildService.GetRosterInfo:output_type -> v1.GetRosterInfoResponse - 6, // 22: v1.GuildService.InviteMember:output_type -> v1.InviteMemberResponse - 8, // 23: v1.GuildService.InviteAccepted:output_type -> v1.InviteAcceptedResponse - 10, // 24: v1.GuildService.Leave:output_type -> v1.LeaveResponse - 12, // 25: v1.GuildService.Kick:output_type -> v1.KickResponse - 14, // 26: v1.GuildService.SetMessageOfTheDay:output_type -> v1.SetMessageOfTheDayResponse - 16, // 27: v1.GuildService.SetGuildInfo:output_type -> v1.SetGuildInfoResponse - 18, // 28: v1.GuildService.SetMemberPublicNote:output_type -> v1.SetNoteResponse - 18, // 29: v1.GuildService.SetMemberOfficerNote:output_type -> v1.SetNoteResponse - 20, // 30: v1.GuildService.UpdateRank:output_type -> v1.RankUpdateResponse - 22, // 31: v1.GuildService.AddRank:output_type -> v1.AddRankResponse - 24, // 32: v1.GuildService.DeleteLastRank:output_type -> v1.DeleteLastRankResponse - 26, // 33: v1.GuildService.PromoteMember:output_type -> v1.PromoteDemoteResponse - 26, // 34: v1.GuildService.DemoteMember:output_type -> v1.PromoteDemoteResponse - 28, // 35: v1.GuildService.SendGuildMessage:output_type -> v1.SendGuildMessageResponse - 20, // [20:36] is the sub-list for method output_type - 4, // [4:20] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 66, // 0: v1.GetRosterInfoResponse.guild:type_name -> v1.GetRosterInfoResponse.Guild + 68, // 1: v1.InviteAcceptedParams.character:type_name -> v1.InviteAcceptedParams.Character + 69, // 2: v1.RankUpdateParams.bankTabRights:type_name -> v1.RankUpdateParams.BankTabRight + 32, // 3: v1.GuildPetition.signatures:type_name -> v1.GuildPetitionSignature + 33, // 4: v1.GetGuildPetitionResponse.petition:type_name -> v1.GuildPetition + 1, // 5: v1.OfferGuildPetitionResponse.status:type_name -> v1.OfferGuildPetitionResponse.Status + 2, // 6: v1.SignGuildPetitionResponse.status:type_name -> v1.SignGuildPetitionResponse.Status + 40, // 7: v1.GuildBankItem.socketEnchants:type_name -> v1.GuildBankSocketEnchant + 41, // 8: v1.GuildBankTab.items:type_name -> v1.GuildBankItem + 3, // 9: v1.GetGuildBankResponse.status:type_name -> v1.GuildBankStatus.Status + 42, // 10: v1.GetGuildBankResponse.tabs:type_name -> v1.GuildBankTab + 41, // 11: v1.GetGuildBankResponse.items:type_name -> v1.GuildBankItem + 3, // 12: v1.GetGuildBankLogResponse.status:type_name -> v1.GuildBankStatus.Status + 47, // 13: v1.GetGuildBankLogResponse.entries:type_name -> v1.GuildBankLogEntry + 3, // 14: v1.GetGuildBankTabTextResponse.status:type_name -> v1.GuildBankStatus.Status + 3, // 15: v1.BuyGuildBankTabResponse.status:type_name -> v1.GuildBankStatus.Status + 41, // 16: v1.DepositGuildBankItemParams.item:type_name -> v1.GuildBankItem + 41, // 17: v1.RollbackGuildBankItemWithdrawParams.item:type_name -> v1.GuildBankItem + 3, // 18: v1.GuildBankActionResponse.status:type_name -> v1.GuildBankStatus.Status + 3, // 19: v1.GuildBankItemMutationResponse.status:type_name -> v1.GuildBankStatus.Status + 41, // 20: v1.GuildBankItemMutationResponse.item:type_name -> v1.GuildBankItem + 67, // 21: v1.GetRosterInfoResponse.Rank.bankTabRights:type_name -> v1.GetRosterInfoResponse.Rank.BankTabRight + 64, // 22: v1.GetRosterInfoResponse.Guild.members:type_name -> v1.GetRosterInfoResponse.Member + 65, // 23: v1.GetRosterInfoResponse.Guild.ranks:type_name -> v1.GetRosterInfoResponse.Rank + 4, // 24: v1.GuildService.GetGuildInfo:input_type -> v1.GetInfoParams + 6, // 25: v1.GuildService.GetRosterInfo:input_type -> v1.GetRosterInfoParams + 8, // 26: v1.GuildService.InviteMember:input_type -> v1.InviteMemberParams + 10, // 27: v1.GuildService.InviteAccepted:input_type -> v1.InviteAcceptedParams + 12, // 28: v1.GuildService.Leave:input_type -> v1.LeaveParams + 14, // 29: v1.GuildService.Kick:input_type -> v1.KickParams + 16, // 30: v1.GuildService.SetMessageOfTheDay:input_type -> v1.SetMessageOfTheDayParams + 18, // 31: v1.GuildService.SetGuildInfo:input_type -> v1.SetGuildInfoParams + 20, // 32: v1.GuildService.SetMemberPublicNote:input_type -> v1.SetNoteParams + 20, // 33: v1.GuildService.SetMemberOfficerNote:input_type -> v1.SetNoteParams + 22, // 34: v1.GuildService.UpdateRank:input_type -> v1.RankUpdateParams + 24, // 35: v1.GuildService.AddRank:input_type -> v1.AddRankParams + 26, // 36: v1.GuildService.DeleteLastRank:input_type -> v1.DeleteLastRankParams + 28, // 37: v1.GuildService.PromoteMember:input_type -> v1.PromoteDemoteParams + 28, // 38: v1.GuildService.DemoteMember:input_type -> v1.PromoteDemoteParams + 30, // 39: v1.GuildService.SendGuildMessage:input_type -> v1.SendGuildMessageParams + 34, // 40: v1.GuildService.GetGuildPetition:input_type -> v1.GetGuildPetitionParams + 36, // 41: v1.GuildService.OfferGuildPetition:input_type -> v1.OfferGuildPetitionParams + 38, // 42: v1.GuildService.SignGuildPetition:input_type -> v1.SignGuildPetitionParams + 44, // 43: v1.GuildService.GetGuildBank:input_type -> v1.GetGuildBankParams + 46, // 44: v1.GuildService.GetGuildBankLog:input_type -> v1.GetGuildBankLogParams + 49, // 45: v1.GuildService.GetGuildBankTabText:input_type -> v1.GetGuildBankTabTextParams + 51, // 46: v1.GuildService.UpdateGuildBankTab:input_type -> v1.UpdateGuildBankTabParams + 52, // 47: v1.GuildService.SetGuildBankTabText:input_type -> v1.SetGuildBankTabTextParams + 53, // 48: v1.GuildService.BuyGuildBankTab:input_type -> v1.BuyGuildBankTabParams + 55, // 49: v1.GuildService.DepositGuildBankMoney:input_type -> v1.DepositGuildBankMoneyParams + 56, // 50: v1.GuildService.WithdrawGuildBankMoney:input_type -> v1.WithdrawGuildBankMoneyParams + 57, // 51: v1.GuildService.RollbackGuildBankMoneyWithdraw:input_type -> v1.RollbackGuildBankMoneyWithdrawParams + 58, // 52: v1.GuildService.DepositGuildBankItem:input_type -> v1.DepositGuildBankItemParams + 59, // 53: v1.GuildService.WithdrawGuildBankItem:input_type -> v1.WithdrawGuildBankItemParams + 60, // 54: v1.GuildService.RollbackGuildBankItemWithdraw:input_type -> v1.RollbackGuildBankItemWithdrawParams + 61, // 55: v1.GuildService.MoveGuildBankItem:input_type -> v1.MoveGuildBankItemParams + 5, // 56: v1.GuildService.GetGuildInfo:output_type -> v1.GetInfoResponse + 7, // 57: v1.GuildService.GetRosterInfo:output_type -> v1.GetRosterInfoResponse + 9, // 58: v1.GuildService.InviteMember:output_type -> v1.InviteMemberResponse + 11, // 59: v1.GuildService.InviteAccepted:output_type -> v1.InviteAcceptedResponse + 13, // 60: v1.GuildService.Leave:output_type -> v1.LeaveResponse + 15, // 61: v1.GuildService.Kick:output_type -> v1.KickResponse + 17, // 62: v1.GuildService.SetMessageOfTheDay:output_type -> v1.SetMessageOfTheDayResponse + 19, // 63: v1.GuildService.SetGuildInfo:output_type -> v1.SetGuildInfoResponse + 21, // 64: v1.GuildService.SetMemberPublicNote:output_type -> v1.SetNoteResponse + 21, // 65: v1.GuildService.SetMemberOfficerNote:output_type -> v1.SetNoteResponse + 23, // 66: v1.GuildService.UpdateRank:output_type -> v1.RankUpdateResponse + 25, // 67: v1.GuildService.AddRank:output_type -> v1.AddRankResponse + 27, // 68: v1.GuildService.DeleteLastRank:output_type -> v1.DeleteLastRankResponse + 29, // 69: v1.GuildService.PromoteMember:output_type -> v1.PromoteDemoteResponse + 29, // 70: v1.GuildService.DemoteMember:output_type -> v1.PromoteDemoteResponse + 31, // 71: v1.GuildService.SendGuildMessage:output_type -> v1.SendGuildMessageResponse + 35, // 72: v1.GuildService.GetGuildPetition:output_type -> v1.GetGuildPetitionResponse + 37, // 73: v1.GuildService.OfferGuildPetition:output_type -> v1.OfferGuildPetitionResponse + 39, // 74: v1.GuildService.SignGuildPetition:output_type -> v1.SignGuildPetitionResponse + 45, // 75: v1.GuildService.GetGuildBank:output_type -> v1.GetGuildBankResponse + 48, // 76: v1.GuildService.GetGuildBankLog:output_type -> v1.GetGuildBankLogResponse + 50, // 77: v1.GuildService.GetGuildBankTabText:output_type -> v1.GetGuildBankTabTextResponse + 62, // 78: v1.GuildService.UpdateGuildBankTab:output_type -> v1.GuildBankActionResponse + 62, // 79: v1.GuildService.SetGuildBankTabText:output_type -> v1.GuildBankActionResponse + 54, // 80: v1.GuildService.BuyGuildBankTab:output_type -> v1.BuyGuildBankTabResponse + 62, // 81: v1.GuildService.DepositGuildBankMoney:output_type -> v1.GuildBankActionResponse + 62, // 82: v1.GuildService.WithdrawGuildBankMoney:output_type -> v1.GuildBankActionResponse + 62, // 83: v1.GuildService.RollbackGuildBankMoneyWithdraw:output_type -> v1.GuildBankActionResponse + 63, // 84: v1.GuildService.DepositGuildBankItem:output_type -> v1.GuildBankItemMutationResponse + 63, // 85: v1.GuildService.WithdrawGuildBankItem:output_type -> v1.GuildBankItemMutationResponse + 63, // 86: v1.GuildService.RollbackGuildBankItemWithdraw:output_type -> v1.GuildBankItemMutationResponse + 63, // 87: v1.GuildService.MoveGuildBankItem:output_type -> v1.GuildBankItemMutationResponse + 56, // [56:88] is the sub-list for method output_type + 24, // [24:56] is the sub-list for method input_type + 24, // [24:24] is the sub-list for extension type_name + 24, // [24:24] is the sub-list for extension extendee + 0, // [0:24] is the sub-list for field type_name } func init() { file_guilds_proto_init() } @@ -2591,8 +6155,212 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetInfoResponse); i { + file_guilds_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetInfoResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetRosterInfoParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetRosterInfoResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InviteMemberParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InviteMemberResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InviteAcceptedParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InviteAcceptedResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LeaveParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LeaveResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*KickParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*KickResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetMessageOfTheDayParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetMessageOfTheDayResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetGuildInfoParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetGuildInfoResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetNoteParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetNoteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RankUpdateParams); i { case 0: return &v.state case 1: @@ -2603,8 +6371,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRosterInfoParams); i { + file_guilds_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RankUpdateResponse); i { case 0: return &v.state case 1: @@ -2615,8 +6383,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRosterInfoResponse); i { + file_guilds_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddRankParams); i { case 0: return &v.state case 1: @@ -2627,8 +6395,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InviteMemberParams); i { + file_guilds_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddRankResponse); i { case 0: return &v.state case 1: @@ -2639,8 +6407,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InviteMemberResponse); i { + file_guilds_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteLastRankParams); i { case 0: return &v.state case 1: @@ -2651,8 +6419,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InviteAcceptedParams); i { + file_guilds_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteLastRankResponse); i { case 0: return &v.state case 1: @@ -2663,8 +6431,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InviteAcceptedResponse); i { + file_guilds_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PromoteDemoteParams); i { case 0: return &v.state case 1: @@ -2675,8 +6443,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LeaveParams); i { + file_guilds_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PromoteDemoteResponse); i { case 0: return &v.state case 1: @@ -2687,8 +6455,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LeaveResponse); i { + file_guilds_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendGuildMessageParams); i { case 0: return &v.state case 1: @@ -2699,8 +6467,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*KickParams); i { + file_guilds_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendGuildMessageResponse); i { case 0: return &v.state case 1: @@ -2711,8 +6479,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*KickResponse); i { + file_guilds_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GuildPetitionSignature); i { case 0: return &v.state case 1: @@ -2723,8 +6491,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetMessageOfTheDayParams); i { + file_guilds_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GuildPetition); i { case 0: return &v.state case 1: @@ -2735,8 +6503,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetMessageOfTheDayResponse); i { + file_guilds_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGuildPetitionParams); i { case 0: return &v.state case 1: @@ -2747,8 +6515,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetGuildInfoParams); i { + file_guilds_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGuildPetitionResponse); i { case 0: return &v.state case 1: @@ -2759,8 +6527,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetGuildInfoResponse); i { + file_guilds_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OfferGuildPetitionParams); i { case 0: return &v.state case 1: @@ -2771,8 +6539,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetNoteParams); i { + file_guilds_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OfferGuildPetitionResponse); i { case 0: return &v.state case 1: @@ -2783,8 +6551,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetNoteResponse); i { + file_guilds_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SignGuildPetitionParams); i { case 0: return &v.state case 1: @@ -2795,8 +6563,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RankUpdateParams); i { + file_guilds_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SignGuildPetitionResponse); i { case 0: return &v.state case 1: @@ -2807,8 +6575,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RankUpdateResponse); i { + file_guilds_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GuildBankSocketEnchant); i { case 0: return &v.state case 1: @@ -2819,8 +6587,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddRankParams); i { + file_guilds_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GuildBankItem); i { case 0: return &v.state case 1: @@ -2831,8 +6599,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddRankResponse); i { + file_guilds_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GuildBankTab); i { case 0: return &v.state case 1: @@ -2843,8 +6611,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteLastRankParams); i { + file_guilds_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GuildBankStatus); i { case 0: return &v.state case 1: @@ -2855,8 +6623,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteLastRankResponse); i { + file_guilds_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGuildBankParams); i { case 0: return &v.state case 1: @@ -2867,8 +6635,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PromoteDemoteParams); i { + file_guilds_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGuildBankResponse); i { case 0: return &v.state case 1: @@ -2879,8 +6647,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PromoteDemoteResponse); i { + file_guilds_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGuildBankLogParams); i { case 0: return &v.state case 1: @@ -2891,8 +6659,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendGuildMessageParams); i { + file_guilds_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GuildBankLogEntry); i { case 0: return &v.state case 1: @@ -2903,8 +6671,8 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendGuildMessageResponse); i { + file_guilds_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGuildBankLogResponse); i { case 0: return &v.state case 1: @@ -2915,7 +6683,187 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_guilds_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGuildBankTabTextParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGuildBankTabTextResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateGuildBankTabParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetGuildBankTabTextParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BuyGuildBankTabParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BuyGuildBankTabResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DepositGuildBankMoneyParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WithdrawGuildBankMoneyParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RollbackGuildBankMoneyWithdrawParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DepositGuildBankItemParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WithdrawGuildBankItemParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RollbackGuildBankItemWithdrawParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MoveGuildBankItemParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GuildBankActionResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GuildBankItemMutationResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetRosterInfoResponse_Member); i { case 0: return &v.state @@ -2927,7 +6875,7 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_guilds_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetRosterInfoResponse_Rank); i { case 0: return &v.state @@ -2939,7 +6887,7 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_guilds_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetRosterInfoResponse_Guild); i { case 0: return &v.state @@ -2951,7 +6899,19 @@ func file_guilds_proto_init() { return nil } } - file_guilds_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_guilds_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetRosterInfoResponse_Rank_BankTabRight); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_guilds_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*InviteAcceptedParams_Character); i { case 0: return &v.state @@ -2963,14 +6923,26 @@ func file_guilds_proto_init() { return nil } } + file_guilds_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RankUpdateParams_BankTabRight); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_guilds_proto_rawDesc, - NumEnums: 1, - NumMessages: 32, + NumEnums: 4, + NumMessages: 66, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/guilds/pb/guilds_grpc.pb.go b/gen/guilds/pb/guilds_grpc.pb.go index 98ef71c..066506c 100644 --- a/gen/guilds/pb/guilds_grpc.pb.go +++ b/gen/guilds/pb/guilds_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v3.20.3 +// - protoc v3.21.12 // source: guilds.proto package pb @@ -19,22 +19,38 @@ import ( const _ = grpc.SupportPackageIsVersion7 const ( - GuildService_GetGuildInfo_FullMethodName = "/v1.GuildService/GetGuildInfo" - GuildService_GetRosterInfo_FullMethodName = "/v1.GuildService/GetRosterInfo" - GuildService_InviteMember_FullMethodName = "/v1.GuildService/InviteMember" - GuildService_InviteAccepted_FullMethodName = "/v1.GuildService/InviteAccepted" - GuildService_Leave_FullMethodName = "/v1.GuildService/Leave" - GuildService_Kick_FullMethodName = "/v1.GuildService/Kick" - GuildService_SetMessageOfTheDay_FullMethodName = "/v1.GuildService/SetMessageOfTheDay" - GuildService_SetGuildInfo_FullMethodName = "/v1.GuildService/SetGuildInfo" - GuildService_SetMemberPublicNote_FullMethodName = "/v1.GuildService/SetMemberPublicNote" - GuildService_SetMemberOfficerNote_FullMethodName = "/v1.GuildService/SetMemberOfficerNote" - GuildService_UpdateRank_FullMethodName = "/v1.GuildService/UpdateRank" - GuildService_AddRank_FullMethodName = "/v1.GuildService/AddRank" - GuildService_DeleteLastRank_FullMethodName = "/v1.GuildService/DeleteLastRank" - GuildService_PromoteMember_FullMethodName = "/v1.GuildService/PromoteMember" - GuildService_DemoteMember_FullMethodName = "/v1.GuildService/DemoteMember" - GuildService_SendGuildMessage_FullMethodName = "/v1.GuildService/SendGuildMessage" + GuildService_GetGuildInfo_FullMethodName = "/v1.GuildService/GetGuildInfo" + GuildService_GetRosterInfo_FullMethodName = "/v1.GuildService/GetRosterInfo" + GuildService_InviteMember_FullMethodName = "/v1.GuildService/InviteMember" + GuildService_InviteAccepted_FullMethodName = "/v1.GuildService/InviteAccepted" + GuildService_Leave_FullMethodName = "/v1.GuildService/Leave" + GuildService_Kick_FullMethodName = "/v1.GuildService/Kick" + GuildService_SetMessageOfTheDay_FullMethodName = "/v1.GuildService/SetMessageOfTheDay" + GuildService_SetGuildInfo_FullMethodName = "/v1.GuildService/SetGuildInfo" + GuildService_SetMemberPublicNote_FullMethodName = "/v1.GuildService/SetMemberPublicNote" + GuildService_SetMemberOfficerNote_FullMethodName = "/v1.GuildService/SetMemberOfficerNote" + GuildService_UpdateRank_FullMethodName = "/v1.GuildService/UpdateRank" + GuildService_AddRank_FullMethodName = "/v1.GuildService/AddRank" + GuildService_DeleteLastRank_FullMethodName = "/v1.GuildService/DeleteLastRank" + GuildService_PromoteMember_FullMethodName = "/v1.GuildService/PromoteMember" + GuildService_DemoteMember_FullMethodName = "/v1.GuildService/DemoteMember" + GuildService_SendGuildMessage_FullMethodName = "/v1.GuildService/SendGuildMessage" + GuildService_GetGuildPetition_FullMethodName = "/v1.GuildService/GetGuildPetition" + GuildService_OfferGuildPetition_FullMethodName = "/v1.GuildService/OfferGuildPetition" + GuildService_SignGuildPetition_FullMethodName = "/v1.GuildService/SignGuildPetition" + GuildService_GetGuildBank_FullMethodName = "/v1.GuildService/GetGuildBank" + GuildService_GetGuildBankLog_FullMethodName = "/v1.GuildService/GetGuildBankLog" + GuildService_GetGuildBankTabText_FullMethodName = "/v1.GuildService/GetGuildBankTabText" + GuildService_UpdateGuildBankTab_FullMethodName = "/v1.GuildService/UpdateGuildBankTab" + GuildService_SetGuildBankTabText_FullMethodName = "/v1.GuildService/SetGuildBankTabText" + GuildService_BuyGuildBankTab_FullMethodName = "/v1.GuildService/BuyGuildBankTab" + GuildService_DepositGuildBankMoney_FullMethodName = "/v1.GuildService/DepositGuildBankMoney" + GuildService_WithdrawGuildBankMoney_FullMethodName = "/v1.GuildService/WithdrawGuildBankMoney" + GuildService_RollbackGuildBankMoneyWithdraw_FullMethodName = "/v1.GuildService/RollbackGuildBankMoneyWithdraw" + GuildService_DepositGuildBankItem_FullMethodName = "/v1.GuildService/DepositGuildBankItem" + GuildService_WithdrawGuildBankItem_FullMethodName = "/v1.GuildService/WithdrawGuildBankItem" + GuildService_RollbackGuildBankItemWithdraw_FullMethodName = "/v1.GuildService/RollbackGuildBankItemWithdraw" + GuildService_MoveGuildBankItem_FullMethodName = "/v1.GuildService/MoveGuildBankItem" ) // GuildServiceClient is the client API for GuildService service. @@ -57,6 +73,22 @@ type GuildServiceClient interface { PromoteMember(ctx context.Context, in *PromoteDemoteParams, opts ...grpc.CallOption) (*PromoteDemoteResponse, error) DemoteMember(ctx context.Context, in *PromoteDemoteParams, opts ...grpc.CallOption) (*PromoteDemoteResponse, error) SendGuildMessage(ctx context.Context, in *SendGuildMessageParams, opts ...grpc.CallOption) (*SendGuildMessageResponse, error) + GetGuildPetition(ctx context.Context, in *GetGuildPetitionParams, opts ...grpc.CallOption) (*GetGuildPetitionResponse, error) + OfferGuildPetition(ctx context.Context, in *OfferGuildPetitionParams, opts ...grpc.CallOption) (*OfferGuildPetitionResponse, error) + SignGuildPetition(ctx context.Context, in *SignGuildPetitionParams, opts ...grpc.CallOption) (*SignGuildPetitionResponse, error) + GetGuildBank(ctx context.Context, in *GetGuildBankParams, opts ...grpc.CallOption) (*GetGuildBankResponse, error) + GetGuildBankLog(ctx context.Context, in *GetGuildBankLogParams, opts ...grpc.CallOption) (*GetGuildBankLogResponse, error) + GetGuildBankTabText(ctx context.Context, in *GetGuildBankTabTextParams, opts ...grpc.CallOption) (*GetGuildBankTabTextResponse, error) + UpdateGuildBankTab(ctx context.Context, in *UpdateGuildBankTabParams, opts ...grpc.CallOption) (*GuildBankActionResponse, error) + SetGuildBankTabText(ctx context.Context, in *SetGuildBankTabTextParams, opts ...grpc.CallOption) (*GuildBankActionResponse, error) + BuyGuildBankTab(ctx context.Context, in *BuyGuildBankTabParams, opts ...grpc.CallOption) (*BuyGuildBankTabResponse, error) + DepositGuildBankMoney(ctx context.Context, in *DepositGuildBankMoneyParams, opts ...grpc.CallOption) (*GuildBankActionResponse, error) + WithdrawGuildBankMoney(ctx context.Context, in *WithdrawGuildBankMoneyParams, opts ...grpc.CallOption) (*GuildBankActionResponse, error) + RollbackGuildBankMoneyWithdraw(ctx context.Context, in *RollbackGuildBankMoneyWithdrawParams, opts ...grpc.CallOption) (*GuildBankActionResponse, error) + DepositGuildBankItem(ctx context.Context, in *DepositGuildBankItemParams, opts ...grpc.CallOption) (*GuildBankItemMutationResponse, error) + WithdrawGuildBankItem(ctx context.Context, in *WithdrawGuildBankItemParams, opts ...grpc.CallOption) (*GuildBankItemMutationResponse, error) + RollbackGuildBankItemWithdraw(ctx context.Context, in *RollbackGuildBankItemWithdrawParams, opts ...grpc.CallOption) (*GuildBankItemMutationResponse, error) + MoveGuildBankItem(ctx context.Context, in *MoveGuildBankItemParams, opts ...grpc.CallOption) (*GuildBankItemMutationResponse, error) } type guildServiceClient struct { @@ -211,6 +243,150 @@ func (c *guildServiceClient) SendGuildMessage(ctx context.Context, in *SendGuild return out, nil } +func (c *guildServiceClient) GetGuildPetition(ctx context.Context, in *GetGuildPetitionParams, opts ...grpc.CallOption) (*GetGuildPetitionResponse, error) { + out := new(GetGuildPetitionResponse) + err := c.cc.Invoke(ctx, GuildService_GetGuildPetition_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) OfferGuildPetition(ctx context.Context, in *OfferGuildPetitionParams, opts ...grpc.CallOption) (*OfferGuildPetitionResponse, error) { + out := new(OfferGuildPetitionResponse) + err := c.cc.Invoke(ctx, GuildService_OfferGuildPetition_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) SignGuildPetition(ctx context.Context, in *SignGuildPetitionParams, opts ...grpc.CallOption) (*SignGuildPetitionResponse, error) { + out := new(SignGuildPetitionResponse) + err := c.cc.Invoke(ctx, GuildService_SignGuildPetition_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) GetGuildBank(ctx context.Context, in *GetGuildBankParams, opts ...grpc.CallOption) (*GetGuildBankResponse, error) { + out := new(GetGuildBankResponse) + err := c.cc.Invoke(ctx, GuildService_GetGuildBank_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) GetGuildBankLog(ctx context.Context, in *GetGuildBankLogParams, opts ...grpc.CallOption) (*GetGuildBankLogResponse, error) { + out := new(GetGuildBankLogResponse) + err := c.cc.Invoke(ctx, GuildService_GetGuildBankLog_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) GetGuildBankTabText(ctx context.Context, in *GetGuildBankTabTextParams, opts ...grpc.CallOption) (*GetGuildBankTabTextResponse, error) { + out := new(GetGuildBankTabTextResponse) + err := c.cc.Invoke(ctx, GuildService_GetGuildBankTabText_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) UpdateGuildBankTab(ctx context.Context, in *UpdateGuildBankTabParams, opts ...grpc.CallOption) (*GuildBankActionResponse, error) { + out := new(GuildBankActionResponse) + err := c.cc.Invoke(ctx, GuildService_UpdateGuildBankTab_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) SetGuildBankTabText(ctx context.Context, in *SetGuildBankTabTextParams, opts ...grpc.CallOption) (*GuildBankActionResponse, error) { + out := new(GuildBankActionResponse) + err := c.cc.Invoke(ctx, GuildService_SetGuildBankTabText_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) BuyGuildBankTab(ctx context.Context, in *BuyGuildBankTabParams, opts ...grpc.CallOption) (*BuyGuildBankTabResponse, error) { + out := new(BuyGuildBankTabResponse) + err := c.cc.Invoke(ctx, GuildService_BuyGuildBankTab_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) DepositGuildBankMoney(ctx context.Context, in *DepositGuildBankMoneyParams, opts ...grpc.CallOption) (*GuildBankActionResponse, error) { + out := new(GuildBankActionResponse) + err := c.cc.Invoke(ctx, GuildService_DepositGuildBankMoney_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) WithdrawGuildBankMoney(ctx context.Context, in *WithdrawGuildBankMoneyParams, opts ...grpc.CallOption) (*GuildBankActionResponse, error) { + out := new(GuildBankActionResponse) + err := c.cc.Invoke(ctx, GuildService_WithdrawGuildBankMoney_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) RollbackGuildBankMoneyWithdraw(ctx context.Context, in *RollbackGuildBankMoneyWithdrawParams, opts ...grpc.CallOption) (*GuildBankActionResponse, error) { + out := new(GuildBankActionResponse) + err := c.cc.Invoke(ctx, GuildService_RollbackGuildBankMoneyWithdraw_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) DepositGuildBankItem(ctx context.Context, in *DepositGuildBankItemParams, opts ...grpc.CallOption) (*GuildBankItemMutationResponse, error) { + out := new(GuildBankItemMutationResponse) + err := c.cc.Invoke(ctx, GuildService_DepositGuildBankItem_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) WithdrawGuildBankItem(ctx context.Context, in *WithdrawGuildBankItemParams, opts ...grpc.CallOption) (*GuildBankItemMutationResponse, error) { + out := new(GuildBankItemMutationResponse) + err := c.cc.Invoke(ctx, GuildService_WithdrawGuildBankItem_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) RollbackGuildBankItemWithdraw(ctx context.Context, in *RollbackGuildBankItemWithdrawParams, opts ...grpc.CallOption) (*GuildBankItemMutationResponse, error) { + out := new(GuildBankItemMutationResponse) + err := c.cc.Invoke(ctx, GuildService_RollbackGuildBankItemWithdraw_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *guildServiceClient) MoveGuildBankItem(ctx context.Context, in *MoveGuildBankItemParams, opts ...grpc.CallOption) (*GuildBankItemMutationResponse, error) { + out := new(GuildBankItemMutationResponse) + err := c.cc.Invoke(ctx, GuildService_MoveGuildBankItem_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // GuildServiceServer is the server API for GuildService service. // All implementations must embed UnimplementedGuildServiceServer // for forward compatibility @@ -231,6 +407,22 @@ type GuildServiceServer interface { PromoteMember(context.Context, *PromoteDemoteParams) (*PromoteDemoteResponse, error) DemoteMember(context.Context, *PromoteDemoteParams) (*PromoteDemoteResponse, error) SendGuildMessage(context.Context, *SendGuildMessageParams) (*SendGuildMessageResponse, error) + GetGuildPetition(context.Context, *GetGuildPetitionParams) (*GetGuildPetitionResponse, error) + OfferGuildPetition(context.Context, *OfferGuildPetitionParams) (*OfferGuildPetitionResponse, error) + SignGuildPetition(context.Context, *SignGuildPetitionParams) (*SignGuildPetitionResponse, error) + GetGuildBank(context.Context, *GetGuildBankParams) (*GetGuildBankResponse, error) + GetGuildBankLog(context.Context, *GetGuildBankLogParams) (*GetGuildBankLogResponse, error) + GetGuildBankTabText(context.Context, *GetGuildBankTabTextParams) (*GetGuildBankTabTextResponse, error) + UpdateGuildBankTab(context.Context, *UpdateGuildBankTabParams) (*GuildBankActionResponse, error) + SetGuildBankTabText(context.Context, *SetGuildBankTabTextParams) (*GuildBankActionResponse, error) + BuyGuildBankTab(context.Context, *BuyGuildBankTabParams) (*BuyGuildBankTabResponse, error) + DepositGuildBankMoney(context.Context, *DepositGuildBankMoneyParams) (*GuildBankActionResponse, error) + WithdrawGuildBankMoney(context.Context, *WithdrawGuildBankMoneyParams) (*GuildBankActionResponse, error) + RollbackGuildBankMoneyWithdraw(context.Context, *RollbackGuildBankMoneyWithdrawParams) (*GuildBankActionResponse, error) + DepositGuildBankItem(context.Context, *DepositGuildBankItemParams) (*GuildBankItemMutationResponse, error) + WithdrawGuildBankItem(context.Context, *WithdrawGuildBankItemParams) (*GuildBankItemMutationResponse, error) + RollbackGuildBankItemWithdraw(context.Context, *RollbackGuildBankItemWithdrawParams) (*GuildBankItemMutationResponse, error) + MoveGuildBankItem(context.Context, *MoveGuildBankItemParams) (*GuildBankItemMutationResponse, error) mustEmbedUnimplementedGuildServiceServer() } @@ -286,6 +478,54 @@ func (UnimplementedGuildServiceServer) DemoteMember(context.Context, *PromoteDem func (UnimplementedGuildServiceServer) SendGuildMessage(context.Context, *SendGuildMessageParams) (*SendGuildMessageResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SendGuildMessage not implemented") } +func (UnimplementedGuildServiceServer) GetGuildPetition(context.Context, *GetGuildPetitionParams) (*GetGuildPetitionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetGuildPetition not implemented") +} +func (UnimplementedGuildServiceServer) OfferGuildPetition(context.Context, *OfferGuildPetitionParams) (*OfferGuildPetitionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method OfferGuildPetition not implemented") +} +func (UnimplementedGuildServiceServer) SignGuildPetition(context.Context, *SignGuildPetitionParams) (*SignGuildPetitionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SignGuildPetition not implemented") +} +func (UnimplementedGuildServiceServer) GetGuildBank(context.Context, *GetGuildBankParams) (*GetGuildBankResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetGuildBank not implemented") +} +func (UnimplementedGuildServiceServer) GetGuildBankLog(context.Context, *GetGuildBankLogParams) (*GetGuildBankLogResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetGuildBankLog not implemented") +} +func (UnimplementedGuildServiceServer) GetGuildBankTabText(context.Context, *GetGuildBankTabTextParams) (*GetGuildBankTabTextResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetGuildBankTabText not implemented") +} +func (UnimplementedGuildServiceServer) UpdateGuildBankTab(context.Context, *UpdateGuildBankTabParams) (*GuildBankActionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateGuildBankTab not implemented") +} +func (UnimplementedGuildServiceServer) SetGuildBankTabText(context.Context, *SetGuildBankTabTextParams) (*GuildBankActionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetGuildBankTabText not implemented") +} +func (UnimplementedGuildServiceServer) BuyGuildBankTab(context.Context, *BuyGuildBankTabParams) (*BuyGuildBankTabResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BuyGuildBankTab not implemented") +} +func (UnimplementedGuildServiceServer) DepositGuildBankMoney(context.Context, *DepositGuildBankMoneyParams) (*GuildBankActionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DepositGuildBankMoney not implemented") +} +func (UnimplementedGuildServiceServer) WithdrawGuildBankMoney(context.Context, *WithdrawGuildBankMoneyParams) (*GuildBankActionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WithdrawGuildBankMoney not implemented") +} +func (UnimplementedGuildServiceServer) RollbackGuildBankMoneyWithdraw(context.Context, *RollbackGuildBankMoneyWithdrawParams) (*GuildBankActionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RollbackGuildBankMoneyWithdraw not implemented") +} +func (UnimplementedGuildServiceServer) DepositGuildBankItem(context.Context, *DepositGuildBankItemParams) (*GuildBankItemMutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DepositGuildBankItem not implemented") +} +func (UnimplementedGuildServiceServer) WithdrawGuildBankItem(context.Context, *WithdrawGuildBankItemParams) (*GuildBankItemMutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WithdrawGuildBankItem not implemented") +} +func (UnimplementedGuildServiceServer) RollbackGuildBankItemWithdraw(context.Context, *RollbackGuildBankItemWithdrawParams) (*GuildBankItemMutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RollbackGuildBankItemWithdraw not implemented") +} +func (UnimplementedGuildServiceServer) MoveGuildBankItem(context.Context, *MoveGuildBankItemParams) (*GuildBankItemMutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MoveGuildBankItem not implemented") +} func (UnimplementedGuildServiceServer) mustEmbedUnimplementedGuildServiceServer() {} // UnsafeGuildServiceServer may be embedded to opt out of forward compatibility for this service. @@ -587,6 +827,294 @@ func _GuildService_SendGuildMessage_Handler(srv interface{}, ctx context.Context return interceptor(ctx, in, info, handler) } +func _GuildService_GetGuildPetition_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetGuildPetitionParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).GetGuildPetition(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_GetGuildPetition_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).GetGuildPetition(ctx, req.(*GetGuildPetitionParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_OfferGuildPetition_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(OfferGuildPetitionParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).OfferGuildPetition(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_OfferGuildPetition_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).OfferGuildPetition(ctx, req.(*OfferGuildPetitionParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_SignGuildPetition_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignGuildPetitionParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).SignGuildPetition(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_SignGuildPetition_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).SignGuildPetition(ctx, req.(*SignGuildPetitionParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_GetGuildBank_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetGuildBankParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).GetGuildBank(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_GetGuildBank_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).GetGuildBank(ctx, req.(*GetGuildBankParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_GetGuildBankLog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetGuildBankLogParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).GetGuildBankLog(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_GetGuildBankLog_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).GetGuildBankLog(ctx, req.(*GetGuildBankLogParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_GetGuildBankTabText_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetGuildBankTabTextParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).GetGuildBankTabText(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_GetGuildBankTabText_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).GetGuildBankTabText(ctx, req.(*GetGuildBankTabTextParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_UpdateGuildBankTab_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateGuildBankTabParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).UpdateGuildBankTab(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_UpdateGuildBankTab_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).UpdateGuildBankTab(ctx, req.(*UpdateGuildBankTabParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_SetGuildBankTabText_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetGuildBankTabTextParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).SetGuildBankTabText(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_SetGuildBankTabText_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).SetGuildBankTabText(ctx, req.(*SetGuildBankTabTextParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_BuyGuildBankTab_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BuyGuildBankTabParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).BuyGuildBankTab(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_BuyGuildBankTab_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).BuyGuildBankTab(ctx, req.(*BuyGuildBankTabParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_DepositGuildBankMoney_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DepositGuildBankMoneyParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).DepositGuildBankMoney(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_DepositGuildBankMoney_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).DepositGuildBankMoney(ctx, req.(*DepositGuildBankMoneyParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_WithdrawGuildBankMoney_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WithdrawGuildBankMoneyParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).WithdrawGuildBankMoney(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_WithdrawGuildBankMoney_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).WithdrawGuildBankMoney(ctx, req.(*WithdrawGuildBankMoneyParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_RollbackGuildBankMoneyWithdraw_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RollbackGuildBankMoneyWithdrawParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).RollbackGuildBankMoneyWithdraw(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_RollbackGuildBankMoneyWithdraw_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).RollbackGuildBankMoneyWithdraw(ctx, req.(*RollbackGuildBankMoneyWithdrawParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_DepositGuildBankItem_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DepositGuildBankItemParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).DepositGuildBankItem(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_DepositGuildBankItem_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).DepositGuildBankItem(ctx, req.(*DepositGuildBankItemParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_WithdrawGuildBankItem_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WithdrawGuildBankItemParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).WithdrawGuildBankItem(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_WithdrawGuildBankItem_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).WithdrawGuildBankItem(ctx, req.(*WithdrawGuildBankItemParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_RollbackGuildBankItemWithdraw_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RollbackGuildBankItemWithdrawParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).RollbackGuildBankItemWithdraw(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_RollbackGuildBankItemWithdraw_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).RollbackGuildBankItemWithdraw(ctx, req.(*RollbackGuildBankItemWithdrawParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _GuildService_MoveGuildBankItem_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MoveGuildBankItemParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GuildServiceServer).MoveGuildBankItem(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GuildService_MoveGuildBankItem_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GuildServiceServer).MoveGuildBankItem(ctx, req.(*MoveGuildBankItemParams)) + } + return interceptor(ctx, in, info, handler) +} + // GuildService_ServiceDesc is the grpc.ServiceDesc for GuildService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -658,6 +1186,70 @@ var GuildService_ServiceDesc = grpc.ServiceDesc{ MethodName: "SendGuildMessage", Handler: _GuildService_SendGuildMessage_Handler, }, + { + MethodName: "GetGuildPetition", + Handler: _GuildService_GetGuildPetition_Handler, + }, + { + MethodName: "OfferGuildPetition", + Handler: _GuildService_OfferGuildPetition_Handler, + }, + { + MethodName: "SignGuildPetition", + Handler: _GuildService_SignGuildPetition_Handler, + }, + { + MethodName: "GetGuildBank", + Handler: _GuildService_GetGuildBank_Handler, + }, + { + MethodName: "GetGuildBankLog", + Handler: _GuildService_GetGuildBankLog_Handler, + }, + { + MethodName: "GetGuildBankTabText", + Handler: _GuildService_GetGuildBankTabText_Handler, + }, + { + MethodName: "UpdateGuildBankTab", + Handler: _GuildService_UpdateGuildBankTab_Handler, + }, + { + MethodName: "SetGuildBankTabText", + Handler: _GuildService_SetGuildBankTabText_Handler, + }, + { + MethodName: "BuyGuildBankTab", + Handler: _GuildService_BuyGuildBankTab_Handler, + }, + { + MethodName: "DepositGuildBankMoney", + Handler: _GuildService_DepositGuildBankMoney_Handler, + }, + { + MethodName: "WithdrawGuildBankMoney", + Handler: _GuildService_WithdrawGuildBankMoney_Handler, + }, + { + MethodName: "RollbackGuildBankMoneyWithdraw", + Handler: _GuildService_RollbackGuildBankMoneyWithdraw_Handler, + }, + { + MethodName: "DepositGuildBankItem", + Handler: _GuildService_DepositGuildBankItem_Handler, + }, + { + MethodName: "WithdrawGuildBankItem", + Handler: _GuildService_WithdrawGuildBankItem_Handler, + }, + { + MethodName: "RollbackGuildBankItemWithdraw", + Handler: _GuildService_RollbackGuildBankItemWithdraw_Handler, + }, + { + MethodName: "MoveGuildBankItem", + Handler: _GuildService_MoveGuildBankItem_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "guilds.proto", diff --git a/gen/mail/pb/mail.pb.go b/gen/mail/pb/mail.pb.go index 708bbb2..e67ac3c 100644 --- a/gen/mail/pb/mail.pb.go +++ b/gen/mail/pb/mail.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v3.20.3 +// protoc v3.21.12 // source: mail.proto package pb diff --git a/gen/mail/pb/mail_grpc.pb.go b/gen/mail/pb/mail_grpc.pb.go index 9ae125e..0c90115 100644 --- a/gen/mail/pb/mail_grpc.pb.go +++ b/gen/mail/pb/mail_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v3.20.3 +// - protoc v3.21.12 // source: mail.proto package pb diff --git a/gen/matchmaking/pb/matchmaking.pb.go b/gen/matchmaking/pb/matchmaking.pb.go index 88c7415..02ac9c8 100644 --- a/gen/matchmaking/pb/matchmaking.pb.go +++ b/gen/matchmaking/pb/matchmaking.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v3.20.3 +// protoc v3.21.12 // source: matchmaking.proto package pb @@ -121,6 +121,277 @@ func (PlayerQueueStatus) EnumDescriptor() ([]byte, []int) { return file_matchmaking_proto_rawDescGZIP(), []int{1} } +type MatchmakingArenaTeamMutationStatus int32 + +const ( + MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_OK MatchmakingArenaTeamMutationStatus = 0 + MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_NOT_FOUND MatchmakingArenaTeamMutationStatus = 1 + MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_MEMBER_MISMATCH MatchmakingArenaTeamMutationStatus = 2 + MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_INVALID_TYPE MatchmakingArenaTeamMutationStatus = 3 + MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_NAME_EXISTS MatchmakingArenaTeamMutationStatus = 4 + MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_ALREADY_IN_TEAM MatchmakingArenaTeamMutationStatus = 5 + MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_ROSTER_FULL MatchmakingArenaTeamMutationStatus = 6 + MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_FAILED MatchmakingArenaTeamMutationStatus = 7 + MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_INVALID_NAME MatchmakingArenaTeamMutationStatus = 8 +) + +// Enum value maps for MatchmakingArenaTeamMutationStatus. +var ( + MatchmakingArenaTeamMutationStatus_name = map[int32]string{ + 0: "MATCHMAKING_ARENA_TEAM_MUTATION_OK", + 1: "MATCHMAKING_ARENA_TEAM_MUTATION_NOT_FOUND", + 2: "MATCHMAKING_ARENA_TEAM_MUTATION_MEMBER_MISMATCH", + 3: "MATCHMAKING_ARENA_TEAM_MUTATION_INVALID_TYPE", + 4: "MATCHMAKING_ARENA_TEAM_MUTATION_NAME_EXISTS", + 5: "MATCHMAKING_ARENA_TEAM_MUTATION_ALREADY_IN_TEAM", + 6: "MATCHMAKING_ARENA_TEAM_MUTATION_ROSTER_FULL", + 7: "MATCHMAKING_ARENA_TEAM_MUTATION_FAILED", + 8: "MATCHMAKING_ARENA_TEAM_MUTATION_INVALID_NAME", + } + MatchmakingArenaTeamMutationStatus_value = map[string]int32{ + "MATCHMAKING_ARENA_TEAM_MUTATION_OK": 0, + "MATCHMAKING_ARENA_TEAM_MUTATION_NOT_FOUND": 1, + "MATCHMAKING_ARENA_TEAM_MUTATION_MEMBER_MISMATCH": 2, + "MATCHMAKING_ARENA_TEAM_MUTATION_INVALID_TYPE": 3, + "MATCHMAKING_ARENA_TEAM_MUTATION_NAME_EXISTS": 4, + "MATCHMAKING_ARENA_TEAM_MUTATION_ALREADY_IN_TEAM": 5, + "MATCHMAKING_ARENA_TEAM_MUTATION_ROSTER_FULL": 6, + "MATCHMAKING_ARENA_TEAM_MUTATION_FAILED": 7, + "MATCHMAKING_ARENA_TEAM_MUTATION_INVALID_NAME": 8, + } +) + +func (x MatchmakingArenaTeamMutationStatus) Enum() *MatchmakingArenaTeamMutationStatus { + p := new(MatchmakingArenaTeamMutationStatus) + *p = x + return p +} + +func (x MatchmakingArenaTeamMutationStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (MatchmakingArenaTeamMutationStatus) Descriptor() protoreflect.EnumDescriptor { + return file_matchmaking_proto_enumTypes[2].Descriptor() +} + +func (MatchmakingArenaTeamMutationStatus) Type() protoreflect.EnumType { + return &file_matchmaking_proto_enumTypes[2] +} + +func (x MatchmakingArenaTeamMutationStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use MatchmakingArenaTeamMutationStatus.Descriptor instead. +func (MatchmakingArenaTeamMutationStatus) EnumDescriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{2} +} + +type LfgState int32 + +const ( + LfgState_LFG_STATE_NONE LfgState = 0 + LfgState_LFG_STATE_ROLECHECK LfgState = 1 + LfgState_LFG_STATE_QUEUED LfgState = 2 + LfgState_LFG_STATE_PROPOSAL LfgState = 3 + LfgState_LFG_STATE_BOOT LfgState = 4 + LfgState_LFG_STATE_DUNGEON LfgState = 5 + LfgState_LFG_STATE_FINISHED_DUNGEON LfgState = 6 + LfgState_LFG_STATE_RAIDBROWSER LfgState = 7 +) + +// Enum value maps for LfgState. +var ( + LfgState_name = map[int32]string{ + 0: "LFG_STATE_NONE", + 1: "LFG_STATE_ROLECHECK", + 2: "LFG_STATE_QUEUED", + 3: "LFG_STATE_PROPOSAL", + 4: "LFG_STATE_BOOT", + 5: "LFG_STATE_DUNGEON", + 6: "LFG_STATE_FINISHED_DUNGEON", + 7: "LFG_STATE_RAIDBROWSER", + } + LfgState_value = map[string]int32{ + "LFG_STATE_NONE": 0, + "LFG_STATE_ROLECHECK": 1, + "LFG_STATE_QUEUED": 2, + "LFG_STATE_PROPOSAL": 3, + "LFG_STATE_BOOT": 4, + "LFG_STATE_DUNGEON": 5, + "LFG_STATE_FINISHED_DUNGEON": 6, + "LFG_STATE_RAIDBROWSER": 7, + } +) + +func (x LfgState) Enum() *LfgState { + p := new(LfgState) + *p = x + return p +} + +func (x LfgState) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (LfgState) Descriptor() protoreflect.EnumDescriptor { + return file_matchmaking_proto_enumTypes[3].Descriptor() +} + +func (LfgState) Type() protoreflect.EnumType { + return &file_matchmaking_proto_enumTypes[3] +} + +func (x LfgState) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use LfgState.Descriptor instead. +func (LfgState) EnumDescriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{3} +} + +type LfgProposalState int32 + +const ( + LfgProposalState_LFG_PROPOSAL_INITIATING LfgProposalState = 0 + LfgProposalState_LFG_PROPOSAL_FAILED LfgProposalState = 1 + LfgProposalState_LFG_PROPOSAL_SUCCESS LfgProposalState = 2 +) + +// Enum value maps for LfgProposalState. +var ( + LfgProposalState_name = map[int32]string{ + 0: "LFG_PROPOSAL_INITIATING", + 1: "LFG_PROPOSAL_FAILED", + 2: "LFG_PROPOSAL_SUCCESS", + } + LfgProposalState_value = map[string]int32{ + "LFG_PROPOSAL_INITIATING": 0, + "LFG_PROPOSAL_FAILED": 1, + "LFG_PROPOSAL_SUCCESS": 2, + } +) + +func (x LfgProposalState) Enum() *LfgProposalState { + p := new(LfgProposalState) + *p = x + return p +} + +func (x LfgProposalState) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (LfgProposalState) Descriptor() protoreflect.EnumDescriptor { + return file_matchmaking_proto_enumTypes[4].Descriptor() +} + +func (LfgProposalState) Type() protoreflect.EnumType { + return &file_matchmaking_proto_enumTypes[4] +} + +func (x LfgProposalState) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use LfgProposalState.Descriptor instead. +func (LfgProposalState) EnumDescriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{4} +} + +type LfgJoinResult int32 + +const ( + LfgJoinResult_LFG_JOIN_OK LfgJoinResult = 0 + LfgJoinResult_LFG_JOIN_FAILED LfgJoinResult = 1 + LfgJoinResult_LFG_JOIN_GROUP_FULL LfgJoinResult = 2 + LfgJoinResult_LFG_JOIN_INTERNAL_ERROR LfgJoinResult = 4 + LfgJoinResult_LFG_JOIN_NOT_MEET_REQS LfgJoinResult = 5 + LfgJoinResult_LFG_JOIN_PARTY_NOT_MEET_REQS LfgJoinResult = 6 + LfgJoinResult_LFG_JOIN_MIXED_RAID_DUNGEON LfgJoinResult = 7 + LfgJoinResult_LFG_JOIN_MULTI_REALM LfgJoinResult = 8 + LfgJoinResult_LFG_JOIN_DISCONNECTED LfgJoinResult = 9 + LfgJoinResult_LFG_JOIN_PARTY_INFO_FAILED LfgJoinResult = 10 + LfgJoinResult_LFG_JOIN_DUNGEON_INVALID LfgJoinResult = 11 + LfgJoinResult_LFG_JOIN_DESERTER LfgJoinResult = 12 + LfgJoinResult_LFG_JOIN_PARTY_DESERTER LfgJoinResult = 13 + LfgJoinResult_LFG_JOIN_RANDOM_COOLDOWN LfgJoinResult = 14 + LfgJoinResult_LFG_JOIN_PARTY_RANDOM_COOLDOWN LfgJoinResult = 15 + LfgJoinResult_LFG_JOIN_TOO_MANY_MEMBERS LfgJoinResult = 16 + LfgJoinResult_LFG_JOIN_USING_BG_SYSTEM LfgJoinResult = 17 +) + +// Enum value maps for LfgJoinResult. +var ( + LfgJoinResult_name = map[int32]string{ + 0: "LFG_JOIN_OK", + 1: "LFG_JOIN_FAILED", + 2: "LFG_JOIN_GROUP_FULL", + 4: "LFG_JOIN_INTERNAL_ERROR", + 5: "LFG_JOIN_NOT_MEET_REQS", + 6: "LFG_JOIN_PARTY_NOT_MEET_REQS", + 7: "LFG_JOIN_MIXED_RAID_DUNGEON", + 8: "LFG_JOIN_MULTI_REALM", + 9: "LFG_JOIN_DISCONNECTED", + 10: "LFG_JOIN_PARTY_INFO_FAILED", + 11: "LFG_JOIN_DUNGEON_INVALID", + 12: "LFG_JOIN_DESERTER", + 13: "LFG_JOIN_PARTY_DESERTER", + 14: "LFG_JOIN_RANDOM_COOLDOWN", + 15: "LFG_JOIN_PARTY_RANDOM_COOLDOWN", + 16: "LFG_JOIN_TOO_MANY_MEMBERS", + 17: "LFG_JOIN_USING_BG_SYSTEM", + } + LfgJoinResult_value = map[string]int32{ + "LFG_JOIN_OK": 0, + "LFG_JOIN_FAILED": 1, + "LFG_JOIN_GROUP_FULL": 2, + "LFG_JOIN_INTERNAL_ERROR": 4, + "LFG_JOIN_NOT_MEET_REQS": 5, + "LFG_JOIN_PARTY_NOT_MEET_REQS": 6, + "LFG_JOIN_MIXED_RAID_DUNGEON": 7, + "LFG_JOIN_MULTI_REALM": 8, + "LFG_JOIN_DISCONNECTED": 9, + "LFG_JOIN_PARTY_INFO_FAILED": 10, + "LFG_JOIN_DUNGEON_INVALID": 11, + "LFG_JOIN_DESERTER": 12, + "LFG_JOIN_PARTY_DESERTER": 13, + "LFG_JOIN_RANDOM_COOLDOWN": 14, + "LFG_JOIN_PARTY_RANDOM_COOLDOWN": 15, + "LFG_JOIN_TOO_MANY_MEMBERS": 16, + "LFG_JOIN_USING_BG_SYSTEM": 17, + } +) + +func (x LfgJoinResult) Enum() *LfgJoinResult { + p := new(LfgJoinResult) + *p = x + return p +} + +func (x LfgJoinResult) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (LfgJoinResult) Descriptor() protoreflect.EnumDescriptor { + return file_matchmaking_proto_enumTypes[5].Descriptor() +} + +func (LfgJoinResult) Type() protoreflect.EnumType { + return &file_matchmaking_proto_enumTypes[5] +} + +func (x LfgJoinResult) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use LfgJoinResult.Descriptor instead. +func (LfgJoinResult) EnumDescriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{5} +} + // Use the same statuses as in gameserver (AC/TC) type BattlegroundStatusChangedRequest_Status int32 @@ -161,11 +432,11 @@ func (x BattlegroundStatusChangedRequest_Status) String() string { } func (BattlegroundStatusChangedRequest_Status) Descriptor() protoreflect.EnumDescriptor { - return file_matchmaking_proto_enumTypes[2].Descriptor() + return file_matchmaking_proto_enumTypes[6].Descriptor() } func (BattlegroundStatusChangedRequest_Status) Type() protoreflect.EnumType { - return &file_matchmaking_proto_enumTypes[2] + return &file_matchmaking_proto_enumTypes[6] } func (x BattlegroundStatusChangedRequest_Status) Number() protoreflect.EnumNumber { @@ -189,6 +460,8 @@ type EnqueueToBattlegroundRequest struct { LeadersLvl uint32 `protobuf:"varint,5,opt,name=leadersLvl,proto3" json:"leadersLvl,omitempty"` BgTypeID uint32 `protobuf:"varint,6,opt,name=bgTypeID,proto3" json:"bgTypeID,omitempty"` TeamID PVPTeamID `protobuf:"varint,7,opt,name=teamID,proto3,enum=v1.PVPTeamID" json:"teamID,omitempty"` + ArenaType uint32 `protobuf:"varint,8,opt,name=arenaType,proto3" json:"arenaType,omitempty"` + IsRated bool `protobuf:"varint,9,opt,name=isRated,proto3" json:"isRated,omitempty"` } func (x *EnqueueToBattlegroundRequest) Reset() { @@ -272,6 +545,20 @@ func (x *EnqueueToBattlegroundRequest) GetTeamID() PVPTeamID { return PVPTeamID_Any } +func (x *EnqueueToBattlegroundRequest) GetArenaType() uint32 { + if x != nil { + return x.ArenaType + } + return 0 +} + +func (x *EnqueueToBattlegroundRequest) GetIsRated() bool { + if x != nil { + return x.IsRated + } + return false +} + type EnqueueToBattlegroundResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -933,20 +1220,17 @@ func (x *BattlegroundStatusChangedResponse) GetApi() string { return "" } -type BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData struct { +type RatedArenaParticipant struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - AssignedBattlegroundInstanceID uint32 `protobuf:"varint,1,opt,name=assignedBattlegroundInstanceID,proto3" json:"assignedBattlegroundInstanceID,omitempty"` - MapID uint32 `protobuf:"varint,2,opt,name=mapID,proto3" json:"mapID,omitempty"` - BattlegroupID uint32 `protobuf:"varint,3,opt,name=battlegroupID,proto3" json:"battlegroupID,omitempty"` - GameserverAddress string `protobuf:"bytes,4,opt,name=gameserverAddress,proto3" json:"gameserverAddress,omitempty"` - GameserverGRPCAddress string `protobuf:"bytes,5,opt,name=gameserverGRPCAddress,proto3" json:"gameserverGRPCAddress,omitempty"` + Team PVPTeamID `protobuf:"varint,1,opt,name=team,proto3,enum=v1.PVPTeamID" json:"team,omitempty"` + PlayerGUID uint64 `protobuf:"varint,2,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` } -func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) Reset() { - *x = BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData{} +func (x *RatedArenaParticipant) Reset() { + *x = RatedArenaParticipant{} if protoimpl.UnsafeEnabled { mi := &file_matchmaking_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -954,13 +1238,13 @@ func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) Reset( } } -func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) String() string { +func (x *RatedArenaParticipant) String() string { return protoimpl.X.MessageStringOf(x) } -func (*BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) ProtoMessage() {} +func (*RatedArenaParticipant) ProtoMessage() {} -func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) ProtoReflect() protoreflect.Message { +func (x *RatedArenaParticipant) ProtoReflect() protoreflect.Message { mi := &file_matchmaking_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -972,73 +1256,136 @@ func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) ProtoR return mi.MessageOf(x) } -// Deprecated: Use BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData.ProtoReflect.Descriptor instead. -func (*BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) Descriptor() ([]byte, []int) { - return file_matchmaking_proto_rawDescGZIP(), []int{5, 0} +// Deprecated: Use RatedArenaParticipant.ProtoReflect.Descriptor instead. +func (*RatedArenaParticipant) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{12} } -func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) GetAssignedBattlegroundInstanceID() uint32 { +func (x *RatedArenaParticipant) GetTeam() PVPTeamID { if x != nil { - return x.AssignedBattlegroundInstanceID + return x.Team + } + return PVPTeamID_Any +} + +func (x *RatedArenaParticipant) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID } return 0 } -func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) GetMapID() uint32 { +type RatedArenaTeamScore struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RealmID uint32 `protobuf:"varint,1,opt,name=realmID,proto3" json:"realmID,omitempty"` + ArenaTeamID uint32 `protobuf:"varint,2,opt,name=arenaTeamID,proto3" json:"arenaTeamID,omitempty"` + TeamName string `protobuf:"bytes,3,opt,name=teamName,proto3" json:"teamName,omitempty"` + RatingChange int32 `protobuf:"varint,4,opt,name=ratingChange,proto3" json:"ratingChange,omitempty"` + MatchmakerRating uint32 `protobuf:"varint,5,opt,name=matchmakerRating,proto3" json:"matchmakerRating,omitempty"` +} + +func (x *RatedArenaTeamScore) Reset() { + *x = RatedArenaTeamScore{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RatedArenaTeamScore) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RatedArenaTeamScore) ProtoMessage() {} + +func (x *RatedArenaTeamScore) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RatedArenaTeamScore.ProtoReflect.Descriptor instead. +func (*RatedArenaTeamScore) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{13} +} + +func (x *RatedArenaTeamScore) GetRealmID() uint32 { if x != nil { - return x.MapID + return x.RealmID } return 0 } -func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) GetBattlegroupID() uint32 { +func (x *RatedArenaTeamScore) GetArenaTeamID() uint32 { if x != nil { - return x.BattlegroupID + return x.ArenaTeamID } return 0 } -func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) GetGameserverAddress() string { +func (x *RatedArenaTeamScore) GetTeamName() string { if x != nil { - return x.GameserverAddress + return x.TeamName } return "" } -func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) GetGameserverGRPCAddress() string { +func (x *RatedArenaTeamScore) GetRatingChange() int32 { if x != nil { - return x.GameserverGRPCAddress + return x.RatingChange } - return "" + return 0 } -type BattlegroundQueueDataForPlayerResponse_QueueSlot struct { +func (x *RatedArenaTeamScore) GetMatchmakerRating() uint32 { + if x != nil { + return x.MatchmakerRating + } + return 0 +} + +type RatedArenaMemberResult struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - BgTypeID uint32 `protobuf:"varint,1,opt,name=bgTypeID,proto3" json:"bgTypeID,omitempty"` - Status PlayerQueueStatus `protobuf:"varint,2,opt,name=status,proto3,enum=v1.PlayerQueueStatus" json:"status,omitempty"` - AssignedBattlegroundData *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData `protobuf:"bytes,3,opt,name=assignedBattlegroundData,proto3,oneof" json:"assignedBattlegroundData,omitempty"` + Team PVPTeamID `protobuf:"varint,1,opt,name=team,proto3,enum=v1.PVPTeamID" json:"team,omitempty"` + PlayerGUID uint64 `protobuf:"varint,2,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + PersonalRating uint32 `protobuf:"varint,3,opt,name=personalRating,proto3" json:"personalRating,omitempty"` + WeekGames uint32 `protobuf:"varint,4,opt,name=weekGames,proto3" json:"weekGames,omitempty"` + SeasonGames uint32 `protobuf:"varint,5,opt,name=seasonGames,proto3" json:"seasonGames,omitempty"` + WeekWins uint32 `protobuf:"varint,6,opt,name=weekWins,proto3" json:"weekWins,omitempty"` + SeasonWins uint32 `protobuf:"varint,7,opt,name=seasonWins,proto3" json:"seasonWins,omitempty"` + MatchmakerRating uint32 `protobuf:"varint,8,opt,name=matchmakerRating,proto3" json:"matchmakerRating,omitempty"` } -func (x *BattlegroundQueueDataForPlayerResponse_QueueSlot) Reset() { - *x = BattlegroundQueueDataForPlayerResponse_QueueSlot{} +func (x *RatedArenaMemberResult) Reset() { + *x = RatedArenaMemberResult{} if protoimpl.UnsafeEnabled { - mi := &file_matchmaking_proto_msgTypes[13] + mi := &file_matchmaking_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *BattlegroundQueueDataForPlayerResponse_QueueSlot) String() string { +func (x *RatedArenaMemberResult) String() string { return protoimpl.X.MessageStringOf(x) } -func (*BattlegroundQueueDataForPlayerResponse_QueueSlot) ProtoMessage() {} +func (*RatedArenaMemberResult) ProtoMessage() {} -func (x *BattlegroundQueueDataForPlayerResponse_QueueSlot) ProtoReflect() protoreflect.Message { - mi := &file_matchmaking_proto_msgTypes[13] +func (x *RatedArenaMemberResult) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1049,191 +1396,2037 @@ func (x *BattlegroundQueueDataForPlayerResponse_QueueSlot) ProtoReflect() protor return mi.MessageOf(x) } -// Deprecated: Use BattlegroundQueueDataForPlayerResponse_QueueSlot.ProtoReflect.Descriptor instead. -func (*BattlegroundQueueDataForPlayerResponse_QueueSlot) Descriptor() ([]byte, []int) { - return file_matchmaking_proto_rawDescGZIP(), []int{5, 1} +// Deprecated: Use RatedArenaMemberResult.ProtoReflect.Descriptor instead. +func (*RatedArenaMemberResult) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{14} } -func (x *BattlegroundQueueDataForPlayerResponse_QueueSlot) GetBgTypeID() uint32 { +func (x *RatedArenaMemberResult) GetTeam() PVPTeamID { if x != nil { - return x.BgTypeID + return x.Team + } + return PVPTeamID_Any +} + +func (x *RatedArenaMemberResult) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID } return 0 } -func (x *BattlegroundQueueDataForPlayerResponse_QueueSlot) GetStatus() PlayerQueueStatus { +func (x *RatedArenaMemberResult) GetPersonalRating() uint32 { if x != nil { - return x.Status + return x.PersonalRating } - return PlayerQueueStatus_NotInQueue + return 0 } -func (x *BattlegroundQueueDataForPlayerResponse_QueueSlot) GetAssignedBattlegroundData() *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData { +func (x *RatedArenaMemberResult) GetWeekGames() uint32 { if x != nil { - return x.AssignedBattlegroundData + return x.WeekGames } - return nil + return 0 } -var File_matchmaking_proto protoreflect.FileDescriptor +func (x *RatedArenaMemberResult) GetSeasonGames() uint32 { + if x != nil { + return x.SeasonGames + } + return 0 +} -var file_matchmaking_proto_rawDesc = []byte{ - 0x0a, 0x11, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, 0x22, 0xf1, 0x01, 0x0a, 0x1c, 0x45, 0x6e, 0x71, 0x75, - 0x65, 0x75, 0x65, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, - 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, - 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x47, 0x55, - 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x47, 0x55, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x74, 0x79, 0x4d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x74, - 0x79, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x73, 0x4c, 0x76, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x73, 0x4c, 0x76, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x67, 0x54, 0x79, - 0x70, 0x65, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x62, 0x67, 0x54, 0x79, - 0x70, 0x65, 0x49, 0x44, 0x12, 0x25, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x56, 0x50, 0x54, 0x65, 0x61, - 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0x31, 0x0a, 0x1d, 0x45, - 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, - 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, - 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x96, - 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x46, - 0x72, 0x6f, 0x6d, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, - 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x2a, 0x0a, 0x10, 0x62, - 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, - 0x75, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x22, 0x31, 0x0a, 0x1d, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x51, 0x75, 0x65, 0x75, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x73, 0x0a, 0x25, 0x42, 0x61, - 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, - 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, - 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, - 0x88, 0x05, 0x0a, 0x26, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, - 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x50, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, - 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x4a, 0x0a, 0x05, - 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x76, 0x31, - 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, - 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x53, 0x6c, 0x6f, - 0x74, 0x52, 0x05, 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x1a, 0x82, 0x02, 0x0a, 0x18, 0x41, 0x73, 0x73, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, - 0x64, 0x44, 0x61, 0x74, 0x61, 0x12, 0x46, 0x0a, 0x1e, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, - 0x64, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x1e, 0x61, - 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, - 0x75, 0x6e, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x14, 0x0a, - 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x61, - 0x70, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x62, 0x61, 0x74, 0x74, - 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x12, 0x2c, 0x0a, 0x11, 0x67, 0x61, 0x6d, - 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x67, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x34, 0x0a, 0x15, 0x67, 0x61, 0x6d, 0x65, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x67, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x1a, 0xfa, 0x01, - 0x0a, 0x09, 0x51, 0x75, 0x65, 0x75, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x62, - 0x67, 0x54, 0x79, 0x70, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x62, - 0x67, 0x54, 0x79, 0x70, 0x65, 0x49, 0x44, 0x12, 0x2d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x51, 0x75, 0x65, 0x75, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x84, 0x01, 0x0a, 0x18, 0x61, 0x73, 0x73, 0x69, 0x67, - 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x44, - 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x76, 0x31, 0x2e, 0x42, - 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x61, - 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, - 0x52, 0x18, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, - 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01, 0x42, 0x1b, 0x0a, - 0x19, 0x5f, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, - 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x61, 0x22, 0xaf, 0x01, 0x0a, 0x1d, 0x50, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4c, 0x65, 0x66, 0x74, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, - 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, - 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, - 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x73, 0x43, 0x72, - 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, - 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x22, 0x32, 0x0a, 0x1e, - 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4c, 0x65, 0x66, 0x74, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, - 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, - 0x22, 0xb1, 0x01, 0x0a, 0x1f, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x6f, 0x69, 0x6e, 0x65, - 0x64, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, - 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, - 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, - 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, - 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, - 0x65, 0x61, 0x6c, 0x6d, 0x22, 0x34, 0x0a, 0x20, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x6f, - 0x69, 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0xa3, 0x02, 0x0a, 0x20, 0x42, - 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, - 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, - 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x12, - 0x43, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x2b, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, - 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x22, 0x4a, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x08, - 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x61, 0x69, 0x74, - 0x51, 0x75, 0x65, 0x75, 0x65, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x57, 0x61, 0x69, 0x74, 0x4a, - 0x6f, 0x69, 0x6e, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x6e, 0x50, 0x72, 0x6f, 0x67, 0x72, - 0x65, 0x73, 0x73, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x10, 0x04, - 0x22, 0x35, 0x0a, 0x21, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x2a, 0x2d, 0x0a, 0x09, 0x50, 0x56, 0x50, 0x54, 0x65, - 0x61, 0x6d, 0x49, 0x44, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x6e, 0x79, 0x10, 0x00, 0x12, 0x0c, 0x0a, - 0x08, 0x41, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x48, - 0x6f, 0x72, 0x64, 0x65, 0x10, 0x02, 0x2a, 0x4d, 0x0a, 0x11, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x51, 0x75, 0x65, 0x75, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, - 0x6f, 0x74, 0x49, 0x6e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x49, - 0x6e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x6e, 0x76, 0x69, - 0x74, 0x65, 0x64, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x6e, 0x50, 0x72, 0x6f, 0x67, 0x72, - 0x65, 0x73, 0x73, 0x10, 0x03, 0x32, 0xfb, 0x04, 0x0a, 0x12, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x6d, - 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5c, 0x0a, 0x15, - 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, - 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x71, 0x75, 0x65, - 0x75, 0x65, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x71, - 0x75, 0x65, 0x75, 0x65, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, - 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x15, 0x52, 0x65, - 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x51, 0x75, - 0x65, 0x75, 0x65, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x51, 0x75, 0x65, 0x75, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x1e, 0x42, 0x61, 0x74, 0x74, - 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, 0x61, 0x74, - 0x61, 0x46, 0x6f, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x76, 0x31, 0x2e, - 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, +func (x *RatedArenaMemberResult) GetWeekWins() uint32 { + if x != nil { + return x.WeekWins + } + return 0 +} + +func (x *RatedArenaMemberResult) GetSeasonWins() uint32 { + if x != nil { + return x.SeasonWins + } + return 0 +} + +func (x *RatedArenaMemberResult) GetMatchmakerRating() uint32 { + if x != nil { + return x.MatchmakerRating + } + return 0 +} + +type FinishRatedArenaMatchRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + OwnerRealmID uint32 `protobuf:"varint,2,opt,name=ownerRealmID,proto3" json:"ownerRealmID,omitempty"` + IsCrossRealm bool `protobuf:"varint,3,opt,name=isCrossRealm,proto3" json:"isCrossRealm,omitempty"` + InstanceID uint32 `protobuf:"varint,4,opt,name=instanceID,proto3" json:"instanceID,omitempty"` + ArenaType uint32 `protobuf:"varint,5,opt,name=arenaType,proto3" json:"arenaType,omitempty"` + WinnerTeam PVPTeamID `protobuf:"varint,6,opt,name=winnerTeam,proto3,enum=v1.PVPTeamID" json:"winnerTeam,omitempty"` + ValidArena bool `protobuf:"varint,7,opt,name=validArena,proto3" json:"validArena,omitempty"` + AllianceArenaTeamID uint32 `protobuf:"varint,8,opt,name=allianceArenaTeamID,proto3" json:"allianceArenaTeamID,omitempty"` + HordeArenaTeamID uint32 `protobuf:"varint,9,opt,name=hordeArenaTeamID,proto3" json:"hordeArenaTeamID,omitempty"` + AllianceArenaMatchmakerRating uint32 `protobuf:"varint,10,opt,name=allianceArenaMatchmakerRating,proto3" json:"allianceArenaMatchmakerRating,omitempty"` + HordeArenaMatchmakerRating uint32 `protobuf:"varint,11,opt,name=hordeArenaMatchmakerRating,proto3" json:"hordeArenaMatchmakerRating,omitempty"` + Participants []*RatedArenaParticipant `protobuf:"bytes,12,rep,name=participants,proto3" json:"participants,omitempty"` +} + +func (x *FinishRatedArenaMatchRequest) Reset() { + *x = FinishRatedArenaMatchRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FinishRatedArenaMatchRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FinishRatedArenaMatchRequest) ProtoMessage() {} + +func (x *FinishRatedArenaMatchRequest) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FinishRatedArenaMatchRequest.ProtoReflect.Descriptor instead. +func (*FinishRatedArenaMatchRequest) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{15} +} + +func (x *FinishRatedArenaMatchRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *FinishRatedArenaMatchRequest) GetOwnerRealmID() uint32 { + if x != nil { + return x.OwnerRealmID + } + return 0 +} + +func (x *FinishRatedArenaMatchRequest) GetIsCrossRealm() bool { + if x != nil { + return x.IsCrossRealm + } + return false +} + +func (x *FinishRatedArenaMatchRequest) GetInstanceID() uint32 { + if x != nil { + return x.InstanceID + } + return 0 +} + +func (x *FinishRatedArenaMatchRequest) GetArenaType() uint32 { + if x != nil { + return x.ArenaType + } + return 0 +} + +func (x *FinishRatedArenaMatchRequest) GetWinnerTeam() PVPTeamID { + if x != nil { + return x.WinnerTeam + } + return PVPTeamID_Any +} + +func (x *FinishRatedArenaMatchRequest) GetValidArena() bool { + if x != nil { + return x.ValidArena + } + return false +} + +func (x *FinishRatedArenaMatchRequest) GetAllianceArenaTeamID() uint32 { + if x != nil { + return x.AllianceArenaTeamID + } + return 0 +} + +func (x *FinishRatedArenaMatchRequest) GetHordeArenaTeamID() uint32 { + if x != nil { + return x.HordeArenaTeamID + } + return 0 +} + +func (x *FinishRatedArenaMatchRequest) GetAllianceArenaMatchmakerRating() uint32 { + if x != nil { + return x.AllianceArenaMatchmakerRating + } + return 0 +} + +func (x *FinishRatedArenaMatchRequest) GetHordeArenaMatchmakerRating() uint32 { + if x != nil { + return x.HordeArenaMatchmakerRating + } + return 0 +} + +func (x *FinishRatedArenaMatchRequest) GetParticipants() []*RatedArenaParticipant { + if x != nil { + return x.Participants + } + return nil +} + +type FinishRatedArenaMatchResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status MatchmakingArenaTeamMutationStatus `protobuf:"varint,2,opt,name=status,proto3,enum=v1.MatchmakingArenaTeamMutationStatus" json:"status,omitempty"` + AllianceScore *RatedArenaTeamScore `protobuf:"bytes,3,opt,name=allianceScore,proto3" json:"allianceScore,omitempty"` + HordeScore *RatedArenaTeamScore `protobuf:"bytes,4,opt,name=hordeScore,proto3" json:"hordeScore,omitempty"` + MemberResults []*RatedArenaMemberResult `protobuf:"bytes,5,rep,name=memberResults,proto3" json:"memberResults,omitempty"` +} + +func (x *FinishRatedArenaMatchResponse) Reset() { + *x = FinishRatedArenaMatchResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FinishRatedArenaMatchResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FinishRatedArenaMatchResponse) ProtoMessage() {} + +func (x *FinishRatedArenaMatchResponse) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FinishRatedArenaMatchResponse.ProtoReflect.Descriptor instead. +func (*FinishRatedArenaMatchResponse) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{16} +} + +func (x *FinishRatedArenaMatchResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *FinishRatedArenaMatchResponse) GetStatus() MatchmakingArenaTeamMutationStatus { + if x != nil { + return x.Status + } + return MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_OK +} + +func (x *FinishRatedArenaMatchResponse) GetAllianceScore() *RatedArenaTeamScore { + if x != nil { + return x.AllianceScore + } + return nil +} + +func (x *FinishRatedArenaMatchResponse) GetHordeScore() *RatedArenaTeamScore { + if x != nil { + return x.HordeScore + } + return nil +} + +func (x *FinishRatedArenaMatchResponse) GetMemberResults() []*RatedArenaMemberResult { + if x != nil { + return x.MemberResults + } + return nil +} + +type LfgMember struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PlayerGUID uint64 `protobuf:"varint,1,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + Roles uint32 `protobuf:"varint,2,opt,name=roles,proto3" json:"roles,omitempty"` + Leader bool `protobuf:"varint,3,opt,name=leader,proto3" json:"leader,omitempty"` + WorldserverID string `protobuf:"bytes,4,opt,name=worldserverID,proto3" json:"worldserverID,omitempty"` + RealmID uint32 `protobuf:"varint,5,opt,name=realmID,proto3" json:"realmID,omitempty"` + QueueLeaderRealmID uint32 `protobuf:"varint,6,opt,name=queueLeaderRealmID,proto3" json:"queueLeaderRealmID,omitempty"` + QueueLeaderGUID uint64 `protobuf:"varint,7,opt,name=queueLeaderGUID,proto3" json:"queueLeaderGUID,omitempty"` +} + +func (x *LfgMember) Reset() { + *x = LfgMember{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LfgMember) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LfgMember) ProtoMessage() {} + +func (x *LfgMember) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LfgMember.ProtoReflect.Descriptor instead. +func (*LfgMember) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{17} +} + +func (x *LfgMember) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *LfgMember) GetRoles() uint32 { + if x != nil { + return x.Roles + } + return 0 +} + +func (x *LfgMember) GetLeader() bool { + if x != nil { + return x.Leader + } + return false +} + +func (x *LfgMember) GetWorldserverID() string { + if x != nil { + return x.WorldserverID + } + return "" +} + +func (x *LfgMember) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *LfgMember) GetQueueLeaderRealmID() uint32 { + if x != nil { + return x.QueueLeaderRealmID + } + return 0 +} + +func (x *LfgMember) GetQueueLeaderGUID() uint64 { + if x != nil { + return x.QueueLeaderGUID + } + return 0 +} + +type LfgProposalMember struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PlayerGUID uint64 `protobuf:"varint,1,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + SelectedRoles uint32 `protobuf:"varint,2,opt,name=selectedRoles,proto3" json:"selectedRoles,omitempty"` + AssignedRole uint32 `protobuf:"varint,3,opt,name=assignedRole,proto3" json:"assignedRole,omitempty"` + Answered bool `protobuf:"varint,4,opt,name=answered,proto3" json:"answered,omitempty"` + Accepted bool `protobuf:"varint,5,opt,name=accepted,proto3" json:"accepted,omitempty"` + RealmID uint32 `protobuf:"varint,6,opt,name=realmID,proto3" json:"realmID,omitempty"` +} + +func (x *LfgProposalMember) Reset() { + *x = LfgProposalMember{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LfgProposalMember) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LfgProposalMember) ProtoMessage() {} + +func (x *LfgProposalMember) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LfgProposalMember.ProtoReflect.Descriptor instead. +func (*LfgProposalMember) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{18} +} + +func (x *LfgProposalMember) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *LfgProposalMember) GetSelectedRoles() uint32 { + if x != nil { + return x.SelectedRoles + } + return 0 +} + +func (x *LfgProposalMember) GetAssignedRole() uint32 { + if x != nil { + return x.AssignedRole + } + return 0 +} + +func (x *LfgProposalMember) GetAnswered() bool { + if x != nil { + return x.Answered + } + return false +} + +func (x *LfgProposalMember) GetAccepted() bool { + if x != nil { + return x.Accepted + } + return false +} + +func (x *LfgProposalMember) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +type LfgStatusData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + State LfgState `protobuf:"varint,1,opt,name=state,proto3,enum=v1.LfgState" json:"state,omitempty"` + ProposalID uint32 `protobuf:"varint,2,opt,name=proposalID,proto3" json:"proposalID,omitempty"` + ProposalState LfgProposalState `protobuf:"varint,3,opt,name=proposalState,proto3,enum=v1.LfgProposalState" json:"proposalState,omitempty"` + DungeonEntry uint32 `protobuf:"varint,4,opt,name=dungeonEntry,proto3" json:"dungeonEntry,omitempty"` + SelectedDungeons []uint32 `protobuf:"varint,5,rep,packed,name=selectedDungeons,proto3" json:"selectedDungeons,omitempty"` + QueuedMembers []*LfgMember `protobuf:"bytes,6,rep,name=queuedMembers,proto3" json:"queuedMembers,omitempty"` + ProposalMembers []*LfgProposalMember `protobuf:"bytes,7,rep,name=proposalMembers,proto3" json:"proposalMembers,omitempty"` + QueuedTimeMilliseconds uint32 `protobuf:"varint,8,opt,name=queuedTimeMilliseconds,proto3" json:"queuedTimeMilliseconds,omitempty"` + TanksNeeded uint32 `protobuf:"varint,9,opt,name=tanksNeeded,proto3" json:"tanksNeeded,omitempty"` + HealersNeeded uint32 `protobuf:"varint,10,opt,name=healersNeeded,proto3" json:"healersNeeded,omitempty"` + DamageNeeded uint32 `protobuf:"varint,11,opt,name=damageNeeded,proto3" json:"damageNeeded,omitempty"` +} + +func (x *LfgStatusData) Reset() { + *x = LfgStatusData{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LfgStatusData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LfgStatusData) ProtoMessage() {} + +func (x *LfgStatusData) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LfgStatusData.ProtoReflect.Descriptor instead. +func (*LfgStatusData) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{19} +} + +func (x *LfgStatusData) GetState() LfgState { + if x != nil { + return x.State + } + return LfgState_LFG_STATE_NONE +} + +func (x *LfgStatusData) GetProposalID() uint32 { + if x != nil { + return x.ProposalID + } + return 0 +} + +func (x *LfgStatusData) GetProposalState() LfgProposalState { + if x != nil { + return x.ProposalState + } + return LfgProposalState_LFG_PROPOSAL_INITIATING +} + +func (x *LfgStatusData) GetDungeonEntry() uint32 { + if x != nil { + return x.DungeonEntry + } + return 0 +} + +func (x *LfgStatusData) GetSelectedDungeons() []uint32 { + if x != nil { + return x.SelectedDungeons + } + return nil +} + +func (x *LfgStatusData) GetQueuedMembers() []*LfgMember { + if x != nil { + return x.QueuedMembers + } + return nil +} + +func (x *LfgStatusData) GetProposalMembers() []*LfgProposalMember { + if x != nil { + return x.ProposalMembers + } + return nil +} + +func (x *LfgStatusData) GetQueuedTimeMilliseconds() uint32 { + if x != nil { + return x.QueuedTimeMilliseconds + } + return 0 +} + +func (x *LfgStatusData) GetTanksNeeded() uint32 { + if x != nil { + return x.TanksNeeded + } + return 0 +} + +func (x *LfgStatusData) GetHealersNeeded() uint32 { + if x != nil { + return x.HealersNeeded + } + return 0 +} + +func (x *LfgStatusData) GetDamageNeeded() uint32 { + if x != nil { + return x.DamageNeeded + } + return 0 +} + +type JoinLfgRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + LeaderGUID uint64 `protobuf:"varint,3,opt,name=leaderGUID,proto3" json:"leaderGUID,omitempty"` + Members []*LfgMember `protobuf:"bytes,4,rep,name=members,proto3" json:"members,omitempty"` + DungeonEntries []uint32 `protobuf:"varint,5,rep,packed,name=dungeonEntries,proto3" json:"dungeonEntries,omitempty"` + Comment string `protobuf:"bytes,6,opt,name=comment,proto3" json:"comment,omitempty"` +} + +func (x *JoinLfgRequest) Reset() { + *x = JoinLfgRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JoinLfgRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinLfgRequest) ProtoMessage() {} + +func (x *JoinLfgRequest) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinLfgRequest.ProtoReflect.Descriptor instead. +func (*JoinLfgRequest) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{20} +} + +func (x *JoinLfgRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *JoinLfgRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *JoinLfgRequest) GetLeaderGUID() uint64 { + if x != nil { + return x.LeaderGUID + } + return 0 +} + +func (x *JoinLfgRequest) GetMembers() []*LfgMember { + if x != nil { + return x.Members + } + return nil +} + +func (x *JoinLfgRequest) GetDungeonEntries() []uint32 { + if x != nil { + return x.DungeonEntries + } + return nil +} + +func (x *JoinLfgRequest) GetComment() string { + if x != nil { + return x.Comment + } + return "" +} + +type JoinLfgResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Result LfgJoinResult `protobuf:"varint,2,opt,name=result,proto3,enum=v1.LfgJoinResult" json:"result,omitempty"` + Status *LfgStatusData `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *JoinLfgResponse) Reset() { + *x = JoinLfgResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JoinLfgResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinLfgResponse) ProtoMessage() {} + +func (x *JoinLfgResponse) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinLfgResponse.ProtoReflect.Descriptor instead. +func (*JoinLfgResponse) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{21} +} + +func (x *JoinLfgResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *JoinLfgResponse) GetResult() LfgJoinResult { + if x != nil { + return x.Result + } + return LfgJoinResult_LFG_JOIN_OK +} + +func (x *JoinLfgResponse) GetStatus() *LfgStatusData { + if x != nil { + return x.Status + } + return nil +} + +type LeaveLfgRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` +} + +func (x *LeaveLfgRequest) Reset() { + *x = LeaveLfgRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LeaveLfgRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LeaveLfgRequest) ProtoMessage() {} + +func (x *LeaveLfgRequest) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LeaveLfgRequest.ProtoReflect.Descriptor instead. +func (*LeaveLfgRequest) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{22} +} + +func (x *LeaveLfgRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *LeaveLfgRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *LeaveLfgRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +type LeaveLfgResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *LeaveLfgResponse) Reset() { + *x = LeaveLfgResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LeaveLfgResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LeaveLfgResponse) ProtoMessage() {} + +func (x *LeaveLfgResponse) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LeaveLfgResponse.ProtoReflect.Descriptor instead. +func (*LeaveLfgResponse) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{23} +} + +func (x *LeaveLfgResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type SetLfgRolesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + Roles uint32 `protobuf:"varint,4,opt,name=roles,proto3" json:"roles,omitempty"` +} + +func (x *SetLfgRolesRequest) Reset() { + *x = SetLfgRolesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetLfgRolesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetLfgRolesRequest) ProtoMessage() {} + +func (x *SetLfgRolesRequest) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetLfgRolesRequest.ProtoReflect.Descriptor instead. +func (*SetLfgRolesRequest) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{24} +} + +func (x *SetLfgRolesRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SetLfgRolesRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *SetLfgRolesRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *SetLfgRolesRequest) GetRoles() uint32 { + if x != nil { + return x.Roles + } + return 0 +} + +type SetLfgRolesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status *LfgStatusData `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *SetLfgRolesResponse) Reset() { + *x = SetLfgRolesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetLfgRolesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetLfgRolesResponse) ProtoMessage() {} + +func (x *SetLfgRolesResponse) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetLfgRolesResponse.ProtoReflect.Descriptor instead. +func (*SetLfgRolesResponse) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{25} +} + +func (x *SetLfgRolesResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SetLfgRolesResponse) GetStatus() *LfgStatusData { + if x != nil { + return x.Status + } + return nil +} + +type AnswerLfgProposalRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + ProposalID uint32 `protobuf:"varint,4,opt,name=proposalID,proto3" json:"proposalID,omitempty"` + Accept bool `protobuf:"varint,5,opt,name=accept,proto3" json:"accept,omitempty"` +} + +func (x *AnswerLfgProposalRequest) Reset() { + *x = AnswerLfgProposalRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AnswerLfgProposalRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AnswerLfgProposalRequest) ProtoMessage() {} + +func (x *AnswerLfgProposalRequest) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AnswerLfgProposalRequest.ProtoReflect.Descriptor instead. +func (*AnswerLfgProposalRequest) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{26} +} + +func (x *AnswerLfgProposalRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *AnswerLfgProposalRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *AnswerLfgProposalRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *AnswerLfgProposalRequest) GetProposalID() uint32 { + if x != nil { + return x.ProposalID + } + return 0 +} + +func (x *AnswerLfgProposalRequest) GetAccept() bool { + if x != nil { + return x.Accept + } + return false +} + +type AnswerLfgProposalResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status *LfgStatusData `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *AnswerLfgProposalResponse) Reset() { + *x = AnswerLfgProposalResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AnswerLfgProposalResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AnswerLfgProposalResponse) ProtoMessage() {} + +func (x *AnswerLfgProposalResponse) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AnswerLfgProposalResponse.ProtoReflect.Descriptor instead. +func (*AnswerLfgProposalResponse) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{27} +} + +func (x *AnswerLfgProposalResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *AnswerLfgProposalResponse) GetStatus() *LfgStatusData { + if x != nil { + return x.Status + } + return nil +} + +type LfgStatusRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,3,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` +} + +func (x *LfgStatusRequest) Reset() { + *x = LfgStatusRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LfgStatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LfgStatusRequest) ProtoMessage() {} + +func (x *LfgStatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LfgStatusRequest.ProtoReflect.Descriptor instead. +func (*LfgStatusRequest) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{28} +} + +func (x *LfgStatusRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *LfgStatusRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *LfgStatusRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +type LfgStatusResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status *LfgStatusData `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *LfgStatusResponse) Reset() { + *x = LfgStatusResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LfgStatusResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LfgStatusResponse) ProtoMessage() {} + +func (x *LfgStatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LfgStatusResponse.ProtoReflect.Descriptor instead. +func (*LfgStatusResponse) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{29} +} + +func (x *LfgStatusResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *LfgStatusResponse) GetStatus() *LfgStatusData { + if x != nil { + return x.Status + } + return nil +} + +type CompleteLfgDungeonPlayer struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RealmID uint32 `protobuf:"varint,1,opt,name=realmID,proto3" json:"realmID,omitempty"` + PlayerGUID uint64 `protobuf:"varint,2,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` +} + +func (x *CompleteLfgDungeonPlayer) Reset() { + *x = CompleteLfgDungeonPlayer{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CompleteLfgDungeonPlayer) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CompleteLfgDungeonPlayer) ProtoMessage() {} + +func (x *CompleteLfgDungeonPlayer) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CompleteLfgDungeonPlayer.ProtoReflect.Descriptor instead. +func (*CompleteLfgDungeonPlayer) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{30} +} + +func (x *CompleteLfgDungeonPlayer) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *CompleteLfgDungeonPlayer) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +type CompleteLfgDungeonRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + CompletedDungeonEntry uint32 `protobuf:"varint,2,opt,name=completedDungeonEntry,proto3" json:"completedDungeonEntry,omitempty"` + SelectedDungeonEntry uint32 `protobuf:"varint,3,opt,name=selectedDungeonEntry,proto3" json:"selectedDungeonEntry,omitempty"` + Players []*CompleteLfgDungeonPlayer `protobuf:"bytes,4,rep,name=players,proto3" json:"players,omitempty"` +} + +func (x *CompleteLfgDungeonRequest) Reset() { + *x = CompleteLfgDungeonRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CompleteLfgDungeonRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CompleteLfgDungeonRequest) ProtoMessage() {} + +func (x *CompleteLfgDungeonRequest) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CompleteLfgDungeonRequest.ProtoReflect.Descriptor instead. +func (*CompleteLfgDungeonRequest) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{31} +} + +func (x *CompleteLfgDungeonRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *CompleteLfgDungeonRequest) GetCompletedDungeonEntry() uint32 { + if x != nil { + return x.CompletedDungeonEntry + } + return 0 +} + +func (x *CompleteLfgDungeonRequest) GetSelectedDungeonEntry() uint32 { + if x != nil { + return x.SelectedDungeonEntry + } + return 0 +} + +func (x *CompleteLfgDungeonRequest) GetPlayers() []*CompleteLfgDungeonPlayer { + if x != nil { + return x.Players + } + return nil +} + +type CompleteLfgDungeonResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` +} + +func (x *CompleteLfgDungeonResponse) Reset() { + *x = CompleteLfgDungeonResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CompleteLfgDungeonResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CompleteLfgDungeonResponse) ProtoMessage() {} + +func (x *CompleteLfgDungeonResponse) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CompleteLfgDungeonResponse.ProtoReflect.Descriptor instead. +func (*CompleteLfgDungeonResponse) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{32} +} + +func (x *CompleteLfgDungeonResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +type BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AssignedBattlegroundInstanceID uint32 `protobuf:"varint,1,opt,name=assignedBattlegroundInstanceID,proto3" json:"assignedBattlegroundInstanceID,omitempty"` + MapID uint32 `protobuf:"varint,2,opt,name=mapID,proto3" json:"mapID,omitempty"` + BattlegroupID uint32 `protobuf:"varint,3,opt,name=battlegroupID,proto3" json:"battlegroupID,omitempty"` + GameserverAddress string `protobuf:"bytes,4,opt,name=gameserverAddress,proto3" json:"gameserverAddress,omitempty"` + GameserverGRPCAddress string `protobuf:"bytes,5,opt,name=gameserverGRPCAddress,proto3" json:"gameserverGRPCAddress,omitempty"` + AssignedTeamID PVPTeamID `protobuf:"varint,6,opt,name=assignedTeamID,proto3,enum=v1.PVPTeamID" json:"assignedTeamID,omitempty"` +} + +func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) Reset() { + *x = BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) ProtoMessage() {} + +func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData.ProtoReflect.Descriptor instead. +func (*BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{5, 0} +} + +func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) GetAssignedBattlegroundInstanceID() uint32 { + if x != nil { + return x.AssignedBattlegroundInstanceID + } + return 0 +} + +func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) GetMapID() uint32 { + if x != nil { + return x.MapID + } + return 0 +} + +func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) GetBattlegroupID() uint32 { + if x != nil { + return x.BattlegroupID + } + return 0 +} + +func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) GetGameserverAddress() string { + if x != nil { + return x.GameserverAddress + } + return "" +} + +func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) GetGameserverGRPCAddress() string { + if x != nil { + return x.GameserverGRPCAddress + } + return "" +} + +func (x *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData) GetAssignedTeamID() PVPTeamID { + if x != nil { + return x.AssignedTeamID + } + return PVPTeamID_Any +} + +type BattlegroundQueueDataForPlayerResponse_QueueSlot struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BgTypeID uint32 `protobuf:"varint,1,opt,name=bgTypeID,proto3" json:"bgTypeID,omitempty"` + Status PlayerQueueStatus `protobuf:"varint,2,opt,name=status,proto3,enum=v1.PlayerQueueStatus" json:"status,omitempty"` + AssignedBattlegroundData *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData `protobuf:"bytes,3,opt,name=assignedBattlegroundData,proto3,oneof" json:"assignedBattlegroundData,omitempty"` +} + +func (x *BattlegroundQueueDataForPlayerResponse_QueueSlot) Reset() { + *x = BattlegroundQueueDataForPlayerResponse_QueueSlot{} + if protoimpl.UnsafeEnabled { + mi := &file_matchmaking_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BattlegroundQueueDataForPlayerResponse_QueueSlot) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BattlegroundQueueDataForPlayerResponse_QueueSlot) ProtoMessage() {} + +func (x *BattlegroundQueueDataForPlayerResponse_QueueSlot) ProtoReflect() protoreflect.Message { + mi := &file_matchmaking_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BattlegroundQueueDataForPlayerResponse_QueueSlot.ProtoReflect.Descriptor instead. +func (*BattlegroundQueueDataForPlayerResponse_QueueSlot) Descriptor() ([]byte, []int) { + return file_matchmaking_proto_rawDescGZIP(), []int{5, 1} +} + +func (x *BattlegroundQueueDataForPlayerResponse_QueueSlot) GetBgTypeID() uint32 { + if x != nil { + return x.BgTypeID + } + return 0 +} + +func (x *BattlegroundQueueDataForPlayerResponse_QueueSlot) GetStatus() PlayerQueueStatus { + if x != nil { + return x.Status + } + return PlayerQueueStatus_NotInQueue +} + +func (x *BattlegroundQueueDataForPlayerResponse_QueueSlot) GetAssignedBattlegroundData() *BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData { + if x != nil { + return x.AssignedBattlegroundData + } + return nil +} + +var File_matchmaking_proto protoreflect.FileDescriptor + +var file_matchmaking_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, 0x22, 0xa9, 0x02, 0x0a, 0x1c, 0x45, 0x6e, 0x71, 0x75, + 0x65, 0x75, 0x65, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x74, 0x79, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x74, + 0x79, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x73, 0x4c, 0x76, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x73, 0x4c, 0x76, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x67, 0x54, 0x79, + 0x70, 0x65, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x62, 0x67, 0x54, 0x79, + 0x70, 0x65, 0x49, 0x44, 0x12, 0x25, 0x0a, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x56, 0x50, 0x54, 0x65, 0x61, + 0x6d, 0x49, 0x44, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x61, + 0x72, 0x65, 0x6e, 0x61, 0x54, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, + 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x73, 0x52, + 0x61, 0x74, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x52, 0x61, + 0x74, 0x65, 0x64, 0x22, 0x31, 0x0a, 0x1d, 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x54, 0x6f, + 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x96, 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x51, 0x75, 0x65, 0x75, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x12, 0x2a, 0x0a, 0x10, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x62, + 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x22, + 0x31, 0x0a, 0x1d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x46, + 0x72, 0x6f, 0x6d, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x22, 0x73, 0x0a, 0x25, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, + 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, + 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0xbf, 0x05, 0x0a, 0x26, 0x42, 0x61, 0x74, 0x74, + 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x46, 0x6f, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x4a, 0x0a, 0x05, 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, + 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x51, 0x75, 0x65, 0x75, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x05, 0x73, 0x6c, 0x6f, 0x74, 0x73, + 0x1a, 0xb9, 0x02, 0x0a, 0x18, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, + 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x61, 0x12, 0x46, 0x0a, + 0x1e, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x1e, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, + 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x62, + 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0d, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, + 0x44, 0x12, 0x2c, 0x0a, 0x11, 0x67, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x67, 0x61, + 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x34, 0x0a, 0x15, 0x67, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x52, 0x50, + 0x43, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, + 0x67, 0x61, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x35, 0x0a, 0x0e, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x56, 0x50, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x0e, 0x61, 0x73, + 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x1a, 0xfa, 0x01, 0x0a, + 0x09, 0x51, 0x75, 0x65, 0x75, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x67, + 0x54, 0x79, 0x70, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x62, 0x67, + 0x54, 0x79, 0x70, 0x65, 0x49, 0x44, 0x12, 0x2d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x51, 0x75, 0x65, 0x75, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x84, 0x01, 0x0a, 0x18, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, + 0x65, 0x64, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x61, + 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, + 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, + 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, + 0x18, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01, 0x42, 0x1b, 0x0a, 0x19, + 0x5f, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x61, 0x22, 0xaf, 0x01, 0x0a, 0x1d, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x4c, 0x65, 0x66, 0x74, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, + 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, + 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, + 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, + 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x22, 0x32, 0x0a, 0x1e, 0x50, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4c, 0x65, 0x66, 0x74, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, + 0xb1, 0x01, 0x0a, 0x1f, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, + 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, + 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, + 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, + 0x22, 0x0a, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, + 0x61, 0x6c, 0x6d, 0x22, 0x34, 0x0a, 0x20, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x6f, 0x69, + 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0xa3, 0x02, 0x0a, 0x20, 0x42, 0x61, + 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, + 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x73, + 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x12, 0x43, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, + 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x22, 0x4a, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x08, 0x0a, + 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x61, 0x69, 0x74, 0x51, + 0x75, 0x65, 0x75, 0x65, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x57, 0x61, 0x69, 0x74, 0x4a, 0x6f, + 0x69, 0x6e, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x6e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, + 0x73, 0x73, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x10, 0x04, 0x22, + 0x35, 0x0a, 0x21, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x5a, 0x0a, 0x15, 0x52, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x72, 0x65, 0x6e, 0x61, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x12, + 0x21, 0x0a, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x56, 0x50, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x04, 0x74, 0x65, + 0x61, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x22, 0xbd, 0x01, 0x0a, 0x13, 0x52, 0x61, 0x74, 0x65, 0x64, 0x41, 0x72, 0x65, 0x6e, + 0x61, 0x54, 0x65, 0x61, 0x6d, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, + 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, + 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x61, 0x72, 0x65, 0x6e, 0x61, + 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x61, 0x6d, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x61, 0x6d, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x6d, + 0x61, 0x6b, 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x10, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, + 0x6e, 0x67, 0x22, 0xab, 0x02, 0x0a, 0x16, 0x52, 0x61, 0x74, 0x65, 0x64, 0x41, 0x72, 0x65, 0x6e, + 0x61, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x21, 0x0a, + 0x04, 0x74, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x76, 0x31, + 0x2e, 0x50, 0x56, 0x50, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x04, 0x74, 0x65, 0x61, 0x6d, + 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, + 0x12, 0x26, 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x61, 0x74, 0x69, + 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, + 0x61, 0x6c, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x65, 0x65, 0x6b, + 0x47, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x77, 0x65, 0x65, + 0x6b, 0x47, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x47, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x47, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x65, 0x65, 0x6b, + 0x57, 0x69, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x77, 0x65, 0x65, 0x6b, + 0x57, 0x69, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x57, 0x69, + 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x57, 0x69, 0x6e, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, + 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, + 0x22, 0xa8, 0x04, 0x0a, 0x1c, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x52, 0x61, 0x74, 0x65, 0x64, + 0x41, 0x72, 0x65, 0x6e, 0x61, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x22, 0x0a, 0x0c, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6f, 0x77, 0x6e, 0x65, 0x72, + 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, + 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, + 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x61, + 0x72, 0x65, 0x6e, 0x61, 0x54, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, + 0x61, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x0a, 0x77, 0x69, 0x6e, + 0x6e, 0x65, 0x72, 0x54, 0x65, 0x61, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x56, 0x50, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x52, 0x0a, 0x77, 0x69, + 0x6e, 0x6e, 0x65, 0x72, 0x54, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x12, 0x30, 0x0a, 0x13, 0x61, 0x6c, 0x6c, 0x69, + 0x61, 0x6e, 0x63, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x61, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x41, + 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x2a, 0x0a, 0x10, 0x68, 0x6f, + 0x72, 0x64, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, + 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x44, 0x0a, 0x1d, 0x61, 0x6c, 0x6c, 0x69, 0x61, 0x6e, + 0x63, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x65, + 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x1d, 0x61, + 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x4d, 0x61, 0x74, 0x63, + 0x68, 0x6d, 0x61, 0x6b, 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x3e, 0x0a, 0x1a, + 0x68, 0x6f, 0x72, 0x64, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x6d, + 0x61, 0x6b, 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x1a, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x4d, 0x61, 0x74, 0x63, + 0x68, 0x6d, 0x61, 0x6b, 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x3d, 0x0a, 0x0c, + 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x64, 0x41, 0x72, 0x65, + 0x6e, 0x61, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x52, 0x0c, 0x70, + 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x73, 0x22, 0xab, 0x02, 0x0a, 0x1d, + 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x52, 0x61, 0x74, 0x65, 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, + 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x3e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x69, 0x6e, 0x67, + 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x3d, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x63, 0x6f, 0x72, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x52, + 0x0d, 0x61, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x37, + 0x0a, 0x0a, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x64, 0x41, 0x72, 0x65, + 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x52, 0x0a, 0x68, 0x6f, 0x72, + 0x64, 0x65, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0d, 0x6d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0xf3, 0x01, 0x0a, 0x09, 0x4c, 0x66, + 0x67, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x0a, + 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6c, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, + 0x72, 0x6c, 0x64, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x2e, 0x0a, 0x12, 0x71, 0x75, 0x65, 0x75, 0x65, 0x4c, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x12, 0x71, 0x75, 0x65, 0x75, 0x65, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, + 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x28, 0x0a, 0x0f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x4c, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, + 0x71, 0x75, 0x65, 0x75, 0x65, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, + 0xcf, 0x01, 0x0a, 0x11, 0x4c, 0x66, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x73, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x61, + 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0c, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x61, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x08, 0x61, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x61, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x22, 0xf9, 0x03, 0x0a, 0x0d, 0x4c, 0x66, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x44, + 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x0c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x66, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x6f, + 0x73, 0x61, 0x6c, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x70, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x49, 0x44, 0x12, 0x3a, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, + 0x73, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x66, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, + 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x0d, 0x52, 0x10, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x44, 0x75, 0x6e, 0x67, 0x65, + 0x6f, 0x6e, 0x73, 0x12, 0x33, 0x0a, 0x0d, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x66, 0x67, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x75, 0x65, + 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x3f, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x66, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x36, 0x0a, 0x16, 0x71, 0x75, 0x65, + 0x75, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x71, 0x75, 0x65, 0x75, 0x65, + 0x64, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x61, 0x6e, 0x6b, 0x73, 0x4e, 0x65, 0x65, 0x64, 0x65, 0x64, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x74, 0x61, 0x6e, 0x6b, 0x73, 0x4e, 0x65, 0x65, + 0x64, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x68, 0x65, 0x61, 0x6c, 0x65, 0x72, 0x73, 0x4e, 0x65, + 0x65, 0x64, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x68, 0x65, 0x61, 0x6c, + 0x65, 0x72, 0x73, 0x4e, 0x65, 0x65, 0x64, 0x65, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x61, 0x6d, + 0x61, 0x67, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0c, 0x64, 0x61, 0x6d, 0x61, 0x67, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x65, 0x64, 0x22, 0xc7, 0x01, + 0x0a, 0x0e, 0x4a, 0x6f, 0x69, 0x6e, 0x4c, 0x66, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, + 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0a, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x27, 0x0a, 0x07, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x66, 0x67, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x07, 0x6d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, + 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0e, 0x64, + 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x79, 0x0a, 0x0f, 0x4a, 0x6f, 0x69, 0x6e, 0x4c, + 0x66, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, + 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x29, 0x0a, 0x06, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x66, 0x67, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, + 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x66, 0x67, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x44, 0x61, 0x74, 0x61, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x22, 0x5d, 0x0a, 0x0f, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x4c, 0x66, 0x67, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, + 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, + 0x44, 0x22, 0x24, 0x0a, 0x10, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x4c, 0x66, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x76, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x4c, 0x66, + 0x67, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x6c, + 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x22, + 0x52, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x66, + 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x44, 0x61, 0x74, 0x61, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x22, 0x9e, 0x01, 0x0a, 0x18, 0x41, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x4c, 0x66, + 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, + 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, + 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x61, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x22, 0x58, 0x0a, 0x19, 0x41, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x4c, 0x66, + 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x66, 0x67, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x44, 0x61, 0x74, 0x61, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x5e, + 0x0a, 0x10, 0x4c, 0x66, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, + 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0x50, + 0x0a, 0x11, 0x4c, 0x66, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x66, 0x67, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x44, 0x61, 0x74, 0x61, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x22, 0x54, 0x0a, 0x18, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x66, 0x67, 0x44, + 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, + 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, + 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0xcf, 0x01, 0x0a, 0x19, 0x43, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x34, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, + 0x74, 0x65, 0x64, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, + 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x32, 0x0a, 0x14, + 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x73, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x36, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4c, + 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, + 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x22, 0x2e, 0x0a, 0x1a, 0x43, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x2a, 0x2d, 0x0a, 0x09, 0x50, 0x56, 0x50, 0x54, + 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x6e, 0x79, 0x10, 0x00, 0x12, 0x0c, + 0x0a, 0x08, 0x41, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, + 0x48, 0x6f, 0x72, 0x64, 0x65, 0x10, 0x02, 0x2a, 0x4d, 0x0a, 0x11, 0x50, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x51, 0x75, 0x65, 0x75, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0e, 0x0a, 0x0a, + 0x4e, 0x6f, 0x74, 0x49, 0x6e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, + 0x49, 0x6e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x6e, 0x76, + 0x69, 0x74, 0x65, 0x64, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x6e, 0x50, 0x72, 0x6f, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x10, 0x03, 0x2a, 0xd7, 0x03, 0x0a, 0x22, 0x4d, 0x61, 0x74, 0x63, 0x68, + 0x6d, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x4d, + 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x0a, + 0x22, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x4d, 0x41, 0x4b, 0x49, 0x4e, 0x47, 0x5f, 0x41, 0x52, 0x45, + 0x4e, 0x41, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x2d, 0x0a, 0x29, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x4d, 0x41, + 0x4b, 0x49, 0x4e, 0x47, 0x5f, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x5f, + 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x55, + 0x4e, 0x44, 0x10, 0x01, 0x12, 0x33, 0x0a, 0x2f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x4d, 0x41, 0x4b, + 0x49, 0x4e, 0x47, 0x5f, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, + 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x5f, 0x4d, + 0x49, 0x53, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x10, 0x02, 0x12, 0x30, 0x0a, 0x2c, 0x4d, 0x41, 0x54, + 0x43, 0x48, 0x4d, 0x41, 0x4b, 0x49, 0x4e, 0x47, 0x5f, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, + 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x56, + 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x03, 0x12, 0x2f, 0x0a, 0x2b, 0x4d, + 0x41, 0x54, 0x43, 0x48, 0x4d, 0x41, 0x4b, 0x49, 0x4e, 0x47, 0x5f, 0x41, 0x52, 0x45, 0x4e, 0x41, + 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, + 0x41, 0x4d, 0x45, 0x5f, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x10, 0x04, 0x12, 0x33, 0x0a, 0x2f, + 0x4d, 0x41, 0x54, 0x43, 0x48, 0x4d, 0x41, 0x4b, 0x49, 0x4e, 0x47, 0x5f, 0x41, 0x52, 0x45, 0x4e, + 0x41, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x41, 0x4c, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, 0x49, 0x4e, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x10, + 0x05, 0x12, 0x2f, 0x0a, 0x2b, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x4d, 0x41, 0x4b, 0x49, 0x4e, 0x47, + 0x5f, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x4f, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x46, 0x55, 0x4c, 0x4c, + 0x10, 0x06, 0x12, 0x2a, 0x0a, 0x26, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x4d, 0x41, 0x4b, 0x49, 0x4e, + 0x47, 0x5f, 0x41, 0x52, 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, + 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x07, 0x12, 0x30, + 0x0a, 0x2c, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x4d, 0x41, 0x4b, 0x49, 0x4e, 0x47, 0x5f, 0x41, 0x52, + 0x45, 0x4e, 0x41, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, + 0x4e, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x10, 0x08, + 0x2a, 0xcb, 0x01, 0x0a, 0x08, 0x4c, 0x66, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, + 0x0e, 0x4c, 0x46, 0x47, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, + 0x00, 0x12, 0x17, 0x0a, 0x13, 0x4c, 0x46, 0x47, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x52, + 0x4f, 0x4c, 0x45, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x4c, 0x46, + 0x47, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x51, 0x55, 0x45, 0x55, 0x45, 0x44, 0x10, 0x02, + 0x12, 0x16, 0x0a, 0x12, 0x4c, 0x46, 0x47, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x50, 0x52, + 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x46, 0x47, 0x5f, + 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x42, 0x4f, 0x4f, 0x54, 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, + 0x4c, 0x46, 0x47, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x55, 0x4e, 0x47, 0x45, 0x4f, + 0x4e, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, 0x4c, 0x46, 0x47, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, + 0x5f, 0x46, 0x49, 0x4e, 0x49, 0x53, 0x48, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x4e, 0x47, 0x45, 0x4f, + 0x4e, 0x10, 0x06, 0x12, 0x19, 0x0a, 0x15, 0x4c, 0x46, 0x47, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, + 0x5f, 0x52, 0x41, 0x49, 0x44, 0x42, 0x52, 0x4f, 0x57, 0x53, 0x45, 0x52, 0x10, 0x07, 0x2a, 0x62, + 0x0a, 0x10, 0x4c, 0x66, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x4c, 0x46, 0x47, 0x5f, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, + 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, + 0x17, 0x0a, 0x13, 0x4c, 0x46, 0x47, 0x5f, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, + 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x4c, 0x46, 0x47, 0x5f, + 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, + 0x10, 0x02, 0x2a, 0xf0, 0x03, 0x0a, 0x0d, 0x4c, 0x66, 0x67, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x46, 0x47, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, + 0x5f, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x46, 0x47, 0x5f, 0x4a, 0x4f, 0x49, + 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x4c, 0x46, + 0x47, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x46, 0x55, 0x4c, + 0x4c, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x4c, 0x46, 0x47, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, 0x5f, + 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, + 0x12, 0x1a, 0x0a, 0x16, 0x4c, 0x46, 0x47, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, 0x5f, 0x4e, 0x4f, 0x54, + 0x5f, 0x4d, 0x45, 0x45, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x53, 0x10, 0x05, 0x12, 0x20, 0x0a, 0x1c, + 0x4c, 0x46, 0x47, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, 0x5f, 0x50, 0x41, 0x52, 0x54, 0x59, 0x5f, 0x4e, + 0x4f, 0x54, 0x5f, 0x4d, 0x45, 0x45, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x53, 0x10, 0x06, 0x12, 0x1f, + 0x0a, 0x1b, 0x4c, 0x46, 0x47, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, 0x5f, 0x4d, 0x49, 0x58, 0x45, 0x44, + 0x5f, 0x52, 0x41, 0x49, 0x44, 0x5f, 0x44, 0x55, 0x4e, 0x47, 0x45, 0x4f, 0x4e, 0x10, 0x07, 0x12, + 0x18, 0x0a, 0x14, 0x4c, 0x46, 0x47, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, 0x5f, 0x4d, 0x55, 0x4c, 0x54, + 0x49, 0x5f, 0x52, 0x45, 0x41, 0x4c, 0x4d, 0x10, 0x08, 0x12, 0x19, 0x0a, 0x15, 0x4c, 0x46, 0x47, + 0x5f, 0x4a, 0x4f, 0x49, 0x4e, 0x5f, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, + 0x45, 0x44, 0x10, 0x09, 0x12, 0x1e, 0x0a, 0x1a, 0x4c, 0x46, 0x47, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, + 0x5f, 0x50, 0x41, 0x52, 0x54, 0x59, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x46, 0x41, 0x49, 0x4c, + 0x45, 0x44, 0x10, 0x0a, 0x12, 0x1c, 0x0a, 0x18, 0x4c, 0x46, 0x47, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, + 0x5f, 0x44, 0x55, 0x4e, 0x47, 0x45, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, + 0x10, 0x0b, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x46, 0x47, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, 0x5f, 0x44, + 0x45, 0x53, 0x45, 0x52, 0x54, 0x45, 0x52, 0x10, 0x0c, 0x12, 0x1b, 0x0a, 0x17, 0x4c, 0x46, 0x47, + 0x5f, 0x4a, 0x4f, 0x49, 0x4e, 0x5f, 0x50, 0x41, 0x52, 0x54, 0x59, 0x5f, 0x44, 0x45, 0x53, 0x45, + 0x52, 0x54, 0x45, 0x52, 0x10, 0x0d, 0x12, 0x1c, 0x0a, 0x18, 0x4c, 0x46, 0x47, 0x5f, 0x4a, 0x4f, + 0x49, 0x4e, 0x5f, 0x52, 0x41, 0x4e, 0x44, 0x4f, 0x4d, 0x5f, 0x43, 0x4f, 0x4f, 0x4c, 0x44, 0x4f, + 0x57, 0x4e, 0x10, 0x0e, 0x12, 0x22, 0x0a, 0x1e, 0x4c, 0x46, 0x47, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, + 0x5f, 0x50, 0x41, 0x52, 0x54, 0x59, 0x5f, 0x52, 0x41, 0x4e, 0x44, 0x4f, 0x4d, 0x5f, 0x43, 0x4f, + 0x4f, 0x4c, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x0f, 0x12, 0x1d, 0x0a, 0x19, 0x4c, 0x46, 0x47, 0x5f, + 0x4a, 0x4f, 0x49, 0x4e, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x4d, 0x41, 0x4e, 0x59, 0x5f, 0x4d, 0x45, + 0x4d, 0x42, 0x45, 0x52, 0x53, 0x10, 0x10, 0x12, 0x1c, 0x0a, 0x18, 0x4c, 0x46, 0x47, 0x5f, 0x4a, + 0x4f, 0x49, 0x4e, 0x5f, 0x55, 0x53, 0x49, 0x4e, 0x47, 0x5f, 0x42, 0x47, 0x5f, 0x53, 0x59, 0x53, + 0x54, 0x45, 0x4d, 0x10, 0x11, 0x32, 0xe5, 0x08, 0x0a, 0x12, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x6d, + 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5c, 0x0a, 0x15, + 0x45, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x71, 0x75, 0x65, + 0x75, 0x65, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x71, + 0x75, 0x65, 0x75, 0x65, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x15, 0x52, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x51, 0x75, + 0x65, 0x75, 0x65, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x51, 0x75, 0x65, 0x75, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x1e, 0x42, 0x61, 0x74, 0x74, + 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x46, 0x6f, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x76, 0x31, 0x2e, + 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, @@ -1257,9 +3450,39 @@ var file_matchmaking_proto_rawDesc = []byte{ 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x14, 0x5a, 0x12, 0x67, 0x65, 0x6e, 0x2f, 0x6d, 0x61, 0x74, 0x63, 0x68, - 0x6d, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x15, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x52, 0x61, 0x74, + 0x65, 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x20, 0x2e, 0x76, + 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x52, 0x61, 0x74, 0x65, 0x64, 0x41, 0x72, 0x65, + 0x6e, 0x61, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, + 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x52, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x72, 0x65, 0x6e, 0x61, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x32, 0x0a, 0x07, 0x4a, 0x6f, 0x69, 0x6e, 0x4c, 0x66, 0x67, 0x12, 0x12, 0x2e, 0x76, + 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x4c, 0x66, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x13, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x4c, 0x66, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x4c, 0x66, + 0x67, 0x12, 0x13, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x4c, 0x66, 0x67, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, + 0x65, 0x4c, 0x66, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0b, + 0x53, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x52, + 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x11, + 0x41, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x4c, 0x66, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, + 0x6c, 0x12, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x4c, 0x66, 0x67, + 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x4c, 0x66, 0x67, 0x50, 0x72, + 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, + 0x0a, 0x09, 0x4c, 0x66, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x66, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x15, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x66, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x12, 0x1d, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x66, 0x67, 0x44, + 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x66, 0x67, 0x44, 0x75, + 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x14, 0x5a, + 0x12, 0x67, 0x65, 0x6e, 0x2f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x69, 0x6e, 0x67, + 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1274,50 +3497,109 @@ func file_matchmaking_proto_rawDescGZIP() []byte { return file_matchmaking_proto_rawDescData } -var file_matchmaking_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_matchmaking_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_matchmaking_proto_enumTypes = make([]protoimpl.EnumInfo, 7) +var file_matchmaking_proto_msgTypes = make([]protoimpl.MessageInfo, 35) var file_matchmaking_proto_goTypes = []interface{}{ - (PVPTeamID)(0), // 0: v1.PVPTeamID - (PlayerQueueStatus)(0), // 1: v1.PlayerQueueStatus - (BattlegroundStatusChangedRequest_Status)(0), // 2: v1.BattlegroundStatusChangedRequest.Status - (*EnqueueToBattlegroundRequest)(nil), // 3: v1.EnqueueToBattlegroundRequest - (*EnqueueToBattlegroundResponse)(nil), // 4: v1.EnqueueToBattlegroundResponse - (*RemovePlayerFromQueueRequest)(nil), // 5: v1.RemovePlayerFromQueueRequest - (*RemovePlayerFromQueueResponse)(nil), // 6: v1.RemovePlayerFromQueueResponse - (*BattlegroundQueueDataForPlayerRequest)(nil), // 7: v1.BattlegroundQueueDataForPlayerRequest - (*BattlegroundQueueDataForPlayerResponse)(nil), // 8: v1.BattlegroundQueueDataForPlayerResponse - (*PlayerLeftBattlegroundRequest)(nil), // 9: v1.PlayerLeftBattlegroundRequest - (*PlayerLeftBattlegroundResponse)(nil), // 10: v1.PlayerLeftBattlegroundResponse - (*PlayerJoinedBattlegroundRequest)(nil), // 11: v1.PlayerJoinedBattlegroundRequest - (*PlayerJoinedBattlegroundResponse)(nil), // 12: v1.PlayerJoinedBattlegroundResponse - (*BattlegroundStatusChangedRequest)(nil), // 13: v1.BattlegroundStatusChangedRequest - (*BattlegroundStatusChangedResponse)(nil), // 14: v1.BattlegroundStatusChangedResponse - (*BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData)(nil), // 15: v1.BattlegroundQueueDataForPlayerResponse.AssignedBattlegroundData - (*BattlegroundQueueDataForPlayerResponse_QueueSlot)(nil), // 16: v1.BattlegroundQueueDataForPlayerResponse.QueueSlot + (PVPTeamID)(0), // 0: v1.PVPTeamID + (PlayerQueueStatus)(0), // 1: v1.PlayerQueueStatus + (MatchmakingArenaTeamMutationStatus)(0), // 2: v1.MatchmakingArenaTeamMutationStatus + (LfgState)(0), // 3: v1.LfgState + (LfgProposalState)(0), // 4: v1.LfgProposalState + (LfgJoinResult)(0), // 5: v1.LfgJoinResult + (BattlegroundStatusChangedRequest_Status)(0), // 6: v1.BattlegroundStatusChangedRequest.Status + (*EnqueueToBattlegroundRequest)(nil), // 7: v1.EnqueueToBattlegroundRequest + (*EnqueueToBattlegroundResponse)(nil), // 8: v1.EnqueueToBattlegroundResponse + (*RemovePlayerFromQueueRequest)(nil), // 9: v1.RemovePlayerFromQueueRequest + (*RemovePlayerFromQueueResponse)(nil), // 10: v1.RemovePlayerFromQueueResponse + (*BattlegroundQueueDataForPlayerRequest)(nil), // 11: v1.BattlegroundQueueDataForPlayerRequest + (*BattlegroundQueueDataForPlayerResponse)(nil), // 12: v1.BattlegroundQueueDataForPlayerResponse + (*PlayerLeftBattlegroundRequest)(nil), // 13: v1.PlayerLeftBattlegroundRequest + (*PlayerLeftBattlegroundResponse)(nil), // 14: v1.PlayerLeftBattlegroundResponse + (*PlayerJoinedBattlegroundRequest)(nil), // 15: v1.PlayerJoinedBattlegroundRequest + (*PlayerJoinedBattlegroundResponse)(nil), // 16: v1.PlayerJoinedBattlegroundResponse + (*BattlegroundStatusChangedRequest)(nil), // 17: v1.BattlegroundStatusChangedRequest + (*BattlegroundStatusChangedResponse)(nil), // 18: v1.BattlegroundStatusChangedResponse + (*RatedArenaParticipant)(nil), // 19: v1.RatedArenaParticipant + (*RatedArenaTeamScore)(nil), // 20: v1.RatedArenaTeamScore + (*RatedArenaMemberResult)(nil), // 21: v1.RatedArenaMemberResult + (*FinishRatedArenaMatchRequest)(nil), // 22: v1.FinishRatedArenaMatchRequest + (*FinishRatedArenaMatchResponse)(nil), // 23: v1.FinishRatedArenaMatchResponse + (*LfgMember)(nil), // 24: v1.LfgMember + (*LfgProposalMember)(nil), // 25: v1.LfgProposalMember + (*LfgStatusData)(nil), // 26: v1.LfgStatusData + (*JoinLfgRequest)(nil), // 27: v1.JoinLfgRequest + (*JoinLfgResponse)(nil), // 28: v1.JoinLfgResponse + (*LeaveLfgRequest)(nil), // 29: v1.LeaveLfgRequest + (*LeaveLfgResponse)(nil), // 30: v1.LeaveLfgResponse + (*SetLfgRolesRequest)(nil), // 31: v1.SetLfgRolesRequest + (*SetLfgRolesResponse)(nil), // 32: v1.SetLfgRolesResponse + (*AnswerLfgProposalRequest)(nil), // 33: v1.AnswerLfgProposalRequest + (*AnswerLfgProposalResponse)(nil), // 34: v1.AnswerLfgProposalResponse + (*LfgStatusRequest)(nil), // 35: v1.LfgStatusRequest + (*LfgStatusResponse)(nil), // 36: v1.LfgStatusResponse + (*CompleteLfgDungeonPlayer)(nil), // 37: v1.CompleteLfgDungeonPlayer + (*CompleteLfgDungeonRequest)(nil), // 38: v1.CompleteLfgDungeonRequest + (*CompleteLfgDungeonResponse)(nil), // 39: v1.CompleteLfgDungeonResponse + (*BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData)(nil), // 40: v1.BattlegroundQueueDataForPlayerResponse.AssignedBattlegroundData + (*BattlegroundQueueDataForPlayerResponse_QueueSlot)(nil), // 41: v1.BattlegroundQueueDataForPlayerResponse.QueueSlot } var file_matchmaking_proto_depIdxs = []int32{ 0, // 0: v1.EnqueueToBattlegroundRequest.teamID:type_name -> v1.PVPTeamID - 16, // 1: v1.BattlegroundQueueDataForPlayerResponse.slots:type_name -> v1.BattlegroundQueueDataForPlayerResponse.QueueSlot - 2, // 2: v1.BattlegroundStatusChangedRequest.status:type_name -> v1.BattlegroundStatusChangedRequest.Status - 1, // 3: v1.BattlegroundQueueDataForPlayerResponse.QueueSlot.status:type_name -> v1.PlayerQueueStatus - 15, // 4: v1.BattlegroundQueueDataForPlayerResponse.QueueSlot.assignedBattlegroundData:type_name -> v1.BattlegroundQueueDataForPlayerResponse.AssignedBattlegroundData - 3, // 5: v1.MatchmakingService.EnqueueToBattleground:input_type -> v1.EnqueueToBattlegroundRequest - 5, // 6: v1.MatchmakingService.RemovePlayerFromQueue:input_type -> v1.RemovePlayerFromQueueRequest - 7, // 7: v1.MatchmakingService.BattlegroundQueueDataForPlayer:input_type -> v1.BattlegroundQueueDataForPlayerRequest - 9, // 8: v1.MatchmakingService.PlayerLeftBattleground:input_type -> v1.PlayerLeftBattlegroundRequest - 11, // 9: v1.MatchmakingService.PlayerJoinedBattleground:input_type -> v1.PlayerJoinedBattlegroundRequest - 13, // 10: v1.MatchmakingService.BattlegroundStatusChanged:input_type -> v1.BattlegroundStatusChangedRequest - 4, // 11: v1.MatchmakingService.EnqueueToBattleground:output_type -> v1.EnqueueToBattlegroundResponse - 6, // 12: v1.MatchmakingService.RemovePlayerFromQueue:output_type -> v1.RemovePlayerFromQueueResponse - 8, // 13: v1.MatchmakingService.BattlegroundQueueDataForPlayer:output_type -> v1.BattlegroundQueueDataForPlayerResponse - 10, // 14: v1.MatchmakingService.PlayerLeftBattleground:output_type -> v1.PlayerLeftBattlegroundResponse - 12, // 15: v1.MatchmakingService.PlayerJoinedBattleground:output_type -> v1.PlayerJoinedBattlegroundResponse - 14, // 16: v1.MatchmakingService.BattlegroundStatusChanged:output_type -> v1.BattlegroundStatusChangedResponse - 11, // [11:17] is the sub-list for method output_type - 5, // [5:11] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 41, // 1: v1.BattlegroundQueueDataForPlayerResponse.slots:type_name -> v1.BattlegroundQueueDataForPlayerResponse.QueueSlot + 6, // 2: v1.BattlegroundStatusChangedRequest.status:type_name -> v1.BattlegroundStatusChangedRequest.Status + 0, // 3: v1.RatedArenaParticipant.team:type_name -> v1.PVPTeamID + 0, // 4: v1.RatedArenaMemberResult.team:type_name -> v1.PVPTeamID + 0, // 5: v1.FinishRatedArenaMatchRequest.winnerTeam:type_name -> v1.PVPTeamID + 19, // 6: v1.FinishRatedArenaMatchRequest.participants:type_name -> v1.RatedArenaParticipant + 2, // 7: v1.FinishRatedArenaMatchResponse.status:type_name -> v1.MatchmakingArenaTeamMutationStatus + 20, // 8: v1.FinishRatedArenaMatchResponse.allianceScore:type_name -> v1.RatedArenaTeamScore + 20, // 9: v1.FinishRatedArenaMatchResponse.hordeScore:type_name -> v1.RatedArenaTeamScore + 21, // 10: v1.FinishRatedArenaMatchResponse.memberResults:type_name -> v1.RatedArenaMemberResult + 3, // 11: v1.LfgStatusData.state:type_name -> v1.LfgState + 4, // 12: v1.LfgStatusData.proposalState:type_name -> v1.LfgProposalState + 24, // 13: v1.LfgStatusData.queuedMembers:type_name -> v1.LfgMember + 25, // 14: v1.LfgStatusData.proposalMembers:type_name -> v1.LfgProposalMember + 24, // 15: v1.JoinLfgRequest.members:type_name -> v1.LfgMember + 5, // 16: v1.JoinLfgResponse.result:type_name -> v1.LfgJoinResult + 26, // 17: v1.JoinLfgResponse.status:type_name -> v1.LfgStatusData + 26, // 18: v1.SetLfgRolesResponse.status:type_name -> v1.LfgStatusData + 26, // 19: v1.AnswerLfgProposalResponse.status:type_name -> v1.LfgStatusData + 26, // 20: v1.LfgStatusResponse.status:type_name -> v1.LfgStatusData + 37, // 21: v1.CompleteLfgDungeonRequest.players:type_name -> v1.CompleteLfgDungeonPlayer + 0, // 22: v1.BattlegroundQueueDataForPlayerResponse.AssignedBattlegroundData.assignedTeamID:type_name -> v1.PVPTeamID + 1, // 23: v1.BattlegroundQueueDataForPlayerResponse.QueueSlot.status:type_name -> v1.PlayerQueueStatus + 40, // 24: v1.BattlegroundQueueDataForPlayerResponse.QueueSlot.assignedBattlegroundData:type_name -> v1.BattlegroundQueueDataForPlayerResponse.AssignedBattlegroundData + 7, // 25: v1.MatchmakingService.EnqueueToBattleground:input_type -> v1.EnqueueToBattlegroundRequest + 9, // 26: v1.MatchmakingService.RemovePlayerFromQueue:input_type -> v1.RemovePlayerFromQueueRequest + 11, // 27: v1.MatchmakingService.BattlegroundQueueDataForPlayer:input_type -> v1.BattlegroundQueueDataForPlayerRequest + 13, // 28: v1.MatchmakingService.PlayerLeftBattleground:input_type -> v1.PlayerLeftBattlegroundRequest + 15, // 29: v1.MatchmakingService.PlayerJoinedBattleground:input_type -> v1.PlayerJoinedBattlegroundRequest + 17, // 30: v1.MatchmakingService.BattlegroundStatusChanged:input_type -> v1.BattlegroundStatusChangedRequest + 22, // 31: v1.MatchmakingService.FinishRatedArenaMatch:input_type -> v1.FinishRatedArenaMatchRequest + 27, // 32: v1.MatchmakingService.JoinLfg:input_type -> v1.JoinLfgRequest + 29, // 33: v1.MatchmakingService.LeaveLfg:input_type -> v1.LeaveLfgRequest + 31, // 34: v1.MatchmakingService.SetLfgRoles:input_type -> v1.SetLfgRolesRequest + 33, // 35: v1.MatchmakingService.AnswerLfgProposal:input_type -> v1.AnswerLfgProposalRequest + 35, // 36: v1.MatchmakingService.LfgStatus:input_type -> v1.LfgStatusRequest + 38, // 37: v1.MatchmakingService.CompleteLfgDungeon:input_type -> v1.CompleteLfgDungeonRequest + 8, // 38: v1.MatchmakingService.EnqueueToBattleground:output_type -> v1.EnqueueToBattlegroundResponse + 10, // 39: v1.MatchmakingService.RemovePlayerFromQueue:output_type -> v1.RemovePlayerFromQueueResponse + 12, // 40: v1.MatchmakingService.BattlegroundQueueDataForPlayer:output_type -> v1.BattlegroundQueueDataForPlayerResponse + 14, // 41: v1.MatchmakingService.PlayerLeftBattleground:output_type -> v1.PlayerLeftBattlegroundResponse + 16, // 42: v1.MatchmakingService.PlayerJoinedBattleground:output_type -> v1.PlayerJoinedBattlegroundResponse + 18, // 43: v1.MatchmakingService.BattlegroundStatusChanged:output_type -> v1.BattlegroundStatusChangedResponse + 23, // 44: v1.MatchmakingService.FinishRatedArenaMatch:output_type -> v1.FinishRatedArenaMatchResponse + 28, // 45: v1.MatchmakingService.JoinLfg:output_type -> v1.JoinLfgResponse + 30, // 46: v1.MatchmakingService.LeaveLfg:output_type -> v1.LeaveLfgResponse + 32, // 47: v1.MatchmakingService.SetLfgRoles:output_type -> v1.SetLfgRolesResponse + 34, // 48: v1.MatchmakingService.AnswerLfgProposal:output_type -> v1.AnswerLfgProposalResponse + 36, // 49: v1.MatchmakingService.LfgStatus:output_type -> v1.LfgStatusResponse + 39, // 50: v1.MatchmakingService.CompleteLfgDungeon:output_type -> v1.CompleteLfgDungeonResponse + 38, // [38:51] is the sub-list for method output_type + 25, // [25:38] is the sub-list for method input_type + 25, // [25:25] is the sub-list for extension type_name + 25, // [25:25] is the sub-list for extension extendee + 0, // [0:25] is the sub-list for field type_name } func init() { file_matchmaking_proto_init() } @@ -1471,7 +3753,7 @@ func file_matchmaking_proto_init() { } } file_matchmaking_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData); i { + switch v := v.(*RatedArenaParticipant); i { case 0: return &v.state case 1: @@ -1483,6 +3765,258 @@ func file_matchmaking_proto_init() { } } file_matchmaking_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RatedArenaTeamScore); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RatedArenaMemberResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FinishRatedArenaMatchRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FinishRatedArenaMatchResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LfgMember); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LfgProposalMember); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LfgStatusData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*JoinLfgRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*JoinLfgResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LeaveLfgRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LeaveLfgResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetLfgRolesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetLfgRolesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AnswerLfgProposalRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AnswerLfgProposalResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LfgStatusRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LfgStatusResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CompleteLfgDungeonPlayer); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CompleteLfgDungeonRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CompleteLfgDungeonResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BattlegroundQueueDataForPlayerResponse_AssignedBattlegroundData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_matchmaking_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BattlegroundQueueDataForPlayerResponse_QueueSlot); i { case 0: return &v.state @@ -1495,14 +4029,14 @@ func file_matchmaking_proto_init() { } } } - file_matchmaking_proto_msgTypes[13].OneofWrappers = []interface{}{} + file_matchmaking_proto_msgTypes[34].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_matchmaking_proto_rawDesc, - NumEnums: 3, - NumMessages: 14, + NumEnums: 7, + NumMessages: 35, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/matchmaking/pb/matchmaking_grpc.pb.go b/gen/matchmaking/pb/matchmaking_grpc.pb.go index 3d3bac4..e0cf9bc 100644 --- a/gen/matchmaking/pb/matchmaking_grpc.pb.go +++ b/gen/matchmaking/pb/matchmaking_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v3.20.3 +// - protoc v3.21.12 // source: matchmaking.proto package pb @@ -25,6 +25,13 @@ const ( MatchmakingService_PlayerLeftBattleground_FullMethodName = "/v1.MatchmakingService/PlayerLeftBattleground" MatchmakingService_PlayerJoinedBattleground_FullMethodName = "/v1.MatchmakingService/PlayerJoinedBattleground" MatchmakingService_BattlegroundStatusChanged_FullMethodName = "/v1.MatchmakingService/BattlegroundStatusChanged" + MatchmakingService_FinishRatedArenaMatch_FullMethodName = "/v1.MatchmakingService/FinishRatedArenaMatch" + MatchmakingService_JoinLfg_FullMethodName = "/v1.MatchmakingService/JoinLfg" + MatchmakingService_LeaveLfg_FullMethodName = "/v1.MatchmakingService/LeaveLfg" + MatchmakingService_SetLfgRoles_FullMethodName = "/v1.MatchmakingService/SetLfgRoles" + MatchmakingService_AnswerLfgProposal_FullMethodName = "/v1.MatchmakingService/AnswerLfgProposal" + MatchmakingService_LfgStatus_FullMethodName = "/v1.MatchmakingService/LfgStatus" + MatchmakingService_CompleteLfgDungeon_FullMethodName = "/v1.MatchmakingService/CompleteLfgDungeon" ) // MatchmakingServiceClient is the client API for MatchmakingService service. @@ -37,6 +44,13 @@ type MatchmakingServiceClient interface { PlayerLeftBattleground(ctx context.Context, in *PlayerLeftBattlegroundRequest, opts ...grpc.CallOption) (*PlayerLeftBattlegroundResponse, error) PlayerJoinedBattleground(ctx context.Context, in *PlayerJoinedBattlegroundRequest, opts ...grpc.CallOption) (*PlayerJoinedBattlegroundResponse, error) BattlegroundStatusChanged(ctx context.Context, in *BattlegroundStatusChangedRequest, opts ...grpc.CallOption) (*BattlegroundStatusChangedResponse, error) + FinishRatedArenaMatch(ctx context.Context, in *FinishRatedArenaMatchRequest, opts ...grpc.CallOption) (*FinishRatedArenaMatchResponse, error) + JoinLfg(ctx context.Context, in *JoinLfgRequest, opts ...grpc.CallOption) (*JoinLfgResponse, error) + LeaveLfg(ctx context.Context, in *LeaveLfgRequest, opts ...grpc.CallOption) (*LeaveLfgResponse, error) + SetLfgRoles(ctx context.Context, in *SetLfgRolesRequest, opts ...grpc.CallOption) (*SetLfgRolesResponse, error) + AnswerLfgProposal(ctx context.Context, in *AnswerLfgProposalRequest, opts ...grpc.CallOption) (*AnswerLfgProposalResponse, error) + LfgStatus(ctx context.Context, in *LfgStatusRequest, opts ...grpc.CallOption) (*LfgStatusResponse, error) + CompleteLfgDungeon(ctx context.Context, in *CompleteLfgDungeonRequest, opts ...grpc.CallOption) (*CompleteLfgDungeonResponse, error) } type matchmakingServiceClient struct { @@ -101,6 +115,69 @@ func (c *matchmakingServiceClient) BattlegroundStatusChanged(ctx context.Context return out, nil } +func (c *matchmakingServiceClient) FinishRatedArenaMatch(ctx context.Context, in *FinishRatedArenaMatchRequest, opts ...grpc.CallOption) (*FinishRatedArenaMatchResponse, error) { + out := new(FinishRatedArenaMatchResponse) + err := c.cc.Invoke(ctx, MatchmakingService_FinishRatedArenaMatch_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *matchmakingServiceClient) JoinLfg(ctx context.Context, in *JoinLfgRequest, opts ...grpc.CallOption) (*JoinLfgResponse, error) { + out := new(JoinLfgResponse) + err := c.cc.Invoke(ctx, MatchmakingService_JoinLfg_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *matchmakingServiceClient) LeaveLfg(ctx context.Context, in *LeaveLfgRequest, opts ...grpc.CallOption) (*LeaveLfgResponse, error) { + out := new(LeaveLfgResponse) + err := c.cc.Invoke(ctx, MatchmakingService_LeaveLfg_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *matchmakingServiceClient) SetLfgRoles(ctx context.Context, in *SetLfgRolesRequest, opts ...grpc.CallOption) (*SetLfgRolesResponse, error) { + out := new(SetLfgRolesResponse) + err := c.cc.Invoke(ctx, MatchmakingService_SetLfgRoles_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *matchmakingServiceClient) AnswerLfgProposal(ctx context.Context, in *AnswerLfgProposalRequest, opts ...grpc.CallOption) (*AnswerLfgProposalResponse, error) { + out := new(AnswerLfgProposalResponse) + err := c.cc.Invoke(ctx, MatchmakingService_AnswerLfgProposal_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *matchmakingServiceClient) LfgStatus(ctx context.Context, in *LfgStatusRequest, opts ...grpc.CallOption) (*LfgStatusResponse, error) { + out := new(LfgStatusResponse) + err := c.cc.Invoke(ctx, MatchmakingService_LfgStatus_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *matchmakingServiceClient) CompleteLfgDungeon(ctx context.Context, in *CompleteLfgDungeonRequest, opts ...grpc.CallOption) (*CompleteLfgDungeonResponse, error) { + out := new(CompleteLfgDungeonResponse) + err := c.cc.Invoke(ctx, MatchmakingService_CompleteLfgDungeon_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MatchmakingServiceServer is the server API for MatchmakingService service. // All implementations must embed UnimplementedMatchmakingServiceServer // for forward compatibility @@ -111,6 +188,13 @@ type MatchmakingServiceServer interface { PlayerLeftBattleground(context.Context, *PlayerLeftBattlegroundRequest) (*PlayerLeftBattlegroundResponse, error) PlayerJoinedBattleground(context.Context, *PlayerJoinedBattlegroundRequest) (*PlayerJoinedBattlegroundResponse, error) BattlegroundStatusChanged(context.Context, *BattlegroundStatusChangedRequest) (*BattlegroundStatusChangedResponse, error) + FinishRatedArenaMatch(context.Context, *FinishRatedArenaMatchRequest) (*FinishRatedArenaMatchResponse, error) + JoinLfg(context.Context, *JoinLfgRequest) (*JoinLfgResponse, error) + LeaveLfg(context.Context, *LeaveLfgRequest) (*LeaveLfgResponse, error) + SetLfgRoles(context.Context, *SetLfgRolesRequest) (*SetLfgRolesResponse, error) + AnswerLfgProposal(context.Context, *AnswerLfgProposalRequest) (*AnswerLfgProposalResponse, error) + LfgStatus(context.Context, *LfgStatusRequest) (*LfgStatusResponse, error) + CompleteLfgDungeon(context.Context, *CompleteLfgDungeonRequest) (*CompleteLfgDungeonResponse, error) mustEmbedUnimplementedMatchmakingServiceServer() } @@ -136,6 +220,27 @@ func (UnimplementedMatchmakingServiceServer) PlayerJoinedBattleground(context.Co func (UnimplementedMatchmakingServiceServer) BattlegroundStatusChanged(context.Context, *BattlegroundStatusChangedRequest) (*BattlegroundStatusChangedResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method BattlegroundStatusChanged not implemented") } +func (UnimplementedMatchmakingServiceServer) FinishRatedArenaMatch(context.Context, *FinishRatedArenaMatchRequest) (*FinishRatedArenaMatchResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FinishRatedArenaMatch not implemented") +} +func (UnimplementedMatchmakingServiceServer) JoinLfg(context.Context, *JoinLfgRequest) (*JoinLfgResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method JoinLfg not implemented") +} +func (UnimplementedMatchmakingServiceServer) LeaveLfg(context.Context, *LeaveLfgRequest) (*LeaveLfgResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method LeaveLfg not implemented") +} +func (UnimplementedMatchmakingServiceServer) SetLfgRoles(context.Context, *SetLfgRolesRequest) (*SetLfgRolesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetLfgRoles not implemented") +} +func (UnimplementedMatchmakingServiceServer) AnswerLfgProposal(context.Context, *AnswerLfgProposalRequest) (*AnswerLfgProposalResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AnswerLfgProposal not implemented") +} +func (UnimplementedMatchmakingServiceServer) LfgStatus(context.Context, *LfgStatusRequest) (*LfgStatusResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method LfgStatus not implemented") +} +func (UnimplementedMatchmakingServiceServer) CompleteLfgDungeon(context.Context, *CompleteLfgDungeonRequest) (*CompleteLfgDungeonResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CompleteLfgDungeon not implemented") +} func (UnimplementedMatchmakingServiceServer) mustEmbedUnimplementedMatchmakingServiceServer() {} // UnsafeMatchmakingServiceServer may be embedded to opt out of forward compatibility for this service. @@ -257,6 +362,132 @@ func _MatchmakingService_BattlegroundStatusChanged_Handler(srv interface{}, ctx return interceptor(ctx, in, info, handler) } +func _MatchmakingService_FinishRatedArenaMatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FinishRatedArenaMatchRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MatchmakingServiceServer).FinishRatedArenaMatch(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MatchmakingService_FinishRatedArenaMatch_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MatchmakingServiceServer).FinishRatedArenaMatch(ctx, req.(*FinishRatedArenaMatchRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _MatchmakingService_JoinLfg_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(JoinLfgRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MatchmakingServiceServer).JoinLfg(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MatchmakingService_JoinLfg_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MatchmakingServiceServer).JoinLfg(ctx, req.(*JoinLfgRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _MatchmakingService_LeaveLfg_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LeaveLfgRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MatchmakingServiceServer).LeaveLfg(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MatchmakingService_LeaveLfg_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MatchmakingServiceServer).LeaveLfg(ctx, req.(*LeaveLfgRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _MatchmakingService_SetLfgRoles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetLfgRolesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MatchmakingServiceServer).SetLfgRoles(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MatchmakingService_SetLfgRoles_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MatchmakingServiceServer).SetLfgRoles(ctx, req.(*SetLfgRolesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _MatchmakingService_AnswerLfgProposal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AnswerLfgProposalRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MatchmakingServiceServer).AnswerLfgProposal(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MatchmakingService_AnswerLfgProposal_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MatchmakingServiceServer).AnswerLfgProposal(ctx, req.(*AnswerLfgProposalRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _MatchmakingService_LfgStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LfgStatusRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MatchmakingServiceServer).LfgStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MatchmakingService_LfgStatus_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MatchmakingServiceServer).LfgStatus(ctx, req.(*LfgStatusRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _MatchmakingService_CompleteLfgDungeon_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CompleteLfgDungeonRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MatchmakingServiceServer).CompleteLfgDungeon(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MatchmakingService_CompleteLfgDungeon_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MatchmakingServiceServer).CompleteLfgDungeon(ctx, req.(*CompleteLfgDungeonRequest)) + } + return interceptor(ctx, in, info, handler) +} + // MatchmakingService_ServiceDesc is the grpc.ServiceDesc for MatchmakingService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -288,6 +519,34 @@ var MatchmakingService_ServiceDesc = grpc.ServiceDesc{ MethodName: "BattlegroundStatusChanged", Handler: _MatchmakingService_BattlegroundStatusChanged_Handler, }, + { + MethodName: "FinishRatedArenaMatch", + Handler: _MatchmakingService_FinishRatedArenaMatch_Handler, + }, + { + MethodName: "JoinLfg", + Handler: _MatchmakingService_JoinLfg_Handler, + }, + { + MethodName: "LeaveLfg", + Handler: _MatchmakingService_LeaveLfg_Handler, + }, + { + MethodName: "SetLfgRoles", + Handler: _MatchmakingService_SetLfgRoles_Handler, + }, + { + MethodName: "AnswerLfgProposal", + Handler: _MatchmakingService_AnswerLfgProposal_Handler, + }, + { + MethodName: "LfgStatus", + Handler: _MatchmakingService_LfgStatus_Handler, + }, + { + MethodName: "CompleteLfgDungeon", + Handler: _MatchmakingService_CompleteLfgDungeon_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "matchmaking.proto", diff --git a/gen/servers-registry/pb/registry.pb.go b/gen/servers-registry/pb/registry.pb.go index 4f5f8d3..5cbdb38 100644 --- a/gen/servers-registry/pb/registry.pb.go +++ b/gen/servers-registry/pb/registry.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.32.0 -// protoc v3.20.3 +// protoc-gen-go v1.36.11 +// protoc v3.21.12 // source: registry.proto package pb @@ -11,6 +11,7 @@ import ( protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -22,27 +23,24 @@ const ( // RegisterGameServer type RegisterGameServerRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - GamePort uint32 `protobuf:"varint,2,opt,name=gamePort,proto3" json:"gamePort,omitempty"` - HealthPort uint32 `protobuf:"varint,3,opt,name=healthPort,proto3" json:"healthPort,omitempty"` - GrpcPort uint32 `protobuf:"varint,4,opt,name=grpcPort,proto3" json:"grpcPort,omitempty"` - RealmID uint32 `protobuf:"varint,5,opt,name=realmID,proto3" json:"realmID,omitempty"` - IsCrossRealm bool `protobuf:"varint,6,opt,name=isCrossRealm,proto3" json:"isCrossRealm,omitempty"` // If true realm id should be 0 - AvailableMaps string `protobuf:"bytes,7,opt,name=availableMaps,proto3" json:"availableMaps,omitempty"` - PreferredHostName string `protobuf:"bytes,8,opt,name=preferredHostName,proto3" json:"preferredHostName,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + GamePort uint32 `protobuf:"varint,2,opt,name=gamePort,proto3" json:"gamePort,omitempty"` + HealthPort uint32 `protobuf:"varint,3,opt,name=healthPort,proto3" json:"healthPort,omitempty"` + GrpcPort uint32 `protobuf:"varint,4,opt,name=grpcPort,proto3" json:"grpcPort,omitempty"` + RealmID uint32 `protobuf:"varint,5,opt,name=realmID,proto3" json:"realmID,omitempty"` + IsCrossRealm bool `protobuf:"varint,6,opt,name=isCrossRealm,proto3" json:"isCrossRealm,omitempty"` // If true realm id should be 0 + AvailableMaps string `protobuf:"bytes,7,opt,name=availableMaps,proto3" json:"availableMaps,omitempty"` + PreferredHostName string `protobuf:"bytes,8,opt,name=preferredHostName,proto3" json:"preferredHostName,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RegisterGameServerRequest) Reset() { *x = RegisterGameServerRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RegisterGameServerRequest) String() string { @@ -53,7 +51,7 @@ func (*RegisterGameServerRequest) ProtoMessage() {} func (x *RegisterGameServerRequest) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -125,22 +123,19 @@ func (x *RegisterGameServerRequest) GetPreferredHostName() string { } type RegisterGameServerResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + AssignedMaps []uint32 `protobuf:"varint,3,rep,packed,name=assignedMaps,proto3" json:"assignedMaps,omitempty"` unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - AssignedMaps []uint32 `protobuf:"varint,3,rep,packed,name=assignedMaps,proto3" json:"assignedMaps,omitempty"` + sizeCache protoimpl.SizeCache } func (x *RegisterGameServerResponse) Reset() { *x = RegisterGameServerResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RegisterGameServerResponse) String() string { @@ -151,7 +146,7 @@ func (*RegisterGameServerResponse) ProtoMessage() {} func (x *RegisterGameServerResponse) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -189,23 +184,20 @@ func (x *RegisterGameServerResponse) GetAssignedMaps() []uint32 { // AvailableGameServersForMapAndRealm type AvailableGameServersForMapAndRealmRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + MapID uint32 `protobuf:"varint,3,opt,name=mapID,proto3" json:"mapID,omitempty"` + IsCrossRealm bool `protobuf:"varint,4,opt,name=isCrossRealm,proto3" json:"isCrossRealm,omitempty"` // Can't be used with realm id unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - MapID uint32 `protobuf:"varint,3,opt,name=mapID,proto3" json:"mapID,omitempty"` - IsCrossRealm bool `protobuf:"varint,4,opt,name=isCrossRealm,proto3" json:"isCrossRealm,omitempty"` // Can't be used with realm id + sizeCache protoimpl.SizeCache } func (x *AvailableGameServersForMapAndRealmRequest) Reset() { *x = AvailableGameServersForMapAndRealmRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AvailableGameServersForMapAndRealmRequest) String() string { @@ -216,7 +208,7 @@ func (*AvailableGameServersForMapAndRealmRequest) ProtoMessage() {} func (x *AvailableGameServersForMapAndRealmRequest) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -260,21 +252,18 @@ func (x *AvailableGameServersForMapAndRealmRequest) GetIsCrossRealm() bool { } type AvailableGameServersForMapAndRealmResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + GameServers []*Server `protobuf:"bytes,2,rep,name=gameServers,proto3" json:"gameServers,omitempty"` unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - GameServers []*Server `protobuf:"bytes,2,rep,name=gameServers,proto3" json:"gameServers,omitempty"` + sizeCache protoimpl.SizeCache } func (x *AvailableGameServersForMapAndRealmResponse) Reset() { *x = AvailableGameServersForMapAndRealmResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AvailableGameServersForMapAndRealmResponse) String() string { @@ -285,7 +274,7 @@ func (*AvailableGameServersForMapAndRealmResponse) ProtoMessage() {} func (x *AvailableGameServersForMapAndRealmResponse) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -316,21 +305,18 @@ func (x *AvailableGameServersForMapAndRealmResponse) GetGameServers() []*Server // RandomGameServerForRealm type RandomGameServerForRealmRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + sizeCache protoimpl.SizeCache } func (x *RandomGameServerForRealmRequest) Reset() { *x = RandomGameServerForRealmRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RandomGameServerForRealmRequest) String() string { @@ -341,7 +327,7 @@ func (*RandomGameServerForRealmRequest) ProtoMessage() {} func (x *RandomGameServerForRealmRequest) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -371,21 +357,18 @@ func (x *RandomGameServerForRealmRequest) GetRealmID() uint32 { } type RandomGameServerForRealmResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + GameServer *Server `protobuf:"bytes,2,opt,name=gameServer,proto3" json:"gameServer,omitempty"` unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - GameServer *Server `protobuf:"bytes,2,opt,name=gameServer,proto3" json:"gameServer,omitempty"` + sizeCache protoimpl.SizeCache } func (x *RandomGameServerForRealmResponse) Reset() { *x = RandomGameServerForRealmResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RandomGameServerForRealmResponse) String() string { @@ -396,7 +379,7 @@ func (*RandomGameServerForRealmResponse) ProtoMessage() {} func (x *RandomGameServerForRealmResponse) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -427,22 +410,19 @@ func (x *RandomGameServerForRealmResponse) GetGameServer() *Server { // ListGameServersForRealm type ListGameServersForRealmRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + IsCrossRealm bool `protobuf:"varint,3,opt,name=isCrossRealm,proto3" json:"isCrossRealm,omitempty"` // Can't be used with realm id unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - IsCrossRealm bool `protobuf:"varint,3,opt,name=isCrossRealm,proto3" json:"isCrossRealm,omitempty"` // Can't be used with realm id + sizeCache protoimpl.SizeCache } func (x *ListGameServersForRealmRequest) Reset() { *x = ListGameServersForRealmRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ListGameServersForRealmRequest) String() string { @@ -453,7 +433,7 @@ func (*ListGameServersForRealmRequest) ProtoMessage() {} func (x *ListGameServersForRealmRequest) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -490,10 +470,7 @@ func (x *ListGameServersForRealmRequest) GetIsCrossRealm() bool { } type GameServerDetailed struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` HealthAddress string `protobuf:"bytes,3,opt,name=healthAddress,proto3" json:"healthAddress,omitempty"` @@ -504,15 +481,15 @@ type GameServerDetailed struct { Diff *GameServerDetailed_Diff `protobuf:"bytes,8,opt,name=diff,proto3" json:"diff,omitempty"` AvailableMaps []uint32 `protobuf:"varint,9,rep,packed,name=availableMaps,proto3" json:"availableMaps,omitempty"` AssignedMaps []uint32 `protobuf:"varint,10,rep,packed,name=assignedMaps,proto3" json:"assignedMaps,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *GameServerDetailed) Reset() { *x = GameServerDetailed{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *GameServerDetailed) String() string { @@ -523,7 +500,7 @@ func (*GameServerDetailed) ProtoMessage() {} func (x *GameServerDetailed) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -609,21 +586,18 @@ func (x *GameServerDetailed) GetAssignedMaps() []uint32 { } type ListGameServersResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + GameServers []*GameServerDetailed `protobuf:"bytes,2,rep,name=gameServers,proto3" json:"gameServers,omitempty"` unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - GameServers []*GameServerDetailed `protobuf:"bytes,2,rep,name=gameServers,proto3" json:"gameServers,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ListGameServersResponse) Reset() { *x = ListGameServersResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ListGameServersResponse) String() string { @@ -634,7 +608,7 @@ func (*ListGameServersResponse) ProtoMessage() {} func (x *ListGameServersResponse) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -665,20 +639,17 @@ func (x *ListGameServersResponse) GetGameServers() []*GameServerDetailed { // ListAllGameServers type ListAllGameServersRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ListAllGameServersRequest) Reset() { *x = ListAllGameServersRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ListAllGameServersRequest) String() string { @@ -689,7 +660,7 @@ func (*ListAllGameServersRequest) ProtoMessage() {} func (x *ListAllGameServersRequest) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -713,22 +684,19 @@ func (x *ListAllGameServersRequest) GetApi() string { // GameServerMapsLoaded type GameServerMapsLoadedRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + GameServerID string `protobuf:"bytes,2,opt,name=gameServerID,proto3" json:"gameServerID,omitempty"` + MapsLoaded []uint32 `protobuf:"varint,3,rep,packed,name=mapsLoaded,proto3" json:"mapsLoaded,omitempty"` unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - GameServerID string `protobuf:"bytes,2,opt,name=gameServerID,proto3" json:"gameServerID,omitempty"` - MapsLoaded []uint32 `protobuf:"varint,3,rep,packed,name=mapsLoaded,proto3" json:"mapsLoaded,omitempty"` + sizeCache protoimpl.SizeCache } func (x *GameServerMapsLoadedRequest) Reset() { *x = GameServerMapsLoadedRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *GameServerMapsLoadedRequest) String() string { @@ -739,7 +707,7 @@ func (*GameServerMapsLoadedRequest) ProtoMessage() {} func (x *GameServerMapsLoadedRequest) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -776,20 +744,17 @@ func (x *GameServerMapsLoadedRequest) GetMapsLoaded() []uint32 { } type GameServerMapsLoadedResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + sizeCache protoimpl.SizeCache } func (x *GameServerMapsLoadedResponse) Reset() { *x = GameServerMapsLoadedResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *GameServerMapsLoadedResponse) String() string { @@ -800,7 +765,7 @@ func (*GameServerMapsLoadedResponse) ProtoMessage() {} func (x *GameServerMapsLoadedResponse) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -824,25 +789,22 @@ func (x *GameServerMapsLoadedResponse) GetApi() string { // RegisterGateway type RegisterGatewayRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - GamePort uint32 `protobuf:"varint,2,opt,name=gamePort,proto3" json:"gamePort,omitempty"` - HealthPort uint32 `protobuf:"varint,3,opt,name=healthPort,proto3" json:"healthPort,omitempty"` - RealmID uint32 `protobuf:"varint,4,opt,name=realmID,proto3" json:"realmID,omitempty"` - IsCrossRealm bool `protobuf:"varint,5,opt,name=isCrossRealm,proto3" json:"isCrossRealm,omitempty"` // Can't be used with realm id - PreferredHostName string `protobuf:"bytes,6,opt,name=preferredHostName,proto3" json:"preferredHostName,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + GamePort uint32 `protobuf:"varint,2,opt,name=gamePort,proto3" json:"gamePort,omitempty"` + HealthPort uint32 `protobuf:"varint,3,opt,name=healthPort,proto3" json:"healthPort,omitempty"` + RealmID uint32 `protobuf:"varint,4,opt,name=realmID,proto3" json:"realmID,omitempty"` + IsCrossRealm bool `protobuf:"varint,5,opt,name=isCrossRealm,proto3" json:"isCrossRealm,omitempty"` // Can't be used with realm id + PreferredHostName string `protobuf:"bytes,6,opt,name=preferredHostName,proto3" json:"preferredHostName,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RegisterGatewayRequest) Reset() { *x = RegisterGatewayRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RegisterGatewayRequest) String() string { @@ -853,7 +815,7 @@ func (*RegisterGatewayRequest) ProtoMessage() {} func (x *RegisterGatewayRequest) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -911,21 +873,18 @@ func (x *RegisterGatewayRequest) GetPreferredHostName() string { } type RegisterGatewayResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + sizeCache protoimpl.SizeCache } func (x *RegisterGatewayResponse) Reset() { *x = RegisterGatewayResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RegisterGatewayResponse) String() string { @@ -936,7 +895,7 @@ func (*RegisterGatewayResponse) ProtoMessage() {} func (x *RegisterGatewayResponse) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -967,21 +926,18 @@ func (x *RegisterGatewayResponse) GetId() string { // GatewaysForRealms type GatewaysForRealmsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmIDs []uint32 `protobuf:"varint,2,rep,packed,name=realmIDs,proto3" json:"realmIDs,omitempty"` unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmIDs []uint32 `protobuf:"varint,2,rep,packed,name=realmIDs,proto3" json:"realmIDs,omitempty"` + sizeCache protoimpl.SizeCache } func (x *GatewaysForRealmsRequest) Reset() { *x = GatewaysForRealmsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *GatewaysForRealmsRequest) String() string { @@ -992,7 +948,7 @@ func (*GatewaysForRealmsRequest) ProtoMessage() {} func (x *GatewaysForRealmsRequest) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1022,21 +978,18 @@ func (x *GatewaysForRealmsRequest) GetRealmIDs() []uint32 { } type GatewaysForRealmsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Gateways []*Server `protobuf:"bytes,2,rep,name=gateways,proto3" json:"gateways,omitempty"` unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - Gateways []*Server `protobuf:"bytes,2,rep,name=gateways,proto3" json:"gateways,omitempty"` + sizeCache protoimpl.SizeCache } func (x *GatewaysForRealmsResponse) Reset() { *x = GatewaysForRealmsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *GatewaysForRealmsResponse) String() string { @@ -1047,7 +1000,7 @@ func (*GatewaysForRealmsResponse) ProtoMessage() {} func (x *GatewaysForRealmsResponse) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1078,21 +1031,18 @@ func (x *GatewaysForRealmsResponse) GetGateways() []*Server { // ListGatewaysForRealm type ListGatewaysForRealmRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ListGatewaysForRealmRequest) Reset() { *x = ListGatewaysForRealmRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ListGatewaysForRealmRequest) String() string { @@ -1103,7 +1053,7 @@ func (*ListGatewaysForRealmRequest) ProtoMessage() {} func (x *ListGatewaysForRealmRequest) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[16] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1133,24 +1083,21 @@ func (x *ListGatewaysForRealmRequest) GetRealmID() uint32 { } type GatewayServerDetailed struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` - HealthAddress string `protobuf:"bytes,3,opt,name=healthAddress,proto3" json:"healthAddress,omitempty"` - RealmID uint32 `protobuf:"varint,4,opt,name=realmID,proto3" json:"realmID,omitempty"` - ActiveConnections uint32 `protobuf:"varint,5,opt,name=activeConnections,proto3" json:"activeConnections,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + HealthAddress string `protobuf:"bytes,3,opt,name=healthAddress,proto3" json:"healthAddress,omitempty"` + RealmID uint32 `protobuf:"varint,4,opt,name=realmID,proto3" json:"realmID,omitempty"` + ActiveConnections uint32 `protobuf:"varint,5,opt,name=activeConnections,proto3" json:"activeConnections,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *GatewayServerDetailed) Reset() { *x = GatewayServerDetailed{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[17] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *GatewayServerDetailed) String() string { @@ -1161,7 +1108,7 @@ func (*GatewayServerDetailed) ProtoMessage() {} func (x *GatewayServerDetailed) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[17] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1212,21 +1159,18 @@ func (x *GatewayServerDetailed) GetActiveConnections() uint32 { } type ListGatewaysForRealmResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Gateways []*GatewayServerDetailed `protobuf:"bytes,2,rep,name=gateways,proto3" json:"gateways,omitempty"` unknownFields protoimpl.UnknownFields - - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - Gateways []*GatewayServerDetailed `protobuf:"bytes,2,rep,name=gateways,proto3" json:"gateways,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ListGatewaysForRealmResponse) Reset() { *x = ListGatewaysForRealmResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[18] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ListGatewaysForRealmResponse) String() string { @@ -1237,7 +1181,7 @@ func (*ListGatewaysForRealmResponse) ProtoMessage() {} func (x *ListGatewaysForRealmResponse) ProtoReflect() protoreflect.Message { mi := &file_registry_proto_msgTypes[18] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1266,25 +1210,152 @@ func (x *ListGatewaysForRealmResponse) GetGateways() []*GatewayServerDetailed { return nil } +// RegisterMatchmakingServer +type RegisterMatchmakingServerRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + ServicePort uint32 `protobuf:"varint,2,opt,name=servicePort,proto3" json:"servicePort,omitempty"` + HealthPort uint32 `protobuf:"varint,3,opt,name=healthPort,proto3" json:"healthPort,omitempty"` + PreferredHostName string `protobuf:"bytes,4,opt,name=preferredHostName,proto3" json:"preferredHostName,omitempty"` + StartedAtUnixMs int64 `protobuf:"varint,5,opt,name=startedAtUnixMs,proto3" json:"startedAtUnixMs,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterMatchmakingServerRequest) Reset() { + *x = RegisterMatchmakingServerRequest{} + mi := &file_registry_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterMatchmakingServerRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterMatchmakingServerRequest) ProtoMessage() {} + +func (x *RegisterMatchmakingServerRequest) ProtoReflect() protoreflect.Message { + mi := &file_registry_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterMatchmakingServerRequest.ProtoReflect.Descriptor instead. +func (*RegisterMatchmakingServerRequest) Descriptor() ([]byte, []int) { + return file_registry_proto_rawDescGZIP(), []int{19} +} + +func (x *RegisterMatchmakingServerRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *RegisterMatchmakingServerRequest) GetServicePort() uint32 { + if x != nil { + return x.ServicePort + } + return 0 +} + +func (x *RegisterMatchmakingServerRequest) GetHealthPort() uint32 { + if x != nil { + return x.HealthPort + } + return 0 +} + +func (x *RegisterMatchmakingServerRequest) GetPreferredHostName() string { + if x != nil { + return x.PreferredHostName + } + return "" +} + +func (x *RegisterMatchmakingServerRequest) GetStartedAtUnixMs() int64 { + if x != nil { + return x.StartedAtUnixMs + } + return 0 +} + +type RegisterMatchmakingServerResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterMatchmakingServerResponse) Reset() { + *x = RegisterMatchmakingServerResponse{} + mi := &file_registry_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterMatchmakingServerResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterMatchmakingServerResponse) ProtoMessage() {} + +func (x *RegisterMatchmakingServerResponse) ProtoReflect() protoreflect.Message { + mi := &file_registry_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterMatchmakingServerResponse.ProtoReflect.Descriptor instead. +func (*RegisterMatchmakingServerResponse) Descriptor() ([]byte, []int) { + return file_registry_proto_rawDescGZIP(), []int{20} +} + +func (x *RegisterMatchmakingServerResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *RegisterMatchmakingServerResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + // Shared type Server struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + IsCrossRealm bool `protobuf:"varint,3,opt,name=isCrossRealm,proto3" json:"isCrossRealm,omitempty"` + GrpcAddress string `protobuf:"bytes,4,opt,name=grpcAddress,proto3" json:"grpcAddress,omitempty"` + Id string `protobuf:"bytes,5,opt,name=id,proto3" json:"id,omitempty"` unknownFields protoimpl.UnknownFields - - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` - IsCrossRealm bool `protobuf:"varint,3,opt,name=isCrossRealm,proto3" json:"isCrossRealm,omitempty"` - GrpcAddress string `protobuf:"bytes,4,opt,name=grpcAddress,proto3" json:"grpcAddress,omitempty"` + sizeCache protoimpl.SizeCache } func (x *Server) Reset() { *x = Server{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[19] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Server) String() string { @@ -1294,8 +1365,8 @@ func (x *Server) String() string { func (*Server) ProtoMessage() {} func (x *Server) ProtoReflect() protoreflect.Message { - mi := &file_registry_proto_msgTypes[19] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_registry_proto_msgTypes[21] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1307,7 +1378,7 @@ func (x *Server) ProtoReflect() protoreflect.Message { // Deprecated: Use Server.ProtoReflect.Descriptor instead. func (*Server) Descriptor() ([]byte, []int) { - return file_registry_proto_rawDescGZIP(), []int{19} + return file_registry_proto_rawDescGZIP(), []int{21} } func (x *Server) GetAddress() string { @@ -1338,25 +1409,29 @@ func (x *Server) GetGrpcAddress() string { return "" } +func (x *Server) GetId() string { + if x != nil { + return x.Id + } + return "" +} + type GameServerDetailed_Diff struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Mean uint32 `protobuf:"varint,1,opt,name=mean,proto3" json:"mean,omitempty"` + Median uint32 `protobuf:"varint,2,opt,name=median,proto3" json:"median,omitempty"` + Percentile95 uint32 `protobuf:"varint,3,opt,name=percentile95,proto3" json:"percentile95,omitempty"` + Percentile99 uint32 `protobuf:"varint,4,opt,name=percentile99,proto3" json:"percentile99,omitempty"` + Max uint32 `protobuf:"varint,5,opt,name=max,proto3" json:"max,omitempty"` unknownFields protoimpl.UnknownFields - - Mean uint32 `protobuf:"varint,1,opt,name=mean,proto3" json:"mean,omitempty"` - Median uint32 `protobuf:"varint,2,opt,name=median,proto3" json:"median,omitempty"` - Percentile95 uint32 `protobuf:"varint,3,opt,name=percentile95,proto3" json:"percentile95,omitempty"` - Percentile99 uint32 `protobuf:"varint,4,opt,name=percentile99,proto3" json:"percentile99,omitempty"` - Max uint32 `protobuf:"varint,5,opt,name=max,proto3" json:"max,omitempty"` + sizeCache protoimpl.SizeCache } func (x *GameServerDetailed_Diff) Reset() { *x = GameServerDetailed_Diff{} - if protoimpl.UnsafeEnabled { - mi := &file_registry_proto_msgTypes[20] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_registry_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *GameServerDetailed_Diff) String() string { @@ -1366,8 +1441,8 @@ func (x *GameServerDetailed_Diff) String() string { func (*GameServerDetailed_Diff) ProtoMessage() {} func (x *GameServerDetailed_Diff) ProtoReflect() protoreflect.Message { - mi := &file_registry_proto_msgTypes[20] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_registry_proto_msgTypes[22] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1419,249 +1494,151 @@ func (x *GameServerDetailed_Diff) GetMax() uint32 { var File_registry_proto protoreflect.FileDescriptor -var file_registry_proto_rawDesc = []byte{ - 0x0a, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x12, 0x02, 0x76, 0x31, 0x22, 0x97, 0x02, 0x0a, 0x19, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x61, 0x70, 0x69, 0x12, 0x1a, 0x0a, 0x08, 0x67, 0x61, 0x6d, 0x65, 0x50, 0x6f, 0x72, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x67, 0x61, 0x6d, 0x65, 0x50, 0x6f, 0x72, 0x74, - 0x12, 0x1e, 0x0a, 0x0a, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x6f, 0x72, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x67, 0x72, 0x70, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x08, 0x67, 0x72, 0x70, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, - 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, - 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, - 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, - 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x76, - 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x70, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x70, 0x73, - 0x12, 0x2c, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x48, 0x6f, 0x73, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x72, 0x65, - 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x62, - 0x0a, 0x1a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, - 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, - 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4d, 0x61, 0x70, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4d, 0x61, - 0x70, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x29, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, - 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x46, 0x6f, 0x72, 0x4d, 0x61, - 0x70, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, - 0x6d, 0x61, 0x70, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x61, 0x70, - 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, - 0x6c, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, - 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x22, 0x6c, 0x0a, 0x2a, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x6c, 0x65, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x46, 0x6f, - 0x72, 0x4d, 0x61, 0x70, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x2c, 0x0a, 0x0b, 0x67, 0x61, 0x6d, 0x65, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0b, 0x67, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x73, 0x22, 0x4d, 0x0a, 0x1f, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x47, 0x61, - 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, - 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, - 0x6d, 0x49, 0x44, 0x22, 0x60, 0x0a, 0x20, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x47, 0x61, 0x6d, - 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x2a, 0x0a, 0x0a, 0x67, 0x61, 0x6d, - 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0a, 0x67, 0x61, 0x6d, 0x65, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x70, 0x0a, 0x1e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x6d, - 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x46, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, - 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, - 0x6d, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, - 0x61, 0x6c, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, - 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x22, 0xfc, 0x03, 0x0a, 0x12, 0x47, 0x61, 0x6d, 0x65, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x0e, - 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, - 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x68, 0x65, 0x61, 0x6c, - 0x74, 0x68, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, - 0x0a, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x73, - 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x12, 0x2c, - 0x0a, 0x11, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x61, 0x63, 0x74, 0x69, 0x76, - 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2f, 0x0a, 0x04, - 0x64, 0x69, 0x66, 0x66, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x65, 0x64, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x52, 0x04, 0x64, 0x69, 0x66, 0x66, 0x12, 0x24, 0x0a, - 0x0d, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x70, 0x73, 0x18, 0x09, - 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x4d, - 0x61, 0x70, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4d, - 0x61, 0x70, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x73, 0x73, 0x69, 0x67, - 0x6e, 0x65, 0x64, 0x4d, 0x61, 0x70, 0x73, 0x1a, 0x8c, 0x01, 0x0a, 0x04, 0x44, 0x69, 0x66, 0x66, - 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x61, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, - 0x6d, 0x65, 0x61, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x12, 0x22, 0x0a, 0x0c, - 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x39, 0x35, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x39, 0x35, - 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x39, 0x39, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, - 0x6c, 0x65, 0x39, 0x39, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x03, 0x6d, 0x61, 0x78, 0x22, 0x65, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, - 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x61, 0x70, 0x69, 0x12, 0x38, 0x0a, 0x0b, 0x67, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, - 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x65, 0x64, - 0x52, 0x0b, 0x67, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x22, 0x2d, 0x0a, - 0x19, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, - 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x73, 0x0a, 0x1b, - 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x73, 0x4c, 0x6f, - 0x61, 0x64, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x22, 0x0a, - 0x0c, 0x67, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x67, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, - 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x70, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x61, 0x70, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x65, - 0x64, 0x22, 0x30, 0x0a, 0x1c, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, - 0x61, 0x70, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x61, 0x70, 0x69, 0x22, 0xd2, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, - 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, - 0x12, 0x1a, 0x0a, 0x08, 0x67, 0x61, 0x6d, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x08, 0x67, 0x61, 0x6d, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, - 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0a, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, - 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, - 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, - 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, - 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x12, 0x2c, 0x0a, 0x11, 0x70, 0x72, - 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, - 0x48, 0x6f, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x3b, 0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x48, 0x0a, 0x18, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, - 0x73, 0x46, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x61, 0x70, 0x69, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x73, 0x22, - 0x55, 0x0a, 0x19, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x46, 0x6f, 0x72, 0x52, 0x65, - 0x61, 0x6c, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, - 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x26, - 0x0a, 0x08, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x08, 0x67, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x22, 0x49, 0x0a, 0x1b, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x46, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, - 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, - 0x44, 0x22, 0xaf, 0x01, 0x0a, 0x15, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x68, 0x65, - 0x61, 0x6c, 0x74, 0x68, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x72, - 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, - 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x2c, 0x0a, 0x11, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x11, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x22, 0x67, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, - 0x61, 0x79, 0x73, 0x46, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x35, 0x0a, 0x08, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x65, 0x64, 0x52, 0x08, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x22, 0x82, 0x01, 0x0a, - 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, - 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0c, 0x69, 0x73, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x12, - 0x20, 0x0a, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x32, 0xdc, 0x06, 0x0a, 0x16, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x53, 0x0a, 0x12, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x47, - 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x83, 0x01, 0x0a, 0x22, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x47, - 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x46, 0x6f, 0x72, 0x4d, 0x61, 0x70, - 0x41, 0x6e, 0x64, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x12, 0x2d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x76, - 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x73, 0x46, 0x6f, 0x72, 0x4d, 0x61, 0x70, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x61, 0x6c, 0x6d, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x76, 0x61, - 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x73, 0x46, 0x6f, 0x72, 0x4d, 0x61, 0x70, 0x41, 0x6e, 0x64, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x18, 0x52, 0x61, 0x6e, 0x64, 0x6f, - 0x6d, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x6f, 0x72, 0x52, 0x65, - 0x61, 0x6c, 0x6d, 0x12, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x47, - 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x6c, - 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x61, - 0x6e, 0x64, 0x6f, 0x6d, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x6f, - 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, - 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x73, 0x46, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x12, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x46, 0x6f, - 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x12, 0x4c, 0x69, - 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, - 0x12, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x47, 0x61, 0x6d, - 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x14, - 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x73, 0x4c, 0x6f, - 0x61, 0x64, 0x65, 0x64, 0x12, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x11, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x46, - 0x6f, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x73, 0x12, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x46, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, - 0x77, 0x61, 0x79, 0x73, 0x46, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x73, 0x46, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x12, 0x1f, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x46, - 0x6f, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, - 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, - 0x46, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x6c, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x42, 0x19, 0x5a, 0x17, 0x67, 0x65, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2d, - 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, -} +const file_registry_proto_rawDesc = "" + + "\n" + + "\x0eregistry.proto\x12\x02v1\"\x97\x02\n" + + "\x19RegisterGameServerRequest\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12\x1a\n" + + "\bgamePort\x18\x02 \x01(\rR\bgamePort\x12\x1e\n" + + "\n" + + "healthPort\x18\x03 \x01(\rR\n" + + "healthPort\x12\x1a\n" + + "\bgrpcPort\x18\x04 \x01(\rR\bgrpcPort\x12\x18\n" + + "\arealmID\x18\x05 \x01(\rR\arealmID\x12\"\n" + + "\fisCrossRealm\x18\x06 \x01(\bR\fisCrossRealm\x12$\n" + + "\ravailableMaps\x18\a \x01(\tR\ravailableMaps\x12,\n" + + "\x11preferredHostName\x18\b \x01(\tR\x11preferredHostName\"b\n" + + "\x1aRegisterGameServerResponse\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12\x0e\n" + + "\x02id\x18\x02 \x01(\tR\x02id\x12\"\n" + + "\fassignedMaps\x18\x03 \x03(\rR\fassignedMaps\"\x91\x01\n" + + ")AvailableGameServersForMapAndRealmRequest\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12\x18\n" + + "\arealmID\x18\x02 \x01(\rR\arealmID\x12\x14\n" + + "\x05mapID\x18\x03 \x01(\rR\x05mapID\x12\"\n" + + "\fisCrossRealm\x18\x04 \x01(\bR\fisCrossRealm\"l\n" + + "*AvailableGameServersForMapAndRealmResponse\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12,\n" + + "\vgameServers\x18\x02 \x03(\v2\n" + + ".v1.ServerR\vgameServers\"M\n" + + "\x1fRandomGameServerForRealmRequest\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12\x18\n" + + "\arealmID\x18\x02 \x01(\rR\arealmID\"`\n" + + " RandomGameServerForRealmResponse\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12*\n" + + "\n" + + "gameServer\x18\x02 \x01(\v2\n" + + ".v1.ServerR\n" + + "gameServer\"p\n" + + "\x1eListGameServersForRealmRequest\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12\x18\n" + + "\arealmID\x18\x02 \x01(\rR\arealmID\x12\"\n" + + "\fisCrossRealm\x18\x03 \x01(\bR\fisCrossRealm\"\xfc\x03\n" + + "\x12GameServerDetailed\x12\x0e\n" + + "\x02ID\x18\x01 \x01(\tR\x02ID\x12\x18\n" + + "\aaddress\x18\x02 \x01(\tR\aaddress\x12$\n" + + "\rhealthAddress\x18\x03 \x01(\tR\rhealthAddress\x12 \n" + + "\vgrpcAddress\x18\x04 \x01(\tR\vgrpcAddress\x12\x18\n" + + "\arealmID\x18\x05 \x01(\rR\arealmID\x12\"\n" + + "\fisCrossRealm\x18\x06 \x01(\bR\fisCrossRealm\x12,\n" + + "\x11activeConnections\x18\a \x01(\rR\x11activeConnections\x12/\n" + + "\x04diff\x18\b \x01(\v2\x1b.v1.GameServerDetailed.DiffR\x04diff\x12$\n" + + "\ravailableMaps\x18\t \x03(\rR\ravailableMaps\x12\"\n" + + "\fassignedMaps\x18\n" + + " \x03(\rR\fassignedMaps\x1a\x8c\x01\n" + + "\x04Diff\x12\x12\n" + + "\x04mean\x18\x01 \x01(\rR\x04mean\x12\x16\n" + + "\x06median\x18\x02 \x01(\rR\x06median\x12\"\n" + + "\fpercentile95\x18\x03 \x01(\rR\fpercentile95\x12\"\n" + + "\fpercentile99\x18\x04 \x01(\rR\fpercentile99\x12\x10\n" + + "\x03max\x18\x05 \x01(\rR\x03max\"e\n" + + "\x17ListGameServersResponse\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x128\n" + + "\vgameServers\x18\x02 \x03(\v2\x16.v1.GameServerDetailedR\vgameServers\"-\n" + + "\x19ListAllGameServersRequest\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\"s\n" + + "\x1bGameServerMapsLoadedRequest\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12\"\n" + + "\fgameServerID\x18\x02 \x01(\tR\fgameServerID\x12\x1e\n" + + "\n" + + "mapsLoaded\x18\x03 \x03(\rR\n" + + "mapsLoaded\"0\n" + + "\x1cGameServerMapsLoadedResponse\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\"\xd2\x01\n" + + "\x16RegisterGatewayRequest\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12\x1a\n" + + "\bgamePort\x18\x02 \x01(\rR\bgamePort\x12\x1e\n" + + "\n" + + "healthPort\x18\x03 \x01(\rR\n" + + "healthPort\x12\x18\n" + + "\arealmID\x18\x04 \x01(\rR\arealmID\x12\"\n" + + "\fisCrossRealm\x18\x05 \x01(\bR\fisCrossRealm\x12,\n" + + "\x11preferredHostName\x18\x06 \x01(\tR\x11preferredHostName\";\n" + + "\x17RegisterGatewayResponse\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12\x0e\n" + + "\x02id\x18\x02 \x01(\tR\x02id\"H\n" + + "\x18GatewaysForRealmsRequest\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12\x1a\n" + + "\brealmIDs\x18\x02 \x03(\rR\brealmIDs\"U\n" + + "\x19GatewaysForRealmsResponse\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12&\n" + + "\bgateways\x18\x02 \x03(\v2\n" + + ".v1.ServerR\bgateways\"I\n" + + "\x1bListGatewaysForRealmRequest\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12\x18\n" + + "\arealmID\x18\x02 \x01(\rR\arealmID\"\xaf\x01\n" + + "\x15GatewayServerDetailed\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x18\n" + + "\aaddress\x18\x02 \x01(\tR\aaddress\x12$\n" + + "\rhealthAddress\x18\x03 \x01(\tR\rhealthAddress\x12\x18\n" + + "\arealmID\x18\x04 \x01(\rR\arealmID\x12,\n" + + "\x11activeConnections\x18\x05 \x01(\rR\x11activeConnections\"g\n" + + "\x1cListGatewaysForRealmResponse\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x125\n" + + "\bgateways\x18\x02 \x03(\v2\x19.v1.GatewayServerDetailedR\bgateways\"\xce\x01\n" + + " RegisterMatchmakingServerRequest\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12 \n" + + "\vservicePort\x18\x02 \x01(\rR\vservicePort\x12\x1e\n" + + "\n" + + "healthPort\x18\x03 \x01(\rR\n" + + "healthPort\x12,\n" + + "\x11preferredHostName\x18\x04 \x01(\tR\x11preferredHostName\x12(\n" + + "\x0fstartedAtUnixMs\x18\x05 \x01(\x03R\x0fstartedAtUnixMs\"E\n" + + "!RegisterMatchmakingServerResponse\x12\x10\n" + + "\x03api\x18\x01 \x01(\tR\x03api\x12\x0e\n" + + "\x02id\x18\x02 \x01(\tR\x02id\"\x92\x01\n" + + "\x06Server\x12\x18\n" + + "\aaddress\x18\x01 \x01(\tR\aaddress\x12\x18\n" + + "\arealmID\x18\x02 \x01(\rR\arealmID\x12\"\n" + + "\fisCrossRealm\x18\x03 \x01(\bR\fisCrossRealm\x12 \n" + + "\vgrpcAddress\x18\x04 \x01(\tR\vgrpcAddress\x12\x0e\n" + + "\x02id\x18\x05 \x01(\tR\x02id2\xc6\a\n" + + "\x16ServersRegistryService\x12S\n" + + "\x12RegisterGameServer\x12\x1d.v1.RegisterGameServerRequest\x1a\x1e.v1.RegisterGameServerResponse\x12\x83\x01\n" + + "\"AvailableGameServersForMapAndRealm\x12-.v1.AvailableGameServersForMapAndRealmRequest\x1a..v1.AvailableGameServersForMapAndRealmResponse\x12e\n" + + "\x18RandomGameServerForRealm\x12#.v1.RandomGameServerForRealmRequest\x1a$.v1.RandomGameServerForRealmResponse\x12Z\n" + + "\x17ListGameServersForRealm\x12\".v1.ListGameServersForRealmRequest\x1a\x1b.v1.ListGameServersResponse\x12P\n" + + "\x12ListAllGameServers\x12\x1d.v1.ListAllGameServersRequest\x1a\x1b.v1.ListGameServersResponse\x12Y\n" + + "\x14GameServerMapsLoaded\x12\x1f.v1.GameServerMapsLoadedRequest\x1a .v1.GameServerMapsLoadedResponse\x12J\n" + + "\x0fRegisterGateway\x12\x1a.v1.RegisterGatewayRequest\x1a\x1b.v1.RegisterGatewayResponse\x12P\n" + + "\x11GatewaysForRealms\x12\x1c.v1.GatewaysForRealmsRequest\x1a\x1d.v1.GatewaysForRealmsResponse\x12Y\n" + + "\x14ListGatewaysForRealm\x12\x1f.v1.ListGatewaysForRealmRequest\x1a .v1.ListGatewaysForRealmResponse\x12h\n" + + "\x19RegisterMatchmakingServer\x12$.v1.RegisterMatchmakingServerRequest\x1a%.v1.RegisterMatchmakingServerResponseB\x19Z\x17gen/servers-registry/pbb\x06proto3" var ( file_registry_proto_rawDescOnce sync.Once - file_registry_proto_rawDescData = file_registry_proto_rawDesc + file_registry_proto_rawDescData []byte ) func file_registry_proto_rawDescGZIP() []byte { file_registry_proto_rawDescOnce.Do(func() { - file_registry_proto_rawDescData = protoimpl.X.CompressGZIP(file_registry_proto_rawDescData) + file_registry_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_registry_proto_rawDesc), len(file_registry_proto_rawDesc))) }) return file_registry_proto_rawDescData } -var file_registry_proto_msgTypes = make([]protoimpl.MessageInfo, 21) -var file_registry_proto_goTypes = []interface{}{ +var file_registry_proto_msgTypes = make([]protoimpl.MessageInfo, 23) +var file_registry_proto_goTypes = []any{ (*RegisterGameServerRequest)(nil), // 0: v1.RegisterGameServerRequest (*RegisterGameServerResponse)(nil), // 1: v1.RegisterGameServerResponse (*AvailableGameServersForMapAndRealmRequest)(nil), // 2: v1.AvailableGameServersForMapAndRealmRequest @@ -1681,15 +1658,17 @@ var file_registry_proto_goTypes = []interface{}{ (*ListGatewaysForRealmRequest)(nil), // 16: v1.ListGatewaysForRealmRequest (*GatewayServerDetailed)(nil), // 17: v1.GatewayServerDetailed (*ListGatewaysForRealmResponse)(nil), // 18: v1.ListGatewaysForRealmResponse - (*Server)(nil), // 19: v1.Server - (*GameServerDetailed_Diff)(nil), // 20: v1.GameServerDetailed.Diff + (*RegisterMatchmakingServerRequest)(nil), // 19: v1.RegisterMatchmakingServerRequest + (*RegisterMatchmakingServerResponse)(nil), // 20: v1.RegisterMatchmakingServerResponse + (*Server)(nil), // 21: v1.Server + (*GameServerDetailed_Diff)(nil), // 22: v1.GameServerDetailed.Diff } var file_registry_proto_depIdxs = []int32{ - 19, // 0: v1.AvailableGameServersForMapAndRealmResponse.gameServers:type_name -> v1.Server - 19, // 1: v1.RandomGameServerForRealmResponse.gameServer:type_name -> v1.Server - 20, // 2: v1.GameServerDetailed.diff:type_name -> v1.GameServerDetailed.Diff + 21, // 0: v1.AvailableGameServersForMapAndRealmResponse.gameServers:type_name -> v1.Server + 21, // 1: v1.RandomGameServerForRealmResponse.gameServer:type_name -> v1.Server + 22, // 2: v1.GameServerDetailed.diff:type_name -> v1.GameServerDetailed.Diff 7, // 3: v1.ListGameServersResponse.gameServers:type_name -> v1.GameServerDetailed - 19, // 4: v1.GatewaysForRealmsResponse.gateways:type_name -> v1.Server + 21, // 4: v1.GatewaysForRealmsResponse.gateways:type_name -> v1.Server 17, // 5: v1.ListGatewaysForRealmResponse.gateways:type_name -> v1.GatewayServerDetailed 0, // 6: v1.ServersRegistryService.RegisterGameServer:input_type -> v1.RegisterGameServerRequest 2, // 7: v1.ServersRegistryService.AvailableGameServersForMapAndRealm:input_type -> v1.AvailableGameServersForMapAndRealmRequest @@ -1700,17 +1679,19 @@ var file_registry_proto_depIdxs = []int32{ 12, // 12: v1.ServersRegistryService.RegisterGateway:input_type -> v1.RegisterGatewayRequest 14, // 13: v1.ServersRegistryService.GatewaysForRealms:input_type -> v1.GatewaysForRealmsRequest 16, // 14: v1.ServersRegistryService.ListGatewaysForRealm:input_type -> v1.ListGatewaysForRealmRequest - 1, // 15: v1.ServersRegistryService.RegisterGameServer:output_type -> v1.RegisterGameServerResponse - 3, // 16: v1.ServersRegistryService.AvailableGameServersForMapAndRealm:output_type -> v1.AvailableGameServersForMapAndRealmResponse - 5, // 17: v1.ServersRegistryService.RandomGameServerForRealm:output_type -> v1.RandomGameServerForRealmResponse - 8, // 18: v1.ServersRegistryService.ListGameServersForRealm:output_type -> v1.ListGameServersResponse - 8, // 19: v1.ServersRegistryService.ListAllGameServers:output_type -> v1.ListGameServersResponse - 11, // 20: v1.ServersRegistryService.GameServerMapsLoaded:output_type -> v1.GameServerMapsLoadedResponse - 13, // 21: v1.ServersRegistryService.RegisterGateway:output_type -> v1.RegisterGatewayResponse - 15, // 22: v1.ServersRegistryService.GatewaysForRealms:output_type -> v1.GatewaysForRealmsResponse - 18, // 23: v1.ServersRegistryService.ListGatewaysForRealm:output_type -> v1.ListGatewaysForRealmResponse - 15, // [15:24] is the sub-list for method output_type - 6, // [6:15] is the sub-list for method input_type + 19, // 15: v1.ServersRegistryService.RegisterMatchmakingServer:input_type -> v1.RegisterMatchmakingServerRequest + 1, // 16: v1.ServersRegistryService.RegisterGameServer:output_type -> v1.RegisterGameServerResponse + 3, // 17: v1.ServersRegistryService.AvailableGameServersForMapAndRealm:output_type -> v1.AvailableGameServersForMapAndRealmResponse + 5, // 18: v1.ServersRegistryService.RandomGameServerForRealm:output_type -> v1.RandomGameServerForRealmResponse + 8, // 19: v1.ServersRegistryService.ListGameServersForRealm:output_type -> v1.ListGameServersResponse + 8, // 20: v1.ServersRegistryService.ListAllGameServers:output_type -> v1.ListGameServersResponse + 11, // 21: v1.ServersRegistryService.GameServerMapsLoaded:output_type -> v1.GameServerMapsLoadedResponse + 13, // 22: v1.ServersRegistryService.RegisterGateway:output_type -> v1.RegisterGatewayResponse + 15, // 23: v1.ServersRegistryService.GatewaysForRealms:output_type -> v1.GatewaysForRealmsResponse + 18, // 24: v1.ServersRegistryService.ListGatewaysForRealm:output_type -> v1.ListGatewaysForRealmResponse + 20, // 25: v1.ServersRegistryService.RegisterMatchmakingServer:output_type -> v1.RegisterMatchmakingServerResponse + 16, // [16:26] is the sub-list for method output_type + 6, // [6:16] is the sub-list for method input_type 6, // [6:6] is the sub-list for extension type_name 6, // [6:6] is the sub-list for extension extendee 0, // [0:6] is the sub-list for field type_name @@ -1721,267 +1702,13 @@ func file_registry_proto_init() { if File_registry_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_registry_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterGameServerRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterGameServerResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AvailableGameServersForMapAndRealmRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AvailableGameServersForMapAndRealmResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RandomGameServerForRealmRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RandomGameServerForRealmResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListGameServersForRealmRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GameServerDetailed); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListGameServersResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListAllGameServersRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GameServerMapsLoadedRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GameServerMapsLoadedResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterGatewayRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterGatewayResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GatewaysForRealmsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GatewaysForRealmsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListGatewaysForRealmRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GatewayServerDetailed); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListGatewaysForRealmResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Server); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_registry_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GameServerDetailed_Diff); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_registry_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_registry_proto_rawDesc), len(file_registry_proto_rawDesc)), NumEnums: 0, - NumMessages: 21, + NumMessages: 23, NumExtensions: 0, NumServices: 1, }, @@ -1990,7 +1717,6 @@ func file_registry_proto_init() { MessageInfos: file_registry_proto_msgTypes, }.Build() File_registry_proto = out.File - file_registry_proto_rawDesc = nil file_registry_proto_goTypes = nil file_registry_proto_depIdxs = nil } diff --git a/gen/servers-registry/pb/registry_grpc.pb.go b/gen/servers-registry/pb/registry_grpc.pb.go index 75ce5f9..1bd9a51 100644 --- a/gen/servers-registry/pb/registry_grpc.pb.go +++ b/gen/servers-registry/pb/registry_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 -// - protoc v3.20.3 +// - protoc-gen-go-grpc v1.6.2 +// - protoc v3.21.12 // source: registry.proto package pb @@ -15,8 +15,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( ServersRegistryService_RegisterGameServer_FullMethodName = "/v1.ServersRegistryService/RegisterGameServer" @@ -28,6 +28,7 @@ const ( ServersRegistryService_RegisterGateway_FullMethodName = "/v1.ServersRegistryService/RegisterGateway" ServersRegistryService_GatewaysForRealms_FullMethodName = "/v1.ServersRegistryService/GatewaysForRealms" ServersRegistryService_ListGatewaysForRealm_FullMethodName = "/v1.ServersRegistryService/ListGatewaysForRealm" + ServersRegistryService_RegisterMatchmakingServer_FullMethodName = "/v1.ServersRegistryService/RegisterMatchmakingServer" ) // ServersRegistryServiceClient is the client API for ServersRegistryService service. @@ -43,6 +44,7 @@ type ServersRegistryServiceClient interface { RegisterGateway(ctx context.Context, in *RegisterGatewayRequest, opts ...grpc.CallOption) (*RegisterGatewayResponse, error) GatewaysForRealms(ctx context.Context, in *GatewaysForRealmsRequest, opts ...grpc.CallOption) (*GatewaysForRealmsResponse, error) ListGatewaysForRealm(ctx context.Context, in *ListGatewaysForRealmRequest, opts ...grpc.CallOption) (*ListGatewaysForRealmResponse, error) + RegisterMatchmakingServer(ctx context.Context, in *RegisterMatchmakingServerRequest, opts ...grpc.CallOption) (*RegisterMatchmakingServerResponse, error) } type serversRegistryServiceClient struct { @@ -54,8 +56,9 @@ func NewServersRegistryServiceClient(cc grpc.ClientConnInterface) ServersRegistr } func (c *serversRegistryServiceClient) RegisterGameServer(ctx context.Context, in *RegisterGameServerRequest, opts ...grpc.CallOption) (*RegisterGameServerResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RegisterGameServerResponse) - err := c.cc.Invoke(ctx, ServersRegistryService_RegisterGameServer_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ServersRegistryService_RegisterGameServer_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -63,8 +66,9 @@ func (c *serversRegistryServiceClient) RegisterGameServer(ctx context.Context, i } func (c *serversRegistryServiceClient) AvailableGameServersForMapAndRealm(ctx context.Context, in *AvailableGameServersForMapAndRealmRequest, opts ...grpc.CallOption) (*AvailableGameServersForMapAndRealmResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AvailableGameServersForMapAndRealmResponse) - err := c.cc.Invoke(ctx, ServersRegistryService_AvailableGameServersForMapAndRealm_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ServersRegistryService_AvailableGameServersForMapAndRealm_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -72,8 +76,9 @@ func (c *serversRegistryServiceClient) AvailableGameServersForMapAndRealm(ctx co } func (c *serversRegistryServiceClient) RandomGameServerForRealm(ctx context.Context, in *RandomGameServerForRealmRequest, opts ...grpc.CallOption) (*RandomGameServerForRealmResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RandomGameServerForRealmResponse) - err := c.cc.Invoke(ctx, ServersRegistryService_RandomGameServerForRealm_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ServersRegistryService_RandomGameServerForRealm_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -81,8 +86,9 @@ func (c *serversRegistryServiceClient) RandomGameServerForRealm(ctx context.Cont } func (c *serversRegistryServiceClient) ListGameServersForRealm(ctx context.Context, in *ListGameServersForRealmRequest, opts ...grpc.CallOption) (*ListGameServersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListGameServersResponse) - err := c.cc.Invoke(ctx, ServersRegistryService_ListGameServersForRealm_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ServersRegistryService_ListGameServersForRealm_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -90,8 +96,9 @@ func (c *serversRegistryServiceClient) ListGameServersForRealm(ctx context.Conte } func (c *serversRegistryServiceClient) ListAllGameServers(ctx context.Context, in *ListAllGameServersRequest, opts ...grpc.CallOption) (*ListGameServersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListGameServersResponse) - err := c.cc.Invoke(ctx, ServersRegistryService_ListAllGameServers_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ServersRegistryService_ListAllGameServers_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -99,8 +106,9 @@ func (c *serversRegistryServiceClient) ListAllGameServers(ctx context.Context, i } func (c *serversRegistryServiceClient) GameServerMapsLoaded(ctx context.Context, in *GameServerMapsLoadedRequest, opts ...grpc.CallOption) (*GameServerMapsLoadedResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GameServerMapsLoadedResponse) - err := c.cc.Invoke(ctx, ServersRegistryService_GameServerMapsLoaded_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ServersRegistryService_GameServerMapsLoaded_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -108,8 +116,9 @@ func (c *serversRegistryServiceClient) GameServerMapsLoaded(ctx context.Context, } func (c *serversRegistryServiceClient) RegisterGateway(ctx context.Context, in *RegisterGatewayRequest, opts ...grpc.CallOption) (*RegisterGatewayResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RegisterGatewayResponse) - err := c.cc.Invoke(ctx, ServersRegistryService_RegisterGateway_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ServersRegistryService_RegisterGateway_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -117,8 +126,9 @@ func (c *serversRegistryServiceClient) RegisterGateway(ctx context.Context, in * } func (c *serversRegistryServiceClient) GatewaysForRealms(ctx context.Context, in *GatewaysForRealmsRequest, opts ...grpc.CallOption) (*GatewaysForRealmsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GatewaysForRealmsResponse) - err := c.cc.Invoke(ctx, ServersRegistryService_GatewaysForRealms_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ServersRegistryService_GatewaysForRealms_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -126,8 +136,19 @@ func (c *serversRegistryServiceClient) GatewaysForRealms(ctx context.Context, in } func (c *serversRegistryServiceClient) ListGatewaysForRealm(ctx context.Context, in *ListGatewaysForRealmRequest, opts ...grpc.CallOption) (*ListGatewaysForRealmResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListGatewaysForRealmResponse) - err := c.cc.Invoke(ctx, ServersRegistryService_ListGatewaysForRealm_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ServersRegistryService_ListGatewaysForRealm_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *serversRegistryServiceClient) RegisterMatchmakingServer(ctx context.Context, in *RegisterMatchmakingServerRequest, opts ...grpc.CallOption) (*RegisterMatchmakingServerResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RegisterMatchmakingServerResponse) + err := c.cc.Invoke(ctx, ServersRegistryService_RegisterMatchmakingServer_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -136,7 +157,7 @@ func (c *serversRegistryServiceClient) ListGatewaysForRealm(ctx context.Context, // ServersRegistryServiceServer is the server API for ServersRegistryService service. // All implementations must embed UnimplementedServersRegistryServiceServer -// for forward compatibility +// for forward compatibility. type ServersRegistryServiceServer interface { RegisterGameServer(context.Context, *RegisterGameServerRequest) (*RegisterGameServerResponse, error) AvailableGameServersForMapAndRealm(context.Context, *AvailableGameServersForMapAndRealmRequest) (*AvailableGameServersForMapAndRealmResponse, error) @@ -147,42 +168,50 @@ type ServersRegistryServiceServer interface { RegisterGateway(context.Context, *RegisterGatewayRequest) (*RegisterGatewayResponse, error) GatewaysForRealms(context.Context, *GatewaysForRealmsRequest) (*GatewaysForRealmsResponse, error) ListGatewaysForRealm(context.Context, *ListGatewaysForRealmRequest) (*ListGatewaysForRealmResponse, error) + RegisterMatchmakingServer(context.Context, *RegisterMatchmakingServerRequest) (*RegisterMatchmakingServerResponse, error) mustEmbedUnimplementedServersRegistryServiceServer() } -// UnimplementedServersRegistryServiceServer must be embedded to have forward compatible implementations. -type UnimplementedServersRegistryServiceServer struct { -} +// UnimplementedServersRegistryServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedServersRegistryServiceServer struct{} func (UnimplementedServersRegistryServiceServer) RegisterGameServer(context.Context, *RegisterGameServerRequest) (*RegisterGameServerResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method RegisterGameServer not implemented") + return nil, status.Error(codes.Unimplemented, "method RegisterGameServer not implemented") } func (UnimplementedServersRegistryServiceServer) AvailableGameServersForMapAndRealm(context.Context, *AvailableGameServersForMapAndRealmRequest) (*AvailableGameServersForMapAndRealmResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method AvailableGameServersForMapAndRealm not implemented") + return nil, status.Error(codes.Unimplemented, "method AvailableGameServersForMapAndRealm not implemented") } func (UnimplementedServersRegistryServiceServer) RandomGameServerForRealm(context.Context, *RandomGameServerForRealmRequest) (*RandomGameServerForRealmResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method RandomGameServerForRealm not implemented") + return nil, status.Error(codes.Unimplemented, "method RandomGameServerForRealm not implemented") } func (UnimplementedServersRegistryServiceServer) ListGameServersForRealm(context.Context, *ListGameServersForRealmRequest) (*ListGameServersResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListGameServersForRealm not implemented") + return nil, status.Error(codes.Unimplemented, "method ListGameServersForRealm not implemented") } func (UnimplementedServersRegistryServiceServer) ListAllGameServers(context.Context, *ListAllGameServersRequest) (*ListGameServersResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListAllGameServers not implemented") + return nil, status.Error(codes.Unimplemented, "method ListAllGameServers not implemented") } func (UnimplementedServersRegistryServiceServer) GameServerMapsLoaded(context.Context, *GameServerMapsLoadedRequest) (*GameServerMapsLoadedResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GameServerMapsLoaded not implemented") + return nil, status.Error(codes.Unimplemented, "method GameServerMapsLoaded not implemented") } func (UnimplementedServersRegistryServiceServer) RegisterGateway(context.Context, *RegisterGatewayRequest) (*RegisterGatewayResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method RegisterGateway not implemented") + return nil, status.Error(codes.Unimplemented, "method RegisterGateway not implemented") } func (UnimplementedServersRegistryServiceServer) GatewaysForRealms(context.Context, *GatewaysForRealmsRequest) (*GatewaysForRealmsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GatewaysForRealms not implemented") + return nil, status.Error(codes.Unimplemented, "method GatewaysForRealms not implemented") } func (UnimplementedServersRegistryServiceServer) ListGatewaysForRealm(context.Context, *ListGatewaysForRealmRequest) (*ListGatewaysForRealmResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListGatewaysForRealm not implemented") + return nil, status.Error(codes.Unimplemented, "method ListGatewaysForRealm not implemented") +} +func (UnimplementedServersRegistryServiceServer) RegisterMatchmakingServer(context.Context, *RegisterMatchmakingServerRequest) (*RegisterMatchmakingServerResponse, error) { + return nil, status.Error(codes.Unimplemented, "method RegisterMatchmakingServer not implemented") } func (UnimplementedServersRegistryServiceServer) mustEmbedUnimplementedServersRegistryServiceServer() { } +func (UnimplementedServersRegistryServiceServer) testEmbeddedByValue() {} // UnsafeServersRegistryServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ServersRegistryServiceServer will @@ -192,6 +221,13 @@ type UnsafeServersRegistryServiceServer interface { } func RegisterServersRegistryServiceServer(s grpc.ServiceRegistrar, srv ServersRegistryServiceServer) { + // If the following call panics, it indicates UnimplementedServersRegistryServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&ServersRegistryService_ServiceDesc, srv) } @@ -357,6 +393,24 @@ func _ServersRegistryService_ListGatewaysForRealm_Handler(srv interface{}, ctx c return interceptor(ctx, in, info, handler) } +func _ServersRegistryService_RegisterMatchmakingServer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegisterMatchmakingServerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServersRegistryServiceServer).RegisterMatchmakingServer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ServersRegistryService_RegisterMatchmakingServer_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServersRegistryServiceServer).RegisterMatchmakingServer(ctx, req.(*RegisterMatchmakingServerRequest)) + } + return interceptor(ctx, in, info, handler) +} + // ServersRegistryService_ServiceDesc is the grpc.ServiceDesc for ServersRegistryService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -400,6 +454,10 @@ var ServersRegistryService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ListGatewaysForRealm", Handler: _ServersRegistryService_ListGatewaysForRealm_Handler, }, + { + MethodName: "RegisterMatchmakingServer", + Handler: _ServersRegistryService_RegisterMatchmakingServer_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "registry.proto", diff --git a/gen/worldserver/pb/worldserver.pb.go b/gen/worldserver/pb/worldserver.pb.go index 0f845e8..e9e2d73 100644 --- a/gen/worldserver/pb/worldserver.pb.go +++ b/gen/worldserver/pb/worldserver.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v3.20.3 +// protoc v3.21.12 // source: worldserver.proto package pb @@ -102,6 +102,61 @@ func (BattlegroundType) EnumDescriptor() ([]byte, []int) { return file_worldserver_proto_rawDescGZIP(), []int{0} } +type TakePlayerItemByPosResponse_Status int32 + +const ( + TakePlayerItemByPosResponse_Success TakePlayerItemByPosResponse_Status = 0 + TakePlayerItemByPosResponse_PlayerNotFound TakePlayerItemByPosResponse_Status = 1 + TakePlayerItemByPosResponse_ItemNotFound TakePlayerItemByPosResponse_Status = 2 + TakePlayerItemByPosResponse_ItemNotTradable TakePlayerItemByPosResponse_Status = 3 + TakePlayerItemByPosResponse_Failed TakePlayerItemByPosResponse_Status = 4 +) + +// Enum value maps for TakePlayerItemByPosResponse_Status. +var ( + TakePlayerItemByPosResponse_Status_name = map[int32]string{ + 0: "Success", + 1: "PlayerNotFound", + 2: "ItemNotFound", + 3: "ItemNotTradable", + 4: "Failed", + } + TakePlayerItemByPosResponse_Status_value = map[string]int32{ + "Success": 0, + "PlayerNotFound": 1, + "ItemNotFound": 2, + "ItemNotTradable": 3, + "Failed": 4, + } +) + +func (x TakePlayerItemByPosResponse_Status) Enum() *TakePlayerItemByPosResponse_Status { + p := new(TakePlayerItemByPosResponse_Status) + *p = x + return p +} + +func (x TakePlayerItemByPosResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (TakePlayerItemByPosResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_worldserver_proto_enumTypes[1].Descriptor() +} + +func (TakePlayerItemByPosResponse_Status) Type() protoreflect.EnumType { + return &file_worldserver_proto_enumTypes[1] +} + +func (x TakePlayerItemByPosResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use TakePlayerItemByPosResponse_Status.Descriptor instead. +func (TakePlayerItemByPosResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{3, 0} +} + type AddExistingItemToPlayerResponse_Status int32 const ( @@ -132,11 +187,11 @@ func (x AddExistingItemToPlayerResponse_Status) String() string { } func (AddExistingItemToPlayerResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_worldserver_proto_enumTypes[1].Descriptor() + return file_worldserver_proto_enumTypes[2].Descriptor() } func (AddExistingItemToPlayerResponse_Status) Type() protoreflect.EnumType { - return &file_worldserver_proto_enumTypes[1] + return &file_worldserver_proto_enumTypes[2] } func (x AddExistingItemToPlayerResponse_Status) Number() protoreflect.EnumNumber { @@ -145,7 +200,7 @@ func (x AddExistingItemToPlayerResponse_Status) Number() protoreflect.EnumNumber // Deprecated: Use AddExistingItemToPlayerResponse_Status.Descriptor instead. func (AddExistingItemToPlayerResponse_Status) EnumDescriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{5, 0} + return file_worldserver_proto_rawDescGZIP(), []int{7, 0} } type CanPlayerJoinBattlegroundQueueResponse_Status int32 @@ -175,11 +230,11 @@ func (x CanPlayerJoinBattlegroundQueueResponse_Status) String() string { } func (CanPlayerJoinBattlegroundQueueResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_worldserver_proto_enumTypes[2].Descriptor() + return file_worldserver_proto_enumTypes[3].Descriptor() } func (CanPlayerJoinBattlegroundQueueResponse_Status) Type() protoreflect.EnumType { - return &file_worldserver_proto_enumTypes[2] + return &file_worldserver_proto_enumTypes[3] } func (x CanPlayerJoinBattlegroundQueueResponse_Status) Number() protoreflect.EnumNumber { @@ -188,7 +243,7 @@ func (x CanPlayerJoinBattlegroundQueueResponse_Status) Number() protoreflect.Enu // Deprecated: Use CanPlayerJoinBattlegroundQueueResponse_Status.Descriptor instead. func (CanPlayerJoinBattlegroundQueueResponse_Status) EnumDescriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{19, 0} + return file_worldserver_proto_rawDescGZIP(), []int{21, 0} } type CanPlayerTeleportToBattlegroundResponse_Status int32 @@ -218,11 +273,11 @@ func (x CanPlayerTeleportToBattlegroundResponse_Status) String() string { } func (CanPlayerTeleportToBattlegroundResponse_Status) Descriptor() protoreflect.EnumDescriptor { - return file_worldserver_proto_enumTypes[3].Descriptor() + return file_worldserver_proto_enumTypes[4].Descriptor() } func (CanPlayerTeleportToBattlegroundResponse_Status) Type() protoreflect.EnumType { - return &file_worldserver_proto_enumTypes[3] + return &file_worldserver_proto_enumTypes[4] } func (x CanPlayerTeleportToBattlegroundResponse_Status) Number() protoreflect.EnumNumber { @@ -231,7 +286,374 @@ func (x CanPlayerTeleportToBattlegroundResponse_Status) Number() protoreflect.En // Deprecated: Use CanPlayerTeleportToBattlegroundResponse_Status.Descriptor instead. func (CanPlayerTeleportToBattlegroundResponse_Status) EnumDescriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{21, 0} + return file_worldserver_proto_rawDescGZIP(), []int{23, 0} +} + +type GetLfgPlayerLockInfoResponse_Status int32 + +const ( + GetLfgPlayerLockInfoResponse_Success GetLfgPlayerLockInfoResponse_Status = 0 + GetLfgPlayerLockInfoResponse_PlayerNotFound GetLfgPlayerLockInfoResponse_Status = 1 + GetLfgPlayerLockInfoResponse_InternalError GetLfgPlayerLockInfoResponse_Status = 2 +) + +// Enum value maps for GetLfgPlayerLockInfoResponse_Status. +var ( + GetLfgPlayerLockInfoResponse_Status_name = map[int32]string{ + 0: "Success", + 1: "PlayerNotFound", + 2: "InternalError", + } + GetLfgPlayerLockInfoResponse_Status_value = map[string]int32{ + "Success": 0, + "PlayerNotFound": 1, + "InternalError": 2, + } +) + +func (x GetLfgPlayerLockInfoResponse_Status) Enum() *GetLfgPlayerLockInfoResponse_Status { + p := new(GetLfgPlayerLockInfoResponse_Status) + *p = x + return p +} + +func (x GetLfgPlayerLockInfoResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (GetLfgPlayerLockInfoResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_worldserver_proto_enumTypes[5].Descriptor() +} + +func (GetLfgPlayerLockInfoResponse_Status) Type() protoreflect.EnumType { + return &file_worldserver_proto_enumTypes[5] +} + +func (x GetLfgPlayerLockInfoResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use GetLfgPlayerLockInfoResponse_Status.Descriptor instead. +func (GetLfgPlayerLockInfoResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{26, 0} +} + +type GetLfgPlayerInfoResponse_Status int32 + +const ( + GetLfgPlayerInfoResponse_Success GetLfgPlayerInfoResponse_Status = 0 + GetLfgPlayerInfoResponse_NoHandler GetLfgPlayerInfoResponse_Status = 1 + GetLfgPlayerInfoResponse_PlayerNotFound GetLfgPlayerInfoResponse_Status = 2 + GetLfgPlayerInfoResponse_InternalError GetLfgPlayerInfoResponse_Status = 3 +) + +// Enum value maps for GetLfgPlayerInfoResponse_Status. +var ( + GetLfgPlayerInfoResponse_Status_name = map[int32]string{ + 0: "Success", + 1: "NoHandler", + 2: "PlayerNotFound", + 3: "InternalError", + } + GetLfgPlayerInfoResponse_Status_value = map[string]int32{ + "Success": 0, + "NoHandler": 1, + "PlayerNotFound": 2, + "InternalError": 3, + } +) + +func (x GetLfgPlayerInfoResponse_Status) Enum() *GetLfgPlayerInfoResponse_Status { + p := new(GetLfgPlayerInfoResponse_Status) + *p = x + return p +} + +func (x GetLfgPlayerInfoResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (GetLfgPlayerInfoResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_worldserver_proto_enumTypes[6].Descriptor() +} + +func (GetLfgPlayerInfoResponse_Status) Type() protoreflect.EnumType { + return &file_worldserver_proto_enumTypes[6] +} + +func (x GetLfgPlayerInfoResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use GetLfgPlayerInfoResponse_Status.Descriptor instead. +func (GetLfgPlayerInfoResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{30, 0} +} + +type GetLfgDungeonInfoResponse_Status int32 + +const ( + GetLfgDungeonInfoResponse_Success GetLfgDungeonInfoResponse_Status = 0 + GetLfgDungeonInfoResponse_NoHandler GetLfgDungeonInfoResponse_Status = 1 + GetLfgDungeonInfoResponse_DungeonNotFound GetLfgDungeonInfoResponse_Status = 2 + GetLfgDungeonInfoResponse_InternalError GetLfgDungeonInfoResponse_Status = 3 +) + +// Enum value maps for GetLfgDungeonInfoResponse_Status. +var ( + GetLfgDungeonInfoResponse_Status_name = map[int32]string{ + 0: "Success", + 1: "NoHandler", + 2: "DungeonNotFound", + 3: "InternalError", + } + GetLfgDungeonInfoResponse_Status_value = map[string]int32{ + "Success": 0, + "NoHandler": 1, + "DungeonNotFound": 2, + "InternalError": 3, + } +) + +func (x GetLfgDungeonInfoResponse_Status) Enum() *GetLfgDungeonInfoResponse_Status { + p := new(GetLfgDungeonInfoResponse_Status) + *p = x + return p +} + +func (x GetLfgDungeonInfoResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (GetLfgDungeonInfoResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_worldserver_proto_enumTypes[7].Descriptor() +} + +func (GetLfgDungeonInfoResponse_Status) Type() protoreflect.EnumType { + return &file_worldserver_proto_enumTypes[7] +} + +func (x GetLfgDungeonInfoResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use GetLfgDungeonInfoResponse_Status.Descriptor instead. +func (GetLfgDungeonInfoResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{32, 0} +} + +type TeleportLfgPlayerResponse_Status int32 + +const ( + TeleportLfgPlayerResponse_Success TeleportLfgPlayerResponse_Status = 0 + TeleportLfgPlayerResponse_NoHandler TeleportLfgPlayerResponse_Status = 1 + TeleportLfgPlayerResponse_PlayerNotFound TeleportLfgPlayerResponse_Status = 2 + TeleportLfgPlayerResponse_InternalError TeleportLfgPlayerResponse_Status = 3 +) + +// Enum value maps for TeleportLfgPlayerResponse_Status. +var ( + TeleportLfgPlayerResponse_Status_name = map[int32]string{ + 0: "Success", + 1: "NoHandler", + 2: "PlayerNotFound", + 3: "InternalError", + } + TeleportLfgPlayerResponse_Status_value = map[string]int32{ + "Success": 0, + "NoHandler": 1, + "PlayerNotFound": 2, + "InternalError": 3, + } +) + +func (x TeleportLfgPlayerResponse_Status) Enum() *TeleportLfgPlayerResponse_Status { + p := new(TeleportLfgPlayerResponse_Status) + *p = x + return p +} + +func (x TeleportLfgPlayerResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (TeleportLfgPlayerResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_worldserver_proto_enumTypes[8].Descriptor() +} + +func (TeleportLfgPlayerResponse_Status) Type() protoreflect.EnumType { + return &file_worldserver_proto_enumTypes[8] +} + +func (x TeleportLfgPlayerResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use TeleportLfgPlayerResponse_Status.Descriptor instead. +func (TeleportLfgPlayerResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{34, 0} +} + +type SetLfgBootVoteResponse_Status int32 + +const ( + SetLfgBootVoteResponse_Success SetLfgBootVoteResponse_Status = 0 + SetLfgBootVoteResponse_NoHandler SetLfgBootVoteResponse_Status = 1 + SetLfgBootVoteResponse_PlayerNotFound SetLfgBootVoteResponse_Status = 2 + SetLfgBootVoteResponse_InternalError SetLfgBootVoteResponse_Status = 3 +) + +// Enum value maps for SetLfgBootVoteResponse_Status. +var ( + SetLfgBootVoteResponse_Status_name = map[int32]string{ + 0: "Success", + 1: "NoHandler", + 2: "PlayerNotFound", + 3: "InternalError", + } + SetLfgBootVoteResponse_Status_value = map[string]int32{ + "Success": 0, + "NoHandler": 1, + "PlayerNotFound": 2, + "InternalError": 3, + } +) + +func (x SetLfgBootVoteResponse_Status) Enum() *SetLfgBootVoteResponse_Status { + p := new(SetLfgBootVoteResponse_Status) + *p = x + return p +} + +func (x SetLfgBootVoteResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SetLfgBootVoteResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_worldserver_proto_enumTypes[9].Descriptor() +} + +func (SetLfgBootVoteResponse_Status) Type() protoreflect.EnumType { + return &file_worldserver_proto_enumTypes[9] +} + +func (x SetLfgBootVoteResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SetLfgBootVoteResponse_Status.Descriptor instead. +func (SetLfgBootVoteResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{36, 0} +} + +type MaterializeLfgProposalResponse_Status int32 + +const ( + MaterializeLfgProposalResponse_Success MaterializeLfgProposalResponse_Status = 0 + MaterializeLfgProposalResponse_NoHandler MaterializeLfgProposalResponse_Status = 1 + MaterializeLfgProposalResponse_DungeonNotFound MaterializeLfgProposalResponse_Status = 2 + MaterializeLfgProposalResponse_NoLocalPlayer MaterializeLfgProposalResponse_Status = 3 + MaterializeLfgProposalResponse_InternalError MaterializeLfgProposalResponse_Status = 4 +) + +// Enum value maps for MaterializeLfgProposalResponse_Status. +var ( + MaterializeLfgProposalResponse_Status_name = map[int32]string{ + 0: "Success", + 1: "NoHandler", + 2: "DungeonNotFound", + 3: "NoLocalPlayer", + 4: "InternalError", + } + MaterializeLfgProposalResponse_Status_value = map[string]int32{ + "Success": 0, + "NoHandler": 1, + "DungeonNotFound": 2, + "NoLocalPlayer": 3, + "InternalError": 4, + } +) + +func (x MaterializeLfgProposalResponse_Status) Enum() *MaterializeLfgProposalResponse_Status { + p := new(MaterializeLfgProposalResponse_Status) + *p = x + return p +} + +func (x MaterializeLfgProposalResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (MaterializeLfgProposalResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_worldserver_proto_enumTypes[10].Descriptor() +} + +func (MaterializeLfgProposalResponse_Status) Type() protoreflect.EnumType { + return &file_worldserver_proto_enumTypes[10] +} + +func (x MaterializeLfgProposalResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use MaterializeLfgProposalResponse_Status.Descriptor instead. +func (MaterializeLfgProposalResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{38, 0} +} + +type CreateGuildResponse_Status int32 + +const ( + CreateGuildResponse_Success CreateGuildResponse_Status = 0 + CreateGuildResponse_NameExists CreateGuildResponse_Status = 1 + CreateGuildResponse_InvalidName CreateGuildResponse_Status = 2 + CreateGuildResponse_LeaderNotFound CreateGuildResponse_Status = 3 + CreateGuildResponse_InternalError CreateGuildResponse_Status = 4 +) + +// Enum value maps for CreateGuildResponse_Status. +var ( + CreateGuildResponse_Status_name = map[int32]string{ + 0: "Success", + 1: "NameExists", + 2: "InvalidName", + 3: "LeaderNotFound", + 4: "InternalError", + } + CreateGuildResponse_Status_value = map[string]int32{ + "Success": 0, + "NameExists": 1, + "InvalidName": 2, + "LeaderNotFound": 3, + "InternalError": 4, + } +) + +func (x CreateGuildResponse_Status) Enum() *CreateGuildResponse_Status { + p := new(CreateGuildResponse_Status) + *p = x + return p +} + +func (x CreateGuildResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (CreateGuildResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_worldserver_proto_enumTypes[11].Descriptor() +} + +func (CreateGuildResponse_Status) Type() protoreflect.EnumType { + return &file_worldserver_proto_enumTypes[11] +} + +func (x CreateGuildResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use CreateGuildResponse_Status.Descriptor instead. +func (CreateGuildResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{40, 0} } // GetPlayerItemsByGuids @@ -353,20 +775,22 @@ func (x *GetPlayerItemsByGuidsResponse) GetItems() []*GetPlayerItemsByGuidsRespo return nil } -// RemoveItemsWithGuidsFromPlayer -type RemoveItemsWithGuidsFromPlayerRequest struct { +// TakePlayerItemByPos +type TakePlayerItemByPosRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - PlayerGuid uint64 `protobuf:"varint,2,opt,name=playerGuid,proto3" json:"playerGuid,omitempty"` - Guids []uint64 `protobuf:"varint,3,rep,packed,name=guids,proto3" json:"guids,omitempty"` - AssignToPlayerGuid uint64 `protobuf:"varint,4,opt,name=assignToPlayerGuid,proto3" json:"assignToPlayerGuid,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + PlayerGuid uint64 `protobuf:"varint,2,opt,name=playerGuid,proto3" json:"playerGuid,omitempty"` + BagSlot uint32 `protobuf:"varint,3,opt,name=bagSlot,proto3" json:"bagSlot,omitempty"` + Slot uint32 `protobuf:"varint,4,opt,name=slot,proto3" json:"slot,omitempty"` + Count uint32 `protobuf:"varint,5,opt,name=count,proto3" json:"count,omitempty"` + AssignToPlayerGuid uint64 `protobuf:"varint,6,opt,name=assignToPlayerGuid,proto3" json:"assignToPlayerGuid,omitempty"` } -func (x *RemoveItemsWithGuidsFromPlayerRequest) Reset() { - *x = RemoveItemsWithGuidsFromPlayerRequest{} +func (x *TakePlayerItemByPosRequest) Reset() { + *x = TakePlayerItemByPosRequest{} if protoimpl.UnsafeEnabled { mi := &file_worldserver_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -374,13 +798,13 @@ func (x *RemoveItemsWithGuidsFromPlayerRequest) Reset() { } } -func (x *RemoveItemsWithGuidsFromPlayerRequest) String() string { +func (x *TakePlayerItemByPosRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RemoveItemsWithGuidsFromPlayerRequest) ProtoMessage() {} +func (*TakePlayerItemByPosRequest) ProtoMessage() {} -func (x *RemoveItemsWithGuidsFromPlayerRequest) ProtoReflect() protoreflect.Message { +func (x *TakePlayerItemByPosRequest) ProtoReflect() protoreflect.Message { mi := &file_worldserver_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -392,50 +816,65 @@ func (x *RemoveItemsWithGuidsFromPlayerRequest) ProtoReflect() protoreflect.Mess return mi.MessageOf(x) } -// Deprecated: Use RemoveItemsWithGuidsFromPlayerRequest.ProtoReflect.Descriptor instead. -func (*RemoveItemsWithGuidsFromPlayerRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use TakePlayerItemByPosRequest.ProtoReflect.Descriptor instead. +func (*TakePlayerItemByPosRequest) Descriptor() ([]byte, []int) { return file_worldserver_proto_rawDescGZIP(), []int{2} } -func (x *RemoveItemsWithGuidsFromPlayerRequest) GetApi() string { +func (x *TakePlayerItemByPosRequest) GetApi() string { if x != nil { return x.Api } return "" } -func (x *RemoveItemsWithGuidsFromPlayerRequest) GetPlayerGuid() uint64 { +func (x *TakePlayerItemByPosRequest) GetPlayerGuid() uint64 { if x != nil { return x.PlayerGuid } return 0 } -func (x *RemoveItemsWithGuidsFromPlayerRequest) GetGuids() []uint64 { +func (x *TakePlayerItemByPosRequest) GetBagSlot() uint32 { if x != nil { - return x.Guids + return x.BagSlot } - return nil + return 0 } -func (x *RemoveItemsWithGuidsFromPlayerRequest) GetAssignToPlayerGuid() uint64 { +func (x *TakePlayerItemByPosRequest) GetSlot() uint32 { + if x != nil { + return x.Slot + } + return 0 +} + +func (x *TakePlayerItemByPosRequest) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *TakePlayerItemByPosRequest) GetAssignToPlayerGuid() uint64 { if x != nil { return x.AssignToPlayerGuid } return 0 } -type RemoveItemsWithGuidsFromPlayerResponse struct { +type TakePlayerItemByPosResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - UpdatedItemsGuids []uint64 `protobuf:"varint,2,rep,packed,name=updatedItemsGuids,proto3" json:"updatedItemsGuids,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status TakePlayerItemByPosResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.TakePlayerItemByPosResponse_Status" json:"status,omitempty"` + Item *TakePlayerItemByPosResponse_Item `protobuf:"bytes,3,opt,name=item,proto3" json:"item,omitempty"` } -func (x *RemoveItemsWithGuidsFromPlayerResponse) Reset() { - *x = RemoveItemsWithGuidsFromPlayerResponse{} +func (x *TakePlayerItemByPosResponse) Reset() { + *x = TakePlayerItemByPosResponse{} if protoimpl.UnsafeEnabled { mi := &file_worldserver_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -443,6 +882,140 @@ func (x *RemoveItemsWithGuidsFromPlayerResponse) Reset() { } } +func (x *TakePlayerItemByPosResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TakePlayerItemByPosResponse) ProtoMessage() {} + +func (x *TakePlayerItemByPosResponse) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TakePlayerItemByPosResponse.ProtoReflect.Descriptor instead. +func (*TakePlayerItemByPosResponse) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{3} +} + +func (x *TakePlayerItemByPosResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *TakePlayerItemByPosResponse) GetStatus() TakePlayerItemByPosResponse_Status { + if x != nil { + return x.Status + } + return TakePlayerItemByPosResponse_Success +} + +func (x *TakePlayerItemByPosResponse) GetItem() *TakePlayerItemByPosResponse_Item { + if x != nil { + return x.Item + } + return nil +} + +// RemoveItemsWithGuidsFromPlayer +type RemoveItemsWithGuidsFromPlayerRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + PlayerGuid uint64 `protobuf:"varint,2,opt,name=playerGuid,proto3" json:"playerGuid,omitempty"` + Guids []uint64 `protobuf:"varint,3,rep,packed,name=guids,proto3" json:"guids,omitempty"` + AssignToPlayerGuid uint64 `protobuf:"varint,4,opt,name=assignToPlayerGuid,proto3" json:"assignToPlayerGuid,omitempty"` +} + +func (x *RemoveItemsWithGuidsFromPlayerRequest) Reset() { + *x = RemoveItemsWithGuidsFromPlayerRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoveItemsWithGuidsFromPlayerRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveItemsWithGuidsFromPlayerRequest) ProtoMessage() {} + +func (x *RemoveItemsWithGuidsFromPlayerRequest) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveItemsWithGuidsFromPlayerRequest.ProtoReflect.Descriptor instead. +func (*RemoveItemsWithGuidsFromPlayerRequest) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{4} +} + +func (x *RemoveItemsWithGuidsFromPlayerRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *RemoveItemsWithGuidsFromPlayerRequest) GetPlayerGuid() uint64 { + if x != nil { + return x.PlayerGuid + } + return 0 +} + +func (x *RemoveItemsWithGuidsFromPlayerRequest) GetGuids() []uint64 { + if x != nil { + return x.Guids + } + return nil +} + +func (x *RemoveItemsWithGuidsFromPlayerRequest) GetAssignToPlayerGuid() uint64 { + if x != nil { + return x.AssignToPlayerGuid + } + return 0 +} + +type RemoveItemsWithGuidsFromPlayerResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + UpdatedItemsGuids []uint64 `protobuf:"varint,2,rep,packed,name=updatedItemsGuids,proto3" json:"updatedItemsGuids,omitempty"` +} + +func (x *RemoveItemsWithGuidsFromPlayerResponse) Reset() { + *x = RemoveItemsWithGuidsFromPlayerResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + func (x *RemoveItemsWithGuidsFromPlayerResponse) String() string { return protoimpl.X.MessageStringOf(x) } @@ -450,7 +1023,7 @@ func (x *RemoveItemsWithGuidsFromPlayerResponse) String() string { func (*RemoveItemsWithGuidsFromPlayerResponse) ProtoMessage() {} func (x *RemoveItemsWithGuidsFromPlayerResponse) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[3] + mi := &file_worldserver_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -463,7 +1036,7 @@ func (x *RemoveItemsWithGuidsFromPlayerResponse) ProtoReflect() protoreflect.Mes // Deprecated: Use RemoveItemsWithGuidsFromPlayerResponse.ProtoReflect.Descriptor instead. func (*RemoveItemsWithGuidsFromPlayerResponse) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{3} + return file_worldserver_proto_rawDescGZIP(), []int{5} } func (x *RemoveItemsWithGuidsFromPlayerResponse) GetApi() string { @@ -489,12 +1062,15 @@ type AddExistingItemToPlayerRequest struct { Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` PlayerGuid uint64 `protobuf:"varint,2,opt,name=playerGuid,proto3" json:"playerGuid,omitempty"` Item *AddExistingItemToPlayerRequest_Item `protobuf:"bytes,3,opt,name=item,proto3" json:"item,omitempty"` + StoreAtPos bool `protobuf:"varint,4,opt,name=storeAtPos,proto3" json:"storeAtPos,omitempty"` + BagSlot uint32 `protobuf:"varint,5,opt,name=bagSlot,proto3" json:"bagSlot,omitempty"` + Slot uint32 `protobuf:"varint,6,opt,name=slot,proto3" json:"slot,omitempty"` } func (x *AddExistingItemToPlayerRequest) Reset() { *x = AddExistingItemToPlayerRequest{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[4] + mi := &file_worldserver_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -507,7 +1083,7 @@ func (x *AddExistingItemToPlayerRequest) String() string { func (*AddExistingItemToPlayerRequest) ProtoMessage() {} func (x *AddExistingItemToPlayerRequest) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[4] + mi := &file_worldserver_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -520,7 +1096,7 @@ func (x *AddExistingItemToPlayerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AddExistingItemToPlayerRequest.ProtoReflect.Descriptor instead. func (*AddExistingItemToPlayerRequest) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{4} + return file_worldserver_proto_rawDescGZIP(), []int{6} } func (x *AddExistingItemToPlayerRequest) GetApi() string { @@ -544,6 +1120,27 @@ func (x *AddExistingItemToPlayerRequest) GetItem() *AddExistingItemToPlayerReque return nil } +func (x *AddExistingItemToPlayerRequest) GetStoreAtPos() bool { + if x != nil { + return x.StoreAtPos + } + return false +} + +func (x *AddExistingItemToPlayerRequest) GetBagSlot() uint32 { + if x != nil { + return x.BagSlot + } + return 0 +} + +func (x *AddExistingItemToPlayerRequest) GetSlot() uint32 { + if x != nil { + return x.Slot + } + return 0 +} + type AddExistingItemToPlayerResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -556,7 +1153,7 @@ type AddExistingItemToPlayerResponse struct { func (x *AddExistingItemToPlayerResponse) Reset() { *x = AddExistingItemToPlayerResponse{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[5] + mi := &file_worldserver_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -569,7 +1166,7 @@ func (x *AddExistingItemToPlayerResponse) String() string { func (*AddExistingItemToPlayerResponse) ProtoMessage() {} func (x *AddExistingItemToPlayerResponse) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[5] + mi := &file_worldserver_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -582,7 +1179,7 @@ func (x *AddExistingItemToPlayerResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AddExistingItemToPlayerResponse.ProtoReflect.Descriptor instead. func (*AddExistingItemToPlayerResponse) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{5} + return file_worldserver_proto_rawDescGZIP(), []int{7} } func (x *AddExistingItemToPlayerResponse) GetApi() string { @@ -612,7 +1209,7 @@ type GetMoneyForPlayerRequest struct { func (x *GetMoneyForPlayerRequest) Reset() { *x = GetMoneyForPlayerRequest{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[6] + mi := &file_worldserver_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -625,7 +1222,7 @@ func (x *GetMoneyForPlayerRequest) String() string { func (*GetMoneyForPlayerRequest) ProtoMessage() {} func (x *GetMoneyForPlayerRequest) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[6] + mi := &file_worldserver_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -638,7 +1235,7 @@ func (x *GetMoneyForPlayerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMoneyForPlayerRequest.ProtoReflect.Descriptor instead. func (*GetMoneyForPlayerRequest) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{6} + return file_worldserver_proto_rawDescGZIP(), []int{8} } func (x *GetMoneyForPlayerRequest) GetApi() string { @@ -667,7 +1264,7 @@ type GetMoneyForPlayerResponse struct { func (x *GetMoneyForPlayerResponse) Reset() { *x = GetMoneyForPlayerResponse{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[7] + mi := &file_worldserver_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -680,7 +1277,7 @@ func (x *GetMoneyForPlayerResponse) String() string { func (*GetMoneyForPlayerResponse) ProtoMessage() {} func (x *GetMoneyForPlayerResponse) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[7] + mi := &file_worldserver_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -693,7 +1290,7 @@ func (x *GetMoneyForPlayerResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMoneyForPlayerResponse.ProtoReflect.Descriptor instead. func (*GetMoneyForPlayerResponse) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{7} + return file_worldserver_proto_rawDescGZIP(), []int{9} } func (x *GetMoneyForPlayerResponse) GetApi() string { @@ -724,7 +1321,7 @@ type ModifyMoneyForPlayerRequest struct { func (x *ModifyMoneyForPlayerRequest) Reset() { *x = ModifyMoneyForPlayerRequest{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[8] + mi := &file_worldserver_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -737,7 +1334,7 @@ func (x *ModifyMoneyForPlayerRequest) String() string { func (*ModifyMoneyForPlayerRequest) ProtoMessage() {} func (x *ModifyMoneyForPlayerRequest) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[8] + mi := &file_worldserver_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -750,7 +1347,7 @@ func (x *ModifyMoneyForPlayerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ModifyMoneyForPlayerRequest.ProtoReflect.Descriptor instead. func (*ModifyMoneyForPlayerRequest) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{8} + return file_worldserver_proto_rawDescGZIP(), []int{10} } func (x *ModifyMoneyForPlayerRequest) GetApi() string { @@ -786,7 +1383,7 @@ type ModifyMoneyForPlayerResponse struct { func (x *ModifyMoneyForPlayerResponse) Reset() { *x = ModifyMoneyForPlayerResponse{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[9] + mi := &file_worldserver_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -799,7 +1396,7 @@ func (x *ModifyMoneyForPlayerResponse) String() string { func (*ModifyMoneyForPlayerResponse) ProtoMessage() {} func (x *ModifyMoneyForPlayerResponse) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[9] + mi := &file_worldserver_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -812,7 +1409,7 @@ func (x *ModifyMoneyForPlayerResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ModifyMoneyForPlayerResponse.ProtoReflect.Descriptor instead. func (*ModifyMoneyForPlayerResponse) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{9} + return file_worldserver_proto_rawDescGZIP(), []int{11} } func (x *ModifyMoneyForPlayerResponse) GetApi() string { @@ -844,7 +1441,7 @@ type CanPlayerInteractWithNPCRequest struct { func (x *CanPlayerInteractWithNPCRequest) Reset() { *x = CanPlayerInteractWithNPCRequest{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[10] + mi := &file_worldserver_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -857,7 +1454,7 @@ func (x *CanPlayerInteractWithNPCRequest) String() string { func (*CanPlayerInteractWithNPCRequest) ProtoMessage() {} func (x *CanPlayerInteractWithNPCRequest) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[10] + mi := &file_worldserver_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -870,7 +1467,7 @@ func (x *CanPlayerInteractWithNPCRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CanPlayerInteractWithNPCRequest.ProtoReflect.Descriptor instead. func (*CanPlayerInteractWithNPCRequest) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{10} + return file_worldserver_proto_rawDescGZIP(), []int{12} } func (x *CanPlayerInteractWithNPCRequest) GetApi() string { @@ -913,7 +1510,7 @@ type CanPlayerInteractWithNPCResponse struct { func (x *CanPlayerInteractWithNPCResponse) Reset() { *x = CanPlayerInteractWithNPCResponse{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[11] + mi := &file_worldserver_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -926,7 +1523,7 @@ func (x *CanPlayerInteractWithNPCResponse) String() string { func (*CanPlayerInteractWithNPCResponse) ProtoMessage() {} func (x *CanPlayerInteractWithNPCResponse) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[11] + mi := &file_worldserver_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -939,7 +1536,7 @@ func (x *CanPlayerInteractWithNPCResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CanPlayerInteractWithNPCResponse.ProtoReflect.Descriptor instead. func (*CanPlayerInteractWithNPCResponse) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{11} + return file_worldserver_proto_rawDescGZIP(), []int{13} } func (x *CanPlayerInteractWithNPCResponse) GetApi() string { @@ -971,7 +1568,7 @@ type CanPlayerInteractWithGameObjectRequest struct { func (x *CanPlayerInteractWithGameObjectRequest) Reset() { *x = CanPlayerInteractWithGameObjectRequest{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[12] + mi := &file_worldserver_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -984,7 +1581,7 @@ func (x *CanPlayerInteractWithGameObjectRequest) String() string { func (*CanPlayerInteractWithGameObjectRequest) ProtoMessage() {} func (x *CanPlayerInteractWithGameObjectRequest) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[12] + mi := &file_worldserver_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -997,7 +1594,7 @@ func (x *CanPlayerInteractWithGameObjectRequest) ProtoReflect() protoreflect.Mes // Deprecated: Use CanPlayerInteractWithGameObjectRequest.ProtoReflect.Descriptor instead. func (*CanPlayerInteractWithGameObjectRequest) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{12} + return file_worldserver_proto_rawDescGZIP(), []int{14} } func (x *CanPlayerInteractWithGameObjectRequest) GetApi() string { @@ -1040,7 +1637,7 @@ type CanPlayerInteractWithGameObjectResponse struct { func (x *CanPlayerInteractWithGameObjectResponse) Reset() { *x = CanPlayerInteractWithGameObjectResponse{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[13] + mi := &file_worldserver_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1053,7 +1650,7 @@ func (x *CanPlayerInteractWithGameObjectResponse) String() string { func (*CanPlayerInteractWithGameObjectResponse) ProtoMessage() {} func (x *CanPlayerInteractWithGameObjectResponse) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[13] + mi := &file_worldserver_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1066,7 +1663,7 @@ func (x *CanPlayerInteractWithGameObjectResponse) ProtoReflect() protoreflect.Me // Deprecated: Use CanPlayerInteractWithGameObjectResponse.ProtoReflect.Descriptor instead. func (*CanPlayerInteractWithGameObjectResponse) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{13} + return file_worldserver_proto_rawDescGZIP(), []int{15} } func (x *CanPlayerInteractWithGameObjectResponse) GetApi() string { @@ -1089,20 +1686,24 @@ type StartBattlegroundRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` - BattlegroundTypeID BattlegroundType `protobuf:"varint,2,opt,name=battlegroundTypeID,proto3,enum=v1.BattlegroundType" json:"battlegroundTypeID,omitempty"` - ArenaType uint32 `protobuf:"varint,3,opt,name=arenaType,proto3" json:"arenaType,omitempty"` // Arenas not supported yet. - IsRated bool `protobuf:"varint,4,opt,name=isRated,proto3" json:"isRated,omitempty"` // Arenas not supported yet. - MapID uint32 `protobuf:"varint,5,opt,name=mapID,proto3" json:"mapID,omitempty"` - BracketLvl uint32 `protobuf:"varint,6,opt,name=bracketLvl,proto3" json:"bracketLvl,omitempty"` - PlayersToAddAlliance []uint64 `protobuf:"varint,7,rep,packed,name=playersToAddAlliance,proto3" json:"playersToAddAlliance,omitempty"` - PlayersToAddHorde []uint64 `protobuf:"varint,8,rep,packed,name=playersToAddHorde,proto3" json:"playersToAddHorde,omitempty"` + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + BattlegroundTypeID BattlegroundType `protobuf:"varint,2,opt,name=battlegroundTypeID,proto3,enum=v1.BattlegroundType" json:"battlegroundTypeID,omitempty"` + ArenaType uint32 `protobuf:"varint,3,opt,name=arenaType,proto3" json:"arenaType,omitempty"` + IsRated bool `protobuf:"varint,4,opt,name=isRated,proto3" json:"isRated,omitempty"` + MapID uint32 `protobuf:"varint,5,opt,name=mapID,proto3" json:"mapID,omitempty"` + BracketLvl uint32 `protobuf:"varint,6,opt,name=bracketLvl,proto3" json:"bracketLvl,omitempty"` + PlayersToAddAlliance []uint64 `protobuf:"varint,7,rep,packed,name=playersToAddAlliance,proto3" json:"playersToAddAlliance,omitempty"` + PlayersToAddHorde []uint64 `protobuf:"varint,8,rep,packed,name=playersToAddHorde,proto3" json:"playersToAddHorde,omitempty"` + AllianceArenaTeamID uint32 `protobuf:"varint,9,opt,name=allianceArenaTeamID,proto3" json:"allianceArenaTeamID,omitempty"` + HordeArenaTeamID uint32 `protobuf:"varint,10,opt,name=hordeArenaTeamID,proto3" json:"hordeArenaTeamID,omitempty"` + AllianceArenaMatchmakerRating uint32 `protobuf:"varint,11,opt,name=allianceArenaMatchmakerRating,proto3" json:"allianceArenaMatchmakerRating,omitempty"` + HordeArenaMatchmakerRating uint32 `protobuf:"varint,12,opt,name=hordeArenaMatchmakerRating,proto3" json:"hordeArenaMatchmakerRating,omitempty"` } func (x *StartBattlegroundRequest) Reset() { *x = StartBattlegroundRequest{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[14] + mi := &file_worldserver_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1115,7 +1716,7 @@ func (x *StartBattlegroundRequest) String() string { func (*StartBattlegroundRequest) ProtoMessage() {} func (x *StartBattlegroundRequest) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[14] + mi := &file_worldserver_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1128,7 +1729,7 @@ func (x *StartBattlegroundRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StartBattlegroundRequest.ProtoReflect.Descriptor instead. func (*StartBattlegroundRequest) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{14} + return file_worldserver_proto_rawDescGZIP(), []int{16} } func (x *StartBattlegroundRequest) GetApi() string { @@ -1187,6 +1788,34 @@ func (x *StartBattlegroundRequest) GetPlayersToAddHorde() []uint64 { return nil } +func (x *StartBattlegroundRequest) GetAllianceArenaTeamID() uint32 { + if x != nil { + return x.AllianceArenaTeamID + } + return 0 +} + +func (x *StartBattlegroundRequest) GetHordeArenaTeamID() uint32 { + if x != nil { + return x.HordeArenaTeamID + } + return 0 +} + +func (x *StartBattlegroundRequest) GetAllianceArenaMatchmakerRating() uint32 { + if x != nil { + return x.AllianceArenaMatchmakerRating + } + return 0 +} + +func (x *StartBattlegroundRequest) GetHordeArenaMatchmakerRating() uint32 { + if x != nil { + return x.HordeArenaMatchmakerRating + } + return 0 +} + type StartBattlegroundResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1200,7 +1829,7 @@ type StartBattlegroundResponse struct { func (x *StartBattlegroundResponse) Reset() { *x = StartBattlegroundResponse{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[15] + mi := &file_worldserver_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1213,7 +1842,7 @@ func (x *StartBattlegroundResponse) String() string { func (*StartBattlegroundResponse) ProtoMessage() {} func (x *StartBattlegroundResponse) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[15] + mi := &file_worldserver_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1226,7 +1855,7 @@ func (x *StartBattlegroundResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StartBattlegroundResponse.ProtoReflect.Descriptor instead. func (*StartBattlegroundResponse) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{15} + return file_worldserver_proto_rawDescGZIP(), []int{17} } func (x *StartBattlegroundResponse) GetApi() string { @@ -1266,7 +1895,7 @@ type AddPlayersToBattlegroundRequest struct { func (x *AddPlayersToBattlegroundRequest) Reset() { *x = AddPlayersToBattlegroundRequest{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[16] + mi := &file_worldserver_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1279,7 +1908,7 @@ func (x *AddPlayersToBattlegroundRequest) String() string { func (*AddPlayersToBattlegroundRequest) ProtoMessage() {} func (x *AddPlayersToBattlegroundRequest) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[16] + mi := &file_worldserver_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1292,7 +1921,7 @@ func (x *AddPlayersToBattlegroundRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AddPlayersToBattlegroundRequest.ProtoReflect.Descriptor instead. func (*AddPlayersToBattlegroundRequest) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{16} + return file_worldserver_proto_rawDescGZIP(), []int{18} } func (x *AddPlayersToBattlegroundRequest) GetApi() string { @@ -1341,7 +1970,7 @@ type AddPlayersToBattlegroundResponse struct { func (x *AddPlayersToBattlegroundResponse) Reset() { *x = AddPlayersToBattlegroundResponse{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[17] + mi := &file_worldserver_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1354,7 +1983,7 @@ func (x *AddPlayersToBattlegroundResponse) String() string { func (*AddPlayersToBattlegroundResponse) ProtoMessage() {} func (x *AddPlayersToBattlegroundResponse) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[17] + mi := &file_worldserver_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1367,7 +1996,7 @@ func (x *AddPlayersToBattlegroundResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AddPlayersToBattlegroundResponse.ProtoReflect.Descriptor instead. func (*AddPlayersToBattlegroundResponse) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{17} + return file_worldserver_proto_rawDescGZIP(), []int{19} } func (x *AddPlayersToBattlegroundResponse) GetApi() string { @@ -1390,7 +2019,7 @@ type CanPlayerJoinBattlegroundQueueRequest struct { func (x *CanPlayerJoinBattlegroundQueueRequest) Reset() { *x = CanPlayerJoinBattlegroundQueueRequest{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[18] + mi := &file_worldserver_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1403,7 +2032,7 @@ func (x *CanPlayerJoinBattlegroundQueueRequest) String() string { func (*CanPlayerJoinBattlegroundQueueRequest) ProtoMessage() {} func (x *CanPlayerJoinBattlegroundQueueRequest) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[18] + mi := &file_worldserver_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1416,7 +2045,7 @@ func (x *CanPlayerJoinBattlegroundQueueRequest) ProtoReflect() protoreflect.Mess // Deprecated: Use CanPlayerJoinBattlegroundQueueRequest.ProtoReflect.Descriptor instead. func (*CanPlayerJoinBattlegroundQueueRequest) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{18} + return file_worldserver_proto_rawDescGZIP(), []int{20} } func (x *CanPlayerJoinBattlegroundQueueRequest) GetApi() string { @@ -1445,7 +2074,7 @@ type CanPlayerJoinBattlegroundQueueResponse struct { func (x *CanPlayerJoinBattlegroundQueueResponse) Reset() { *x = CanPlayerJoinBattlegroundQueueResponse{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[19] + mi := &file_worldserver_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1458,7 +2087,7 @@ func (x *CanPlayerJoinBattlegroundQueueResponse) String() string { func (*CanPlayerJoinBattlegroundQueueResponse) ProtoMessage() {} func (x *CanPlayerJoinBattlegroundQueueResponse) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[19] + mi := &file_worldserver_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1471,7 +2100,7 @@ func (x *CanPlayerJoinBattlegroundQueueResponse) ProtoReflect() protoreflect.Mes // Deprecated: Use CanPlayerJoinBattlegroundQueueResponse.ProtoReflect.Descriptor instead. func (*CanPlayerJoinBattlegroundQueueResponse) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{19} + return file_worldserver_proto_rawDescGZIP(), []int{21} } func (x *CanPlayerJoinBattlegroundQueueResponse) GetApi() string { @@ -1501,7 +2130,7 @@ type CanPlayerTeleportToBattlegroundRequest struct { func (x *CanPlayerTeleportToBattlegroundRequest) Reset() { *x = CanPlayerTeleportToBattlegroundRequest{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[20] + mi := &file_worldserver_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1514,7 +2143,7 @@ func (x *CanPlayerTeleportToBattlegroundRequest) String() string { func (*CanPlayerTeleportToBattlegroundRequest) ProtoMessage() {} func (x *CanPlayerTeleportToBattlegroundRequest) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[20] + mi := &file_worldserver_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1527,7 +2156,7 @@ func (x *CanPlayerTeleportToBattlegroundRequest) ProtoReflect() protoreflect.Mes // Deprecated: Use CanPlayerTeleportToBattlegroundRequest.ProtoReflect.Descriptor instead. func (*CanPlayerTeleportToBattlegroundRequest) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{20} + return file_worldserver_proto_rawDescGZIP(), []int{22} } func (x *CanPlayerTeleportToBattlegroundRequest) GetApi() string { @@ -1556,7 +2185,7 @@ type CanPlayerTeleportToBattlegroundResponse struct { func (x *CanPlayerTeleportToBattlegroundResponse) Reset() { *x = CanPlayerTeleportToBattlegroundResponse{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[21] + mi := &file_worldserver_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1569,7 +2198,7 @@ func (x *CanPlayerTeleportToBattlegroundResponse) String() string { func (*CanPlayerTeleportToBattlegroundResponse) ProtoMessage() {} func (x *CanPlayerTeleportToBattlegroundResponse) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[21] + mi := &file_worldserver_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1582,7 +2211,7 @@ func (x *CanPlayerTeleportToBattlegroundResponse) ProtoReflect() protoreflect.Me // Deprecated: Use CanPlayerTeleportToBattlegroundResponse.ProtoReflect.Descriptor instead. func (*CanPlayerTeleportToBattlegroundResponse) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{21} + return file_worldserver_proto_rawDescGZIP(), []int{23} } func (x *CanPlayerTeleportToBattlegroundResponse) GetApi() string { @@ -1599,41 +2228,32 @@ func (x *CanPlayerTeleportToBattlegroundResponse) GetStatus() CanPlayerTeleportT return CanPlayerTeleportToBattlegroundResponse_Success } -type GetPlayerItemsByGuidsResponse_Item struct { +type LfgDungeonLock struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` - Entry uint32 `protobuf:"varint,2,opt,name=entry,proto3" json:"entry,omitempty"` - Owner uint64 `protobuf:"varint,3,opt,name=owner,proto3" json:"owner,omitempty"` - BagSlot uint32 `protobuf:"varint,4,opt,name=bagSlot,proto3" json:"bagSlot,omitempty"` - Slot uint32 `protobuf:"varint,5,opt,name=slot,proto3" json:"slot,omitempty"` - IsTradable bool `protobuf:"varint,6,opt,name=isTradable,proto3" json:"isTradable,omitempty"` - Count uint32 `protobuf:"varint,7,opt,name=count,proto3" json:"count,omitempty"` - Flags uint32 `protobuf:"varint,8,opt,name=flags,proto3" json:"flags,omitempty"` - Durability uint32 `protobuf:"varint,9,opt,name=durability,proto3" json:"durability,omitempty"` - RandomPropertyID uint32 `protobuf:"varint,10,opt,name=randomPropertyID,proto3" json:"randomPropertyID,omitempty"` - Text string `protobuf:"bytes,11,opt,name=text,proto3" json:"text,omitempty"` + DungeonEntry uint32 `protobuf:"varint,1,opt,name=dungeonEntry,proto3" json:"dungeonEntry,omitempty"` + LockStatus uint32 `protobuf:"varint,2,opt,name=lockStatus,proto3" json:"lockStatus,omitempty"` } -func (x *GetPlayerItemsByGuidsResponse_Item) Reset() { - *x = GetPlayerItemsByGuidsResponse_Item{} +func (x *LfgDungeonLock) Reset() { + *x = LfgDungeonLock{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[22] + mi := &file_worldserver_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetPlayerItemsByGuidsResponse_Item) String() string { +func (x *LfgDungeonLock) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetPlayerItemsByGuidsResponse_Item) ProtoMessage() {} +func (*LfgDungeonLock) ProtoMessage() {} -func (x *GetPlayerItemsByGuidsResponse_Item) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[22] +func (x *LfgDungeonLock) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1644,119 +2264,265 @@ func (x *GetPlayerItemsByGuidsResponse_Item) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use GetPlayerItemsByGuidsResponse_Item.ProtoReflect.Descriptor instead. -func (*GetPlayerItemsByGuidsResponse_Item) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{1, 0} +// Deprecated: Use LfgDungeonLock.ProtoReflect.Descriptor instead. +func (*LfgDungeonLock) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{24} } -func (x *GetPlayerItemsByGuidsResponse_Item) GetGuid() uint64 { +func (x *LfgDungeonLock) GetDungeonEntry() uint32 { if x != nil { - return x.Guid + return x.DungeonEntry } return 0 } -func (x *GetPlayerItemsByGuidsResponse_Item) GetEntry() uint32 { +func (x *LfgDungeonLock) GetLockStatus() uint32 { if x != nil { - return x.Entry + return x.LockStatus } return 0 } -func (x *GetPlayerItemsByGuidsResponse_Item) GetOwner() uint64 { +type GetLfgPlayerLockInfoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + PlayerGUID uint64 `protobuf:"varint,2,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + DungeonEntries []uint32 `protobuf:"varint,3,rep,packed,name=dungeonEntries,proto3" json:"dungeonEntries,omitempty"` +} + +func (x *GetLfgPlayerLockInfoRequest) Reset() { + *x = GetLfgPlayerLockInfoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetLfgPlayerLockInfoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLfgPlayerLockInfoRequest) ProtoMessage() {} + +func (x *GetLfgPlayerLockInfoRequest) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetLfgPlayerLockInfoRequest.ProtoReflect.Descriptor instead. +func (*GetLfgPlayerLockInfoRequest) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{25} +} + +func (x *GetLfgPlayerLockInfoRequest) GetApi() string { if x != nil { - return x.Owner + return x.Api } - return 0 + return "" } -func (x *GetPlayerItemsByGuidsResponse_Item) GetBagSlot() uint32 { +func (x *GetLfgPlayerLockInfoRequest) GetPlayerGUID() uint64 { if x != nil { - return x.BagSlot + return x.PlayerGUID } return 0 } -func (x *GetPlayerItemsByGuidsResponse_Item) GetSlot() uint32 { +func (x *GetLfgPlayerLockInfoRequest) GetDungeonEntries() []uint32 { if x != nil { - return x.Slot + return x.DungeonEntries } - return 0 + return nil } -func (x *GetPlayerItemsByGuidsResponse_Item) GetIsTradable() bool { +type GetLfgPlayerLockInfoResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status GetLfgPlayerLockInfoResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.GetLfgPlayerLockInfoResponse_Status" json:"status,omitempty"` + Locks []*LfgDungeonLock `protobuf:"bytes,3,rep,name=locks,proto3" json:"locks,omitempty"` + // AzerothCore LfgJoinResult for player-wide join restrictions not represented + // as per-dungeon lock rows, such as deserter or random dungeon cooldown. + JoinResult uint32 `protobuf:"varint,4,opt,name=joinResult,proto3" json:"joinResult,omitempty"` + // AzerothCore DBC-filtered and canonicalized dungeon entries accepted from + // the client selection. Gateway must queue these instead of raw client slots. + ValidDungeonEntries []uint32 `protobuf:"varint,5,rep,packed,name=validDungeonEntries,proto3" json:"validDungeonEntries,omitempty"` +} + +func (x *GetLfgPlayerLockInfoResponse) Reset() { + *x = GetLfgPlayerLockInfoResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetLfgPlayerLockInfoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLfgPlayerLockInfoResponse) ProtoMessage() {} + +func (x *GetLfgPlayerLockInfoResponse) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetLfgPlayerLockInfoResponse.ProtoReflect.Descriptor instead. +func (*GetLfgPlayerLockInfoResponse) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{26} +} + +func (x *GetLfgPlayerLockInfoResponse) GetApi() string { if x != nil { - return x.IsTradable + return x.Api } - return false + return "" } -func (x *GetPlayerItemsByGuidsResponse_Item) GetCount() uint32 { +func (x *GetLfgPlayerLockInfoResponse) GetStatus() GetLfgPlayerLockInfoResponse_Status { if x != nil { - return x.Count + return x.Status } - return 0 + return GetLfgPlayerLockInfoResponse_Success } -func (x *GetPlayerItemsByGuidsResponse_Item) GetFlags() uint32 { +func (x *GetLfgPlayerLockInfoResponse) GetLocks() []*LfgDungeonLock { if x != nil { - return x.Flags + return x.Locks + } + return nil +} + +func (x *GetLfgPlayerLockInfoResponse) GetJoinResult() uint32 { + if x != nil { + return x.JoinResult } return 0 } -func (x *GetPlayerItemsByGuidsResponse_Item) GetDurability() uint32 { +func (x *GetLfgPlayerLockInfoResponse) GetValidDungeonEntries() []uint32 { if x != nil { - return x.Durability + return x.ValidDungeonEntries + } + return nil +} + +type LfgRewardItem struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ItemID uint32 `protobuf:"varint,1,opt,name=itemID,proto3" json:"itemID,omitempty"` + DisplayID uint32 `protobuf:"varint,2,opt,name=displayID,proto3" json:"displayID,omitempty"` + Count uint32 `protobuf:"varint,3,opt,name=count,proto3" json:"count,omitempty"` +} + +func (x *LfgRewardItem) Reset() { + *x = LfgRewardItem{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LfgRewardItem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LfgRewardItem) ProtoMessage() {} + +func (x *LfgRewardItem) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LfgRewardItem.ProtoReflect.Descriptor instead. +func (*LfgRewardItem) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{27} +} + +func (x *LfgRewardItem) GetItemID() uint32 { + if x != nil { + return x.ItemID } return 0 } -func (x *GetPlayerItemsByGuidsResponse_Item) GetRandomPropertyID() uint32 { +func (x *LfgRewardItem) GetDisplayID() uint32 { if x != nil { - return x.RandomPropertyID + return x.DisplayID } return 0 } -func (x *GetPlayerItemsByGuidsResponse_Item) GetText() string { +func (x *LfgRewardItem) GetCount() uint32 { if x != nil { - return x.Text + return x.Count } - return "" + return 0 } -type AddExistingItemToPlayerRequest_Item struct { +type LfgRandomDungeonInfo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` - Entry uint32 `protobuf:"varint,2,opt,name=entry,proto3" json:"entry,omitempty"` - Count uint32 `protobuf:"varint,7,opt,name=count,proto3" json:"count,omitempty"` - Flags uint32 `protobuf:"varint,8,opt,name=flags,proto3" json:"flags,omitempty"` - Durability uint32 `protobuf:"varint,9,opt,name=durability,proto3" json:"durability,omitempty"` - RandomPropertyID uint32 `protobuf:"varint,10,opt,name=randomPropertyID,proto3" json:"randomPropertyID,omitempty"` - Text string `protobuf:"bytes,11,opt,name=text,proto3" json:"text,omitempty"` + DungeonEntry uint32 `protobuf:"varint,1,opt,name=dungeonEntry,proto3" json:"dungeonEntry,omitempty"` + Done bool `protobuf:"varint,2,opt,name=done,proto3" json:"done,omitempty"` + RewardMoney uint32 `protobuf:"varint,3,opt,name=rewardMoney,proto3" json:"rewardMoney,omitempty"` + RewardXP uint32 `protobuf:"varint,4,opt,name=rewardXP,proto3" json:"rewardXP,omitempty"` + RewardUnknown1 uint32 `protobuf:"varint,5,opt,name=rewardUnknown1,proto3" json:"rewardUnknown1,omitempty"` + RewardUnknown2 uint32 `protobuf:"varint,6,opt,name=rewardUnknown2,proto3" json:"rewardUnknown2,omitempty"` + RewardItems []*LfgRewardItem `protobuf:"bytes,7,rep,name=rewardItems,proto3" json:"rewardItems,omitempty"` } -func (x *AddExistingItemToPlayerRequest_Item) Reset() { - *x = AddExistingItemToPlayerRequest_Item{} +func (x *LfgRandomDungeonInfo) Reset() { + *x = LfgRandomDungeonInfo{} if protoimpl.UnsafeEnabled { - mi := &file_worldserver_proto_msgTypes[23] + mi := &file_worldserver_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *AddExistingItemToPlayerRequest_Item) String() string { +func (x *LfgRandomDungeonInfo) String() string { return protoimpl.X.MessageStringOf(x) } -func (*AddExistingItemToPlayerRequest_Item) ProtoMessage() {} +func (*LfgRandomDungeonInfo) ProtoMessage() {} -func (x *AddExistingItemToPlayerRequest_Item) ProtoReflect() protoreflect.Message { - mi := &file_worldserver_proto_msgTypes[23] +func (x *LfgRandomDungeonInfo) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1767,76 +2533,1285 @@ func (x *AddExistingItemToPlayerRequest_Item) ProtoReflect() protoreflect.Messag return mi.MessageOf(x) } -// Deprecated: Use AddExistingItemToPlayerRequest_Item.ProtoReflect.Descriptor instead. -func (*AddExistingItemToPlayerRequest_Item) Descriptor() ([]byte, []int) { - return file_worldserver_proto_rawDescGZIP(), []int{4, 0} +// Deprecated: Use LfgRandomDungeonInfo.ProtoReflect.Descriptor instead. +func (*LfgRandomDungeonInfo) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{28} } -func (x *AddExistingItemToPlayerRequest_Item) GetGuid() uint64 { +func (x *LfgRandomDungeonInfo) GetDungeonEntry() uint32 { if x != nil { - return x.Guid + return x.DungeonEntry } return 0 } -func (x *AddExistingItemToPlayerRequest_Item) GetEntry() uint32 { +func (x *LfgRandomDungeonInfo) GetDone() bool { if x != nil { - return x.Entry + return x.Done } - return 0 + return false } -func (x *AddExistingItemToPlayerRequest_Item) GetCount() uint32 { +func (x *LfgRandomDungeonInfo) GetRewardMoney() uint32 { if x != nil { - return x.Count + return x.RewardMoney } return 0 } -func (x *AddExistingItemToPlayerRequest_Item) GetFlags() uint32 { +func (x *LfgRandomDungeonInfo) GetRewardXP() uint32 { if x != nil { - return x.Flags + return x.RewardXP } return 0 } -func (x *AddExistingItemToPlayerRequest_Item) GetDurability() uint32 { +func (x *LfgRandomDungeonInfo) GetRewardUnknown1() uint32 { if x != nil { - return x.Durability + return x.RewardUnknown1 } return 0 } -func (x *AddExistingItemToPlayerRequest_Item) GetRandomPropertyID() uint32 { +func (x *LfgRandomDungeonInfo) GetRewardUnknown2() uint32 { if x != nil { - return x.RandomPropertyID + return x.RewardUnknown2 } return 0 } -func (x *AddExistingItemToPlayerRequest_Item) GetText() string { +func (x *LfgRandomDungeonInfo) GetRewardItems() []*LfgRewardItem { if x != nil { - return x.Text + return x.RewardItems + } + return nil +} + +type GetLfgPlayerInfoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + PlayerGUID uint64 `protobuf:"varint,2,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` +} + +func (x *GetLfgPlayerInfoRequest) Reset() { + *x = GetLfgPlayerInfoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetLfgPlayerInfoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLfgPlayerInfoRequest) ProtoMessage() {} + +func (x *GetLfgPlayerInfoRequest) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetLfgPlayerInfoRequest.ProtoReflect.Descriptor instead. +func (*GetLfgPlayerInfoRequest) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{29} +} + +func (x *GetLfgPlayerInfoRequest) GetApi() string { + if x != nil { + return x.Api } return "" } -var File_worldserver_proto protoreflect.FileDescriptor +func (x *GetLfgPlayerInfoRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} -var file_worldserver_proto_rawDesc = []byte{ - 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, 0x22, 0x66, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x50, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x42, 0x79, 0x47, 0x75, 0x69, 0x64, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x75, 0x69, - 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x52, 0x05, 0x67, 0x75, 0x69, 0x64, 0x73, 0x22, - 0x92, 0x03, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, - 0x6d, 0x73, 0x42, 0x79, 0x47, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x61, 0x70, 0x69, 0x12, 0x3c, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x79, 0x65, +type GetLfgPlayerInfoResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status GetLfgPlayerInfoResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.GetLfgPlayerInfoResponse_Status" json:"status,omitempty"` + RandomDungeons []*LfgRandomDungeonInfo `protobuf:"bytes,3,rep,name=randomDungeons,proto3" json:"randomDungeons,omitempty"` + Locks []*LfgDungeonLock `protobuf:"bytes,4,rep,name=locks,proto3" json:"locks,omitempty"` +} + +func (x *GetLfgPlayerInfoResponse) Reset() { + *x = GetLfgPlayerInfoResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetLfgPlayerInfoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLfgPlayerInfoResponse) ProtoMessage() {} + +func (x *GetLfgPlayerInfoResponse) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetLfgPlayerInfoResponse.ProtoReflect.Descriptor instead. +func (*GetLfgPlayerInfoResponse) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{30} +} + +func (x *GetLfgPlayerInfoResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetLfgPlayerInfoResponse) GetStatus() GetLfgPlayerInfoResponse_Status { + if x != nil { + return x.Status + } + return GetLfgPlayerInfoResponse_Success +} + +func (x *GetLfgPlayerInfoResponse) GetRandomDungeons() []*LfgRandomDungeonInfo { + if x != nil { + return x.RandomDungeons + } + return nil +} + +func (x *GetLfgPlayerInfoResponse) GetLocks() []*LfgDungeonLock { + if x != nil { + return x.Locks + } + return nil +} + +type GetLfgDungeonInfoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + DungeonEntry uint32 `protobuf:"varint,2,opt,name=dungeonEntry,proto3" json:"dungeonEntry,omitempty"` +} + +func (x *GetLfgDungeonInfoRequest) Reset() { + *x = GetLfgDungeonInfoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetLfgDungeonInfoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLfgDungeonInfoRequest) ProtoMessage() {} + +func (x *GetLfgDungeonInfoRequest) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetLfgDungeonInfoRequest.ProtoReflect.Descriptor instead. +func (*GetLfgDungeonInfoRequest) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{31} +} + +func (x *GetLfgDungeonInfoRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetLfgDungeonInfoRequest) GetDungeonEntry() uint32 { + if x != nil { + return x.DungeonEntry + } + return 0 +} + +type GetLfgDungeonInfoResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status GetLfgDungeonInfoResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.GetLfgDungeonInfoResponse_Status" json:"status,omitempty"` + DungeonEntry uint32 `protobuf:"varint,3,opt,name=dungeonEntry,proto3" json:"dungeonEntry,omitempty"` + DungeonID uint32 `protobuf:"varint,4,opt,name=dungeonID,proto3" json:"dungeonID,omitempty"` + MapID uint32 `protobuf:"varint,5,opt,name=mapID,proto3" json:"mapID,omitempty"` + TypeID uint32 `protobuf:"varint,6,opt,name=typeID,proto3" json:"typeID,omitempty"` + Difficulty uint32 `protobuf:"varint,7,opt,name=difficulty,proto3" json:"difficulty,omitempty"` +} + +func (x *GetLfgDungeonInfoResponse) Reset() { + *x = GetLfgDungeonInfoResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetLfgDungeonInfoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLfgDungeonInfoResponse) ProtoMessage() {} + +func (x *GetLfgDungeonInfoResponse) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetLfgDungeonInfoResponse.ProtoReflect.Descriptor instead. +func (*GetLfgDungeonInfoResponse) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{32} +} + +func (x *GetLfgDungeonInfoResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *GetLfgDungeonInfoResponse) GetStatus() GetLfgDungeonInfoResponse_Status { + if x != nil { + return x.Status + } + return GetLfgDungeonInfoResponse_Success +} + +func (x *GetLfgDungeonInfoResponse) GetDungeonEntry() uint32 { + if x != nil { + return x.DungeonEntry + } + return 0 +} + +func (x *GetLfgDungeonInfoResponse) GetDungeonID() uint32 { + if x != nil { + return x.DungeonID + } + return 0 +} + +func (x *GetLfgDungeonInfoResponse) GetMapID() uint32 { + if x != nil { + return x.MapID + } + return 0 +} + +func (x *GetLfgDungeonInfoResponse) GetTypeID() uint32 { + if x != nil { + return x.TypeID + } + return 0 +} + +func (x *GetLfgDungeonInfoResponse) GetDifficulty() uint32 { + if x != nil { + return x.Difficulty + } + return 0 +} + +type TeleportLfgPlayerRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + PlayerGUID uint64 `protobuf:"varint,2,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + Out bool `protobuf:"varint,3,opt,name=out,proto3" json:"out,omitempty"` + DungeonEntry uint32 `protobuf:"varint,4,opt,name=dungeonEntry,proto3" json:"dungeonEntry,omitempty"` +} + +func (x *TeleportLfgPlayerRequest) Reset() { + *x = TeleportLfgPlayerRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TeleportLfgPlayerRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TeleportLfgPlayerRequest) ProtoMessage() {} + +func (x *TeleportLfgPlayerRequest) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TeleportLfgPlayerRequest.ProtoReflect.Descriptor instead. +func (*TeleportLfgPlayerRequest) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{33} +} + +func (x *TeleportLfgPlayerRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *TeleportLfgPlayerRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *TeleportLfgPlayerRequest) GetOut() bool { + if x != nil { + return x.Out + } + return false +} + +func (x *TeleportLfgPlayerRequest) GetDungeonEntry() uint32 { + if x != nil { + return x.DungeonEntry + } + return 0 +} + +type TeleportLfgPlayerResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status TeleportLfgPlayerResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.TeleportLfgPlayerResponse_Status" json:"status,omitempty"` +} + +func (x *TeleportLfgPlayerResponse) Reset() { + *x = TeleportLfgPlayerResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TeleportLfgPlayerResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TeleportLfgPlayerResponse) ProtoMessage() {} + +func (x *TeleportLfgPlayerResponse) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TeleportLfgPlayerResponse.ProtoReflect.Descriptor instead. +func (*TeleportLfgPlayerResponse) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{34} +} + +func (x *TeleportLfgPlayerResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *TeleportLfgPlayerResponse) GetStatus() TeleportLfgPlayerResponse_Status { + if x != nil { + return x.Status + } + return TeleportLfgPlayerResponse_Success +} + +type SetLfgBootVoteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + PlayerGUID uint64 `protobuf:"varint,2,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + Agree bool `protobuf:"varint,3,opt,name=agree,proto3" json:"agree,omitempty"` +} + +func (x *SetLfgBootVoteRequest) Reset() { + *x = SetLfgBootVoteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetLfgBootVoteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetLfgBootVoteRequest) ProtoMessage() {} + +func (x *SetLfgBootVoteRequest) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[35] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetLfgBootVoteRequest.ProtoReflect.Descriptor instead. +func (*SetLfgBootVoteRequest) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{35} +} + +func (x *SetLfgBootVoteRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SetLfgBootVoteRequest) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *SetLfgBootVoteRequest) GetAgree() bool { + if x != nil { + return x.Agree + } + return false +} + +type SetLfgBootVoteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status SetLfgBootVoteResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.SetLfgBootVoteResponse_Status" json:"status,omitempty"` +} + +func (x *SetLfgBootVoteResponse) Reset() { + *x = SetLfgBootVoteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetLfgBootVoteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetLfgBootVoteResponse) ProtoMessage() {} + +func (x *SetLfgBootVoteResponse) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[36] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetLfgBootVoteResponse.ProtoReflect.Descriptor instead. +func (*SetLfgBootVoteResponse) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{36} +} + +func (x *SetLfgBootVoteResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *SetLfgBootVoteResponse) GetStatus() SetLfgBootVoteResponse_Status { + if x != nil { + return x.Status + } + return SetLfgBootVoteResponse_Success +} + +// MaterializeLfgProposal +type MaterializeLfgProposalRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + RealmID uint32 `protobuf:"varint,2,opt,name=realmID,proto3" json:"realmID,omitempty"` + ProposalID uint32 `protobuf:"varint,3,opt,name=proposalID,proto3" json:"proposalID,omitempty"` + DungeonEntry uint32 `protobuf:"varint,4,opt,name=dungeonEntry,proto3" json:"dungeonEntry,omitempty"` + LeaderGUID uint64 `protobuf:"varint,5,opt,name=leaderGUID,proto3" json:"leaderGUID,omitempty"` + Members []*MaterializeLfgProposalRequest_Member `protobuf:"bytes,6,rep,name=members,proto3" json:"members,omitempty"` +} + +func (x *MaterializeLfgProposalRequest) Reset() { + *x = MaterializeLfgProposalRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MaterializeLfgProposalRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MaterializeLfgProposalRequest) ProtoMessage() {} + +func (x *MaterializeLfgProposalRequest) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MaterializeLfgProposalRequest.ProtoReflect.Descriptor instead. +func (*MaterializeLfgProposalRequest) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{37} +} + +func (x *MaterializeLfgProposalRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *MaterializeLfgProposalRequest) GetRealmID() uint32 { + if x != nil { + return x.RealmID + } + return 0 +} + +func (x *MaterializeLfgProposalRequest) GetProposalID() uint32 { + if x != nil { + return x.ProposalID + } + return 0 +} + +func (x *MaterializeLfgProposalRequest) GetDungeonEntry() uint32 { + if x != nil { + return x.DungeonEntry + } + return 0 +} + +func (x *MaterializeLfgProposalRequest) GetLeaderGUID() uint64 { + if x != nil { + return x.LeaderGUID + } + return 0 +} + +func (x *MaterializeLfgProposalRequest) GetMembers() []*MaterializeLfgProposalRequest_Member { + if x != nil { + return x.Members + } + return nil +} + +type MaterializeLfgProposalResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status MaterializeLfgProposalResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.MaterializeLfgProposalResponse_Status" json:"status,omitempty"` +} + +func (x *MaterializeLfgProposalResponse) Reset() { + *x = MaterializeLfgProposalResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MaterializeLfgProposalResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MaterializeLfgProposalResponse) ProtoMessage() {} + +func (x *MaterializeLfgProposalResponse) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MaterializeLfgProposalResponse.ProtoReflect.Descriptor instead. +func (*MaterializeLfgProposalResponse) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{38} +} + +func (x *MaterializeLfgProposalResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *MaterializeLfgProposalResponse) GetStatus() MaterializeLfgProposalResponse_Status { + if x != nil { + return x.Status + } + return MaterializeLfgProposalResponse_Success +} + +type CreateGuildRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + LeaderGuid uint64 `protobuf:"varint,2,opt,name=leaderGuid,proto3" json:"leaderGuid,omitempty"` + GuildName string `protobuf:"bytes,3,opt,name=guildName,proto3" json:"guildName,omitempty"` +} + +func (x *CreateGuildRequest) Reset() { + *x = CreateGuildRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateGuildRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateGuildRequest) ProtoMessage() {} + +func (x *CreateGuildRequest) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[39] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateGuildRequest.ProtoReflect.Descriptor instead. +func (*CreateGuildRequest) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{39} +} + +func (x *CreateGuildRequest) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *CreateGuildRequest) GetLeaderGuid() uint64 { + if x != nil { + return x.LeaderGuid + } + return 0 +} + +func (x *CreateGuildRequest) GetGuildName() string { + if x != nil { + return x.GuildName + } + return "" +} + +type CreateGuildResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"` + Status CreateGuildResponse_Status `protobuf:"varint,2,opt,name=status,proto3,enum=v1.CreateGuildResponse_Status" json:"status,omitempty"` + GuildId uint64 `protobuf:"varint,3,opt,name=guildId,proto3" json:"guildId,omitempty"` +} + +func (x *CreateGuildResponse) Reset() { + *x = CreateGuildResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateGuildResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateGuildResponse) ProtoMessage() {} + +func (x *CreateGuildResponse) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[40] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateGuildResponse.ProtoReflect.Descriptor instead. +func (*CreateGuildResponse) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{40} +} + +func (x *CreateGuildResponse) GetApi() string { + if x != nil { + return x.Api + } + return "" +} + +func (x *CreateGuildResponse) GetStatus() CreateGuildResponse_Status { + if x != nil { + return x.Status + } + return CreateGuildResponse_Success +} + +func (x *CreateGuildResponse) GetGuildId() uint64 { + if x != nil { + return x.GuildId + } + return 0 +} + +type GetPlayerItemsByGuidsResponse_Item struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` + Entry uint32 `protobuf:"varint,2,opt,name=entry,proto3" json:"entry,omitempty"` + Owner uint64 `protobuf:"varint,3,opt,name=owner,proto3" json:"owner,omitempty"` + BagSlot uint32 `protobuf:"varint,4,opt,name=bagSlot,proto3" json:"bagSlot,omitempty"` + Slot uint32 `protobuf:"varint,5,opt,name=slot,proto3" json:"slot,omitempty"` + IsTradable bool `protobuf:"varint,6,opt,name=isTradable,proto3" json:"isTradable,omitempty"` + Count uint32 `protobuf:"varint,7,opt,name=count,proto3" json:"count,omitempty"` + Flags uint32 `protobuf:"varint,8,opt,name=flags,proto3" json:"flags,omitempty"` + Durability uint32 `protobuf:"varint,9,opt,name=durability,proto3" json:"durability,omitempty"` + RandomPropertyID int32 `protobuf:"varint,10,opt,name=randomPropertyID,proto3" json:"randomPropertyID,omitempty"` + Text string `protobuf:"bytes,11,opt,name=text,proto3" json:"text,omitempty"` +} + +func (x *GetPlayerItemsByGuidsResponse_Item) Reset() { + *x = GetPlayerItemsByGuidsResponse_Item{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetPlayerItemsByGuidsResponse_Item) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPlayerItemsByGuidsResponse_Item) ProtoMessage() {} + +func (x *GetPlayerItemsByGuidsResponse_Item) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[41] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetPlayerItemsByGuidsResponse_Item.ProtoReflect.Descriptor instead. +func (*GetPlayerItemsByGuidsResponse_Item) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{1, 0} +} + +func (x *GetPlayerItemsByGuidsResponse_Item) GetGuid() uint64 { + if x != nil { + return x.Guid + } + return 0 +} + +func (x *GetPlayerItemsByGuidsResponse_Item) GetEntry() uint32 { + if x != nil { + return x.Entry + } + return 0 +} + +func (x *GetPlayerItemsByGuidsResponse_Item) GetOwner() uint64 { + if x != nil { + return x.Owner + } + return 0 +} + +func (x *GetPlayerItemsByGuidsResponse_Item) GetBagSlot() uint32 { + if x != nil { + return x.BagSlot + } + return 0 +} + +func (x *GetPlayerItemsByGuidsResponse_Item) GetSlot() uint32 { + if x != nil { + return x.Slot + } + return 0 +} + +func (x *GetPlayerItemsByGuidsResponse_Item) GetIsTradable() bool { + if x != nil { + return x.IsTradable + } + return false +} + +func (x *GetPlayerItemsByGuidsResponse_Item) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *GetPlayerItemsByGuidsResponse_Item) GetFlags() uint32 { + if x != nil { + return x.Flags + } + return 0 +} + +func (x *GetPlayerItemsByGuidsResponse_Item) GetDurability() uint32 { + if x != nil { + return x.Durability + } + return 0 +} + +func (x *GetPlayerItemsByGuidsResponse_Item) GetRandomPropertyID() int32 { + if x != nil { + return x.RandomPropertyID + } + return 0 +} + +func (x *GetPlayerItemsByGuidsResponse_Item) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +type TakePlayerItemByPosResponse_Item struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` + Entry uint32 `protobuf:"varint,2,opt,name=entry,proto3" json:"entry,omitempty"` + Owner uint64 `protobuf:"varint,3,opt,name=owner,proto3" json:"owner,omitempty"` + BagSlot uint32 `protobuf:"varint,4,opt,name=bagSlot,proto3" json:"bagSlot,omitempty"` + Slot uint32 `protobuf:"varint,5,opt,name=slot,proto3" json:"slot,omitempty"` + IsTradable bool `protobuf:"varint,6,opt,name=isTradable,proto3" json:"isTradable,omitempty"` + Count uint32 `protobuf:"varint,7,opt,name=count,proto3" json:"count,omitempty"` + Flags uint32 `protobuf:"varint,8,opt,name=flags,proto3" json:"flags,omitempty"` + Durability uint32 `protobuf:"varint,9,opt,name=durability,proto3" json:"durability,omitempty"` + RandomPropertyID int32 `protobuf:"varint,10,opt,name=randomPropertyID,proto3" json:"randomPropertyID,omitempty"` + Text string `protobuf:"bytes,11,opt,name=text,proto3" json:"text,omitempty"` +} + +func (x *TakePlayerItemByPosResponse_Item) Reset() { + *x = TakePlayerItemByPosResponse_Item{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TakePlayerItemByPosResponse_Item) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TakePlayerItemByPosResponse_Item) ProtoMessage() {} + +func (x *TakePlayerItemByPosResponse_Item) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[42] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TakePlayerItemByPosResponse_Item.ProtoReflect.Descriptor instead. +func (*TakePlayerItemByPosResponse_Item) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{3, 0} +} + +func (x *TakePlayerItemByPosResponse_Item) GetGuid() uint64 { + if x != nil { + return x.Guid + } + return 0 +} + +func (x *TakePlayerItemByPosResponse_Item) GetEntry() uint32 { + if x != nil { + return x.Entry + } + return 0 +} + +func (x *TakePlayerItemByPosResponse_Item) GetOwner() uint64 { + if x != nil { + return x.Owner + } + return 0 +} + +func (x *TakePlayerItemByPosResponse_Item) GetBagSlot() uint32 { + if x != nil { + return x.BagSlot + } + return 0 +} + +func (x *TakePlayerItemByPosResponse_Item) GetSlot() uint32 { + if x != nil { + return x.Slot + } + return 0 +} + +func (x *TakePlayerItemByPosResponse_Item) GetIsTradable() bool { + if x != nil { + return x.IsTradable + } + return false +} + +func (x *TakePlayerItemByPosResponse_Item) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *TakePlayerItemByPosResponse_Item) GetFlags() uint32 { + if x != nil { + return x.Flags + } + return 0 +} + +func (x *TakePlayerItemByPosResponse_Item) GetDurability() uint32 { + if x != nil { + return x.Durability + } + return 0 +} + +func (x *TakePlayerItemByPosResponse_Item) GetRandomPropertyID() int32 { + if x != nil { + return x.RandomPropertyID + } + return 0 +} + +func (x *TakePlayerItemByPosResponse_Item) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +type AddExistingItemToPlayerRequest_Item struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Guid uint64 `protobuf:"varint,1,opt,name=guid,proto3" json:"guid,omitempty"` + Entry uint32 `protobuf:"varint,2,opt,name=entry,proto3" json:"entry,omitempty"` + Count uint32 `protobuf:"varint,7,opt,name=count,proto3" json:"count,omitempty"` + Flags uint32 `protobuf:"varint,8,opt,name=flags,proto3" json:"flags,omitempty"` + Durability uint32 `protobuf:"varint,9,opt,name=durability,proto3" json:"durability,omitempty"` + RandomPropertyID int32 `protobuf:"varint,10,opt,name=randomPropertyID,proto3" json:"randomPropertyID,omitempty"` + Text string `protobuf:"bytes,11,opt,name=text,proto3" json:"text,omitempty"` +} + +func (x *AddExistingItemToPlayerRequest_Item) Reset() { + *x = AddExistingItemToPlayerRequest_Item{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddExistingItemToPlayerRequest_Item) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddExistingItemToPlayerRequest_Item) ProtoMessage() {} + +func (x *AddExistingItemToPlayerRequest_Item) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[43] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddExistingItemToPlayerRequest_Item.ProtoReflect.Descriptor instead. +func (*AddExistingItemToPlayerRequest_Item) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{6, 0} +} + +func (x *AddExistingItemToPlayerRequest_Item) GetGuid() uint64 { + if x != nil { + return x.Guid + } + return 0 +} + +func (x *AddExistingItemToPlayerRequest_Item) GetEntry() uint32 { + if x != nil { + return x.Entry + } + return 0 +} + +func (x *AddExistingItemToPlayerRequest_Item) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *AddExistingItemToPlayerRequest_Item) GetFlags() uint32 { + if x != nil { + return x.Flags + } + return 0 +} + +func (x *AddExistingItemToPlayerRequest_Item) GetDurability() uint32 { + if x != nil { + return x.Durability + } + return 0 +} + +func (x *AddExistingItemToPlayerRequest_Item) GetRandomPropertyID() int32 { + if x != nil { + return x.RandomPropertyID + } + return 0 +} + +func (x *AddExistingItemToPlayerRequest_Item) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +type MaterializeLfgProposalRequest_Member struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PlayerGUID uint64 `protobuf:"varint,1,opt,name=playerGUID,proto3" json:"playerGUID,omitempty"` + SelectedRoles uint32 `protobuf:"varint,2,opt,name=selectedRoles,proto3" json:"selectedRoles,omitempty"` + AssignedRole uint32 `protobuf:"varint,3,opt,name=assignedRole,proto3" json:"assignedRole,omitempty"` + QueueLeaderGUID uint64 `protobuf:"varint,4,opt,name=queueLeaderGUID,proto3" json:"queueLeaderGUID,omitempty"` +} + +func (x *MaterializeLfgProposalRequest_Member) Reset() { + *x = MaterializeLfgProposalRequest_Member{} + if protoimpl.UnsafeEnabled { + mi := &file_worldserver_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MaterializeLfgProposalRequest_Member) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MaterializeLfgProposalRequest_Member) ProtoMessage() {} + +func (x *MaterializeLfgProposalRequest_Member) ProtoReflect() protoreflect.Message { + mi := &file_worldserver_proto_msgTypes[44] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MaterializeLfgProposalRequest_Member.ProtoReflect.Descriptor instead. +func (*MaterializeLfgProposalRequest_Member) Descriptor() ([]byte, []int) { + return file_worldserver_proto_rawDescGZIP(), []int{37, 0} +} + +func (x *MaterializeLfgProposalRequest_Member) GetPlayerGUID() uint64 { + if x != nil { + return x.PlayerGUID + } + return 0 +} + +func (x *MaterializeLfgProposalRequest_Member) GetSelectedRoles() uint32 { + if x != nil { + return x.SelectedRoles + } + return 0 +} + +func (x *MaterializeLfgProposalRequest_Member) GetAssignedRole() uint32 { + if x != nil { + return x.AssignedRole + } + return 0 +} + +func (x *MaterializeLfgProposalRequest_Member) GetQueueLeaderGUID() uint64 { + if x != nil { + return x.QueueLeaderGUID + } + return 0 +} + +var File_worldserver_proto protoreflect.FileDescriptor + +var file_worldserver_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, 0x22, 0x66, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x42, 0x79, 0x47, 0x75, 0x69, 0x64, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x75, 0x69, + 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x52, 0x05, 0x67, 0x75, 0x69, 0x64, 0x73, 0x22, + 0x92, 0x03, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, + 0x6d, 0x73, 0x42, 0x79, 0x47, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x3c, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x42, 0x79, 0x47, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x1a, 0xa0, 0x02, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x75, @@ -1854,36 +3829,88 @@ var file_worldserver_proto_rawDesc = []byte{ 0x6c, 0x61, 0x67, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, 0x72, - 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x49, 0x44, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, + 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x49, 0x44, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x74, 0x65, 0x78, 0x74, 0x22, 0x9f, 0x01, 0x0a, 0x25, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, - 0x74, 0x65, 0x6d, 0x73, 0x57, 0x69, 0x74, 0x68, 0x47, 0x75, 0x69, 0x64, 0x73, 0x46, 0x72, 0x6f, - 0x6d, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, - 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, - 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, - 0x12, 0x14, 0x0a, 0x05, 0x67, 0x75, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x52, - 0x05, 0x67, 0x75, 0x69, 0x64, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, - 0x54, 0x6f, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x12, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x54, 0x6f, 0x50, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x22, 0x68, 0x0a, 0x26, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x49, 0x74, 0x65, 0x6d, 0x73, 0x57, 0x69, 0x74, 0x68, 0x47, 0x75, 0x69, 0x64, 0x73, 0x46, 0x72, - 0x6f, 0x6d, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, - 0x70, 0x69, 0x12, 0x2c, 0x0a, 0x11, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, - 0x6d, 0x73, 0x47, 0x75, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x11, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x47, 0x75, 0x69, 0x64, 0x73, - 0x22, 0xce, 0x02, 0x0a, 0x1e, 0x41, 0x64, 0x64, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, - 0x49, 0x74, 0x65, 0x6d, 0x54, 0x6f, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x74, 0x65, 0x78, 0x74, 0x22, 0xc2, 0x01, 0x0a, 0x1a, 0x54, 0x61, 0x6b, 0x65, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x79, 0x50, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x47, 0x75, 0x69, 0x64, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x78, 0x69, 0x73, - 0x74, 0x69, 0x6e, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x54, 0x6f, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, - 0x65, 0x6d, 0x1a, 0xbc, 0x01, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x67, + 0x72, 0x47, 0x75, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x67, 0x53, 0x6c, 0x6f, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x62, 0x61, 0x67, 0x53, 0x6c, 0x6f, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, + 0x6c, 0x6f, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x73, 0x73, + 0x69, 0x67, 0x6e, 0x54, 0x6f, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x54, 0x6f, 0x50, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x22, 0xaa, 0x04, 0x0a, 0x1b, 0x54, 0x61, + 0x6b, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x79, 0x50, 0x6f, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3e, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x61, 0x6b, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, 0x6d, 0x42, + 0x79, 0x50, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x38, 0x0a, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x61, 0x6b, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x79, 0x50, + 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x1a, 0xa0, 0x02, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, + 0x0a, 0x04, 0x67, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, 0x75, + 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x18, + 0x0a, 0x07, 0x62, 0x61, 0x67, 0x53, 0x6c, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x62, 0x61, 0x67, 0x53, 0x6c, 0x6f, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x1e, 0x0a, 0x0a, + 0x69, 0x73, 0x54, 0x72, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0a, 0x69, 0x73, 0x54, 0x72, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x75, 0x72, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x75, + 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x61, 0x6e, 0x64, + 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x49, 0x44, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x10, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x79, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x5c, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, 0x00, 0x12, + 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, + 0x64, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x74, 0x65, 0x6d, 0x4e, 0x6f, 0x74, 0x46, 0x6f, + 0x75, 0x6e, 0x64, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x74, 0x65, 0x6d, 0x4e, 0x6f, 0x74, + 0x54, 0x72, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x10, 0x04, 0x22, 0x9f, 0x01, 0x0a, 0x25, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x57, 0x69, 0x74, 0x68, 0x47, 0x75, 0x69, 0x64, 0x73, 0x46, + 0x72, 0x6f, 0x6d, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x75, + 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x75, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x04, 0x52, 0x05, 0x67, 0x75, 0x69, 0x64, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x73, 0x73, 0x69, + 0x67, 0x6e, 0x54, 0x6f, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x54, 0x6f, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x22, 0x68, 0x0a, 0x26, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x57, 0x69, 0x74, 0x68, 0x47, 0x75, 0x69, 0x64, 0x73, + 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x2c, 0x0a, 0x11, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x49, + 0x74, 0x65, 0x6d, 0x73, 0x47, 0x75, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, + 0x11, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x47, 0x75, 0x69, + 0x64, 0x73, 0x22, 0x9c, 0x03, 0x0a, 0x1e, 0x41, 0x64, 0x64, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, + 0x6e, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x54, 0x6f, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x47, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x78, + 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x54, 0x6f, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x41, 0x74, 0x50, + 0x6f, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x41, + 0x74, 0x50, 0x6f, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x67, 0x53, 0x6c, 0x6f, 0x74, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x62, 0x61, 0x67, 0x53, 0x6c, 0x6f, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x6c, + 0x6f, 0x74, 0x1a, 0xbc, 0x01, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x67, 0x75, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, @@ -1892,7 +3919,7 @@ var file_worldserver_proto_rawDesc = []byte{ 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x70, 0x65, - 0x72, 0x74, 0x79, 0x49, 0x44, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x72, 0x61, 0x6e, + 0x72, 0x74, 0x79, 0x49, 0x44, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x9b, 0x01, 0x0a, 0x1f, 0x41, 0x64, 0x64, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, @@ -1957,7 +3984,7 @@ var file_worldserver_proto_rawDesc = []byte{ 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x61, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x63, 0x61, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, - 0x74, 0x22, 0xc2, 0x02, 0x0a, 0x18, 0x53, 0x74, 0x61, 0x72, 0x74, 0x42, 0x61, 0x74, 0x74, 0x6c, + 0x74, 0x22, 0xa6, 0x04, 0x0a, 0x18, 0x53, 0x74, 0x61, 0x72, 0x74, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x44, 0x0a, 0x12, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, @@ -1977,158 +4004,413 @@ var file_worldserver_proto_rawDesc = []byte{ 0x41, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x41, 0x64, 0x64, 0x48, 0x6f, 0x72, 0x64, 0x65, 0x18, 0x08, 0x20, 0x03, 0x28, 0x04, 0x52, 0x11, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x41, 0x64, - 0x64, 0x48, 0x6f, 0x72, 0x64, 0x65, 0x22, 0x79, 0x0a, 0x19, 0x53, 0x74, 0x61, 0x72, 0x74, 0x42, - 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x10, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, - 0x44, 0x22, 0xfb, 0x01, 0x0a, 0x1f, 0x41, 0x64, 0x64, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, - 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x44, 0x0a, 0x12, 0x62, 0x61, 0x74, 0x74, 0x6c, - 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x49, 0x44, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, - 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x12, 0x62, 0x61, 0x74, 0x74, 0x6c, - 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x49, 0x44, 0x12, 0x1e, 0x0a, - 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x32, 0x0a, + 0x64, 0x48, 0x6f, 0x72, 0x64, 0x65, 0x12, 0x30, 0x0a, 0x13, 0x61, 0x6c, 0x6c, 0x69, 0x61, 0x6e, + 0x63, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x13, 0x61, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x72, 0x65, + 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x2a, 0x0a, 0x10, 0x68, 0x6f, 0x72, 0x64, + 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x10, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x54, 0x65, + 0x61, 0x6d, 0x49, 0x44, 0x12, 0x44, 0x0a, 0x1d, 0x61, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, + 0x41, 0x72, 0x65, 0x6e, 0x61, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, 0x65, 0x72, 0x52, + 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x1d, 0x61, 0x6c, 0x6c, + 0x69, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x6d, + 0x61, 0x6b, 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x3e, 0x0a, 0x1a, 0x68, 0x6f, + 0x72, 0x64, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x6d, 0x61, 0x6b, + 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x1a, + 0x68, 0x6f, 0x72, 0x64, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x6d, + 0x61, 0x6b, 0x65, 0x72, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x79, 0x0a, 0x19, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x10, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x49, 0x44, 0x22, 0xfb, 0x01, 0x0a, 0x1f, 0x41, 0x64, 0x64, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x44, 0x0a, 0x12, 0x62, + 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x74, + 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x12, 0x62, + 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x49, + 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, + 0x44, 0x12, 0x32, 0x0a, 0x14, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x41, 0x64, + 0x64, 0x41, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x52, 0x14, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x6c, - 0x69, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x52, 0x14, 0x70, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, - 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x41, 0x64, - 0x64, 0x48, 0x6f, 0x72, 0x64, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x04, 0x52, 0x11, 0x70, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x41, 0x64, 0x64, 0x48, 0x6f, 0x72, 0x64, 0x65, 0x22, - 0x34, 0x0a, 0x20, 0x41, 0x64, 0x64, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x42, - 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x59, 0x0a, 0x25, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, + 0x69, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, + 0x54, 0x6f, 0x41, 0x64, 0x64, 0x48, 0x6f, 0x72, 0x64, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x04, + 0x52, 0x11, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x41, 0x64, 0x64, 0x48, 0x6f, + 0x72, 0x64, 0x65, 0x22, 0x34, 0x0a, 0x20, 0x41, 0x64, 0x64, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x73, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x59, 0x0a, 0x25, 0x43, 0x61, 0x6e, + 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x6f, 0x69, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, + 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x70, 0x69, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x22, 0x9c, 0x01, 0x0a, 0x26, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x6f, 0x69, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, - 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, - 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, - 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, - 0x22, 0x9c, 0x01, 0x0a, 0x26, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x6f, - 0x69, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, - 0x65, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x49, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x31, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x6f, 0x69, 0x6e, - 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x15, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, 0x00, 0x22, - 0x5a, 0x0a, 0x26, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x65, 0x6c, 0x65, + 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, + 0x69, 0x12, 0x49, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x31, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x4a, 0x6f, 0x69, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x15, 0x0a, 0x06, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x10, 0x00, 0x22, 0x5a, 0x0a, 0x26, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, + 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, + 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, + 0x9e, 0x01, 0x0a, 0x27, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, + 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x4a, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, - 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, + 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x15, 0x0a, 0x06, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, 0x00, + 0x22, 0x54, 0x0a, 0x0e, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x4c, 0x6f, + 0x63, 0x6b, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, + 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x6f, 0x63, 0x6b, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x77, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, + 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x26, 0x0a, 0x0e, 0x64, 0x75, 0x6e, 0x67, 0x65, + 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, + 0x0e, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, + 0xab, 0x02, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x4c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x4c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x28, 0x0a, 0x05, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, + 0x6f, 0x6e, 0x4c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x1e, 0x0a, + 0x0a, 0x6a, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0a, 0x6a, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x30, 0x0a, + 0x13, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, + 0x72, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x13, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, + 0x3c, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x02, 0x22, 0x5b, 0x0a, + 0x0d, 0x4c, 0x66, 0x67, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x16, + 0x0a, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, + 0x69, 0x74, 0x65, 0x6d, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, + 0x79, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, + 0x61, 0x79, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x91, 0x02, 0x0a, 0x14, 0x4c, + 0x66, 0x67, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, + 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x6f, 0x6e, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x64, 0x6f, 0x6e, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x72, + 0x65, 0x77, 0x61, 0x72, 0x64, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0b, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x12, 0x1a, 0x0a, + 0x08, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x58, 0x50, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x08, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x58, 0x50, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x77, + 0x61, 0x72, 0x64, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x31, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0e, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, + 0x31, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, + 0x77, 0x6e, 0x32, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x72, 0x65, 0x77, 0x61, 0x72, + 0x64, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x32, 0x12, 0x33, 0x0a, 0x0b, 0x72, 0x65, 0x77, + 0x61, 0x72, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x66, 0x67, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x49, 0x74, 0x65, + 0x6d, 0x52, 0x0b, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x4b, + 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0x9e, 0x01, 0x0a, 0x27, - 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x4a, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x15, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x0b, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, 0x00, 0x2a, 0x9a, 0x02, 0x0a, - 0x10, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, - 0x6c, 0x74, 0x65, 0x72, 0x61, 0x63, 0x56, 0x61, 0x6c, 0x6c, 0x65, 0x79, 0x10, 0x01, 0x12, 0x10, - 0x0a, 0x0c, 0x57, 0x61, 0x72, 0x73, 0x6f, 0x6e, 0x67, 0x47, 0x75, 0x6c, 0x63, 0x68, 0x10, 0x02, - 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x72, 0x61, 0x74, 0x68, 0x69, 0x42, 0x61, 0x73, 0x69, 0x6e, 0x10, - 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x61, 0x67, 0x72, 0x61, 0x6e, 0x64, 0x41, 0x72, 0x65, 0x6e, - 0x61, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x42, 0x6c, 0x61, 0x64, 0x65, 0x73, 0x45, 0x64, 0x67, - 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x6c, 0x6c, 0x41, - 0x72, 0x65, 0x6e, 0x61, 0x73, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x79, 0x65, 0x4f, 0x66, - 0x54, 0x68, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x6d, 0x10, 0x07, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x75, - 0x69, 0x6e, 0x73, 0x4f, 0x66, 0x4c, 0x6f, 0x72, 0x64, 0x61, 0x65, 0x72, 0x6f, 0x6e, 0x10, 0x08, - 0x12, 0x17, 0x0a, 0x13, 0x53, 0x74, 0x72, 0x61, 0x6e, 0x64, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x41, - 0x6e, 0x63, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x10, 0x09, 0x12, 0x11, 0x0a, 0x0d, 0x44, 0x61, 0x6c, - 0x61, 0x72, 0x61, 0x6e, 0x53, 0x65, 0x77, 0x65, 0x72, 0x73, 0x10, 0x0a, 0x12, 0x0f, 0x0a, 0x0b, - 0x52, 0x69, 0x6e, 0x67, 0x4f, 0x66, 0x56, 0x61, 0x6c, 0x6f, 0x72, 0x10, 0x0b, 0x12, 0x12, 0x0a, - 0x0e, 0x49, 0x73, 0x6c, 0x65, 0x4f, 0x66, 0x43, 0x6f, 0x6e, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, - 0x1e, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x42, 0x61, 0x74, 0x74, 0x6c, - 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x20, 0x32, 0x8d, 0x09, 0x0a, 0x12, 0x57, 0x6f, - 0x72, 0x6c, 0x64, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x5c, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, - 0x6d, 0x73, 0x42, 0x79, 0x47, 0x75, 0x69, 0x64, 0x73, 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x42, 0x79, 0x47, - 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x42, - 0x79, 0x47, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, - 0x0a, 0x1e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x57, 0x69, 0x74, - 0x68, 0x47, 0x75, 0x69, 0x64, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x12, 0x29, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, - 0x73, 0x57, 0x69, 0x74, 0x68, 0x47, 0x75, 0x69, 0x64, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x57, 0x69, 0x74, 0x68, - 0x47, 0x75, 0x69, 0x64, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x17, 0x41, 0x64, 0x64, 0x45, 0x78, + 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0xa2, 0x02, 0x0a, 0x18, + 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x3b, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x40, 0x0a, 0x0e, 0x72, 0x61, 0x6e, 0x64, 0x6f, + 0x6d, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x66, 0x67, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x44, 0x75, + 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x72, 0x61, 0x6e, 0x64, 0x6f, + 0x6d, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x0a, 0x05, 0x6c, 0x6f, 0x63, + 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x66, + 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x4c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x6c, 0x6f, + 0x63, 0x6b, 0x73, 0x22, 0x4b, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, + 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, + 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x02, 0x12, 0x11, 0x0a, + 0x0d, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x03, + 0x22, 0x50, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, + 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x22, + 0x0a, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x22, 0xc9, 0x02, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, + 0x67, 0x65, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x44, 0x75, + 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x49, + 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, + 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x05, 0x6d, 0x61, 0x70, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x79, 0x70, 0x65, + 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x74, 0x79, 0x70, 0x65, 0x49, 0x44, + 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, + 0x22, 0x4c, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x75, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x48, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x72, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, + 0x6e, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x03, 0x22, 0x82, + 0x01, 0x0a, 0x18, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x66, 0x67, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, + 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1e, 0x0a, + 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x10, 0x0a, + 0x03, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x6f, 0x75, 0x74, 0x12, + 0x22, 0x0a, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x22, 0xb8, 0x01, 0x0a, 0x19, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x4c, 0x66, 0x67, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x61, 0x70, 0x69, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x4c, 0x66, 0x67, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x22, 0x4b, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x53, + 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x48, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x03, 0x22, 0x5f, + 0x0a, 0x15, 0x53, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x42, 0x6f, 0x6f, 0x74, 0x56, 0x6f, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x67, 0x72, + 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x67, 0x72, 0x65, 0x65, 0x22, + 0xb2, 0x01, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x42, 0x6f, 0x6f, 0x74, 0x56, 0x6f, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, + 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x39, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x42, 0x6f, 0x6f, 0x74, 0x56, 0x6f, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x4b, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, 0x00, 0x12, 0x0d, + 0x0a, 0x09, 0x4e, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x10, 0x01, 0x12, 0x12, 0x0a, + 0x0e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, + 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x10, 0x03, 0x22, 0x92, 0x03, 0x0a, 0x1d, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x4c, 0x66, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, + 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x6d, + 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x49, 0x44, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, + 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x64, 0x75, 0x6e, 0x67, 0x65, 0x6f, + 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x47, 0x55, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6c, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x42, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x74, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x4c, 0x66, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, + 0x73, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x1a, 0x9c, 0x01, 0x0a, 0x06, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x47, + 0x55, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x47, 0x55, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x73, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x61, + 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0c, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x12, + 0x28, 0x0a, 0x0f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x47, 0x55, + 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x4c, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x47, 0x55, 0x49, 0x44, 0x22, 0xd6, 0x01, 0x0a, 0x1e, 0x4d, 0x61, + 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x4c, 0x66, 0x67, 0x50, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x41, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, + 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x4c, + 0x66, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x22, 0x5f, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x53, + 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x6f, 0x48, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x44, 0x75, 0x6e, 0x67, 0x65, + 0x6f, 0x6e, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, + 0x4e, 0x6f, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x10, 0x03, 0x12, + 0x11, 0x0a, 0x0d, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x10, 0x04, 0x22, 0x64, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x75, 0x69, 0x6c, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, + 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x75, + 0x69, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, + 0x75, 0x69, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xd8, 0x01, 0x0a, 0x13, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x70, 0x69, 0x12, 0x36, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x75, + 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, + 0x69, 0x6c, 0x64, 0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, + 0x6c, 0x64, 0x49, 0x64, 0x22, 0x5d, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, + 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, + 0x61, 0x6d, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x49, + 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, + 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x03, + 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x10, 0x04, 0x2a, 0x9a, 0x02, 0x0a, 0x10, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, + 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x61, 0x63, 0x56, 0x61, 0x6c, + 0x6c, 0x65, 0x79, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x57, 0x61, 0x72, 0x73, 0x6f, 0x6e, 0x67, + 0x47, 0x75, 0x6c, 0x63, 0x68, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x72, 0x61, 0x74, 0x68, + 0x69, 0x42, 0x61, 0x73, 0x69, 0x6e, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x61, 0x67, 0x72, + 0x61, 0x6e, 0x64, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x42, 0x6c, + 0x61, 0x64, 0x65, 0x73, 0x45, 0x64, 0x67, 0x65, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x10, 0x05, 0x12, + 0x0d, 0x0a, 0x09, 0x41, 0x6c, 0x6c, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x73, 0x10, 0x06, 0x12, 0x11, + 0x0a, 0x0d, 0x45, 0x79, 0x65, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x6d, 0x10, + 0x07, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x75, 0x69, 0x6e, 0x73, 0x4f, 0x66, 0x4c, 0x6f, 0x72, 0x64, + 0x61, 0x65, 0x72, 0x6f, 0x6e, 0x10, 0x08, 0x12, 0x17, 0x0a, 0x13, 0x53, 0x74, 0x72, 0x61, 0x6e, + 0x64, 0x4f, 0x66, 0x54, 0x68, 0x65, 0x41, 0x6e, 0x63, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x10, 0x09, + 0x12, 0x11, 0x0a, 0x0d, 0x44, 0x61, 0x6c, 0x61, 0x72, 0x61, 0x6e, 0x53, 0x65, 0x77, 0x65, 0x72, + 0x73, 0x10, 0x0a, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x69, 0x6e, 0x67, 0x4f, 0x66, 0x56, 0x61, 0x6c, + 0x6f, 0x72, 0x10, 0x0b, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x73, 0x6c, 0x65, 0x4f, 0x66, 0x43, 0x6f, + 0x6e, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x1e, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x61, 0x6e, 0x64, + 0x6f, 0x6d, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x20, + 0x32, 0x9d, 0x0e, 0x0a, 0x12, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5c, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x42, 0x79, 0x47, 0x75, 0x69, 0x64, 0x73, + 0x12, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, + 0x74, 0x65, 0x6d, 0x73, 0x42, 0x79, 0x47, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x42, 0x79, 0x47, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x13, 0x54, 0x61, 0x6b, 0x65, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x79, 0x50, 0x6f, 0x73, 0x12, 0x1e, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x61, 0x6b, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, 0x6d, + 0x42, 0x79, 0x50, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x61, 0x6b, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x74, 0x65, 0x6d, + 0x42, 0x79, 0x50, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, + 0x1e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x57, 0x69, 0x74, 0x68, + 0x47, 0x75, 0x69, 0x64, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, + 0x29, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, + 0x57, 0x69, 0x74, 0x68, 0x47, 0x75, 0x69, 0x64, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x57, 0x69, 0x74, 0x68, 0x47, + 0x75, 0x69, 0x64, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x17, 0x41, 0x64, 0x64, 0x45, 0x78, 0x69, + 0x73, 0x74, 0x69, 0x6e, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x54, 0x6f, 0x50, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x12, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, + 0x6e, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x54, 0x6f, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x54, 0x6f, 0x50, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x12, 0x22, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x78, 0x69, 0x73, 0x74, - 0x69, 0x6e, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x54, 0x6f, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x45, - 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x54, 0x6f, 0x50, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x11, 0x47, - 0x65, 0x74, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x46, 0x6f, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x12, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x46, 0x6f, - 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x46, 0x6f, 0x72, 0x50, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, - 0x14, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x46, 0x6f, 0x72, 0x50, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x11, 0x47, 0x65, + 0x74, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x46, 0x6f, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, + 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x46, 0x6f, 0x72, + 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x46, 0x6f, 0x72, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x14, + 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x46, 0x6f, 0x72, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, + 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x46, 0x6f, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x46, 0x6f, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x69, - 0x66, 0x79, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x46, 0x6f, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x18, 0x43, 0x61, 0x6e, 0x50, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x57, 0x69, 0x74, - 0x68, 0x4e, 0x50, 0x43, 0x12, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4e, - 0x50, 0x43, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, - 0x57, 0x69, 0x74, 0x68, 0x4e, 0x50, 0x43, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x7a, 0x0a, 0x1f, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x6e, 0x74, 0x65, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x18, 0x43, 0x61, 0x6e, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x57, 0x69, 0x74, 0x68, + 0x4e, 0x50, 0x43, 0x12, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x50, + 0x43, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, + 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x57, + 0x69, 0x74, 0x68, 0x4e, 0x50, 0x43, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7a, + 0x0a, 0x1f, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x61, 0x63, 0x74, 0x57, 0x69, 0x74, 0x68, 0x47, 0x61, 0x6d, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x12, 0x2a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x57, 0x69, 0x74, 0x68, 0x47, 0x61, 0x6d, 0x65, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x57, 0x69, 0x74, 0x68, 0x47, 0x61, 0x6d, 0x65, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x12, 0x2a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x57, 0x69, 0x74, 0x68, 0x47, 0x61, 0x6d, - 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x61, 0x63, 0x74, 0x57, 0x69, 0x74, 0x68, 0x47, 0x61, 0x6d, 0x65, 0x4f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x11, 0x53, - 0x74, 0x61, 0x72, 0x74, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, - 0x12, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x42, 0x61, 0x74, 0x74, 0x6c, - 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, - 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, - 0x18, 0x41, 0x64, 0x64, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x42, 0x61, 0x74, - 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x41, - 0x64, 0x64, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, - 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, - 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, - 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x1e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x4a, 0x6f, 0x69, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, - 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x29, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x6f, 0x69, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, - 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x11, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, + 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, + 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x18, + 0x41, 0x64, 0x64, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, + 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, + 0x64, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, + 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, + 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x42, + 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x1e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x6f, 0x69, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, - 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7a, 0x0a, - 0x1f, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x29, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x4a, 0x6f, 0x69, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, + 0x6f, 0x69, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x51, + 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7a, 0x0a, 0x1f, + 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, + 0x2a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, - 0x12, 0x2a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x54, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, - 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x54, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x54, 0x6f, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x6e, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x14, 0x5a, 0x12, 0x67, 0x65, 0x6e, - 0x2f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x62, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4c, + 0x66, 0x67, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x1f, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x4c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x4c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x4c, 0x66, 0x67, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, + 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x50, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, + 0x65, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x4c, 0x66, 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x66, + 0x67, 0x44, 0x75, 0x6e, 0x67, 0x65, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x11, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x4c, 0x66, 0x67, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x66, 0x67, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x66, 0x67, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x4c, 0x66, 0x67, + 0x42, 0x6f, 0x6f, 0x74, 0x56, 0x6f, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, + 0x74, 0x4c, 0x66, 0x67, 0x42, 0x6f, 0x6f, 0x74, 0x56, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x66, 0x67, 0x42, + 0x6f, 0x6f, 0x74, 0x56, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x5f, 0x0a, 0x16, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x4c, 0x66, + 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x12, 0x21, 0x2e, 0x76, 0x31, 0x2e, 0x4d, + 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x4c, 0x66, 0x67, 0x50, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, + 0x31, 0x2e, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x4c, 0x66, 0x67, + 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3e, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x12, + 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x75, 0x69, 0x6c, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x42, 0x14, 0x5a, 0x12, 0x67, 0x65, 0x6e, 0x2f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2143,73 +4425,132 @@ func file_worldserver_proto_rawDescGZIP() []byte { return file_worldserver_proto_rawDescData } -var file_worldserver_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_worldserver_proto_msgTypes = make([]protoimpl.MessageInfo, 24) +var file_worldserver_proto_enumTypes = make([]protoimpl.EnumInfo, 12) +var file_worldserver_proto_msgTypes = make([]protoimpl.MessageInfo, 45) var file_worldserver_proto_goTypes = []interface{}{ (BattlegroundType)(0), // 0: v1.BattlegroundType - (AddExistingItemToPlayerResponse_Status)(0), // 1: v1.AddExistingItemToPlayerResponse.Status - (CanPlayerJoinBattlegroundQueueResponse_Status)(0), // 2: v1.CanPlayerJoinBattlegroundQueueResponse.Status - (CanPlayerTeleportToBattlegroundResponse_Status)(0), // 3: v1.CanPlayerTeleportToBattlegroundResponse.Status - (*GetPlayerItemsByGuidsRequest)(nil), // 4: v1.GetPlayerItemsByGuidsRequest - (*GetPlayerItemsByGuidsResponse)(nil), // 5: v1.GetPlayerItemsByGuidsResponse - (*RemoveItemsWithGuidsFromPlayerRequest)(nil), // 6: v1.RemoveItemsWithGuidsFromPlayerRequest - (*RemoveItemsWithGuidsFromPlayerResponse)(nil), // 7: v1.RemoveItemsWithGuidsFromPlayerResponse - (*AddExistingItemToPlayerRequest)(nil), // 8: v1.AddExistingItemToPlayerRequest - (*AddExistingItemToPlayerResponse)(nil), // 9: v1.AddExistingItemToPlayerResponse - (*GetMoneyForPlayerRequest)(nil), // 10: v1.GetMoneyForPlayerRequest - (*GetMoneyForPlayerResponse)(nil), // 11: v1.GetMoneyForPlayerResponse - (*ModifyMoneyForPlayerRequest)(nil), // 12: v1.ModifyMoneyForPlayerRequest - (*ModifyMoneyForPlayerResponse)(nil), // 13: v1.ModifyMoneyForPlayerResponse - (*CanPlayerInteractWithNPCRequest)(nil), // 14: v1.CanPlayerInteractWithNPCRequest - (*CanPlayerInteractWithNPCResponse)(nil), // 15: v1.CanPlayerInteractWithNPCResponse - (*CanPlayerInteractWithGameObjectRequest)(nil), // 16: v1.CanPlayerInteractWithGameObjectRequest - (*CanPlayerInteractWithGameObjectResponse)(nil), // 17: v1.CanPlayerInteractWithGameObjectResponse - (*StartBattlegroundRequest)(nil), // 18: v1.StartBattlegroundRequest - (*StartBattlegroundResponse)(nil), // 19: v1.StartBattlegroundResponse - (*AddPlayersToBattlegroundRequest)(nil), // 20: v1.AddPlayersToBattlegroundRequest - (*AddPlayersToBattlegroundResponse)(nil), // 21: v1.AddPlayersToBattlegroundResponse - (*CanPlayerJoinBattlegroundQueueRequest)(nil), // 22: v1.CanPlayerJoinBattlegroundQueueRequest - (*CanPlayerJoinBattlegroundQueueResponse)(nil), // 23: v1.CanPlayerJoinBattlegroundQueueResponse - (*CanPlayerTeleportToBattlegroundRequest)(nil), // 24: v1.CanPlayerTeleportToBattlegroundRequest - (*CanPlayerTeleportToBattlegroundResponse)(nil), // 25: v1.CanPlayerTeleportToBattlegroundResponse - (*GetPlayerItemsByGuidsResponse_Item)(nil), // 26: v1.GetPlayerItemsByGuidsResponse.Item - (*AddExistingItemToPlayerRequest_Item)(nil), // 27: v1.AddExistingItemToPlayerRequest.Item + (TakePlayerItemByPosResponse_Status)(0), // 1: v1.TakePlayerItemByPosResponse.Status + (AddExistingItemToPlayerResponse_Status)(0), // 2: v1.AddExistingItemToPlayerResponse.Status + (CanPlayerJoinBattlegroundQueueResponse_Status)(0), // 3: v1.CanPlayerJoinBattlegroundQueueResponse.Status + (CanPlayerTeleportToBattlegroundResponse_Status)(0), // 4: v1.CanPlayerTeleportToBattlegroundResponse.Status + (GetLfgPlayerLockInfoResponse_Status)(0), // 5: v1.GetLfgPlayerLockInfoResponse.Status + (GetLfgPlayerInfoResponse_Status)(0), // 6: v1.GetLfgPlayerInfoResponse.Status + (GetLfgDungeonInfoResponse_Status)(0), // 7: v1.GetLfgDungeonInfoResponse.Status + (TeleportLfgPlayerResponse_Status)(0), // 8: v1.TeleportLfgPlayerResponse.Status + (SetLfgBootVoteResponse_Status)(0), // 9: v1.SetLfgBootVoteResponse.Status + (MaterializeLfgProposalResponse_Status)(0), // 10: v1.MaterializeLfgProposalResponse.Status + (CreateGuildResponse_Status)(0), // 11: v1.CreateGuildResponse.Status + (*GetPlayerItemsByGuidsRequest)(nil), // 12: v1.GetPlayerItemsByGuidsRequest + (*GetPlayerItemsByGuidsResponse)(nil), // 13: v1.GetPlayerItemsByGuidsResponse + (*TakePlayerItemByPosRequest)(nil), // 14: v1.TakePlayerItemByPosRequest + (*TakePlayerItemByPosResponse)(nil), // 15: v1.TakePlayerItemByPosResponse + (*RemoveItemsWithGuidsFromPlayerRequest)(nil), // 16: v1.RemoveItemsWithGuidsFromPlayerRequest + (*RemoveItemsWithGuidsFromPlayerResponse)(nil), // 17: v1.RemoveItemsWithGuidsFromPlayerResponse + (*AddExistingItemToPlayerRequest)(nil), // 18: v1.AddExistingItemToPlayerRequest + (*AddExistingItemToPlayerResponse)(nil), // 19: v1.AddExistingItemToPlayerResponse + (*GetMoneyForPlayerRequest)(nil), // 20: v1.GetMoneyForPlayerRequest + (*GetMoneyForPlayerResponse)(nil), // 21: v1.GetMoneyForPlayerResponse + (*ModifyMoneyForPlayerRequest)(nil), // 22: v1.ModifyMoneyForPlayerRequest + (*ModifyMoneyForPlayerResponse)(nil), // 23: v1.ModifyMoneyForPlayerResponse + (*CanPlayerInteractWithNPCRequest)(nil), // 24: v1.CanPlayerInteractWithNPCRequest + (*CanPlayerInteractWithNPCResponse)(nil), // 25: v1.CanPlayerInteractWithNPCResponse + (*CanPlayerInteractWithGameObjectRequest)(nil), // 26: v1.CanPlayerInteractWithGameObjectRequest + (*CanPlayerInteractWithGameObjectResponse)(nil), // 27: v1.CanPlayerInteractWithGameObjectResponse + (*StartBattlegroundRequest)(nil), // 28: v1.StartBattlegroundRequest + (*StartBattlegroundResponse)(nil), // 29: v1.StartBattlegroundResponse + (*AddPlayersToBattlegroundRequest)(nil), // 30: v1.AddPlayersToBattlegroundRequest + (*AddPlayersToBattlegroundResponse)(nil), // 31: v1.AddPlayersToBattlegroundResponse + (*CanPlayerJoinBattlegroundQueueRequest)(nil), // 32: v1.CanPlayerJoinBattlegroundQueueRequest + (*CanPlayerJoinBattlegroundQueueResponse)(nil), // 33: v1.CanPlayerJoinBattlegroundQueueResponse + (*CanPlayerTeleportToBattlegroundRequest)(nil), // 34: v1.CanPlayerTeleportToBattlegroundRequest + (*CanPlayerTeleportToBattlegroundResponse)(nil), // 35: v1.CanPlayerTeleportToBattlegroundResponse + (*LfgDungeonLock)(nil), // 36: v1.LfgDungeonLock + (*GetLfgPlayerLockInfoRequest)(nil), // 37: v1.GetLfgPlayerLockInfoRequest + (*GetLfgPlayerLockInfoResponse)(nil), // 38: v1.GetLfgPlayerLockInfoResponse + (*LfgRewardItem)(nil), // 39: v1.LfgRewardItem + (*LfgRandomDungeonInfo)(nil), // 40: v1.LfgRandomDungeonInfo + (*GetLfgPlayerInfoRequest)(nil), // 41: v1.GetLfgPlayerInfoRequest + (*GetLfgPlayerInfoResponse)(nil), // 42: v1.GetLfgPlayerInfoResponse + (*GetLfgDungeonInfoRequest)(nil), // 43: v1.GetLfgDungeonInfoRequest + (*GetLfgDungeonInfoResponse)(nil), // 44: v1.GetLfgDungeonInfoResponse + (*TeleportLfgPlayerRequest)(nil), // 45: v1.TeleportLfgPlayerRequest + (*TeleportLfgPlayerResponse)(nil), // 46: v1.TeleportLfgPlayerResponse + (*SetLfgBootVoteRequest)(nil), // 47: v1.SetLfgBootVoteRequest + (*SetLfgBootVoteResponse)(nil), // 48: v1.SetLfgBootVoteResponse + (*MaterializeLfgProposalRequest)(nil), // 49: v1.MaterializeLfgProposalRequest + (*MaterializeLfgProposalResponse)(nil), // 50: v1.MaterializeLfgProposalResponse + (*CreateGuildRequest)(nil), // 51: v1.CreateGuildRequest + (*CreateGuildResponse)(nil), // 52: v1.CreateGuildResponse + (*GetPlayerItemsByGuidsResponse_Item)(nil), // 53: v1.GetPlayerItemsByGuidsResponse.Item + (*TakePlayerItemByPosResponse_Item)(nil), // 54: v1.TakePlayerItemByPosResponse.Item + (*AddExistingItemToPlayerRequest_Item)(nil), // 55: v1.AddExistingItemToPlayerRequest.Item + (*MaterializeLfgProposalRequest_Member)(nil), // 56: v1.MaterializeLfgProposalRequest.Member } var file_worldserver_proto_depIdxs = []int32{ - 26, // 0: v1.GetPlayerItemsByGuidsResponse.items:type_name -> v1.GetPlayerItemsByGuidsResponse.Item - 27, // 1: v1.AddExistingItemToPlayerRequest.item:type_name -> v1.AddExistingItemToPlayerRequest.Item - 1, // 2: v1.AddExistingItemToPlayerResponse.status:type_name -> v1.AddExistingItemToPlayerResponse.Status - 0, // 3: v1.StartBattlegroundRequest.battlegroundTypeID:type_name -> v1.BattlegroundType - 0, // 4: v1.AddPlayersToBattlegroundRequest.battlegroundTypeID:type_name -> v1.BattlegroundType - 2, // 5: v1.CanPlayerJoinBattlegroundQueueResponse.status:type_name -> v1.CanPlayerJoinBattlegroundQueueResponse.Status - 3, // 6: v1.CanPlayerTeleportToBattlegroundResponse.status:type_name -> v1.CanPlayerTeleportToBattlegroundResponse.Status - 4, // 7: v1.WorldServerService.GetPlayerItemsByGuids:input_type -> v1.GetPlayerItemsByGuidsRequest - 6, // 8: v1.WorldServerService.RemoveItemsWithGuidsFromPlayer:input_type -> v1.RemoveItemsWithGuidsFromPlayerRequest - 8, // 9: v1.WorldServerService.AddExistingItemToPlayer:input_type -> v1.AddExistingItemToPlayerRequest - 10, // 10: v1.WorldServerService.GetMoneyForPlayer:input_type -> v1.GetMoneyForPlayerRequest - 12, // 11: v1.WorldServerService.ModifyMoneyForPlayer:input_type -> v1.ModifyMoneyForPlayerRequest - 14, // 12: v1.WorldServerService.CanPlayerInteractWithNPC:input_type -> v1.CanPlayerInteractWithNPCRequest - 16, // 13: v1.WorldServerService.CanPlayerInteractWithGameObject:input_type -> v1.CanPlayerInteractWithGameObjectRequest - 18, // 14: v1.WorldServerService.StartBattleground:input_type -> v1.StartBattlegroundRequest - 20, // 15: v1.WorldServerService.AddPlayersToBattleground:input_type -> v1.AddPlayersToBattlegroundRequest - 22, // 16: v1.WorldServerService.CanPlayerJoinBattlegroundQueue:input_type -> v1.CanPlayerJoinBattlegroundQueueRequest - 24, // 17: v1.WorldServerService.CanPlayerTeleportToBattleground:input_type -> v1.CanPlayerTeleportToBattlegroundRequest - 5, // 18: v1.WorldServerService.GetPlayerItemsByGuids:output_type -> v1.GetPlayerItemsByGuidsResponse - 7, // 19: v1.WorldServerService.RemoveItemsWithGuidsFromPlayer:output_type -> v1.RemoveItemsWithGuidsFromPlayerResponse - 9, // 20: v1.WorldServerService.AddExistingItemToPlayer:output_type -> v1.AddExistingItemToPlayerResponse - 11, // 21: v1.WorldServerService.GetMoneyForPlayer:output_type -> v1.GetMoneyForPlayerResponse - 13, // 22: v1.WorldServerService.ModifyMoneyForPlayer:output_type -> v1.ModifyMoneyForPlayerResponse - 15, // 23: v1.WorldServerService.CanPlayerInteractWithNPC:output_type -> v1.CanPlayerInteractWithNPCResponse - 17, // 24: v1.WorldServerService.CanPlayerInteractWithGameObject:output_type -> v1.CanPlayerInteractWithGameObjectResponse - 19, // 25: v1.WorldServerService.StartBattleground:output_type -> v1.StartBattlegroundResponse - 21, // 26: v1.WorldServerService.AddPlayersToBattleground:output_type -> v1.AddPlayersToBattlegroundResponse - 23, // 27: v1.WorldServerService.CanPlayerJoinBattlegroundQueue:output_type -> v1.CanPlayerJoinBattlegroundQueueResponse - 25, // 28: v1.WorldServerService.CanPlayerTeleportToBattleground:output_type -> v1.CanPlayerTeleportToBattlegroundResponse - 18, // [18:29] is the sub-list for method output_type - 7, // [7:18] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 53, // 0: v1.GetPlayerItemsByGuidsResponse.items:type_name -> v1.GetPlayerItemsByGuidsResponse.Item + 1, // 1: v1.TakePlayerItemByPosResponse.status:type_name -> v1.TakePlayerItemByPosResponse.Status + 54, // 2: v1.TakePlayerItemByPosResponse.item:type_name -> v1.TakePlayerItemByPosResponse.Item + 55, // 3: v1.AddExistingItemToPlayerRequest.item:type_name -> v1.AddExistingItemToPlayerRequest.Item + 2, // 4: v1.AddExistingItemToPlayerResponse.status:type_name -> v1.AddExistingItemToPlayerResponse.Status + 0, // 5: v1.StartBattlegroundRequest.battlegroundTypeID:type_name -> v1.BattlegroundType + 0, // 6: v1.AddPlayersToBattlegroundRequest.battlegroundTypeID:type_name -> v1.BattlegroundType + 3, // 7: v1.CanPlayerJoinBattlegroundQueueResponse.status:type_name -> v1.CanPlayerJoinBattlegroundQueueResponse.Status + 4, // 8: v1.CanPlayerTeleportToBattlegroundResponse.status:type_name -> v1.CanPlayerTeleportToBattlegroundResponse.Status + 5, // 9: v1.GetLfgPlayerLockInfoResponse.status:type_name -> v1.GetLfgPlayerLockInfoResponse.Status + 36, // 10: v1.GetLfgPlayerLockInfoResponse.locks:type_name -> v1.LfgDungeonLock + 39, // 11: v1.LfgRandomDungeonInfo.rewardItems:type_name -> v1.LfgRewardItem + 6, // 12: v1.GetLfgPlayerInfoResponse.status:type_name -> v1.GetLfgPlayerInfoResponse.Status + 40, // 13: v1.GetLfgPlayerInfoResponse.randomDungeons:type_name -> v1.LfgRandomDungeonInfo + 36, // 14: v1.GetLfgPlayerInfoResponse.locks:type_name -> v1.LfgDungeonLock + 7, // 15: v1.GetLfgDungeonInfoResponse.status:type_name -> v1.GetLfgDungeonInfoResponse.Status + 8, // 16: v1.TeleportLfgPlayerResponse.status:type_name -> v1.TeleportLfgPlayerResponse.Status + 9, // 17: v1.SetLfgBootVoteResponse.status:type_name -> v1.SetLfgBootVoteResponse.Status + 56, // 18: v1.MaterializeLfgProposalRequest.members:type_name -> v1.MaterializeLfgProposalRequest.Member + 10, // 19: v1.MaterializeLfgProposalResponse.status:type_name -> v1.MaterializeLfgProposalResponse.Status + 11, // 20: v1.CreateGuildResponse.status:type_name -> v1.CreateGuildResponse.Status + 12, // 21: v1.WorldServerService.GetPlayerItemsByGuids:input_type -> v1.GetPlayerItemsByGuidsRequest + 14, // 22: v1.WorldServerService.TakePlayerItemByPos:input_type -> v1.TakePlayerItemByPosRequest + 16, // 23: v1.WorldServerService.RemoveItemsWithGuidsFromPlayer:input_type -> v1.RemoveItemsWithGuidsFromPlayerRequest + 18, // 24: v1.WorldServerService.AddExistingItemToPlayer:input_type -> v1.AddExistingItemToPlayerRequest + 20, // 25: v1.WorldServerService.GetMoneyForPlayer:input_type -> v1.GetMoneyForPlayerRequest + 22, // 26: v1.WorldServerService.ModifyMoneyForPlayer:input_type -> v1.ModifyMoneyForPlayerRequest + 24, // 27: v1.WorldServerService.CanPlayerInteractWithNPC:input_type -> v1.CanPlayerInteractWithNPCRequest + 26, // 28: v1.WorldServerService.CanPlayerInteractWithGameObject:input_type -> v1.CanPlayerInteractWithGameObjectRequest + 28, // 29: v1.WorldServerService.StartBattleground:input_type -> v1.StartBattlegroundRequest + 30, // 30: v1.WorldServerService.AddPlayersToBattleground:input_type -> v1.AddPlayersToBattlegroundRequest + 32, // 31: v1.WorldServerService.CanPlayerJoinBattlegroundQueue:input_type -> v1.CanPlayerJoinBattlegroundQueueRequest + 34, // 32: v1.WorldServerService.CanPlayerTeleportToBattleground:input_type -> v1.CanPlayerTeleportToBattlegroundRequest + 37, // 33: v1.WorldServerService.GetLfgPlayerLockInfo:input_type -> v1.GetLfgPlayerLockInfoRequest + 41, // 34: v1.WorldServerService.GetLfgPlayerInfo:input_type -> v1.GetLfgPlayerInfoRequest + 43, // 35: v1.WorldServerService.GetLfgDungeonInfo:input_type -> v1.GetLfgDungeonInfoRequest + 45, // 36: v1.WorldServerService.TeleportLfgPlayer:input_type -> v1.TeleportLfgPlayerRequest + 47, // 37: v1.WorldServerService.SetLfgBootVote:input_type -> v1.SetLfgBootVoteRequest + 49, // 38: v1.WorldServerService.MaterializeLfgProposal:input_type -> v1.MaterializeLfgProposalRequest + 51, // 39: v1.WorldServerService.CreateGuild:input_type -> v1.CreateGuildRequest + 13, // 40: v1.WorldServerService.GetPlayerItemsByGuids:output_type -> v1.GetPlayerItemsByGuidsResponse + 15, // 41: v1.WorldServerService.TakePlayerItemByPos:output_type -> v1.TakePlayerItemByPosResponse + 17, // 42: v1.WorldServerService.RemoveItemsWithGuidsFromPlayer:output_type -> v1.RemoveItemsWithGuidsFromPlayerResponse + 19, // 43: v1.WorldServerService.AddExistingItemToPlayer:output_type -> v1.AddExistingItemToPlayerResponse + 21, // 44: v1.WorldServerService.GetMoneyForPlayer:output_type -> v1.GetMoneyForPlayerResponse + 23, // 45: v1.WorldServerService.ModifyMoneyForPlayer:output_type -> v1.ModifyMoneyForPlayerResponse + 25, // 46: v1.WorldServerService.CanPlayerInteractWithNPC:output_type -> v1.CanPlayerInteractWithNPCResponse + 27, // 47: v1.WorldServerService.CanPlayerInteractWithGameObject:output_type -> v1.CanPlayerInteractWithGameObjectResponse + 29, // 48: v1.WorldServerService.StartBattleground:output_type -> v1.StartBattlegroundResponse + 31, // 49: v1.WorldServerService.AddPlayersToBattleground:output_type -> v1.AddPlayersToBattlegroundResponse + 33, // 50: v1.WorldServerService.CanPlayerJoinBattlegroundQueue:output_type -> v1.CanPlayerJoinBattlegroundQueueResponse + 35, // 51: v1.WorldServerService.CanPlayerTeleportToBattleground:output_type -> v1.CanPlayerTeleportToBattlegroundResponse + 38, // 52: v1.WorldServerService.GetLfgPlayerLockInfo:output_type -> v1.GetLfgPlayerLockInfoResponse + 42, // 53: v1.WorldServerService.GetLfgPlayerInfo:output_type -> v1.GetLfgPlayerInfoResponse + 44, // 54: v1.WorldServerService.GetLfgDungeonInfo:output_type -> v1.GetLfgDungeonInfoResponse + 46, // 55: v1.WorldServerService.TeleportLfgPlayer:output_type -> v1.TeleportLfgPlayerResponse + 48, // 56: v1.WorldServerService.SetLfgBootVote:output_type -> v1.SetLfgBootVoteResponse + 50, // 57: v1.WorldServerService.MaterializeLfgProposal:output_type -> v1.MaterializeLfgProposalResponse + 52, // 58: v1.WorldServerService.CreateGuild:output_type -> v1.CreateGuildResponse + 40, // [40:59] is the sub-list for method output_type + 21, // [21:40] is the sub-list for method input_type + 21, // [21:21] is the sub-list for extension type_name + 21, // [21:21] is the sub-list for extension extendee + 0, // [0:21] is the sub-list for field type_name } func init() { file_worldserver_proto_init() } @@ -2243,7 +4584,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveItemsWithGuidsFromPlayerRequest); i { + switch v := v.(*TakePlayerItemByPosRequest); i { case 0: return &v.state case 1: @@ -2255,7 +4596,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveItemsWithGuidsFromPlayerResponse); i { + switch v := v.(*TakePlayerItemByPosResponse); i { case 0: return &v.state case 1: @@ -2267,7 +4608,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddExistingItemToPlayerRequest); i { + switch v := v.(*RemoveItemsWithGuidsFromPlayerRequest); i { case 0: return &v.state case 1: @@ -2279,7 +4620,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddExistingItemToPlayerResponse); i { + switch v := v.(*RemoveItemsWithGuidsFromPlayerResponse); i { case 0: return &v.state case 1: @@ -2291,7 +4632,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMoneyForPlayerRequest); i { + switch v := v.(*AddExistingItemToPlayerRequest); i { case 0: return &v.state case 1: @@ -2303,7 +4644,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMoneyForPlayerResponse); i { + switch v := v.(*AddExistingItemToPlayerResponse); i { case 0: return &v.state case 1: @@ -2315,7 +4656,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ModifyMoneyForPlayerRequest); i { + switch v := v.(*GetMoneyForPlayerRequest); i { case 0: return &v.state case 1: @@ -2327,7 +4668,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ModifyMoneyForPlayerResponse); i { + switch v := v.(*GetMoneyForPlayerResponse); i { case 0: return &v.state case 1: @@ -2339,7 +4680,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CanPlayerInteractWithNPCRequest); i { + switch v := v.(*ModifyMoneyForPlayerRequest); i { case 0: return &v.state case 1: @@ -2351,7 +4692,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CanPlayerInteractWithNPCResponse); i { + switch v := v.(*ModifyMoneyForPlayerResponse); i { case 0: return &v.state case 1: @@ -2363,7 +4704,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CanPlayerInteractWithGameObjectRequest); i { + switch v := v.(*CanPlayerInteractWithNPCRequest); i { case 0: return &v.state case 1: @@ -2375,7 +4716,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CanPlayerInteractWithGameObjectResponse); i { + switch v := v.(*CanPlayerInteractWithNPCResponse); i { case 0: return &v.state case 1: @@ -2387,7 +4728,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StartBattlegroundRequest); i { + switch v := v.(*CanPlayerInteractWithGameObjectRequest); i { case 0: return &v.state case 1: @@ -2399,7 +4740,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StartBattlegroundResponse); i { + switch v := v.(*CanPlayerInteractWithGameObjectResponse); i { case 0: return &v.state case 1: @@ -2411,7 +4752,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddPlayersToBattlegroundRequest); i { + switch v := v.(*StartBattlegroundRequest); i { case 0: return &v.state case 1: @@ -2423,7 +4764,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddPlayersToBattlegroundResponse); i { + switch v := v.(*StartBattlegroundResponse); i { case 0: return &v.state case 1: @@ -2435,7 +4776,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CanPlayerJoinBattlegroundQueueRequest); i { + switch v := v.(*AddPlayersToBattlegroundRequest); i { case 0: return &v.state case 1: @@ -2447,7 +4788,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CanPlayerJoinBattlegroundQueueResponse); i { + switch v := v.(*AddPlayersToBattlegroundResponse); i { case 0: return &v.state case 1: @@ -2459,7 +4800,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CanPlayerTeleportToBattlegroundRequest); i { + switch v := v.(*CanPlayerJoinBattlegroundQueueRequest); i { case 0: return &v.state case 1: @@ -2471,7 +4812,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CanPlayerTeleportToBattlegroundResponse); i { + switch v := v.(*CanPlayerJoinBattlegroundQueueResponse); i { case 0: return &v.state case 1: @@ -2483,7 +4824,7 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPlayerItemsByGuidsResponse_Item); i { + switch v := v.(*CanPlayerTeleportToBattlegroundRequest); i { case 0: return &v.state case 1: @@ -2495,6 +4836,246 @@ func file_worldserver_proto_init() { } } file_worldserver_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CanPlayerTeleportToBattlegroundResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LfgDungeonLock); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLfgPlayerLockInfoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLfgPlayerLockInfoResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LfgRewardItem); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LfgRandomDungeonInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLfgPlayerInfoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLfgPlayerInfoResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLfgDungeonInfoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLfgDungeonInfoResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TeleportLfgPlayerRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TeleportLfgPlayerResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetLfgBootVoteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetLfgBootVoteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MaterializeLfgProposalRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MaterializeLfgProposalResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateGuildRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateGuildResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetPlayerItemsByGuidsResponse_Item); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TakePlayerItemByPosResponse_Item); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_worldserver_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AddExistingItemToPlayerRequest_Item); i { case 0: return &v.state @@ -2506,14 +5087,26 @@ func file_worldserver_proto_init() { return nil } } + file_worldserver_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MaterializeLfgProposalRequest_Member); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_worldserver_proto_rawDesc, - NumEnums: 4, - NumMessages: 24, + NumEnums: 12, + NumMessages: 45, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/worldserver/pb/worldserver_grpc.pb.go b/gen/worldserver/pb/worldserver_grpc.pb.go index 769043a..cf54e4e 100644 --- a/gen/worldserver/pb/worldserver_grpc.pb.go +++ b/gen/worldserver/pb/worldserver_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v3.20.3 +// - protoc v3.21.12 // source: worldserver.proto package pb @@ -20,6 +20,7 @@ const _ = grpc.SupportPackageIsVersion7 const ( WorldServerService_GetPlayerItemsByGuids_FullMethodName = "/v1.WorldServerService/GetPlayerItemsByGuids" + WorldServerService_TakePlayerItemByPos_FullMethodName = "/v1.WorldServerService/TakePlayerItemByPos" WorldServerService_RemoveItemsWithGuidsFromPlayer_FullMethodName = "/v1.WorldServerService/RemoveItemsWithGuidsFromPlayer" WorldServerService_AddExistingItemToPlayer_FullMethodName = "/v1.WorldServerService/AddExistingItemToPlayer" WorldServerService_GetMoneyForPlayer_FullMethodName = "/v1.WorldServerService/GetMoneyForPlayer" @@ -30,6 +31,13 @@ const ( WorldServerService_AddPlayersToBattleground_FullMethodName = "/v1.WorldServerService/AddPlayersToBattleground" WorldServerService_CanPlayerJoinBattlegroundQueue_FullMethodName = "/v1.WorldServerService/CanPlayerJoinBattlegroundQueue" WorldServerService_CanPlayerTeleportToBattleground_FullMethodName = "/v1.WorldServerService/CanPlayerTeleportToBattleground" + WorldServerService_GetLfgPlayerLockInfo_FullMethodName = "/v1.WorldServerService/GetLfgPlayerLockInfo" + WorldServerService_GetLfgPlayerInfo_FullMethodName = "/v1.WorldServerService/GetLfgPlayerInfo" + WorldServerService_GetLfgDungeonInfo_FullMethodName = "/v1.WorldServerService/GetLfgDungeonInfo" + WorldServerService_TeleportLfgPlayer_FullMethodName = "/v1.WorldServerService/TeleportLfgPlayer" + WorldServerService_SetLfgBootVote_FullMethodName = "/v1.WorldServerService/SetLfgBootVote" + WorldServerService_MaterializeLfgProposal_FullMethodName = "/v1.WorldServerService/MaterializeLfgProposal" + WorldServerService_CreateGuild_FullMethodName = "/v1.WorldServerService/CreateGuild" ) // WorldServerServiceClient is the client API for WorldServerService service. @@ -38,6 +46,7 @@ const ( type WorldServerServiceClient interface { // Items GetPlayerItemsByGuids(ctx context.Context, in *GetPlayerItemsByGuidsRequest, opts ...grpc.CallOption) (*GetPlayerItemsByGuidsResponse, error) + TakePlayerItemByPos(ctx context.Context, in *TakePlayerItemByPosRequest, opts ...grpc.CallOption) (*TakePlayerItemByPosResponse, error) RemoveItemsWithGuidsFromPlayer(ctx context.Context, in *RemoveItemsWithGuidsFromPlayerRequest, opts ...grpc.CallOption) (*RemoveItemsWithGuidsFromPlayerResponse, error) AddExistingItemToPlayer(ctx context.Context, in *AddExistingItemToPlayerRequest, opts ...grpc.CallOption) (*AddExistingItemToPlayerResponse, error) // Money @@ -51,6 +60,15 @@ type WorldServerServiceClient interface { AddPlayersToBattleground(ctx context.Context, in *AddPlayersToBattlegroundRequest, opts ...grpc.CallOption) (*AddPlayersToBattlegroundResponse, error) CanPlayerJoinBattlegroundQueue(ctx context.Context, in *CanPlayerJoinBattlegroundQueueRequest, opts ...grpc.CallOption) (*CanPlayerJoinBattlegroundQueueResponse, error) CanPlayerTeleportToBattleground(ctx context.Context, in *CanPlayerTeleportToBattlegroundRequest, opts ...grpc.CallOption) (*CanPlayerTeleportToBattlegroundResponse, error) + // LFG + GetLfgPlayerLockInfo(ctx context.Context, in *GetLfgPlayerLockInfoRequest, opts ...grpc.CallOption) (*GetLfgPlayerLockInfoResponse, error) + GetLfgPlayerInfo(ctx context.Context, in *GetLfgPlayerInfoRequest, opts ...grpc.CallOption) (*GetLfgPlayerInfoResponse, error) + GetLfgDungeonInfo(ctx context.Context, in *GetLfgDungeonInfoRequest, opts ...grpc.CallOption) (*GetLfgDungeonInfoResponse, error) + TeleportLfgPlayer(ctx context.Context, in *TeleportLfgPlayerRequest, opts ...grpc.CallOption) (*TeleportLfgPlayerResponse, error) + SetLfgBootVote(ctx context.Context, in *SetLfgBootVoteRequest, opts ...grpc.CallOption) (*SetLfgBootVoteResponse, error) + MaterializeLfgProposal(ctx context.Context, in *MaterializeLfgProposalRequest, opts ...grpc.CallOption) (*MaterializeLfgProposalResponse, error) + // Guilds + CreateGuild(ctx context.Context, in *CreateGuildRequest, opts ...grpc.CallOption) (*CreateGuildResponse, error) } type worldServerServiceClient struct { @@ -70,6 +88,15 @@ func (c *worldServerServiceClient) GetPlayerItemsByGuids(ctx context.Context, in return out, nil } +func (c *worldServerServiceClient) TakePlayerItemByPos(ctx context.Context, in *TakePlayerItemByPosRequest, opts ...grpc.CallOption) (*TakePlayerItemByPosResponse, error) { + out := new(TakePlayerItemByPosResponse) + err := c.cc.Invoke(ctx, WorldServerService_TakePlayerItemByPos_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *worldServerServiceClient) RemoveItemsWithGuidsFromPlayer(ctx context.Context, in *RemoveItemsWithGuidsFromPlayerRequest, opts ...grpc.CallOption) (*RemoveItemsWithGuidsFromPlayerResponse, error) { out := new(RemoveItemsWithGuidsFromPlayerResponse) err := c.cc.Invoke(ctx, WorldServerService_RemoveItemsWithGuidsFromPlayer_FullMethodName, in, out, opts...) @@ -160,12 +187,76 @@ func (c *worldServerServiceClient) CanPlayerTeleportToBattleground(ctx context.C return out, nil } +func (c *worldServerServiceClient) GetLfgPlayerLockInfo(ctx context.Context, in *GetLfgPlayerLockInfoRequest, opts ...grpc.CallOption) (*GetLfgPlayerLockInfoResponse, error) { + out := new(GetLfgPlayerLockInfoResponse) + err := c.cc.Invoke(ctx, WorldServerService_GetLfgPlayerLockInfo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *worldServerServiceClient) GetLfgPlayerInfo(ctx context.Context, in *GetLfgPlayerInfoRequest, opts ...grpc.CallOption) (*GetLfgPlayerInfoResponse, error) { + out := new(GetLfgPlayerInfoResponse) + err := c.cc.Invoke(ctx, WorldServerService_GetLfgPlayerInfo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *worldServerServiceClient) GetLfgDungeonInfo(ctx context.Context, in *GetLfgDungeonInfoRequest, opts ...grpc.CallOption) (*GetLfgDungeonInfoResponse, error) { + out := new(GetLfgDungeonInfoResponse) + err := c.cc.Invoke(ctx, WorldServerService_GetLfgDungeonInfo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *worldServerServiceClient) TeleportLfgPlayer(ctx context.Context, in *TeleportLfgPlayerRequest, opts ...grpc.CallOption) (*TeleportLfgPlayerResponse, error) { + out := new(TeleportLfgPlayerResponse) + err := c.cc.Invoke(ctx, WorldServerService_TeleportLfgPlayer_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *worldServerServiceClient) SetLfgBootVote(ctx context.Context, in *SetLfgBootVoteRequest, opts ...grpc.CallOption) (*SetLfgBootVoteResponse, error) { + out := new(SetLfgBootVoteResponse) + err := c.cc.Invoke(ctx, WorldServerService_SetLfgBootVote_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *worldServerServiceClient) MaterializeLfgProposal(ctx context.Context, in *MaterializeLfgProposalRequest, opts ...grpc.CallOption) (*MaterializeLfgProposalResponse, error) { + out := new(MaterializeLfgProposalResponse) + err := c.cc.Invoke(ctx, WorldServerService_MaterializeLfgProposal_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *worldServerServiceClient) CreateGuild(ctx context.Context, in *CreateGuildRequest, opts ...grpc.CallOption) (*CreateGuildResponse, error) { + out := new(CreateGuildResponse) + err := c.cc.Invoke(ctx, WorldServerService_CreateGuild_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // WorldServerServiceServer is the server API for WorldServerService service. // All implementations must embed UnimplementedWorldServerServiceServer // for forward compatibility type WorldServerServiceServer interface { // Items GetPlayerItemsByGuids(context.Context, *GetPlayerItemsByGuidsRequest) (*GetPlayerItemsByGuidsResponse, error) + TakePlayerItemByPos(context.Context, *TakePlayerItemByPosRequest) (*TakePlayerItemByPosResponse, error) RemoveItemsWithGuidsFromPlayer(context.Context, *RemoveItemsWithGuidsFromPlayerRequest) (*RemoveItemsWithGuidsFromPlayerResponse, error) AddExistingItemToPlayer(context.Context, *AddExistingItemToPlayerRequest) (*AddExistingItemToPlayerResponse, error) // Money @@ -179,6 +270,15 @@ type WorldServerServiceServer interface { AddPlayersToBattleground(context.Context, *AddPlayersToBattlegroundRequest) (*AddPlayersToBattlegroundResponse, error) CanPlayerJoinBattlegroundQueue(context.Context, *CanPlayerJoinBattlegroundQueueRequest) (*CanPlayerJoinBattlegroundQueueResponse, error) CanPlayerTeleportToBattleground(context.Context, *CanPlayerTeleportToBattlegroundRequest) (*CanPlayerTeleportToBattlegroundResponse, error) + // LFG + GetLfgPlayerLockInfo(context.Context, *GetLfgPlayerLockInfoRequest) (*GetLfgPlayerLockInfoResponse, error) + GetLfgPlayerInfo(context.Context, *GetLfgPlayerInfoRequest) (*GetLfgPlayerInfoResponse, error) + GetLfgDungeonInfo(context.Context, *GetLfgDungeonInfoRequest) (*GetLfgDungeonInfoResponse, error) + TeleportLfgPlayer(context.Context, *TeleportLfgPlayerRequest) (*TeleportLfgPlayerResponse, error) + SetLfgBootVote(context.Context, *SetLfgBootVoteRequest) (*SetLfgBootVoteResponse, error) + MaterializeLfgProposal(context.Context, *MaterializeLfgProposalRequest) (*MaterializeLfgProposalResponse, error) + // Guilds + CreateGuild(context.Context, *CreateGuildRequest) (*CreateGuildResponse, error) mustEmbedUnimplementedWorldServerServiceServer() } @@ -189,6 +289,9 @@ type UnimplementedWorldServerServiceServer struct { func (UnimplementedWorldServerServiceServer) GetPlayerItemsByGuids(context.Context, *GetPlayerItemsByGuidsRequest) (*GetPlayerItemsByGuidsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetPlayerItemsByGuids not implemented") } +func (UnimplementedWorldServerServiceServer) TakePlayerItemByPos(context.Context, *TakePlayerItemByPosRequest) (*TakePlayerItemByPosResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TakePlayerItemByPos not implemented") +} func (UnimplementedWorldServerServiceServer) RemoveItemsWithGuidsFromPlayer(context.Context, *RemoveItemsWithGuidsFromPlayerRequest) (*RemoveItemsWithGuidsFromPlayerResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RemoveItemsWithGuidsFromPlayer not implemented") } @@ -219,6 +322,27 @@ func (UnimplementedWorldServerServiceServer) CanPlayerJoinBattlegroundQueue(cont func (UnimplementedWorldServerServiceServer) CanPlayerTeleportToBattleground(context.Context, *CanPlayerTeleportToBattlegroundRequest) (*CanPlayerTeleportToBattlegroundResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CanPlayerTeleportToBattleground not implemented") } +func (UnimplementedWorldServerServiceServer) GetLfgPlayerLockInfo(context.Context, *GetLfgPlayerLockInfoRequest) (*GetLfgPlayerLockInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetLfgPlayerLockInfo not implemented") +} +func (UnimplementedWorldServerServiceServer) GetLfgPlayerInfo(context.Context, *GetLfgPlayerInfoRequest) (*GetLfgPlayerInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetLfgPlayerInfo not implemented") +} +func (UnimplementedWorldServerServiceServer) GetLfgDungeonInfo(context.Context, *GetLfgDungeonInfoRequest) (*GetLfgDungeonInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetLfgDungeonInfo not implemented") +} +func (UnimplementedWorldServerServiceServer) TeleportLfgPlayer(context.Context, *TeleportLfgPlayerRequest) (*TeleportLfgPlayerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TeleportLfgPlayer not implemented") +} +func (UnimplementedWorldServerServiceServer) SetLfgBootVote(context.Context, *SetLfgBootVoteRequest) (*SetLfgBootVoteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetLfgBootVote not implemented") +} +func (UnimplementedWorldServerServiceServer) MaterializeLfgProposal(context.Context, *MaterializeLfgProposalRequest) (*MaterializeLfgProposalResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MaterializeLfgProposal not implemented") +} +func (UnimplementedWorldServerServiceServer) CreateGuild(context.Context, *CreateGuildRequest) (*CreateGuildResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateGuild not implemented") +} func (UnimplementedWorldServerServiceServer) mustEmbedUnimplementedWorldServerServiceServer() {} // UnsafeWorldServerServiceServer may be embedded to opt out of forward compatibility for this service. @@ -250,6 +374,24 @@ func _WorldServerService_GetPlayerItemsByGuids_Handler(srv interface{}, ctx cont return interceptor(ctx, in, info, handler) } +func _WorldServerService_TakePlayerItemByPos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TakePlayerItemByPosRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorldServerServiceServer).TakePlayerItemByPos(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WorldServerService_TakePlayerItemByPos_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorldServerServiceServer).TakePlayerItemByPos(ctx, req.(*TakePlayerItemByPosRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _WorldServerService_RemoveItemsWithGuidsFromPlayer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RemoveItemsWithGuidsFromPlayerRequest) if err := dec(in); err != nil { @@ -430,6 +572,132 @@ func _WorldServerService_CanPlayerTeleportToBattleground_Handler(srv interface{} return interceptor(ctx, in, info, handler) } +func _WorldServerService_GetLfgPlayerLockInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetLfgPlayerLockInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorldServerServiceServer).GetLfgPlayerLockInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WorldServerService_GetLfgPlayerLockInfo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorldServerServiceServer).GetLfgPlayerLockInfo(ctx, req.(*GetLfgPlayerLockInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WorldServerService_GetLfgPlayerInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetLfgPlayerInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorldServerServiceServer).GetLfgPlayerInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WorldServerService_GetLfgPlayerInfo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorldServerServiceServer).GetLfgPlayerInfo(ctx, req.(*GetLfgPlayerInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WorldServerService_GetLfgDungeonInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetLfgDungeonInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorldServerServiceServer).GetLfgDungeonInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WorldServerService_GetLfgDungeonInfo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorldServerServiceServer).GetLfgDungeonInfo(ctx, req.(*GetLfgDungeonInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WorldServerService_TeleportLfgPlayer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TeleportLfgPlayerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorldServerServiceServer).TeleportLfgPlayer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WorldServerService_TeleportLfgPlayer_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorldServerServiceServer).TeleportLfgPlayer(ctx, req.(*TeleportLfgPlayerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WorldServerService_SetLfgBootVote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetLfgBootVoteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorldServerServiceServer).SetLfgBootVote(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WorldServerService_SetLfgBootVote_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorldServerServiceServer).SetLfgBootVote(ctx, req.(*SetLfgBootVoteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WorldServerService_MaterializeLfgProposal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MaterializeLfgProposalRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorldServerServiceServer).MaterializeLfgProposal(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WorldServerService_MaterializeLfgProposal_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorldServerServiceServer).MaterializeLfgProposal(ctx, req.(*MaterializeLfgProposalRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WorldServerService_CreateGuild_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateGuildRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorldServerServiceServer).CreateGuild(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WorldServerService_CreateGuild_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorldServerServiceServer).CreateGuild(ctx, req.(*CreateGuildRequest)) + } + return interceptor(ctx, in, info, handler) +} + // WorldServerService_ServiceDesc is the grpc.ServiceDesc for WorldServerService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -441,6 +709,10 @@ var WorldServerService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetPlayerItemsByGuids", Handler: _WorldServerService_GetPlayerItemsByGuids_Handler, }, + { + MethodName: "TakePlayerItemByPos", + Handler: _WorldServerService_TakePlayerItemByPos_Handler, + }, { MethodName: "RemoveItemsWithGuidsFromPlayer", Handler: _WorldServerService_RemoveItemsWithGuidsFromPlayer_Handler, @@ -481,6 +753,34 @@ var WorldServerService_ServiceDesc = grpc.ServiceDesc{ MethodName: "CanPlayerTeleportToBattleground", Handler: _WorldServerService_CanPlayerTeleportToBattleground_Handler, }, + { + MethodName: "GetLfgPlayerLockInfo", + Handler: _WorldServerService_GetLfgPlayerLockInfo_Handler, + }, + { + MethodName: "GetLfgPlayerInfo", + Handler: _WorldServerService_GetLfgPlayerInfo_Handler, + }, + { + MethodName: "GetLfgDungeonInfo", + Handler: _WorldServerService_GetLfgDungeonInfo_Handler, + }, + { + MethodName: "TeleportLfgPlayer", + Handler: _WorldServerService_TeleportLfgPlayer_Handler, + }, + { + MethodName: "SetLfgBootVote", + Handler: _WorldServerService_SetLfgBootVote_Handler, + }, + { + MethodName: "MaterializeLfgProposal", + Handler: _WorldServerService_MaterializeLfgProposal_Handler, + }, + { + MethodName: "CreateGuild", + Handler: _WorldServerService_CreateGuild_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "worldserver.proto", diff --git a/go.mod b/go.mod index efd3cfb..a58dd15 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.22 require ( github.com/go-mysql-org/go-mysql v1.10.1-0.20241221150101-2f4217957dd5 github.com/go-sql-driver/mysql v1.8.1 + github.com/google/uuid v1.6.0 github.com/ilyakaznacheev/cleanenv v1.5.0 github.com/nats-io/nats.go v1.38.0 github.com/pingcap/tidb/pkg/parser v0.0.0-20250124080159-3f742662039e @@ -21,13 +22,13 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/BurntSushi/toml v1.4.0 // indirect + github.com/DATA-DOG/go-sqlmock v1.5.2 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/mattn/go-colorable v0.1.14 // indirect diff --git a/go.sum b/go.sum index fab66ab..4aa21fb 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -42,6 +44,7 @@ github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2l github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= diff --git a/shared/authidentity/identity.go b/shared/authidentity/identity.go new file mode 100644 index 0000000..388f1ed --- /dev/null +++ b/shared/authidentity/identity.go @@ -0,0 +1,116 @@ +package authidentity + +import "strings" + +const ( + MaxClassicUsernameLen = 16 + MaxEmailLen = 255 +) + +func NormalizeLoginIdentity(identity string) string { + return strings.TrimSpace(identity) +} + +func IsValidLoginIdentity(identity string) bool { + normalized := NormalizeLoginIdentity(identity) + if identity != normalized { + return false + } + return IsValidClassicUsername(normalized) +} + +func IsValidClassicUsername(username string) bool { + if len(username) == 0 || len(username) > MaxClassicUsernameLen { + return false + } + for i := 0; i < len(username); i++ { + c := username[i] + if isASCIILetter(c) || isASCIIDigit(c) || c == '_' || c == '-' || c == '.' { + continue + } + return false + } + return true +} + +func IsValidEmail(email string) bool { + normalized := NormalizeLoginIdentity(email) + if email != normalized { + return false + } + email = normalized + if len(email) == 0 || len(email) > MaxEmailLen || !isPrintableASCII(email) { + return false + } + if strings.Count(email, "@") != 1 { + return false + } + + parts := strings.Split(email, "@") + local, domain := parts[0], parts[1] + if !isValidEmailLocalPart(local) || !isValidEmailDomain(domain) { + return false + } + return true +} + +func isValidEmailLocalPart(local string) bool { + if len(local) == 0 || len(local) > 64 { + return false + } + if local[0] == '.' || local[len(local)-1] == '.' || strings.Contains(local, "..") { + return false + } + for i := 0; i < len(local); i++ { + c := local[i] + if isASCIILetter(c) || isASCIIDigit(c) { + continue + } + switch c { + case '.', '_', '%', '+', '-': + continue + default: + return false + } + } + return true +} + +func isValidEmailDomain(domain string) bool { + if len(domain) == 0 || len(domain) > 253 || !strings.Contains(domain, ".") { + return false + } + labels := strings.Split(domain, ".") + for _, label := range labels { + if len(label) == 0 || len(label) > 63 { + return false + } + if label[0] == '-' || label[len(label)-1] == '-' { + return false + } + for i := 0; i < len(label); i++ { + c := label[i] + if !isASCIILetter(c) && !isASCIIDigit(c) && c != '-' { + return false + } + } + } + return true +} + +func isPrintableASCII(value string) bool { + for i := 0; i < len(value); i++ { + if value[i] < 0x21 || value[i] > 0x7E { + return false + } + } + return true +} + +func isASCIILetter(c byte) bool { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') +} + +func isASCIIDigit(c byte) bool { + return c >= '0' && c <= '9' +} diff --git a/shared/events/consumer-gateway.go b/shared/events/consumer-gateway.go index d7b33d3..60d86c4 100644 --- a/shared/events/consumer-gateway.go +++ b/shared/events/consumer-gateway.go @@ -10,6 +10,11 @@ type GWCharacterLoggedInHandler interface { HandleCharacterLoggedIn(payload GWEventCharacterLoggedInPayload) error } +type GWGatewayStartedHandler interface { + // HandleGatewayStarted handles gateway lifecycle start events. + HandleGatewayStarted(payload GWEventGatewayStartedPayload) error +} + type GWCharacterLoggedOutHandler interface { // HandleCharacterLoggedOut handles player logged out events. HandleCharacterLoggedOut(payload GWEventCharacterLoggedOutPayload) error @@ -20,6 +25,11 @@ type GWCharactersUpdatesHandler interface { HandleCharactersUpdates(payload GWEventCharactersUpdatesPayload) error } +type GWGuildCreatedHandler interface { + // HandleGuildCreated handles guild creation events observed by gateway. + HandleGuildCreated(payload GWEventGuildCreatedPayload) error +} + // GatewayConsumer listens to gateway events and handles events if there are handlers. type GatewayConsumer interface { // Listen is non-blocking operation that listens to the gateway events. @@ -37,10 +47,12 @@ func NewGatewayConsumer(nc *nats.Conn, options ...GatewayConsumerOption) Gateway opt.apply(params) } return &gatewayConsumerImpl{ - nc: nc, - loggedInHandler: params.loggedInHandler, - loggedOutHandler: params.loggedOutHandler, - charsUpdatesHandler: params.charsUpdatesHandler, + nc: nc, + gatewayStartedHandler: params.gatewayStartedHandler, + loggedInHandler: params.loggedInHandler, + loggedOutHandler: params.loggedOutHandler, + charsUpdatesHandler: params.charsUpdatesHandler, + guildCreatedHandler: params.guildCreatedHandler, } } @@ -49,6 +61,14 @@ type GatewayConsumerOption interface { apply(*gatewayConsumerParams) } +// WithGWConsumerGatewayStartedHandler creates gateway consumer option with gateway started handler. +// If not specified, listener will ignore this kind of events. +func WithGWConsumerGatewayStartedHandler(h GWGatewayStartedHandler) GatewayConsumerOption { + return newFuncGatewayConsumerOption(func(params *gatewayConsumerParams) { + params.gatewayStartedHandler = h + }) +} + // WithGWConsumerLoggedInHandler creates gateway consumer option with logged in handler. // If not specified, listener will ignore these kind of events. func WithGWConsumerLoggedInHandler(h GWCharacterLoggedInHandler) GatewayConsumerOption { @@ -73,6 +93,14 @@ func WithGWConsumerCharsUpdatesHandler(h GWCharactersUpdatesHandler) GatewayCons }) } +// WithGWConsumerGuildCreatedHandler creates gateway consumer option with guild created handler. +// If not specified, listener will ignore this kind of events. +func WithGWConsumerGuildCreatedHandler(h GWGuildCreatedHandler) GatewayConsumerOption { + return newFuncGatewayConsumerOption(func(params *gatewayConsumerParams) { + params.guildCreatedHandler = h + }) +} + // funcGatewayConsumerOption wraps a function that modifies funcGatewayConsumerOption into an // implementation of the GatewayConsumerOption interface. type funcGatewayConsumerOption struct { @@ -91,9 +119,11 @@ func newFuncGatewayConsumerOption(f func(*gatewayConsumerParams)) *funcGatewayCo // gatewayConsumerParams list of all possible parameters of GatewayConsumer. type gatewayConsumerParams struct { - loggedInHandler GWCharacterLoggedInHandler - loggedOutHandler GWCharacterLoggedOutHandler - charsUpdatesHandler GWCharactersUpdatesHandler + gatewayStartedHandler GWGatewayStartedHandler + loggedInHandler GWCharacterLoggedInHandler + loggedOutHandler GWCharacterLoggedOutHandler + charsUpdatesHandler GWCharactersUpdatesHandler + guildCreatedHandler GWGuildCreatedHandler } // gatewayConsumerImpl implementation of GatewayConsumer. @@ -102,13 +132,37 @@ type gatewayConsumerImpl struct { // subs is all subscriptions list. subs []*nats.Subscription - loggedInHandler GWCharacterLoggedInHandler - loggedOutHandler GWCharacterLoggedOutHandler - charsUpdatesHandler GWCharactersUpdatesHandler + gatewayStartedHandler GWGatewayStartedHandler + loggedInHandler GWCharacterLoggedInHandler + loggedOutHandler GWCharacterLoggedOutHandler + charsUpdatesHandler GWCharactersUpdatesHandler + guildCreatedHandler GWGuildCreatedHandler } // Listen is non-blocking operation that listens to gateway events. func (c *gatewayConsumerImpl) Listen() error { + if c.gatewayStartedHandler != nil { + sub, err := c.nc.Subscribe(GWEventGatewayStarted.SubjectName(), func(msg *nats.Msg) { + payload := GWEventGatewayStartedPayload{} + _, err := Unmarshal(msg.Data, &payload) + if err != nil { + log.Error().Err(err).Msg("can't read GWEventGatewayStarted (payload part) event") + return + } + + err = c.gatewayStartedHandler.HandleGatewayStarted(payload) + if err != nil { + log.Error().Err(err).Msg("can't handle GWEventGatewayStarted event") + return + } + }) + if err != nil { + return err + } + + c.subs = append(c.subs, sub) + } + if c.loggedInHandler != nil { sub, err := c.nc.Subscribe(GWEventCharacterLoggedIn.SubjectName(), func(msg *nats.Msg) { loggedInP := GWEventCharacterLoggedInPayload{} @@ -175,6 +229,28 @@ func (c *gatewayConsumerImpl) Listen() error { c.subs = append(c.subs, sub) } + if c.guildCreatedHandler != nil { + sub, err := c.nc.Subscribe(GWEventGuildCreated.SubjectName(), func(msg *nats.Msg) { + guildCreatedP := GWEventGuildCreatedPayload{} + _, err := Unmarshal(msg.Data, &guildCreatedP) + if err != nil { + log.Error().Err(err).Msg("can't read GWEventGuildCreated (payload part) event") + return + } + + err = c.guildCreatedHandler.HandleGuildCreated(guildCreatedP) + if err != nil { + log.Error().Err(err).Msg("can't handle GWEventGuildCreated event") + return + } + }) + if err != nil { + return err + } + + c.subs = append(c.subs, sub) + } + return nil } diff --git a/shared/events/consumer-group.go b/shared/events/consumer-group.go index 086bee8..caa68dc 100644 --- a/shared/events/consumer-group.go +++ b/shared/events/consumer-group.go @@ -10,6 +10,11 @@ type GroupEventInviteCreatedHandler interface { GroupInviteCreatedEvent(payload *GroupEventInviteCreatedPayload) error } +type GroupEventInviteDeclinedHandler interface { + // GroupInviteDeclinedEvent handles group invite declined events. + GroupInviteDeclinedEvent(payload *GroupEventInviteDeclinedPayload) error +} + type GroupEventGroupCreatedHandler interface { // GroupCreatedEvent handles group invite created events. GroupCreatedEvent(payload *GroupEventGroupCreatedPayload) error @@ -65,6 +70,42 @@ type GroupEventGroupDifficultyChangedHandler interface { GroupDifficultyChangedEvent(payload *GroupEventGroupDifficultyChangedPayload) error } +type GroupEventReadyCheckStartedHandler interface { + GroupReadyCheckStartedEvent(payload *GroupEventReadyCheckStartedPayload) error +} + +type GroupEventReadyCheckMemberStateHandler interface { + GroupReadyCheckMemberStateEvent(payload *GroupEventReadyCheckMemberStatePayload) error +} + +type GroupEventReadyCheckFinishedHandler interface { + GroupReadyCheckFinishedEvent(payload *GroupEventReadyCheckFinishedPayload) error +} + +type GroupEventMemberSubGroupChangedHandler interface { + GroupMemberSubGroupChangedEvent(payload *GroupEventMemberSubGroupChangedPayload) error +} + +type GroupEventMemberFlagsChangedHandler interface { + GroupMemberFlagsChangedEvent(payload *GroupEventMemberFlagsChangedPayload) error +} + +type GroupEventMemberStateChangedHandler interface { + GroupMemberStateChangedEvent(payload *GroupEventMemberStateChangedPayload) error +} + +type GroupEventMemberStatesChangedHandler interface { + GroupMemberStatesChangedEvent(payload *GroupEventMemberStatesChangedPayload) error +} + +type GroupEventInstanceResetRequestHandler interface { + GroupInstanceResetRequestEvent(payload *GroupEventInstanceResetRequestPayload) error +} + +type GroupEventInstanceBindExtensionRequestHandler interface { + GroupInstanceBindExtensionRequestEvent(payload *GroupEventInstanceBindExtensionRequestPayload) error +} + // GroupEventsConsumer listens to group events and handles events if there are handlers. type GroupEventsConsumer interface { // Listen is non-blocking operation that listens to the group events. @@ -84,6 +125,7 @@ func NewGroupEventsConsumer(nc *nats.Conn, options ...GroupEventsConsumerOption) return &groupEventsConsumerImpl{ nc: nc, inviteCreatedHandler: params.inviteCreatedHandler, + inviteDeclinedHandler: params.inviteDeclinedHandler, groupCreatedHandler: params.groupCreatedHandler, groupMemberOnlineStatusChangedHandler: params.groupMemberOnlineStatusChangedHandler, groupMemberLeftHandler: params.groupMemberLeftHandler, @@ -95,6 +137,15 @@ func NewGroupEventsConsumer(nc *nats.Conn, options ...GroupEventsConsumerOption) newMessageHandler: params.newMessageHandler, newTargetIconHandler: params.newTargetIconHandler, difficultyChangedHandler: params.difficultyChangedHandler, + readyCheckStartedHandler: params.readyCheckStartedHandler, + readyCheckMemberStateHandler: params.readyCheckMemberStateHandler, + readyCheckFinishedHandler: params.readyCheckFinishedHandler, + memberSubGroupChangedHandler: params.memberSubGroupChangedHandler, + memberFlagsChangedHandler: params.memberFlagsChangedHandler, + memberStateChangedHandler: params.memberStateChangedHandler, + memberStatesChangedHandler: params.memberStatesChangedHandler, + instanceResetRequestHandler: params.instanceResetRequestHandler, + instanceBindExtensionRequestHandler: params.instanceBindExtensionRequestHandler, } } @@ -111,6 +162,14 @@ func WithGroupEventConsumerInviteCreatedHandler(h GroupEventInviteCreatedHandler }) } +// WithGroupEventConsumerInviteDeclinedHandler creates group events consumer option with invite declined handler. +// If not specified, listener will ignore this kind of events. +func WithGroupEventConsumerInviteDeclinedHandler(h GroupEventInviteDeclinedHandler) GroupEventsConsumerOption { + return newFuncGroupEventsConsumerOption(func(params *groupEventsConsumerParams) { + params.inviteDeclinedHandler = h + }) +} + // WithGroupEventConsumerGroupCreatedHandler creates group events consumer option with group created handler. // If not specified, listener will ignore this kind of events. func WithGroupEventConsumerGroupCreatedHandler(h GroupEventGroupCreatedHandler) GroupEventsConsumerOption { @@ -200,6 +259,60 @@ func WithGroupDifficultyChangedHandler(h GroupEventGroupDifficultyChangedHandler }) } +func WithGroupEventReadyCheckStartedHandler(h GroupEventReadyCheckStartedHandler) GroupEventsConsumerOption { + return newFuncGroupEventsConsumerOption(func(params *groupEventsConsumerParams) { + params.readyCheckStartedHandler = h + }) +} + +func WithGroupEventReadyCheckMemberStateHandler(h GroupEventReadyCheckMemberStateHandler) GroupEventsConsumerOption { + return newFuncGroupEventsConsumerOption(func(params *groupEventsConsumerParams) { + params.readyCheckMemberStateHandler = h + }) +} + +func WithGroupEventReadyCheckFinishedHandler(h GroupEventReadyCheckFinishedHandler) GroupEventsConsumerOption { + return newFuncGroupEventsConsumerOption(func(params *groupEventsConsumerParams) { + params.readyCheckFinishedHandler = h + }) +} + +func WithGroupEventMemberSubGroupChangedHandler(h GroupEventMemberSubGroupChangedHandler) GroupEventsConsumerOption { + return newFuncGroupEventsConsumerOption(func(params *groupEventsConsumerParams) { + params.memberSubGroupChangedHandler = h + }) +} + +func WithGroupEventMemberFlagsChangedHandler(h GroupEventMemberFlagsChangedHandler) GroupEventsConsumerOption { + return newFuncGroupEventsConsumerOption(func(params *groupEventsConsumerParams) { + params.memberFlagsChangedHandler = h + }) +} + +func WithGroupEventMemberStateChangedHandler(h GroupEventMemberStateChangedHandler) GroupEventsConsumerOption { + return newFuncGroupEventsConsumerOption(func(params *groupEventsConsumerParams) { + params.memberStateChangedHandler = h + }) +} + +func WithGroupEventMemberStatesChangedHandler(h GroupEventMemberStatesChangedHandler) GroupEventsConsumerOption { + return newFuncGroupEventsConsumerOption(func(params *groupEventsConsumerParams) { + params.memberStatesChangedHandler = h + }) +} + +func WithGroupEventInstanceResetRequestHandler(h GroupEventInstanceResetRequestHandler) GroupEventsConsumerOption { + return newFuncGroupEventsConsumerOption(func(params *groupEventsConsumerParams) { + params.instanceResetRequestHandler = h + }) +} + +func WithGroupEventInstanceBindExtensionRequestHandler(h GroupEventInstanceBindExtensionRequestHandler) GroupEventsConsumerOption { + return newFuncGroupEventsConsumerOption(func(params *groupEventsConsumerParams) { + params.instanceBindExtensionRequestHandler = h + }) +} + // funcGatewayConsumerOption wraps a function that modifies funcGatewayConsumerOption into an // implementation of the GatewayConsumerOption interface. type funcGroupEventsConsumerOption struct { @@ -219,6 +332,7 @@ func newFuncGroupEventsConsumerOption(f func(*groupEventsConsumerParams)) *funcG // groupEventsConsumerParams list of all possible parameters of GroupEventsConsumer. type groupEventsConsumerParams struct { inviteCreatedHandler GroupEventInviteCreatedHandler + inviteDeclinedHandler GroupEventInviteDeclinedHandler groupCreatedHandler GroupEventGroupCreatedHandler groupMemberOnlineStatusChangedHandler GroupEventGroupMemberOnlineStatusChangedHandler groupMemberLeftHandler GroupEventGroupMemberLeftHandler @@ -230,6 +344,15 @@ type groupEventsConsumerParams struct { newMessageHandler GroupEventNewMessageHandler newTargetIconHandler GroupEventNewTargetIconHandler difficultyChangedHandler GroupEventGroupDifficultyChangedHandler + readyCheckStartedHandler GroupEventReadyCheckStartedHandler + readyCheckMemberStateHandler GroupEventReadyCheckMemberStateHandler + readyCheckFinishedHandler GroupEventReadyCheckFinishedHandler + memberSubGroupChangedHandler GroupEventMemberSubGroupChangedHandler + memberFlagsChangedHandler GroupEventMemberFlagsChangedHandler + memberStateChangedHandler GroupEventMemberStateChangedHandler + memberStatesChangedHandler GroupEventMemberStatesChangedHandler + instanceResetRequestHandler GroupEventInstanceResetRequestHandler + instanceBindExtensionRequestHandler GroupEventInstanceBindExtensionRequestHandler } // groupEventsConsumerImpl implementation of GroupEventsConsumer. @@ -239,6 +362,7 @@ type groupEventsConsumerImpl struct { subs []*nats.Subscription inviteCreatedHandler GroupEventInviteCreatedHandler + inviteDeclinedHandler GroupEventInviteDeclinedHandler groupCreatedHandler GroupEventGroupCreatedHandler groupMemberOnlineStatusChangedHandler GroupEventGroupMemberOnlineStatusChangedHandler groupMemberLeftHandler GroupEventGroupMemberLeftHandler @@ -250,6 +374,15 @@ type groupEventsConsumerImpl struct { newMessageHandler GroupEventNewMessageHandler newTargetIconHandler GroupEventNewTargetIconHandler difficultyChangedHandler GroupEventGroupDifficultyChangedHandler + readyCheckStartedHandler GroupEventReadyCheckStartedHandler + readyCheckMemberStateHandler GroupEventReadyCheckMemberStateHandler + readyCheckFinishedHandler GroupEventReadyCheckFinishedHandler + memberSubGroupChangedHandler GroupEventMemberSubGroupChangedHandler + memberFlagsChangedHandler GroupEventMemberFlagsChangedHandler + memberStateChangedHandler GroupEventMemberStateChangedHandler + memberStatesChangedHandler GroupEventMemberStatesChangedHandler + instanceResetRequestHandler GroupEventInstanceResetRequestHandler + instanceBindExtensionRequestHandler GroupEventInstanceBindExtensionRequestHandler } // Listen is non-blocking operation that listens to gateway events. @@ -276,6 +409,28 @@ func (c *groupEventsConsumerImpl) Listen() error { c.subs = append(c.subs, sub) } + if c.inviteDeclinedHandler != nil { + sub, err := c.nc.Subscribe(GroupEventInviteDeclined.SubjectName(), func(msg *nats.Msg) { + payload := GroupEventInviteDeclinedPayload{} + _, err := Unmarshal(msg.Data, &payload) + if err != nil { + log.Error().Err(err).Msg("can't read GroupEventInviteDeclined (payload part) event") + return + } + + err = c.inviteDeclinedHandler.GroupInviteDeclinedEvent(&payload) + if err != nil { + log.Error().Err(err).Msg("can't handle GroupEventInviteDeclined event") + return + } + }) + if err != nil { + return err + } + + c.subs = append(c.subs, sub) + } + if c.groupCreatedHandler != nil { sub, err := c.nc.Subscribe(GroupEventGroupCreated.SubjectName(), func(msg *nats.Msg) { payload := GroupEventGroupCreatedPayload{} @@ -518,6 +673,82 @@ func (c *groupEventsConsumerImpl) Listen() error { c.subs = append(c.subs, sub) } + if c.readyCheckStartedHandler != nil { + if err := subscribeGroupEvent(c, GroupEventGroupReadyCheckStarted, "GroupEventGroupReadyCheckStarted", c.readyCheckStartedHandler.GroupReadyCheckStartedEvent); err != nil { + return err + } + } + + if c.readyCheckMemberStateHandler != nil { + if err := subscribeGroupEvent(c, GroupEventGroupReadyCheckMemberState, "GroupEventGroupReadyCheckMemberState", c.readyCheckMemberStateHandler.GroupReadyCheckMemberStateEvent); err != nil { + return err + } + } + + if c.readyCheckFinishedHandler != nil { + if err := subscribeGroupEvent(c, GroupEventGroupReadyCheckFinished, "GroupEventGroupReadyCheckFinished", c.readyCheckFinishedHandler.GroupReadyCheckFinishedEvent); err != nil { + return err + } + } + + if c.memberSubGroupChangedHandler != nil { + if err := subscribeGroupEvent(c, GroupEventGroupMemberSubGroupChanged, "GroupEventGroupMemberSubGroupChanged", c.memberSubGroupChangedHandler.GroupMemberSubGroupChangedEvent); err != nil { + return err + } + } + + if c.memberFlagsChangedHandler != nil { + if err := subscribeGroupEvent(c, GroupEventGroupMemberFlagsChanged, "GroupEventGroupMemberFlagsChanged", c.memberFlagsChangedHandler.GroupMemberFlagsChangedEvent); err != nil { + return err + } + } + + if c.memberStateChangedHandler != nil { + if err := subscribeGroupEvent(c, GroupEventGroupMemberStateChanged, "GroupEventGroupMemberStateChanged", c.memberStateChangedHandler.GroupMemberStateChangedEvent); err != nil { + return err + } + } + + if c.memberStatesChangedHandler != nil { + if err := subscribeGroupEvent(c, GroupEventGroupMemberStatesChanged, "GroupEventGroupMemberStatesChanged", c.memberStatesChangedHandler.GroupMemberStatesChangedEvent); err != nil { + return err + } + } + + if c.instanceResetRequestHandler != nil { + if err := subscribeGroupEvent(c, GroupEventGroupInstanceResetRequest, "GroupEventGroupInstanceResetRequest", c.instanceResetRequestHandler.GroupInstanceResetRequestEvent); err != nil { + return err + } + } + + if c.instanceBindExtensionRequestHandler != nil { + if err := subscribeGroupEvent(c, GroupEventGroupInstanceBindExtensionRequest, "GroupEventGroupInstanceBindExtensionRequest", c.instanceBindExtensionRequestHandler.GroupInstanceBindExtensionRequestEvent); err != nil { + return err + } + } + + return nil +} + +func subscribeGroupEvent[T any](c *groupEventsConsumerImpl, event GroupServiceEvent, eventName string, handler func(*T) error) error { + sub, err := c.nc.Subscribe(event.SubjectName(), func(msg *nats.Msg) { + payload := new(T) + _, err := Unmarshal(msg.Data, payload) + if err != nil { + log.Error().Err(err).Msgf("can't read %s (payload part) event", eventName) + return + } + + if err = handler(payload); err != nil { + log.Error().Err(err).Msgf("can't handle %s event", eventName) + return + } + }) + if err != nil { + return err + } + + c.subs = append(c.subs, sub) return nil } diff --git a/shared/events/events-characters.go b/shared/events/events-characters.go index 2046c31..24eb9af 100644 --- a/shared/events/events-characters.go +++ b/shared/events/events-characters.go @@ -8,6 +8,12 @@ type CharactersServiceEvent int const ( // CharEventCharsDisconnectedUnhealthyGW event that contains players that were connected to unhealthy gateway CharEventCharsDisconnectedUnhealthyGW CharactersServiceEvent = iota + 1 + + // CharEventArenaTeamInviteCreated is emitted when charserver accepts an arena team invite request. + CharEventArenaTeamInviteCreated + + // CharEventArenaTeamNativeEvent is emitted after a committed arena team mutation. + CharEventArenaTeamNativeEvent ) // SubjectName is key that nats uses @@ -15,13 +21,37 @@ func (e CharactersServiceEvent) SubjectName() string { switch e { case CharEventCharsDisconnectedUnhealthyGW: return "char.chars.unhealthy.gw" + case CharEventArenaTeamInviteCreated: + return "char.arena-team.invite-created" + case CharEventArenaTeamNativeEvent: + return "char.arena-team.native-event" } panic(fmt.Errorf("unk event %d", e)) } // CharEventCharsDisconnectedUnhealthyGWPayload represents payload of CharEventCharsDisconnectedUnhealthyGW event type CharEventCharsDisconnectedUnhealthyGWPayload struct { - RealmID uint32 - GatewayID string - CharactersGUID []uint64 + RealmID uint32 + GatewayID string + EventTimeUnixNano uint64 + CharactersGUID []uint64 +} + +type CharEventArenaTeamInviteCreatedPayload struct { + RealmID uint32 + TargetGUID uint64 + TargetName string + InviterGUID uint64 + InviterName string + ArenaTeamID uint32 + TeamName string +} + +type CharEventArenaTeamNativeEventPayload struct { + RealmID uint32 + ReceiverGUIDs []uint64 + ArenaTeamID uint32 + Event uint8 + EventGUID uint64 + Args []string } diff --git a/shared/events/events-chat.go b/shared/events/events-chat.go index b8e0557..98f1257 100644 --- a/shared/events/events-chat.go +++ b/shared/events/events-chat.go @@ -37,45 +37,59 @@ func (e ChatServiceEvent) SubjectName(gatewayID string) string { // ChatEventIncomingWhisperPayload represents payload of ChatEventIncomingWhisper event type ChatEventIncomingWhisperPayload struct { - SenderGUID uint64 - SenderName string - SenderRace uint8 - ReceiverGUID uint64 - ReceiverName string - Language uint32 - Msg string + SenderRealmID uint32 + SenderGUID uint64 + SenderName string + SenderRace uint8 + SenderClass uint8 + SenderGender uint8 + SenderChatTag uint8 + ReceiverRealmID uint32 + ReceiverGUID uint64 + ReceiverName string + Language uint32 + Msg string } // ChatEventChannelMessagePayload represents payload of ChatEventChannelMessage event type ChatEventChannelMessagePayload struct { - RealmID uint32 - ChannelName string - ChannelID uint32 - SenderGUID uint64 - SenderName string - Language uint32 - Message string + RealmID uint32 + ChannelName string + ChannelID uint32 + TeamID uint32 + SenderGUID uint64 + SenderName string + Language uint32 + Message string + SenderChatTag uint8 } // ChatEventChannelJoinedPayload represents payload of ChatEventChannelJoined event type ChatEventChannelJoinedPayload struct { - ServiceID string - RealmID uint32 - ChannelName string - ChannelID uint32 - PlayerGUID uint64 - PlayerName string - PlayerFlags uint8 // MEMBER_FLAG_* + ServiceID string + RealmID uint32 + ChannelName string + ChannelID uint32 + ChannelFlags uint32 + TeamID uint32 + NumMembers uint32 + PlayerGUID uint64 + PlayerName string + PlayerFlags uint8 // MEMBER_FLAG_* } // ChatEventChannelLeftPayload represents payload of ChatEventChannelLeft event type ChatEventChannelLeftPayload struct { - ServiceID string - RealmID uint32 - ChannelName string - ChannelID uint32 - PlayerGUID uint64 - PlayerName string + ServiceID string + RealmID uint32 + ChannelName string + ChannelID uint32 + ChannelFlags uint32 + TeamID uint32 + NumMembers uint32 + PlayerGUID uint64 + PlayerName string + Silent bool } // ChatEventChannelNotificationPayload represents payload of ChatEventChannelNotification event @@ -83,6 +97,9 @@ type ChatEventChannelNotificationPayload struct { RealmID uint32 ChannelName string ChannelID uint32 + ChannelFlags uint32 + TeamID uint32 + NumMembers uint32 NotifyType uint8 // ChatNotify type from Channel.h TargetGUID uint64 TargetName string diff --git a/shared/events/events-friends.go b/shared/events/events-friends.go index fd382ac..b4e6add 100644 --- a/shared/events/events-friends.go +++ b/shared/events/events-friends.go @@ -29,7 +29,7 @@ type FriendEventStatusChangePayload struct { ServiceID string RealmID uint32 PlayerGUID uint64 - Status uint8 // 0=offline, 1=online + Status uint8 // 0=offline, 1=online Area uint32 Level uint32 ClassID uint32 diff --git a/shared/events/events-gateway.go b/shared/events/events-gateway.go index 53b1e57..97f4cb6 100644 --- a/shared/events/events-gateway.go +++ b/shared/events/events-gateway.go @@ -14,6 +14,12 @@ const ( // GWEventCharactersUpdates pack of characters update that occurs every N seconds. GWEventCharactersUpdates + + // GWEventGuildCreated is event that occurs when gateway observes successful guild creation from worldserver. + GWEventGuildCreated + + // GWEventGatewayStarted is event that occurs when gateway is registered and ready to accept new sessions. + GWEventGatewayStarted ) // SubjectName is key that nats uses. @@ -25,50 +31,73 @@ func (e GatewayEvent) SubjectName() string { return "gw.char.logged-out" case GWEventCharactersUpdates: return "gw.char.chars-updates" + case GWEventGuildCreated: + return "gw.guild.created" + case GWEventGatewayStarted: + return "gw.gateway.started" } panic(fmt.Errorf("unk event %d", e)) } -// GWEventCharacterLoggedInPayload represents payload of GWEventCharacterLoggedIn event. -type GWEventCharacterLoggedInPayload struct { +// GWEventGatewayStartedPayload represents payload of GWEventGatewayStarted event. +type GWEventGatewayStartedPayload struct { RealmID uint32 GatewayID string - CharGUID uint64 - CharName string - CharRace uint8 - CharClass uint8 - CharGender uint8 - CharLevel uint8 - CharZone uint32 - CharMap uint32 - CharPosX float32 - CharPosY float32 - CharPosZ float32 - CharGuildID uint32 - AccountID uint32 + StartedAtMs uint64 +} + +// GWEventCharacterLoggedInPayload represents payload of GWEventCharacterLoggedIn event. +type GWEventCharacterLoggedInPayload struct { + RealmID uint32 + GatewayID string + EventTimeUnixNano uint64 + CharGUID uint64 + CharName string + CharRace uint8 + CharClass uint8 + CharGender uint8 + CharLevel uint8 + CharZone uint32 + CharMap uint32 + CharPosX float32 + CharPosY float32 + CharPosZ float32 + CharGuildID uint32 + AccountID uint32 } // GWEventCharacterLoggedOutPayload represents payload of GWEventCharacterLoggedOut event. type GWEventCharacterLoggedOutPayload struct { - RealmID uint32 - GatewayID string - CharGUID uint64 - CharName string - CharGuildID uint32 - AccountID uint32 + RealmID uint32 + GatewayID string + EventTimeUnixNano uint64 + CharGUID uint64 + CharName string + CharGuildID uint32 + AccountID uint32 } type GWEventCharactersUpdatesPayload struct { - RealmID uint32 - GatewayID string - Updates []*CharacterUpdate + RealmID uint32 + GatewayID string + EventTimeUnixNano uint64 + Updates []*CharacterUpdate +} + +// GWEventGuildCreatedPayload represents payload of GWEventGuildCreated event. +type GWEventGuildCreatedPayload struct { + RealmID uint32 + GatewayID string + LeaderGUID uint64 + GuildName string } // CharacterUpdate represents new values of fields for the character. type CharacterUpdate struct { - ID uint64 `json:"i"` - Lvl *uint8 `json:"l,omitempty"` - Map *uint32 `json:"m,omitempty"` - Area *uint32 `json:"a,omitempty"` - Zone *uint32 `json:"z,omitempty"` + ID uint64 `json:"i"` + EventTimeUnixNano uint64 `json:"t,omitempty"` + Lvl *uint8 `json:"l,omitempty"` + Map *uint32 `json:"m,omitempty"` + Area *uint32 `json:"a,omitempty"` + Zone *uint32 `json:"z,omitempty"` } diff --git a/shared/events/events-group.go b/shared/events/events-group.go index 918781c..337efff 100644 --- a/shared/events/events-group.go +++ b/shared/events/events-group.go @@ -41,6 +41,20 @@ const ( // GroupEventGroupDifficultyChanged group event when dungeon or raid difficulty changed for the group GroupEventGroupDifficultyChanged + + GroupEventGroupReadyCheckStarted + GroupEventGroupReadyCheckMemberState + GroupEventGroupReadyCheckFinished + GroupEventGroupMemberSubGroupChanged + GroupEventGroupMemberFlagsChanged + GroupEventGroupMemberStateChanged + GroupEventGroupInstanceResetRequest + GroupEventGroupInstanceBindExtensionRequest + + // GroupEventInviteDeclined group event when player invite declined + GroupEventInviteDeclined + + GroupEventGroupMemberStatesChanged ) // SubjectName is key that nats uses @@ -48,6 +62,8 @@ func (e GroupServiceEvent) SubjectName() string { switch e { case GroupEventInviteCreated: return "group.invite.created" + case GroupEventInviteDeclined: + return "group.invite.declined" case GroupEventGroupCreated: return "group.created" case GroupEventGroupMemberOnlineStatusChanged: @@ -70,6 +86,24 @@ func (e GroupServiceEvent) SubjectName() string { return "group.targeticons.new" case GroupEventGroupDifficultyChanged: return "group.difficulty.changed" + case GroupEventGroupReadyCheckStarted: + return "group.readycheck.started" + case GroupEventGroupReadyCheckMemberState: + return "group.readycheck.member.state" + case GroupEventGroupReadyCheckFinished: + return "group.readycheck.finished" + case GroupEventGroupMemberSubGroupChanged: + return "group.member.subgroup.changed" + case GroupEventGroupMemberFlagsChanged: + return "group.member.flags.changed" + case GroupEventGroupMemberStateChanged: + return "group.member.state.changed" + case GroupEventGroupMemberStatesChanged: + return "group.member.states.changed" + case GroupEventGroupInstanceResetRequest: + return "group.instance.reset.request" + case GroupEventGroupInstanceBindExtensionRequest: + return "group.instance.bind.extension.request" } panic(fmt.Errorf("unk event %d", e)) } @@ -88,6 +122,15 @@ type GroupEventInviteCreatedPayload struct { InviteeName string } +type GroupEventInviteDeclinedPayload struct { + ServiceID string + RealmID uint32 + + InviterGUID uint64 + InviteeGUID uint64 + InviteeName string +} + type GroupEventGroupCreatedPayload struct { ServiceID string RealmID uint32 @@ -101,6 +144,7 @@ type GroupEventGroupCreatedPayload struct { Difficulty uint8 RaidDifficulty uint8 MasterLooterGuid uint64 + LfgDungeonEntry uint32 Members []GroupMember } @@ -186,8 +230,9 @@ type GroupEventNewMessagePayload struct { GroupID uint - SenderGUID uint64 - SenderName string + SenderGUID uint64 + SenderName string + SenderChatTag uint8 Language uint32 Msg string @@ -230,3 +275,130 @@ type GroupMember struct { SubGroup uint8 Roles uint8 } + +type GroupEventReadyCheckStartedPayload struct { + ServiceID string + RealmID uint32 + GroupID uint + LeaderGUID uint64 + DurationMs uint32 + Receivers []uint64 +} + +type GroupEventReadyCheckMemberStatePayload struct { + ServiceID string + RealmID uint32 + GroupID uint + MemberGUID uint64 + State uint8 // 0 waiting, 1 ready, 2 not ready + Receivers []uint64 +} + +type GroupEventReadyCheckFinishedPayload struct { + ServiceID string + RealmID uint32 + GroupID uint + Receivers []uint64 +} + +type GroupEventMemberSubGroupChangedPayload struct { + ServiceID string + RealmID uint32 + GroupID uint + MemberGUID uint64 + SubGroup uint8 + Receivers []uint64 +} + +type GroupEventMemberFlagsChangedPayload struct { + ServiceID string + RealmID uint32 + GroupID uint + MemberGUID uint64 + Flags uint8 + Roles uint8 + Receivers []uint64 +} + +type GroupEventMemberStateChangedPayload struct { + ServiceID string + RealmID uint32 + GroupID uint + SourceGatewayID string + SourceWorldserverID string + MemberGUID uint64 + Online bool + Level uint8 + Class uint8 + ZoneID uint32 + MapID uint32 + Health uint32 + MaxHealth uint32 + PowerType uint8 + Power uint32 + MaxPower uint32 + AurasKnown bool + Auras []GroupMemberAuraState + DeadKnown bool + Dead bool + GhostKnown bool + Ghost bool + Receivers []uint64 +} + +type GroupEventMemberStatesChangedPayload struct { + ServiceID string + RealmID uint32 + GroupID uint + SourceGatewayID string + SourceWorldserverID string + States []GroupMemberStateUpdate + Receivers []uint64 +} + +type GroupMemberStateUpdate struct { + MemberGUID uint64 + Online bool + Level uint8 + Class uint8 + ZoneID uint32 + MapID uint32 + Health uint32 + MaxHealth uint32 + PowerType uint8 + Power uint32 + MaxPower uint32 + AurasKnown bool + Auras []GroupMemberAuraState + DeadKnown bool + Dead bool + GhostKnown bool + Ghost bool +} + +type GroupMemberAuraState struct { + Slot uint8 + SpellID uint32 + Flags uint8 +} + +type GroupEventInstanceResetRequestPayload struct { + ServiceID string + RealmID uint32 + GroupID uint + PlayerGUID uint64 + MapID uint32 + Difficulty uint8 + Receivers []uint64 +} + +type GroupEventInstanceBindExtensionRequestPayload struct { + ServiceID string + RealmID uint32 + GroupID uint + PlayerGUID uint64 + MapID uint32 + Difficulty uint8 + Extended bool + Receivers []uint64 +} diff --git a/shared/events/events-guild.go b/shared/events/events-guild.go index 986af18..afb7f2b 100644 --- a/shared/events/events-guild.go +++ b/shared/events/events-guild.go @@ -47,6 +47,12 @@ const ( // GuildEventNewMessage guild event when guild member sent some message GuildEventNewMessage + + // GuildEventPetitionOffered guild event when a guild charter is offered to a same-realm target. + GuildEventPetitionOffered + + // GuildEventPetitionSigned guild event when a same-realm guild charter signature was processed. + GuildEventPetitionSigned ) // SubjectName is key that nats uses @@ -80,6 +86,10 @@ func (e GuildServiceEvent) SubjectName() string { return "guild.info.updated" case GuildEventNewMessage: return "guild.message.new" + case GuildEventPetitionOffered: + return "guild.petition.offered" + case GuildEventPetitionSigned: + return "guild.petition.signed" } panic(fmt.Errorf("unk event %d", e)) } @@ -231,8 +241,9 @@ type GuildEventNewMessagePayload struct { GuildID uint64 - SenderGUID uint64 - SenderName string + SenderGUID uint64 + SenderName string + SenderChatTag uint8 Language uint32 Msg string @@ -241,3 +252,36 @@ type GuildEventNewMessagePayload struct { Receivers []uint64 } + +type GuildPetitionSignature struct { + PlayerGUID uint64 + PlayerAccount uint32 +} + +type GuildEventPetitionOfferedPayload struct { + ServiceID string + RealmID uint32 + + PetitionGUID uint64 + PetitionID uint32 + OwnerGUID uint64 + TargetGUID uint64 + TargetName string + GuildName string + + RequiredSigns uint32 + Signatures []GuildPetitionSignature +} + +type GuildEventPetitionSignedPayload struct { + ServiceID string + RealmID uint32 + + PetitionGUID uint64 + OwnerGUID uint64 + SignerGUID uint64 + SignerName string + NativeStatus uint32 + + RequiredSigns uint32 +} diff --git a/shared/events/events-matchmaking.go b/shared/events/events-matchmaking.go index 7ad2bb5..ccfd886 100644 --- a/shared/events/events-matchmaking.go +++ b/shared/events/events-matchmaking.go @@ -17,6 +17,12 @@ const ( // MatchmakingEventPlayersInviteExpired matchmaking event for battleground and arena queue when players invite expired. MatchmakingEventPlayersInviteExpired + + // MatchmakingEventLfgStatusChanged matchmaking event for LFG queue, role check, and proposal state changes. + MatchmakingEventLfgStatusChanged + + // MatchmakingEventLfgProposalAccepted matchmaking event emitted when every LFG proposal member accepts. + MatchmakingEventLfgProposalAccepted ) // SubjectName is key that nats uses @@ -28,6 +34,10 @@ func (e MatchmakingServiceEvent) SubjectName() string { return "matchmaking.pvpqueue.invited" case MatchmakingEventPlayersInviteExpired: return "matchmaking.pvpqueue.invite.expired" + case MatchmakingEventLfgStatusChanged: + return "matchmaking.lfg.status.changed" + case MatchmakingEventLfgProposalAccepted: + return "matchmaking.lfg.proposal.accepted" } panic(fmt.Errorf("unk event %d", e)) } @@ -78,3 +88,96 @@ type MatchmakingEventPlayersInviteExpiredPayload struct { PlayersGUID []guid.LowType QueueSlotByPlayer map[guid.LowType]uint8 } + +type MatchmakingLfgState uint8 + +const ( + MatchmakingLfgStateNone MatchmakingLfgState = iota + MatchmakingLfgStateRoleCheck + MatchmakingLfgStateQueued + MatchmakingLfgStateProposal + MatchmakingLfgStateBoot + MatchmakingLfgStateDungeon + MatchmakingLfgStateFinishedDungeon + MatchmakingLfgStateRaidBrowser +) + +type MatchmakingLfgProposalState uint8 + +const ( + MatchmakingLfgProposalInitiating MatchmakingLfgProposalState = iota + MatchmakingLfgProposalFailed + MatchmakingLfgProposalSuccess +) + +type MatchmakingLfgProposalFailure uint8 + +const ( + MatchmakingLfgProposalFailureNone MatchmakingLfgProposalFailure = iota + MatchmakingLfgProposalFailureFailed + MatchmakingLfgProposalFailureDeclined +) + +type MatchmakingLfgMember struct { + RealmID uint32 + PlayerGUID guid.LowType + Roles uint8 + Leader bool + QueueLeaderRealmID uint32 + QueueLeaderGUID guid.LowType +} + +type MatchmakingLfgProposalMember struct { + RealmID uint32 + PlayerGUID guid.LowType + SelectedRoles uint8 + AssignedRole uint8 + Answered bool + Accepted bool +} + +type MatchmakingLfgStatusPayload struct { + State MatchmakingLfgState + ProposalID uint32 + ProposalState MatchmakingLfgProposalState + ProposalFailure MatchmakingLfgProposalFailure + DungeonEntry uint32 + SelectedDungeons []uint32 + QueuedMembers []MatchmakingLfgMember + ProposalMembers []MatchmakingLfgProposalMember + + QueuedTimeMilliseconds uint32 + TanksNeeded uint8 + HealersNeeded uint8 + DamageNeeded uint8 +} + +type MatchmakingEventLfgStatusChangedPayload struct { + RealmID uint32 + PlayersGUID []guid.LowType + Status MatchmakingLfgStatusPayload +} + +type MatchmakingLfgProposalAcceptedMember struct { + RealmID uint32 + PlayerGUID guid.LowType + SelectedRoles uint8 + AssignedRole uint8 + QueueLeaderRealmID uint32 + QueueLeaderGUID guid.LowType + WorldserverID string +} + +type MatchmakingEventLfgProposalAcceptedPayload struct { + RealmID uint32 + LeaderRealmID uint32 + BattlegroupID uint32 + CrossRealm bool + ProposalID uint32 + GroupID uint32 + DungeonEntry uint32 + LeaderGUID guid.LowType + LeaderWorldserverID string + PlayersGUID []guid.LowType + Members []MatchmakingLfgProposalAcceptedMember +} diff --git a/shared/events/events-serversregistry.go b/shared/events/events-serversregistry.go index 36571ac..57a3559 100644 --- a/shared/events/events-serversregistry.go +++ b/shared/events/events-serversregistry.go @@ -20,6 +20,12 @@ const ( // ServerRegistryEventGSRemoved is event that occurs when server registry removes game server (unhealthy or shutdown). ServerRegistryEventGSRemoved + + // ServerRegistryEventMatchmakingRemovedUnhealthy is event that occurs when matchmaking service becomes unhealthy. + ServerRegistryEventMatchmakingRemovedUnhealthy + + // ServerRegistryEventMatchmakingRecovered is event that occurs when matchmaking service becomes healthy again. + ServerRegistryEventMatchmakingRecovered ) // SubjectName is key that nats uses. @@ -35,6 +41,10 @@ func (e ServerRegistryEvent) SubjectName() string { return "sr.gs.added" case ServerRegistryEventGSRemoved: return "sr.gs.removed" + case ServerRegistryEventMatchmakingRemovedUnhealthy: + return "sr.matchmaking.removed.unhealthy" + case ServerRegistryEventMatchmakingRecovered: + return "sr.matchmaking.recovered" } panic(fmt.Errorf("unk event %d", e)) } @@ -49,10 +59,11 @@ type ServerRegistryEventGWAddedPayload struct { // ServerRegistryEventGWRemovedUnhealthyPayload represents payload of ServerRegistryEventGWRemovedUnhealthy event. type ServerRegistryEventGWRemovedUnhealthyPayload struct { - ID string - Address string - HealthCheckAddr string - RealmID uint32 + ID string + Address string + HealthCheckAddr string + RealmID uint32 + EventTimeUnixNano uint64 } type GameServer struct { @@ -112,3 +123,18 @@ type ServerRegistryEventGSAddedPayload struct { type ServerRegistryEventGSRemovedPayload struct { GameServer GameServer } + +type MatchmakingService struct { + Address string + HealthCheckAddr string + ObservedAtUnixMs int64 +} + +type ServerRegistryEventMatchmakingRemovedUnhealthyPayload struct { + MatchmakingService MatchmakingService + Error string +} + +type ServerRegistryEventMatchmakingRecoveredPayload struct { + MatchmakingService MatchmakingService +} diff --git a/shared/events/producer-characters.go b/shared/events/producer-characters.go index 6ba7519..ad2d8b2 100644 --- a/shared/events/producer-characters.go +++ b/shared/events/producer-characters.go @@ -2,12 +2,15 @@ package events import ( "encoding/json" + "time" "github.com/nats-io/nats.go" ) type CharactersServiceProducer interface { CharsDisconnectedUnhealthyLB(payload *CharEventCharsDisconnectedUnhealthyGWPayload) error + ArenaTeamInviteCreated(payload *CharEventArenaTeamInviteCreatedPayload) error + ArenaTeamNativeEvent(payload *CharEventArenaTeamNativeEventPayload) error } type charactersServiceProducerNatsJSON struct { @@ -24,9 +27,20 @@ func NewCharactersServiceProducerNatsJSON(conn *nats.Conn, ver string) Character } func (c *charactersServiceProducerNatsJSON) CharsDisconnectedUnhealthyLB(payload *CharEventCharsDisconnectedUnhealthyGWPayload) error { + if payload.EventTimeUnixNano == 0 { + payload.EventTimeUnixNano = uint64(time.Now().UnixNano()) + } return c.publish(CharEventCharsDisconnectedUnhealthyGW, payload) } +func (c *charactersServiceProducerNatsJSON) ArenaTeamInviteCreated(payload *CharEventArenaTeamInviteCreatedPayload) error { + return c.publish(CharEventArenaTeamInviteCreated, payload) +} + +func (c *charactersServiceProducerNatsJSON) ArenaTeamNativeEvent(payload *CharEventArenaTeamNativeEventPayload) error { + return c.publish(CharEventArenaTeamNativeEvent, payload) +} + func (c *charactersServiceProducerNatsJSON) publish(e CharactersServiceEvent, payload interface{}) error { msg := EventToSendGenericPayload{ Version: c.ver, diff --git a/shared/events/producer-gateway.go b/shared/events/producer-gateway.go index 299d2c3..e0d93a6 100644 --- a/shared/events/producer-gateway.go +++ b/shared/events/producer-gateway.go @@ -2,15 +2,18 @@ package events import ( "encoding/json" + "time" "github.com/nats-io/nats.go" ) //go:generate mockery --name=GatewayProducer type GatewayProducer interface { + GatewayStarted(payload *GWEventGatewayStartedPayload) error CharacterLoggedIn(payload *GWEventCharacterLoggedInPayload) error CharacterLoggedOut(payload *GWEventCharacterLoggedOutPayload) error CharactersUpdates(payload *GWEventCharactersUpdatesPayload) error + GuildCreated(payload *GWEventGuildCreatedPayload) error } type gatewayProducerNatsJSON struct { @@ -30,24 +33,48 @@ func NewGatewayProducerNatsJSON(conn *nats.Conn, ver string, realmID uint32, gat } } +func (p *gatewayProducerNatsJSON) GatewayStarted(payload *GWEventGatewayStartedPayload) error { + payload.RealmID = p.RealmID + payload.GatewayID = p.ID + if payload.StartedAtMs == 0 { + payload.StartedAtMs = uint64(time.Now().UnixMilli()) + } + return p.publish(GWEventGatewayStarted, payload) +} + func (p *gatewayProducerNatsJSON) CharacterLoggedIn(payload *GWEventCharacterLoggedInPayload) error { payload.RealmID = p.RealmID payload.GatewayID = p.ID + if payload.EventTimeUnixNano == 0 { + payload.EventTimeUnixNano = uint64(time.Now().UnixNano()) + } return p.publish(GWEventCharacterLoggedIn, payload) } func (p *gatewayProducerNatsJSON) CharacterLoggedOut(payload *GWEventCharacterLoggedOutPayload) error { payload.RealmID = p.RealmID payload.GatewayID = p.ID + if payload.EventTimeUnixNano == 0 { + payload.EventTimeUnixNano = uint64(time.Now().UnixNano()) + } return p.publish(GWEventCharacterLoggedOut, payload) } func (p *gatewayProducerNatsJSON) CharactersUpdates(payload *GWEventCharactersUpdatesPayload) error { payload.RealmID = p.RealmID payload.GatewayID = p.ID + if payload.EventTimeUnixNano == 0 { + payload.EventTimeUnixNano = uint64(time.Now().UnixNano()) + } return p.publish(GWEventCharactersUpdates, payload) } +func (p *gatewayProducerNatsJSON) GuildCreated(payload *GWEventGuildCreatedPayload) error { + payload.RealmID = p.RealmID + payload.GatewayID = p.ID + return p.publish(GWEventGuildCreated, payload) +} + func (p *gatewayProducerNatsJSON) publish(e GatewayEvent, payload interface{}) error { msg := EventToSendGenericPayload{ Version: p.ver, diff --git a/shared/events/producer-group.go b/shared/events/producer-group.go index ef1fa65..b371ead 100644 --- a/shared/events/producer-group.go +++ b/shared/events/producer-group.go @@ -13,6 +13,9 @@ type GroupServiceProducer interface { // InviteCreated publishes an event for an invite being created. InviteCreated(payload *GroupEventInviteCreatedPayload) error + // InviteDeclined publishes an event for an invite being declined. + InviteDeclined(payload *GroupEventInviteDeclinedPayload) error + // GroupCreated publishes an event for a group being created. GroupCreated(payload *GroupEventGroupCreatedPayload) error @@ -45,6 +48,16 @@ type GroupServiceProducer interface { // SendChatMessage publishes an event for a new chat message in a group or raid. SendChatMessage(payload *GroupEventNewMessagePayload) error + + GroupReadyCheckStarted(payload *GroupEventReadyCheckStartedPayload) error + GroupReadyCheckMemberState(payload *GroupEventReadyCheckMemberStatePayload) error + GroupReadyCheckFinished(payload *GroupEventReadyCheckFinishedPayload) error + GroupMemberSubGroupChanged(payload *GroupEventMemberSubGroupChangedPayload) error + GroupMemberFlagsChanged(payload *GroupEventMemberFlagsChangedPayload) error + GroupMemberStateChanged(payload *GroupEventMemberStateChangedPayload) error + GroupMemberStatesChanged(payload *GroupEventMemberStatesChangedPayload) error + GroupInstanceResetRequest(payload *GroupEventInstanceResetRequestPayload) error + GroupInstanceBindExtensionRequest(payload *GroupEventInstanceBindExtensionRequestPayload) error } // groupServiceProducerNatsJSON implements the GroupServiceProducer interface using NATS as the underlying message broker. @@ -65,6 +78,10 @@ func (s *groupServiceProducerNatsJSON) InviteCreated(payload *GroupEventInviteCr return s.publish(GroupEventInviteCreated, payload) } +func (s *groupServiceProducerNatsJSON) InviteDeclined(payload *GroupEventInviteDeclinedPayload) error { + return s.publish(GroupEventInviteDeclined, payload) +} + func (s *groupServiceProducerNatsJSON) GroupCreated(payload *GroupEventGroupCreatedPayload) error { return s.publish(GroupEventGroupCreated, payload) } @@ -123,3 +140,39 @@ func (s *groupServiceProducerNatsJSON) publish(e GroupServiceEvent, payload inte return s.conn.Publish(e.SubjectName(), d) } + +func (s *groupServiceProducerNatsJSON) GroupReadyCheckStarted(payload *GroupEventReadyCheckStartedPayload) error { + return s.publish(GroupEventGroupReadyCheckStarted, payload) +} + +func (s *groupServiceProducerNatsJSON) GroupReadyCheckMemberState(payload *GroupEventReadyCheckMemberStatePayload) error { + return s.publish(GroupEventGroupReadyCheckMemberState, payload) +} + +func (s *groupServiceProducerNatsJSON) GroupReadyCheckFinished(payload *GroupEventReadyCheckFinishedPayload) error { + return s.publish(GroupEventGroupReadyCheckFinished, payload) +} + +func (s *groupServiceProducerNatsJSON) GroupMemberSubGroupChanged(payload *GroupEventMemberSubGroupChangedPayload) error { + return s.publish(GroupEventGroupMemberSubGroupChanged, payload) +} + +func (s *groupServiceProducerNatsJSON) GroupMemberFlagsChanged(payload *GroupEventMemberFlagsChangedPayload) error { + return s.publish(GroupEventGroupMemberFlagsChanged, payload) +} + +func (s *groupServiceProducerNatsJSON) GroupMemberStateChanged(payload *GroupEventMemberStateChangedPayload) error { + return s.publish(GroupEventGroupMemberStateChanged, payload) +} + +func (s *groupServiceProducerNatsJSON) GroupMemberStatesChanged(payload *GroupEventMemberStatesChangedPayload) error { + return s.publish(GroupEventGroupMemberStatesChanged, payload) +} + +func (s *groupServiceProducerNatsJSON) GroupInstanceResetRequest(payload *GroupEventInstanceResetRequestPayload) error { + return s.publish(GroupEventGroupInstanceResetRequest, payload) +} + +func (s *groupServiceProducerNatsJSON) GroupInstanceBindExtensionRequest(payload *GroupEventInstanceBindExtensionRequestPayload) error { + return s.publish(GroupEventGroupInstanceBindExtensionRequest, payload) +} diff --git a/shared/events/producer-guild.go b/shared/events/producer-guild.go index 446c5e1..e65681a 100644 --- a/shared/events/producer-guild.go +++ b/shared/events/producer-guild.go @@ -28,6 +28,9 @@ type GuildServiceProducer interface { GuildInfoUpdated(payload *GuildEventGuildInfoUpdatedPayload) error NewMessage(payload *GuildEventNewMessagePayload) error + + PetitionOffered(payload *GuildEventPetitionOfferedPayload) error + PetitionSigned(payload *GuildEventPetitionSignedPayload) error } type guildServiceProducerNatsJSON struct { @@ -98,6 +101,14 @@ func (s *guildServiceProducerNatsJSON) NewMessage(payload *GuildEventNewMessageP return s.publish(GuildEventNewMessage, payload) } +func (s *guildServiceProducerNatsJSON) PetitionOffered(payload *GuildEventPetitionOfferedPayload) error { + return s.publish(GuildEventPetitionOffered, payload) +} + +func (s *guildServiceProducerNatsJSON) PetitionSigned(payload *GuildEventPetitionSignedPayload) error { + return s.publish(GuildEventPetitionSigned, payload) +} + func (s *guildServiceProducerNatsJSON) publish(e GuildServiceEvent, payload interface{}) error { msg := EventToSendGenericPayload{ Version: s.ver, diff --git a/shared/events/producer-matchmaking.go b/shared/events/producer-matchmaking.go index a87115f..8c5023c 100644 --- a/shared/events/producer-matchmaking.go +++ b/shared/events/producer-matchmaking.go @@ -11,6 +11,8 @@ type MatchmakingServiceProducer interface { JoinedQueue(payload *MatchmakingEventPlayersQueuedPayload) error InvitedToBGOrArena(payload *MatchmakingEventPlayersInvitedPayload) error InviteExpired(payload *MatchmakingEventPlayersInviteExpiredPayload) error + LfgStatusChanged(payload *MatchmakingEventLfgStatusChangedPayload) error + LfgProposalAccepted(payload *MatchmakingEventLfgProposalAcceptedPayload) error } type matchmakingServiceProducerNatsJSON struct { @@ -37,6 +39,14 @@ func (c *matchmakingServiceProducerNatsJSON) InviteExpired(payload *MatchmakingE return c.publish(MatchmakingEventPlayersInviteExpired, payload) } +func (c *matchmakingServiceProducerNatsJSON) LfgStatusChanged(payload *MatchmakingEventLfgStatusChangedPayload) error { + return c.publish(MatchmakingEventLfgStatusChanged, payload) +} + +func (c *matchmakingServiceProducerNatsJSON) LfgProposalAccepted(payload *MatchmakingEventLfgProposalAcceptedPayload) error { + return c.publish(MatchmakingEventLfgProposalAccepted, payload) +} + func (c *matchmakingServiceProducerNatsJSON) publish(e MatchmakingServiceEvent, payload interface{}) error { msg := EventToSendGenericPayload{ Version: c.ver, diff --git a/shared/events/producer-serversregistry.go b/shared/events/producer-serversregistry.go index bcf23e1..4384bbd 100644 --- a/shared/events/producer-serversregistry.go +++ b/shared/events/producer-serversregistry.go @@ -2,6 +2,7 @@ package events import ( "encoding/json" + "time" "github.com/nats-io/nats.go" ) @@ -13,6 +14,8 @@ type ServerRegistryProducer interface { GSMapsReassigned(payload *ServerRegistryEventGSMapsReassignedPayload) error GSAdded(payload *ServerRegistryEventGSAddedPayload) error GSRemoved(payload *ServerRegistryEventGSRemovedPayload) error + MatchmakingRemovedUnhealthy(payload *ServerRegistryEventMatchmakingRemovedUnhealthyPayload) error + MatchmakingRecovered(payload *ServerRegistryEventMatchmakingRecoveredPayload) error } type serverRegistryProducerNatsJSON struct { @@ -32,6 +35,9 @@ func (s serverRegistryProducerNatsJSON) GatewayAdded(payload *ServerRegistryEven } func (s serverRegistryProducerNatsJSON) GatewayRemovedUnhealthy(payload *ServerRegistryEventGWRemovedUnhealthyPayload) error { + if payload.EventTimeUnixNano == 0 { + payload.EventTimeUnixNano = uint64(time.Now().UnixNano()) + } return s.publish(ServerRegistryEventGWRemovedUnhealthy, payload) } @@ -47,6 +53,14 @@ func (s serverRegistryProducerNatsJSON) GSRemoved(payload *ServerRegistryEventGS return s.publish(ServerRegistryEventGSRemoved, payload) } +func (s serverRegistryProducerNatsJSON) MatchmakingRemovedUnhealthy(payload *ServerRegistryEventMatchmakingRemovedUnhealthyPayload) error { + return s.publish(ServerRegistryEventMatchmakingRemovedUnhealthy, payload) +} + +func (s serverRegistryProducerNatsJSON) MatchmakingRecovered(payload *ServerRegistryEventMatchmakingRecoveredPayload) error { + return s.publish(ServerRegistryEventMatchmakingRecovered, payload) +} + func (s *serverRegistryProducerNatsJSON) publish(e ServerRegistryEvent, payload interface{}) error { msg := EventToSendGenericPayload{ Version: s.ver, diff --git a/shared/groupstatetrace/trace.go b/shared/groupstatetrace/trace.go new file mode 100644 index 0000000..c7944f3 --- /dev/null +++ b/shared/groupstatetrace/trace.go @@ -0,0 +1,137 @@ +package groupstatetrace + +import ( + "os" + "strconv" + "strings" + "sync" + "time" + + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +const ( + EnvGUIDs = "TC9_GROUP_STATE_TRACE_GUIDS" + EnvGUIDFile = "TC9_GROUP_STATE_TRACE_FILE" + Message = "TC9_GROUP_STATE_TRACE" +) + +type traceConfig struct { + raw string + all bool + guids map[uint64]struct{} +} + +var traceCache = struct { + sync.RWMutex + envRaw string + filePath string + loadedAt time.Time + cfg traceConfig +}{} + +func Enabled(memberGUIDs ...uint64) bool { + return currentConfig().matches(memberGUIDs...) +} + +func Event(logger *zerolog.Logger, stage string, memberGUIDs ...uint64) *zerolog.Event { + cfg := currentConfig() + if !cfg.matches(memberGUIDs...) { + return nil + } + + var event *zerolog.Event + if logger != nil { + event = logger.Info() + } else { + event = log.Info() + } + + return event. + Str("tc9Trace", "group-state"). + Str("traceStage", stage). + Str("traceGUIDs", cfg.raw) +} + +func currentConfig() traceConfig { + envRaw := os.Getenv(EnvGUIDs) + filePath := os.Getenv(EnvGUIDFile) + now := time.Now() + + traceCache.RLock() + if envRaw == traceCache.envRaw && filePath == traceCache.filePath && now.Sub(traceCache.loadedAt) < time.Second { + cfg := traceCache.cfg + traceCache.RUnlock() + return cfg + } + traceCache.RUnlock() + + raw := envRaw + if filePath != "" { + if data, err := os.ReadFile(filePath); err == nil { + raw = strings.Trim(envRaw+","+string(data), ", \t\r\n") + } + } + cfg := parseConfig(raw) + + traceCache.Lock() + traceCache.envRaw = envRaw + traceCache.filePath = filePath + traceCache.loadedAt = now + traceCache.cfg = cfg + traceCache.Unlock() + + return cfg +} + +func parseConfig(raw string) traceConfig { + cfg := traceConfig{ + raw: raw, + guids: map[uint64]struct{}{}, + } + + for _, token := range strings.FieldsFunc(raw, func(r rune) bool { + return r == ',' || r == ';' || r == ' ' || r == '\t' || r == '\n' + }) { + token = strings.TrimSpace(token) + if token == "" { + continue + } + if token == "*" { + cfg.all = true + continue + } + + guid, err := strconv.ParseUint(token, 10, 64) + if err != nil || guid == 0 { + continue + } + cfg.guids[guid] = struct{}{} + } + + return cfg +} + +func (cfg traceConfig) matches(memberGUIDs ...uint64) bool { + if cfg.raw == "" { + return false + } + if cfg.all { + return true + } + if len(cfg.guids) == 0 { + return false + } + + for _, guid := range memberGUIDs { + if guid == 0 { + continue + } + if _, ok := cfg.guids[guid]; ok { + return true + } + } + + return false +} diff --git a/shared/repo/charactersdb.go b/shared/repo/charactersdb.go index 1c805e5..b1dc772 100644 --- a/shared/repo/charactersdb.go +++ b/shared/repo/charactersdb.go @@ -3,6 +3,7 @@ package repo import ( "database/sql" "fmt" + "sort" ) // PreparedStatement represents prepared statement with id. @@ -16,6 +17,7 @@ type PreparedStatement interface { type CharactersDB interface { DBByRealm(realmID uint32) *sql.DB + RealmIDs() []uint32 SetDBForRealm(realmID uint32, db *sql.DB) PreparedStatement(realm uint32, stmt PreparedStatement) *sql.Stmt @@ -42,6 +44,17 @@ func (c *characterDBImpl) DBByRealm(realmID uint32) *sql.DB { return c.dbByReam[realmID].db } +func (c *characterDBImpl) RealmIDs() []uint32 { + realmIDs := make([]uint32, 0, len(c.dbByReam)) + for realmID := range c.dbByReam { + realmIDs = append(realmIDs, realmID) + } + sort.Slice(realmIDs, func(i, j int) bool { + return realmIDs[i] < realmIDs[j] + }) + return realmIDs +} + func (c *characterDBImpl) SetDBForRealm(realmID uint32, db *sql.DB) { c.dbByReam[realmID] = dbWithPreparedStmts{ db: db, diff --git a/shared/wow/arena/team_id.go b/shared/wow/arena/team_id.go new file mode 100644 index 0000000..13bba01 --- /dev/null +++ b/shared/wow/arena/team_id.go @@ -0,0 +1,24 @@ +package arena + +const ( + teamIDRealmShift = 24 + teamIDLowMask = uint32(0x00FFFFFF) +) + +func NewCrossrealmTeamID(realmID uint16, teamID uint32) uint32 { + if realmID == 0 || teamID == 0 { + return teamID + } + return uint32(realmID)<> teamIDRealmShift +} + +func TeamIDCounter(teamID uint32) uint32 { + if TeamIDRealmID(teamID) == 0 { + return teamID + } + return teamID & teamIDLowMask +} diff --git a/shared/wow/guid/player.go b/shared/wow/guid/player.go index e201aec..41e8c9a 100644 --- a/shared/wow/guid/player.go +++ b/shared/wow/guid/player.go @@ -5,6 +5,18 @@ type PlayerUnwrapped struct { LowGUID LowType } +// Player GUID convention: +// - Persistent ToCloud9 service keys use (realmID, low player DB guid). +// - Same-realm client/AzerothCore player ObjectGuid values are serialized as +// the low DB guid. +// - Foreign-realm client/AzerothCore player ObjectGuid values are serialized +// as realm-scoped player GUIDs: realmID << 32 | low DB guid. +// - Already encoded non-player ObjectGuid values are preserved unchanged. +// +// Use these helpers at service, gateway, and libsidecar boundaries instead of +// open-coding shifts/masks. Do not normalize already serialized client packets +// before forwarding them to the owning worldserver. + func NewPlayerUnwrappedFromRawGUID(g ObjectGuid) PlayerUnwrapped { // TODO: we probably should validate guid here return PlayerUnwrapped{ @@ -23,3 +35,58 @@ func NewPlayerUnwrapped(realm uint16, low uint32) PlayerUnwrapped { func (u PlayerUnwrapped) Wrap() ObjectGuid { return NewCrossrealmPlayerGUID(u.RealmID, u.LowGUID) } + +func PlayerRealmIDOrDefault(defaultRealmID uint32, playerGUID uint64) uint32 { + if playerGUID == 0 || playerGUID>>48 != 0 { + return defaultRealmID + } + if realmID := uint32((playerGUID >> 32) & 0xffff); realmID != 0 { + return realmID + } + return defaultRealmID +} + +func PlayerLowGUID(playerGUID uint64) uint64 { + if playerGUID == 0 || playerGUID>>48 != 0 { + return playerGUID + } + if playerGUID>>32 == 0 { + return playerGUID + } + return playerGUID & 0xffffffff +} + +func PlayerGUIDForRealm(groupRealmID, playerRealmID uint32, playerGUID uint64) uint64 { + if playerGUID == 0 || playerGUID>>48 != 0 { + return playerGUID + } + + lowGUID := PlayerLowGUID(playerGUID) + if playerRealmID == 0 { + playerRealmID = PlayerRealmIDOrDefault(groupRealmID, playerGUID) + } + if playerRealmID == 0 || playerRealmID == groupRealmID { + return lowGUID + } + + return NewCrossrealmPlayerGUID(uint16(playerRealmID), LowType(lowGUID)).GetRawValue() +} + +func NormalizePlayerGUIDForRealm(defaultRealmID uint32, playerGUID uint64) uint64 { + if playerGUID == 0 || playerGUID>>48 != 0 { + return playerGUID + } + realmID := uint32((playerGUID >> 32) & 0xffff) + if realmID == 0 || realmID == defaultRealmID { + return PlayerLowGUID(playerGUID) + } + return playerGUID +} + +func SamePlayer(defaultRealmA uint32, playerA uint64, defaultRealmB uint32, playerB uint64) bool { + if playerA == 0 || playerB == 0 { + return false + } + return PlayerRealmIDOrDefault(defaultRealmA, playerA) == PlayerRealmIDOrDefault(defaultRealmB, playerB) && + PlayerLowGUID(playerA) == PlayerLowGUID(playerB) +} diff --git a/shared/wow/power.go b/shared/wow/power.go new file mode 100644 index 0000000..1974180 --- /dev/null +++ b/shared/wow/power.go @@ -0,0 +1,50 @@ +package wow + +const ( + ClassWarrior uint8 = 1 + ClassRogue uint8 = 4 + ClassDeathKnight uint8 = 6 + + PowerTypeMana uint8 = 0 + PowerTypeRage uint8 = 1 + PowerTypeEnergy uint8 = 3 + PowerTypeRunicPower uint8 = 6 +) + +func FixedPrimaryPowerTypeForClass(classID uint8) (uint8, bool) { + switch classID { + case ClassWarrior: + return PowerTypeRage, true + case ClassRogue: + return PowerTypeEnergy, true + case ClassDeathKnight: + return PowerTypeRunicPower, true + default: + return PowerTypeMana, false + } +} + +func DefaultMaxPowerForClass(classID uint8) uint32 { + switch classID { + case ClassWarrior, ClassDeathKnight: + return 1000 + case ClassRogue: + return 100 + default: + return 0 + } +} + +func IsFixedClassInactivePowerType(classID, powerType uint8) bool { + expectedPowerType, fixed := FixedPrimaryPowerTypeForClass(classID) + if !fixed || powerType == expectedPowerType { + return false + } + + switch powerType { + case PowerTypeRage, PowerTypeEnergy, PowerTypeRunicPower: + return true + default: + return false + } +} From 6e5e093652664bf6b8ac4929dfab6890dcc5da26 Mon Sep 17 00:00:00 2001 From: VG-Prog Date: Fri, 22 May 2026 21:45:36 +0200 Subject: [PATCH 02/11] feat(Cluster/Discovery): Harden registry readiness Wire service discovery, map readiness, stale-safe health and metrics observers, degraded game-server health handling, gateway-scoped cleanup, and shared GUID allocation support. Registry and health code now distinguish world-loop degraded state from process or transport death while preserving live map ownership. --- apps/authserver/crypto/srp6/srp6.go | 31 +++- apps/authserver/service/realm.go | 26 ++- apps/authserver/session/authsession.go | 54 +++++- apps/guidserver/service/guid.go | 2 +- .../cmd/servers-registry/main.go | 24 ++- apps/servers-registry/config/config.go | 12 ++ .../mapbalancing/binpack/distributor.go | 24 +-- apps/servers-registry/repo/game-server.go | 6 +- .../repo/game-server_inmem.go | 7 + .../repo/game-server_redis.go | 55 ++++-- .../repo/gateway-server_redis.go | 70 ++++++-- .../server/registry-server-debug-logger.go | 5 + .../server/registry-server.go | 64 ++++++- apps/servers-registry/service/game-server.go | 104 ++++++++++- .../service/matchmaking-monitor.go | 165 ++++++++++++++++++ .../service/matchmaking-server.go | 149 ++++++++++++++++ shared/healthandmetrics/health-checker.go | 103 ++++++++++- shared/healthandmetrics/metrics-reader.go | 46 ++++- shared/healthandmetrics/server.go | 51 +++++- 19 files changed, 894 insertions(+), 104 deletions(-) create mode 100644 apps/servers-registry/service/matchmaking-monitor.go create mode 100644 apps/servers-registry/service/matchmaking-server.go diff --git a/apps/authserver/crypto/srp6/srp6.go b/apps/authserver/crypto/srp6/srp6.go index 400f37e..4206e24 100644 --- a/apps/authserver/crypto/srp6/srp6.go +++ b/apps/authserver/crypto/srp6/srp6.go @@ -30,7 +30,10 @@ type SRP6 struct { } func (s *SRP6) DataForClient() (B []byte, g []byte, N []byte, _s []byte) { - B, g, N, _s = s._B, bigIntToBytesLittleEndian(_g), bigIntToBytesLittleEndian(_N), s._s + B = fixedLittleEndianBytes(s._B, 32) + g = bigIntToBytesLittleEndian(_g) + N = fixedLittleEndianBytes(bigIntToBytesLittleEndian(_N), 32) + _s = fixedLittleEndianBytes(s._s, 32) return } @@ -41,7 +44,7 @@ func NewSRP(username string, salt []byte, verifier []byte) *SRP6 { b = randBytesProvider(b) _b := bigIntFromLittleEndian(b) - usrHash := sha1.Sum([]byte(username)) + usrHash := sha1.Sum([]byte(normalizeAccountName(username))) return &SRP6{ _I: usrHash[:], @@ -144,10 +147,20 @@ func _B(b, v *big.Int) []byte { } func ReconnectChallengeValid(username string, R1, R2, reconnectProof, K []byte) bool { - hash := sha1.Sum(slices.AppendBytes([]byte(username), R1, reconnectProof, K)) + hash := sha1.Sum(slices.AppendBytes([]byte(normalizeAccountName(username)), R1, reconnectProof, K)) return slices.SameBytes(R2, hash[:]) } +func normalizeAccountName(username string) string { + normalized := []rune(username) + for i, r := range normalized { + if r >= 'a' && r <= 'z' { + normalized[i] = r - ('a' - 'A') + } + } + return string(normalized) +} + func switchEndian(b []byte) []byte { r := make([]byte, len(b)) copy(r, b) @@ -161,6 +174,18 @@ func bigIntToBytesLittleEndian(i *big.Int) []byte { return switchEndian(i.Bytes()) } +func fixedLittleEndianBytes(b []byte, size int) []byte { + if len(b) == size { + out := make([]byte, size) + copy(out, b) + return out + } + + out := make([]byte, size) + copy(out, b) + return out +} + func bigIntFromLittleEndian(b []byte) *big.Int { u := &big.Int{} u.SetBytes(switchEndian(b)) diff --git a/apps/authserver/service/realm.go b/apps/authserver/service/realm.go index bd31f68..7eb53ed 100644 --- a/apps/authserver/service/realm.go +++ b/apps/authserver/service/realm.go @@ -2,6 +2,7 @@ package service import ( "context" + "time" "github.com/walkline/ToCloud9/apps/authserver/repo" "github.com/walkline/ToCloud9/gen/servers-registry/pb" @@ -12,6 +13,10 @@ const ( RealmFlagVersionMismatch = 0x1 RealmFlagOffline = 0x2 RealmFlagRecommended = 0x20 + + offlineRealmAddress = "0.0.0.0:0" + + realmGatewayLookupTimeout = 2 * time.Second ) type RealmListItem struct { @@ -54,17 +59,23 @@ func (r *realmServiceImpl) RealmListForAccount(ctx context.Context, account *rep realmIDs = append(realmIDs, realm.ID) } - gatewaysResp, err := r.servRegistry.GatewaysForRealms(ctx, &pb.GatewaysForRealmsRequest{ + gatewayCtx := ctx + var cancel context.CancelFunc + if deadline, ok := ctx.Deadline(); !ok || time.Until(deadline) > realmGatewayLookupTimeout { + gatewayCtx, cancel = context.WithTimeout(ctx, realmGatewayLookupTimeout) + defer cancel() + } + + gatewaysResp, err := r.servRegistry.GatewaysForRealms(gatewayCtx, &pb.GatewaysForRealmsRequest{ Api: "v1.0", RealmIDs: realmIDs, }) - if err != nil { - return nil, err - } gatewaysAddressesMap := map[uint32]string{} - for _, lb := range gatewaysResp.Gateways { - gatewaysAddressesMap[lb.RealmID] = lb.Address + if err == nil && gatewaysResp != nil { + for _, lb := range gatewaysResp.Gateways { + gatewaysAddressesMap[lb.RealmID] = lb.Address + } } chars, err := r.realmRepo.CountCharsPerRealmByAccountID(ctx, account.ID) @@ -80,8 +91,9 @@ func (r *realmServiceImpl) RealmListForAccount(ctx context.Context, account *rep result := []RealmListItem{} for _, realm := range realms { address, found := gatewaysAddressesMap[realm.ID] - if !found { + if !found || address == "" { realm.Flag |= RealmFlagOffline + address = offlineRealmAddress } else { realm.Flag &^= RealmFlagOffline } diff --git a/apps/authserver/session/authsession.go b/apps/authserver/session/authsession.go index 35b2a79..d7d1774 100644 --- a/apps/authserver/session/authsession.go +++ b/apps/authserver/session/authsession.go @@ -6,6 +6,7 @@ import ( "crypto/rand" "encoding/binary" "errors" + "fmt" "io" "io/ioutil" "net" @@ -56,6 +57,15 @@ type CommandData struct { Size int } +const ( + authChallengeLoginSizeOverhead = 30 + maxAuthChallengeLoginLen = 16 +) + +// Matches AzerothCore authserver's VersionChallenge constant for 3.3.5a auth +// challenge and reconnect challenge responses. +var authVersionChallenge = [16]byte{0xBA, 0xA3, 0x1E, 0x99, 0xA0, 0x0B, 0x21, 0x57, 0xFC, 0x37, 0x3F, 0xB3, 0x69, 0xCD, 0xD2, 0xF1} + var Commands = map[Command]Status{ CommandLogonChallenge: StatusChallenge, CommandLogonProof: StatusLogonProof, @@ -172,10 +182,13 @@ func (s *AuthSession) HandleReconnectChallenge() error { if err != nil { return err } + if err = validateAuthChallengeLogin(d.Size, d.ILen); err != nil { + return err + } login := make([]byte, d.ILen) - _, err = s.conn.Read(login) + _, err = io.ReadFull(s.conn, login) if err != nil { return err } @@ -189,6 +202,10 @@ func (s *AuthSession) HandleReconnectChallenge() error { if err != nil { return err } + if s.account == nil || len(s.account.SessionKeyAuth) == 0 { + s.logger.Debug().Msg("Reconnect challenge rejected: account not found or missing session key") + return s.Write(CommandReconnectChallenge, AuthResultUnkAccount) + } s.reconnectProof = make([]byte, 16) _, err = rand.Read(s.reconnectProof) @@ -202,7 +219,7 @@ func (s *AuthSession) HandleReconnectChallenge() error { CommandReconnectChallenge, AuthResultSuccess, s.reconnectProof, - []byte{0xBA, 0xA3, 0x1E, 0x99, 0xA0, 0x0B, 0x21, 0x57, 0xFC, 0x37, 0x3F, 0xB3, 0x69, 0xCD, 0xD2, 0xF1}, + authVersionChallenge[:], ) if err != nil { return err @@ -269,10 +286,13 @@ func (s *AuthSession) HandleLogonChallenge() error { if err != nil { return err } + if err = validateAuthChallengeLogin(d.Size, d.ILen); err != nil { + return err + } login := make([]byte, d.ILen) - _, err = s.conn.Read(login) + _, err = io.ReadFull(s.conn, login) if err != nil { return err } @@ -288,7 +308,7 @@ func (s *AuthSession) HandleLogonChallenge() error { } if s.account == nil { - return s.Write(CommandLogonProof, AuthResultUnkAccount, uint16(0)) + return s.Write([]byte{byte(CommandLogonChallenge), 0}, AuthResultUnkAccount) } s.srp = srp6.NewSRP(string(login), s.account.Salt, s.account.Verifier) @@ -303,7 +323,7 @@ func (s *AuthSession) HandleLogonChallenge() error { byte(32), N, _s, - []byte{0xBA, 0xA3, 0x1E, 0x99, 0xA0, 0x0B, 0x21, 0x57, 0xFC, 0x37, 0x3F, 0xB3, 0x69, 0xCD, 0xD2, 0xF1}, + authVersionChallenge[:], byte(0), ) if err != nil { @@ -441,8 +461,18 @@ func (s *AuthSession) Write(v ...interface{}) error { return s.write(s.conn, v...) } +func validateAuthChallengeLogin(size uint16, loginLen uint8) error { + if loginLen == 0 || loginLen > maxAuthChallengeLoginLen { + return fmt.Errorf("invalid auth challenge login length: %d", loginLen) + } + if int(size) != authChallengeLoginSizeOverhead+int(loginLen) { + return fmt.Errorf("invalid auth challenge size: %d for login length %d", size, loginLen) + } + return nil +} + func (s *AuthSession) write(writer io.Writer, v ...interface{}) error { - var err error + packet := new(bytes.Buffer) for i := range v { d := v[i] switch d.(type) { @@ -450,10 +480,20 @@ func (s *AuthSession) write(writer io.Writer, v ...interface{}) error { d = append([]byte(d.(string)), 0) } - err = binary.Write(writer, binary.LittleEndian, d) + err := binary.Write(packet, binary.LittleEndian, d) if err != nil { return err } } + if packet.Len() == 0 { + return nil + } + n, err := writer.Write(packet.Bytes()) + if err != nil { + return err + } + if n != packet.Len() { + return io.ErrShortWrite + } return nil } diff --git a/apps/guidserver/service/guid.go b/apps/guidserver/service/guid.go index a419710..3ad9a50 100644 --- a/apps/guidserver/service/guid.go +++ b/apps/guidserver/service/guid.go @@ -87,7 +87,7 @@ func NewGuidService(ctx context.Context, mysql repo.MaxGuidProvider, redisStorag return nil, err } - err = redisStorage.SetMaxGuidForItems(ctx, realmID, max) + err = redisStorage.SetMaxGuidForInstances(ctx, realmID, max) if err != nil { return nil, err } diff --git a/apps/servers-registry/cmd/servers-registry/main.go b/apps/servers-registry/cmd/servers-registry/main.go index 62220c9..9a6a3b4 100644 --- a/apps/servers-registry/cmd/servers-registry/main.go +++ b/apps/servers-registry/cmd/servers-registry/main.go @@ -26,6 +26,8 @@ import ( "github.com/walkline/ToCloud9/shared/healthandmetrics" ) +const gameServerMetricsReadTimeout = 6 * time.Second + func main() { mainContext, cancel := context.WithCancel(context.Background()) defer cancel() @@ -69,9 +71,10 @@ func main() { healthChecker := healthandmetrics.NewHealthChecker(time.Second*4, 4, healthandmetrics.NewHttpHealthCheckProcessor(time.Second*15)) go healthChecker.Start() - metricsConsumer := healthandmetrics.NewMetricsConsumer(time.Second*5, 3, healthandmetrics.NewHttpPrometheusMetricsReader(time.Second)) + metricsConsumer := healthandmetrics.NewMetricsConsumer(time.Second*5, 3, healthandmetrics.NewHttpPrometheusMetricsReader(gameServerMetricsReadTimeout)) go metricsConsumer.Start() + serverRegistryProducer := events.NewServerRegistryProducerNatsJSON(nc, "0.0.1") supportedRealms := conf.RealmsID gameServersService, err := service.NewGameServer( mainContext, @@ -79,7 +82,7 @@ func main() { healthChecker, metricsConsumer, binpack.NewBinPackBalancer(binpack.DefaultMapsWeight), // TODO: implement providing custom maps weight list. - events.NewServerRegistryProducerNatsJSON(nc, "0.0.1"), + serverRegistryProducer, supportedRealms, ) if err != nil { @@ -91,14 +94,27 @@ func main() { repo.NewGatewayRedisRepo(rdb), healthChecker, metricsConsumer, - events.NewServerRegistryProducerNatsJSON(nc, "0.0.1"), + serverRegistryProducer, []uint32{1}, ) if err != nil { log.Fatal().Err(err).Msg("can't create gateway service") } - registryService := server.NewServersRegistry(gameServersService, gatewayService) + matchmakingService := service.NewMatchmaking(healthChecker, serverRegistryProducer) + + if conf.MatchmakingServiceHealthCheckAddress != "" { + matchmakingMonitor := service.NewMatchmakingHealthMonitor( + conf.MatchmakingServiceAddress, + conf.MatchmakingServiceHealthCheckAddress, + time.Duration(conf.MatchmakingServiceHealthCheckIntervalMs)*time.Millisecond, + time.Duration(conf.MatchmakingServiceHealthCheckTimeoutMs)*time.Millisecond, + serverRegistryProducer, + ) + go matchmakingMonitor.Start(mainContext) + } + + registryService := server.NewServersRegistry(gameServersService, gatewayService, matchmakingService) if conf.LogLevel == zerolog.DebugLevel { registryService = server.NewServersRegistryDebugLoggerMiddleware(registryService, log.Logger) } diff --git a/apps/servers-registry/config/config.go b/apps/servers-registry/config/config.go index 4d31277..32ec969 100644 --- a/apps/servers-registry/config/config.go +++ b/apps/servers-registry/config/config.go @@ -17,6 +17,18 @@ type Config struct { // NatsURL is nats connection url NatsURL string `yaml:"natsUrl" env:"NATS_URL" env-default:"nats://nats:4222"` + // MatchmakingServiceAddress is address of matchmaking grpc service. It is used only for event context. + MatchmakingServiceAddress string `yaml:"matchmakingServiceAddress" env:"MATCHMAKING_SERVICE_ADDRESS" env-default:"localhost:8994"` + + // MatchmakingServiceHealthCheckAddress is address for matchmaking health checks. Empty disables monitoring. + MatchmakingServiceHealthCheckAddress string `yaml:"matchmakingServiceHealthCheckAddress" env:"MATCHMAKING_SERVICE_HEALTH_CHECK_ADDRESS" env-default:""` + + // MatchmakingServiceHealthCheckIntervalMs is the registry polling cadence for matchmaking health. + MatchmakingServiceHealthCheckIntervalMs uint32 `yaml:"matchmakingServiceHealthCheckIntervalMs" env:"MATCHMAKING_SERVICE_HEALTH_CHECK_INTERVAL_MS" env-default:"4000"` + + // MatchmakingServiceHealthCheckTimeoutMs is the http timeout for matchmaking health probes. + MatchmakingServiceHealthCheckTimeoutMs uint32 `yaml:"matchmakingServiceHealthCheckTimeoutMs" env:"MATCHMAKING_SERVICE_HEALTH_CHECK_TIMEOUT_MS" env-default:"15000"` + // RealmsIDs is id of realms that the system supports. RealmsID []uint32 `yaml:"realmsID" env:"REALMs_ID" env-default:"1"` } diff --git a/apps/servers-registry/mapbalancing/binpack/distributor.go b/apps/servers-registry/mapbalancing/binpack/distributor.go index 7b27bf1..b63580c 100644 --- a/apps/servers-registry/mapbalancing/binpack/distributor.go +++ b/apps/servers-registry/mapbalancing/binpack/distributor.go @@ -94,6 +94,9 @@ func (k *binpackBalancer) greedyBinPackBalancer(weights MapsWeight, servers []re } sort.Slice(mw, func(i, j int) bool { + if mw[i].weight == mw[j].weight { + return mw[i].mapID < mw[j].mapID + } return mw[i].weight > mw[j].weight }) @@ -112,25 +115,14 @@ func (k *binpackBalancer) greedyBinPackBalancer(weights MapsWeight, servers []re } } - if len(packing) > len(servers) { - lenDiff := len(servers) - len(packing) - for i := 0; i < lenDiff; i++ { - - for serversItr, j := 0, 0; j < lenDiff; j++ { - if serversItr >= len(servers) { - serversItr = 0 - } - - servers[serversItr].AssignedMapsToHandle = append(servers[serversItr].AssignedMapsToHandle, packing[i][j].mapID) - serversItr++ - } + for i := range packing { + serverIndex := i + if serverIndex >= len(servers) { + serverIndex = i % len(servers) } - packing = packing[:len(servers)] - } - for i := range packing { for j := range packing[i] { - servers[i].AssignedMapsToHandle = append(servers[i].AssignedMapsToHandle, packing[i][j].mapID) + servers[serverIndex].AssignedMapsToHandle = append(servers[serverIndex].AssignedMapsToHandle, packing[i][j].mapID) } } diff --git a/apps/servers-registry/repo/game-server.go b/apps/servers-registry/repo/game-server.go index 59c10ec..1b36f1c 100644 --- a/apps/servers-registry/repo/game-server.go +++ b/apps/servers-registry/repo/game-server.go @@ -66,9 +66,9 @@ func (g *GameServer) IsAllMapsAvailable() bool { func (g *GameServer) Copy() GameServer { cp := *g - copy(cp.AvailableMaps, g.AvailableMaps) - copy(cp.AssignedMapsToHandle, g.AssignedMapsToHandle) - copy(cp.AssignedButPendingMaps, g.AssignedButPendingMaps) + cp.AvailableMaps = append([]uint32(nil), g.AvailableMaps...) + cp.AssignedMapsToHandle = append([]uint32(nil), g.AssignedMapsToHandle...) + cp.AssignedButPendingMaps = append([]uint32(nil), g.AssignedButPendingMaps...) return cp } diff --git a/apps/servers-registry/repo/game-server_inmem.go b/apps/servers-registry/repo/game-server_inmem.go index 743ae09..8ae0657 100644 --- a/apps/servers-registry/repo/game-server_inmem.go +++ b/apps/servers-registry/repo/game-server_inmem.go @@ -22,6 +22,13 @@ func (g *gameServerInMemRepo) Upsert(ctx context.Context, server *GameServer) er server.ID = server.Address } + for i := range g.storage { + if g.storage[i].ID == server.ID { + g.storage[i] = *server + return nil + } + } + g.storage = append(g.storage, *server) return nil diff --git a/apps/servers-registry/repo/game-server_redis.go b/apps/servers-registry/repo/game-server_redis.go index ba9c864..08ad6c5 100644 --- a/apps/servers-registry/repo/game-server_redis.go +++ b/apps/servers-registry/repo/game-server_redis.go @@ -27,28 +27,34 @@ func (g *gameServerRedisRepo) Upsert(ctx context.Context, server *GameServer) er server.ID = g.generateID(server.Address) } - d, err := json.Marshal(server) + key := g.key(server.ID) + previous, err := g.serverByKey(ctx, key) if err != nil { return err } - key := g.key(server.ID) - status := g.rdb.Set(ctx, key, d, 0) - if status.Err() != nil { - return status.Err() + d, err := json.Marshal(server) + if err != nil { + return err } - res := g.rdb.SAdd(ctx, g.realmIndexKey(server.RealmID, server.IsCrossRealm), key) - if res.Err() != nil { - g.rdb.Del(ctx, key) - return res.Err() + newIndexKey := g.realmIndexKey(server.RealmID, server.IsCrossRealm) + pipe := g.rdb.TxPipeline() + pipe.Set(ctx, key, d, 0) + pipe.SAdd(ctx, newIndexKey, key) + if previous != nil { + if oldIndexKey := g.realmIndexKey(previous.RealmID, previous.IsCrossRealm); oldIndexKey != newIndexKey { + pipe.SRem(ctx, oldIndexKey, key) + } } - return nil + _, err = pipe.Exec(ctx) + return err } func (g *gameServerRedisRepo) Update(ctx context.Context, id string, f func(*GameServer) *GameServer) error { - res := g.rdb.Get(ctx, g.key(id)) + oldKey := g.key(id) + res := g.rdb.Get(ctx, oldKey) if res.Err() != nil { return res.Err() } @@ -58,6 +64,8 @@ func (g *gameServerRedisRepo) Update(ctx context.Context, id string, f func(*Gam if err != nil { return err } + oldRealmID := v.RealmID + oldIsCrossRealm := v.IsCrossRealm newV := f(v) d, err := json.Marshal(newV) @@ -65,9 +73,22 @@ func (g *gameServerRedisRepo) Update(ctx context.Context, id string, f func(*Gam return err } - key := g.key(newV.ID) - status := g.rdb.Set(ctx, key, d, 0) - return status.Err() + newKey := g.key(newV.ID) + newIndexKey := g.realmIndexKey(newV.RealmID, newV.IsCrossRealm) + oldIndexKey := g.realmIndexKey(oldRealmID, oldIsCrossRealm) + + pipe := g.rdb.TxPipeline() + pipe.Set(ctx, newKey, d, 0) + pipe.SAdd(ctx, newIndexKey, newKey) + if oldIndexKey != newIndexKey || oldKey != newKey { + pipe.SRem(ctx, oldIndexKey, oldKey) + } + if oldKey != newKey { + pipe.Del(ctx, oldKey) + } + + _, err = pipe.Exec(ctx) + return err } func (g *gameServerRedisRepo) Remove(ctx context.Context, id string) error { @@ -178,7 +199,11 @@ func (g *gameServerRedisRepo) listForRealmOrCrossRealm(ctx context.Context, real } func (g *gameServerRedisRepo) One(ctx context.Context, id string) (*GameServer, error) { - getRes := g.rdb.Get(ctx, g.key(id)) + return g.serverByKey(ctx, g.key(id)) +} + +func (g *gameServerRedisRepo) serverByKey(ctx context.Context, key string) (*GameServer, error) { + getRes := g.rdb.Get(ctx, key) if getRes.Err() != nil { if errors.Is(getRes.Err(), redis.Nil) { return nil, nil diff --git a/apps/servers-registry/repo/gateway-server_redis.go b/apps/servers-registry/repo/gateway-server_redis.go index d3c39c8..1616cae 100644 --- a/apps/servers-registry/repo/gateway-server_redis.go +++ b/apps/servers-registry/repo/gateway-server_redis.go @@ -25,28 +25,34 @@ func (g *gatewayRedisRepo) Add(ctx context.Context, server *GatewayServer) (*Gat server.HealthCheckAddr = strings.ToLower(server.HealthCheckAddr) server.ID = g.generateID(server.HealthCheckAddr) - d, err := json.Marshal(server) + key := g.key(server.ID) + previous, err := g.gatewayByKey(ctx, key) if err != nil { return nil, err } - key := g.key(server.ID) - status := g.rdb.Set(ctx, key, d, 0) - if status.Err() != nil { - return nil, status.Err() + d, err := json.Marshal(server) + if err != nil { + return nil, err } - res := g.rdb.SAdd(ctx, g.realmIndexKey(server.RealmID), key) - if res.Err() != nil { - g.rdb.Del(ctx, key) - return nil, res.Err() + newIndexKey := g.realmIndexKey(server.RealmID) + pipe := g.rdb.TxPipeline() + pipe.Set(ctx, key, d, 0) + pipe.SAdd(ctx, newIndexKey, key) + if previous != nil { + if oldIndexKey := g.realmIndexKey(previous.RealmID); oldIndexKey != newIndexKey { + pipe.SRem(ctx, oldIndexKey, key) + } } - return server, nil + _, err = pipe.Exec(ctx) + return server, err } func (g *gatewayRedisRepo) Update(ctx context.Context, id string, f func(GatewayServer) GatewayServer) error { - res := g.rdb.Get(ctx, g.key(id)) + oldKey := g.key(id) + res := g.rdb.Get(ctx, oldKey) if res.Err() != nil { return res.Err() } @@ -56,6 +62,7 @@ func (g *gatewayRedisRepo) Update(ctx context.Context, id string, f func(Gateway if err != nil { return err } + oldRealmID := v.RealmID newV := f(*v) d, err := json.Marshal(newV) @@ -63,9 +70,22 @@ func (g *gatewayRedisRepo) Update(ctx context.Context, id string, f func(Gateway return err } - key := g.key(newV.ID) - status := g.rdb.Set(ctx, key, d, 0) - return status.Err() + newKey := g.key(newV.ID) + newIndexKey := g.realmIndexKey(newV.RealmID) + oldIndexKey := g.realmIndexKey(oldRealmID) + + pipe := g.rdb.TxPipeline() + pipe.Set(ctx, newKey, d, 0) + pipe.SAdd(ctx, newIndexKey, newKey) + if oldIndexKey != newIndexKey || oldKey != newKey { + pipe.SRem(ctx, oldIndexKey, oldKey) + } + if oldKey != newKey { + pipe.Del(ctx, oldKey) + } + + _, err = pipe.Exec(ctx) + return err } func (g *gatewayRedisRepo) Remove(ctx context.Context, healthCheckAddress string) error { @@ -125,6 +145,28 @@ func (g *gatewayRedisRepo) ListByRealm(ctx context.Context, realmID uint32) ([]G return result, nil } +func (g *gatewayRedisRepo) gatewayByKey(ctx context.Context, key string) (*GatewayServer, error) { + getRes := g.rdb.Get(ctx, key) + if getRes.Err() != nil { + if errors.Is(getRes.Err(), redis.Nil) { + return nil, nil + } + return nil, getRes.Err() + } + + resBytes, err := getRes.Bytes() + if err != nil { + return nil, err + } + + obj := &GatewayServer{} + if err = json.Unmarshal(resBytes, obj); err != nil { + return nil, err + } + + return obj, nil +} + func (g *gatewayRedisRepo) realmIndexKey(realmID uint32) string { return fmt.Sprintf("realm:%d:gws", realmID) } diff --git a/apps/servers-registry/server/registry-server-debug-logger.go b/apps/servers-registry/server/registry-server-debug-logger.go index c8320ed..d37694c 100644 --- a/apps/servers-registry/server/registry-server-debug-logger.go +++ b/apps/servers-registry/server/registry-server-debug-logger.go @@ -118,3 +118,8 @@ func (s *serversRegistryDebugLoggerMiddleware) ListGatewaysForRealm(ctx context. resp, err = s.realService.ListGatewaysForRealm(ctx, request) return } + +func (s *serversRegistryDebugLoggerMiddleware) RegisterMatchmakingServer(ctx context.Context, request *pb.RegisterMatchmakingServerRequest) (*pb.RegisterMatchmakingServerResponse, error) { + // Logs already inside. + return s.realService.RegisterMatchmakingServer(ctx, request) +} diff --git a/apps/servers-registry/server/registry-server.go b/apps/servers-registry/server/registry-server.go index 8b2afde..65ab13b 100644 --- a/apps/servers-registry/server/registry-server.go +++ b/apps/servers-registry/server/registry-server.go @@ -3,6 +3,7 @@ package server import ( "context" "fmt" + "sort" "strconv" "strings" @@ -20,12 +21,14 @@ type serversRegistry struct { pb.UnimplementedServersRegistryServiceServer gService service.GameServer lbService service.Gateway + mmService service.Matchmaking } -func NewServersRegistry(gService service.GameServer, lbService service.Gateway) pb.ServersRegistryServiceServer { +func NewServersRegistry(gService service.GameServer, lbService service.Gateway, mmService service.Matchmaking) pb.ServersRegistryServiceServer { return &serversRegistry{ gService: gService, lbService: lbService, + mmService: mmService, } } @@ -69,6 +72,7 @@ func (s *serversRegistry) AvailableGameServersForMapAndRealm(ctx context.Context resultServers := make([]*pb.Server, 0, len(servers)) for i := range servers { resultServers = append(resultServers, &pb.Server{ + Id: servers[i].ID, Address: servers[i].Address, RealmID: servers[i].RealmID, IsCrossRealm: servers[i].IsCrossRealm, @@ -174,6 +178,7 @@ func (s *serversRegistry) RandomGameServerForRealm(ctx context.Context, request return &pb.RandomGameServerForRealmResponse{ Api: ver, GameServer: &pb.Server{ + Id: server.ID, Address: server.Address, RealmID: server.RealmID, }, @@ -227,6 +232,7 @@ func (s *serversRegistry) GatewaysForRealms(ctx context.Context, request *pb.Gat } servers = append(servers, &pb.Server{ + Id: server.ID, Address: server.Address, RealmID: server.RealmID, }) @@ -261,6 +267,34 @@ func (s *serversRegistry) ListGatewaysForRealm(ctx context.Context, request *pb. }, nil } +func (s *serversRegistry) RegisterMatchmakingServer(ctx context.Context, request *pb.RegisterMatchmakingServerRequest) (*pb.RegisterMatchmakingServerResponse, error) { + log.Info().Interface("request", request).Msg("New request to add matchmaking server") + + host := "localhost" + if p, ok := peer.FromContext(ctx); ok && p.Addr != nil { + host = removePortFromAddress(p.Addr.String()) + } + if request.PreferredHostName != "" { + host = request.PreferredHostName + } + + matchmakingServer := &service.MatchmakingServer{ + Address: fmt.Sprintf("%s:%d", host, request.ServicePort), + HealthCheckAddr: fmt.Sprintf("%s:%d", host, request.HealthPort), + StartedAtUnixMs: request.StartedAtUnixMs, + } + + server, err := s.mmService.Register(ctx, matchmakingServer) + if err != nil { + return nil, err + } + + return &pb.RegisterMatchmakingServerResponse{ + Api: ver, + Id: server.ID, + }, nil +} + func removePortFromAddress(address string) string { for i := len(address) - 1; i >= 0; i-- { if address[i] == ':' { @@ -272,20 +306,32 @@ func removePortFromAddress(address string) string { } func stringToAvailableMaps(s string) []uint32 { - v := strings.Split(s, ",") - if len(v) == 0 { - return []uint32{} - } + parts := strings.Split(s, ",") + seen := map[uint32]struct{}{} + result := make([]uint32, 0, len(parts)) + for _, raw := range parts { + token := strings.TrimSpace(raw) + if token == "" { + continue + } - result := make([]uint32, 0, len(v)) - for i := range v { - r, err := strconv.Atoi(v[i]) + r, err := strconv.ParseUint(token, 10, 32) if err != nil { + log.Warn().Str("availableMap", raw).Msg("ignoring invalid available map id") continue } - result = append(result, uint32(r)) + mapID := uint32(r) + if _, ok := seen[mapID]; ok { + continue + } + seen[mapID] = struct{}{} + result = append(result, mapID) } + sort.Slice(result, func(i, j int) bool { + return result[i] < result[j] + }) + return result } diff --git a/apps/servers-registry/service/game-server.go b/apps/servers-registry/service/game-server.go index b060372..1b80098 100644 --- a/apps/servers-registry/service/game-server.go +++ b/apps/servers-registry/service/game-server.go @@ -2,8 +2,11 @@ package service import ( "context" + "errors" "fmt" "math/rand" + "net" + "net/http" "sort" "github.com/rs/zerolog/log" @@ -100,7 +103,7 @@ func NewGameServer( func (g *gameServerImpl) Register(ctx context.Context, server *repo.GameServer) error { sort.Slice(server.AvailableMaps, func(i, j int) bool { - return server.AvailableMaps[i] <= server.AvailableMaps[j] + return server.AvailableMaps[i] < server.AvailableMaps[j] }) if err := g.checker.AddHealthCheckObject(server); err != nil { @@ -173,14 +176,26 @@ func (g *gameServerImpl) AvailableForMapAndRealm(ctx context.Context, mapID uint return nil, err } + hasExplicitMapServer := false + for _, server := range servers { + if !server.IsAllMapsAvailable() && containsMapID(server.AvailableMaps, mapID) { + hasExplicitMapServer = true + break + } + } + result := []repo.GameServer{} for _, server := range servers { + if hasExplicitMapServer && server.IsAllMapsAvailable() { + continue + } + if server.CanHandleMap(mapID) { result = append(result, server) } } - return append(result), nil + return result, nil } func (g *gameServerImpl) RandomServerForRealm(ctx context.Context, realmID uint32) (*repo.GameServer, error) { @@ -233,6 +248,7 @@ func (g *gameServerImpl) MapsLoadedForServer(ctx context.Context, serverID strin return nil, fmt.Errorf("game server not found") } + pendingBefore := len(server.AssignedButPendingMaps) newPendingMaps := []uint32{} for i := range server.AssignedButPendingMaps { hasMap := false @@ -248,11 +264,37 @@ func (g *gameServerImpl) MapsLoadedForServer(ctx context.Context, serverID strin } server.AssignedButPendingMaps = newPendingMaps + log.Info(). + Str("serverID", serverID). + Str("address", server.Address). + Str("grpcAddress", server.GRPCAddress). + Str("healthCheckAddress", server.HealthCheckAddr). + Uint32("realmID", server.RealmID). + Bool("isCrossRealm", server.IsCrossRealm). + Int("loadedMaps", len(maps)). + Int("pendingBefore", pendingBefore). + Int("pendingAfter", len(newPendingMaps)). + Msg("Game server maps marked ready") return server, g.r.Upsert(ctx, server) } func (g *gameServerImpl) onServerUnhealthy(server *repo.GameServer, err error) { + if isDegradedGameServerHealthError(err) { + log.Warn(). + Err(err). + Str("address", server.Address). + Str("healthCheckAddress", server.HealthCheckAddr). + Str("serverID", server.ID). + Uint32("realmID", server.RealmID). + Bool("isCrossRealm", server.IsCrossRealm). + Msg("Game Server world-loop health degraded; preserving registered routing membership") + if addErr := g.checker.AddHealthCheckObject(server); addErr != nil { + log.Error().Err(addErr).Str("serverID", server.ID).Msg("can't re-add degraded game server to health checker") + } + return + } + log.Warn(). Err(err). Str("address", server.Address). @@ -304,6 +346,16 @@ func (g *gameServerImpl) onServerUnhealthy(server *repo.GameServer, err error) { } } +func isDegradedGameServerHealthError(err error) bool { + var statusErr *healthandmetrics.HTTPStatusError + if errors.As(err, &statusErr) { + return statusErr.StatusCode == http.StatusServiceUnavailable || statusErr.StatusCode == http.StatusGatewayTimeout + } + + var netErr net.Error + return errors.As(err, &netErr) && netErr.Timeout() +} + func (g *gameServerImpl) distributeMapsToServers(ctx context.Context, servers []repo.GameServer) ([]repo.GameServer, error) { serversBefore := make([]repo.GameServer, len(servers)) for i, server := range servers { @@ -318,6 +370,7 @@ func (g *gameServerImpl) distributeMapsToServers(ctx context.Context, servers [] ID: distributed[i].ID, Address: distributed[i].Address, RealmID: distributed[i].RealmID, + IsCrossRealm: distributed[i].IsCrossRealm, AvailableMaps: distributed[i].AvailableMaps, NewAssignedMapsToHandle: distributed[i].AssignedMapsToHandle, } @@ -331,12 +384,13 @@ func (g *gameServerImpl) distributeMapsToServers(ctx context.Context, servers [] } for i := range distributed { - // Mark new maps as pending. for _, server := range res { if server.ID == distributed[i].ID { - // No need to have confirmation for assignment on startup. - if len(server.OldAssignedMapsToHandle) > 0 { - distributed[i].AssignedButPendingMaps = server.OnlyNewMaps() + for _, previousServer := range serversBefore { + if previousServer.ID == distributed[i].ID { + distributed[i].AssignedButPendingMaps = pendingMapsAfterReassignment(previousServer, server) + break + } } break } @@ -357,6 +411,44 @@ func (g *gameServerImpl) distributeMapsToServers(ctx context.Context, servers [] return distributed, nil } +func pendingMapsAfterReassignment(previous repo.GameServer, reassigned events.GameServer) []uint32 { + pending := make([]uint32, 0, len(previous.AssignedButPendingMaps)+len(reassigned.NewAssignedMapsToHandle)) + seen := map[uint32]struct{}{} + + addPending := func(mapID uint32) { + if _, ok := seen[mapID]; ok { + return + } + seen[mapID] = struct{}{} + pending = append(pending, mapID) + } + + for _, mapID := range previous.AssignedButPendingMaps { + if containsMapID(reassigned.NewAssignedMapsToHandle, mapID) { + addPending(mapID) + } + } + + for _, mapID := range reassigned.OnlyNewMaps() { + addPending(mapID) + } + + sort.Slice(pending, func(i, j int) bool { + return pending[i] < pending[j] + }) + + return pending +} + +func containsMapID(maps []uint32, mapID uint32) bool { + for _, candidate := range maps { + if candidate == mapID { + return true + } + } + return false +} + func (g *gameServerImpl) onMetricsUpdate(server *repo.GameServer, m *healthandmetrics.MetricsRead) { err := g.r.Update(context.Background(), server.ID, func(s *repo.GameServer) *repo.GameServer { s.ActiveConnections = uint32(m.ActiveConnections) diff --git a/apps/servers-registry/service/matchmaking-monitor.go b/apps/servers-registry/service/matchmaking-monitor.go new file mode 100644 index 0000000..a3bef0b --- /dev/null +++ b/apps/servers-registry/service/matchmaking-monitor.go @@ -0,0 +1,165 @@ +package service + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "time" + + "github.com/rs/zerolog/log" + + "github.com/walkline/ToCloud9/shared/events" + "github.com/walkline/ToCloud9/shared/healthandmetrics" +) + +const ( + defaultMatchmakingHealthCheckInterval = 4 * time.Second + defaultMatchmakingHealthCheckTimeout = 15 * time.Second +) + +type MatchmakingHealthMonitor struct { + serviceAddress string + healthCheckAddress string + interval time.Duration + client *http.Client + eventsProducer events.ServerRegistryProducer + + lastHealthy *bool + lastStartedAtUnixMs int64 +} + +type matchmakingHealthPayload struct { + StartedAtUnixMs int64 `json:"startedAtUnixMs"` +} + +func NewMatchmakingHealthMonitor( + serviceAddress string, + healthCheckAddress string, + interval time.Duration, + timeout time.Duration, + eventsProducer events.ServerRegistryProducer, +) *MatchmakingHealthMonitor { + if interval <= 0 { + interval = defaultMatchmakingHealthCheckInterval + } + if timeout <= 0 { + timeout = defaultMatchmakingHealthCheckTimeout + } + + return &MatchmakingHealthMonitor{ + serviceAddress: serviceAddress, + healthCheckAddress: healthCheckAddress, + interval: interval, + client: &http.Client{ + Timeout: timeout, + }, + eventsProducer: eventsProducer, + } +} + +func (m *MatchmakingHealthMonitor) Start(ctx context.Context) { + if m.healthCheckAddress == "" { + return + } + + m.checkOnce(ctx) + + ticker := time.NewTicker(m.interval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + m.checkOnce(ctx) + case <-ctx.Done(): + return + } + } +} + +func (m *MatchmakingHealthMonitor) checkOnce(ctx context.Context) { + status, err := m.check(ctx) + if err != nil { + if m.lastHealthy == nil || *m.lastHealthy { + m.publishUnhealthy(err) + } + healthy := false + m.lastHealthy = &healthy + return + } + + if m.lastHealthy != nil && !*m.lastHealthy { + m.publishRecovered() + } else if m.lastHealthy != nil && + *m.lastHealthy && + status.StartedAtUnixMs != 0 && + m.lastStartedAtUnixMs != 0 && + status.StartedAtUnixMs != m.lastStartedAtUnixMs { + m.publishUnhealthy(fmt.Errorf("matchmaking service restarted")) + } + m.lastStartedAtUnixMs = status.StartedAtUnixMs + healthy := true + m.lastHealthy = &healthy +} + +func (m *MatchmakingHealthMonitor) check(ctx context.Context) (matchmakingHealthPayload, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://"+m.healthCheckAddress+healthandmetrics.HealthCheckURL, nil) + if err != nil { + return matchmakingHealthPayload{}, err + } + + resp, err := m.client.Do(req) + if err != nil { + return matchmakingHealthPayload{}, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return matchmakingHealthPayload{}, fmt.Errorf("bad status code %d", resp.StatusCode) + } + + var status matchmakingHealthPayload + if err := json.NewDecoder(io.LimitReader(resp.Body, 4096)).Decode(&status); err != nil { + return matchmakingHealthPayload{}, nil + } + + return status, nil +} + +func (m *MatchmakingHealthMonitor) publishUnhealthy(err error) { + log.Warn(). + Err(err). + Str("address", m.serviceAddress). + Str("healthCheckAddress", m.healthCheckAddress). + Msg("Matchmaking service unhealthy") + + if produceErr := m.eventsProducer.MatchmakingRemovedUnhealthy(&events.ServerRegistryEventMatchmakingRemovedUnhealthyPayload{ + MatchmakingService: m.payload(), + Error: err.Error(), + }); produceErr != nil { + log.Error().Err(produceErr).Msg("can't produce unhealthy matchmaking event") + } +} + +func (m *MatchmakingHealthMonitor) publishRecovered() { + log.Info(). + Str("address", m.serviceAddress). + Str("healthCheckAddress", m.healthCheckAddress). + Msg("Matchmaking service recovered") + + if err := m.eventsProducer.MatchmakingRecovered(&events.ServerRegistryEventMatchmakingRecoveredPayload{ + MatchmakingService: m.payload(), + }); err != nil { + log.Error().Err(err).Msg("can't produce recovered matchmaking event") + } +} + +func (m *MatchmakingHealthMonitor) payload() events.MatchmakingService { + return events.MatchmakingService{ + Address: m.serviceAddress, + HealthCheckAddr: m.healthCheckAddress, + ObservedAtUnixMs: time.Now().UnixMilli(), + } +} diff --git a/apps/servers-registry/service/matchmaking-server.go b/apps/servers-registry/service/matchmaking-server.go new file mode 100644 index 0000000..2365c45 --- /dev/null +++ b/apps/servers-registry/service/matchmaking-server.go @@ -0,0 +1,149 @@ +package service + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/rs/zerolog/log" + + "github.com/walkline/ToCloud9/shared/events" + "github.com/walkline/ToCloud9/shared/healthandmetrics" +) + +type Matchmaking interface { + Register(ctx context.Context, server *MatchmakingServer) (*MatchmakingServer, error) +} + +type MatchmakingServer struct { + ID string + Address string + HealthCheckAddr string + StartedAtUnixMs int64 +} + +func (s *MatchmakingServer) HealthCheckAddress() string { + return s.HealthCheckAddr +} + +type matchmakingImpl struct { + checker healthandmetrics.HealthChecker + eProducer events.ServerRegistryProducer + + mu sync.Mutex + current *MatchmakingServer +} + +func NewMatchmaking(checker healthandmetrics.HealthChecker, eProducer events.ServerRegistryProducer) Matchmaking { + service := &matchmakingImpl{ + checker: checker, + eProducer: eProducer, + } + + checker.AddFailedObserver(func(object healthandmetrics.HealthCheckObject, err error) { + if server, ok := object.(*MatchmakingServer); ok { + service.onServerUnhealthy(server, err) + } + }) + + return service +} + +func (m *matchmakingImpl) Register(ctx context.Context, server *MatchmakingServer) (*MatchmakingServer, error) { + _ = ctx + + if server == nil { + return nil, fmt.Errorf("matchmaking server is nil") + } + if server.Address == "" { + return nil, fmt.Errorf("matchmaking server address is empty") + } + if server.HealthCheckAddr == "" { + return nil, fmt.Errorf("matchmaking health check address is empty") + } + if server.ID == "" { + server.ID = server.Address + } + + m.mu.Lock() + + if m.current != nil && sameMatchmakingInstance(m.current, server) { + if err := m.checker.AddHealthCheckObject(m.current); err != nil { + m.mu.Unlock() + return nil, err + } + current := m.current + m.mu.Unlock() + return current, nil + } + + restarted := m.current != nil + if m.current != nil { + if err := m.checker.RemoveHealthCheckObject(m.current); err != nil { + m.mu.Unlock() + return nil, err + } + } + + if err := m.checker.AddHealthCheckObject(server); err != nil { + m.mu.Unlock() + return nil, err + } + m.current = server + m.mu.Unlock() + + if restarted { + m.publishUnhealthy(server, fmt.Errorf("matchmaking service restarted")) + } + + log.Info(). + Str("address", server.Address). + Str("healthCheckAddress", server.HealthCheckAddr). + Int64("startedAtUnixMs", server.StartedAtUnixMs). + Msg("Registered matchmaking server") + + return server, nil +} + +func (m *matchmakingImpl) onServerUnhealthy(server *MatchmakingServer, err error) { + m.mu.Lock() + if m.current != server { + m.mu.Unlock() + return + } + m.current = nil + m.mu.Unlock() + + if removeErr := m.checker.RemoveHealthCheckObject(server); removeErr != nil { + log.Error().Err(removeErr).Msg("can't remove matchmaking from health checker") + } + + m.publishUnhealthy(server, err) +} + +func (m *matchmakingImpl) publishUnhealthy(server *MatchmakingServer, err error) { + log.Warn(). + Err(err). + Str("address", server.Address). + Str("healthCheckAddress", server.HealthCheckAddr). + Msg("Matchmaking service unhealthy") + + if produceErr := m.eProducer.MatchmakingRemovedUnhealthy(&events.ServerRegistryEventMatchmakingRemovedUnhealthyPayload{ + MatchmakingService: events.MatchmakingService{ + Address: server.Address, + HealthCheckAddr: server.HealthCheckAddr, + ObservedAtUnixMs: time.Now().UnixMilli(), + }, + Error: err.Error(), + }); produceErr != nil { + log.Error().Err(produceErr).Msg("can't produce unhealthy matchmaking event") + } +} + +func sameMatchmakingInstance(a, b *MatchmakingServer) bool { + return a.Address == b.Address && + a.HealthCheckAddr == b.HealthCheckAddr && + a.StartedAtUnixMs != 0 && + a.StartedAtUnixMs == b.StartedAtUnixMs +} diff --git a/shared/healthandmetrics/health-checker.go b/shared/healthandmetrics/health-checker.go index d7f7f7e..b425579 100644 --- a/shared/healthandmetrics/health-checker.go +++ b/shared/healthandmetrics/health-checker.go @@ -3,6 +3,7 @@ package healthandmetrics import ( "fmt" "net/http" + "reflect" "sync" "time" ) @@ -27,6 +28,16 @@ type HealthCheckProcessor interface { Check(HealthCheckObject) error } +const defaultHealthCheckFailureThreshold = 2 + +type HTTPStatusError struct { + StatusCode int +} + +func (e *HTTPStatusError) Error() string { + return fmt.Sprintf("bad status code %d", e.StatusCode) +} + type healthCheckResult struct { obj HealthCheckObject err error @@ -45,17 +56,23 @@ type healthChecker struct { successObservers []HealthCheckSuccessObserver failedObservers []HealthCheckFailedObserver + failureCountsMu sync.Mutex + failureCounts map[string]int + failureThreshold int + results chan healthCheckResult queue chan HealthCheckObject } func NewHealthChecker(delay time.Duration, processorsCount int, processor HealthCheckProcessor) HealthChecker { return &healthChecker{ - delay: delay, - processorsCount: processorsCount, - processor: processor, - results: make(chan healthCheckResult, 100), - queue: make(chan HealthCheckObject, 100), + delay: delay, + processorsCount: processorsCount, + processor: processor, + failureCounts: map[string]int{}, + failureThreshold: defaultHealthCheckFailureThreshold, + results: make(chan healthCheckResult, 100), + queue: make(chan HealthCheckObject, 100), } } @@ -63,14 +80,16 @@ func (h *healthChecker) AddHealthCheckObject(object HealthCheckObject) error { h.objectsMu.Lock() defer h.objectsMu.Unlock() - // Check if we already have this observable. - for _, o := range h.objects { + for i, o := range h.objects { if o.HealthCheckAddress() == object.HealthCheckAddress() { + h.objects[i] = object + h.resetFailureCount(object) return nil } } h.objects = append(h.objects, object) + h.resetFailureCount(object) return nil } @@ -80,11 +99,16 @@ func (h *healthChecker) RemoveHealthCheckObject(object HealthCheckObject) error for i := range h.objects { if h.objects[i].HealthCheckAddress() == object.HealthCheckAddress() { + if !sameHealthCheckObject(h.objects[i], object) { + return nil + } h.objects = append(h.objects[:i], h.objects[i+1:]...) + h.clearFailureCount(object) return nil } } + h.clearFailureCount(object) return nil } @@ -134,7 +158,15 @@ func (h *healthChecker) makeIteration() { } func (h *healthChecker) handleResult(result healthCheckResult) { + if !h.isCurrentHealthCheckObject(result.obj) { + return + } + if result.err != nil { + if h.recordFailure(result.obj) < h.failureThreshold { + return + } + h.RemoveHealthCheckObject(result.obj) h.observersMu.RLock() @@ -144,6 +176,8 @@ func (h *healthChecker) handleResult(result healthCheckResult) { observer(result.obj, result.err) } } else { + h.resetFailureCount(result.obj) + h.observersMu.RLock() defer h.observersMu.RUnlock() @@ -153,6 +187,57 @@ func (h *healthChecker) handleResult(result healthCheckResult) { } } +func (h *healthChecker) isCurrentHealthCheckObject(object HealthCheckObject) bool { + h.objectsMu.RLock() + defer h.objectsMu.RUnlock() + + for _, current := range h.objects { + if current.HealthCheckAddress() != object.HealthCheckAddress() { + continue + } + + return sameHealthCheckObject(current, object) + } + + return false +} + +func sameHealthCheckObject(a, b HealthCheckObject) bool { + if a == nil || b == nil { + return a == b + } + + aType := reflect.TypeOf(a) + if aType != reflect.TypeOf(b) || !aType.Comparable() { + return true + } + + return a == b +} + +func (h *healthChecker) recordFailure(object HealthCheckObject) int { + h.failureCountsMu.Lock() + defer h.failureCountsMu.Unlock() + + address := object.HealthCheckAddress() + h.failureCounts[address]++ + return h.failureCounts[address] +} + +func (h *healthChecker) resetFailureCount(object HealthCheckObject) { + h.failureCountsMu.Lock() + defer h.failureCountsMu.Unlock() + + h.failureCounts[object.HealthCheckAddress()] = 0 +} + +func (h *healthChecker) clearFailureCount(object HealthCheckObject) { + h.failureCountsMu.Lock() + defer h.failureCountsMu.Unlock() + + delete(h.failureCounts, object.HealthCheckAddress()) +} + func (h *healthChecker) process(processorsCount int) { for i := 0; i < processorsCount; i++ { go func() { @@ -184,8 +269,10 @@ func (h *httpHealthCheckProcessor) Check(object HealthCheckObject) error { return err } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { - return fmt.Errorf("bad status code %d", resp.StatusCode) + return &HTTPStatusError{StatusCode: resp.StatusCode} } return nil diff --git a/shared/healthandmetrics/metrics-reader.go b/shared/healthandmetrics/metrics-reader.go index 73a6245..0cbee32 100644 --- a/shared/healthandmetrics/metrics-reader.go +++ b/shared/healthandmetrics/metrics-reader.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "net/http" + "reflect" "sync" "time" @@ -25,7 +26,7 @@ type MetricsRead struct { Delay99Percentile int DelayMax int - Raw []dto.MetricFamily + Raw []*dto.MetricFamily } type MetricsObserver func(MetricsObservable, *MetricsRead) @@ -67,9 +68,9 @@ func (m *metricsConsumerImpl) AddMetricsObservable(observable MetricsObservable) m.objectsMu.Lock() defer m.objectsMu.Unlock() - // Check if we already have this observable. - for _, object := range m.objects { + for i, object := range m.objects { if object.MetricsAddress() == observable.MetricsAddress() { + m.objects[i] = observable return nil } } @@ -84,6 +85,9 @@ func (m *metricsConsumerImpl) RemoveMetricsObservable(observable MetricsObservab for i := range m.objects { if m.objects[i].MetricsAddress() == observable.MetricsAddress() { + if !sameMetricsObservable(m.objects[i], observable) { + return nil + } m.objects = append(m.objects[:i], m.objects[i+1:]...) return nil } @@ -146,6 +150,10 @@ func (m *metricsConsumerImpl) makeIteration() { } func (m *metricsConsumerImpl) handleResult(result metricsReadResult) { + if !m.isCurrentMetricsObservable(result.Observable) { + return + } + if result.Err != nil { log.Error().Err(result.Err).Msgf("failed to read metrics for %s object", result.Observable.MetricsAddress()) return @@ -159,6 +167,34 @@ func (m *metricsConsumerImpl) handleResult(result metricsReadResult) { } } +func (m *metricsConsumerImpl) isCurrentMetricsObservable(observable MetricsObservable) bool { + m.objectsMu.RLock() + defer m.objectsMu.RUnlock() + + for _, current := range m.objects { + if current.MetricsAddress() != observable.MetricsAddress() { + continue + } + + return sameMetricsObservable(current, observable) + } + + return false +} + +func sameMetricsObservable(a, b MetricsObservable) bool { + if a == nil || b == nil { + return a == b + } + + aType := reflect.TypeOf(a) + if aType != reflect.TypeOf(b) || !aType.Comparable() { + return true + } + + return a == b +} + func NewMetricsConsumer(delay time.Duration, processorsCount int, reader MetricsReader) MetricsConsumer { return &metricsConsumerImpl{ delay: delay, @@ -192,7 +228,7 @@ func (h httpPrometheusMetricsReader) Read(observable MetricsObservable) (*Metric return nil, fmt.Errorf("bad status code %d", resp.StatusCode) } - metrics := []dto.MetricFamily{} + metrics := []*dto.MetricFamily{} dec := expfmt.NewDecoder(resp.Body, expfmt.NewFormat(expfmt.TypeTextPlain)) for { @@ -204,7 +240,7 @@ func (h httpPrometheusMetricsReader) Read(observable MetricsObservable) (*Metric if err != nil { return nil, err } - metrics = append(metrics, result) + metrics = append(metrics, &result) } results := MetricsRead{ diff --git a/shared/healthandmetrics/server.go b/shared/healthandmetrics/server.go index 9b8e4d3..7bfd5f6 100644 --- a/shared/healthandmetrics/server.go +++ b/shared/healthandmetrics/server.go @@ -2,39 +2,60 @@ package healthandmetrics import ( "context" + "encoding/json" + "fmt" "net/http" + "time" ) var ( HealthCheckURL = "/healthcheck" MetricsURL = "/metrics" - healthCheckOKPayload = []byte(`{"status":"OK"}`) + healthCheckOKPayload = "OK" ) type Server interface { ListenAndServe() error Shutdown(ctx context.Context) error Port() string + StartedAtUnixMs() int64 } +type HealthProbe func(context.Context) error + type server struct { http.Server - port string + port string + startedAtUnixMs int64 +} + +type healthCheckPayload struct { + Status string `json:"status"` + StartedAtUnixMs int64 `json:"startedAtUnixMs"` } func NewServer(port string, metricsHandler http.Handler) Server { - mux := http.NewServeMux() - mux.HandleFunc(HealthCheckURL, func(w http.ResponseWriter, r *http.Request) { - w.Write(healthCheckOKPayload) + return NewServerWithHealthProbe(port, metricsHandler, nil) +} + +func NewServerWithHealthProbe(port string, metricsHandler http.Handler, healthProbe HealthProbe) Server { + startedAtUnixMs := time.Now().UnixMilli() + payload, _ := json.Marshal(healthCheckPayload{ + Status: healthCheckOKPayload, + StartedAtUnixMs: startedAtUnixMs, }) + mux := http.NewServeMux() + mux.HandleFunc(HealthCheckURL, healthCheckHandler(payload, healthProbe)) + if metricsHandler != nil { mux.Handle(MetricsURL, metricsHandler) } return &server{ - port: port, + port: port, + startedAtUnixMs: startedAtUnixMs, Server: http.Server{ Addr: ":" + port, Handler: mux, @@ -42,6 +63,24 @@ func NewServer(port string, metricsHandler http.Handler) Server { } } +func healthCheckHandler(okPayload []byte, healthProbe HealthProbe) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if healthProbe != nil { + if err := healthProbe(r.Context()); err != nil { + w.WriteHeader(http.StatusServiceUnavailable) + _, _ = w.Write([]byte(fmt.Sprintf(`{"status":"UNHEALTHY","error":%q}`, err.Error()))) + return + } + } + + w.Write(okPayload) + } +} + func (s *server) Port() string { return s.port } + +func (s *server) StartedAtUnixMs() int64 { + return s.startedAtUnixMs +} From b8ddf29790ee5e4da6d56e507909328832449a09 Mon Sep 17 00:00:00 2001 From: VG-Prog Date: Fri, 22 May 2026 22:37:25 +0200 Subject: [PATCH 03/11] fix(Cluster/Discovery): Drain degraded game servers from placement Keep world-loop degraded game servers registered for ownership and existing lookups, but mark them as non-admitting so new player placement skips them. Clear the drain state on successful health recovery and fall back to healthy all-map nodes when an assigned owner is degraded. --- apps/servers-registry/repo/game-server.go | 5 ++ apps/servers-registry/service/game-server.go | 94 +++++++++++++++++++- 2 files changed, 95 insertions(+), 4 deletions(-) diff --git a/apps/servers-registry/repo/game-server.go b/apps/servers-registry/repo/game-server.go index 1b36f1c..cc61094 100644 --- a/apps/servers-registry/repo/game-server.go +++ b/apps/servers-registry/repo/game-server.go @@ -27,6 +27,7 @@ type GameServer struct { ActiveConnections uint32 Diff DiffData + HealthDegraded bool // AssignedMapsToHandle list of maps that loadbalancer algorithm assigned for this server. AssignedMapsToHandle []uint32 @@ -64,6 +65,10 @@ func (g *GameServer) IsAllMapsAvailable() bool { return len(g.AvailableMaps) == 0 } +func (g *GameServer) AcceptsNewPlayers() bool { + return !g.HealthDegraded +} + func (g *GameServer) Copy() GameServer { cp := *g cp.AvailableMaps = append([]uint32(nil), g.AvailableMaps...) diff --git a/apps/servers-registry/service/game-server.go b/apps/servers-registry/service/game-server.go index 1b80098..8e64ab1 100644 --- a/apps/servers-registry/service/game-server.go +++ b/apps/servers-registry/service/game-server.go @@ -58,6 +58,12 @@ func NewGameServer( } }) + checker.AddSuccessObserver(func(object healthandmetrics.HealthCheckObject) { + if gs, ok := object.(*repo.GameServer); ok { + service.onServerHealthy(gs) + } + }) + metrics.AddObserver(func(observable healthandmetrics.MetricsObservable, read *healthandmetrics.MetricsRead) { if gs, ok := observable.(*repo.GameServer); ok { service.onMetricsUpdate(gs, read) @@ -105,6 +111,7 @@ func (g *gameServerImpl) Register(ctx context.Context, server *repo.GameServer) sort.Slice(server.AvailableMaps, func(i, j int) bool { return server.AvailableMaps[i] < server.AvailableMaps[j] }) + server.HealthDegraded = false if err := g.checker.AddHealthCheckObject(server); err != nil { return err @@ -176,8 +183,9 @@ func (g *gameServerImpl) AvailableForMapAndRealm(ctx context.Context, mapID uint return nil, err } + admissionServers := gameServersAcceptingNewPlayers(servers) hasExplicitMapServer := false - for _, server := range servers { + for _, server := range admissionServers { if !server.IsAllMapsAvailable() && containsMapID(server.AvailableMaps, mapID) { hasExplicitMapServer = true break @@ -185,7 +193,7 @@ func (g *gameServerImpl) AvailableForMapAndRealm(ctx context.Context, mapID uint } result := []repo.GameServer{} - for _, server := range servers { + for _, server := range admissionServers { if hasExplicitMapServer && server.IsAllMapsAvailable() { continue } @@ -195,6 +203,22 @@ func (g *gameServerImpl) AvailableForMapAndRealm(ctx context.Context, mapID uint } } + if len(result) == 0 && hasDegradedMapOwner(servers, mapID) { + for _, server := range admissionServers { + if server.IsAllMapsAvailable() { + result = append(result, server) + } + } + if len(result) > 0 { + log.Warn(). + Uint32("mapID", mapID). + Uint32("realmID", realmID). + Bool("isCrossRealm", isCrossRealm). + Int("candidates", len(result)). + Msg("Using healthy all-map game servers while assigned owner is health-degraded") + } + } + return result, nil } @@ -203,6 +227,7 @@ func (g *gameServerImpl) RandomServerForRealm(ctx context.Context, realmID uint3 if err != nil { return nil, err } + servers = gameServersAcceptingNewPlayers(servers) if len(servers) == 0 { return nil, nil @@ -279,8 +304,48 @@ func (g *gameServerImpl) MapsLoadedForServer(ctx context.Context, serverID strin return server, g.r.Upsert(ctx, server) } +func (g *gameServerImpl) onServerHealthy(server *repo.GameServer) { + wasDegraded := false + err := g.r.Update(context.Background(), server.ID, func(s *repo.GameServer) *repo.GameServer { + wasDegraded = s.HealthDegraded + s.HealthDegraded = false + return s + }) + if err != nil { + log.Error(). + Err(err). + Str("address", server.Address). + Str("healthCheckAddress", server.HealthCheckAddr). + Str("serverID", server.ID). + Msg("can't clear degraded game server health state") + return + } + if wasDegraded { + log.Info(). + Str("address", server.Address). + Str("healthCheckAddress", server.HealthCheckAddr). + Str("serverID", server.ID). + Uint32("realmID", server.RealmID). + Bool("isCrossRealm", server.IsCrossRealm). + Msg("Game Server health recovered; accepting new player placement") + } +} + func (g *gameServerImpl) onServerUnhealthy(server *repo.GameServer, err error) { if isDegradedGameServerHealthError(err) { + updateErr := g.r.Update(context.Background(), server.ID, func(s *repo.GameServer) *repo.GameServer { + s.HealthDegraded = true + return s + }) + if updateErr != nil { + log.Error(). + Err(updateErr). + Str("address", server.Address). + Str("healthCheckAddress", server.HealthCheckAddr). + Str("serverID", server.ID). + Msg("can't mark degraded game server as non-admitting") + } + log.Warn(). Err(err). Str("address", server.Address). @@ -288,8 +353,10 @@ func (g *gameServerImpl) onServerUnhealthy(server *repo.GameServer, err error) { Str("serverID", server.ID). Uint32("realmID", server.RealmID). Bool("isCrossRealm", server.IsCrossRealm). - Msg("Game Server world-loop health degraded; preserving registered routing membership") - if addErr := g.checker.AddHealthCheckObject(server); addErr != nil { + Msg("Game Server world-loop health degraded; preserving ownership but draining new player placement") + degraded := server.Copy() + degraded.HealthDegraded = true + if addErr := g.checker.AddHealthCheckObject(°raded); addErr != nil { log.Error().Err(addErr).Str("serverID", server.ID).Msg("can't re-add degraded game server to health checker") } return @@ -449,6 +516,25 @@ func containsMapID(maps []uint32, mapID uint32) bool { return false } +func gameServersAcceptingNewPlayers(servers []repo.GameServer) []repo.GameServer { + result := make([]repo.GameServer, 0, len(servers)) + for _, server := range servers { + if server.AcceptsNewPlayers() { + result = append(result, server) + } + } + return result +} + +func hasDegradedMapOwner(servers []repo.GameServer, mapID uint32) bool { + for _, server := range servers { + if server.HealthDegraded && server.CanHandleMap(mapID) { + return true + } + } + return false +} + func (g *gameServerImpl) onMetricsUpdate(server *repo.GameServer, m *healthandmetrics.MetricsRead) { err := g.r.Update(context.Background(), server.ID, func(s *repo.GameServer) *repo.GameServer { s.ActiveConnections = uint32(m.ActiveConnections) From aaffff308409455f003bc8776bd2437b847c10b3 Mon Sep 17 00:00:00 2001 From: VG-Prog Date: Fri, 22 May 2026 21:47:55 +0200 Subject: [PATCH 04/11] feat(Cluster/Gateway): Add cluster-aware client routing Route gateway sessions through cluster-aware worldserver selection, native transport handoff, bounded retry/backoff, cross-service event listeners, and client-facing packet rendering for social, group, guild, guild bank, LFG, battleground, arena, mail, channel, and player-state flows. --- apps/gateway/balancer.go | 50 + apps/gateway/cmd/gateway/main.go | 31 + apps/gateway/config/config.go | 54 + .../gateway/events-broadcaster/broadcaster.go | 679 +++++- .../events-broadcaster/chat-channels.go | 74 +- apps/gateway/packet/opcode.go | 1 + apps/gateway/packet/opcode_string.go | 8 +- .../service/characters-updates-barrier.go | 10 +- apps/gateway/service/listener-characters.go | 77 + apps/gateway/service/listener-chat.go | 67 +- apps/gateway/service/listener-group.go | 48 + apps/gateway/service/listener-guild.go | 20 + apps/gateway/service/listener-matchmaking.go | 45 + .../service/listener-serversregistry.go | 55 + .../service/player-state-updates-barrier.go | 747 ++++++ apps/gateway/service/realmnames.go | 19 + apps/gateway/session/account_data.go | 163 ++ apps/gateway/session/arena_team.go | 603 +++++ apps/gateway/session/aura-player-state.go | 280 +++ apps/gateway/session/battleground.go | 438 +++- apps/gateway/session/channels.go | 467 ++-- apps/gateway/session/channels_moderation.go | 141 +- apps/gateway/session/character.go | 267 ++- apps/gateway/session/chat.go | 355 ++- apps/gateway/session/cluster_transport.go | 399 ++++ apps/gateway/session/error.go | 27 +- apps/gateway/session/group-cluster-extra.go | 934 ++++++++ apps/gateway/session/group-state-trace.go | 74 + apps/gateway/session/groups.go | 502 ++-- apps/gateway/session/guild.go | 471 +++- apps/gateway/session/guild_bank.go | 659 +++++ apps/gateway/session/guild_petition.go | 343 +++ apps/gateway/session/handler-events.go | 21 + apps/gateway/session/handler.go | 200 +- apps/gateway/session/interceptors.go | 426 +++- apps/gateway/session/lfg.go | 2119 +++++++++++++++++ apps/gateway/session/mail.go | 4 +- apps/gateway/session/movements.go | 2 + apps/gateway/session/name_query.go | 142 ++ .../session/object-update-player-state.go | 552 +++++ apps/gateway/session/player-state-packets.go | 271 +++ apps/gateway/session/redirect_ready.go | 63 + apps/gateway/session/session-lifecycle.go | 151 ++ apps/gateway/session/session-registry.go | 137 ++ apps/gateway/session/session.go | 882 ++++++- apps/gateway/session/social.go | 75 +- .../gateway/sockets/gamesocket/game-socket.go | 86 +- apps/gateway/sockets/packets-reader.go | 2 +- apps/gateway/sockets/socketmock/socket.go | 35 +- 49 files changed, 12095 insertions(+), 1181 deletions(-) create mode 100644 apps/gateway/service/listener-characters.go create mode 100644 apps/gateway/service/listener-serversregistry.go create mode 100644 apps/gateway/service/player-state-updates-barrier.go create mode 100644 apps/gateway/session/account_data.go create mode 100644 apps/gateway/session/arena_team.go create mode 100644 apps/gateway/session/aura-player-state.go create mode 100644 apps/gateway/session/cluster_transport.go create mode 100644 apps/gateway/session/group-cluster-extra.go create mode 100644 apps/gateway/session/group-state-trace.go create mode 100644 apps/gateway/session/guild_bank.go create mode 100644 apps/gateway/session/guild_petition.go create mode 100644 apps/gateway/session/lfg.go create mode 100644 apps/gateway/session/name_query.go create mode 100644 apps/gateway/session/object-update-player-state.go create mode 100644 apps/gateway/session/player-state-packets.go create mode 100644 apps/gateway/session/redirect_ready.go create mode 100644 apps/gateway/session/session-lifecycle.go create mode 100644 apps/gateway/session/session-registry.go diff --git a/apps/gateway/balancer.go b/apps/gateway/balancer.go index 76e6f1a..617e443 100644 --- a/apps/gateway/balancer.go +++ b/apps/gateway/balancer.go @@ -4,6 +4,56 @@ var RealmID uint32 var RetrievedGatewayID string +// AllowTwoSideInteractionGuild mirrors AzerothCore's +// AllowTwoSide.Interaction.Guild for gateway-owned guild operations. +var AllowTwoSideInteractionGuild bool + +// AllowTwoSideInteractionChannel mirrors AzerothCore's +// AllowTwoSide.Interaction.Channel for gateway-owned channel operations. +var AllowTwoSideInteractionChannel bool + +// AllowTwoSideInteractionArena mirrors AzerothCore's +// AllowTwoSide.Interaction.Arena for gateway-owned arena-team operations. +var AllowTwoSideInteractionArena bool + +// MaxPlayerLevel mirrors AzerothCore's MaxPlayerLevel for gateway-owned checks. +var MaxPlayerLevel uint32 + +// ArenaCurrentSeason mirrors AzerothCore ArenaSeasonMgr for gateway-owned arena charter creation. +var ArenaCurrentSeason uint32 + +// LegacyArenaStartRating mirrors AzerothCore's legacy arena start rating. +var LegacyArenaStartRating uint32 + +// ArenaStartRating mirrors AzerothCore's current arena start rating. +var ArenaStartRating uint32 + +// ArenaStartPersonalRating mirrors AzerothCore's Arena.StartPersonalRating. +var ArenaStartPersonalRating uint32 + +// ArenaStartMatchmakerRating mirrors AzerothCore's Arena.StartMatchmakerRating. +var ArenaStartMatchmakerRating uint32 + +func EffectiveArenaStartRating() uint32 { + if ArenaCurrentSeason < 6 { + return LegacyArenaStartRating + } + return ArenaStartRating +} + +func EffectiveArenaStartPersonalRating(teamRating uint32) uint32 { + if ArenaStartPersonalRating > 0 { + return ArenaStartPersonalRating + } + if ArenaCurrentSeason < 6 { + return 1500 + } + if teamRating >= 1000 { + return 1000 + } + return 0 +} + const ( Ver = "0.0.1" SupportedCharServiceVer = "0.0.1" diff --git a/apps/gateway/cmd/gateway/main.go b/apps/gateway/cmd/gateway/main.go index b28bb76..da1ad80 100644 --- a/apps/gateway/cmd/gateway/main.go +++ b/apps/gateway/cmd/gateway/main.go @@ -133,15 +133,32 @@ func main() { log.Fatal().Err(err).Msg("can't listen to matchmaking events-broadcaster") } + serversRegistryListener := service.NewServersRegistryNatsListener(nc, broadcaster) + err = serversRegistryListener.Listen() + if err != nil { + log.Fatal().Err(err).Msg("can't listen to servers-registry events-broadcaster") + } + friendsListener := service.NewFriendsNatsListener(nc, broadcaster) err = friendsListener.Listen() if err != nil { log.Fatal().Err(err).Msg("can't listen to friends events-broadcaster") } + charactersListener := service.NewCharactersNatsListener(nc, broadcaster) + err = charactersListener.Listen() + if err != nil { + log.Fatal().Err(err).Msg("can't listen to characters events-broadcaster") + } + producer := events.NewGatewayProducerNatsJSON(nc, root.Ver, root.RealmID, root.RetrievedGatewayID) + if err := producer.GatewayStarted(&events.GWEventGatewayStartedPayload{}); err != nil { + log.Error().Err(err).Str("gatewayID", root.RetrievedGatewayID).Msg("can't publish gateway started event") + } charsUpdsBarrier := service.NewCharactersUpdatesBarrier(&log.Logger, producer, time.Second) go charsUpdsBarrier.Run(context.TODO()) + playerStateUpdatesBarrier := service.NewPlayerStateUpdatesBarrier(&log.Logger, groupClient, root.SupportedGroupServiceVer, root.RealmID, root.RetrievedGatewayID, 5*time.Second) + go playerStateUpdatesBarrier.Run(context.TODO()) realmNamesServive, err := service.NewRealmNamesService(context.Background(), repo.NewRealmNamesMySQLRepo(authDB)) if err != nil { @@ -152,6 +169,16 @@ func main() { Str("address", l.Addr().String()). Msg("🚀 Gateway started!") + gamesocket.ConfigureAuthSessionKeyRefresh( + time.Millisecond*time.Duration(conf.AuthSessionKeyRefreshDelayMs), + int(conf.AuthSessionKeyRefreshAttempts), + ) + session.ConfigureWorldserverConnectRetry( + time.Millisecond*time.Duration(conf.WorldserverConnectRetryWaitMs), + time.Millisecond*time.Duration(conf.WorldserverConnectRetryMaxWaitMs), + ) + + sessionRegistry := session.NewSessionRegistry() for { conn, err := l.Accept() if err != nil { @@ -171,10 +198,14 @@ func main() { EventsProducer: producer, EventsBroadcaster: broadcaster, ChatChannelsEventBroadcaster: chatChannelsBroadcasterService, + SessionRegistry: sessionRegistry, CharsUpdsBarrier: charsUpdsBarrier, + PlayerStateUpdatesBarrier: playerStateUpdatesBarrier, RealmNamesService: realmNamesServive, GameServerGRPCConnMgr: gameserverconn.DefaultGameServerGRPCConnMgr, PacketProcessTimeout: time.Second * time.Duration(conf.PacketProcessTimeoutSecs), + WorldAuthAttemptTimeout: time.Millisecond * time.Duration(conf.WorldAuthAttemptTimeoutMs), + WorldAuthSessionReadyDelay: time.Millisecond * time.Duration(conf.WorldAuthSessionReadyDelayMs), ShowGameserverConnChangeToClient: conf.ShowGameserverConnChangeToClient, }) go func() { diff --git a/apps/gateway/config/config.go b/apps/gateway/config/config.go index 2476edd..ec833db 100644 --- a/apps/gateway/config/config.go +++ b/apps/gateway/config/config.go @@ -56,8 +56,53 @@ type Config struct { // PacketProcessTimeoutSecs is the time given to process single opcode (if it's not forwarded to game server). PacketProcessTimeoutSecs uint32 `yaml:"packetProcessTimeoutSecs" env:"PACKET_PROCESS_TIMEOUT_SECS" env-default:"20"` + // WorldAuthAttemptTimeoutMs is the per-attempt guard while waiting for worldserver SMSG_AUTH_RESPONSE. + WorldAuthAttemptTimeoutMs uint32 `yaml:"worldAuthAttemptTimeoutMs" env:"WORLD_AUTH_ATTEMPT_TIMEOUT_MS" env-default:"5000"` + + // WorldAuthSessionReadyDelayMs is the post-auth delay before CMSG_PLAYER_LOGIN is sent to worldserver. + WorldAuthSessionReadyDelayMs uint32 `yaml:"worldAuthSessionReadyDelayMs" env:"WORLD_AUTH_SESSION_READY_DELAY_MS" env-default:"300"` + + // WorldserverConnectRetryWaitMs is the initial wait between retryable worldserver login attempts. + WorldserverConnectRetryWaitMs uint32 `yaml:"worldserverConnectRetryWaitMs" env:"WORLDSERVER_CONNECT_RETRY_WAIT_MS" env-default:"200"` + + // WorldserverConnectRetryMaxWaitMs caps exponential backoff between retryable worldserver login attempts. + WorldserverConnectRetryMaxWaitMs uint32 `yaml:"worldserverConnectRetryMaxWaitMs" env:"WORLDSERVER_CONNECT_RETRY_MAX_WAIT_MS" env-default:"2000"` + + // AuthSessionKeyRefreshDelayMs is the delay between auth DB session-key refresh attempts after a digest mismatch. + AuthSessionKeyRefreshDelayMs uint32 `yaml:"authSessionKeyRefreshDelayMs" env:"AUTH_SESSION_KEY_REFRESH_DELAY_MS" env-default:"250"` + + // AuthSessionKeyRefreshAttempts is the number of auth DB session-key refresh attempts after a digest mismatch. + AuthSessionKeyRefreshAttempts uint32 `yaml:"authSessionKeyRefreshAttempts" env:"AUTH_SESSION_KEY_REFRESH_ATTEMPTS" env-default:"120"` + // ShowGameserverConnChangeToClient when enabled sends chat system message to the player with information about connection change. ShowGameserverConnChangeToClient bool `yaml:"showGameserverConnChangeToClient" env:"SHOW_GAMESERVER_CONN_CHANGE_TO_CLIENT" env-default:"true"` + + // AllowTwoSideInteractionGuild mirrors AzerothCore's AllowTwoSide.Interaction.Guild for gateway-owned guild operations. + AllowTwoSideInteractionGuild bool `yaml:"allowTwoSideInteractionGuild" env:"ALLOW_TWO_SIDE_INTERACTION_GUILD" env-default:"false"` + + // AllowTwoSideInteractionChannel mirrors AzerothCore's AllowTwoSide.Interaction.Channel for gateway-owned channel operations. + AllowTwoSideInteractionChannel bool `yaml:"allowTwoSideInteractionChannel" env:"ALLOW_TWO_SIDE_INTERACTION_CHANNEL" env-default:"false"` + + // AllowTwoSideInteractionArena mirrors AzerothCore's AllowTwoSide.Interaction.Arena for gateway-owned arena-team operations. + AllowTwoSideInteractionArena bool `yaml:"allowTwoSideInteractionArena" env:"ALLOW_TWO_SIDE_INTERACTION_ARENA" env-default:"false"` + + // MaxPlayerLevel mirrors AzerothCore's MaxPlayerLevel for gateway-owned arena-team invitation checks. + MaxPlayerLevel uint32 `yaml:"maxPlayerLevel" env:"MAX_PLAYER_LEVEL" env-default:"80"` + + // ArenaCurrentSeason mirrors AzerothCore's ArenaSeasonMgr for gateway-owned arena charter creation. + ArenaCurrentSeason uint32 `yaml:"arenaCurrentSeason" env:"ARENA_CURRENT_SEASON" env-default:"8"` + + // LegacyArenaStartRating mirrors AzerothCore's Arena.LegacyStartRating. + LegacyArenaStartRating uint32 `yaml:"legacyArenaStartRating" env:"LEGACY_ARENA_START_RATING" env-default:"1500"` + + // ArenaStartRating mirrors AzerothCore's Arena.StartRating. + ArenaStartRating uint32 `yaml:"arenaStartRating" env:"ARENA_START_RATING" env-default:"0"` + + // ArenaStartPersonalRating mirrors AzerothCore's Arena.StartPersonalRating. + ArenaStartPersonalRating uint32 `yaml:"arenaStartPersonalRating" env:"ARENA_START_PERSONAL_RATING" env-default:"0"` + + // ArenaStartMatchmakerRating mirrors AzerothCore's Arena.StartMatchmakerRating. + ArenaStartMatchmakerRating uint32 `yaml:"arenaStartMatchmakerRating" env:"ARENA_START_MATCHMAKER_RATING" env-default:"1500"` } func (c Config) PortInt() (p int) { @@ -82,6 +127,15 @@ func LoadConfig() (*Config, error) { } gateway.RealmID = uint32(c.Root.RealmID) + gateway.AllowTwoSideInteractionGuild = c.Root.AllowTwoSideInteractionGuild + gateway.AllowTwoSideInteractionChannel = c.Root.AllowTwoSideInteractionChannel + gateway.AllowTwoSideInteractionArena = c.Root.AllowTwoSideInteractionArena + gateway.MaxPlayerLevel = c.Root.MaxPlayerLevel + gateway.ArenaCurrentSeason = c.Root.ArenaCurrentSeason + gateway.LegacyArenaStartRating = c.Root.LegacyArenaStartRating + gateway.ArenaStartRating = c.Root.ArenaStartRating + gateway.ArenaStartPersonalRating = c.Root.ArenaStartPersonalRating + gateway.ArenaStartMatchmakerRating = c.Root.ArenaStartMatchmakerRating return &c.Root, nil } diff --git a/apps/gateway/events-broadcaster/broadcaster.go b/apps/gateway/events-broadcaster/broadcaster.go index 1ba5817..af62bf2 100644 --- a/apps/gateway/events-broadcaster/broadcaster.go +++ b/apps/gateway/events-broadcaster/broadcaster.go @@ -1,9 +1,10 @@ package events_broadcaster import ( - "strings" "sync" + "github.com/rs/zerolog/log" + "github.com/walkline/ToCloud9/apps/gateway" "github.com/walkline/ToCloud9/shared/events" "github.com/walkline/ToCloud9/shared/wow/guid" @@ -25,6 +26,8 @@ const ( EventTypeGuildRankCreated EventTypeGuildRankDeleted EventTypeGuildNewMessage + EventTypeGuildPetitionOffered + EventTypeGuildPetitionSigned EventTypeGroupInviteCreated EventTypeGroupCreated EventTypeGroupMemberOnlineStatusChanged @@ -37,9 +40,20 @@ const ( EventTypeGroupNewMessage EventTypeGroupNewTargetIcon EventTypeGroupDifficultyChanged + EventTypeGroupReadyCheckStarted + EventTypeGroupReadyCheckMemberState + EventTypeGroupReadyCheckFinished + EventTypeGroupMemberSubGroupChanged + EventTypeGroupMemberFlagsChanged + EventTypeGroupMemberStateChanged + EventTypeGroupMemberStatesChanged + EventTypeGroupInviteDeclined EventTypeMMJoinedPVPQueue EventTypeMMInvitedToBGOrArena EventTypeMMInviteToBGOrArenaExpired + EventTypeMMLfgStatusChanged + EventTypeMMLfgProposalAccepted + EventTypeMMServiceUnavailable EventTypeFriendStatusChange EventTypeFriendAdded EventTypeFriendRemoved @@ -48,49 +62,68 @@ const ( EventTypeChannelJoined EventTypeChannelLeft EventTypeChannelNotification + EventTypeArenaTeamInviteCreated + EventTypeArenaTeamNativeEvent ) type IncomingWhisperPayload struct { - SenderGUID uint64 - SenderName string - SenderRace uint8 - ReceiverGUID uint64 - ReceiverName string - Language uint32 - Msg string + SenderRealmID uint32 + SenderGUID uint64 + SenderName string + SenderRace uint8 + SenderClass uint8 + SenderGender uint8 + SenderChatTag uint8 + ReceiverRealmID uint32 + ReceiverGUID uint64 + ReceiverName string + Language uint32 + Msg string } type ChannelMessagePayload struct { - RealmID uint32 - ChannelName string - ChannelID uint32 - SenderGUID uint64 - SenderName string - Language uint32 - Message string + RealmID uint32 + ChannelName string + ChannelID uint32 + TeamID uint32 + SenderGUID uint64 + SenderName string + Language uint32 + Message string + SenderChatTag uint8 } type ChannelJoinedPayload struct { - RealmID uint32 - ChannelName string - ChannelID uint32 - PlayerGUID uint64 - PlayerName string - PlayerFlags uint8 + RealmID uint32 + ChannelName string + ChannelID uint32 + ChannelFlags uint32 + TeamID uint32 + NumMembers uint32 + PlayerGUID uint64 + PlayerName string + PlayerFlags uint8 } type ChannelLeftPayload struct { - RealmID uint32 - ChannelName string - ChannelID uint32 - PlayerGUID uint64 - PlayerName string + RealmID uint32 + ChannelName string + ChannelID uint32 + ChannelFlags uint32 + TeamID uint32 + NumMembers uint32 + PlayerGUID uint64 + PlayerName string + Silent bool } type ChannelNotificationPayload struct { RealmID uint32 ChannelName string ChannelID uint32 + ChannelFlags uint32 + TeamID uint32 + NumMembers uint32 NotifyType uint8 TargetGUID uint64 TargetName string @@ -137,7 +170,10 @@ type Broadcaster interface { NewGuildRankCreatedEvent(payload *events.GuildEventRankCreatedPayload) NewGuildRankDeletedEvent(payload *events.GuildEventRankDeletedPayload) NewGuildMessageEvent(payload *events.GuildEventNewMessagePayload) + NewGuildPetitionOfferedEvent(payload *events.GuildEventPetitionOfferedPayload) + NewGuildPetitionSignedEvent(payload *events.GuildEventPetitionSignedPayload) NewGroupInviteCreatedEvent(payload *events.GroupEventInviteCreatedPayload) + NewGroupInviteDeclinedEvent(payload *events.GroupEventInviteDeclinedPayload) NewGroupCreatedEvent(payload *events.GroupEventGroupCreatedPayload) NewGroupMemberOnlineStatusChangedEvent(payload *events.GroupEventGroupMemberOnlineStatusChangedPayload) NewGroupMemberLeftEvent(payload *events.GroupEventGroupMemberLeftPayload) @@ -149,10 +185,20 @@ type Broadcaster interface { NewGroupMessageEvent(payload *events.GroupEventNewMessagePayload) NewGroupTargetIconEvent(payload *events.GroupEventNewTargetIconPayload) NewGroupDifficultyChangedEvent(payload *events.GroupEventGroupDifficultyChangedPayload) + NewGroupReadyCheckStartedEvent(payload *events.GroupEventReadyCheckStartedPayload) + NewGroupReadyCheckMemberStateEvent(payload *events.GroupEventReadyCheckMemberStatePayload) + NewGroupReadyCheckFinishedEvent(payload *events.GroupEventReadyCheckFinishedPayload) + NewGroupMemberSubGroupChangedEvent(payload *events.GroupEventMemberSubGroupChangedPayload) + NewGroupMemberFlagsChangedEvent(payload *events.GroupEventMemberFlagsChangedPayload) + NewGroupMemberStateChangedEvent(payload *events.GroupEventMemberStateChangedPayload) + NewGroupMemberStatesChangedEvent(payload *events.GroupEventMemberStatesChangedPayload) NewMatchmakingJoinedPVPQueueEvent(payload *events.MatchmakingEventPlayersQueuedPayload) NewMatchmakingInvitedToBGOrArenaEvent(payload *events.MatchmakingEventPlayersInvitedPayload) NewMatchmakingInviteToBGOrArenaExpiredEvent(payload *events.MatchmakingEventPlayersInviteExpiredPayload) + NewMatchmakingLfgStatusChangedEvent(payload *events.MatchmakingEventLfgStatusChangedPayload) + NewMatchmakingLfgProposalAcceptedEvent(payload *events.MatchmakingEventLfgProposalAcceptedPayload) + NewMatchmakingServiceUnavailableEvent(payload *events.ServerRegistryEventMatchmakingRemovedUnhealthyPayload) NewFriendStatusChangeEvent(payload *events.FriendEventStatusChangePayload) NewFriendAddedEvent(payload *events.FriendEventAddedPayload) @@ -163,6 +209,8 @@ type Broadcaster interface { NewChannelJoinedEvent(payload *ChannelJoinedPayload) NewChannelLeftEvent(payload *ChannelLeftPayload) NewChannelNotificationEvent(payload *ChannelNotificationPayload) + NewArenaTeamInviteCreatedEvent(payload *events.CharEventArenaTeamInviteCreatedPayload) + NewArenaTeamNativeEvent(payload *events.CharEventArenaTeamNativeEventPayload) } type broadcasterImpl struct { @@ -197,154 +245,269 @@ func (b *broadcasterImpl) UnregisterCharacter(charGUID uint64) { b.channelsMu.Unlock() } +func (b *broadcasterImpl) sendEvent(ch chan Event, event Event) { + if ch == nil { + return + } + + select { + case ch <- event: + default: + log.Warn(). + Int("eventType", int(event.Type)). + Msg("dropping gateway event because session event queue is full") + } +} + func (b *broadcasterImpl) NewIncomingWhisperEvent(payload *IncomingWhisperPayload) { b.channelsMu.RLock() - ch, ok := b.channels[payload.ReceiverGUID] + ch, ok := b.channelForGUIDLocked(payload.ReceiverGUID) b.channelsMu.RUnlock() if !ok { return } - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeIncomingWhisper, Payload: payload, - } + }) } func (b *broadcasterImpl) NewIncomingMailEvent(payload *events.MailEventIncomingMailPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + b.channelsMu.RLock() - ch, ok := b.channels[payload.ReceiverGUID] + ch, ok := b.channelForGUIDLocked(payload.ReceiverGUID) b.channelsMu.RUnlock() if !ok { return } - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeIncomingMail, Payload: payload, - } + }) } func (b *broadcasterImpl) NewGuildInviteCreatedEvent(payload *GuildInviteCreatedPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + b.channelsMu.RLock() - ch, ok := b.channels[payload.InviteeGUID] + ch, ok := b.channelForGUIDLocked(payload.InviteeGUID) b.channelsMu.RUnlock() if !ok { return } - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeGuildInviteCreated, Payload: payload, - } + }) } func (b *broadcasterImpl) NewGuildMemberPromoteEvent(payload *events.GuildEventMemberPromotePayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + for _, ch := range b.channelsForGUIDs(payload.MembersOnline) { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeGuildMemberPromoted, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGuildMemberDemoteEvent(payload *events.GuildEventMemberDemotePayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + for _, ch := range b.channelsForGUIDs(payload.MembersOnline) { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeGuildMemberDemoted, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGuildMemberAddedEvent(payload *events.GuildEventMemberAddedPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + for _, ch := range b.channelsForGUIDs(payload.MembersOnline) { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeGuildMemberAdded, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGuildMemberLeftEvent(payload *events.GuildEventMemberLeftPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + for _, ch := range b.channelsForGUIDs(payload.MembersOnline) { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeGuildMemberLeft, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGuildMemberKickedEvent(payload *events.GuildEventMemberKickedPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + for _, ch := range b.channelsForGUIDs(payload.MembersOnline) { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeGuildMemberKicked, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGuildMOTDUpdatedEvent(payload *events.GuildEventMOTDUpdatedPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + for _, ch := range b.channelsForGUIDs(payload.MembersOnline) { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeGuildMOTDUpdated, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGuildRankUpdatedEvent(payload *events.GuildEventRankUpdatedPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + for _, ch := range b.channelsForGUIDs(payload.MembersOnline) { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeGuildRankUpdated, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGuildRankCreatedEvent(payload *events.GuildEventRankCreatedPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + for _, ch := range b.channelsForGUIDs(payload.MembersOnline) { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeGuildRankCreated, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGuildRankDeletedEvent(payload *events.GuildEventRankDeletedPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + for _, ch := range b.channelsForGUIDs(payload.MembersOnline) { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeGuildRankDeleted, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGuildMessageEvent(payload *events.GuildEventNewMessagePayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + for _, ch := range b.channelsForGUIDs(payload.Receivers) { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeGuildNewMessage, Payload: payload, - } + }) + } +} + +func (b *broadcasterImpl) NewGuildPetitionOfferedEvent(payload *events.GuildEventPetitionOfferedPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + + b.channelsMu.RLock() + ch, ok := b.channelForGUIDLocked(payload.TargetGUID) + b.channelsMu.RUnlock() + + if !ok { + return } + + b.sendEvent(ch, Event{ + Type: EventTypeGuildPetitionOffered, + Payload: payload, + }) +} + +func (b *broadcasterImpl) NewGuildPetitionSignedEvent(payload *events.GuildEventPetitionSignedPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + + b.channelsMu.RLock() + ch, ok := b.channelForGUIDLocked(payload.OwnerGUID) + b.channelsMu.RUnlock() + + if !ok { + return + } + + b.sendEvent(ch, Event{ + Type: EventTypeGuildPetitionSigned, + Payload: payload, + }) } func (b *broadcasterImpl) NewGroupInviteCreatedEvent(payload *events.GroupEventInviteCreatedPayload) { b.channelsMu.RLock() - ch, ok := b.channels[payload.InviteeGUID] + ch, ok := b.channelForGroupGUIDLocked(payload.RealmID, payload.InviteeGUID) b.channelsMu.RUnlock() if !ok { return } - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeGroupInviteCreated, Payload: payload, + }) +} + +func (b *broadcasterImpl) NewGroupInviteDeclinedEvent(payload *events.GroupEventInviteDeclinedPayload) { + b.channelsMu.RLock() + ch, ok := b.channelForGroupGUIDLocked(payload.RealmID, payload.InviterGUID) + b.channelsMu.RUnlock() + + if !ok { + return } + + b.sendEvent(ch, Event{ + Type: EventTypeGroupInviteDeclined, + Payload: payload, + }) } func (b *broadcasterImpl) NewGroupCreatedEvent(payload *events.GroupEventGroupCreatedPayload) { @@ -352,236 +515,414 @@ func (b *broadcasterImpl) NewGroupCreatedEvent(payload *events.GroupEventGroupCr for i := range payload.Members { membersGuids[i] = payload.Members[i].MemberGUID } - for _, ch := range b.channelsForGUIDs(membersGuids) { - ch <- Event{ + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, membersGuids) { + b.sendEvent(ch, Event{ Type: EventTypeGroupCreated, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGroupMemberOnlineStatusChangedEvent(payload *events.GroupEventGroupMemberOnlineStatusChangedPayload) { - for _, ch := range b.channelsForGUIDs(payload.OnlineMembers) { - ch <- Event{ + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.OnlineMembers) { + b.sendEvent(ch, Event{ Type: EventTypeGroupMemberOnlineStatusChanged, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGroupMemberLeftEvent(payload *events.GroupEventGroupMemberLeftPayload) { - for _, ch := range b.channelsForGUIDs(payload.OnlineMembers) { - ch <- Event{ + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.OnlineMembers) { + b.sendEvent(ch, Event{ Type: EventTypeGroupMemberLeft, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGroupDisbandEvent(payload *events.GroupEventGroupDisbandPayload) { - for _, ch := range b.channelsForGUIDs(payload.OnlineMembers) { - ch <- Event{ + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.OnlineMembers) { + b.sendEvent(ch, Event{ Type: EventTypeGroupDisband, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGroupMemberAddedEvent(payload *events.GroupEventGroupMemberAddedPayload) { - for _, ch := range b.channelsForGUIDs(payload.OnlineMembers) { - ch <- Event{ + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.OnlineMembers) { + b.sendEvent(ch, Event{ Type: EventTypeGroupMemberAdded, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGroupLeaderChangedEvent(payload *events.GroupEventGroupLeaderChangedPayload) { - for _, ch := range b.channelsForGUIDs(payload.OnlineMembers) { - ch <- Event{ + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.OnlineMembers) { + b.sendEvent(ch, Event{ Type: EventTypeGroupLeaderChanged, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGroupLootTypeChangedEvent(payload *events.GroupEventGroupLootTypeChangedPayload) { - for _, ch := range b.channelsForGUIDs(payload.OnlineMembers) { - ch <- Event{ + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.OnlineMembers) { + b.sendEvent(ch, Event{ Type: EventTypeGroupLootTypeChanged, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGroupConvertedToRaidEvent(payload *events.GroupEventGroupConvertedToRaidPayload) { - for _, ch := range b.channelsForGUIDs(payload.OnlineMembers) { - ch <- Event{ + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.OnlineMembers) { + b.sendEvent(ch, Event{ Type: EventTypeGroupConvertedToRaid, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGroupMessageEvent(payload *events.GroupEventNewMessagePayload) { - for _, ch := range b.channelsForGUIDs(payload.Receivers) { - ch <- Event{ + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.Receivers) { + b.sendEvent(ch, Event{ Type: EventTypeGroupNewMessage, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGroupTargetIconEvent(payload *events.GroupEventNewTargetIconPayload) { - for _, ch := range b.channelsForGUIDs(payload.Receivers) { - ch <- Event{ + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.Receivers) { + b.sendEvent(ch, Event{ Type: EventTypeGroupNewTargetIcon, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewGroupDifficultyChangedEvent(payload *events.GroupEventGroupDifficultyChangedPayload) { - for _, ch := range b.channelsForGUIDs(payload.Receivers) { - ch <- Event{ + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.Receivers) { + b.sendEvent(ch, Event{ Type: EventTypeGroupDifficultyChanged, Payload: payload, - } + }) + } +} + +func (b *broadcasterImpl) NewGroupReadyCheckStartedEvent(payload *events.GroupEventReadyCheckStartedPayload) { + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.Receivers) { + b.sendEvent(ch, Event{ + Type: EventTypeGroupReadyCheckStarted, + Payload: payload, + }) + } +} + +func (b *broadcasterImpl) NewGroupReadyCheckMemberStateEvent(payload *events.GroupEventReadyCheckMemberStatePayload) { + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.Receivers) { + b.sendEvent(ch, Event{ + Type: EventTypeGroupReadyCheckMemberState, + Payload: payload, + }) + } +} + +func (b *broadcasterImpl) NewGroupReadyCheckFinishedEvent(payload *events.GroupEventReadyCheckFinishedPayload) { + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.Receivers) { + b.sendEvent(ch, Event{ + Type: EventTypeGroupReadyCheckFinished, + Payload: payload, + }) + } +} + +func (b *broadcasterImpl) NewGroupMemberSubGroupChangedEvent(payload *events.GroupEventMemberSubGroupChangedPayload) { + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.Receivers) { + b.sendEvent(ch, Event{ + Type: EventTypeGroupMemberSubGroupChanged, + Payload: payload, + }) + } +} + +func (b *broadcasterImpl) NewGroupMemberFlagsChangedEvent(payload *events.GroupEventMemberFlagsChangedPayload) { + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.Receivers) { + b.sendEvent(ch, Event{ + Type: EventTypeGroupMemberFlagsChanged, + Payload: payload, + }) + } +} + +func (b *broadcasterImpl) NewGroupMemberStateChangedEvent(payload *events.GroupEventMemberStateChangedPayload) { + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.Receivers) { + b.sendEvent(ch, Event{ + Type: EventTypeGroupMemberStateChanged, + Payload: payload, + }) + } +} + +func (b *broadcasterImpl) NewGroupMemberStatesChangedEvent(payload *events.GroupEventMemberStatesChangedPayload) { + for _, ch := range b.channelsForGroupGUIDs(payload.RealmID, payload.Receivers) { + b.sendEvent(ch, Event{ + Type: EventTypeGroupMemberStatesChanged, + Payload: payload, + }) } } func (b *broadcasterImpl) NewMatchmakingJoinedPVPQueueEvent(payload *events.MatchmakingEventPlayersQueuedPayload) { - if payload.RealmID != gateway.RealmID { + if !isLocalRealmEvent(payload.RealmID) { return } for _, ch := range b.channelsForGUIDs(convertLowGUIDsToUint64(payload.PlayersGUID)) { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeMMJoinedPVPQueue, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewMatchmakingInvitedToBGOrArenaEvent(payload *events.MatchmakingEventPlayersInvitedPayload) { - if payload.RealmID != gateway.RealmID { + if !isLocalRealmEvent(payload.RealmID) { return } for _, ch := range b.channelsForGUIDs(convertLowGUIDsToUint64(payload.PlayersGUID)) { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeMMInvitedToBGOrArena, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewMatchmakingInviteToBGOrArenaExpiredEvent(payload *events.MatchmakingEventPlayersInviteExpiredPayload) { - if payload.RealmID != gateway.RealmID { + if !isLocalRealmEvent(payload.RealmID) { return } for _, ch := range b.channelsForGUIDs(convertLowGUIDsToUint64(payload.PlayersGUID)) { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeMMInviteToBGOrArenaExpired, Payload: payload, - } + }) + } +} + +func (b *broadcasterImpl) NewMatchmakingLfgStatusChangedEvent(payload *events.MatchmakingEventLfgStatusChangedPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + + for _, ch := range b.channelsForGUIDs(convertLowGUIDsToUint64(payload.PlayersGUID)) { + b.sendEvent(ch, Event{ + Type: EventTypeMMLfgStatusChanged, + Payload: payload, + }) + } +} + +func (b *broadcasterImpl) NewMatchmakingLfgProposalAcceptedEvent(payload *events.MatchmakingEventLfgProposalAcceptedPayload) { + playerGUIDs := lfgProposalAcceptedLocalPlayers(payload) + if len(playerGUIDs) == 0 { + return + } + + for _, ch := range b.channelsForGUIDs(playerGUIDs) { + b.sendEvent(ch, Event{ + Type: EventTypeMMLfgProposalAccepted, + Payload: payload, + }) + } +} + +func (b *broadcasterImpl) NewMatchmakingServiceUnavailableEvent(payload *events.ServerRegistryEventMatchmakingRemovedUnhealthyPayload) { + b.channelsMu.RLock() + channels := make([]chan Event, 0, len(b.channels)) + for _, ch := range b.channels { + channels = append(channels, ch) + } + b.channelsMu.RUnlock() + + for _, ch := range channels { + b.sendEvent(ch, Event{ + Type: EventTypeMMServiceUnavailable, + Payload: payload, + }) } } func (b *broadcasterImpl) NewFriendStatusChangeEvent(payload *events.FriendEventStatusChangePayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + for _, ch := range b.channelsForGUIDs(payload.NotifyPlayers) { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeFriendStatusChange, Payload: payload, - } + }) } } func (b *broadcasterImpl) NewFriendAddedEvent(payload *events.FriendEventAddedPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + b.channelsMu.RLock() - ch, ok := b.channels[payload.PlayerGUID] + ch, ok := b.channelForGUIDLocked(payload.PlayerGUID) b.channelsMu.RUnlock() if !ok { return } - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeFriendAdded, Payload: payload, - } + }) } func (b *broadcasterImpl) NewFriendRemovedEvent(payload *events.FriendEventRemovedPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + b.channelsMu.RLock() - ch, ok := b.channels[payload.PlayerGUID] + ch, ok := b.channelForGUIDLocked(payload.PlayerGUID) b.channelsMu.RUnlock() if !ok { return } - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeFriendRemoved, Payload: payload, - } + }) } func (b *broadcasterImpl) NewFriendNoteUpdateEvent(payload *events.FriendEventNoteUpdatePayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + b.channelsMu.RLock() - ch, ok := b.channels[payload.PlayerGUID] + ch, ok := b.channelForGUIDLocked(payload.PlayerGUID) b.channelsMu.RUnlock() if !ok { return } - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeFriendNoteUpdate, Payload: payload, + }) +} + +func (b *broadcasterImpl) NewArenaTeamInviteCreatedEvent(payload *events.CharEventArenaTeamInviteCreatedPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + + b.channelsMu.RLock() + ch, ok := b.channelForGUIDLocked(payload.TargetGUID) + b.channelsMu.RUnlock() + + if !ok { + return + } + + b.sendEvent(ch, Event{ + Type: EventTypeArenaTeamInviteCreated, + Payload: payload, + }) +} + +func (b *broadcasterImpl) NewArenaTeamNativeEvent(payload *events.CharEventArenaTeamNativeEventPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + + b.channelsMu.RLock() + defer b.channelsMu.RUnlock() + + for _, receiverGUID := range payload.ReceiverGUIDs { + ch, ok := b.channelForGUIDLocked(receiverGUID) + if !ok { + continue + } + + b.sendEvent(ch, Event{ + Type: EventTypeArenaTeamNativeEvent, + Payload: payload, + }) } } func (b *broadcasterImpl) NewChannelMessageEvent(payload *ChannelMessagePayload) { - b.chatChannelsService.BroadcastToChannel(strings.ToLower(payload.ChannelName), Event{ + if !isLocalRealmEvent(payload.RealmID) { + return + } + + b.chatChannelsService.BroadcastToScopedChannel(payload.RealmID, payload.TeamID, payload.ChannelName, Event{ Type: EventTypeChannelMessage, Payload: payload, }) } func (b *broadcasterImpl) NewChannelJoinedEvent(payload *ChannelJoinedPayload) { - b.chatChannelsService.BroadcastToChannel(strings.ToLower(payload.ChannelName), Event{ + if !isLocalRealmEvent(payload.RealmID) { + return + } + + b.chatChannelsService.BroadcastToScopedChannel(payload.RealmID, payload.TeamID, payload.ChannelName, Event{ Type: EventTypeChannelJoined, Payload: payload, }) } func (b *broadcasterImpl) NewChannelLeftEvent(payload *ChannelLeftPayload) { - b.chatChannelsService.BroadcastToChannel(strings.ToLower(payload.ChannelName), Event{ + if !isLocalRealmEvent(payload.RealmID) { + return + } + + b.chatChannelsService.BroadcastToScopedChannel(payload.RealmID, payload.TeamID, payload.ChannelName, Event{ Type: EventTypeChannelLeft, Payload: payload, }) } func (b *broadcasterImpl) NewChannelNotificationEvent(payload *ChannelNotificationPayload) { + if !isLocalRealmEvent(payload.RealmID) { + return + } + // If AffectsPlayer is set, send only to that specific player (e.g., invitations) if payload.AffectsPlayer != 0 { b.channelsMu.RLock() - ch, ok := b.channels[payload.AffectsPlayer] + ch, ok := b.channelForGUIDLocked(payload.AffectsPlayer) b.channelsMu.RUnlock() if ok { - ch <- Event{ + b.sendEvent(ch, Event{ Type: EventTypeChannelNotification, Payload: payload, - } + }) } return } // Otherwise broadcast to all channel members - b.chatChannelsService.BroadcastToChannel(strings.ToLower(payload.ChannelName), Event{ + b.chatChannelsService.BroadcastToScopedChannel(payload.RealmID, payload.TeamID, payload.ChannelName, Event{ Type: EventTypeChannelNotification, Payload: payload, }) @@ -589,12 +930,19 @@ func (b *broadcasterImpl) NewChannelNotificationEvent(payload *ChannelNotificati func (b *broadcasterImpl) channelsForGUIDs(guids []uint64) []chan Event { channels := make([]chan Event, 0, len(guids)) + seen := make(map[chan Event]struct{}, len(guids)) b.channelsMu.RLock() for _, guid := range guids { - ch, ok := b.channels[guid] + ch, ok := b.channelForGUIDLocked(guid) if !ok { continue } + + if _, ok = seen[ch]; ok { + continue + } + + seen[ch] = struct{}{} channels = append(channels, ch) } b.channelsMu.RUnlock() @@ -602,6 +950,103 @@ func (b *broadcasterImpl) channelsForGUIDs(guids []uint64) []chan Event { return channels } +func (b *broadcasterImpl) channelsForGroupGUIDs(groupRealmID uint32, guids []uint64) []chan Event { + channels := make([]chan Event, 0, len(guids)) + seen := make(map[chan Event]struct{}, len(guids)) + b.channelsMu.RLock() + for _, guid := range guids { + ch, ok := b.channelForGroupGUIDLocked(groupRealmID, guid) + if !ok { + continue + } + + if _, ok = seen[ch]; ok { + continue + } + + seen[ch] = struct{}{} + channels = append(channels, ch) + } + b.channelsMu.RUnlock() + + return channels +} + +func isLocalRealmEvent(realmID uint32) bool { + return realmID == gateway.RealmID +} + +func lfgProposalAcceptedLocalPlayers(payload *events.MatchmakingEventLfgProposalAcceptedPayload) []uint64 { + if payload == nil { + return nil + } + if len(payload.Members) == 0 { + if !isLocalRealmEvent(payload.RealmID) { + return nil + } + return convertLowGUIDsToUint64(payload.PlayersGUID) + } + + players := make([]uint64, 0, len(payload.Members)) + for _, member := range payload.Members { + realmID := member.RealmID + if realmID == 0 { + realmID = payload.RealmID + } + if realmID != gateway.RealmID { + continue + } + players = append(players, uint64(member.PlayerGUID)) + } + return players +} + +func (b *broadcasterImpl) channelForGUIDLocked(playerGUID uint64) (chan Event, bool) { + ch, ok := b.channels[playerGUID] + if ok { + return ch, true + } + + lowGUID := playerLowGUID(playerGUID) + if lowGUID == playerGUID { + return nil, false + } + + ch, ok = b.channels[lowGUID] + return ch, ok +} + +func (b *broadcasterImpl) channelForGroupGUIDLocked(groupRealmID uint32, playerGUID uint64) (chan Event, bool) { + if !isLocalGroupPlayer(groupRealmID, playerGUID) { + return nil, false + } + return b.channelForGUIDLocked(playerGUID) +} + +func isLocalGroupPlayer(groupRealmID uint32, playerGUID uint64) bool { + if playerGUID == 0 { + return false + } + if playerGUID>>48 == 0 { + if playerRealmID := uint32((playerGUID >> 32) & 0xffff); playerRealmID != 0 { + return playerRealmID == gateway.RealmID + } + } + return groupRealmID == gateway.RealmID +} + +func playerLowGUID(playerGUID uint64) uint64 { + if playerGUID == 0 || playerGUID>>32 == 0 || playerGUID>>48 != 0 { + return playerGUID + } + + if uint32((playerGUID>>32)&0xffff) != gateway.RealmID { + return playerGUID + } + + return playerGUID & 0xffffffff +} + func convertLowGUIDsToUint64(s []guid.LowType) []uint64 { r := make([]uint64, len(s)) for i, lowType := range s { diff --git a/apps/gateway/events-broadcaster/chat-channels.go b/apps/gateway/events-broadcaster/chat-channels.go index 996e810..d0018d9 100644 --- a/apps/gateway/events-broadcaster/chat-channels.go +++ b/apps/gateway/events-broadcaster/chat-channels.go @@ -1,11 +1,26 @@ package events_broadcaster import ( + "strings" "sync" "github.com/rs/zerolog/log" ) +type ChatChannelScope struct { + RealmID uint32 + TeamID uint32 + Name string +} + +func NewChatChannelScope(realmID uint32, teamID uint32, name string) ChatChannelScope { + return ChatChannelScope{ + RealmID: realmID, + TeamID: teamID, + Name: strings.ToLower(name), + } +} + type PlayerStreams struct { streams map[uint64]chan Event mu sync.RWMutex @@ -91,41 +106,41 @@ func (c *ChatChannel) Members() []uint64 { } type ChatChannelsInMemRepo struct { - channels map[string]*ChatChannel + channels map[ChatChannelScope]*ChatChannel channelsMu sync.RWMutex } func NewChatChannelsInMemRepo() *ChatChannelsInMemRepo { return &ChatChannelsInMemRepo{ - channels: make(map[string]*ChatChannel), + channels: make(map[ChatChannelScope]*ChatChannel), } } -func (r *ChatChannelsInMemRepo) GetOrCreate(name string) *ChatChannel { +func (r *ChatChannelsInMemRepo) GetOrCreate(scope ChatChannelScope) *ChatChannel { r.channelsMu.Lock() defer r.channelsMu.Unlock() - if ch, ok := r.channels[name]; ok { + if ch, ok := r.channels[scope]; ok { return ch } - ch := NewChatChannel(name) - r.channels[name] = ch + ch := NewChatChannel(scope.Name) + r.channels[scope] = ch return ch } -func (r *ChatChannelsInMemRepo) Get(name string) *ChatChannel { +func (r *ChatChannelsInMemRepo) Get(scope ChatChannelScope) *ChatChannel { r.channelsMu.RLock() defer r.channelsMu.RUnlock() - return r.channels[name] + return r.channels[scope] } -func (r *ChatChannelsInMemRepo) Remove(name string) { +func (r *ChatChannelsInMemRepo) Remove(scope ChatChannelScope) { r.channelsMu.Lock() defer r.channelsMu.Unlock() - delete(r.channels, name) + delete(r.channels, scope) } type ChatChannelsService struct { @@ -133,7 +148,7 @@ type ChatChannelsService struct { playerStreams *PlayerStreams // reverse index: player -> channels - playerChannels map[uint64]map[string]struct{} + playerChannels map[uint64]map[ChatChannelScope]struct{} pcMu sync.RWMutex } @@ -141,33 +156,43 @@ func NewChatChannelsService() *ChatChannelsService { return &ChatChannelsService{ repo: NewChatChannelsInMemRepo(), playerStreams: NewPlayerStreams(), - playerChannels: make(map[uint64]map[string]struct{}), + playerChannels: make(map[uint64]map[ChatChannelScope]struct{}), } } func (s *ChatChannelsService) AddPlayerToChannel(playerGUID uint64, chanName string) <-chan Event { - channel := s.repo.GetOrCreate(chanName) + return s.AddPlayerToScopedChannel(playerGUID, 0, 0, chanName) +} + +func (s *ChatChannelsService) AddPlayerToScopedChannel(playerGUID uint64, realmID uint32, teamID uint32, chanName string) <-chan Event { + scope := NewChatChannelScope(realmID, teamID, chanName) + channel := s.repo.GetOrCreate(scope) channel.AddMember(playerGUID) s.pcMu.Lock() if _, ok := s.playerChannels[playerGUID]; !ok { - s.playerChannels[playerGUID] = make(map[string]struct{}) + s.playerChannels[playerGUID] = make(map[ChatChannelScope]struct{}) } - s.playerChannels[playerGUID][chanName] = struct{}{} + s.playerChannels[playerGUID][scope] = struct{}{} s.pcMu.Unlock() return s.playerStreams.GetOrCreate(playerGUID) } func (s *ChatChannelsService) RemovePlayerFromChannel(playerGUID uint64, chanName string) { - channel := s.repo.Get(chanName) + s.RemovePlayerFromScopedChannel(playerGUID, 0, 0, chanName) +} + +func (s *ChatChannelsService) RemovePlayerFromScopedChannel(playerGUID uint64, realmID uint32, teamID uint32, chanName string) { + scope := NewChatChannelScope(realmID, teamID, chanName) + channel := s.repo.Get(scope) if channel != nil { channel.RemoveMember(playerGUID) } s.pcMu.Lock() if chans, ok := s.playerChannels[playerGUID]; ok { - delete(chans, chanName) + delete(chans, scope) if len(chans) == 0 { delete(s.playerChannels, playerGUID) } @@ -176,7 +201,12 @@ func (s *ChatChannelsService) RemovePlayerFromChannel(playerGUID uint64, chanNam } func (s *ChatChannelsService) BroadcastToChannel(channelName string, event Event) { - channel := s.repo.Get(channelName) + s.BroadcastToScopedChannel(0, 0, channelName, event) +} + +func (s *ChatChannelsService) BroadcastToScopedChannel(realmID uint32, teamID uint32, channelName string, event Event) { + scope := NewChatChannelScope(realmID, teamID, channelName) + channel := s.repo.Get(scope) if channel == nil { return } @@ -193,7 +223,9 @@ func (s *ChatChannelsService) BroadcastToChannel(channelName string, event Event log.Warn(). Uint64("playerGUID", playerGUID). Int("eventType", int(event.Type)). - Str("channel", channelName). + Uint32("realmID", realmID). + Uint32("teamID", teamID). + Str("channel", scope.Name). Msg("Dropped channel event because channel is full") } } @@ -208,8 +240,8 @@ func (s *ChatChannelsService) DisconnectPlayer(playerGUID uint64) { s.pcMu.Unlock() if ok { - for chanName := range chans { - channel := s.repo.Get(chanName) + for scope := range chans { + channel := s.repo.Get(scope) if channel != nil { channel.RemoveMember(playerGUID) } diff --git a/apps/gateway/packet/opcode.go b/apps/gateway/packet/opcode.go index 55dec11..aa9ecba 100644 --- a/apps/gateway/packet/opcode.go +++ b/apps/gateway/packet/opcode.go @@ -1316,4 +1316,5 @@ const ( SMsgMultipleMoves Opcode = 0x51E TC9CMsgPrepareForRedirect Opcode = 0x51F TC9SMsgReadyForRedirect Opcode = 0x520 + TC9SMsgWorldSessionReady Opcode = 0x521 ) diff --git a/apps/gateway/packet/opcode_string.go b/apps/gateway/packet/opcode_string.go index 0d5a53f..b83dcfa 100644 --- a/apps/gateway/packet/opcode_string.go +++ b/apps/gateway/packet/opcode_string.go @@ -1327,9 +1327,9 @@ const _Opcode_name = "CMsgBootMeCMsgDBLookupSMsgDBLookupCMsgQueryObjectPositionS var _Opcode_index = [...]uint16{0, 10, 22, 34, 57, 80, 103, 126, 143, 161, 172, 183, 206, 228, 250, 262, 276, 293, 311, 325, 345, 361, 386, 402, 417, 439, 458, 477, 491, 505, 528, 549, 566, 581, 592, 603, 620, 634, 651, 668, 685, 702, 715, 734, 748, 761, 777, 793, 812, 832, 862, 879, 896, 914, 928, 940, 954, 974, 988, 1000, 1014, 1029, 1041, 1060, 1079, 1103, 1124, 1142, 1157, 1172, 1188, 1204, 1218, 1232, 1248, 1265, 1283, 1301, 1317, 1336, 1349, 1370, 1386, 1410, 1424, 1446, 1465, 1486, 1513, 1542, 1559, 1584, 1598, 1620, 1639, 1666, 1683, 1708, 1715, 1722, 1731, 1740, 1755, 1770, 1786, 1799, 1812, 1831, 1844, 1857, 1872, 1887, 1902, 1917, 1932, 1948, 1964, 1981, 2002, 2019, 2037, 2055, 2069, 2085, 2103, 2116, 2136, 2158, 2179, 2194, 2209, 2224, 2245, 2267, 2289, 2302, 2315, 2330, 2345, 2361, 2376, 2390, 2405, 2421, 2436, 2449, 2463, 2485, 2499, 2514, 2529, 2544, 2560, 2577, 2592, 2607, 2626, 2645, 2661, 2681, 2703, 2718, 2735, 2752, 2767, 2781, 2797, 2821, 2840, 2856, 2873, 2884, 2896, 2908, 2922, 2940, 2956, 2970, 2986, 3010, 3025, 3044, 3064, 3075, 3097, 3120, 3137, 3148, 3168, 3189, 3204, 3223, 3244, 3260, 3277, 3295, 3315, 3330, 3350, 3368, 3392, 3407, 3423, 3438, 3461, 3479, 3506, 3528, 3552, 3571, 3595, 3614, 3642, 3665, 3688, 3711, 3729, 3756, 3772, 3787, 3806, 3821, 3838, 3854, 3876, 3898, 3921, 3947, 3974, 4004, 4028, 4055, 4072, 4092, 4111, 4133, 4144, 4157, 4173, 4190, 4210, 4226, 4245, 4263, 4279, 4297, 4313, 4325, 4350, 4370, 4390, 4413, 4434, 4451, 4467, 4484, 4501, 4521, 4530, 4539, 4552, 4565, 4588, 4611, 4632, 4651, 4668, 4688, 4700, 4715, 4728, 4749, 4767, 4782, 4808, 4825, 4836, 4860, 4877, 4891, 4904, 4919, 4934, 4951, 4966, 4982, 5000, 5016, 5031, 5054, 5076, 5097, 5119, 5138, 5157, 5175, 5194, 5211, 5228, 5244, 5263, 5279, 5292, 5306, 5320, 5334, 5345, 5361, 5378, 5395, 5409, 5430, 5447, 5462, 5478, 5499, 5513, 5529, 5551, 5575, 5595, 5610, 5624, 5639, 5653, 5678, 5702, 5730, 5755, 5780, 5803, 5828, 5848, 5865, 5881, 5898, 5914, 5934, 5949, 5963, 5984, 6003, 6023, 6040, 6055, 6078, 6094, 6114, 6135, 6143, 6156, 6171, 6187, 6210, 6225, 6244, 6262, 6280, 6298, 6315, 6334, 6350, 6366, 6380, 6396, 6413, 6428, 6446, 6469, 6489, 6509, 6527, 6543, 6556, 6570, 6583, 6601, 6614, 6625, 6640, 6662, 6679, 6697, 6713, 6730, 6745, 6770, 6790, 6809, 6832, 6856, 6885, 6911, 6936, 6963, 6989, 7016, 7041, 7067, 7093, 7113, 7140, 7165, 7186, 7209, 7225, 7246, 7272, 7295, 7317, 7339, 7361, 7383, 7403, 7420, 7437, 7449, 7461, 7472, 7489, 7500, 7513, 7534, 7556, 7573, 7590, 7613, 7631, 7658, 7674, 7695, 7710, 7725, 7740, 7759, 7782, 7802, 7820, 7839, 7857, 7869, 7884, 7905, 7925, 7945, 7960, 7986, 8012, 8028, 8051, 8069, 8086, 8104, 8129, 8146, 8171, 8188, 8203, 8210, 8226, 8240, 8254, 8267, 8288, 8301, 8316, 8333, 8345, 8360, 8374, 8387, 8405, 8422, 8442, 8462, 8481, 8489, 8497, 8514, 8536, 8551, 8568, 8584, 8601, 8626, 8635, 8646, 8670, 8684, 8695, 8713, 8738, 8755, 8770, 8786, 8800, 8816, 8834, 8857, 8876, 8890, 8906, 8932, 8951, 8976, 8998, 9008, 9021, 9047, 9073, 9083, 9102, 9123, 9139, 9155, 9171, 9183, 9201, 9219, 9241, 9263, 9283, 9305, 9326, 9347, 9373, 9405, 9416, 9438, 9459, 9480, 9498, 9529, 9554, 9568, 9592, 9616, 9636, 9660, 9684, 9708, 9724, 9744, 9760, 9776, 9787, 9810, 9833, 9846, 9861, 9873, 9890, 9903, 9917, 9932, 9947, 9963, 9975, 9990, 10013, 10029, 10042, 10058, 10074, 10096, 10119, 10139, 10159, 10171, 10189, 10204, 10222, 10241, 10260, 10279, 10303, 10325, 10342, 10360, 10377, 10402, 10419, 10435, 10453, 10475, 10489, 10511, 10527, 10546, 10563, 10582, 10603, 10629, 10644, 10663, 10676, 10695, 10710, 10729, 10750, 10770, 10795, 10814, 10838, 10859, 10885, 10914, 10942, 10956, 10977, 10993, 11016, 11042, 11069, 11093, 11116, 11127, 11149, 11167, 11184, 11201, 11226, 11244, 11262, 11275, 11290, 11307, 11323, 11342, 11359, 11377, 11390, 11409, 11427, 11439, 11457, 11474, 11493, 11516, 11543, 11564, 11585, 11606, 11622, 11642, 11658, 11675, 11697, 11716, 11736, 11756, 11771, 11784, 11814, 11834, 11858, 11873, 11894, 11920, 11940, 11956, 11978, 11994, 12009, 12035, 12053, 12077, 12097, 12117, 12137, 12154, 12169, 12181, 12198, 12210, 12228, 12246, 12268, 12288, 12312, 12326, 12348, 12368, 12385, 12403, 12418, 12442, 12455, 12473, 12489, 12508, 12519, 12541, 12554, 12571, 12588, 12610, 12625, 12641, 12658, 12675, 12700, 12718, 12741, 12762, 12779, 12798, 12818, 12835, 12860, 12881, 12895, 12909, 12927, 12944, 12967, 12987, 13006, 13025, 13047, 13067, 13089, 13102, 13123, 13144, 13163, 13183, 13204, 13226, 13247, 13271, 13298, 13326, 13357, 13380, 13406, 13419, 13439, 13464, 13489, 13513, 13526, 13540, 13554, 13581, 13611, 13628, 13645, 13673, 13699, 13719, 13732, 13746, 13767, 13791, 13811, 13822, 13841, 13858, 13875, 13891, 13913, 13936, 13955, 13972, 13990, 14011, 14036, 14058, 14080, 14106, 14127, 14147, 14172, 14196, 14218, 14242, 14265, 14287, 14310, 14332, 14356, 14381, 14398, 14416, 14441, 14464, 14483, 14502, 14523, 14543, 14565, 14586, 14604, 14622, 14643, 14663, 14681, 14698, 14721, 14743, 14762, 14779, 14791, 14809, 14828, 14843, 14867, 14891, 14914, 14932, 14959, 14983, 15006, 15027, 15048, 15075, 15091, 15114, 15131, 15155, 15185, 15213, 15238, 15258, 15292, 15310, 15332, 15355, 15363, 15404, 15447, 15491, 15512, 15532, 15549, 15568, 15588, 15602, 15616, 15635, 15661, 15704, 15722, 15748, 15767, 15786, 15805, 15824, 15843, 15863, 15881, 15900, 15920, 15939, 15957, 15982, 16000, 16017, 16035, 16046, 16058, 16075, 16093, 16110, 16131, 16152, 16174, 16191, 16209, 16226, 16245, 16263, 16282, 16297, 16312, 16330, 16355, 16383, 16400, 16415, 16442, 16458, 16473, 16485, 16504, 16518, 16538, 16557, 16582, 16603, 16628, 16653, 16679, 16700, 16730, 16755, 16781, 16810, 16840, 16873, 16897, 16925, 16953, 16973, 16997, 17024, 17038, 17052, 17075, 17093, 17115, 17130, 17146, 17164, 17186, 17207, 17230, 17251, 17270, 17285, 17303, 17330, 17365, 17409, 17430, 17458, 17479, 17509, 17539, 17562, 17591, 17619, 17657, 17687, 17706, 17733, 17760, 17782, 17801, 17813, 17832, 17856, 17878, 17900, 17925, 17938, 17955, 17970, 17991, 18018, 18043, 18065, 18093, 18121, 18146, 18174, 18201, 18231, 18246, 18261, 18292, 18312, 18333, 18354, 18368, 18393, 18405, 18423, 18446, 18466, 18481, 18504, 18525, 18550, 18573, 18587, 18607, 18629, 18654, 18679, 18701, 18719, 18738, 18758, 18778, 18803, 18821, 18839, 18855, 18873, 18894, 18916, 18936, 18957, 18976, 18992, 19014, 19037, 19058, 19075, 19097, 19116, 19138, 19163, 19189, 19209, 19228, 19243, 19261, 19279, 19300, 19317, 19335, 19355, 19373, 19387, 19399, 19421, 19440, 19460, 19479, 19505, 19526, 19550, 19572, 19591, 19613, 19642, 19675, 19708, 19721, 19744, 19760, 19781, 19801, 19823, 19837, 19853, 19875, 19899, 19923, 19940, 19956, 19974, 19996, 20015, 20048, 20076, 20102, 20134, 20157, 20180, 20198, 20219, 20240, 20260, 20283, 20306, 20331, 20347, 20376, 20395, 20415, 20435, 20458, 20478, 20501, 20522, 20542, 20565, 20588, 20609, 20632, 20653, 20682, 20705, 20737, 20761, 20782, 20805, 20826, 20849, 20879, 20902, 20927, 20955, 20985, 21013, 21048, 21082, 21111, 21140, 21177, 21197, 21222, 21248, 21261, 21276, 21289, 21302, 21316, 21329, 21342, 21355, 21369, 21391, 21410, 21425, 21446, 21464, 21484, 21500, 21519, 21543, 21562, 21586, 21613, 21635, 21663, 21691, 21724, 21751, 21780, 21796, 21813, 21829, 21841, 21862, 21887, 21905, 21933, 21963, 21991, 22019, 22044, 22064, 22094, 22115, 22132, 22149, 22165, 22187, 22213, 22239, 22267, 22285, 22306, 22323, 22345, 22362, 22378, 22393, 22413, 22436, 22452, 22468, 22483, 22498, 22513, 22529, 22543, 22558, 22573, 22592, 22610, 22636, 22663, 22690, 22708, 22732, 22746, 22762, 22779, 22793, 22812, 22838, 22857, 22878, 22912, 22934, 22969, 22988, 23010, 23029, 23051, 23073, 23096, 23109, 23131, 23160, 23181, 23203, 23231, 23243, 23265, 23289, 23318, 23345, 23372, 23401, 23430, 23456, 23474, 23488, 23508, 23534, 23568, 23579, 23590, 23613, 23643, 23663, 23683, 23711, 23736, 23751, 23774, 23800, 23832, 23854, 23877, 23903, 23925, 23956, 23978, 24001, 24026, 24049, 24068, 24090, 24115, 24136, 24160, 24180, 24208, 24235, 24254, 24279, 24292, 24305, 24326, 24347, 24371, 24388, 24411, 24440, 24477, 24502, 24531, 24568, 24598, 24636, 24666, 24691, 24720, 24749, 24783, 24826, 24846, 24862, 24878, 24899, 24921, 24942, 24968, 24994, 25020, 25034, 25048, 25075, 25102, 25120, 25137, 25166, 25184, 25202, 25221, 25239, 25267, 25291, 25323, 25338, 25363, 25389, 25412, 25435, 25454, 25479, 25504, 25519, 25539, 25560, 25578, 25599, 25615, 25634, 25660, 25684, 25705, 25725, 25749, 25772, 25798, 25820, 25844, 25869, 25904, 25939, 25974, 25991, 26016, 26039} func (i Opcode) String() string { - i -= 1 - if i >= Opcode(len(_Opcode_index)-1) { - return "Opcode(" + strconv.FormatInt(int64(i+1), 10) + ")" + idx := int(i) - 1 + if i < 1 || idx >= len(_Opcode_index)-1 { + return "Opcode(" + strconv.FormatInt(int64(i), 10) + ")" } - return _Opcode_name[_Opcode_index[i]:_Opcode_index[i+1]] + return _Opcode_name[_Opcode_index[idx]:_Opcode_index[idx+1]] } diff --git a/apps/gateway/service/characters-updates-barrier.go b/apps/gateway/service/characters-updates-barrier.go index 990f00e..f8136bf 100644 --- a/apps/gateway/service/characters-updates-barrier.go +++ b/apps/gateway/service/characters-updates-barrier.go @@ -28,15 +28,15 @@ func NewCharactersUpdatesBarrier(logger *zerolog.Logger, producer events.Gateway } func (b *CharactersUpdatesBarrier) UpdateLevel(charGUID uint64, lvl uint8) { - b.updsChan <- events.CharacterUpdate{ID: charGUID, Lvl: &lvl} + b.updsChan <- events.CharacterUpdate{ID: charGUID, EventTimeUnixNano: uint64(time.Now().UnixNano()), Lvl: &lvl} } func (b *CharactersUpdatesBarrier) UpdateZone(charGUID uint64, area, zone uint32) { - b.updsChan <- events.CharacterUpdate{ID: charGUID, Area: &area, Zone: &zone} + b.updsChan <- events.CharacterUpdate{ID: charGUID, EventTimeUnixNano: uint64(time.Now().UnixNano()), Area: &area, Zone: &zone} } func (b *CharactersUpdatesBarrier) UpdateMap(charGUID uint64, mapID uint32) { - b.updsChan <- events.CharacterUpdate{ID: charGUID, Map: &mapID} + b.updsChan <- events.CharacterUpdate{ID: charGUID, EventTimeUnixNano: uint64(time.Now().UnixNano()), Map: &mapID} } func (b *CharactersUpdatesBarrier) Run(ctx context.Context) { @@ -100,6 +100,10 @@ func (b *CharactersUpdatesBarrier) send(upds map[uint64]*events.CharacterUpdate) } func mergeCharUpdates(oldCharUpd, newCharUpd events.CharacterUpdate) events.CharacterUpdate { + if newCharUpd.EventTimeUnixNano > oldCharUpd.EventTimeUnixNano { + oldCharUpd.EventTimeUnixNano = newCharUpd.EventTimeUnixNano + } + if newCharUpd.Lvl != nil { oldCharUpd.Lvl = newCharUpd.Lvl } diff --git a/apps/gateway/service/listener-characters.go b/apps/gateway/service/listener-characters.go new file mode 100644 index 0000000..d86d0e2 --- /dev/null +++ b/apps/gateway/service/listener-characters.go @@ -0,0 +1,77 @@ +package service + +import ( + "encoding/json" + + "github.com/nats-io/nats.go" + "github.com/rs/zerolog/log" + + eBroadcaster "github.com/walkline/ToCloud9/apps/gateway/events-broadcaster" + "github.com/walkline/ToCloud9/shared/events" +) + +type charactersNatsListener struct { + nc *nats.Conn + subs []*nats.Subscription + broadcaster eBroadcaster.Broadcaster +} + +func NewCharactersNatsListener(nc *nats.Conn, broadcaster eBroadcaster.Broadcaster) Listener { + return &charactersNatsListener{ + nc: nc, + broadcaster: broadcaster, + } +} + +func (c *charactersNatsListener) Listen() error { + if err := c.newSubscribe(events.CharEventArenaTeamInviteCreated, func() (interface{}, func()) { + d := &events.CharEventArenaTeamInviteCreatedPayload{} + return d, func() { + c.broadcaster.NewArenaTeamInviteCreatedEvent(d) + } + }); err != nil { + return err + } + + return c.newSubscribe(events.CharEventArenaTeamNativeEvent, func() (interface{}, func()) { + d := &events.CharEventArenaTeamNativeEventPayload{} + return d, func() { + c.broadcaster.NewArenaTeamNativeEvent(d) + } + }) +} + +func (c *charactersNatsListener) Stop() error { + for _, sub := range c.subs { + if err := sub.Unsubscribe(); err != nil { + return err + } + } + return nil +} + +func (c *charactersNatsListener) newSubscribe(event events.CharactersServiceEvent, payloadAndHandler func() (interface{}, func())) error { + sb, err := c.nc.Subscribe(event.SubjectName(), func(msg *nats.Msg) { + p := events.EventToReadGenericPayload{} + err := json.Unmarshal(msg.Data, &p) + if err != nil { + log.Error().Err(err).Msgf("can't read %v event", event) + return + } + + payload, handler := payloadAndHandler() + err = json.Unmarshal(p.Payload, payload) + if err != nil { + log.Error().Err(err).Msgf("can't read %d (payload part) event", event) + return + } + + handler() + }) + if err != nil { + return err + } + + c.subs = append(c.subs, sb) + return nil +} diff --git a/apps/gateway/service/listener-chat.go b/apps/gateway/service/listener-chat.go index 11fa14b..1b0a76a 100644 --- a/apps/gateway/service/listener-chat.go +++ b/apps/gateway/service/listener-chat.go @@ -43,13 +43,18 @@ func (c *chatNatsListener) Listen() error { } c.broadcaster.NewIncomingWhisperEvent(&eBroadcaster.IncomingWhisperPayload{ - SenderGUID: chatMsg.SenderGUID, - SenderName: chatMsg.SenderName, - SenderRace: chatMsg.SenderRace, - ReceiverGUID: chatMsg.ReceiverGUID, - ReceiverName: chatMsg.ReceiverName, - Language: chatMsg.Language, - Msg: chatMsg.Msg, + SenderRealmID: chatMsg.SenderRealmID, + SenderGUID: chatMsg.SenderGUID, + SenderName: chatMsg.SenderName, + SenderRace: chatMsg.SenderRace, + SenderClass: chatMsg.SenderClass, + SenderGender: chatMsg.SenderGender, + SenderChatTag: chatMsg.SenderChatTag, + ReceiverRealmID: chatMsg.ReceiverRealmID, + ReceiverGUID: chatMsg.ReceiverGUID, + ReceiverName: chatMsg.ReceiverName, + Language: chatMsg.Language, + Msg: chatMsg.Msg, }) }) if err != nil { @@ -75,13 +80,15 @@ func (c *chatNatsListener) Listen() error { } c.broadcaster.NewChannelMessageEvent(&eBroadcaster.ChannelMessagePayload{ - RealmID: channelMsg.RealmID, - ChannelName: channelMsg.ChannelName, - ChannelID: channelMsg.ChannelID, - SenderGUID: channelMsg.SenderGUID, - SenderName: channelMsg.SenderName, - Language: channelMsg.Language, - Message: channelMsg.Message, + RealmID: channelMsg.RealmID, + ChannelName: channelMsg.ChannelName, + ChannelID: channelMsg.ChannelID, + TeamID: channelMsg.TeamID, + SenderGUID: channelMsg.SenderGUID, + SenderName: channelMsg.SenderName, + Language: channelMsg.Language, + Message: channelMsg.Message, + SenderChatTag: channelMsg.SenderChatTag, }) }) if err != nil { @@ -107,12 +114,15 @@ func (c *chatNatsListener) Listen() error { } c.broadcaster.NewChannelJoinedEvent(&eBroadcaster.ChannelJoinedPayload{ - RealmID: channelJoin.RealmID, - ChannelName: channelJoin.ChannelName, - ChannelID: channelJoin.ChannelID, - PlayerGUID: channelJoin.PlayerGUID, - PlayerName: channelJoin.PlayerName, - PlayerFlags: channelJoin.PlayerFlags, + RealmID: channelJoin.RealmID, + ChannelName: channelJoin.ChannelName, + ChannelID: channelJoin.ChannelID, + ChannelFlags: channelJoin.ChannelFlags, + TeamID: channelJoin.TeamID, + NumMembers: channelJoin.NumMembers, + PlayerGUID: channelJoin.PlayerGUID, + PlayerName: channelJoin.PlayerName, + PlayerFlags: channelJoin.PlayerFlags, }) }) if err != nil { @@ -138,11 +148,15 @@ func (c *chatNatsListener) Listen() error { } c.broadcaster.NewChannelLeftEvent(&eBroadcaster.ChannelLeftPayload{ - RealmID: channelLeft.RealmID, - ChannelName: channelLeft.ChannelName, - ChannelID: channelLeft.ChannelID, - PlayerGUID: channelLeft.PlayerGUID, - PlayerName: channelLeft.PlayerName, + RealmID: channelLeft.RealmID, + ChannelName: channelLeft.ChannelName, + ChannelID: channelLeft.ChannelID, + ChannelFlags: channelLeft.ChannelFlags, + TeamID: channelLeft.TeamID, + NumMembers: channelLeft.NumMembers, + PlayerGUID: channelLeft.PlayerGUID, + PlayerName: channelLeft.PlayerName, + Silent: channelLeft.Silent, }) }) if err != nil { @@ -171,6 +185,9 @@ func (c *chatNatsListener) Listen() error { RealmID: channelNotif.RealmID, ChannelName: channelNotif.ChannelName, ChannelID: channelNotif.ChannelID, + ChannelFlags: channelNotif.ChannelFlags, + TeamID: channelNotif.TeamID, + NumMembers: channelNotif.NumMembers, NotifyType: channelNotif.NotifyType, TargetGUID: channelNotif.TargetGUID, TargetName: channelNotif.TargetName, diff --git a/apps/gateway/service/listener-group.go b/apps/gateway/service/listener-group.go index 560bd2e..3523012 100644 --- a/apps/gateway/service/listener-group.go +++ b/apps/gateway/service/listener-group.go @@ -19,6 +19,7 @@ func NewGroupNatsListener(nc *nats.Conn, broadcaster eBroadcaster.Broadcaster) L listener.consumer = events.NewGroupEventsConsumer( nc, events.WithGroupEventConsumerInviteCreatedHandler(listener), + events.WithGroupEventConsumerInviteDeclinedHandler(listener), events.WithGroupEventConsumerGroupCreatedHandler(listener), events.WithGroupEventConsumerGroupMemberOnlineStatusChangedHandler(listener), events.WithGroupEventConsumerGroupMemberLeftHandler(listener), @@ -30,6 +31,13 @@ func NewGroupNatsListener(nc *nats.Conn, broadcaster eBroadcaster.Broadcaster) L events.WithGroupEventNewChatMessageHandler(listener), events.WithGroupEventNewTargetIconHandler(listener), events.WithGroupDifficultyChangedHandler(listener), + events.WithGroupEventReadyCheckStartedHandler(listener), + events.WithGroupEventReadyCheckMemberStateHandler(listener), + events.WithGroupEventReadyCheckFinishedHandler(listener), + events.WithGroupEventMemberSubGroupChangedHandler(listener), + events.WithGroupEventMemberFlagsChangedHandler(listener), + events.WithGroupEventMemberStateChangedHandler(listener), + events.WithGroupEventMemberStatesChangedHandler(listener), ) return listener @@ -48,6 +56,11 @@ func (l *groupNatsListener) GroupInviteCreatedEvent(payload *events.GroupEventIn return nil } +func (l *groupNatsListener) GroupInviteDeclinedEvent(payload *events.GroupEventInviteDeclinedPayload) error { + l.broadcaster.NewGroupInviteDeclinedEvent(payload) + return nil +} + func (l *groupNatsListener) GroupCreatedEvent(payload *events.GroupEventGroupCreatedPayload) error { l.broadcaster.NewGroupCreatedEvent(payload) return nil @@ -102,3 +115,38 @@ func (l *groupNatsListener) GroupDifficultyChangedEvent(payload *events.GroupEve l.broadcaster.NewGroupDifficultyChangedEvent(payload) return nil } + +func (l *groupNatsListener) GroupReadyCheckStartedEvent(payload *events.GroupEventReadyCheckStartedPayload) error { + l.broadcaster.NewGroupReadyCheckStartedEvent(payload) + return nil +} + +func (l *groupNatsListener) GroupReadyCheckMemberStateEvent(payload *events.GroupEventReadyCheckMemberStatePayload) error { + l.broadcaster.NewGroupReadyCheckMemberStateEvent(payload) + return nil +} + +func (l *groupNatsListener) GroupReadyCheckFinishedEvent(payload *events.GroupEventReadyCheckFinishedPayload) error { + l.broadcaster.NewGroupReadyCheckFinishedEvent(payload) + return nil +} + +func (l *groupNatsListener) GroupMemberSubGroupChangedEvent(payload *events.GroupEventMemberSubGroupChangedPayload) error { + l.broadcaster.NewGroupMemberSubGroupChangedEvent(payload) + return nil +} + +func (l *groupNatsListener) GroupMemberFlagsChangedEvent(payload *events.GroupEventMemberFlagsChangedPayload) error { + l.broadcaster.NewGroupMemberFlagsChangedEvent(payload) + return nil +} + +func (l *groupNatsListener) GroupMemberStateChangedEvent(payload *events.GroupEventMemberStateChangedPayload) error { + l.broadcaster.NewGroupMemberStateChangedEvent(payload) + return nil +} + +func (l *groupNatsListener) GroupMemberStatesChangedEvent(payload *events.GroupEventMemberStatesChangedPayload) error { + l.broadcaster.NewGroupMemberStatesChangedEvent(payload) + return nil +} diff --git a/apps/gateway/service/listener-guild.go b/apps/gateway/service/listener-guild.go index 21c1cf9..96b1e19 100644 --- a/apps/gateway/service/listener-guild.go +++ b/apps/gateway/service/listener-guild.go @@ -142,6 +142,26 @@ func (g *guildNatsListener) Listen() error { return err } + err = g.newSubscribe(events.GuildEventPetitionOffered, func() (interface{}, func()) { + d := &events.GuildEventPetitionOfferedPayload{} + return d, func() { + g.broadcaster.NewGuildPetitionOfferedEvent(d) + } + }) + if err != nil { + return err + } + + err = g.newSubscribe(events.GuildEventPetitionSigned, func() (interface{}, func()) { + d := &events.GuildEventPetitionSignedPayload{} + return d, func() { + g.broadcaster.NewGuildPetitionSignedEvent(d) + } + }) + if err != nil { + return err + } + return nil } diff --git a/apps/gateway/service/listener-matchmaking.go b/apps/gateway/service/listener-matchmaking.go index 505b2ed..4ccf029 100644 --- a/apps/gateway/service/listener-matchmaking.go +++ b/apps/gateway/service/listener-matchmaking.go @@ -89,6 +89,51 @@ func (c *matchmakingNatsListener) Listen() error { return err } + c.subs = append(c.subs, sb) + sb, err = c.nc.Subscribe(events.MatchmakingEventLfgStatusChanged.SubjectName(), func(msg *nats.Msg) { + p := events.EventToReadGenericPayload{} + err := json.Unmarshal(msg.Data, &p) + if err != nil { + log.Error().Err(err).Msg("can't read MatchmakingEventLfgStatusChanged event") + return + } + + eventPayload := events.MatchmakingEventLfgStatusChangedPayload{} + err = json.Unmarshal(p.Payload, &eventPayload) + if err != nil { + log.Error().Err(err).Msg("can't read MatchmakingEventLfgStatusChanged (payload part) event") + return + } + + c.broadcaster.NewMatchmakingLfgStatusChangedEvent(&eventPayload) + }) + if err != nil { + return err + } + + c.subs = append(c.subs, sb) + + sb, err = c.nc.Subscribe(events.MatchmakingEventLfgProposalAccepted.SubjectName(), func(msg *nats.Msg) { + p := events.EventToReadGenericPayload{} + err := json.Unmarshal(msg.Data, &p) + if err != nil { + log.Error().Err(err).Msg("can't read MatchmakingEventLfgProposalAccepted event") + return + } + + eventPayload := events.MatchmakingEventLfgProposalAcceptedPayload{} + err = json.Unmarshal(p.Payload, &eventPayload) + if err != nil { + log.Error().Err(err).Msg("can't read MatchmakingEventLfgProposalAccepted (payload part) event") + return + } + + c.broadcaster.NewMatchmakingLfgProposalAcceptedEvent(&eventPayload) + }) + if err != nil { + return err + } + c.subs = append(c.subs, sb) return nil } diff --git a/apps/gateway/service/listener-serversregistry.go b/apps/gateway/service/listener-serversregistry.go new file mode 100644 index 0000000..7a3f637 --- /dev/null +++ b/apps/gateway/service/listener-serversregistry.go @@ -0,0 +1,55 @@ +package service + +import ( + "github.com/nats-io/nats.go" + "github.com/rs/zerolog/log" + + eBroadcaster "github.com/walkline/ToCloud9/apps/gateway/events-broadcaster" + "github.com/walkline/ToCloud9/shared/events" +) + +type serversRegistryNatsListener struct { + nc *nats.Conn + subs []*nats.Subscription + broadcaster eBroadcaster.Broadcaster +} + +func NewServersRegistryNatsListener(nc *nats.Conn, broadcaster eBroadcaster.Broadcaster) Listener { + return &serversRegistryNatsListener{ + nc: nc, + broadcaster: broadcaster, + } +} + +func (c *serversRegistryNatsListener) Listen() error { + sb, err := c.nc.Subscribe(events.ServerRegistryEventMatchmakingRemovedUnhealthy.SubjectName(), func(msg *nats.Msg) { + payload := events.ServerRegistryEventMatchmakingRemovedUnhealthyPayload{} + _, err := events.Unmarshal(msg.Data, &payload) + if err != nil { + log.Error().Err(err).Msg("can't read ServerRegistryEventMatchmakingRemovedUnhealthy event") + return + } + + c.broadcaster.NewMatchmakingServiceUnavailableEvent(&payload) + }) + if err != nil { + return err + } + + c.subs = append(c.subs, sb) + return nil +} + +func (c *serversRegistryNatsListener) Stop() error { + return c.unsubscribe() +} + +func (c *serversRegistryNatsListener) unsubscribe() error { + for _, sub := range c.subs { + if err := sub.Unsubscribe(); err != nil { + return err + } + } + + return nil +} diff --git a/apps/gateway/service/player-state-updates-barrier.go b/apps/gateway/service/player-state-updates-barrier.go new file mode 100644 index 0000000..4283be3 --- /dev/null +++ b/apps/gateway/service/player-state-updates-barrier.go @@ -0,0 +1,747 @@ +package service + +import ( + "context" + "sort" + "strconv" + "strings" + "time" + + "github.com/rs/zerolog" + + "github.com/walkline/ToCloud9/gen/group/pb" + "github.com/walkline/ToCloud9/shared/groupstatetrace" + "github.com/walkline/ToCloud9/shared/wow" +) + +const incompletePlayerStateRetention = 30 * time.Second + +type PlayerStateSnapshot struct { + MemberGUID uint64 + SourceWorldserverID string + Online *bool + Level *uint8 + Class *uint8 + ZoneID *uint32 + MapID *uint32 + InstanceID *uint32 + Health *uint32 + MaxHealth *uint32 + PowerType *uint8 + Power *uint32 + MaxPower *uint32 + AurasKnown bool + Auras []PlayerAuraSnapshot + TimestampMs uint64 + Dead *bool + Ghost *bool +} + +type PlayerAuraSnapshot struct { + Slot uint8 + SpellID uint32 + Flags uint8 +} + +type PlayerStateUpdatesBarrier struct { + logger *zerolog.Logger + + groupServiceClient pb.GroupServiceClient + api string + realmID uint32 + sourceGatewayID string + updsChan chan queuedPlayerStateSnapshot + + flushInterval time.Duration +} + +type queuedPlayerStateSnapshot struct { + snapshot PlayerStateSnapshot + flush bool +} + +func NewPlayerStateUpdatesBarrier( + logger *zerolog.Logger, + groupServiceClient pb.GroupServiceClient, + api string, + realmID uint32, + sourceGatewayID string, + flushInterval time.Duration, +) *PlayerStateUpdatesBarrier { + return &PlayerStateUpdatesBarrier{ + logger: logger, + groupServiceClient: groupServiceClient, + api: api, + realmID: realmID, + sourceGatewayID: sourceGatewayID, + updsChan: make(chan queuedPlayerStateSnapshot, 1000), + flushInterval: flushInterval, + } +} + +func (b *PlayerStateUpdatesBarrier) Update(snapshot PlayerStateSnapshot) { + b.update(snapshot, false) +} + +func (b *PlayerStateUpdatesBarrier) UpdateAndFlush(snapshot PlayerStateSnapshot) { + b.update(snapshot, true) +} + +func (b *PlayerStateUpdatesBarrier) update(snapshot PlayerStateSnapshot, flush bool) { + if snapshot.MemberGUID == 0 { + return + } + + select { + case b.updsChan <- queuedPlayerStateSnapshot{snapshot: snapshot, flush: flush}: + default: + b.logger.Warn(). + Uint64("memberGUID", snapshot.MemberGUID). + Str("sourceWorldserverID", snapshot.SourceWorldserverID). + Msg("dropping player state snapshot because barrier queue is full") + } +} + +func (b *PlayerStateUpdatesBarrier) Run(ctx context.Context) { + t := time.NewTicker(b.flushInterval) + defer t.Stop() + + buffer := map[string]map[uint64]PlayerStateSnapshot{} + lastSent := map[uint64]PlayerStateSnapshot{} + + for { + select { + case <-ctx.Done(): + return + case <-t.C: + if err := b.send(ctx, buffer, lastSent); err != nil { + b.logger.Error().Err(err).Msg("can't send player state updates") + continue + } + case queued := <-b.updsChan: + snapshot := queued.snapshot + bufferPlayerStateSnapshot(buffer, lastSent, snapshot) + if (queued.flush || snapshot.AurasKnown) && hasSendableBufferedSnapshot(buffer, snapshot.MemberGUID) { + if err := b.send(ctx, buffer, lastSent); err != nil { + b.logger.Error().Err(err).Msg("can't send player state updates") + continue + } + } + } + } +} + +func hasSendableBufferedSnapshot(buffer map[string]map[uint64]PlayerStateSnapshot, memberGUID uint64) bool { + for _, updatesByMember := range buffer { + if snapshot, ok := updatesByMember[memberGUID]; ok { + return snapshot.IsSendable() + } + } + + return false +} + +func bufferPlayerStateSnapshot(buffer map[string]map[uint64]PlayerStateSnapshot, lastSent map[uint64]PlayerStateSnapshot, snapshot PlayerStateSnapshot) { + sourceWorldserverID := snapshot.SourceWorldserverID + if sourceWorldserverID == "" { + sourceWorldserverID = lastSent[snapshot.MemberGUID].SourceWorldserverID + snapshot.SourceWorldserverID = sourceWorldserverID + } + + base := lastSent[snapshot.MemberGUID] + for bufferedSource, updatesByMember := range buffer { + if oldSnapshot, ok := updatesByMember[snapshot.MemberGUID]; ok { + base = oldSnapshot + if bufferedSource != sourceWorldserverID { + delete(updatesByMember, snapshot.MemberGUID) + if len(updatesByMember) == 0 { + delete(buffer, bufferedSource) + } + } + } + } + + if shouldDropInactiveDirectPowerSnapshot(base, snapshot) { + return + } + snapshot = normalizeFixedClassPlayerPower(snapshot) + + if buffer[sourceWorldserverID] == nil { + buffer[sourceWorldserverID] = map[uint64]PlayerStateSnapshot{} + } + + buffer[sourceWorldserverID][snapshot.MemberGUID] = normalizeFixedClassPlayerPower(mergePlayerStateSnapshots(base, snapshot)) +} + +func shouldDropInactiveDirectPowerSnapshot(base, update PlayerStateSnapshot) bool { + if base.PowerType == nil || update.PowerType == nil || update.Power == nil || *base.PowerType == *update.PowerType { + return false + } + + return update.InstanceID == nil && + update.Health == nil && + update.MaxHealth == nil && + update.MaxPower == nil && + !update.AurasKnown +} + +func (b *PlayerStateUpdatesBarrier) send(ctx context.Context, buffer map[string]map[uint64]PlayerStateSnapshot, lastSent map[uint64]PlayerStateSnapshot) error { + nowMs := uint64(time.Now().UnixMilli()) + for sourceWorldserverID, updatesByMember := range buffer { + snapshots := make([]*pb.PlayerStateSnapshot, 0, len(updatesByMember)) + for memberGUID, snapshot := range updatesByMember { + if !snapshot.IsSendable() || isInitialAuraOnlySnapshot(snapshot, lastSent) { + if event := groupstatetrace.Event(b.logger, "gateway.barrier.skip", memberGUID); event != nil { + tracePlayerStateSnapshot(event, snapshot). + Str("sourceWorldserverID", sourceWorldserverID). + Bool("sendable", snapshot.IsSendable()). + Bool("initialAuraOnly", isInitialAuraOnlySnapshot(snapshot, lastSent)). + Bool("hasLastSent", lastSent[memberGUID].MemberGUID != 0). + Msg(groupstatetrace.Message) + } + if snapshot.AurasKnown && b.logger != nil { + b.logger.Debug(). + Uint64("memberGUID", memberGUID). + Str("sourceWorldserverID", sourceWorldserverID). + Bool("hasOnline", snapshot.Online != nil). + Bool("hasLevel", snapshot.Level != nil). + Bool("hasClass", snapshot.Class != nil). + Bool("hasZone", snapshot.ZoneID != nil). + Bool("hasMap", snapshot.MapID != nil). + Bool("hasHealth", snapshot.Health != nil). + Bool("hasMaxHealth", snapshot.MaxHealth != nil). + Bool("hasPowerType", snapshot.PowerType != nil). + Bool("hasPower", snapshot.Power != nil). + Bool("hasMaxPower", snapshot.MaxPower != nil). + Bool("hasLastSent", lastSent[memberGUID].MemberGUID != 0). + Msg("TC9 skipping player aura state: player state snapshot has no complete baseline") + } + continue + } + + if oldSnapshot, ok := lastSent[memberGUID]; ok && snapshot.Equal(oldSnapshot) { + continue + } + + snapshot = ensureMonotonicPlayerStateTimestamp(snapshot, lastSent) + transmitSnapshot := snapshotForPlayerStateTransmission(snapshot, lastSent[memberGUID]) + updatesByMember[memberGUID] = snapshot + if event := groupstatetrace.Event(b.logger, "gateway.barrier.send", memberGUID); event != nil { + tracePlayerStateSnapshot(event, transmitSnapshot). + Str("sourceWorldserverID", sourceWorldserverID). + Msg(groupstatetrace.Message) + } + snapshots = append(snapshots, transmitSnapshot.ToProto()) + } + + if len(snapshots) == 0 { + pruneCompleteBufferedSnapshots(buffer, lastSent, sourceWorldserverID) + continue + } + + if b.logger != nil { + b.logger.Debug(). + Str("sourceWorldserverID", sourceWorldserverID). + Int("snapshotCount", len(snapshots)). + Msg("TC9 sending player state batch") + } + + if _, err := b.groupServiceClient.BulkUpdateMemberStates(ctx, &pb.BulkUpdateMemberStatesRequest{ + Api: b.api, + RealmID: b.realmID, + SourceGatewayID: b.sourceGatewayID, + SourceWorldserverID: sourceWorldserverID, + Snapshots: snapshots, + }); err != nil { + return err + } + + pruneCompleteBufferedSnapshots(buffer, lastSent, sourceWorldserverID) + } + + pruneStaleIncompleteBufferedSnapshots(buffer, nowMs) + return nil +} + +func tracePlayerStateSnapshot(event *zerolog.Event, snapshot PlayerStateSnapshot) *zerolog.Event { + event = event. + Uint64("memberGUID", snapshot.MemberGUID). + Str("snapshotSourceWorldserverID", snapshot.SourceWorldserverID). + Bool("hasOnline", snapshot.Online != nil). + Bool("hasLevel", snapshot.Level != nil). + Bool("hasClass", snapshot.Class != nil). + Bool("hasZone", snapshot.ZoneID != nil). + Bool("hasMap", snapshot.MapID != nil). + Bool("hasInstance", snapshot.InstanceID != nil). + Bool("hasHealth", snapshot.Health != nil). + Bool("hasMaxHealth", snapshot.MaxHealth != nil). + Bool("hasPowerType", snapshot.PowerType != nil). + Bool("hasPower", snapshot.Power != nil). + Bool("hasMaxPower", snapshot.MaxPower != nil). + Bool("hasDead", snapshot.Dead != nil). + Bool("hasGhost", snapshot.Ghost != nil). + Bool("aurasKnown", snapshot.AurasKnown). + Int("auraCount", len(snapshot.Auras)). + Uint64("timestampMs", snapshot.TimestampMs) + if auraSpells := FormatPlayerAuraTrace(snapshot.Auras); auraSpells != "" { + event = event.Str("auraSpells", auraSpells) + } + + if snapshot.Online != nil { + event = event.Bool("online", *snapshot.Online) + } + if snapshot.Level != nil { + event = event.Uint8("level", *snapshot.Level) + } + if snapshot.Class != nil { + event = event.Uint8("class", *snapshot.Class) + } + if snapshot.ZoneID != nil { + event = event.Uint32("zoneID", *snapshot.ZoneID) + } + if snapshot.MapID != nil { + event = event.Uint32("mapID", *snapshot.MapID) + } + if snapshot.InstanceID != nil { + event = event.Uint32("instanceID", *snapshot.InstanceID) + } + if snapshot.Health != nil { + event = event.Uint32("health", *snapshot.Health) + } + if snapshot.MaxHealth != nil { + event = event.Uint32("maxHealth", *snapshot.MaxHealth) + } + if snapshot.PowerType != nil { + event = event.Uint8("powerType", *snapshot.PowerType) + } + if snapshot.Power != nil { + event = event.Uint32("power", *snapshot.Power) + } + if snapshot.MaxPower != nil { + event = event.Uint32("maxPower", *snapshot.MaxPower) + } + if snapshot.Dead != nil { + event = event.Bool("dead", *snapshot.Dead) + } + if snapshot.Ghost != nil { + event = event.Bool("ghost", *snapshot.Ghost) + } + + return event +} + +func normalizeFixedClassPlayerPower(snapshot PlayerStateSnapshot) PlayerStateSnapshot { + if snapshot.Class == nil || snapshot.PowerType == nil { + return snapshot + } + if !wow.IsFixedClassInactivePowerType(*snapshot.Class, *snapshot.PowerType) { + return snapshot + } + if snapshot.Power == nil && snapshot.MaxPower == nil { + return snapshot + } + + powerType, _ := wow.FixedPrimaryPowerTypeForClass(*snapshot.Class) + snapshot.PowerType = &powerType + if snapshot.Power != nil { + power := uint32(0) + snapshot.Power = &power + } + if snapshot.MaxPower == nil || *snapshot.MaxPower == 0 { + if maxPower := wow.DefaultMaxPowerForClass(*snapshot.Class); maxPower != 0 { + snapshot.MaxPower = &maxPower + } + } + + return snapshot +} + +func FormatPlayerAuraTrace(auras []PlayerAuraSnapshot) string { + auras = normalizePlayerAuras(auras) + if len(auras) == 0 { + return "" + } + + parts := make([]string, 0, len(auras)) + for _, aura := range auras { + parts = append(parts, strconv.Itoa(int(aura.Slot))+":"+strconv.FormatUint(uint64(aura.SpellID), 10)+":"+strconv.Itoa(int(aura.Flags))) + } + + return strings.Join(parts, ",") +} + +func isInitialAuraOnlySnapshot(snapshot PlayerStateSnapshot, lastSent map[uint64]PlayerStateSnapshot) bool { + if snapshot.IsComplete() || !snapshot.isAuraOnlySendable() { + return false + } + if snapshot.hasVitalEvidence() { + return false + } + + return lastSent[snapshot.MemberGUID].MemberGUID == 0 +} + +func ensureMonotonicPlayerStateTimestamp(snapshot PlayerStateSnapshot, lastSent map[uint64]PlayerStateSnapshot) PlayerStateSnapshot { + if snapshot.TimestampMs == 0 { + return snapshot + } + + last, ok := lastSent[snapshot.MemberGUID] + if !ok || last.TimestampMs == 0 || last.TimestampMs == ^uint64(0) || snapshot.TimestampMs > last.TimestampMs { + return snapshot + } + + snapshot.TimestampMs = last.TimestampMs + 1 + return snapshot +} + +func pruneCompleteBufferedSnapshots(buffer map[string]map[uint64]PlayerStateSnapshot, lastSent map[uint64]PlayerStateSnapshot, sourceWorldserverID string) { + updatesByMember := buffer[sourceWorldserverID] + for memberGUID, snapshot := range updatesByMember { + if !snapshot.IsSendable() || isInitialAuraOnlySnapshot(snapshot, lastSent) { + continue + } + + lastSent[memberGUID] = snapshot + delete(updatesByMember, memberGUID) + } + + if len(updatesByMember) == 0 { + delete(buffer, sourceWorldserverID) + } +} + +func pruneStaleIncompleteBufferedSnapshots(buffer map[string]map[uint64]PlayerStateSnapshot, nowMs uint64) { + retentionMs := uint64(incompletePlayerStateRetention / time.Millisecond) + for sourceWorldserverID, updatesByMember := range buffer { + for memberGUID, snapshot := range updatesByMember { + if snapshot.IsSendable() || snapshot.TimestampMs == 0 { + continue + } + + if snapshot.TimestampMs+retentionMs < nowMs { + delete(updatesByMember, memberGUID) + } + } + + if len(updatesByMember) == 0 { + delete(buffer, sourceWorldserverID) + } + } +} + +func mergePlayerStateSnapshots(base, update PlayerStateSnapshot) PlayerStateSnapshot { + sourceChanged := update.SourceWorldserverID != "" && base.SourceWorldserverID != "" && update.SourceWorldserverID != base.SourceWorldserverID + mapChanged := update.MapID != nil && base.MapID != nil && *update.MapID != *base.MapID + powerTypeChanged := update.PowerType != nil && base.PowerType != nil && *update.PowerType != *base.PowerType + if sourceChanged || mapChanged { + base.InstanceID = nil + } + if sourceChanged { + base.Health = nil + base.MaxHealth = nil + base.PowerType = nil + base.Power = nil + base.MaxPower = nil + base.Dead = nil + base.Ghost = nil + } + if powerTypeChanged { + base.Power = nil + base.MaxPower = nil + } + + if update.MemberGUID != 0 { + base.MemberGUID = update.MemberGUID + } + if update.SourceWorldserverID != "" { + base.SourceWorldserverID = update.SourceWorldserverID + } + if update.Online != nil { + base.Online = update.Online + } + if update.Level != nil { + base.Level = update.Level + } + if update.Class != nil { + base.Class = update.Class + } + if update.ZoneID != nil { + base.ZoneID = update.ZoneID + } + if update.MapID != nil { + base.MapID = update.MapID + } + if update.InstanceID != nil { + base.InstanceID = update.InstanceID + } + if update.Health != nil { + base.Health = update.Health + } + if update.MaxHealth != nil { + base.MaxHealth = update.MaxHealth + } + if update.PowerType != nil { + base.PowerType = update.PowerType + } + if update.Power != nil { + base.Power = update.Power + } + if update.MaxPower != nil { + base.MaxPower = update.MaxPower + } + if update.Dead != nil { + base.Dead = update.Dead + } + if update.Ghost != nil { + base.Ghost = update.Ghost + } + if update.AurasKnown { + base.AurasKnown = true + base.Auras = normalizePlayerAuras(update.Auras) + } + if update.TimestampMs != 0 { + base.TimestampMs = update.TimestampMs + } + + return fillMissingDeadPlayerPower(base) +} + +func fillMissingDeadPlayerPower(snapshot PlayerStateSnapshot) PlayerStateSnapshot { + if snapshot.Power != nil || + snapshot.Health == nil || + *snapshot.Health > 1 || + snapshot.PowerType == nil || + snapshot.MaxPower == nil { + return snapshot + } + + zeroPower := uint32(0) + snapshot.Power = &zeroPower + return snapshot +} + +func snapshotForPlayerStateTransmission(snapshot, lastSent PlayerStateSnapshot) PlayerStateSnapshot { + if !shouldTransmitAuraOnlySnapshot(snapshot, lastSent) { + return stripIncompletePowerForTransmission(snapshot) + } + + snapshot.Health = nil + snapshot.MaxHealth = nil + snapshot.PowerType = nil + snapshot.Power = nil + snapshot.MaxPower = nil + return snapshot +} + +func stripIncompletePowerForTransmission(snapshot PlayerStateSnapshot) PlayerStateSnapshot { + if snapshot.Power != nil { + return snapshot + } + + snapshot.PowerType = nil + snapshot.MaxPower = nil + return snapshot +} + +func shouldTransmitAuraOnlySnapshot(snapshot, lastSent PlayerStateSnapshot) bool { + if !snapshot.AurasKnown || !snapshot.IsComplete() || lastSent.MemberGUID == 0 { + return false + } + + return snapshot.MemberGUID == lastSent.MemberGUID && + boolValue(snapshot.Online) == boolValue(lastSent.Online) && + uint8Value(snapshot.Level) == uint8Value(lastSent.Level) && + uint8Value(snapshot.Class) == uint8Value(lastSent.Class) && + uint32Value(snapshot.ZoneID) == uint32Value(lastSent.ZoneID) && + uint32Value(snapshot.MapID) == uint32Value(lastSent.MapID) && + uint32PtrEqual(snapshot.InstanceID, lastSent.InstanceID) && + uint32Value(snapshot.Health) == uint32Value(lastSent.Health) && + uint32Value(snapshot.MaxHealth) == uint32Value(lastSent.MaxHealth) && + uint8Value(snapshot.PowerType) == uint8Value(lastSent.PowerType) && + uint32Value(snapshot.Power) == uint32Value(lastSent.Power) && + uint32Value(snapshot.MaxPower) == uint32Value(lastSent.MaxPower) +} + +func (s PlayerStateSnapshot) IsComplete() bool { + return s.MemberGUID != 0 && + s.Online != nil && + s.Level != nil && + s.Class != nil && + s.ZoneID != nil && + s.MapID != nil && + s.Health != nil && + s.MaxHealth != nil && + s.PowerType != nil && + s.Power != nil && + s.MaxPower != nil +} + +func (s PlayerStateSnapshot) IsSendable() bool { + return s.IsComplete() || s.isAuraOnlySendable() +} + +func (s PlayerStateSnapshot) hasVitalEvidence() bool { + return s.Health != nil || + s.MaxHealth != nil || + s.PowerType != nil || + s.Power != nil || + s.MaxPower != nil || + s.Dead != nil || + s.Ghost != nil +} + +func (s PlayerStateSnapshot) isAuraOnlySendable() bool { + return s.MemberGUID != 0 && + s.AurasKnown && + s.Online != nil && + s.Level != nil && + s.Class != nil && + s.ZoneID != nil && + s.MapID != nil +} + +func (s PlayerStateSnapshot) Equal(other PlayerStateSnapshot) bool { + if !s.IsSendable() || !other.IsSendable() { + return false + } + + return s.MemberGUID == other.MemberGUID && + s.SourceWorldserverID == other.SourceWorldserverID && + boolValue(s.Online) == boolValue(other.Online) && + uint8Value(s.Level) == uint8Value(other.Level) && + uint8Value(s.Class) == uint8Value(other.Class) && + uint32Value(s.ZoneID) == uint32Value(other.ZoneID) && + uint32Value(s.MapID) == uint32Value(other.MapID) && + uint32PtrEqual(s.InstanceID, other.InstanceID) && + uint32Value(s.Health) == uint32Value(other.Health) && + uint32Value(s.MaxHealth) == uint32Value(other.MaxHealth) && + uint8Value(s.PowerType) == uint8Value(other.PowerType) && + uint32Value(s.Power) == uint32Value(other.Power) && + uint32Value(s.MaxPower) == uint32Value(other.MaxPower) && + s.AurasKnown == other.AurasKnown && + playerAurasEqual(s.Auras, other.Auras) && + boolPtrEqual(s.Dead, other.Dead) && + boolPtrEqual(s.Ghost, other.Ghost) +} + +func (s PlayerStateSnapshot) ToProto() *pb.PlayerStateSnapshot { + s.Auras = normalizePlayerAuras(s.Auras) + + return &pb.PlayerStateSnapshot{ + MemberGUID: s.MemberGUID, + Online: boolValue(s.Online), + Level: uint32(uint8Value(s.Level)), + ClassID: uint32(uint8Value(s.Class)), + ZoneID: uint32Value(s.ZoneID), + MapID: uint32Value(s.MapID), + InstanceID: s.InstanceID, + Health: uint32Value(s.Health), + MaxHealth: uint32Value(s.MaxHealth), + PowerType: uint32(uint8Value(s.PowerType)), + Power: uint32Value(s.Power), + MaxPower: uint32Value(s.MaxPower), + TimestampMs: s.TimestampMs, + AurasKnown: s.AurasKnown, + Auras: playerAurasToProto(s.Auras), + Dead: s.Dead, + Ghost: s.Ghost, + } +} + +func boolPtrEqual(a, b *bool) bool { + if a == nil || b == nil { + return a == b + } + return *a == *b +} + +func boolValue(v *bool) bool { + if v == nil { + return false + } + return *v +} + +func uint8Value(v *uint8) uint8 { + if v == nil { + return 0 + } + return *v +} + +func uint32Value(v *uint32) uint32 { + if v == nil { + return 0 + } + return *v +} + +func uint32PtrEqual(a, b *uint32) bool { + if a == nil || b == nil { + return a == b + } + + return *a == *b +} + +func normalizePlayerAuras(auras []PlayerAuraSnapshot) []PlayerAuraSnapshot { + const maxGroupAuraSlots = 64 + + if len(auras) == 0 { + return nil + } + + bySlot := make(map[uint8]PlayerAuraSnapshot, len(auras)) + for _, aura := range auras { + if aura.Slot >= maxGroupAuraSlots || aura.SpellID == 0 { + continue + } + bySlot[aura.Slot] = aura + } + + out := make([]PlayerAuraSnapshot, 0, len(bySlot)) + for _, aura := range bySlot { + out = append(out, aura) + } + + sort.Slice(out, func(i, j int) bool { + return out[i].Slot < out[j].Slot + }) + + return out +} + +func playerAurasEqual(a, b []PlayerAuraSnapshot) bool { + a = normalizePlayerAuras(a) + b = normalizePlayerAuras(b) + + if len(a) != len(b) { + return false + } + + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} + +func playerAurasToProto(auras []PlayerAuraSnapshot) []*pb.PlayerAuraSnapshot { + if len(auras) == 0 { + return nil + } + + out := make([]*pb.PlayerAuraSnapshot, 0, len(auras)) + for _, aura := range auras { + out = append(out, &pb.PlayerAuraSnapshot{ + Slot: uint32(aura.Slot), + SpellID: aura.SpellID, + Flags: uint32(aura.Flags), + }) + } + + return out +} diff --git a/apps/gateway/service/realmnames.go b/apps/gateway/service/realmnames.go index 88c7ae1..08e554c 100644 --- a/apps/gateway/service/realmnames.go +++ b/apps/gateway/service/realmnames.go @@ -3,6 +3,7 @@ package service import ( "context" "errors" + "strings" "github.com/walkline/ToCloud9/apps/gateway/repo" ) @@ -33,3 +34,21 @@ func (r *RealmNamesService) NameByID(ctx context.Context, realmID uint32) (strin } return realm.Name, nil } + +func (r *RealmNamesService) IDByName(ctx context.Context, name string) (uint32, error) { + normalizedName := normalizeRealmName(name) + for _, realm := range r.cache { + if normalizeRealmName(realm.Name) == normalizedName { + return realm.RealmID, nil + } + } + + return 0, errors.New("realm not found") +} + +func normalizeRealmName(name string) string { + name = strings.ToLower(name) + name = strings.ReplaceAll(name, " ", "") + name = strings.ReplaceAll(name, "'", "") + return name +} diff --git a/apps/gateway/session/account_data.go b/apps/gateway/session/account_data.go new file mode 100644 index 0000000..aab2800 --- /dev/null +++ b/apps/gateway/session/account_data.go @@ -0,0 +1,163 @@ +package session + +import ( + "bytes" + "compress/zlib" + "context" + "fmt" + "io" + + root "github.com/walkline/ToCloud9/apps/gateway" + "github.com/walkline/ToCloud9/apps/gateway/packet" + pbChar "github.com/walkline/ToCloud9/gen/characters/pb" +) + +const ( + numAccountDataTypes = 8 + globalAccountDataMask = 0x15 + maxAccountDataSize = 0xFFFF + accountDataSuccessCode = 0 +) + +func (s *GameSession) RequestAccountData(ctx context.Context, p *packet.Packet) error { + if s.worldSocket != nil { + s.worldSocket.SendPacket(p) + return nil + } + + reader := p.Reader() + dataType := reader.Uint32() + if err := reader.Error(); err != nil { + return err + } + if dataType >= numAccountDataTypes { + return nil + } + + accountData, err := s.charServiceClient.AccountDataForAccount(ctx, &pbChar.AccountDataForAccountRequest{ + Api: root.SupportedCharServiceVer, + AccountID: s.accountID, + RealmID: root.RealmID, + }) + if err != nil { + return err + } + + var storedData string + var storedTime uint32 + for _, item := range accountData.AccountData { + if item.Type == dataType { + storedData = item.Data + storedTime = uint32(item.Time) + break + } + } + + compressedData, err := compressAccountDataPayload(storedData) + if err != nil { + return err + } + + resp := packet.NewWriterWithSize(packet.SMsgUpdateAccountData, uint32(8+4+4+4+len(compressedData))) + resp.Uint64(0) + resp.Uint32(dataType) + resp.Uint32(storedTime) + resp.Uint32(uint32(len(storedData))) + resp.Bytes(compressedData) + s.gameSocket.Send(resp) + return nil +} + +func (s *GameSession) UpdateAccountData(ctx context.Context, p *packet.Packet) error { + if s.worldSocket != nil { + s.worldSocket.SendPacket(p) + return nil + } + + reader := p.Reader() + dataType := reader.Uint32() + timestamp := reader.Uint32() + decompressedSize := reader.Uint32() + if err := reader.Error(); err != nil { + return err + } + if dataType >= numAccountDataTypes { + return nil + } + if decompressedSize > maxAccountDataSize { + return nil + } + + data := "" + if decompressedSize > 0 { + compressedData, err := io.ReadAll(reader.RawReader()) + if err != nil { + return err + } + + decompressedData, err := decompressAccountDataPayload(compressedData) + if err != nil { + return err + } + if len(decompressedData) != int(decompressedSize) { + return fmt.Errorf("account data decompressed size mismatch: got %d, expected %d", len(decompressedData), decompressedSize) + } + data = string(trimAccountDataCString(decompressedData)) + } else { + timestamp = 0 + } + + if globalAccountDataMask&(uint32(1)<= 0 { + return data[:index] + } + return data +} diff --git a/apps/gateway/session/arena_team.go b/apps/gateway/session/arena_team.go new file mode 100644 index 0000000..044f18a --- /dev/null +++ b/apps/gateway/session/arena_team.go @@ -0,0 +1,603 @@ +package session + +import ( + "context" + "strings" + + root "github.com/walkline/ToCloud9/apps/gateway" + eBroadcaster "github.com/walkline/ToCloud9/apps/gateway/events-broadcaster" + "github.com/walkline/ToCloud9/apps/gateway/packet" + pbChar "github.com/walkline/ToCloud9/gen/characters/pb" + pbMM "github.com/walkline/ToCloud9/gen/matchmaking/pb" + pbWorld "github.com/walkline/ToCloud9/gen/worldserver/pb" + "github.com/walkline/ToCloud9/shared/events" +) + +const ( + arenaTeamActionCreate uint32 = 0x00 + arenaTeamActionInvite uint32 = 0x01 + arenaTeamActionQuit uint32 = 0x03 + + arenaTeamErrorSuccess uint32 = 0 + arenaTeamErrorInternal uint32 = 0x01 + arenaTeamErrorAlreadyInTeam uint32 = 0x03 + arenaTeamErrorAlreadyInvited uint32 = 0x05 + arenaTeamErrorInvalidName uint32 = 0x06 + arenaTeamErrorNameExists uint32 = 0x07 + arenaTeamErrorLeaderLeave uint32 = 0x08 + arenaTeamErrorPermissions uint32 = 0x08 + arenaTeamErrorPlayerNotFound uint32 = 0x0B + arenaTeamErrorNotAllied uint32 = 0x0C + arenaTeamErrorIgnoringYou uint32 = 0x13 + arenaTeamErrorTargetTooLow uint32 = 0x15 + arenaTeamErrorTooManyMembers uint32 = 0x17 + arenaTeamErrorNotFound uint32 = 0x1B + arenaTeamErrorLocked uint32 = 0x1E +) + +func (s *GameSession) HandleArenaTeamPetitionTurnIn(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + petitionGUID := reader.Uint64() + if reader.Error() != nil || s.character == nil || s.charServiceClient == nil { + s.forwardPacketToWorldserver(p) + return nil + } + + petitionResp, err := s.charServiceClient.GetArenaTeamPetition(ctx, &pbChar.GetArenaTeamPetitionRequest{ + Api: root.Ver, + RealmID: root.RealmID, + PetitionGUID: petitionGUID, + }) + if err != nil { + return err + } + if petitionResp.GetStatus() == pbChar.GetArenaTeamPetitionResponse_NotFound || + petitionResp.GetStatus() == pbChar.GetArenaTeamPetitionResponse_NotArena || + petitionResp.GetPetition() == nil { + s.forwardPacketToWorldserver(p) + return nil + } + if petitionResp.GetStatus() != pbChar.GetArenaTeamPetitionResponse_Ok { + s.sendArenaTeamCommandResult(arenaTeamActionCreate, "", "", arenaTeamErrorInternal) + return nil + } + + petition := petitionResp.GetPetition() + background := reader.Uint32() + icon := reader.Uint32() + iconColor := reader.Uint32() + border := reader.Uint32() + borderColor := reader.Uint32() + if reader.Error() != nil { + s.sendArenaTeamCommandResult(arenaTeamActionCreate, petition.GetName(), "", arenaTeamErrorInternal) + return nil + } + + if s.gameServerGRPCClient == nil { + s.sendArenaTeamCommandResult(arenaTeamActionCreate, petition.GetName(), "", arenaTeamErrorInternal) + return nil + } + + items, err := s.gameServerGRPCClient.GetPlayerItemsByGuids(ctx, &pbWorld.GetPlayerItemsByGuidsRequest{ + Api: root.SupportedGameServerVer, + PlayerGuid: s.character.GUID, + Guids: []uint64{petitionGUID}, + }) + if err != nil { + return err + } + if len(items.GetItems()) != 1 { + s.sendArenaTeamCommandResult(arenaTeamActionCreate, petition.GetName(), "", arenaTeamErrorInternal) + return nil + } + + startRating := root.EffectiveArenaStartRating() + resp, err := s.charServiceClient.CreateArenaTeamFromPetition(ctx, &pbChar.CreateArenaTeamFromPetitionRequest{ + Api: root.Ver, + RealmID: root.RealmID, + CaptainGUID: s.character.GUID, + PetitionGUID: petitionGUID, + ArenaType: petition.GetArenaType(), + BackgroundColor: background, + EmblemStyle: icon, + EmblemColor: iconColor, + BorderStyle: border, + BorderColor: borderColor, + StartRating: startRating, + StartPersonalRating: root.EffectiveArenaStartPersonalRating(startRating), + StartMatchmakerRating: root.ArenaStartMatchmakerRating, + }) + if err != nil { + return err + } + + switch resp.GetStatus() { + case pbChar.CreateArenaTeamFromPetitionResponse_Ok: + removed, err := s.gameServerGRPCClient.RemoveItemsWithGuidsFromPlayer(ctx, &pbWorld.RemoveItemsWithGuidsFromPlayerRequest{ + Api: root.SupportedGameServerVer, + PlayerGuid: s.character.GUID, + Guids: []uint64{petitionGUID}, + AssignToPlayerGuid: 0, + }) + if err != nil { + return err + } + if len(removed.GetUpdatedItemsGuids()) != 1 { + s.sendArenaTeamCommandResult(arenaTeamActionCreate, petition.GetName(), "", arenaTeamErrorInternal) + return nil + } + + s.sendArenaTeamCommandResult(arenaTeamActionCreate, petition.GetName(), "", arenaTeamErrorSuccess) + s.sendTurnInPetitionResult(petitionTurnOK) + case pbChar.CreateArenaTeamFromPetitionResponse_NameExists: + s.sendArenaTeamCommandResult(arenaTeamActionCreate, petition.GetName(), "", arenaTeamErrorNameExists) + case pbChar.CreateArenaTeamFromPetitionResponse_AlreadyInTeam: + s.sendArenaTeamCommandResult(arenaTeamActionCreate, petition.GetName(), "", arenaTeamErrorAlreadyInTeam) + case pbChar.CreateArenaTeamFromPetitionResponse_NotEnoughSignatures: + s.sendTurnInPetitionResult(petitionTurnNeedMoreSignatures) + case pbChar.CreateArenaTeamFromPetitionResponse_InvalidName: + s.sendArenaTeamCommandResult(arenaTeamActionCreate, petition.GetName(), "", arenaTeamErrorInvalidName) + default: + s.sendArenaTeamCommandResult(arenaTeamActionCreate, petition.GetName(), "", arenaTeamErrorInternal) + } + + return nil +} + +func (s *GameSession) HandleArenaTeamQuery(ctx context.Context, p *packet.Packet) error { + teamID := p.Reader().Uint32() + team, err := s.arenaTeamForGateway(ctx, teamID) + if err != nil || team == nil { + return err + } + + s.sendArenaTeamQuery(team) + s.sendArenaTeamStats(team) + return nil +} + +func (s *GameSession) HandleArenaTeamRoster(ctx context.Context, p *packet.Packet) error { + teamID := p.Reader().Uint32() + team, err := s.arenaTeamForGateway(ctx, teamID) + if err != nil || team == nil { + return err + } + + s.sendArenaTeamRoster(team) + return nil +} + +func (s *GameSession) HandleArenaTeamInvite(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + teamID := reader.Uint32() + targetName := reader.String() + if reader.Error() != nil || s.character == nil { + return nil + } + + targetResp, err := s.charServiceClient.CharacterOnlineByName(ctx, &pbChar.CharacterOnlineByNameRequest{ + Api: root.Ver, + RealmID: root.RealmID, + CharacterName: targetName, + }) + if err != nil { + return err + } + if targetResp.GetCharacter() == nil || targetResp.GetCharacter().GetRealmID() != root.RealmID { + s.sendArenaTeamCommandResult(arenaTeamActionCreate, "", targetName, arenaTeamErrorPlayerNotFound) + return nil + } + target := targetResp.GetCharacter() + if root.MaxPlayerLevel > 0 && target.GetCharLvl() < root.MaxPlayerLevel { + s.sendArenaTeamCommandResult(arenaTeamActionCreate, "", target.GetCharName(), arenaTeamErrorTargetTooLow) + return nil + } + if !s.arenaCanInviteRace(uint8(target.GetCharRace())) { + s.sendArenaTeamCommandResult(arenaTeamActionInvite, "", "", arenaTeamErrorNotAllied) + return nil + } + + resp, err := s.charServiceClient.InviteArenaTeamMember(ctx, &pbChar.InviteArenaTeamMemberRequest{ + Api: root.Ver, + RealmID: root.RealmID, + ArenaTeamID: teamID, + InviterGUID: s.character.GUID, + InviterName: s.character.Name, + TargetGUID: target.GetCharGUID(), + TargetName: target.GetCharName(), + }) + if err != nil { + return err + } + + if resp.GetStatus() != pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK { + s.sendArenaTeamCommandResult(arenaTeamActionInvite, "", target.GetCharName(), arenaTeamMutationStatusToNativeError(resp.GetStatus())) + return nil + } + + s.sendArenaTeamCommandResult(arenaTeamActionInvite, resp.GetTeam().GetName(), target.GetCharName(), arenaTeamErrorSuccess) + return nil +} + +func (s *GameSession) HandleArenaTeamAccept(ctx context.Context, _ *packet.Packet) error { + if s.character == nil { + return nil + } + + resp, err := s.charServiceClient.AcceptArenaTeamInvite(ctx, &pbChar.AcceptArenaTeamInviteRequest{ + Api: root.Ver, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + PlayerName: s.character.Name, + }) + if err != nil { + return err + } + if resp.GetStatus() != pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK { + s.sendArenaTeamCommandResult(arenaTeamActionCreate, "", "", arenaTeamMutationStatusToNativeError(resp.GetStatus())) + return nil + } + + team := resp.GetTeam() + s.sendArenaTeamQuery(team) + s.sendArenaTeamRoster(team) + s.sendArenaTeamStats(team) + return nil +} + +func (s *GameSession) HandleArenaTeamDecline(ctx context.Context, _ *packet.Packet) error { + if s.character == nil { + return nil + } + + _, err := s.charServiceClient.DeclineArenaTeamInvite(ctx, &pbChar.DeclineArenaTeamInviteRequest{ + Api: root.Ver, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + }) + return err +} + +func (s *GameSession) HandleArenaTeamLeave(ctx context.Context, p *packet.Packet) error { + teamID := p.Reader().Uint32() + team, err := s.arenaTeamForGateway(ctx, teamID) + if err != nil || team == nil || s.character == nil { + return err + } + if locked, err := s.arenaTeamMutationLocked(ctx, team); err != nil { + return err + } else if locked { + s.sendArenaTeamCommandResult(arenaTeamActionQuit, "", "", arenaTeamErrorLocked) + return nil + } + + if team.GetCaptainGUID() == s.character.GUID { + if len(team.GetMembers()) > 1 { + s.sendArenaTeamCommandResult(arenaTeamActionQuit, "", "", arenaTeamErrorLeaderLeave) + return nil + } + resp, err := s.charServiceClient.DisbandArenaTeam(ctx, &pbChar.DisbandArenaTeamRequest{ + Api: root.Ver, + RealmID: root.RealmID, + ArenaTeamID: teamID, + ActorGUID: s.character.GUID, + }) + if err != nil { + return err + } + if resp.GetStatus() != pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK { + s.sendArenaTeamCommandResult(arenaTeamActionCreate, team.GetName(), "", arenaTeamMutationStatusToNativeError(resp.GetStatus())) + return nil + } + return nil + } + + resp, err := s.charServiceClient.RemoveArenaTeamMember(ctx, &pbChar.RemoveArenaTeamMemberRequest{ + Api: root.Ver, + RealmID: root.RealmID, + ArenaTeamID: teamID, + PlayerGUID: s.character.GUID, + ActorGUID: s.character.GUID, + }) + if err != nil { + return err + } + if resp.GetStatus() != pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK { + s.sendArenaTeamCommandResult(arenaTeamActionQuit, team.GetName(), "", arenaTeamMutationStatusToNativeError(resp.GetStatus())) + return nil + } + + s.sendArenaTeamCommandResult(arenaTeamActionQuit, team.GetName(), "", arenaTeamErrorSuccess) + return nil +} + +func (s *GameSession) HandleArenaTeamRemove(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + teamID := reader.Uint32() + targetName := reader.String() + if reader.Error() != nil || s.character == nil { + return nil + } + + team, err := s.arenaTeamForGateway(ctx, teamID) + if err != nil || team == nil { + return err + } + if locked, err := s.arenaTeamMutationLocked(ctx, team); err != nil { + return err + } else if locked { + s.sendArenaTeamCommandResult(arenaTeamActionQuit, "", "", arenaTeamErrorLocked) + return nil + } + target := arenaTeamMemberByName(team, targetName) + if target == nil { + s.sendArenaTeamCommandResult(arenaTeamActionCreate, "", targetName, arenaTeamErrorPlayerNotFound) + return nil + } + + resp, err := s.charServiceClient.RemoveArenaTeamMember(ctx, &pbChar.RemoveArenaTeamMemberRequest{ + Api: root.Ver, + RealmID: root.RealmID, + ArenaTeamID: teamID, + PlayerGUID: target.GetPlayerGUID(), + ActorGUID: s.character.GUID, + }) + if err != nil { + return err + } + if resp.GetStatus() != pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK { + s.sendArenaTeamCommandResult(arenaTeamActionCreate, team.GetName(), target.GetName(), arenaTeamMutationStatusToNativeError(resp.GetStatus())) + return nil + } + + return nil +} + +func (s *GameSession) HandleArenaTeamDisband(ctx context.Context, p *packet.Packet) error { + teamID := p.Reader().Uint32() + team, err := s.arenaTeamForGateway(ctx, teamID) + if err != nil || team == nil || s.character == nil { + return err + } + if locked, err := s.arenaTeamMutationLocked(ctx, team); err != nil { + return err + } else if locked { + return nil + } + + resp, err := s.charServiceClient.DisbandArenaTeam(ctx, &pbChar.DisbandArenaTeamRequest{ + Api: root.Ver, + RealmID: root.RealmID, + ArenaTeamID: teamID, + ActorGUID: s.character.GUID, + }) + if err != nil { + return err + } + if resp.GetStatus() != pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK { + s.sendArenaTeamCommandResult(arenaTeamActionCreate, team.GetName(), "", arenaTeamMutationStatusToNativeError(resp.GetStatus())) + return nil + } + + return nil +} + +func (s *GameSession) HandleArenaTeamLeader(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + teamID := reader.Uint32() + targetName := reader.String() + if reader.Error() != nil || s.character == nil { + return nil + } + + team, err := s.arenaTeamForGateway(ctx, teamID) + if err != nil || team == nil { + return err + } + target := arenaTeamMemberByName(team, targetName) + if target == nil { + s.sendArenaTeamCommandResult(arenaTeamActionCreate, "", targetName, arenaTeamErrorPlayerNotFound) + return nil + } + + resp, err := s.charServiceClient.SetArenaTeamCaptain(ctx, &pbChar.SetArenaTeamCaptainRequest{ + Api: root.Ver, + RealmID: root.RealmID, + ArenaTeamID: teamID, + CaptainGUID: target.GetPlayerGUID(), + ActorGUID: s.character.GUID, + }) + if err != nil { + return err + } + if resp.GetStatus() != pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK { + s.sendArenaTeamCommandResult(arenaTeamActionCreate, team.GetName(), target.GetName(), arenaTeamMutationStatusToNativeError(resp.GetStatus())) + return nil + } + + return nil +} + +func (s *GameSession) HandleEventArenaTeamInviteCreated(_ context.Context, e *eBroadcaster.Event) error { + payload := e.Payload.(*events.CharEventArenaTeamInviteCreatedPayload) + resp := packet.NewWriterWithSize(packet.SMsgArenaTeamInvite, 0) + resp.String(payload.InviterName) + resp.String(payload.TeamName) + s.gameSocket.Send(resp) + return nil +} + +func (s *GameSession) HandleEventArenaTeamNativeEvent(_ context.Context, e *eBroadcaster.Event) error { + payload := e.Payload.(*events.CharEventArenaTeamNativeEventPayload) + s.sendArenaTeamEvent(payload.Event, payload.EventGUID, payload.Args...) + return nil +} + +func (s *GameSession) arenaCanInviteRace(targetRace uint8) bool { + if root.AllowTwoSideInteractionArena { + return true + } + if s == nil || s.character == nil { + return false + } + + return guildSameFactionByRace(s.character.Race, targetRace) +} + +func (s *GameSession) arenaTeamMutationLocked(ctx context.Context, team *pbChar.ArenaTeamData) (bool, error) { + if s.matchmakingServiceClient == nil || team == nil { + return false, nil + } + + for _, member := range team.GetMembers() { + resp, err := s.matchmakingServiceClient.BattlegroundQueueDataForPlayer(ctx, &pbMM.BattlegroundQueueDataForPlayerRequest{ + Api: root.SupportedMatchmakingServiceVer, + RealmID: root.RealmID, + PlayerGUID: member.GetPlayerGUID(), + }) + if err != nil { + return false, err + } + for _, slot := range resp.GetSlots() { + if isArenaBattlegroundTypeID(slot.GetBgTypeID()) && slot.GetStatus() != pbMM.PlayerQueueStatus_NotInQueue { + return true, nil + } + } + } + + return false, nil +} + +func (s *GameSession) arenaTeamForGateway(ctx context.Context, teamID uint32) (*pbChar.ArenaTeamData, error) { + if s.charServiceClient == nil { + return nil, nil + } + + resp, err := s.charServiceClient.GetArenaTeam(ctx, &pbChar.GetArenaTeamRequest{ + Api: root.Ver, + RealmID: root.RealmID, + ArenaTeamID: teamID, + }) + if err != nil { + return nil, err + } + if resp.GetStatus() != pbChar.GetArenaTeamResponse_Ok || resp.GetTeam() == nil { + return nil, nil + } + return resp.GetTeam(), nil +} + +func (s *GameSession) sendArenaTeamCommandResult(action uint32, team string, player string, errorID uint32) { + resp := packet.NewWriterWithSize(packet.SMsgArenaTeamCommandResult, 0) + resp.Uint32(action) + resp.String(team) + resp.String(player) + resp.Uint32(errorID) + s.gameSocket.Send(resp) +} + +func (s *GameSession) sendArenaTeamQuery(team *pbChar.ArenaTeamData) { + resp := packet.NewWriterWithSize(packet.SMsgArenaTeamQueryResponse, 0) + resp.Uint32(team.GetArenaTeamID()) + resp.String(team.GetName()) + resp.Uint32(team.GetType()) + resp.Uint32(team.GetBackgroundColor()) + resp.Uint32(team.GetEmblemStyle()) + resp.Uint32(team.GetEmblemColor()) + resp.Uint32(team.GetBorderStyle()) + resp.Uint32(team.GetBorderColor()) + s.gameSocket.Send(resp) +} + +func (s *GameSession) sendArenaTeamStats(team *pbChar.ArenaTeamData) { + resp := packet.NewWriterWithSize(packet.SMsgArenaTeamStats, 0) + resp.Uint32(team.GetArenaTeamID()) + resp.Uint32(team.GetRating()) + resp.Uint32(team.GetWeekGames()) + resp.Uint32(team.GetWeekWins()) + resp.Uint32(team.GetSeasonGames()) + resp.Uint32(team.GetSeasonWins()) + resp.Uint32(team.GetRank()) + s.gameSocket.Send(resp) +} + +func (s *GameSession) sendArenaTeamRoster(team *pbChar.ArenaTeamData) { + resp := packet.NewWriterWithSize(packet.SMsgArenaTeamRoster, 0) + resp.Uint32(team.GetArenaTeamID()) + resp.Uint8(0) + resp.Uint32(uint32(len(team.GetMembers()))) + resp.Uint32(team.GetType()) + + for _, member := range team.GetMembers() { + resp.Uint64(member.GetPlayerGUID()) + resp.Bool(member.GetOnline()) + resp.String(member.GetName()) + if member.GetPlayerGUID() == team.GetCaptainGUID() { + resp.Uint32(0) + } else { + resp.Uint32(1) + } + resp.Uint8(uint8(member.GetLevel())) + resp.Uint8(uint8(member.GetClass())) + resp.Uint32(member.GetWeekGames()) + resp.Uint32(member.GetWeekWins()) + resp.Uint32(member.GetSeasonGames()) + resp.Uint32(member.GetSeasonWins()) + resp.Uint32(member.GetPersonalRating()) + } + + s.gameSocket.Send(resp) +} + +func (s *GameSession) sendArenaTeamEvent(event uint8, guid uint64, args ...string) { + resp := packet.NewWriterWithSize(packet.SMsgArenaTeamEvent, 0) + resp.Uint8(event) + resp.Uint8(uint8(len(args))) + for _, arg := range args { + resp.String(arg) + } + if guid > 0 { + resp.Uint64(guid) + } + s.gameSocket.Send(resp) +} + +func arenaTeamMemberByName(team *pbChar.ArenaTeamData, name string) *pbChar.ArenaTeamMemberData { + for _, member := range team.GetMembers() { + if strings.EqualFold(member.GetName(), name) { + return member + } + } + return nil +} + +func arenaTeamMutationStatusToNativeError(status pbChar.ArenaTeamMutationStatus) uint32 { + switch status { + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK: + return arenaTeamErrorSuccess + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_NOT_FOUND: + return arenaTeamErrorNotFound + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_MEMBER_MISMATCH: + return arenaTeamErrorPlayerNotFound + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_INVALID_TYPE: + return arenaTeamErrorNotFound + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_NAME_EXISTS: + return arenaTeamErrorNameExists + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_ALREADY_IN_TEAM: + return arenaTeamErrorAlreadyInTeam + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_ROSTER_FULL: + return arenaTeamErrorTooManyMembers + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_INVALID_NAME: + return arenaTeamErrorInvalidName + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_PERMISSION_DENIED: + return arenaTeamErrorPermissions + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_LEADER_LEAVE: + return arenaTeamErrorLeaderLeave + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_ALREADY_INVITED: + return arenaTeamErrorAlreadyInvited + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_IGNORING_YOU: + return arenaTeamErrorIgnoringYou + default: + return arenaTeamErrorInternal + } +} diff --git a/apps/gateway/session/aura-player-state.go b/apps/gateway/session/aura-player-state.go new file mode 100644 index 0000000..f55625a --- /dev/null +++ b/apps/gateway/session/aura-player-state.go @@ -0,0 +1,280 @@ +package session + +import ( + "context" + "time" + + "github.com/walkline/ToCloud9/apps/gateway/packet" + "github.com/walkline/ToCloud9/apps/gateway/service" + "github.com/walkline/ToCloud9/shared/groupstatetrace" +) + +const ( + maxGroupAuraSlots = 64 + + auraFlagCaster uint8 = 0x08 + auraFlagDuration uint8 = 0x20 +) + +func (s *GameSession) InterceptAuraUpdate(_ context.Context, p *packet.Packet) error { + s.gameSocket.SendPacket(p) + + if p.Source != packet.SourceWorldServer { + s.logSkippedAuraState("packet source is not worldserver", p) + return nil + } + if s.character == nil { + s.logSkippedAuraState("session has no character", p) + return nil + } + if s.playerStateUpdatesBarrier == nil { + s.logSkippedAuraState("player state barrier is not configured", p) + return nil + } + + if s.pendingRedirectID != "" { + s.logSkippedAuraState("redirect is pending", p) + return nil + } + if !s.playerWorldActive { + s.logSkippedAuraState("player is not world-active", p) + return nil + } + + memberGUID, updated, err := s.applyPlayerAuraUpdatePacket(p) + if err != nil { + if s.logger != nil { + s.logger.Debug().Err(err).Msg("can't parse aura update packet for player aura state extraction") + } + return nil + } + if !updated { + return nil + } + + s.publishPlayerAuraState(memberGUID) + return nil +} + +func (s *GameSession) applyPlayerAuraUpdatePacket(p *packet.Packet) (uint64, bool, error) { + r := p.Reader() + rawGUID := r.ReadGUID() + if err := r.Error(); err != nil { + return 0, false, err + } + + currentMemberGUID := currentCharacterMemberGUID(s.character.GUID) + + memberGUID := playerDBGUIDFromObjectUpdateGUID(rawGUID) + if memberGUID == 0 { + if s.logger != nil { + s.logger.Debug(). + Str("opcode", p.Opcode.String()). + Uint64("rawGUID", rawGUID). + Uint64("sessionCharacterGUID", s.character.GUID). + Msg("TC9 skipping player aura state: guid is not a player") + } + return 0, false, nil + } + + auras := s.ensurePlayerAuraState(memberGUID) + if p.Opcode == packet.SMsgAuraUpdateAll { + auras = map[uint8]service.PlayerAuraSnapshot{} + s.setPlayerAuraState(memberGUID, auras) + for r.Left() > 0 { + aura, active, err := readPlayerAuraUpdate(r) + if err != nil { + return 0, false, err + } + if active && aura.Slot < maxGroupAuraSlots { + auras[aura.Slot] = aura + } + } + return memberGUID, true, nil + } + + aura, active, err := readPlayerAuraUpdate(r) + if err != nil { + return 0, false, err + } + if aura.Slot >= maxGroupAuraSlots { + if s.logger != nil { + s.logger.Debug(). + Str("opcode", p.Opcode.String()). + Uint8("slot", aura.Slot). + Uint64("memberGUID", memberGUID). + Msg("TC9 skipping player aura state: aura slot is outside group frame range") + } + return memberGUID, false, nil + } + if active { + auras[aura.Slot] = aura + } else { + delete(auras, aura.Slot) + } + + if memberGUID == currentMemberGUID { + s.playerAuraState = auras + } + + return memberGUID, true, nil +} + +func (s *GameSession) logSkippedAuraState(reason string, p *packet.Packet) { + if s.logger == nil { + return + } + + s.logger.Debug(). + Str("reason", reason). + Str("opcode", p.Opcode.String()). + Uint32("source", uint32(p.Source)). + Uint64("sessionCharacterGUID", func() uint64 { + if s.character == nil { + return 0 + } + return s.character.GUID + }()). + Msg("TC9 skipping player aura state") +} + +func readPlayerAuraUpdate(r *packet.Reader) (service.PlayerAuraSnapshot, bool, error) { + slot := r.Uint8() + spellID := r.Uint32() + if err := r.Error(); err != nil { + return service.PlayerAuraSnapshot{}, false, err + } + + aura := service.PlayerAuraSnapshot{Slot: slot, SpellID: spellID} + if spellID == 0 { + return aura, false, nil + } + + flags := r.Uint8() + _ = r.Uint8() // caster level, not used by party/raid member stats + _ = r.Uint8() // stack amount or charges, not used by party/raid member stats + if flags&auraFlagCaster == 0 { + _ = r.ReadGUID() + } + if flags&auraFlagDuration != 0 { + _ = r.Uint32() + _ = r.Uint32() + } + if err := r.Error(); err != nil { + return service.PlayerAuraSnapshot{}, false, err + } + + aura.Flags = flags + return aura, true, nil +} + +func (s *GameSession) resetPlayerAuraState() { + s.playerAuraState = map[uint8]service.PlayerAuraSnapshot{} + s.observedPlayerAuraStates = map[uint64]map[uint8]service.PlayerAuraSnapshot{} +} + +func (s *GameSession) publishPlayerAuraState(memberGUID uint64) { + if memberGUID == 0 || s.character == nil || s.playerStateUpdatesBarrier == nil { + return + } + + currentMemberGUID := currentCharacterMemberGUID(s.character.GUID) + + snapshot := service.PlayerStateSnapshot{ + MemberGUID: memberGUID, + AurasKnown: true, + Auras: s.playerAuraStateSlice(memberGUID), + TimestampMs: uint64(time.Now().UnixMilli()), + } + ghost := playerAurasContainGhost(snapshot.Auras) + snapshot.Ghost = &ghost + if memberGUID == currentMemberGUID { + s.fillPlayerStateSnapshotSessionFields(&snapshot) + } else { + snapshot.SourceWorldserverID = s.currentWorldserverSourceID() + } + + if s.logger != nil { + s.logger.Debug(). + Uint64("sessionMemberGUID", currentMemberGUID). + Uint64("memberGUID", memberGUID). + Bool("ownMember", memberGUID == currentMemberGUID). + Str("sourceWorldserverID", snapshot.SourceWorldserverID). + Int("auraCount", len(snapshot.Auras)). + Msg("TC9 publishing player aura state") + } + if event := groupstatetrace.Event(s.logger, "gateway.aura.snapshot", snapshot.MemberGUID); event != nil { + traceSessionPlayerStateSnapshot(event, snapshot). + Uint32("accountID", s.accountID). + Bool("ownMember", memberGUID == currentMemberGUID). + Msg(groupstatetrace.Message) + } + + s.playerStateUpdatesBarrier.Update(snapshot) +} + +func playerAurasContainGhost(auras []service.PlayerAuraSnapshot) bool { + for _, aura := range auras { + switch aura.SpellID { + case ghostAuraSpellID, wispSpiritAuraSpellID: + return true + } + } + return false +} + +func (s *GameSession) playerAuraStateSlice(memberGUID uint64) []service.PlayerAuraSnapshot { + aurasBySlot := s.playerAuraStateForMember(memberGUID) + if len(aurasBySlot) == 0 { + return nil + } + + auras := make([]service.PlayerAuraSnapshot, 0, len(aurasBySlot)) + for _, aura := range aurasBySlot { + auras = append(auras, aura) + } + + return auras +} + +func (s *GameSession) playerAuraStateForMember(memberGUID uint64) map[uint8]service.PlayerAuraSnapshot { + if s.observedPlayerAuraStates != nil { + if auras := s.observedPlayerAuraStates[memberGUID]; auras != nil { + return auras + } + } + + if s.character != nil && memberGUID == currentCharacterMemberGUID(s.character.GUID) { + return s.playerAuraState + } + + return nil +} + +func (s *GameSession) ensurePlayerAuraState(memberGUID uint64) map[uint8]service.PlayerAuraSnapshot { + if s.observedPlayerAuraStates == nil { + s.observedPlayerAuraStates = map[uint64]map[uint8]service.PlayerAuraSnapshot{} + } + + auras := s.observedPlayerAuraStates[memberGUID] + if auras == nil && s.character != nil && memberGUID == currentCharacterMemberGUID(s.character.GUID) { + auras = s.playerAuraState + } + if auras == nil { + auras = map[uint8]service.PlayerAuraSnapshot{} + } + s.setPlayerAuraState(memberGUID, auras) + + return auras +} + +func (s *GameSession) setPlayerAuraState(memberGUID uint64, auras map[uint8]service.PlayerAuraSnapshot) { + if s.observedPlayerAuraStates == nil { + s.observedPlayerAuraStates = map[uint64]map[uint8]service.PlayerAuraSnapshot{} + } + + s.observedPlayerAuraStates[memberGUID] = auras + if s.character != nil && memberGUID == currentCharacterMemberGUID(s.character.GUID) { + s.playerAuraState = auras + } +} diff --git a/apps/gateway/session/battleground.go b/apps/gateway/session/battleground.go index c35f042..b52ff07 100644 --- a/apps/gateway/session/battleground.go +++ b/apps/gateway/session/battleground.go @@ -3,13 +3,11 @@ package session import ( "context" "fmt" - "time" - - "github.com/rs/zerolog/log" "github.com/walkline/ToCloud9/apps/gateway" eBroadcaster "github.com/walkline/ToCloud9/apps/gateway/events-broadcaster" "github.com/walkline/ToCloud9/apps/gateway/packet" + "github.com/walkline/ToCloud9/apps/gateway/sockets" pbGroup "github.com/walkline/ToCloud9/gen/group/pb" "github.com/walkline/ToCloud9/gen/matchmaking/pb" pbGameServ "github.com/walkline/ToCloud9/gen/worldserver/pb" @@ -28,6 +26,37 @@ const ( BattlegroundStatusLeaving ) +const ( + nagrandArenaBattlegroundTypeID uint32 = 4 + bladesEdgeBattlegroundTypeID uint32 = 5 + allArenasBattlegroundTypeID uint32 = 6 + ruinsOfLordaeronBattlegroundID uint32 = 8 + dalaranSewersBattlegroundTypeID uint32 = 10 + ringOfValorBattlegroundTypeID uint32 = 11 + defaultBattlegroundQueueSlot uint8 = 0 + + // AzerothCore SharedDefines.h: PLAYER_MAX_BATTLEGROUND_QUEUES. + playerMaxBattlegroundQueues uint8 = 2 +) + +const ( + nagrandArenaMapID uint32 = 559 + bladesEdgeArenaMapID uint32 = 562 + ruinsOfLordaeronMapID uint32 = 572 + dalaranSewersArenaMapID uint32 = 617 + ringOfValorArenaMapID uint32 = 618 +) + +var arenaNativeWorldportMapIDs = []uint32{ + nagrandArenaMapID, + bladesEdgeArenaMapID, + ruinsOfLordaeronMapID, + dalaranSewersArenaMapID, + ringOfValorArenaMapID, +} + +const battlegroundQueueUnavailableMessage = "Battleground queue was reset because the matchmaking service restarted. Please queue again." + func (s *GameSession) HandleEnqueueToBattleground(ctx context.Context, p *packet.Packet) error { r := p.Reader() /*battlemasterGUID*/ _ = r.Uint64() @@ -35,6 +64,79 @@ func (s *GameSession) HandleEnqueueToBattleground(ctx context.Context, p *packet /*instanceID*/ _ = r.Uint32() joinAsGroup := r.Uint8() + return s.enqueueToPVPQueue(ctx, bgTypeID, joinAsGroup, 0, false) +} + +func (s *GameSession) HandleEnqueueToArena(ctx context.Context, p *packet.Packet) error { + r := p.Reader() + battlemasterGUID := r.Uint64() + arenaSlot := r.Uint8() + joinAsGroup := r.Uint8() + isRated := r.Uint8() != 0 + + if isRated && joinAsGroup == 0 { + return nil + } + + arenaType, ok := arenaTypeBySlot(arenaSlot) + if !ok { + return fmt.Errorf("unknown arena slot: %d", arenaSlot) + } + + if s.logger != nil && s.character != nil { + s.logger.Debug(). + Uint64("playerGUID", s.character.GUID). + Uint64("battlemasterGUID", battlemasterGUID). + Uint8("arenaSlot", arenaSlot). + Uint32("arenaType", arenaType). + Uint8("joinAsGroup", joinAsGroup). + Bool("isRated", isRated). + Msg("TC9 arena queue request") + } + + if err := s.enqueueToPVPQueue(ctx, allArenasBattlegroundTypeID, joinAsGroup, arenaType, isRated); err != nil { + sendGroupJoinedBattlegroundError(s.gameSocket) + return nil + } + + return nil +} + +func sendGroupJoinedBattlegroundError(gameSocket sockets.Socket) { + const errBattlegroundJoinFailed int32 = -12 + resp := packet.NewWriterWithSize(packet.SMsgGroupJoinedBattleground, 4) + resp.Int32(errBattlegroundJoinFailed) + gameSocket.Send(resp) +} + +func isArenaBattlegroundTypeID(bgTypeID uint32) bool { + switch bgTypeID { + case nagrandArenaBattlegroundTypeID, + bladesEdgeBattlegroundTypeID, + allArenasBattlegroundTypeID, + ruinsOfLordaeronBattlegroundID, + dalaranSewersBattlegroundTypeID, + ringOfValorBattlegroundTypeID: + return true + default: + return false + } +} + +func arenaTypeBySlot(arenaSlot uint8) (uint32, bool) { + switch arenaSlot { + case 0: + return 2, true + case 1: + return 3, true + case 2: + return 5, true + default: + return 0, false + } +} + +func (s *GameSession) enqueueToPVPQueue(ctx context.Context, bgTypeID uint32, joinAsGroup uint8, arenaType uint32, isRated bool) error { members := []uint64{} if joinAsGroup > 0 { groupResp, err := s.groupServiceClient.GetGroupByMember(ctx, &pbGroup.GetGroupByMemberRequest{ @@ -45,6 +147,10 @@ func (s *GameSession) HandleEnqueueToBattleground(ctx context.Context, p *packet if err != nil { return NewGroupServiceUnavailableErr(err) } + if groupResp.GetGroup() == nil || groupResp.GetGroup().GetLeader() != s.character.GUID { + sendGroupJoinedBattlegroundError(s.gameSocket) + return nil + } for _, member := range groupResp.Group.Members { // Don't add leader, since we have the leader field @@ -79,6 +185,19 @@ func (s *GameSession) HandleEnqueueToBattleground(ctx context.Context, p *packet teamID = pb.PVPTeamID_Horde } + if s.logger != nil { + s.logger.Debug(). + Uint64("playerGUID", s.character.GUID). + Uint32("realmID", gateway.RealmID). + Uint32("bgTypeID", bgTypeID). + Uint32("arenaType", arenaType). + Uint8("joinAsGroup", joinAsGroup). + Bool("isRated", isRated). + Interface("partyMembers", members). + Str("teamID", teamID.String()). + Msg("TC9 PVP enqueue resolved") + } + _, err = s.matchmakingServiceClient.EnqueueToBattleground(ctx, &pb.EnqueueToBattlegroundRequest{ Api: gateway.SupportedMatchmakingServiceVer, RealmID: gateway.RealmID, @@ -87,6 +206,8 @@ func (s *GameSession) HandleEnqueueToBattleground(ctx context.Context, p *packet LeadersLvl: uint32(s.character.Level), BgTypeID: bgTypeID, TeamID: teamID, + ArenaType: arenaType, + IsRated: isRated, }) if err != nil { return err @@ -133,10 +254,7 @@ func (s *GameSession) leaveBattlegroundQueue(ctx context.Context, bgTypeID uint3 return fmt.Errorf("error on removing player from queue: %w", err) } - resp := packet.NewWriterWithSize(packet.SMsgBattlefieldStatus, 6+4) - resp.Uint32(0) - resp.Uint64(0) - s.gameSocket.Send(resp) + s.clearPVPQueueForType(bgTypeID) return nil } @@ -155,7 +273,8 @@ func (s *GameSession) enterBattleground(ctx context.Context) error { return nil } - bgData := res.Slots[0].AssignedBattlegroundData + selectedSlot := res.Slots[0] + bgData := selectedSlot.AssignedBattlegroundData if bgData == nil { return fmt.Errorf("no battleground data found in HandleBattlegroundPort") @@ -163,141 +282,119 @@ func (s *GameSession) enterBattleground(ctx context.Context) error { s.gameServerGRPCConnMgr.AddAddressMapping(bgData.GameserverAddress, bgData.GameserverGRPCAddress) - oldServerAddress := s.worldSocket.Address() desiredServerAddress := bgData.GameserverAddress - s.character.ignoreNextInterceptToNewMap = &bgData.MapID - crossrealmAdjustedPlayerGUID := s.character.GUID isCrossrealm := bgData.BattlegroupID != 0 if isCrossrealm { crossrealmAdjustedPlayerGUID = guid.NewCrossrealmPlayerGUID(uint16(gateway.RealmID), guid.LowType(s.character.GUID)).GetRawValue() } - if desiredServerAddress != oldServerAddress { - err = s.battlegroundPlayerRedirect(ctx, crossrealmAdjustedPlayerGUID, desiredServerAddress) - if err != nil { - return fmt.Errorf("battleground player redirect failed: %w", err) - } - } - grpcClient, err := s.gameServerGRPCConnMgr.GRPCConnByGameServerAddress(bgData.GameserverAddress) if err != nil { return fmt.Errorf("gameServerGRPCConnMgr.GRPCConnByGameServerAddress failed: %w", err) } - _, err = grpcClient.AddPlayersToBattleground(ctx, &pbGameServ.AddPlayersToBattlegroundRequest{ - Api: "0.0.1", - BattlegroundTypeID: pbGameServ.BattlegroundType(res.Slots[0].BgTypeID), - InstanceID: bgData.AssignedBattlegroundInstanceID, - // TODO: clarify alliance & horde situation - PlayersToAddAlliance: []uint64{crossrealmAdjustedPlayerGUID}, - PlayersToAddHorde: nil, - }) - if err != nil { - return fmt.Errorf("AddPlayersToBattleground failed: %w", err) + feature := clusterTransferFeatureBattleground + operation := "battleground owner native worldport" + if isArenaBattlegroundTypeID(selectedSlot.BgTypeID) { + feature = clusterTransferFeatureArena + operation = "arena owner native worldport" + } + transferRouting := pvpOwnerMapTransferRouting(feature, isCrossrealm) + + if s.logger != nil { + s.logger.Debug(). + Uint64("playerGUID", s.character.GUID). + Uint64("loginPlayerGUID", crossrealmAdjustedPlayerGUID). + Uint32("realmID", gateway.RealmID). + Uint32("bgTypeID", selectedSlot.BgTypeID). + Uint32("instanceID", bgData.AssignedBattlegroundInstanceID). + Uint32("mapID", bgData.MapID). + Uint32("battlegroupID", bgData.BattlegroupID). + Str("gameserverAddress", bgData.GameserverAddress). + Str("gameserverGRPCAddress", bgData.GameserverGRPCAddress). + Str("feature", feature.String()). + Bool("isCrossrealm", isCrossrealm). + Msg("TC9 PVP owner placement selected") } - _, err = s.matchmakingServiceClient.PlayerJoinedBattleground(ctx, &pb.PlayerJoinedBattlegroundRequest{ - Api: gateway.SupportedMatchmakingServiceVer, - RealmID: gateway.RealmID, - PlayerGUID: s.character.GUID, - InstanceID: bgData.AssignedBattlegroundInstanceID, - IsCrossRealm: isCrossrealm, - }) - if err != nil { - return fmt.Errorf("PlayerJoinedBattleground failed: %w", err) + forwardOptions := nativeWorldportForwardOptions{ + synthesizeTransferPendingForNewMap: true, + expectedMapID: bgData.MapID, + } + if feature == clusterTransferFeatureArena { + forwardOptions.acceptedMapIDs = arenaNativeWorldportMapIDs } - s.character.GroupMangedByGameServer = true + if err := s.startClusterOwnerNativeWorldportTransport(ctx, clusterOwnerNativeWorldportTransport{ + feature: feature, + operation: operation, + sessionPlayerGUID: s.character.GUID, + loginPlayerGUID: crossrealmAdjustedPlayerGUID, + targetAddress: desiredServerAddress, + routing: transferRouting, + forwardAfterPlacement: true, + reloadManagedGroupAfterTransfer: true, + forwardOptions: forwardOptions, + onOwnerPlaced: func(ctx context.Context) error { + var alliancePlayers []uint64 + var hordePlayers []uint64 + switch bgData.AssignedTeamID { + case pb.PVPTeamID_Horde: + hordePlayers = []uint64{crossrealmAdjustedPlayerGUID} + default: + alliancePlayers = []uint64{crossrealmAdjustedPlayerGUID} + } - // ignore all packets except new world - if err := s.processWorldPacketsInPlace(ctx, func(p *packet.Packet) (stopProcessing bool, err error) { - if p.Opcode != packet.SMsgNewWorld { - return false, nil - } + _, err := grpcClient.AddPlayersToBattleground(ctx, &pbGameServ.AddPlayersToBattlegroundRequest{ + Api: "0.0.1", + BattlegroundTypeID: pbGameServ.BattlegroundType(selectedSlot.BgTypeID), + InstanceID: bgData.AssignedBattlegroundInstanceID, + PlayersToAddAlliance: alliancePlayers, + PlayersToAddHorde: hordePlayers, + }) + if err != nil { + return fmt.Errorf("AddPlayersToBattleground failed: %w", err) + } - mapID := p.Reader().Uint32() - if mapID != s.character.Map { - resp := packet.NewWriterWithSize(packet.SMsgTransferPending, 0) - resp.Uint32(mapID) - s.gameSocket.Send(resp) - } - s.gameSocket.WriteChannel() <- p - return true, nil + _, err = s.matchmakingServiceClient.PlayerJoinedBattleground(ctx, &pb.PlayerJoinedBattlegroundRequest{ + Api: gateway.SupportedMatchmakingServiceVer, + RealmID: gateway.RealmID, + PlayerGUID: s.character.GUID, + InstanceID: bgData.AssignedBattlegroundInstanceID, + IsCrossRealm: isCrossrealm, + }) + if err != nil { + return fmt.Errorf("PlayerJoinedBattleground failed: %w", err) + } + + return nil + }, }); err != nil { - log.Err(err).Msg("failed to filter character login packets") + return fmt.Errorf("%s failed: %w", operation, err) } return nil } -func (s *GameSession) battlegroundPlayerRedirect(ctx context.Context, playerGuid uint64, desiredGameServerAddress string) error { - oldServerAddress := s.worldSocket.Address() - - saveAndClosePacket := packet.NewWriterWithSize(packet.TC9CMsgPrepareForRedirect, 0) - s.worldSocket.Send(saveAndClosePacket) - - confirmationIsSuccessfulChan := make(chan bool) - confirmationContext, cancel := context.WithTimeout(ctx, time.Second*5) - defer cancel() - go func() { - defer close(confirmationIsSuccessfulChan) - for { - select { - case <-confirmationContext.Done(): - confirmationIsSuccessfulChan <- false - return - case p, open := <-s.worldSocket.ReadChannel(): - if !open { - // If socket closed, then it also not bad, let's assume that as a good sign as well. - confirmationIsSuccessfulChan <- true - return - } - if p.Opcode == packet.TC9SMsgReadyForRedirect { - confirmationIsSuccessfulChan <- p.Reader().Uint8() == 0 - return - } - } - } - }() - - // Waits till new value in chan. - isReadyForRedirect := <-confirmationIsSuccessfulChan - if !isReadyForRedirect { - return fmt.Errorf("failed to redirect player with account %d, world server failed to prepare", s.accountID) - } - - s.worldSocket.Close() - s.worldSocket = nil - - newSocket, err := s.connectToGameServerWithAddress(ctx, playerGuid, desiredGameServerAddress, nil) - if err != nil { - return fmt.Errorf("connectToGameServerWithAddress failed: %w, address: %s", err, desiredGameServerAddress) +func pvpOwnerMapTransferRouting(feature clusterTransferFeature, isCrossrealm bool) *mapTransferRouting { + routing := &mapTransferRouting{ + realmID: gateway.RealmID, + feature: feature, } - - select { - case _, open := <-newSocket.ReadChannel(): - if !open { - return fmt.Errorf("world socket closed") - } - case <-ctx.Done(): - return ctx.Err() - } - - s.worldSocket = newSocket - - if s.showGameserverConnChangeToClient { - s.SendSysMessage(fmt.Sprintf("You have been redirected from %s to %s gameserver.", oldServerAddress, desiredGameServerAddress)) + if isCrossrealm { + routing.realmID = 0 + routing.isCrossRealm = true } - - return nil + return routing } func (s *GameSession) HandleEventMMJoinedPVPQueue(ctx context.Context, e *eBroadcaster.Event) error { eventData := e.Payload.(*events.MatchmakingEventPlayersQueuedPayload) + queueSlot := eventData.QueueSlotByPlayer[guid.LowType(s.character.GUID)] resp := packet.NewWriterWithSize(packet.SMsgBattlefieldStatus, 0) - resp.Uint32(uint32(eventData.QueueSlotByPlayer[guid.LowType(s.character.GUID)])) + resp.Uint32(uint32(queueSlot)) resp.Uint8(eventData.ArenaType) if eventData.ArenaType == 0 { resp.Uint8(0) // unk flag @@ -321,6 +418,7 @@ func (s *GameSession) HandleEventMMJoinedPVPQueue(ctx context.Context, e *eBroad resp.Uint32(0) s.gameSocket.Send(resp) + s.rememberPVPQueueSlot(uint32(eventData.TypeID), queueSlot) s.character.bgInviteOrderingFix.waitingJoinToQueue = false if s.character.bgInviteOrderingFix.pendingInvitePacket != nil { @@ -333,8 +431,9 @@ func (s *GameSession) HandleEventMMJoinedPVPQueue(ctx context.Context, e *eBroad func (s *GameSession) HandleEventMMInvitedToBGOrArena(ctx context.Context, e *eBroadcaster.Event) error { eventData := e.Payload.(*events.MatchmakingEventPlayersInvitedPayload) + queueSlot := eventData.QueueSlotByPlayer[guid.LowType(s.character.GUID)] resp := packet.NewWriterWithSize(packet.SMsgBattlefieldStatus, 0) - resp.Uint32(uint32(eventData.QueueSlotByPlayer[guid.LowType(s.character.GUID)])) + resp.Uint32(uint32(queueSlot)) resp.Uint8(eventData.ArenaType) if eventData.ArenaType == 0 { resp.Uint8(0) // unk flag @@ -358,6 +457,8 @@ func (s *GameSession) HandleEventMMInvitedToBGOrArena(ctx context.Context, e *eB resp.Uint64(0) resp.Uint32(eventData.TimeToAcceptMilliseconds) + s.rememberPVPQueueSlot(uint32(eventData.TypeID), queueSlot) + // In some cases Invite event can arrive faster than JoinToQueue. // In this case wait for JoinToQueue event and then send Invite. if s.character.bgInviteOrderingFix.waitingJoinToQueue { @@ -370,10 +471,115 @@ func (s *GameSession) HandleEventMMInvitedToBGOrArena(ctx context.Context, e *eB } func (s *GameSession) HandleEventMMInviteToBGOrArenaExpired(ctx context.Context, e *eBroadcaster.Event) error { - resp := packet.NewWriterWithSize(packet.SMsgBattlefieldStatus, 6+4) - resp.Uint32(0) - resp.Uint64(0) - s.gameSocket.Send(resp) + eventData := e.Payload.(*events.MatchmakingEventPlayersInviteExpiredPayload) + if queueSlot, ok := eventData.QueueSlotByPlayer[guid.LowType(s.character.GUID)]; ok { + s.sendBattlegroundStatusNone(queueSlot) + s.forgetPVPQueueSlotBySlot(queueSlot) + } else { + s.clearTrackedPVPQueueSlots() + } return nil } + +func (s *GameSession) HandleEventMMServiceUnavailable(ctx context.Context, e *eBroadcaster.Event) error { + if s.character == nil || s.character.GroupMangedByGameServer { + return nil + } + + if s.clearPVPQueuesAfterMatchmakingUnavailable() { + s.SendSysMessage(battlegroundQueueUnavailableMessage) + } + if s.clearLfgAfterMatchmakingUnavailable() { + s.SendSysMessage(lfgQueueUnavailableMessage) + } + + return nil +} + +func (s *GameSession) rememberPVPQueueSlot(bgTypeID uint32, queueSlot uint8) { + if queueSlot >= playerMaxBattlegroundQueues { + return + } + + if s.character.pvpQueueSlotsByType == nil { + s.character.pvpQueueSlotsByType = map[uint32]uint8{} + } + + s.character.pvpQueueSlotsByType[bgTypeID] = queueSlot +} + +func (s *GameSession) clearPVPQueueForType(bgTypeID uint32) { + if s.character.pvpQueueSlotsByType != nil { + if queueSlot, ok := s.character.pvpQueueSlotsByType[bgTypeID]; ok { + s.sendBattlegroundStatusNone(queueSlot) + delete(s.character.pvpQueueSlotsByType, bgTypeID) + return + } + } + + s.sendBattlegroundStatusNone(defaultBattlegroundQueueSlot) +} + +func (s *GameSession) forgetPVPQueueSlotBySlot(queueSlot uint8) { + if s.character.pvpQueueSlotsByType == nil { + return + } + + for bgTypeID, storedSlot := range s.character.pvpQueueSlotsByType { + if storedSlot == queueSlot { + delete(s.character.pvpQueueSlotsByType, bgTypeID) + } + } +} + +func (s *GameSession) clearPVPQueuesAfterMatchmakingUnavailable() bool { + hasPendingQueueJoin := s.character.bgInviteOrderingFix.waitingJoinToQueue || + s.character.bgInviteOrderingFix.pendingInvitePacket != nil + + clearedSlots := s.clearTrackedPVPQueueSlots() + if clearedSlots == 0 && hasPendingQueueJoin { + s.clearAllPVPQueueSlots() + clearedSlots = int(playerMaxBattlegroundQueues) + } + + s.character.bgInviteOrderingFix.waitingJoinToQueue = false + s.character.bgInviteOrderingFix.pendingInvitePacket = nil + + return clearedSlots > 0 || hasPendingQueueJoin +} + +func (s *GameSession) clearTrackedPVPQueueSlots() int { + if s.character.pvpQueueSlotsByType == nil { + return 0 + } + + slots := map[uint8]struct{}{} + for _, queueSlot := range s.character.pvpQueueSlotsByType { + if queueSlot >= playerMaxBattlegroundQueues { + continue + } + slots[queueSlot] = struct{}{} + } + + s.character.pvpQueueSlotsByType = map[uint32]uint8{} + + for queueSlot := range slots { + s.sendBattlegroundStatusNone(queueSlot) + } + + return len(slots) +} + +func (s *GameSession) clearAllPVPQueueSlots() { + for queueSlot := uint8(0); queueSlot < playerMaxBattlegroundQueues; queueSlot++ { + s.sendBattlegroundStatusNone(queueSlot) + } +} + +func (s *GameSession) sendBattlegroundStatusNone(queueSlot uint8) { + resp := packet.NewWriterWithSize(packet.SMsgBattlefieldStatus, 4+8) + resp.Uint32(uint32(queueSlot)) + resp.Uint64(0) + s.gameSocket.Send(resp) +} diff --git a/apps/gateway/session/channels.go b/apps/gateway/session/channels.go index 5914823..1e110e0 100644 --- a/apps/gateway/session/channels.go +++ b/apps/gateway/session/channels.go @@ -2,7 +2,6 @@ package session import ( "context" - "fmt" "strings" "time" @@ -11,6 +10,7 @@ import ( "github.com/walkline/ToCloud9/apps/gateway/packet" pbChat "github.com/walkline/ToCloud9/gen/chat/pb" "github.com/walkline/ToCloud9/shared/wow" + wowguid "github.com/walkline/ToCloud9/shared/wow/guid" ) // Channel IDs from ChatChannels.dbc @@ -43,6 +43,22 @@ const ( MemberFlagModerator uint8 = 0x02 ) +// Player flags and chat tags mirror AzerothCore Player.h values used by Player::GetChatTag. +const ( + playerFlagAFK uint32 = 0x00000002 + playerFlagDND uint32 = 0x00000004 + playerFlagDeveloper uint32 = 0x00008000 + playerFlagCommentator2 uint32 = 0x00400000 + + playerExtraFlagGMChat uint32 = 0x00000020 + + chatTagAFK uint8 = 0x01 + chatTagDND uint8 = 0x02 + chatTagGM uint8 = 0x04 + chatTagCOM uint8 = 0x08 + chatTagDEV uint8 = 0x10 +) + // WorldserverChannelInfo holds channel data from worldserver type WorldserverChannelInfo struct { Name string @@ -69,9 +85,12 @@ type ChannelMembership struct { // ChannelInfo holds information about a channel the player is a member of type ChannelInfo struct { - Name string - ChannelID uint32 - Flags uint8 + Name string + ChannelID uint32 + Flags uint8 + RealmID uint32 + TeamID uint32 + ServicePlayerGUID uint64 } func NewChannelMembership(playerGUID uint64, eventsBroadcaster *eBroadcaster.ChatChannelsService) *ChannelMembership { @@ -85,17 +104,38 @@ func NewChannelMembership(playerGUID uint64, eventsBroadcaster *eBroadcaster.Cha } func (cm *ChannelMembership) AddChannel(name string, channelID uint32, flags uint8) { + cm.AddScopedChannel(name, channelID, flags, 0, 0) +} + +func (cm *ChannelMembership) AddScopedChannel(name string, channelID uint32, flags uint8, realmID uint32, teamID uint32, servicePlayerGUID ...uint64) { + cm.addChannel(name, channelID, flags, realmID, teamID, true, servicePlayerGUID...) +} + +func (cm *ChannelMembership) AddLocalChannel(name string, channelID uint32, flags uint8, realmID uint32, teamID uint32, servicePlayerGUID ...uint64) { + cm.addChannel(name, channelID, flags, realmID, teamID, false, servicePlayerGUID...) +} + +func (cm *ChannelMembership) addChannel(name string, channelID uint32, flags uint8, realmID uint32, teamID uint32, registerEvents bool, servicePlayerGUID ...uint64) { // Normalize channel name to lowercase for case-insensitive lookups // This matches the behavior in chatserver's channelKey() function normalizedName := strings.ToLower(name) + playerGUID := cm.playerGUID + if len(servicePlayerGUID) > 0 && servicePlayerGUID[0] != 0 { + playerGUID = servicePlayerGUID[0] + } cm.channels[normalizedName] = &ChannelInfo{ - Name: name, // Store original case for display - ChannelID: channelID, - Flags: flags, + Name: name, // Store original case for display + ChannelID: channelID, + Flags: flags, + RealmID: realmID, + TeamID: teamID, + ServicePlayerGUID: playerGUID, } - cm.events = cm.eventsBroadcaster.AddPlayerToChannel(cm.playerGUID, normalizedName) + if registerEvents { + cm.events = cm.eventsBroadcaster.AddPlayerToScopedChannel(cm.playerGUID, realmID, teamID, normalizedName) + } if !cm.initialChannelsLoaded { const initialJoiningWaitingTime = 5 * time.Second @@ -103,9 +143,12 @@ func (cm *ChannelMembership) AddChannel(name string, channelID uint32, flags uin cm.initialChannelsLoaded = true } else { cm.initialChannels[normalizedName] = &ChannelInfo{ - Name: name, - ChannelID: channelID, - Flags: flags, + Name: name, + ChannelID: channelID, + Flags: flags, + RealmID: realmID, + TeamID: teamID, + ServicePlayerGUID: playerGUID, } cm.lastJoinDateToTrackInitialJoining = time.Now() } @@ -114,8 +157,32 @@ func (cm *ChannelMembership) AddChannel(name string, channelID uint32, flags uin func (cm *ChannelMembership) RemoveChannel(name string) { normalizedName := strings.ToLower(name) + if ch := cm.channels[normalizedName]; ch != nil { + cm.eventsBroadcaster.RemovePlayerFromScopedChannel(cm.playerGUID, ch.RealmID, ch.TeamID, normalizedName) + } delete(cm.channels, normalizedName) - cm.eventsBroadcaster.RemovePlayerFromChannel(cm.playerGUID, normalizedName) +} + +func (cm *ChannelMembership) RemoveLocalChannelsByID(channelID uint32, exceptName string) { + if channelID == ChannelIDCustom { + return + } + + normalizedExceptName := strings.ToLower(exceptName) + for normalizedName, ch := range cm.channels { + if normalizedName == normalizedExceptName || ch.ChannelID != channelID || ch.Flags&ChannelFlagCustom != 0 { + continue + } + + delete(cm.channels, normalizedName) + } + for normalizedName, ch := range cm.initialChannels { + if normalizedName == normalizedExceptName || ch.ChannelID != channelID || ch.Flags&ChannelFlagCustom != 0 { + continue + } + + delete(cm.initialChannels, normalizedName) + } } func (cm *ChannelMembership) GetChannel(name string) *ChannelInfo { @@ -131,6 +198,17 @@ func (cm *ChannelMembership) IsMember(name string) bool { return exists } +func (cm *ChannelMembership) IsMemberForEvent(name string, realmID uint32, teamID uint32) bool { + ch := cm.GetChannel(name) + if ch == nil { + return false + } + if ch.RealmID == 0 && ch.TeamID == 0 { + return true + } + return ch.RealmID == realmID && ch.TeamID == teamID +} + func (cm *ChannelMembership) GetAllChannels() []*ChannelInfo { channels := make([]*ChannelInfo, 0, len(cm.channels)) for _, ch := range cm.channels { @@ -139,6 +217,24 @@ func (cm *ChannelMembership) GetAllChannels() []*ChannelInfo { return channels } +func (s *GameSession) isGatewayManagedChannel(channelName string) bool { + if s == nil || s.channelMembership == nil { + return false + } + + ch := s.channelMembership.GetChannel(channelName) + return ch != nil && ch.Flags&ChannelFlagCustom != 0 +} + +func (s *GameSession) forwardNativeChannelPacket(channelName string, p *packet.Packet) bool { + if s.isGatewayManagedChannel(channelName) || s.worldSocket == nil { + return false + } + + s.worldSocket.SendPacket(p) + return true +} + // Chat notify types (from Channel.h) const ( ChatJoinedNotice uint8 = 0x00 @@ -188,7 +284,10 @@ func (s *GameSession) HandleJoinChannel(ctx context.Context, p *packet.Packet) e _ = r.Uint8() // unknown2 channelName := r.String() password := r.String() - fmt.Println("Join Channel", channelName, channelID) + if r.Error() != nil || strings.TrimSpace(channelName) == "" { + return nil + } + // If it's a system channel, just forward it to worldserver since it has all required DBC data // and we will hook to worldserver response. if channelID != 0 && s.worldSocket != nil { @@ -206,14 +305,15 @@ func (s *GameSession) HandleJoinChannel(ctx context.Context, p *packet.Packet) e ChannelIDGuildRecrument: 131122, ChannelIDLookingForGroup: 262201, } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForID(channelID) // Call chat service to join the channel resp, err := s.chatServiceClient.JoinChannel(ctx, &pbChat.JoinChannelRequest{ Api: root.Ver, - RealmID: root.RealmID, - PlayerGUID: s.character.GUID, + RealmID: channelRealmID, + PlayerGUID: servicePlayerGUID, PlayerName: s.character.Name, - TeamID: s.getTeamID(), + TeamID: channelTeamID, ChannelName: channelName, ChannelID: channelID, Password: password, @@ -228,11 +328,10 @@ func (s *GameSession) HandleJoinChannel(ctx context.Context, p *packet.Packet) e switch resp.Status { case pbChat.JoinChannelResponse_Ok: // Add to local membership - use channel Flags, not memberFlags! - s.channelMembership.AddChannel(channelName, resp.Channel.ChannelID, uint8(resp.Channel.Flags)) + s.channelMembership.AddScopedChannel(channelName, resp.Channel.ChannelID, uint8(resp.Channel.Flags), channelRealmID, uint32(channelTeamID), servicePlayerGUID) ch := s.channelMembership.GetChannel(channelName) notify := s.ChannelNotify(ch) - notify.Joined(s.character.GUID) notify.YouJoined() // For custom channels, if the player became owner, send mode change notification @@ -265,14 +364,23 @@ func (s *GameSession) HandleLeaveChannel(ctx context.Context, p *packet.Packet) r := p.Reader() _ = r.Uint32() // unknown channelName := r.String() + if s.forwardNativeChannelPacket(channelName, p) { + return nil + } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) + + ch := s.channelMembership.GetChannel(channelName) + if ch == nil { + ch = &ChannelInfo{Name: channelName} + } // Call chat service to leave the channel resp, err := s.chatServiceClient.LeaveChannel(ctx, &pbChat.LeaveChannelRequest{ Api: root.Ver, - RealmID: root.RealmID, - PlayerGUID: s.character.GUID, + RealmID: channelRealmID, + PlayerGUID: servicePlayerGUID, ChannelName: channelName, - TeamID: s.getTeamID(), + TeamID: channelTeamID, }) if err != nil { return err @@ -281,7 +389,7 @@ func (s *GameSession) HandleLeaveChannel(ctx context.Context, p *packet.Packet) // Handle response switch resp.Status { case pbChat.LeaveChannelResponse_Ok: - notify := s.ChannelNotify(&ChannelInfo{Name: channelName}) + notify := s.ChannelNotify(ch) // Remove from local membership s.channelMembership.RemoveChannel(channelName) @@ -298,14 +406,18 @@ func (s *GameSession) HandleLeaveChannel(ctx context.Context, p *packet.Packet) func (s *GameSession) HandleChannelList(ctx context.Context, p *packet.Packet) error { r := p.Reader() channelName := r.String() + if s.forwardNativeChannelPacket(channelName, p) { + return nil + } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) // Call chat service to get channel members resp, err := s.chatServiceClient.GetChannelList(ctx, &pbChat.GetChannelListRequest{ Api: root.Ver, - RealmID: root.RealmID, - PlayerGUID: s.character.GUID, + RealmID: channelRealmID, + PlayerGUID: servicePlayerGUID, ChannelName: channelName, - TeamID: s.getTeamID(), + TeamID: channelTeamID, }) if err != nil { s.logger.Error().Err(err).Str("channelName", channelName).Msg("Failed to get channel list from chat service") @@ -329,7 +441,7 @@ func (s *GameSession) HandleChannelList(ctx context.Context, p *packet.Packet) e w.Uint32(uint32(len(resp.Members))) for _, member := range resp.Members { - w.Uint64(member.Guid) + w.Uint64(playerObjectGUIDForRealm(channelInfo.RealmID, member.Guid)) w.Uint8(uint8(member.Flags)) // Note: Some implementations include player name here, but AC 3.3.5a doesn't // The client looks up names by GUID @@ -386,7 +498,14 @@ func (n channelNotify) YouJoined() { } func (n channelNotify) YouLeft() { - n.send(n.header(ChatYouLeftNotice)) + w := n.header(ChatYouLeftNotice) + w.Uint32(n.ch.ChannelID) + if n.ch.ChannelID != ChannelIDCustom { + w.Uint8(1) + } else { + w.Uint8(0) + } + n.send(w) } func (n channelNotify) ModeChange(playerGUID uint64, oldFlags, newFlags uint8) { @@ -413,21 +532,76 @@ func (n channelNotify) Simple(notifyType uint8) { n.send(n.header(notifyType)) } +func channelNotifyString(primary, secondary, fallback string) string { + if primary != "" { + return primary + } + if secondary != "" { + return secondary + } + return fallback +} + +func appendAzerothCoreChannelNotifyPayload(w *packet.Writer, notifyType uint8, targetGUID, secondGUID uint64, targetName, extraData string, oldFlags, newFlags uint8) { + switch notifyType { + case ChatJoinedNotice, + ChatLeftNotice, + ChatPasswordChangedNotice, + ChatOwnerChangedNotice, + ChatAnnouncementsOnNotice, + ChatAnnouncementsOffNotice, + ChatModerationOnNotice, + ChatModerationOffNotice, + ChatPlayerAlreadyMemberNotice, + ChatInviteNotice, + ChatVoiceOnNotice, + ChatVoiceOffNotice: + w.Uint64(targetGUID) + case ChatModeChangeNotice: + w.Uint64(targetGUID) + w.Uint8(oldFlags) + w.Uint8(newFlags) + case ChatPlayerKickedNotice, + ChatPlayerBannedNotice, + ChatPlayerUnbannedNotice: + w.Uint64(targetGUID) + w.Uint64(secondGUID) + case ChatPlayerNotFoundNotice, + ChatChannelOwnerNotice, + ChatPlayerNotBannedNotice, + ChatPlayerInvitedNotice, + ChatPlayerInviteBannedNotice: + w.String(channelNotifyString(targetName, extraData, "")) + } +} + // SendChannelMessage sends a channel message to the client -func (s *GameSession) SendChannelMessage(channelName string, senderGUID uint64, senderName string, language uint32, message string) { - w := packet.NewWriterWithSize(packet.SMsgMessageChat, 0) - w.Uint8(uint8(ChatTypeChannel)) // CHAT_MSG_CHANNEL = 0x11 = 17 - w.Uint32(language) // int32 language - w.Uint64(senderGUID) // ObjectGuid senderGUID - w.Uint32(0) // uint32 flags - w.String(channelName) // string channelName (comes AFTER flags!) - w.Uint64(s.character.GUID) - msgLen := uint32(len(message) + 1) - w.Uint32(msgLen) // uint32 message length - w.String(message) // string message - w.Uint8(0) // uint8 chatTag +func (s *GameSession) SendChannelMessage(channelName string, senderRealmID uint32, senderGUID uint64, senderName string, language uint32, message string, chatTag uint8) { + s.sendAzerothCorePlayerChat(ChatTypeChannel, language, senderRealmID, senderGUID, senderName, s.character.GUID, channelName, message, chatTag) +} - s.gameSocket.Send(w) +func (s *GameSession) currentChatTag() uint8 { + if s == nil || s.character == nil { + return 0 + } + + var tag uint8 + if s.character.ExtraFlags&playerExtraFlagGMChat != 0 { + tag |= chatTagGM + } + if s.character.PlayerFlags&playerFlagDND != 0 { + tag |= chatTagDND + } + if s.character.PlayerFlags&playerFlagAFK != 0 { + tag |= chatTagAFK + } + if s.character.PlayerFlags&playerFlagCommentator2 != 0 { + tag |= chatTagCOM + } + if s.character.PlayerFlags&playerFlagDeveloper != 0 { + tag |= chatTagDEV + } + return tag } func (s *GameSession) getTeamID() pbChat.TeamID { @@ -438,17 +612,72 @@ func (s *GameSession) getTeamID() pbChat.TeamID { return pbChat.TeamID_TEAM_NEUTRAL } +func (s *GameSession) channelTeamID() pbChat.TeamID { + if root.AllowTwoSideInteractionChannel { + return pbChat.TeamID_TEAM_ALLIANCE + } + return s.getTeamID() +} + +func (s *GameSession) channelScopeForID(channelID uint32) (uint32, pbChat.TeamID, uint64) { + realmID := root.RealmID + if channelID != ChannelIDCustom { + realmID = s.dbcChannelRealmID() + } + return realmID, s.channelTeamID(), s.channelServicePlayerGUID(realmID) +} + +func (s *GameSession) channelScopeForName(channelName string) (uint32, pbChat.TeamID, uint64) { + if s != nil && s.channelMembership != nil { + if ch := s.channelMembership.GetChannel(channelName); ch != nil { + return ch.RealmID, pbChat.TeamID(ch.TeamID), ch.ServicePlayerGUID + } + } + return s.channelScopeForID(ChannelIDCustom) +} + +func (s *GameSession) dbcChannelRealmID() uint32 { + for _, routing := range []*mapTransferRouting{s.currentMapTransferRouting, s.activeMapTransferRouting, s.pendingMapTransferRouting} { + if routing != nil && routing.isCrossRealm { + return routing.realmID + } + } + return root.RealmID +} + +func (s *GameSession) channelServicePlayerGUID(channelRealmID uint32) uint64 { + if s == nil || s.character == nil { + return 0 + } + if channelRealmID != 0 && channelRealmID == root.RealmID { + return s.character.GUID + } + return wowguid.PlayerGUIDForRealm(channelRealmID, root.RealmID, s.character.GUID) +} + +func channelMessageLanguage(language uint32) uint32 { + if root.AllowTwoSideInteractionChannel { + return 0 // LANG_UNIVERSAL, matching AzerothCore Channel::Say. + } + return language +} + // SendChannelMessageToChat sends a channel message via the chat service func (s *GameSession) SendChannelMessageToChat(ctx context.Context, channelName string, message string, language uint32) error { + language = channelMessageLanguage(language) + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) + chatTag := s.currentChatTag() + resp, err := s.chatServiceClient.SendChannelMessage(ctx, &pbChat.SendChannelMessageRequest{ - Api: root.Ver, - RealmID: root.RealmID, - SenderGUID: s.character.GUID, - SenderName: s.character.Name, - ChannelName: channelName, - Language: language, - Message: message, - TeamID: s.getTeamID(), + Api: root.Ver, + RealmID: channelRealmID, + SenderGUID: servicePlayerGUID, + SenderName: s.character.Name, + ChannelName: channelName, + Language: language, + Message: message, + TeamID: channelTeamID, + SenderChatTag: uint32(chatTag), }) if err != nil { s.logger.Error().Err(err).Str("channelName", channelName).Msg("Failed to send channel message") @@ -458,7 +687,7 @@ func (s *GameSession) SendChannelMessageToChat(ctx context.Context, channelName switch resp.Status { case pbChat.SendChannelMessageResponse_Ok: // Echo the message back to the sender (like guild/party messages) - s.SendChannelMessage(channelName, s.character.GUID, s.character.Name, language, message) + s.SendChannelMessage(channelName, channelRealmID, servicePlayerGUID, s.character.Name, language, message, chatTag) case pbChat.SendChannelMessageResponse_NotMember: s.ChannelNotify(&ChannelInfo{Name: channelName}).Simple(ChatNotMemberNotice) case pbChat.SendChannelMessageResponse_Muted: @@ -473,19 +702,17 @@ func (s *GameSession) SendChannelMessageToChat(ctx context.Context, channelName func (s *GameSession) HandleEventChannelMessage(ctx context.Context, e *eBroadcaster.Event) error { eventData := e.Payload.(*eBroadcaster.ChannelMessagePayload) - fmt.Println("eventData:", eventData.Message, s.character.Name, "from", eventData.SenderName) - // Only send if we're a member of this channel - if !s.channelMembership.IsMember(eventData.ChannelName) { + if !s.channelMembership.IsMemberForEvent(eventData.ChannelName, eventData.RealmID, eventData.TeamID) { return nil } // Don't send to the sender (they already got the echo) - if s.character != nil && s.character.GUID == eventData.SenderGUID { + if s.character != nil && wowguid.SamePlayer(root.RealmID, s.character.GUID, eventData.RealmID, eventData.SenderGUID) { return nil } - s.SendChannelMessage(eventData.ChannelName, eventData.SenderGUID, eventData.SenderName, eventData.Language, eventData.Message) + s.SendChannelMessage(eventData.ChannelName, eventData.RealmID, eventData.SenderGUID, eventData.SenderName, eventData.Language, eventData.Message, eventData.SenderChatTag) return nil } @@ -493,12 +720,12 @@ func (s *GameSession) HandleEventChannelJoined(ctx context.Context, e *eBroadcas eventData := e.Payload.(*eBroadcaster.ChannelJoinedPayload) // Only send if we're a member of this channel - if !s.channelMembership.IsMember(eventData.ChannelName) { + if !s.channelMembership.IsMemberForEvent(eventData.ChannelName, eventData.RealmID, eventData.TeamID) { return nil } // Don't send to the player who just joined (they got YOU_JOINED notification) - if s.character != nil && s.character.GUID == eventData.PlayerGUID { + if s.character != nil && wowguid.SamePlayer(root.RealmID, s.character.GUID, eventData.RealmID, eventData.PlayerGUID) { return nil } @@ -509,24 +736,27 @@ func (s *GameSession) HandleEventChannelJoined(ctx context.Context, e *eBroadcas } } - s.ChannelNotify(&ChannelInfo{Name: eventData.ChannelName}).Joined(eventData.PlayerGUID) + s.ChannelNotify(&ChannelInfo{Name: eventData.ChannelName}).Joined(playerObjectGUIDForRealm(eventData.RealmID, eventData.PlayerGUID)) return nil } func (s *GameSession) HandleEventChannelLeft(ctx context.Context, e *eBroadcaster.Event) error { eventData := e.Payload.(*eBroadcaster.ChannelLeftPayload) + if eventData.Silent { + return nil + } // Only send if we're a member of this channel - if !s.channelMembership.IsMember(eventData.ChannelName) { + if !s.channelMembership.IsMemberForEvent(eventData.ChannelName, eventData.RealmID, eventData.TeamID) { return nil } // Don't send to the player who just left (they got YOU_LEFT notification) - if s.character != nil && s.character.GUID == eventData.PlayerGUID { + if s.character != nil && wowguid.SamePlayer(root.RealmID, s.character.GUID, eventData.RealmID, eventData.PlayerGUID) { return nil } - s.ChannelNotify(&ChannelInfo{Name: eventData.ChannelName}).Left(eventData.PlayerGUID) + s.ChannelNotify(&ChannelInfo{Name: eventData.ChannelName}).Left(playerObjectGUIDForRealm(eventData.RealmID, eventData.PlayerGUID)) return nil } @@ -535,77 +765,22 @@ func (s *GameSession) InterceptWorldserverChannelNotify(ctx context.Context, p * notifyType := r.Uint8() channelName := r.String() - // Read flags and channelID - flags := r.Uint8() - channelID := r.Uint32() - _ = r.Uint32() // unknown field - - // Ignore custom channels - we handle those separately - if flags&ChannelFlagCustom != 0 { - return nil - } - switch notifyType { - case ChatYouLeftNotice: - _, err := s.chatServiceClient.LeaveChannel(ctx, &pbChat.LeaveChannelRequest{ - Api: root.Ver, - RealmID: root.RealmID, - PlayerGUID: s.character.GUID, - ChannelName: channelName, - TeamID: s.getTeamID(), - }) - if err != nil { - return fmt.Errorf("failed to leave channel: %w", err) - } - - // Remove from local membership - s.channelMembership.RemoveChannel(channelName) case ChatYouJoinedNotice: - if channelID != 0 { - for _, ch := range s.channelMembership.channels { - if ch.ChannelID == channelID { - _, err := s.chatServiceClient.LeaveChannel(ctx, &pbChat.LeaveChannelRequest{ - Api: root.Ver, - RealmID: root.RealmID, - PlayerGUID: s.character.GUID, - ChannelName: ch.Name, - TeamID: s.getTeamID(), - }) - if err != nil { - return fmt.Errorf("failed to leave channel: %w", err) - } - - // Remove from local membership - s.channelMembership.RemoveChannel(ch.Name) - break - } - } + flags := r.Uint8() + channelID := r.Uint32() + _ = r.Uint32() + if r.Error() == nil && flags&ChannelFlagCustom == 0 && s.channelMembership != nil { + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForID(channelID) + s.channelMembership.RemoveLocalChannelsByID(channelID, channelName) + s.channelMembership.AddLocalChannel(channelName, channelID, flags, channelRealmID, uint32(channelTeamID), servicePlayerGUID) } - - // Join via chat service, passing worldserver's channel ID AND flags - _, err := s.chatServiceClient.JoinChannel(ctx, &pbChat.JoinChannelRequest{ - Api: root.Ver, - RealmID: root.RealmID, - PlayerGUID: s.character.GUID, - PlayerName: s.character.Name, - TeamID: s.getTeamID(), - ChannelName: channelName, - ChannelID: channelID, - Password: "", - ChannelFlags: uint32(flags), - }) - if err != nil { - return fmt.Errorf("failed to join channel: %w", err) + case ChatYouLeftNotice: + _ = r.Uint32() + _ = r.Uint8() + if r.Error() == nil && s.channelMembership != nil { + s.channelMembership.RemoveChannel(channelName) } - s.channelMembership.AddChannel(channelName, channelID, flags) - default: - s.logger.Debug(). - Str("channelName", channelName). - Uint8("flags", flags). - Uint32("channelID", channelID). - Uint8("notifyType", notifyType). - Msg("Unhandled worldserver channel notification received") - return nil } s.gameSocket.SendPacket(p) @@ -635,8 +810,14 @@ func (s *GameSession) HandleEventChannelNotification(ctx context.Context, e *eBr // For invitations (CHAT_INVITE_NOTICE), we don't need to be a member const ChatInviteNotice = 0x18 + if eventData.AffectsPlayer != 0 { + if s.character == nil || !wowguid.SamePlayer(root.RealmID, s.character.GUID, eventData.RealmID, eventData.AffectsPlayer) { + return nil + } + } + // Check if we're a member (not required for invitations) - isMember := s.channelMembership.IsMember(eventData.ChannelName) + isMember := s.channelMembership.IsMemberForEvent(eventData.ChannelName, eventData.RealmID, eventData.TeamID) if !isMember && eventData.NotifyType != ChatInviteNotice { return nil } @@ -645,19 +826,25 @@ func (s *GameSession) HandleEventChannelNotification(ctx context.Context, e *eBr w := packet.NewWriterWithSize(packet.SMsgChannelNotify, 0) w.Uint8(eventData.NotifyType) w.String(eventData.ChannelName) - - // Different notification types include different data - switch eventData.NotifyType { - case ChatOwnerChangedNotice: // 0x08 - includes new owner GUID - w.Uint64(eventData.TargetGUID) - case ChatModeChangeNotice: // 0x0C - includes target GUID and flags - w.Uint64(eventData.TargetGUID) - w.Uint8(eventData.OldFlags) - w.Uint8(eventData.NewFlags) - case ChatInviteNotice: // 0x18 - includes inviter GUID - w.Uint64(eventData.TargetGUID) - } + targetGUID := playerObjectGUIDForRealm(eventData.RealmID, eventData.TargetGUID) + secondGUID := playerObjectGUIDForRealm(eventData.RealmID, eventData.SecondGUID) + appendAzerothCoreChannelNotifyPayload( + w, + eventData.NotifyType, + targetGUID, + secondGUID, + eventData.TargetName, + eventData.ExtraData, + eventData.OldFlags, + eventData.NewFlags, + ) s.gameSocket.Send(w) + if s.character != nil && + (eventData.NotifyType == ChatPlayerKickedNotice || eventData.NotifyType == ChatPlayerBannedNotice) && + wowguid.SamePlayer(root.RealmID, s.character.GUID, eventData.RealmID, eventData.TargetGUID) && + s.channelMembership != nil { + s.channelMembership.RemoveChannel(eventData.ChannelName) + } return nil } diff --git a/apps/gateway/session/channels_moderation.go b/apps/gateway/session/channels_moderation.go index 2533f35..58da90a 100644 --- a/apps/gateway/session/channels_moderation.go +++ b/apps/gateway/session/channels_moderation.go @@ -12,15 +12,19 @@ import ( func (s *GameSession) HandleChannelPassword(ctx context.Context, p *packet.Packet) error { r := p.Reader() channelName := r.String() + if s.forwardNativeChannelPacket(channelName, p) { + return nil + } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) password := r.String() resp, err := s.chatServiceClient.SetChannelPassword(ctx, &pbChat.SetChannelPasswordRequest{ Api: root.Ver, - RealmID: root.RealmID, - SetterGUID: s.character.GUID, + RealmID: channelRealmID, + SetterGUID: servicePlayerGUID, ChannelName: channelName, Password: password, - TeamID: s.getTeamID(), + TeamID: channelTeamID, }) if err != nil { s.logger.Error().Err(err).Str("channelName", channelName).Msg("Failed to set channel password") @@ -35,7 +39,7 @@ func (s *GameSession) HandleChannelPassword(ctx context.Context, p *packet.Packe switch resp.Status { case pbChat.SetChannelPasswordResponse_Ok: - s.ChannelNotify(ch).Simple(ChatPasswordChangedNotice) + s.ChannelNotify(ch).PlayerAction(ChatPasswordChangedNotice, s.character.GUID) case pbChat.SetChannelPasswordResponse_NotMember: s.ChannelNotify(ch).Simple(ChatNotMemberNotice) case pbChat.SetChannelPasswordResponse_NotOwner: @@ -49,15 +53,19 @@ func (s *GameSession) HandleChannelPassword(ctx context.Context, p *packet.Packe func (s *GameSession) HandleChannelSetOwner(ctx context.Context, p *packet.Packet) error { r := p.Reader() channelName := r.String() + if s.forwardNativeChannelPacket(channelName, p) { + return nil + } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) targetName := r.String() resp, err := s.chatServiceClient.SetChannelOwner(ctx, &pbChat.SetChannelOwnerRequest{ Api: root.Ver, - RealmID: root.RealmID, - SetterGUID: s.character.GUID, + RealmID: channelRealmID, + SetterGUID: servicePlayerGUID, ChannelName: channelName, TargetName: targetName, - TeamID: s.getTeamID(), + TeamID: channelTeamID, }) if err != nil { s.logger.Error().Err(err).Str("channelName", channelName).Str("targetName", targetName).Msg("Failed to set channel owner") @@ -71,7 +79,7 @@ func (s *GameSession) HandleChannelSetOwner(ctx context.Context, p *packet.Packe switch resp.Status { case pbChat.SetChannelOwnerResponse_Ok: - s.ChannelNotify(ch).PlayerName(ChatOwnerChangedNotice, targetName) + // Chatserver broadcasts AzerothCore-shaped mode/owner notifications with the target GUID. case pbChat.SetChannelOwnerResponse_NotMember: s.ChannelNotify(ch).Simple(ChatNotMemberNotice) case pbChat.SetChannelOwnerResponse_NotOwner: @@ -87,15 +95,19 @@ func (s *GameSession) HandleChannelSetOwner(ctx context.Context, p *packet.Packe func (s *GameSession) HandleChannelSetModerator(ctx context.Context, p *packet.Packet) error { r := p.Reader() channelName := r.String() + if s.forwardNativeChannelPacket(channelName, p) { + return nil + } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) targetName := r.String() resp, err := s.chatServiceClient.SetChannelModerator(ctx, &pbChat.SetChannelModeratorRequest{ Api: root.Ver, - RealmID: root.RealmID, - SetterGUID: s.character.GUID, + RealmID: channelRealmID, + SetterGUID: servicePlayerGUID, ChannelName: channelName, TargetName: targetName, - TeamID: s.getTeamID(), + TeamID: channelTeamID, }) if err != nil { s.logger.Error().Err(err).Str("channelName", channelName).Str("targetName", targetName).Msg("Failed to set channel moderator") @@ -109,8 +121,7 @@ func (s *GameSession) HandleChannelSetModerator(ctx context.Context, p *packet.P switch resp.Status { case pbChat.SetChannelModeratorResponse_Ok: - // Notify all that moderator status was granted - s.ChannelNotify(ch).PlayerName(ChatModeChangeNotice, targetName) + // Chatserver broadcasts the AzerothCore-shaped mode-change notification with the target GUID. case pbChat.SetChannelModeratorResponse_NotMember: s.ChannelNotify(ch).Simple(ChatNotMemberNotice) case pbChat.SetChannelModeratorResponse_NotOwner: @@ -126,15 +137,19 @@ func (s *GameSession) HandleChannelSetModerator(ctx context.Context, p *packet.P func (s *GameSession) HandleChannelUnsetModerator(ctx context.Context, p *packet.Packet) error { r := p.Reader() channelName := r.String() + if s.forwardNativeChannelPacket(channelName, p) { + return nil + } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) targetName := r.String() resp, err := s.chatServiceClient.UnsetChannelModerator(ctx, &pbChat.UnsetChannelModeratorRequest{ Api: root.Ver, - RealmID: root.RealmID, - SetterGUID: s.character.GUID, + RealmID: channelRealmID, + SetterGUID: servicePlayerGUID, ChannelName: channelName, TargetName: targetName, - TeamID: s.getTeamID(), + TeamID: channelTeamID, }) if err != nil { s.logger.Error().Err(err).Str("channelName", channelName).Str("targetName", targetName).Msg("Failed to unset channel moderator") @@ -148,7 +163,7 @@ func (s *GameSession) HandleChannelUnsetModerator(ctx context.Context, p *packet switch resp.Status { case pbChat.UnsetChannelModeratorResponse_Ok: - s.ChannelNotify(ch).PlayerName(ChatModeChangeNotice, targetName) + // Chatserver broadcasts the AzerothCore-shaped mode-change notification with the target GUID. case pbChat.UnsetChannelModeratorResponse_NotMember: s.ChannelNotify(ch).Simple(ChatNotMemberNotice) case pbChat.UnsetChannelModeratorResponse_NotOwner: @@ -164,15 +179,19 @@ func (s *GameSession) HandleChannelUnsetModerator(ctx context.Context, p *packet func (s *GameSession) HandleChannelMute(ctx context.Context, p *packet.Packet) error { r := p.Reader() channelName := r.String() + if s.forwardNativeChannelPacket(channelName, p) { + return nil + } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) targetName := r.String() resp, err := s.chatServiceClient.SetChannelMute(ctx, &pbChat.SetChannelMuteRequest{ Api: root.Ver, - RealmID: root.RealmID, - MuterGUID: s.character.GUID, + RealmID: channelRealmID, + MuterGUID: servicePlayerGUID, ChannelName: channelName, TargetName: targetName, - TeamID: s.getTeamID(), + TeamID: channelTeamID, }) if err != nil { s.logger.Error().Err(err).Str("channelName", channelName).Str("targetName", targetName).Msg("Failed to mute player") @@ -186,7 +205,7 @@ func (s *GameSession) HandleChannelMute(ctx context.Context, p *packet.Packet) e switch resp.Status { case pbChat.SetChannelMuteResponse_Ok: - s.ChannelNotify(ch).PlayerName(ChatMutedNotice, targetName) + // Chatserver broadcasts the AzerothCore-shaped mode-change notification with the target GUID. case pbChat.SetChannelMuteResponse_NotMember: s.ChannelNotify(ch).Simple(ChatNotMemberNotice) case pbChat.SetChannelMuteResponse_NotModerator: @@ -202,15 +221,19 @@ func (s *GameSession) HandleChannelMute(ctx context.Context, p *packet.Packet) e func (s *GameSession) HandleChannelUnmute(ctx context.Context, p *packet.Packet) error { r := p.Reader() channelName := r.String() + if s.forwardNativeChannelPacket(channelName, p) { + return nil + } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) targetName := r.String() resp, err := s.chatServiceClient.UnsetChannelMute(ctx, &pbChat.UnsetChannelMuteRequest{ Api: root.Ver, - RealmID: root.RealmID, - UnmuterGUID: s.character.GUID, + RealmID: channelRealmID, + UnmuterGUID: servicePlayerGUID, ChannelName: channelName, TargetName: targetName, - TeamID: s.getTeamID(), + TeamID: channelTeamID, }) if err != nil { s.logger.Error().Err(err).Str("channelName", channelName).Str("targetName", targetName).Msg("Failed to unmute player") @@ -224,7 +247,7 @@ func (s *GameSession) HandleChannelUnmute(ctx context.Context, p *packet.Packet) switch resp.Status { case pbChat.UnsetChannelMuteResponse_Ok: - s.ChannelNotify(ch).PlayerName(ChatMutedNotice, targetName) + // Chatserver broadcasts the AzerothCore-shaped mode-change notification with the target GUID. case pbChat.UnsetChannelMuteResponse_NotMember: s.ChannelNotify(ch).Simple(ChatNotMemberNotice) case pbChat.UnsetChannelMuteResponse_NotModerator: @@ -240,15 +263,19 @@ func (s *GameSession) HandleChannelUnmute(ctx context.Context, p *packet.Packet) func (s *GameSession) HandleChannelInvite(ctx context.Context, p *packet.Packet) error { r := p.Reader() channelName := r.String() + if s.forwardNativeChannelPacket(channelName, p) { + return nil + } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) targetName := r.String() resp, err := s.chatServiceClient.InviteToChannel(ctx, &pbChat.InviteToChannelRequest{ Api: root.Ver, - RealmID: root.RealmID, - InviterGUID: s.character.GUID, + RealmID: channelRealmID, + InviterGUID: servicePlayerGUID, ChannelName: channelName, TargetName: targetName, - TeamID: s.getTeamID(), + TeamID: channelTeamID, }) if err != nil { s.logger.Error().Err(err).Str("channelName", channelName).Str("targetName", targetName).Msg("Failed to invite to channel") @@ -270,7 +297,7 @@ func (s *GameSession) HandleChannelInvite(ctx context.Context, p *packet.Packet) case pbChat.InviteToChannelResponse_PlayerNotFound: s.ChannelNotify(ch).PlayerName(ChatPlayerNotFoundNotice, targetName) case pbChat.InviteToChannelResponse_PlayerAlreadyMember: - s.ChannelNotify(ch).PlayerName(ChatPlayerAlreadyMemberNotice, targetName) + // AzerothCore expects a player GUID here; avoid sending a name-shaped packet. case pbChat.InviteToChannelResponse_WrongFaction: s.ChannelNotify(ch).Simple(ChatInviteWrongFactionNotice) case pbChat.InviteToChannelResponse_PlayerBanned: @@ -284,15 +311,19 @@ func (s *GameSession) HandleChannelInvite(ctx context.Context, p *packet.Packet) func (s *GameSession) HandleChannelKick(ctx context.Context, p *packet.Packet) error { r := p.Reader() channelName := r.String() + if s.forwardNativeChannelPacket(channelName, p) { + return nil + } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) targetName := r.String() resp, err := s.chatServiceClient.KickFromChannel(ctx, &pbChat.KickFromChannelRequest{ Api: root.Ver, - RealmID: root.RealmID, - KickerGUID: s.character.GUID, + RealmID: channelRealmID, + KickerGUID: servicePlayerGUID, ChannelName: channelName, TargetName: targetName, - TeamID: s.getTeamID(), + TeamID: channelTeamID, }) if err != nil { s.logger.Error().Err(err).Str("channelName", channelName).Str("targetName", targetName).Msg("Failed to kick from channel") @@ -306,7 +337,7 @@ func (s *GameSession) HandleChannelKick(ctx context.Context, p *packet.Packet) e switch resp.Status { case pbChat.KickFromChannelResponse_Ok: - s.ChannelNotify(ch).PlayerName(ChatPlayerKickedNotice, targetName) + // Chatserver broadcasts the AzerothCore-shaped kick notification with both player GUIDs. case pbChat.KickFromChannelResponse_NotMember: s.ChannelNotify(ch).Simple(ChatNotMemberNotice) case pbChat.KickFromChannelResponse_NotModerator: @@ -322,15 +353,19 @@ func (s *GameSession) HandleChannelKick(ctx context.Context, p *packet.Packet) e func (s *GameSession) HandleChannelBan(ctx context.Context, p *packet.Packet) error { r := p.Reader() channelName := r.String() + if s.forwardNativeChannelPacket(channelName, p) { + return nil + } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) targetName := r.String() resp, err := s.chatServiceClient.BanFromChannel(ctx, &pbChat.BanFromChannelRequest{ Api: root.Ver, - RealmID: root.RealmID, - BannerGUID: s.character.GUID, + RealmID: channelRealmID, + BannerGUID: servicePlayerGUID, ChannelName: channelName, TargetName: targetName, - TeamID: s.getTeamID(), + TeamID: channelTeamID, }) if err != nil { s.logger.Error().Err(err).Str("channelName", channelName).Str("targetName", targetName).Msg("Failed to ban from channel") @@ -344,7 +379,7 @@ func (s *GameSession) HandleChannelBan(ctx context.Context, p *packet.Packet) er switch resp.Status { case pbChat.BanFromChannelResponse_Ok: - s.ChannelNotify(ch).PlayerName(ChatPlayerBannedNotice, targetName) + // Chatserver broadcasts the AzerothCore-shaped ban notification with both player GUIDs. case pbChat.BanFromChannelResponse_NotMember: s.ChannelNotify(ch).Simple(ChatNotMemberNotice) case pbChat.BanFromChannelResponse_NotModerator: @@ -360,15 +395,19 @@ func (s *GameSession) HandleChannelBan(ctx context.Context, p *packet.Packet) er func (s *GameSession) HandleChannelUnban(ctx context.Context, p *packet.Packet) error { r := p.Reader() channelName := r.String() + if s.forwardNativeChannelPacket(channelName, p) { + return nil + } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) targetName := r.String() resp, err := s.chatServiceClient.UnbanFromChannel(ctx, &pbChat.UnbanFromChannelRequest{ Api: root.Ver, - RealmID: root.RealmID, - UnbannerGUID: s.character.GUID, + RealmID: channelRealmID, + UnbannerGUID: servicePlayerGUID, ChannelName: channelName, TargetName: targetName, - TeamID: s.getTeamID(), + TeamID: channelTeamID, }) if err != nil { s.logger.Error().Err(err).Str("channelName", channelName).Str("targetName", targetName).Msg("Failed to unban from channel") @@ -382,7 +421,7 @@ func (s *GameSession) HandleChannelUnban(ctx context.Context, p *packet.Packet) switch resp.Status { case pbChat.UnbanFromChannelResponse_Ok: - s.ChannelNotify(ch).PlayerName(ChatPlayerUnbannedNotice, targetName) + // Chatserver broadcasts the AzerothCore-shaped unban notification with both player GUIDs. case pbChat.UnbanFromChannelResponse_NotMember: s.ChannelNotify(ch).Simple(ChatNotMemberNotice) case pbChat.UnbanFromChannelResponse_NotModerator: @@ -398,13 +437,17 @@ func (s *GameSession) HandleChannelUnban(ctx context.Context, p *packet.Packet) func (s *GameSession) HandleChannelAnnouncements(ctx context.Context, p *packet.Packet) error { r := p.Reader() channelName := r.String() + if s.forwardNativeChannelPacket(channelName, p) { + return nil + } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) resp, err := s.chatServiceClient.ToggleChannelAnnouncements(ctx, &pbChat.ToggleChannelAnnouncementsRequest{ Api: root.Ver, - RealmID: root.RealmID, - TogglerGUID: s.character.GUID, + RealmID: channelRealmID, + TogglerGUID: servicePlayerGUID, ChannelName: channelName, - TeamID: s.getTeamID(), + TeamID: channelTeamID, }) if err != nil { s.logger.Error().Err(err).Str("channelName", channelName).Msg("Failed to toggle announcements") @@ -436,13 +479,17 @@ func (s *GameSession) HandleChannelAnnouncements(ctx context.Context, p *packet. func (s *GameSession) HandleChannelModerate(ctx context.Context, p *packet.Packet) error { r := p.Reader() channelName := r.String() + if s.forwardNativeChannelPacket(channelName, p) { + return nil + } + channelRealmID, channelTeamID, servicePlayerGUID := s.channelScopeForName(channelName) resp, err := s.chatServiceClient.ToggleChannelModeration(ctx, &pbChat.ToggleChannelModerationRequest{ Api: root.Ver, - RealmID: root.RealmID, - TogglerGUID: s.character.GUID, + RealmID: channelRealmID, + TogglerGUID: servicePlayerGUID, ChannelName: channelName, - TeamID: s.getTeamID(), + TeamID: channelTeamID, }) if err != nil { s.logger.Error().Err(err).Str("channelName", channelName).Msg("Failed to toggle moderation") diff --git a/apps/gateway/session/character.go b/apps/gateway/session/character.go index e38e8e4..cac46b9 100644 --- a/apps/gateway/session/character.go +++ b/apps/gateway/session/character.go @@ -11,17 +11,23 @@ import ( pbServ "github.com/walkline/ToCloud9/gen/servers-registry/pb" ) +const ( + charDeleteSuccess = uint8(0x47) + charDeleteFailed = uint8(0x48) + charDeleteFailedArenaCaptain = uint8(0x4B) +) + func (s *GameSession) CharactersList(ctx context.Context, p *packet.Packet) error { + if s.character != nil { + s.onLoggedOut() + } + if s.worldSocket != nil { socket := s.worldSocket s.worldSocket = nil socket.Close() } - if s.character != nil { - s.onLoggedOut() - } - r, err := s.charServiceClient.CharactersToLoginForAccount(ctx, &pbChar.CharactersToLoginForAccountRequest{ Api: root.SupportedCharServiceVer, AccountID: s.accountID, @@ -31,9 +37,23 @@ func (s *GameSession) CharactersList(ctx context.Context, p *packet.Packet) erro return err } - resp := packet.NewWriterWithSize(packet.SMsgCharEnum, 0) - resp.Uint8(uint8(len(r.Characters))) + characters := make([]*pbChar.LogInCharacter, 0, len(r.Characters)) for _, character := range r.Characters { + if character.AccountID != s.accountID { + s.logger.Error(). + Uint32("sessionAccount", s.accountID). + Uint32("characterAccount", character.AccountID). + Uint64("character", character.GUID). + Msg("Blocked cross-account character from character list") + continue + } + + characters = append(characters, character) + } + + resp := packet.NewWriterWithSize(packet.SMsgCharEnum, 0) + resp.Uint8(uint8(len(characters))) + for _, character := range characters { resp.Uint64(character.GUID) resp.String(character.Name) resp.Uint8(uint8(character.Race)) @@ -87,80 +107,115 @@ func (s *GameSession) CreateCharacter(ctx context.Context, p *packet.Packet) err s.gameSocket.Send(resp) } - serverResult, err := s.serversRegistryClient.RandomGameServerForRealm(ctx, &pbServ.RandomGameServerForRealmRequest{ - Api: root.SupportedServerRegistryVer, - RealmID: root.RealmID, - }) + resp, err := s.sendCharacterMutationToWorld(ctx, p, packet.SMsgCharCreate) if err != nil { sendCreateFailed() return err } - if serverResult.GameServer == nil { - sendCreateFailed() - return fmt.Errorf("no available game servers to handle 0x%X packet", uint16(p.Opcode)) - } + s.gameSocket.SendPacket(resp) + return nil +} - socket, err := WorldSocketCreator(s.logger, serverResult.GameServer.Address) - if err != nil { - sendCreateFailed() - return fmt.Errorf("can't connect to the world server, err: %w", err) +func (s *GameSession) DeleteCharacter(ctx context.Context, p *packet.Packet) error { + sendDelFailed := func(code uint8) { + resp := packet.NewWriterWithSize(packet.SMsgCharDelete, 1) + resp.Uint8(code) + s.gameSocket.Send(resp) } - go socket.ListenAndProcess(s.ctx) - newCtx, cancel := context.WithTimeout(s.ctx, time.Second*20) - defer cancel() - - waitDone := make(chan struct{}) - go func() { - defer func() { waitDone <- struct{}{} }() - - for { - select { - case p, open := <-socket.ReadChannel(): - if !open { - return - } - s.gameSocket.WriteChannel() <- p - if p.Opcode == packet.SMsgCharCreate { - socket.Close() - return - } - - case <-newCtx.Done(): - if s.worldSocket != nil { - s.worldSocket.Close() - } - return + deleteGUID, parseErr := characterDeleteGUID(p) + if s.charServiceClient != nil { + if parseErr != nil || deleteGUID == 0 { + sendDelFailed(charDeleteFailed) + if parseErr != nil { + return parseErr } + return fmt.Errorf("character delete packet has empty guid") } - }() - socket.SendPacket(s.authPacket) + validateResp, err := s.charServiceClient.ValidateArenaTeamCharacterDelete(ctx, &pbChar.ValidateArenaTeamCharacterDeleteRequest{ + Api: root.SupportedCharServiceVer, + RealmID: root.RealmID, + PlayerGUID: deleteGUID, + }) + if err != nil { + sendDelFailed(charDeleteFailed) + return err + } + if validateResp == nil { + sendDelFailed(charDeleteFailed) + return nil + } + switch validateResp.GetStatus() { + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK: + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_LEADER_LEAVE: + sendDelFailed(charDeleteFailedArenaCaptain) + return nil + default: + sendDelFailed(charDeleteFailed) + return nil + } + } - // we need to give some time to add session on the world side - time.Sleep(time.Millisecond * 300) + resp, err := s.sendCharacterMutationToWorld(ctx, p, packet.SMsgCharDelete) + if err != nil { + sendDelFailed(charDeleteFailed) + return err + } - socket.SendPacket(p) + if s.charServiceClient != nil && characterDeleteSucceeded(resp) { + cleanupResp, cleanupErr := s.charServiceClient.RemovePlayerFromArenaTeams(ctx, &pbChar.RemovePlayerFromArenaTeamsRequest{ + Api: root.SupportedCharServiceVer, + RealmID: root.RealmID, + PlayerGUID: deleteGUID, + }) + if cleanupErr != nil { + s.logger.Error().Err(cleanupErr).Uint64("playerGUID", deleteGUID).Msg("Failed to remove deleted character from arena teams") + } else if cleanupResp == nil { + s.logger.Error().Uint64("playerGUID", deleteGUID).Msg("Charserver returned nil deleted character arena team cleanup response") + } else if cleanupResp.GetStatus() != pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK { + s.logger.Error().Uint64("playerGUID", deleteGUID).Stringer("status", cleanupResp.GetStatus()).Msg("Charserver rejected deleted character arena team cleanup") + } + } - <-waitDone + s.gameSocket.SendPacket(resp) + return nil +} - select { - case <-newCtx.Done(): - sendCreateFailed() - return fmt.Errorf("character creation timeouted, gameserver: %s", serverResult.GameServer.Address) - default: +func characterDeleteGUID(p *packet.Packet) (uint64, error) { + if p == nil { + return 0, fmt.Errorf("nil character delete packet") } + reader := p.Reader() + guid := reader.Uint64() + return guid, reader.Error() +} - return nil +func characterDeleteSucceeded(p *packet.Packet) bool { + if p == nil || p.Opcode != packet.SMsgCharDelete { + return false + } + reader := p.Reader() + code := reader.Uint8() + return reader.Error() == nil && code == charDeleteSuccess } -func (s *GameSession) DeleteCharacter(ctx context.Context, p *packet.Packet) error { - sendDelFailed := func() { - const deleteFailedCode = uint8(0x48) - resp := packet.NewWriterWithSize(packet.SMsgCharDelete, 1) - resp.Uint8(deleteFailedCode) - s.gameSocket.Send(resp) +func (s *GameSession) sendCharacterMutationToWorld(ctx context.Context, p *packet.Packet, responseOpcode packet.Opcode) (*packet.Packet, error) { + if ctx == nil { + ctx = s.ctx + } + if ctx == nil { + ctx = context.Background() + } + if _, ok := ctx.Deadline(); !ok { + timeout := s.packetProcessTimeout + if timeout == 0 { + timeout = defaultPacketProcessingTimeout + } + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, timeout) + defer cancel() } serverResult, err := s.serversRegistryClient.RandomGameServerForRealm(ctx, &pbServ.RandomGameServerForRealmRequest{ @@ -168,68 +223,64 @@ func (s *GameSession) DeleteCharacter(ctx context.Context, p *packet.Packet) err RealmID: root.RealmID, }) if err != nil { - sendDelFailed() - return err + return nil, err } if serverResult.GameServer == nil { - sendDelFailed() - return fmt.Errorf("no available game servers to handle 0x%X packet", uint16(p.Opcode)) + return nil, fmt.Errorf("no available game servers to handle 0x%X packet", uint16(p.Opcode)) } socket, err := WorldSocketCreator(s.logger, serverResult.GameServer.Address) if err != nil { - sendDelFailed() - return fmt.Errorf("can't connect to the world server, err: %w", err) + return nil, fmt.Errorf("can't connect to the world server, err: %w", err) } + defer socket.Close() go socket.ListenAndProcess(s.ctx) - newCtx, cancel := context.WithTimeout(s.ctx, time.Second*5) - defer cancel() - - waitDone := make(chan struct{}) - go func() { - defer func() { waitDone <- struct{}{} }() - - for { - select { - case p, open := <-socket.ReadChannel(): - if !open { - return - } - s.gameSocket.WriteChannel() <- p - if p.Opcode == packet.SMsgCharDelete { - socket.Close() - return - } - - case <-newCtx.Done(): - if s.worldSocket != nil { - s.worldSocket.Close() - } - return - } - } - }() + authStarted := time.Now() socket.SendPacket(s.authPacket) + authTimeout := s.worldAuthAttemptTimeout + if authTimeout == 0 { + authTimeout = worldAuthAttemptTimeout + } + if deadline, ok := ctx.Deadline(); ok { + if remaining := time.Until(deadline); remaining < authTimeout { + authTimeout = remaining + } + } + authCtx, cancel := context.WithTimeout(ctx, authTimeout) + defer cancel() + if err := s.waitForWorldAuthResponse(authCtx, socket, 0, serverResult.GameServer.Address, authStarted); err != nil { + return nil, err + } - // we need to give some time to add session on the world side - time.Sleep(time.Millisecond * 300) + worldAuthReadyDelay := s.worldAuthSessionReadyDelay + if worldAuthReadyDelay == 0 { + worldAuthReadyDelay = worldAuthSessionReadyDelay + } + if err := s.waitForWorldAuthSessionReady(ctx, socket, 0, serverResult.GameServer.Address, worldAuthReadyDelay); err != nil { + return nil, err + } socket.SendPacket(p) - <-waitDone - - select { - case <-newCtx.Done(): - sendDelFailed() - return fmt.Errorf("character deletion timeouted, gameserver: %s", serverResult.GameServer.Address) - default: + for { + select { + case resp, open := <-socket.ReadChannel(): + if !open { + return nil, fmt.Errorf("world socket closed before %s response, gameserver: %s", responseOpcode.String(), serverResult.GameServer.Address) + } + if resp.Opcode == responseOpcode { + return resp, nil + } + s.logger.Debug(). + Str("opcode", resp.Opcode.String()). + Str("expectedOpcode", responseOpcode.String()). + Str("gameserver", serverResult.GameServer.Address). + Msg("Discarding internal character mutation packet from worldserver") + case <-ctx.Done(): + return nil, fmt.Errorf("character mutation timeout waiting for %s, gameserver: %s: %w", responseOpcode.String(), serverResult.GameServer.Address, ctx.Err()) + } } - - // Let's wait some moment because delete command may take some time on worldserver side. - time.Sleep(time.Second * 1) - - return nil } diff --git a/apps/gateway/session/chat.go b/apps/gateway/session/chat.go index 6c2d242..7f41541 100644 --- a/apps/gateway/session/chat.go +++ b/apps/gateway/session/chat.go @@ -8,6 +8,7 @@ import ( root "github.com/walkline/ToCloud9/apps/gateway" eBroadcaster "github.com/walkline/ToCloud9/apps/gateway/events-broadcaster" "github.com/walkline/ToCloud9/apps/gateway/packet" + pbChar "github.com/walkline/ToCloud9/gen/characters/pb" pbChat "github.com/walkline/ToCloud9/gen/chat/pb" pbGroup "github.com/walkline/ToCloud9/gen/group/pb" pbGuild "github.com/walkline/ToCloud9/gen/guilds/pb" @@ -29,6 +30,7 @@ const ( ChatTypeWhisperInform ChatTypeChannel = 0x11 ChatTypeRaidLeader = 0x27 + ChatTypeRaidWarning = 0x28 ChatTypePartyLeader = 0x33 ) @@ -45,6 +47,54 @@ func (s *GameSession) SendSysMessage(msg string) { s.gameSocket.Send(resp) } +func (s *GameSession) SendPlayerNotFoundNotice(name string) { + resp := packet.NewWriterWithSize(packet.SMsgChatPlayerNotFound, uint32(len(name)+1)) + resp.String(name) + s.gameSocket.Send(resp) +} + +func (s *GameSession) SendPlayerAmbiguousNotice(name string) { + resp := packet.NewWriterWithSize(packet.SMsgChatPlayerAmbiguous, uint32(len(name)+1)) + resp.String(name) + s.gameSocket.Send(resp) +} + +func (s *GameSession) sendAzerothCorePlayerChat(chatType ChatType, language uint32, senderRealmID uint32, senderGUID uint64, senderName string, receiverGUID uint64, channelName string, msg string, chatTag uint8) { + senderObjectGUID := playerObjectGUIDForRealm(senderRealmID, senderGUID) + gmMessage := chatTag&chatTagGM != 0 + opcode := packet.SMsgMessageChat + if gmMessage { + opcode = packet.SMsgGmMessageChat + } + + resp := packet.NewWriterWithSize(opcode, 0) + resp.Uint8(uint8(chatType)) + resp.Uint32(language) + resp.Uint64(senderObjectGUID) + resp.Uint32(0) + + switch chatType { + case ChatTypeWhisperForeign: + resp.Uint32(uint32(len(senderName) + 1)) + resp.String(senderName) + resp.Uint64(receiverGUID) + default: + if gmMessage { + resp.Uint32(uint32(len(senderName) + 1)) + resp.String(senderName) + } + if chatType == ChatTypeChannel { + resp.String(channelName) + } + resp.Uint64(receiverGUID) + } + + resp.Uint32(uint32(len(msg) + 1)) + resp.String(msg) + resp.Uint8(chatTag) + s.gameSocket.Send(resp) +} + func (s *GameSession) HandleChatMessage(ctx context.Context, p *packet.Packet) error { r := p.Reader() msgType := r.Uint32() @@ -61,35 +111,71 @@ func (s *GameSession) HandleChatMessage(ctx context.Context, p *packet.Packet) e case ChatTypeWhisper: to = r.String() msg = r.String() + receiverRealmID, receiverName := s.whisperTargetNameRealm(ctx, to) + gatewayValidatedGameplayCrossrealmWhisper := false + if receiverRealmID != 0 && receiverRealmID != root.RealmID { + var err error + gatewayValidatedGameplayCrossrealmWhisper, err = s.gameplayCrossrealmWhisperAllowed(ctx, receiverRealmID, receiverName) + if err != nil { + return err + } + allowed := gatewayValidatedGameplayCrossrealmWhisper + if !allowed { + allowed, err = s.explicitCrossrealmWhisperAllowed(ctx, receiverRealmID, receiverName) + } + if err != nil { + return err + } + if !allowed { + s.SendPlayerNotFoundNotice(to) + return nil + } + } + res, err := s.chatServiceClient.SendWhisperMessage(ctx, &pbChat.SendWhisperMessageRequest{ - Api: root.Ver, - RealmID: root.RealmID, - SenderGUID: s.character.GUID, - SenderName: s.character.Name, - SenderRace: uint32(s.character.Race), - Language: lang, - ReceiverName: to, - Msg: msg, + Api: root.Ver, + RealmID: root.RealmID, + SenderGUID: s.character.GUID, + SenderAccountID: s.senderAccountID(), + SenderName: s.character.Name, + SenderRace: uint32(s.character.Race), + SenderClass: uint32(s.character.Class), + SenderGender: uint32(s.character.Gender), + Language: lang, + ReceiverRealmID: receiverRealmID, + ReceiverName: receiverName, + Msg: msg, + GatewayValidatedGameplayCrossrealmWhisper: gatewayValidatedGameplayCrossrealmWhisper, + SenderChatTag: uint32(s.currentChatTag()), }) - // TODO: handle response - if err != nil { return err } + if res.GetStatus() == pbChat.SendWhisperMessageResponse_CharacterAmbiguous { + s.SendPlayerAmbiguousNotice(to) + return nil + } + if res.GetStatus() == pbChat.SendWhisperMessageResponse_CharacterNotFound || res.GetReceiverGUID() == 0 { + s.SendPlayerNotFoundNotice(to) + return nil + } + if res.GetStatus() != pbChat.SendWhisperMessageResponse_Ok { + return fmt.Errorf("can't send whisper to %s: %s", to, res.GetStatus().String()) + } - resp := packet.NewWriterWithSize(packet.SMsgMessageChat, 0) - resp.Uint8(uint8(ChatTypeWhisperInform)) - resp.Uint32(lang) - resp.Uint64(res.ReceiverGUID) - resp.Uint32(0) // some flags - resp.Uint64(res.ReceiverGUID) - resp.Uint32(uint32(len(msg) + 1)) - resp.String(msg) - resp.Uint8(0) // chat tag - s.gameSocket.Send(resp) - case ChatTypeGuild: + if res.GetReceiverRealmID() != 0 && res.GetReceiverRealmID() != root.RealmID { + receiverDisplayName := res.GetReceiverName() + if receiverDisplayName == "" { + receiverDisplayName = receiverName + } + s.sendNameQueryResponse(ctx, res.GetReceiverGUID(), receiverDisplayName, res.GetReceiverRace(), res.GetReceiverClass(), res.GetReceiverGender()) + } + + s.sendAzerothCorePlayerChat(ChatTypeWhisperInform, lang, res.ReceiverRealmID, res.ReceiverGUID, res.ReceiverName, res.ReceiverGUID, "", msg, 0) + case ChatTypeGuild, ChatTypeOfficer: msg = r.String() + chatTag := s.currentChatTag() handled, err := s.handleCommandMsgIfNeeded(ctx, msg) if err != nil { @@ -100,31 +186,28 @@ func (s *GameSession) HandleChatMessage(ctx context.Context, p *packet.Packet) e return nil } + if s.forwardAzerothCommandMsgIfNeeded(msg, p) { + return nil + } + _, err = s.guildServiceClient.SendGuildMessage(ctx, &pbGuild.SendGuildMessageParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), SenderGUID: s.character.GUID, Language: lang, Message: msg, - IsOfficerMessage: false, + IsOfficerMessage: ChatType(msgType) == ChatTypeOfficer, + SenderChatTag: uint32(chatTag), }) if err != nil { return err } - resp := packet.NewWriterWithSize(packet.SMsgMessageChat, 0) - resp.Uint8(uint8(ChatTypeGuild)) - resp.Uint32(lang) - resp.Uint64(s.character.GUID) - resp.Uint32(0) // some flags - resp.Uint64(s.character.GUID) - resp.Uint32(uint32(len(msg) + 1)) - resp.String(msg) - resp.Uint8(0) // chat tag - s.gameSocket.Send(resp) - case ChatTypeParty, ChatTypePartyLeader, ChatTypeRaid, ChatTypeRaidLeader: + s.sendAzerothCorePlayerChat(ChatType(msgType), lang, root.RealmID, s.character.GUID, s.character.Name, 0, "", msg, chatTag) + case ChatTypeParty, ChatTypePartyLeader, ChatTypeRaid, ChatTypeRaidLeader, ChatTypeRaidWarning: msg = r.String() + chatTag := s.currentChatTag() handled, err := s.handleCommandMsgIfNeeded(ctx, msg) if err != nil { @@ -135,29 +218,25 @@ func (s *GameSession) HandleChatMessage(ctx context.Context, p *packet.Packet) e return nil } + if s.forwardAzerothCommandMsgIfNeeded(msg, p) { + return nil + } + _, err = s.groupServiceClient.SendMessage(ctx, &pbGroup.SendGroupMessageParams{ - Api: root.Ver, - RealmID: root.RealmID, - SenderGUID: s.character.GUID, - Language: lang, - Message: msg, - MessageType: msgType, + Api: root.Ver, + RealmID: root.RealmID, + SenderGUID: s.character.GUID, + Language: lang, + Message: msg, + MessageType: msgType, + SenderChatTag: uint32(chatTag), }) if err != nil { return err } - resp := packet.NewWriterWithSize(packet.SMsgMessageChat, 0) - resp.Uint8(uint8(msgType)) - resp.Uint32(lang) - resp.Uint64(s.character.GUID) - resp.Uint32(0) // some flags - resp.Uint64(s.character.GUID) - resp.Uint32(uint32(len(msg) + 1)) - resp.String(msg) - resp.Uint8(0) // chat tag - s.gameSocket.Send(resp) + s.sendAzerothCorePlayerChat(ChatType(msgType), lang, root.RealmID, s.character.GUID, s.character.Name, 0, "", msg, chatTag) case ChatTypeChannel: channelName := r.String() @@ -172,6 +251,15 @@ func (s *GameSession) HandleChatMessage(ctx context.Context, p *packet.Packet) e return nil } + if s.forwardAzerothCommandMsgIfNeeded(msg, p) { + return nil + } + + if !s.isGatewayManagedChannel(channelName) && s.worldSocket != nil { + s.worldSocket.WriteChannel() <- p + return nil + } + // Send channel message through chat service return s.SendChannelMessageToChat(ctx, channelName, msg, lang) @@ -205,21 +293,168 @@ func (s *GameSession) HandleChatMessage(ctx context.Context, p *packet.Packet) e func (s *GameSession) HandleEventIncomingWhisperMessage(ctx context.Context, e *eBroadcaster.Event) error { eventData := e.Payload.(*eBroadcaster.IncomingWhisperPayload) + senderGUID := playerObjectGUIDForRealm(eventData.SenderRealmID, eventData.SenderGUID) - resp := packet.NewWriterWithSize(packet.SMsgMessageChat, 0) - resp.Uint8(uint8(ChatTypeWhisper)) - resp.Uint32(eventData.Language) - resp.Uint64(eventData.SenderGUID) - resp.Uint32(0) // some flags - resp.Uint64(eventData.SenderGUID) - resp.Uint32(uint32(len(eventData.Msg) + 1)) - resp.String(eventData.Msg) - resp.Uint8(0) // chat tag - s.gameSocket.Send(resp) + if eventData.SenderRealmID != 0 && eventData.SenderRealmID != root.RealmID { + s.sendNameQueryResponse(ctx, senderGUID, eventData.SenderName, uint32(eventData.SenderRace), uint32(eventData.SenderClass), uint32(eventData.SenderGender)) + s.sendAzerothCorePlayerChat(ChatTypeWhisperForeign, eventData.Language, eventData.SenderRealmID, eventData.SenderGUID, eventData.SenderName, senderGUID, "", eventData.Msg, eventData.SenderChatTag) + return nil + } + + s.sendAzerothCorePlayerChat(ChatTypeWhisper, eventData.Language, eventData.SenderRealmID, eventData.SenderGUID, eventData.SenderName, senderGUID, "", eventData.Msg, eventData.SenderChatTag) return nil } +func (s *GameSession) whisperTargetNameRealm(ctx context.Context, characterName string) (uint32, string) { + if s.realmNamesService == nil { + return 0, characterName + } + + separator := strings.LastIndex(characterName, "-") + if separator <= 0 || separator == len(characterName)-1 { + return 0, characterName + } + + name := characterName[:separator] + realmName := characterName[separator+1:] + realmID, err := s.realmNamesService.IDByName(ctx, realmName) + if err != nil { + return 0, characterName + } + + return realmID, name +} + +func (s *GameSession) senderAccountID() uint32 { + if s.accountID != 0 { + return s.accountID + } + if s.character != nil { + return s.character.AccountID + } + return 0 +} + +func (s *GameSession) explicitCrossrealmWhisperAllowed(ctx context.Context, receiverRealmID uint32, receiverName string) (bool, error) { + senderAccountID := s.senderAccountID() + if s.charServiceClient == nil || senderAccountID == 0 { + return false, nil + } + + charRes, err := s.charServiceClient.CharacterByName(ctx, &pbChar.CharacterByNameRequest{ + Api: root.Ver, + RealmID: receiverRealmID, + CharacterName: receiverName, + }) + if err != nil { + return false, fmt.Errorf("failed to lookup crossrealm whisper target: %w", err) + } + if charRes.GetCharacter() == nil || charRes.GetCharacter().GetAccountID() == 0 { + return false, nil + } + + friendRes, err := s.charServiceClient.AreRealIDFriends(ctx, &pbChar.AreRealIDFriendsRequest{ + Api: root.Ver, + AccountID: senderAccountID, + FriendAccountID: charRes.GetCharacter().GetAccountID(), + }) + if err != nil { + return false, fmt.Errorf("failed to validate real id whisper relation: %w", err) + } + + return friendRes.GetAccepted(), nil +} + +func (s *GameSession) gameplayCrossrealmWhisperAllowed(ctx context.Context, receiverRealmID uint32, receiverName string) (bool, error) { + if s == nil || s.character == nil || s.charServiceClient == nil || s.character.Map == 0 { + return false, nil + } + if !s.hasGameplayCrossrealmWhisperContext() { + return false, nil + } + + targetRes, err := s.charServiceClient.CharacterOnlineByName(ctx, &pbChar.CharacterOnlineByNameRequest{ + Api: root.Ver, + RealmID: receiverRealmID, + CharacterName: receiverName, + }) + if err != nil { + return false, fmt.Errorf("failed to lookup online crossrealm gameplay whisper target: %w", err) + } + + target := targetRes.GetCharacter() + if target == nil || target.GetRealmID() != receiverRealmID || target.GetCharMap() != s.character.Map { + return false, nil + } + + s.logger.Debug(). + Uint32("senderRealmID", root.RealmID). + Uint32("receiverRealmID", receiverRealmID). + Uint32("mapID", s.character.Map). + Str("receiverName", receiverName). + Msg("Allowed explicit crossrealm whisper in shared gameplay context") + return true, nil +} + +func (s *GameSession) hasGameplayCrossrealmWhisperContext() bool { + return gameplayCrossrealmWhisperRouting(s.currentMapTransferRouting) || + gameplayCrossrealmWhisperRouting(s.activeMapTransferRouting) || + gameplayCrossrealmWhisperRouting(s.pendingMapTransferRouting) +} + +func gameplayCrossrealmWhisperRouting(routing *mapTransferRouting) bool { + if routing == nil || !routing.isCrossRealm || routing.realmID != 0 { + return false + } + + switch routing.feature { + case clusterTransferFeatureLFG, clusterTransferFeatureBattleground, clusterTransferFeatureArena, clusterTransferFeatureWintergrasp: + return true + default: + return false + } +} + +func isAzerothCommandMessage(msg string) bool { + if len(msg) < 2 { + return false + } + if msg[0] != '.' && msg[0] != '!' { + return false + } + return msg[1] != msg[0] +} + +func (s *GameSession) forwardAzerothCommandMsgIfNeeded(msg string, p *packet.Packet) bool { + if !isAzerothCommandMessage(msg) { + return false + } + s.trackLocalGMChatCommand(msg) + if s.worldSocket != nil { + s.worldSocket.WriteChannel() <- p + } + return true +} + +func (s *GameSession) trackLocalGMChatCommand(msg string) { + if s == nil || s.character == nil { + return + } + + args := strings.Fields(strings.ToLower(msg)) + if len(args) != 3 || args[0] != ".gm" || args[1] != "chat" { + return + } + + switch args[2] { + case "on": + s.character.ExtraFlags |= playerExtraFlagGMChat + case "off": + s.character.ExtraFlags &^= playerExtraFlagGMChat + } +} + // TODO: rewrite commands handler with some better and more manageable constructions. func (s *GameSession) handleCommandMsgIfNeeded(ctx context.Context, msg string) ( /* isHandled */ bool, error) { const TC9CommandPrefix = ".tc9 " diff --git a/apps/gateway/session/cluster_transport.go b/apps/gateway/session/cluster_transport.go new file mode 100644 index 0000000..941acac --- /dev/null +++ b/apps/gateway/session/cluster_transport.go @@ -0,0 +1,399 @@ +package session + +import ( + "context" + "fmt" + "time" + + root "github.com/walkline/ToCloud9/apps/gateway" + "github.com/walkline/ToCloud9/apps/gateway/packet" + "github.com/walkline/ToCloud9/apps/gateway/sockets" + "github.com/walkline/ToCloud9/shared/wow/guid" +) + +type mapTransferRouting struct { + realmID uint32 + isCrossRealm bool + feature clusterTransferFeature + ownerAddress string +} + +type clusterTransferFeature uint8 + +const ( + clusterTransferFeatureGeneric clusterTransferFeature = iota + clusterTransferFeatureLFG + clusterTransferFeatureBattleground + clusterTransferFeatureArena + clusterTransferFeatureWintergrasp +) + +const transferAbortNotFound uint8 = 3 + +func (feature clusterTransferFeature) String() string { + switch feature { + case clusterTransferFeatureLFG: + return "lfg" + case clusterTransferFeatureBattleground: + return "battleground" + case clusterTransferFeatureArena: + return "arena" + case clusterTransferFeatureWintergrasp: + return "wintergrasp" + case clusterTransferFeatureGeneric: + return "generic" + default: + return "unknown" + } +} + +type clusterNativeWorldportTransport struct { + feature clusterTransferFeature + operation string + playerGUID uint64 + routing *mapTransferRouting + reloadManagedGroupAfterTransfer bool + start func(context.Context) error +} + +type clusterOwnerNativeWorldportTransport struct { + feature clusterTransferFeature + operation string + sessionPlayerGUID uint64 + loginPlayerGUID uint64 + targetAddress string + targetWorldserverID string + routing *mapTransferRouting + forwardAfterPlacement bool + forwardOptions nativeWorldportForwardOptions + reloadManagedGroupAfterTransfer bool + onOwnerPlaced func(context.Context) error +} + +type nativeWorldportForwardOptions struct { + feature clusterTransferFeature + synthesizeTransferPendingForNewMap bool + expectedMapID uint32 + acceptedMapIDs []uint32 +} + +func clusterTransferPrepareRedirectPacket(feature clusterTransferFeature) *packet.Writer { + return packet.NewWriterWithSize(packet.TC9CMsgPrepareForRedirect, 1).Uint8(uint8(feature)) +} + +func cloneMapTransferRouting(routing *mapTransferRouting) *mapTransferRouting { + if routing == nil { + return nil + } + cloned := *routing + return &cloned +} + +func mapTransferRoutingUsesCrossrealmOwner(routing *mapTransferRouting) bool { + return routing != nil && routing.isCrossRealm +} + +func shouldBlockLocalFallbackForMapTransfer(routing *mapTransferRouting) bool { + return mapTransferRoutingUsesCrossrealmOwner(routing) +} + +func shouldKeepMapTransferOnCurrentOwner(routing *mapTransferRouting, currentAddress string) bool { + return routing != nil && routing.ownerAddress != "" && routing.ownerAddress == currentAddress +} + +func (s *GameSession) sendTransferAborted(mapID uint32, reason uint8) { + if s == nil || s.gameSocket == nil { + return + } + s.gameSocket.SendPacket(packet.NewWriterWithSize(packet.SMsgTransferAborted, 5).Uint32(mapID).Uint8(reason).ToPacket()) +} + +func mapTransferLoginPlayerGUID(playerGUID uint64, routing *mapTransferRouting) uint64 { + if mapTransferRoutingUsesCrossrealmOwner(routing) { + return guid.PlayerGUIDForRealm(0, root.RealmID, playerGUID) + } + return playerGUID +} + +func (s *GameSession) setPendingMapTransferRouting(routing *mapTransferRouting) { + s.pendingMapTransferRouting = cloneMapTransferRouting(routing) +} + +func (s *GameSession) clearPendingMapTransferRouting() { + s.pendingMapTransferRouting = nil +} + +func (s *GameSession) activatePendingMapTransferRouting() { + s.activeMapTransferRouting = s.pendingMapTransferRouting + s.pendingMapTransferRouting = nil +} + +func (s *GameSession) clearActiveMapTransferRouting() { + s.activeMapTransferRouting = nil +} + +func (s *GameSession) setCurrentMapTransferRouting(routing *mapTransferRouting) { + s.currentMapTransferRouting = cloneMapTransferRouting(routing) + s.clearPendingMapTransferRouting() +} + +func (s *GameSession) startClusterNativeWorldportTransport(ctx context.Context, transport clusterNativeWorldportTransport) error { + if s == nil { + return fmt.Errorf("can't start %s: session is nil", transport.operation) + } + if transport.start == nil { + return fmt.Errorf("can't start %s for player %d: native transport start function is nil", transport.operation, transport.playerGUID) + } + + routing := cloneMapTransferRouting(transport.routing) + if routing != nil && routing.feature == clusterTransferFeatureGeneric { + routing.feature = transport.feature + } + s.setPendingMapTransferRouting(routing) + + if err := transport.start(ctx); err != nil { + s.clearPendingMapTransferRouting() + return fmt.Errorf("%s failed for player %d: %w", transport.operation, transport.playerGUID, err) + } + + if transport.reloadManagedGroupAfterTransfer && s.character != nil { + s.character.GroupMangedByGameServer = true + } + + return nil +} + +func (s *GameSession) startClusterOwnerNativeWorldportTransport(ctx context.Context, transport clusterOwnerNativeWorldportTransport) error { + if s == nil { + return fmt.Errorf("can't start %s: session is nil", transport.operation) + } + if transport.sessionPlayerGUID == 0 { + return fmt.Errorf("can't start %s: session player guid is empty", transport.operation) + } + if transport.loginPlayerGUID == 0 { + transport.loginPlayerGUID = transport.sessionPlayerGUID + } + + redirectedToOwner := false + if transport.targetAddress != "" { + if s.worldSocket == nil { + return fmt.Errorf("can't start %s for player %d: world socket is nil", transport.operation, transport.sessionPlayerGUID) + } + if s.worldSocket.Address() != transport.targetAddress { + if err := s.redirectPlayerToGameServerAddressForTransfer(ctx, transport.feature, transport.loginPlayerGUID, transport.targetAddress, transport.targetWorldserverID); err != nil { + s.clearPendingMapTransferRouting() + return fmt.Errorf("%s redirect to worldserver %q failed for player %d: %w", transport.operation, transport.targetWorldserverID, transport.sessionPlayerGUID, err) + } + redirectedToOwner = true + } else if transport.targetWorldserverID != "" { + s.worldserverID = transport.targetWorldserverID + } + } else if transport.targetWorldserverID != "" { + s.worldserverID = transport.targetWorldserverID + } + + routing := cloneMapTransferRouting(transport.routing) + if routing != nil && routing.feature == clusterTransferFeatureGeneric { + routing.feature = transport.feature + } + if routing != nil && transport.targetAddress != "" { + routing.ownerAddress = transport.targetAddress + } + s.armClusterOwnerNativeWorldport(routing) + if s.logger != nil && routing != nil { + s.logger.Debug(). + Uint64("character", transport.sessionPlayerGUID). + Uint32("realmID", routing.realmID). + Bool("isCrossrealm", routing.isCrossRealm). + Str("feature", routing.feature.String()). + Str("ownerAddress", routing.ownerAddress). + Msg("TC9 armed owner native worldport routing") + } + + if transport.onOwnerPlaced != nil { + if err := transport.onOwnerPlaced(ctx); err != nil { + return fmt.Errorf("%s owner placement hook failed for player %d: %w", transport.operation, transport.sessionPlayerGUID, err) + } + } + if transport.reloadManagedGroupAfterTransfer && s.character != nil { + s.character.GroupMangedByGameServer = true + } + + if transport.forwardAfterPlacement || redirectedToOwner { + transport.forwardOptions.feature = transport.feature + return s.forwardNextNativeWorldport(ctx, transport.forwardOptions) + } + return nil +} + +func (s *GameSession) armClusterOwnerNativeWorldport(routing *mapTransferRouting) { + s.setCurrentMapTransferRouting(routing) + s.setPendingMapTransferRouting(routing) +} + +func (s *GameSession) clearPendingRedirectState() { + s.pendingRedirectID = "" + s.pendingRedirectAt = time.Time{} +} + +func (s *GameSession) forwardNextNativeWorldport(ctx context.Context, options nativeWorldportForwardOptions) error { + sentTransferPending := false + return s.processWorldPacketsInPlace(ctx, func(p *packet.Packet) (bool, error) { + switch p.Opcode { + case packet.SMsgTransferPending: + s.clearPendingRedirectState() + sentTransferPending = true + s.gameSocket.SendPacket(p) + return false, nil + case packet.SMsgNewWorld: + s.clearPendingRedirectState() + mapID := p.Reader().Uint32() + if !nativeWorldportAcceptsMapID(options, mapID) { + if s.logger != nil { + s.logger.Debug(). + Uint16("opcode", uint16(p.Opcode)). + Uint32("account", s.accountID). + Uint32("map", mapID). + Uint32("expectedMap", options.expectedMapID). + Interface("acceptedMaps", options.acceptedMapIDs). + Str("feature", options.feature.String()). + Msg("TC9 dropping intermediate native worldport for unexpected map") + } + return false, nil + } + if options.synthesizeTransferPendingForNewMap && !sentTransferPending && s.character != nil && mapID != s.character.Map { + resp := packet.NewWriterWithSize(packet.SMsgTransferPending, 0) + resp.Uint32(mapID) + s.gameSocket.Send(resp) + } + return true, s.forwardNewWorldPacket(ctx, p, mapID) + case packet.SMsgTransferAborted, packet.SMsgLFGTeleportDenied: + s.clearPendingRedirectState() + s.clearPendingMapTransferRouting() + if s.teleportingToNewMap == nil { + s.clearActiveMapTransferRouting() + } + s.gameSocket.SendPacket(p) + return true, nil + default: + if s.logger != nil { + s.logger.Debug(). + Uint16("opcode", uint16(p.Opcode)). + Uint32("account", s.accountID). + Str("feature", options.feature.String()). + Msg("TC9 dropping intermediate native worldport packet") + } + return false, nil + } + }) +} + +func nativeWorldportAcceptsMapID(options nativeWorldportForwardOptions, mapID uint32) bool { + if options.expectedMapID == 0 && len(options.acceptedMapIDs) == 0 { + return true + } + if options.expectedMapID != 0 && mapID == options.expectedMapID { + return true + } + for _, acceptedMapID := range options.acceptedMapIDs { + if mapID == acceptedMapID { + return true + } + } + return false +} + +func (s *GameSession) redirectPlayerToGameServerAddress(ctx context.Context, playerGuid uint64, desiredGameServerAddress string, desiredWorldserverID string) error { + return s.redirectPlayerToGameServerAddressForTransfer(ctx, clusterTransferFeatureGeneric, playerGuid, desiredGameServerAddress, desiredWorldserverID) +} + +func (s *GameSession) redirectPlayerToGameServerAddressForTransfer(ctx context.Context, feature clusterTransferFeature, playerGuid uint64, desiredGameServerAddress string, desiredWorldserverID string) error { + oldServerAddress := s.worldSocket.Address() + redirectID := fmt.Sprintf("%d-%d-%d", s.accountID, playerGuid, time.Now().UnixNano()) + redirectStarted := time.Now() + s.pendingRedirectID = redirectID + s.pendingRedirectAt = redirectStarted + clearPendingRedirect := func() { + if s.pendingRedirectID == redirectID { + s.pendingRedirectID = "" + s.pendingRedirectAt = time.Time{} + } + } + + saveAndClosePacket := clusterTransferPrepareRedirectPacket(feature) + s.worldSocket.Send(saveAndClosePacket) + + confirmationContext, cancel := context.WithTimeout(ctx, s.packetProcessTimeout) + defer cancel() + if err := s.waitForSourceRedirectReady(confirmationContext, s.worldSocket, redirectID, playerGuid, 0, oldServerAddress, desiredGameServerAddress); err != nil { + clearPendingRedirect() + return fmt.Errorf("failed to redirect player with account %d: %w", s.accountID, err) + } + + s.worldSocket.Close() + s.worldSocket = nil + + newSocket, err := s.connectToGameServerWithAddressRetry(ctx, playerGuid, desiredGameServerAddress, nil) + if err != nil { + clearPendingRedirect() + return fmt.Errorf("connectToGameServerWithAddress failed: %w, address: %s", err, desiredGameServerAddress) + } + + if err := s.waitForTargetWorldLoginVerify(ctx, newSocket, redirectID, playerGuid, desiredGameServerAddress); err != nil { + clearPendingRedirect() + newSocket.Close() + return err + } + + s.worldSocket = newSocket + if desiredWorldserverID != "" { + s.worldserverID = desiredWorldserverID + } else { + s.worldserverID = s.canonicalWorldserverIDForAddress(ctx, desiredGameServerAddress) + } + + if s.showGameserverConnChangeToClient { + s.SendSysMessage(fmt.Sprintf("You have been redirected from %s to %s gameserver.", oldServerAddress, desiredGameServerAddress)) + } + + return nil +} + +func (s *GameSession) waitForTargetWorldLoginVerify(ctx context.Context, socket sockets.Socket, redirectID string, playerGUID uint64, targetAddress string) error { + for { + select { + case p, open := <-socket.ReadChannel(): + if !open { + return fmt.Errorf("target world socket closed before login verify, redirect %s, account %d, character %d, target %s", redirectID, s.accountID, playerGUID, targetAddress) + } + + switch p.Opcode { + case packet.SMsgLoginVerifyWorld: + s.observeWorldLoginVerify(p) + return nil + case packet.SMsgCharacterLoginFailed: + status := uint8(packet.LoginErrorCodeLoginFailed) + if p.Size > 0 { + reader := p.Reader() + status = reader.Uint8() + if err := reader.Error(); err != nil { + return fmt.Errorf("target worldserver sent malformed login failed packet, redirect %s, account %d, character %d, target %s: %w", redirectID, s.accountID, playerGUID, targetAddress, err) + } + } + return fmt.Errorf("target worldserver rejected login with status %d, redirect %s, account %d, character %d, target %s", status, redirectID, s.accountID, playerGUID, targetAddress) + default: + if s.logger != nil { + s.logger.Debug(). + Str("redirect", redirectID). + Uint32("account", s.accountID). + Uint64("character", playerGUID). + Uint16("opcode", uint16(p.Opcode)). + Str("target", targetAddress). + Msg("Dropping target world packet while waiting for login verify") + } + } + case <-ctx.Done(): + return fmt.Errorf("timed out waiting for target login verify, redirect %s, account %d, character %d, target %s: %w", redirectID, s.accountID, playerGUID, targetAddress, ctx.Err()) + } + } +} diff --git a/apps/gateway/session/error.go b/apps/gateway/session/error.go index 9be6eda..12f5658 100644 --- a/apps/gateway/session/error.go +++ b/apps/gateway/session/error.go @@ -1,5 +1,7 @@ package session +import "strings" + type UserFriendlyError struct { UserError string RealError error @@ -17,8 +19,31 @@ func NewMailServiceUnavailableErr(err error) error { } func NewGroupServiceUnavailableErr(err error) error { + userError := "Group service unavailable. Try again later." + if isGroupPermissionError(err) { + userError = "You are not the group leader or assistant." + } + return &UserFriendlyError{ - UserError: "Group service unavailable. Try again later.", + UserError: userError, RealError: err, } } + +func isGroupPermissionError(err error) bool { + if err == nil { + return false + } + + return strings.Contains(strings.ToLower(err.Error()), "not enough permissions") +} + +func isSilentGroupMutationError(err error) bool { + if err == nil { + return false + } + + errMsg := strings.ToLower(err.Error()) + return strings.Contains(errMsg, "lfg group does not allow this operation") || + strings.Contains(errMsg, "invalid group operation") +} diff --git a/apps/gateway/session/group-cluster-extra.go b/apps/gateway/session/group-cluster-extra.go new file mode 100644 index 0000000..6fe31c3 --- /dev/null +++ b/apps/gateway/session/group-cluster-extra.go @@ -0,0 +1,934 @@ +package session + +import ( + "context" + "fmt" + "sort" + "strconv" + "strings" + + root "github.com/walkline/ToCloud9/apps/gateway" + eBroadcaster "github.com/walkline/ToCloud9/apps/gateway/events-broadcaster" + "github.com/walkline/ToCloud9/apps/gateway/packet" + "github.com/walkline/ToCloud9/gen/group/pb" + "github.com/walkline/ToCloud9/shared/events" + "github.com/walkline/ToCloud9/shared/groupstatetrace" + wowguid "github.com/walkline/ToCloud9/shared/wow/guid" +) + +const ( + readyCheckDefaultDurationMs uint32 = 35000 + + groupMemberFlagAssistant uint8 = 0x01 + groupMemberFlagMainTank uint8 = 0x02 + groupMemberFlagMainAssist uint8 = 0x04 + + subGroupSwapPendingFlag uint32 = 0x80 + + memberStatusOnline uint16 = 0x0001 + memberStatusDead uint16 = 0x0004 + memberStatusGhost uint16 = 0x0008 + + ghostAuraSpellID uint32 = 8326 + wispSpiritAuraSpellID uint32 = 20584 + + groupUpdateFlagStatus uint32 = 0x00000001 + groupUpdateFlagCurHP uint32 = 0x00000002 + groupUpdateFlagMaxHP uint32 = 0x00000004 + groupUpdateFlagPowerType uint32 = 0x00000008 + groupUpdateFlagCurPower uint32 = 0x00000010 + groupUpdateFlagMaxPower uint32 = 0x00000020 + groupUpdateFlagLevel uint32 = 0x00000040 + groupUpdateFlagZone uint32 = 0x00000080 + groupUpdateFlagAuras uint32 = 0x00000200 +) + +type groupMemberAuraRenderKey struct { + realmID uint32 + memberGUID uint64 +} + +func (s *GameSession) HandleRaidReadyCheck(ctx context.Context, p *packet.Packet) error { + if p.Source == packet.SourceWorldServer { + // Gateway-owned ready-check events already render these packets. + return nil + } + + r := p.Reader() + + if r.Left() == 0 { + _, err := s.groupServiceClient.StartReadyCheck(ctx, &pb.StartReadyCheckRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + LeaderGUID: s.character.GUID, + DurationMs: readyCheckDefaultDurationMs, + }) + if err != nil { + return NewGroupServiceUnavailableErr(err) + } + + return nil + } + + state := r.Uint8() + if err := r.Error(); err != nil { + return err + } + + _, err := s.groupServiceClient.SetReadyCheckMemberState(ctx, &pb.SetReadyCheckMemberStateRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + MemberGUID: s.character.GUID, + State: uint32(state), + }) + if err != nil { + return NewGroupServiceUnavailableErr(err) + } + + return nil +} + +func (s *GameSession) HandleRaidReadyCheckConfirm(_ context.Context, _ *packet.Packet) error { + return nil +} + +func (s *GameSession) HandleRaidReadyCheckFinished(ctx context.Context, p *packet.Packet) error { + if p.Source == packet.SourceWorldServer { + // Gateway-owned ready-check events already render these packets. + return nil + } + + _, err := s.groupServiceClient.FinishReadyCheck(ctx, &pb.FinishReadyCheckRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + }) + if err != nil { + return NewGroupServiceUnavailableErr(err) + } + + return nil +} + +func (s *GameSession) HandleGroupChangeSubGroup(ctx context.Context, p *packet.Packet) error { + if p.Source == packet.SourceWorldServer { + s.gameSocket.WriteChannel() <- p + return nil + } + + r := p.Reader() + memberName := r.String() + subGroup := r.Uint8() + + if err := r.Error(); err != nil { + return err + } + + groupResp, err := s.groupServiceClient.GetGroupByMember(ctx, &pb.GetGroupByMemberRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + Player: s.character.GUID, + }) + if err != nil { + return NewGroupServiceUnavailableErr(err) + } + if groupResp.Group == nil { + return nil + } + + member := s.groupMemberByName(ctx, groupResp.Group, memberName) + if member == nil { + return fmt.Errorf("group member %q not found", memberName) + } + + _, err = s.groupServiceClient.ChangeMemberSubGroup(ctx, &pb.ChangeMemberSubGroupRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + UpdaterGUID: s.character.GUID, + MemberGUID: member.Guid, + SubGroup: uint32(subGroup), + }) + if err != nil { + return s.deniedGroupMutationErr(ctx, err) + } + + return nil +} + +func (s *GameSession) HandleGroupSwapSubGroup(ctx context.Context, p *packet.Packet) error { + if p.Source == packet.SourceWorldServer { + s.gameSocket.WriteChannel() <- p + return nil + } + + r := p.Reader() + memberName1 := r.String() + memberName2 := r.String() + + if err := r.Error(); err != nil { + return err + } + + groupResp, err := s.groupServiceClient.GetGroupByMember(ctx, &pb.GetGroupByMemberRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + Player: s.character.GUID, + }) + if err != nil { + return NewGroupServiceUnavailableErr(err) + } + if groupResp.Group == nil { + return nil + } + + member1 := s.groupMemberByName(ctx, groupResp.Group, memberName1) + if member1 == nil { + return fmt.Errorf("group member %q not found", memberName1) + } + + member2 := s.groupMemberByName(ctx, groupResp.Group, memberName2) + if member2 == nil { + return fmt.Errorf("group member %q not found", memberName2) + } + + if member1.SubGroup == member2.SubGroup { + return nil + } + + if _, err = s.groupServiceClient.ChangeMemberSubGroup(ctx, &pb.ChangeMemberSubGroupRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + UpdaterGUID: s.character.GUID, + MemberGUID: member1.Guid, + SubGroup: member2.SubGroup | subGroupSwapPendingFlag, + }); err != nil { + return s.deniedGroupMutationErr(ctx, err) + } + + _, err = s.groupServiceClient.ChangeMemberSubGroup(ctx, &pb.ChangeMemberSubGroupRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + UpdaterGUID: s.character.GUID, + MemberGUID: member2.Guid, + SubGroup: member1.SubGroup, + }) + if err != nil { + return s.deniedGroupMutationErr(ctx, err) + } + + return nil +} + +func (s *GameSession) deniedGroupMutationErr(ctx context.Context, err error) error { + if isGroupPermissionError(err) { + if syncErr := s.SendCurrentGroupUpdate(ctx); syncErr != nil { + s.logger.Debug().Err(syncErr).Msg("can't resync group after denied group mutation") + } + } + + return NewGroupServiceUnavailableErr(err) +} + +func (s *GameSession) HandleGroupAssistantLeader(ctx context.Context, p *packet.Packet) error { + if p.Source == packet.SourceWorldServer { + s.gameSocket.WriteChannel() <- p + return nil + } + + r := p.Reader() + + memberGUID := readGUIDThenBoolCompatible(r) + apply := readLastBoolCompatible(r) + + if err := r.Error(); err != nil { + return err + } + + return s.setGroupMemberFlag(ctx, memberGUID, groupMemberFlagAssistant, apply) +} + +func (s *GameSession) HandlePartyAssignment(ctx context.Context, p *packet.Packet) error { + if p.Source == packet.SourceWorldServer { + s.gameSocket.WriteChannel() <- p + return nil + } + + r := p.Reader() + + assignment := r.Uint8() + apply := r.Uint8() != 0 + memberGUID := readRemainingGUIDCompatible(r) + + if err := r.Error(); err != nil { + return err + } + + var flag uint8 + switch assignment { + case 0: + flag = groupMemberFlagMainTank + case 1: + flag = groupMemberFlagMainAssist + default: + return nil + } + + return s.setGroupMemberFlag(ctx, memberGUID, flag, apply) +} + +func (s *GameSession) HandleResetInstances(ctx context.Context, p *packet.Packet) error { + if p.Source == packet.SourceWorldServer { + s.gameSocket.WriteChannel() <- p + return nil + } + + _, err := s.groupServiceClient.ResetInstance(ctx, &pb.ResetInstanceRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + MapID: 0, + Difficulty: 0, + }) + if err != nil { + return NewGroupServiceUnavailableErr(err) + } + + return nil +} + +func (s *GameSession) HandleSetSavedInstanceExtend(ctx context.Context, p *packet.Packet) error { + if p.Source == packet.SourceWorldServer { + s.gameSocket.WriteChannel() <- p + return nil + } + + r := p.Reader() + + mapID := r.Uint32() + difficulty := r.Uint32() + extended := r.Uint8() != 0 + + if err := r.Error(); err != nil { + return err + } + + _, err := s.groupServiceClient.SetInstanceBindExtension(ctx, &pb.SetInstanceBindExtensionRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + MapID: mapID, + Difficulty: difficulty, + Extended: extended, + }) + if err != nil { + return NewGroupServiceUnavailableErr(err) + } + + return nil +} + +func (s *GameSession) HandleEventGroupReadyCheckStarted(ctx context.Context, e *eBroadcaster.Event) error { + eventData := e.Payload.(*events.GroupEventReadyCheckStartedPayload) + + resp := packet.NewWriterWithSize(packet.MsgRaidReadyCheck, 8) + resp.Uint64(playerObjectGUIDForRealm(eventData.RealmID, eventData.LeaderGUID)) + s.gameSocket.Send(resp) + return nil +} + +func (s *GameSession) HandleEventGroupReadyCheckMemberState(ctx context.Context, e *eBroadcaster.Event) error { + eventData := e.Payload.(*events.GroupEventReadyCheckMemberStatePayload) + + if ok, err := s.isCurrentCharacterGroupLeaderOrAssistant(ctx, eventData.RealmID, eventData.GroupID); err != nil || !ok { + return err + } + + resp := packet.NewWriterWithSize(packet.MsgRaidReadyCheckConfirm, 9) + resp.Uint64(playerObjectGUIDForRealm(eventData.RealmID, eventData.MemberGUID)) + if eventData.State == 1 { + resp.Uint8(1) + } else { + resp.Uint8(0) + } + s.gameSocket.Send(resp) + return nil +} + +func (s *GameSession) HandleEventGroupReadyCheckFinished(_ context.Context, _ *eBroadcaster.Event) error { + resp := packet.NewWriterWithSize(packet.MsgRaidReadyCheckFinished, 0) + s.gameSocket.Send(resp) + return nil +} + +func (s *GameSession) HandleEventGroupMemberSubGroupChanged(ctx context.Context, e *eBroadcaster.Event) error { + if s.character == nil { + return nil + } + + // AzerothCore applies this event through libsidecar and emits the roster. + return nil +} + +func (s *GameSession) HandleEventGroupMemberFlagsChanged(ctx context.Context, e *eBroadcaster.Event) error { + if s.character == nil { + return nil + } + + // AzerothCore applies this event through libsidecar and emits the roster. + return nil +} + +func (s *GameSession) HandleEventGroupMemberStateChanged(ctx context.Context, e *eBroadcaster.Event) error { + if s.clusterGroupPresentationBlocked() { + return nil + } + + eventData := e.Payload.(*events.GroupEventMemberStateChangedPayload) + return s.sendGroupMemberStateChanged(eventData) +} + +func (s *GameSession) HandleEventGroupMemberStatesChanged(ctx context.Context, e *eBroadcaster.Event) error { + if s.clusterGroupPresentationBlocked() { + return nil + } + + eventData := e.Payload.(*events.GroupEventMemberStatesChangedPayload) + for _, state := range eventData.States { + payload := groupMemberStateChangedPayloadFromBatch(eventData, state) + if err := s.sendGroupMemberStateChanged(&payload); err != nil { + return err + } + } + return nil +} + +func (s *GameSession) sendGroupMemberStateChanged(eventData *events.GroupEventMemberStateChangedPayload) error { + if s.shouldDropSourceWorldMemberStateEcho(eventData) { + return nil + } + + resp := packet.NewWriterWithSize(packet.SMsgPartyMemberStats, 64) + resp.GUID(playerObjectGUIDForRealm(eventData.RealmID, eventData.MemberGUID)) + + updateMask := groupUpdateFlagStatus + hasHealth := eventData.Online && eventData.MaxHealth > 0 + hasPower := eventData.Online && eventData.MaxPower > 0 + + if hasHealth { + updateMask |= groupUpdateFlagCurHP | groupUpdateFlagMaxHP + } + + if hasPower { + updateMask |= groupUpdateFlagPowerType | groupUpdateFlagCurPower | groupUpdateFlagMaxPower + } + + if eventData.Level != 0 { + updateMask |= groupUpdateFlagLevel + } + + if eventData.ZoneID != 0 { + updateMask |= groupUpdateFlagZone + } + + renderAuras := compactGroupMemberAuras(eventData.Auras) + + if eventData.Online && eventData.AurasKnown { + updateMask |= groupUpdateFlagAuras + } + + status := groupMemberStatusFromState(eventData, renderAuras) + + if event := groupstatetrace.Event(s.logger, "gateway.member_state.packet", eventData.MemberGUID); event != nil { + event. + Uint32("realmID", eventData.RealmID). + Uint64("memberGUID", eventData.MemberGUID). + Uint32("accountID", s.accountID). + Str("sourceGatewayID", eventData.SourceGatewayID). + Str("sourceWorldserverID", eventData.SourceWorldserverID). + Bool("online", eventData.Online). + Uint8("level", eventData.Level). + Uint8("class", eventData.Class). + Uint32("zoneID", eventData.ZoneID). + Uint32("mapID", eventData.MapID). + Bool("hasHealth", hasHealth). + Uint32("health", eventData.Health). + Uint32("maxHealth", eventData.MaxHealth). + Bool("hasPower", hasPower). + Uint8("powerType", eventData.PowerType). + Uint32("power", eventData.Power). + Uint32("maxPower", eventData.MaxPower). + Bool("dead", eventData.Dead). + Bool("deadKnown", eventData.DeadKnown). + Bool("ghost", eventData.Ghost). + Bool("ghostKnown", eventData.GhostKnown). + Bool("aurasKnown", eventData.AurasKnown). + Int("auraCount", len(renderAuras)). + Str("auraSpells", formatGroupMemberAuraTrace(renderAuras)). + Uint16("status", status). + Uint32("updateMask", updateMask). + Msg(groupstatetrace.Message) + } + + resp.Uint32(updateMask) + + resp.Uint16(status) + + if hasHealth { + health := eventData.Health + if health > eventData.MaxHealth { + health = eventData.MaxHealth + } + + resp.Uint32(health) + resp.Uint32(eventData.MaxHealth) + } + + if hasPower { + power := eventData.Power + if power > eventData.MaxPower { + power = eventData.MaxPower + } + + resp.Uint8(eventData.PowerType) + resp.Uint16(clampGroupPacketPower(power)) + resp.Uint16(clampGroupPacketPower(eventData.MaxPower)) + } + + if updateMask&groupUpdateFlagLevel != 0 { + resp.Uint16(uint16(eventData.Level)) + } + + if updateMask&groupUpdateFlagZone != 0 { + resp.Uint16(uint16(eventData.ZoneID)) + } + + if updateMask&groupUpdateFlagAuras != 0 { + s.writeGroupMemberAuraDelta(resp, eventData.RealmID, eventData.MemberGUID, renderAuras) + } else if !eventData.Online { + s.clearRenderedGroupMemberAuras(eventData.RealmID, eventData.MemberGUID) + } + + s.gameSocket.Send(resp) + return nil +} + +func formatGroupMemberAuraTrace(auras []events.GroupMemberAuraState) string { + auras = compactGroupMemberAuras(auras) + if len(auras) == 0 { + return "" + } + + parts := make([]string, 0, len(auras)) + for _, aura := range auras { + parts = append(parts, strconv.Itoa(int(aura.Slot))+":"+strconv.FormatUint(uint64(aura.SpellID), 10)+":"+strconv.Itoa(int(aura.Flags))) + } + + return strings.Join(parts, ",") +} + +func groupMemberStatusFromState(eventData *events.GroupEventMemberStateChangedPayload, auras []events.GroupMemberAuraState) uint16 { + if !eventData.Online { + return 0 + } + + status := memberStatusOnline + if eventData.Ghost || hasGhostAura(auras) { + status |= memberStatusGhost + } else if eventData.Dead || (!eventData.DeadKnown && eventData.MaxHealth > 0 && eventData.Health == 0) { + status |= memberStatusDead + } + + return status +} + +func hasGhostAura(auras []events.GroupMemberAuraState) bool { + for _, aura := range auras { + switch aura.SpellID { + case ghostAuraSpellID, wispSpiritAuraSpellID: + return true + } + } + + return false +} + +func groupMemberStateChangedPayloadFromBatch(batch *events.GroupEventMemberStatesChangedPayload, state events.GroupMemberStateUpdate) events.GroupEventMemberStateChangedPayload { + return events.GroupEventMemberStateChangedPayload{ + ServiceID: batch.ServiceID, + RealmID: batch.RealmID, + GroupID: batch.GroupID, + SourceGatewayID: batch.SourceGatewayID, + SourceWorldserverID: batch.SourceWorldserverID, + MemberGUID: state.MemberGUID, + Online: state.Online, + Level: state.Level, + Class: state.Class, + ZoneID: state.ZoneID, + MapID: state.MapID, + Health: state.Health, + MaxHealth: state.MaxHealth, + PowerType: state.PowerType, + Power: state.Power, + MaxPower: state.MaxPower, + AurasKnown: state.AurasKnown, + Auras: state.Auras, + DeadKnown: state.DeadKnown, + Dead: state.Dead, + GhostKnown: state.GhostKnown, + Ghost: state.Ghost, + Receivers: batch.Receivers, + } +} + +func (s *GameSession) shouldDropSourceWorldMemberStateEcho(eventData *events.GroupEventMemberStateChangedPayload) bool { + if eventData.SourceWorldserverID == "" { + return false + } + + if !s.isCurrentWorldserverSourceID(eventData.SourceWorldserverID) { + return false + } + + return s.character != nil && samePlayerGUID(eventData.RealmID, eventData.MemberGUID, sessionRealmID(eventData.RealmID), s.character.GUID) +} + +func (s *GameSession) writeGroupMemberAuraDelta(resp *packet.Writer, realmID uint32, memberGUID uint64, auras []events.GroupMemberAuraState) { + current := groupMemberAuraStateBySlot(auras) + previous := s.renderedGroupMemberAurasFor(realmID, memberGUID) + + auraBySlot := make(map[uint8]events.GroupMemberAuraState, len(current)) + var auraMask uint64 + for slot, aura := range current { + auraBySlot[slot] = aura + auraMask |= uint64(1) << slot + } + for slot := range previous { + if _, found := current[slot]; !found { + auraMask |= uint64(1) << slot + } + } + + resp.Uint64(auraMask) + for slot := uint8(0); slot < maxGroupAuraSlots; slot++ { + if auraMask&(uint64(1)<= maxGroupAuraSlots || aura.SpellID == 0 { + continue + } + byOriginalSlot[aura.Slot] = aura + } + if len(byOriginalSlot) == 0 { + return nil + } + + normalized := make([]events.GroupMemberAuraState, 0, len(byOriginalSlot)) + for _, aura := range byOriginalSlot { + normalized = append(normalized, aura) + } + sort.Slice(normalized, func(i, j int) bool { + return normalized[i].Slot < normalized[j].Slot + }) + + return normalized +} + +func (s *GameSession) setGroupMemberFlag(ctx context.Context, memberGUID uint64, flag uint8, apply bool) error { + memberGUID = playerDBGUIDFromClientGUID(memberGUID) + + groupResp, err := s.groupServiceClient.GetGroupByMember(ctx, &pb.GetGroupByMemberRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + Player: s.character.GUID, + }) + if err != nil { + return NewGroupServiceUnavailableErr(err) + } + + if groupResp.Group == nil { + return nil + } + + groupRealmID := groupHomeRealmIDFromPB(groupResp.Group) + var current *pb.GetGroupResponse_GroupMember + for _, member := range groupResp.Group.Members { + if samePlayerGUID(groupRealmID, member.Guid, root.RealmID, memberGUID) { + current = member + break + } + } + + if current == nil { + return nil + } + + flags := uint8(current.Flags) + if apply { + flags |= flag + } else { + flags &^= flag + } + + _, err = s.groupServiceClient.SetMemberFlags(ctx, &pb.SetMemberFlagsRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + UpdaterGUID: s.character.GUID, + MemberGUID: current.Guid, + Flags: uint32(flags), + Roles: current.Roles, + }) + if err != nil { + return NewGroupServiceUnavailableErr(err) + } + + return nil +} + +func (s *GameSession) isCurrentCharacterGroupLeaderOrAssistant(ctx context.Context, realmID uint32, groupID uint) (bool, error) { + groupResp, err := s.groupServiceClient.GetGroup(ctx, &pb.GetGroupRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: realmID, + GroupID: uint32(groupID), + }) + if err != nil { + return false, NewGroupServiceUnavailableErr(err) + } + + if groupResp.Group == nil { + return false, nil + } + + groupRealmID := groupHomeRealmIDFromPB(groupResp.Group) + if samePlayerGUID(groupRealmID, groupResp.Group.Leader, root.RealmID, s.character.GUID) { + return true, nil + } + + for _, member := range groupResp.Group.Members { + if samePlayerGUID(groupRealmID, member.Guid, root.RealmID, s.character.GUID) { + return uint8(member.Flags)&groupMemberFlagAssistant != 0, nil + } + } + + return false, nil +} + +func (s *GameSession) groupMemberByName(ctx context.Context, group *pb.GetGroupResponse_Group, memberName string) *pb.GetGroupResponse_GroupMember { + if group == nil || memberName == "" { + return nil + } + + groupRealmID := groupHomeRealmIDFromPB(group) + requestedName, requestedRealm, hasRequestedRealm := strings.Cut(memberName, "-") + var fallback *pb.GetGroupResponse_GroupMember + var ambiguousFallback bool + + for _, member := range group.Members { + if strings.EqualFold(member.Name, memberName) { + return member + } + + if !hasRequestedRealm || !strings.EqualFold(member.Name, requestedName) { + continue + } + + memberRealmID := groupMemberRealmID(groupRealmID, member) + if s.realmNamesService != nil { + if realmName, err := s.realmNamesService.NameByID(ctx, memberRealmID); err == nil && + normalizedRealmName(realmName) == normalizedRealmName(requestedRealm) { + return member + } + } + + if fallback == nil && !ambiguousFallback { + fallback = member + continue + } + + ambiguousFallback = true + fallback = nil + } + + return fallback +} + +func normalizedRealmName(name string) string { + name = strings.ToLower(name) + name = strings.ReplaceAll(name, " ", "") + name = strings.ReplaceAll(name, "'", "") + return name +} + +func playerObjectGUIDForRealm(realmID uint32, playerGUID uint64) uint64 { + // Gateway packet rendering uses client-facing player ObjectGuid values: + // local members stay low DB GUIDs, foreign members keep realm-scoped + // ObjectGuids. This is for rendering new packets only; forwarded client + // packets must keep their original serialized GUIDs. + playerRealmID := wowguid.PlayerRealmIDOrDefault(realmID, playerGUID) + return wowguid.PlayerGUIDForRealm(root.RealmID, playerRealmID, playerGUID) +} + +func playerObjectGUIDForMember(groupRealmID uint32, member *pb.GetGroupResponse_GroupMember) uint64 { + if member == nil { + return 0 + } + + return playerObjectGUIDForRealm(groupMemberRealmID(groupRealmID, member), member.Guid) +} + +func groupMemberRealmID(groupRealmID uint32, member *pb.GetGroupResponse_GroupMember) uint32 { + if member == nil { + return groupRealmID + } + if member.RealmID != 0 { + return member.RealmID + } + + return playerRealmIDOrDefault(groupRealmID, member.Guid) +} + +func groupHomeRealmIDFromPB(group *pb.GetGroupResponse_Group) uint32 { + if group != nil && group.RealmID != 0 { + return group.RealmID + } + + return root.RealmID +} + +func sessionRealmID(fallbackRealmID uint32) uint32 { + if root.RealmID != 0 { + return root.RealmID + } + + return fallbackRealmID +} + +func samePlayerGUID(leftDefaultRealmID uint32, leftGUID uint64, rightDefaultRealmID uint32, rightGUID uint64) bool { + return wowguid.SamePlayer(leftDefaultRealmID, leftGUID, rightDefaultRealmID, rightGUID) +} + +func playerRealmIDOrDefault(defaultRealmID uint32, playerGUID uint64) uint32 { + return wowguid.PlayerRealmIDOrDefault(defaultRealmID, playerGUID) +} + +func playerLowGUIDValue(playerGUID uint64) uint64 { + return wowguid.PlayerLowGUID(playerGUID) +} + +func playerDBGUIDFromClientGUID(playerGUID uint64) uint64 { + if playerGUID == 0 { + return 0 + } + + if playerGUID>>48 != 0 { + return playerGUID + } + + guidRealmID := uint32((playerGUID >> 32) & 0xffff) + if guidRealmID != 0 && guidRealmID == root.RealmID { + return playerGUID & 0xffffffff + } + + return playerGUID +} + +func groupObjectGUID(groupID uint64) uint64 { + if groupID == 0 { + return 0 + } + + return uint64(0x1F50)<<48 | groupID +} + +func clampGroupPacketPower(power uint32) uint16 { + if power > uint32(^uint16(0)) { + return ^uint16(0) + } + + return uint16(power) +} + +func readGUIDThenBoolCompatible(r *packet.Reader) uint64 { + if r.Left() == 9 { + return r.Uint64() + } + + return r.ReadGUID() +} + +func readLastBoolCompatible(r *packet.Reader) bool { + if r.Left() == 0 { + return false + } + + return r.Uint8() != 0 +} + +func readRemainingGUIDCompatible(r *packet.Reader) uint64 { + if r.Left() == 8 { + return r.Uint64() + } + + return r.ReadGUID() +} diff --git a/apps/gateway/session/group-state-trace.go b/apps/gateway/session/group-state-trace.go new file mode 100644 index 0000000..b608442 --- /dev/null +++ b/apps/gateway/session/group-state-trace.go @@ -0,0 +1,74 @@ +package session + +import ( + "github.com/rs/zerolog" + + "github.com/walkline/ToCloud9/apps/gateway/service" +) + +func traceSessionPlayerStateSnapshot(event *zerolog.Event, snapshot service.PlayerStateSnapshot) *zerolog.Event { + event = event. + Uint64("memberGUID", snapshot.MemberGUID). + Str("sourceWorldserverID", snapshot.SourceWorldserverID). + Bool("hasOnline", snapshot.Online != nil). + Bool("hasLevel", snapshot.Level != nil). + Bool("hasClass", snapshot.Class != nil). + Bool("hasZone", snapshot.ZoneID != nil). + Bool("hasMap", snapshot.MapID != nil). + Bool("hasInstance", snapshot.InstanceID != nil). + Bool("hasHealth", snapshot.Health != nil). + Bool("hasMaxHealth", snapshot.MaxHealth != nil). + Bool("hasPowerType", snapshot.PowerType != nil). + Bool("hasPower", snapshot.Power != nil). + Bool("hasMaxPower", snapshot.MaxPower != nil). + Bool("hasDead", snapshot.Dead != nil). + Bool("hasGhost", snapshot.Ghost != nil). + Bool("aurasKnown", snapshot.AurasKnown). + Int("auraCount", len(snapshot.Auras)). + Uint64("timestampMs", snapshot.TimestampMs) + if auraSpells := service.FormatPlayerAuraTrace(snapshot.Auras); auraSpells != "" { + event = event.Str("auraSpells", auraSpells) + } + + if snapshot.Online != nil { + event = event.Bool("online", *snapshot.Online) + } + if snapshot.Level != nil { + event = event.Uint8("level", *snapshot.Level) + } + if snapshot.Class != nil { + event = event.Uint8("class", *snapshot.Class) + } + if snapshot.ZoneID != nil { + event = event.Uint32("zoneID", *snapshot.ZoneID) + } + if snapshot.MapID != nil { + event = event.Uint32("mapID", *snapshot.MapID) + } + if snapshot.InstanceID != nil { + event = event.Uint32("instanceID", *snapshot.InstanceID) + } + if snapshot.Health != nil { + event = event.Uint32("health", *snapshot.Health) + } + if snapshot.MaxHealth != nil { + event = event.Uint32("maxHealth", *snapshot.MaxHealth) + } + if snapshot.PowerType != nil { + event = event.Uint8("powerType", *snapshot.PowerType) + } + if snapshot.Power != nil { + event = event.Uint32("power", *snapshot.Power) + } + if snapshot.MaxPower != nil { + event = event.Uint32("maxPower", *snapshot.MaxPower) + } + if snapshot.Dead != nil { + event = event.Bool("dead", *snapshot.Dead) + } + if snapshot.Ghost != nil { + event = event.Bool("ghost", *snapshot.Ghost) + } + + return event +} diff --git a/apps/gateway/session/groups.go b/apps/gateway/session/groups.go index d6d8462..849dd8b 100644 --- a/apps/gateway/session/groups.go +++ b/apps/gateway/session/groups.go @@ -3,6 +3,7 @@ package session import ( "context" "fmt" + "strings" root "github.com/walkline/ToCloud9/apps/gateway" eBroadcaster "github.com/walkline/ToCloud9/apps/gateway/events-broadcaster" @@ -34,6 +35,12 @@ const ( GroupResultNotLeader ) +const ( + groupTypeFlagLFG uint8 = 0x08 + groupLfgDungeonStateNotDone uint8 = 0 + groupLfgDungeonStateFinished uint8 = 2 +) + type groupResult struct { Operation GroupOperation MemberName string @@ -50,8 +57,31 @@ func (r groupResult) BuildPacket() *packet.Packet { return w.ToPacket() } +func (s *GameSession) characterNameRealm(ctx context.Context, characterName string) (uint32, string) { + name, realmName, hasRealm := strings.Cut(characterName, "-") + if !hasRealm || name == "" || realmName == "" || s.realmNamesService == nil { + return root.RealmID, characterName + } + + realmID, err := s.realmNamesService.IDByName(ctx, realmName) + if err != nil { + return root.RealmID, characterName + } + + return realmID, name +} + +func scopedCharacterGUID(requestRealmID uint32, characterRealmID uint32, characterGUID uint64) uint64 { + if characterRealmID == 0 { + characterRealmID = requestRealmID + } + + return playerObjectGUIDForRealm(characterRealmID, characterGUID) +} + func (s *GameSession) HandleGroupInvite(ctx context.Context, p *packet.Packet) error { playerName := p.Reader().String() + targetRealmID, targetName := s.characterNameRealm(ctx, playerName) res := groupResult{ Operation: GroupOperationInvite, @@ -60,8 +90,8 @@ func (s *GameSession) HandleGroupInvite(ctx context.Context, p *packet.Packet) e resp, err := s.charServiceClient.CharacterOnlineByName(ctx, &pbChar.CharacterOnlineByNameRequest{ Api: root.Ver, - RealmID: root.RealmID, - CharacterName: playerName, + RealmID: targetRealmID, + CharacterName: targetName, }) if err != nil { return err @@ -77,7 +107,7 @@ func (s *GameSession) HandleGroupInvite(ctx context.Context, p *packet.Packet) e Api: root.SupportedGroupServiceVer, RealmID: root.RealmID, Inviter: s.character.GUID, - Invited: resp.Character.CharGUID, + Invited: scopedCharacterGUID(targetRealmID, resp.Character.RealmID, resp.Character.CharGUID), InviterName: s.character.Name, InvitedName: resp.Character.CharName, }) @@ -110,66 +140,29 @@ func (s *GameSession) HandleEventGroupInviteCreated(ctx context.Context, e *eBro return nil } -func (s *GameSession) HandleEventGroupMemberOnlineStatusChanged(ctx context.Context, e *eBroadcaster.Event) error { - eventData := e.Payload.(*events.GroupEventGroupMemberOnlineStatusChangedPayload) - - // TODO: we can handle this with less requests to the group service. - return s.SendGroupUpdate(ctx, eventData.GroupID) -} +func (s *GameSession) HandleEventGroupInviteDeclined(ctx context.Context, e *eBroadcaster.Event) error { + eventData := e.Payload.(*events.GroupEventInviteDeclinedPayload) -func (s *GameSession) HandleEventGroupCreated(ctx context.Context, e *eBroadcaster.Event) error { - eventData := e.Payload.(*events.GroupEventGroupCreatedPayload) + resp := packet.NewWriterWithSize(packet.SMsgGroupDecline, uint32(len(eventData.InviteeName)+1)) + resp.String(eventData.InviteeName) + s.gameSocket.Send(resp) - var member *events.GroupMember - for i, memberItr := range eventData.Members { - if memberItr.MemberGUID == s.character.GUID { - member = &eventData.Members[i] - break - } - } + return nil +} - if member == nil { - return fmt.Errorf("group member not found for player %d", s.character.GUID) +func (s *GameSession) HandleEventGroupMemberOnlineStatusChanged(ctx context.Context, e *eBroadcaster.Event) error { + eventData := e.Payload.(*events.GroupEventGroupMemberOnlineStatusChangedPayload) + if s.character == nil || samePlayerGUID(eventData.RealmID, eventData.MemberGUID, sessionRealmID(eventData.RealmID), s.character.GUID) { + return nil } - - resp := packet.NewWriterWithSize(packet.SMsgGroupList, 0) - resp.Uint8(eventData.GroupType) - resp.Uint8(member.SubGroup) - resp.Uint8(member.MemberFlags) - resp.Uint8(member.Roles) - - resp.Uint64(member.MemberGUID) - s.groupUpdateCounter++ - resp.Uint32(s.groupUpdateCounter) - resp.Uint32(uint32(len(eventData.Members) - 1)) - for _, memberItr := range eventData.Members { - if memberItr.MemberGUID == s.character.GUID { - continue - } - - var onlineFlag uint8 = 0 - if memberItr.IsOnline { - onlineFlag = 1 - } - - resp.String(memberItr.MemberName) - resp.Uint64(memberItr.MemberGUID) - resp.Uint8(onlineFlag) - resp.Uint8(memberItr.SubGroup) - resp.Uint8(memberItr.MemberFlags) - resp.Uint8(memberItr.Roles) + if s.clusterGroupPresentationBlocked() { + return nil } - resp.Uint64(eventData.LeaderGUID) - resp.Uint8(eventData.LootMethod) - resp.Uint64(eventData.MasterLooterGuid) - resp.Uint8(eventData.LootThreshold) - resp.Uint8(eventData.Difficulty) - resp.Uint8(eventData.RaidDifficulty) - resp.Uint8(0) // heroic: m_raidDifficulty >= RAID_DIFFICULTY_10MAN_HEROIC - - s.gameSocket.Send(resp) + return s.SendGroupUpdateInRealm(ctx, eventData.RealmID, eventData.GroupID) +} +func (s *GameSession) HandleEventGroupCreated(ctx context.Context, e *eBroadcaster.Event) error { return nil } @@ -187,16 +180,45 @@ func (s *GameSession) HandleGroupInviteAccept(ctx context.Context, _ *packet.Pac } func (s *GameSession) HandleGroupInviteDeclined(ctx context.Context, _ *packet.Packet) error { + _, err := s.groupServiceClient.DeclineInvite(ctx, &pb.DeclineInviteParams{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + Player: s.character.GUID, + }) + if err != nil { + return NewGroupServiceUnavailableErr(err) + } return nil } func (s *GameSession) HandleGroupUninvite(ctx context.Context, p *packet.Packet) error { + if forwarded, err := s.forwardGroupUninviteToWorldserverIfLfgDungeon(ctx, p); forwarded || err != nil { + return err + } + playerName := p.Reader().String() + + groupResp, err := s.groupServiceClient.GetGroupByMember(ctx, &pb.GetGroupByMemberRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + Player: s.character.GUID, + }) + if err != nil { + return NewGroupServiceUnavailableErr(err) + } + + if groupResp.Group != nil { + if member := s.groupMemberByName(ctx, groupResp.Group, playerName); member != nil { + return s.groupUninviteWithGUID(ctx, member.Guid, member.Name, "") + } + } + + targetRealmID, targetName := s.characterNameRealm(ctx, playerName) resp, err := s.charServiceClient.CharacterByName(ctx, &pbChar.CharacterByNameRequest{ Api: root.Ver, - RealmID: root.RealmID, - CharacterName: playerName, + RealmID: targetRealmID, + CharacterName: targetName, }) if err != nil { return err @@ -213,27 +235,57 @@ func (s *GameSession) HandleGroupUninvite(ctx context.Context, p *packet.Packet) return nil } - return s.groupUninviteWithGUID(ctx, resp.Character.CharGUID, resp.Character.CharName, "") + return s.groupUninviteWithGUID(ctx, scopedCharacterGUID(targetRealmID, resp.Character.RealmID, resp.Character.CharGUID), resp.Character.CharName, "") } func (s *GameSession) HandleGroupUninviteGUID(ctx context.Context, p *packet.Packet) error { + if forwarded, err := s.forwardGroupUninviteToWorldserverIfLfgDungeon(ctx, p); forwarded || err != nil { + return err + } + r := p.Reader() - guid := r.Uint64() + guid := playerDBGUIDFromClientGUID(r.Uint64()) reason := r.String() return s.groupUninviteWithGUID(ctx, guid, "", reason) } -func (s *GameSession) HandleGroupLeave(ctx context.Context, _ *packet.Packet) error { - _, err := s.groupServiceClient.Leave(ctx, &pb.GroupLeaveParams{ +func (s *GameSession) forwardGroupUninviteToWorldserverIfLfgDungeon(ctx context.Context, p *packet.Packet) (bool, error) { + return s.forwardPacketToWorldserverIfLfgDungeon(ctx, p) +} + +func (s *GameSession) HandleGroupLeave(ctx context.Context, p *packet.Packet) error { + forwarded, err := s.forwardPacketToWorldserverIfLfgDungeon(ctx, p) + if err != nil { + return err + } + + if s.groupServiceClient == nil { + if forwarded { + return nil + } + return NewGroupServiceUnavailableErr(fmt.Errorf("group service client is nil")) + } + + _, err = s.groupServiceClient.Leave(ctx, &pb.GroupLeaveParams{ Api: root.SupportedGroupServiceVer, RealmID: root.RealmID, Player: s.character.GUID, }) if err != nil { + if forwarded { + if s.logger != nil { + s.logger.Warn().Err(err).Msg("forwarded LFG dungeon group leave to worldserver but groupservice cleanup failed") + } + return nil + } return NewGroupServiceUnavailableErr(err) } + if forwarded { + return nil + } + res := groupResult{ Operation: GroupOperationLeave, Result: GroupResultOk, @@ -249,6 +301,9 @@ func (s *GameSession) HandleGroupConvertToRaid(ctx context.Context, _ *packet.Pa Player: s.character.GUID, }) if err != nil { + if isSilentGroupMutationError(err) { + return nil + } return NewGroupServiceUnavailableErr(err) } @@ -257,21 +312,27 @@ func (s *GameSession) HandleGroupConvertToRaid(ctx context.Context, _ *packet.Pa Result: GroupResultOk, } s.gameSocket.SendPacket(res.BuildPacket()) - return nil + return s.SendCurrentGroupUpdate(ctx) } func (s *GameSession) HandleGroupSetLeader(ctx context.Context, p *packet.Packet) error { + r := p.Reader() + newLeader := playerDBGUIDFromClientGUID(r.Uint64()) + if err := r.Error(); err != nil { + return err + } + _, err := s.groupServiceClient.ChangeLeader(ctx, &pb.ChangeLeaderParams{ Api: root.SupportedGroupServiceVer, RealmID: root.RealmID, Player: s.character.GUID, - NewLeader: p.Reader().Uint64(), + NewLeader: newLeader, }) if err != nil { return NewGroupServiceUnavailableErr(err) } - return nil + return s.SendCurrentGroupUpdate(ctx) } func (s *GameSession) HandleSetGroupTargetIcon(ctx context.Context, p *packet.Packet) error { @@ -283,12 +344,17 @@ func (s *GameSession) HandleSetGroupTargetIcon(ctx context.Context, p *packet.Pa return s.sendGroupListOfTargetIcons(ctx) } + targetGUID := playerDBGUIDFromClientGUID(reader.Uint64()) + if err := reader.Error(); err != nil { + return err + } + _, err := s.groupServiceClient.SetGroupTargetIcon(ctx, &pb.SetGroupTargetIconRequest{ Api: root.SupportedGroupServiceVer, RealmID: root.RealmID, SetterGUID: s.character.GUID, IconID: uint32(iconIDOrAction), - TargetGUID: reader.Uint64(), + TargetGUID: targetGUID, }) if err != nil { return NewGroupServiceUnavailableErr(err) @@ -301,8 +367,11 @@ func (s *GameSession) HandleSetLootMethod(ctx context.Context, p *packet.Packet) reader := p.Reader() method := reader.Uint32() - looter := reader.Uint64() + looter := playerDBGUIDFromClientGUID(reader.Uint64()) threshold := reader.Uint32() + if err := reader.Error(); err != nil { + return err + } _, err := s.groupServiceClient.SetLootMethod(ctx, &pb.SetLootMethodRequest{ Api: root.SupportedGroupServiceVer, @@ -313,6 +382,9 @@ func (s *GameSession) HandleSetLootMethod(ctx context.Context, p *packet.Packet) LootThreshold: threshold, }) if err != nil { + if isSilentGroupMutationError(err) { + return nil + } return NewGroupServiceUnavailableErr(err) } @@ -325,7 +397,11 @@ func (s *GameSession) HandleSetDungeonDifficulty(ctx context.Context, p *packet. return nil } - difficulty := p.Reader().Uint32() + reader := p.Reader() + difficulty := reader.Uint32() + if err := reader.Error(); err != nil { + return err + } groupResp, err := s.groupServiceClient.GetGroupByMember(ctx, &pb.GetGroupByMemberRequest{ Api: root.SupportedGroupServiceVer, @@ -345,14 +421,23 @@ func (s *GameSession) HandleSetDungeonDifficulty(ctx context.Context, p *packet. return nil } - _, err = s.groupServiceClient.SetDungeonDifficulty(ctx, &pb.SetDungeonDifficultyRequest{ + resp, err := s.groupServiceClient.SetDungeonDifficulty(ctx, &pb.SetDungeonDifficultyRequest{ Api: root.SupportedGroupServiceVer, RealmID: root.RealmID, PlayerGUID: s.character.GUID, Difficulty: difficulty, }) + if err != nil { + if isSilentGroupMutationError(err) { + return nil + } + return NewGroupServiceUnavailableErr(err) + } + if resp.GetStatus() != pb.SetDungeonDifficultyResponse_Ok { + s.sendDungeonDifficulty(groupResp.Group.Difficulty) + } - return err + return nil } func (s *GameSession) HandleSetRaidDifficulty(ctx context.Context, p *packet.Packet) error { @@ -361,7 +446,11 @@ func (s *GameSession) HandleSetRaidDifficulty(ctx context.Context, p *packet.Pac return nil } - difficulty := p.Reader().Uint32() + reader := p.Reader() + difficulty := reader.Uint32() + if err := reader.Error(); err != nil { + return err + } groupResp, err := s.groupServiceClient.GetGroupByMember(ctx, &pb.GetGroupByMemberRequest{ Api: root.SupportedGroupServiceVer, @@ -381,14 +470,23 @@ func (s *GameSession) HandleSetRaidDifficulty(ctx context.Context, p *packet.Pac return nil } - _, err = s.groupServiceClient.SetRaidDifficulty(ctx, &pb.SetRaidDifficultyRequest{ + resp, err := s.groupServiceClient.SetRaidDifficulty(ctx, &pb.SetRaidDifficultyRequest{ Api: root.SupportedGroupServiceVer, RealmID: root.RealmID, PlayerGUID: s.character.GUID, Difficulty: difficulty, }) + if err != nil { + if isSilentGroupMutationError(err) { + return nil + } + return NewGroupServiceUnavailableErr(err) + } + if resp.GetStatus() != pb.SetRaidDifficultyResponse_Ok { + s.sendRaidDifficulty(groupResp.Group.RaidDifficulty) + } - return err + return nil } func (s *GameSession) HandleEventGroupNewTargetIcon(ctx context.Context, e *eBroadcaster.Event) error { @@ -398,9 +496,9 @@ func (s *GameSession) HandleEventGroupNewTargetIcon(ctx context.Context, e *eBro resp := packet.NewWriterWithSize(packet.MsgRaidTargetUpdate, uint32(1+8+1+8)) resp.Uint8(singleItemPacket) - resp.Uint64(eventData.Updater) + resp.Uint64(playerObjectGUIDForRealm(eventData.RealmID, eventData.Updater)) resp.Uint8(eventData.IconID) - resp.Uint64(eventData.Target) + resp.Uint64(playerObjectGUIDForRealm(eventData.RealmID, eventData.Target)) s.gameSocket.Send(resp) @@ -411,24 +509,32 @@ func (s *GameSession) HandleEventGroupDifficultyChanged(ctx context.Context, e * eventData := e.Payload.(*events.GroupEventGroupDifficultyChangedPayload) if eventData.DungeonDifficulty != nil { - resp := packet.NewWriterWithSize(packet.MsgSetDungeonDifficulty, 12) - resp.Uint32(uint32(*eventData.DungeonDifficulty)) - resp.Uint32(1) - resp.Uint32(1) - s.gameSocket.Send(resp) + s.sendDungeonDifficulty(uint32(*eventData.DungeonDifficulty)) } if eventData.RaidDifficulty != nil { - resp := packet.NewWriterWithSize(packet.MsgSetRaidDifficulty, 12) - resp.Uint32(uint32(*eventData.RaidDifficulty)) - resp.Uint32(1) - resp.Uint32(1) - s.gameSocket.Send(resp) + s.sendRaidDifficulty(uint32(*eventData.RaidDifficulty)) } return nil } +func (s *GameSession) sendDungeonDifficulty(difficulty uint32) { + resp := packet.NewWriterWithSize(packet.MsgSetDungeonDifficulty, 12) + resp.Uint32(difficulty) + resp.Uint32(1) + resp.Uint32(1) + s.gameSocket.Send(resp) +} + +func (s *GameSession) sendRaidDifficulty(difficulty uint32) { + resp := packet.NewWriterWithSize(packet.MsgSetRaidDifficulty, 12) + resp.Uint32(difficulty) + resp.Uint32(1) + resp.Uint32(1) + s.gameSocket.Send(resp) +} + func (s *GameSession) sendGroupListOfTargetIcons(ctx context.Context) error { gr, err := s.groupServiceClient.GetGroupByMember(ctx, &pb.GetGroupByMemberRequest{ Api: root.SupportedGroupServiceVer, @@ -453,7 +559,8 @@ func (s *GameSession) sendGroupListOfTargetIcons(ctx context.Context) error { } resp.Uint8(uint8(i)) - resp.Uint64(targetGUID) + groupRealmID := groupHomeRealmIDFromPB(gr.Group) + resp.Uint64(playerObjectGUIDForRealm(groupRealmID, targetGUID)) } s.gameSocket.Send(resp) @@ -462,6 +569,8 @@ func (s *GameSession) sendGroupListOfTargetIcons(ctx context.Context) error { } func (s *GameSession) groupUninviteWithGUID(ctx context.Context, player uint64, playerName, reason string) error { + player = playerDBGUIDFromClientGUID(player) + _, err := s.groupServiceClient.Uninvite(ctx, &pb.UninviteParams{ Api: root.SupportedGroupServiceVer, RealmID: root.RealmID, @@ -497,13 +606,21 @@ func (s *GameSession) LoadGroupForPlayer(ctx context.Context) error { return nil } - return s.SendGroupUpdate(ctx, uint(res.GroupID)) + groupRealmID := res.GroupRealmID + if groupRealmID == 0 { + groupRealmID = root.RealmID + } + return s.SendGroupUpdateInRealm(ctx, groupRealmID, uint(res.GroupID)) } func (s *GameSession) SendGroupUpdate(ctx context.Context, groupID uint) error { + return s.SendGroupUpdateInRealm(ctx, root.RealmID, groupID) +} + +func (s *GameSession) SendGroupUpdateInRealm(ctx context.Context, realmID uint32, groupID uint) error { groupResp, err := s.groupServiceClient.GetGroup(ctx, &pb.GetGroupRequest{ Api: root.SupportedGroupServiceVer, - RealmID: root.RealmID, + RealmID: realmID, GroupID: uint32(groupID), }) if err != nil { @@ -514,10 +631,40 @@ func (s *GameSession) SendGroupUpdate(ctx context.Context, groupID uint) error { return nil } + return s.sendGroupUpdateFromGroup(groupResp.Group) +} + +func (s *GameSession) SendCurrentGroupUpdate(ctx context.Context) error { + if s.character == nil { + return nil + } + + groupResp, err := s.groupServiceClient.GetGroupByMember(ctx, &pb.GetGroupByMemberRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + Player: s.character.GUID, + }) + if err != nil { + return NewGroupServiceUnavailableErr(err) + } + + if groupResp.Group == nil { + return nil + } + + return s.sendGroupUpdateFromGroup(groupResp.Group) +} + +func (s *GameSession) sendGroupUpdateFromGroup(group *pb.GetGroupResponse_Group) error { + if s.clusterGroupPresentationBlocked() { + return nil + } + + groupRealmID := groupHomeRealmIDFromPB(group) var member *pb.GetGroupResponse_GroupMember - for i, memberItr := range groupResp.Group.Members { - if memberItr.Guid == s.character.GUID { - member = groupResp.Group.Members[i] + for i, memberItr := range group.Members { + if samePlayerGUID(groupRealmID, memberItr.Guid, sessionRealmID(groupRealmID), s.character.GUID) { + member = group.Members[i] break } } @@ -527,17 +674,20 @@ func (s *GameSession) SendGroupUpdate(ctx context.Context, groupID uint) error { } resp := packet.NewWriterWithSize(packet.SMsgGroupList, 0) - resp.Uint8(uint8(groupResp.Group.GroupType)) + resp.Uint8(uint8(group.GroupType)) resp.Uint8(uint8(member.SubGroup)) resp.Uint8(uint8(member.Flags)) resp.Uint8(uint8(member.Roles)) + if uint8(group.GroupType)&groupTypeFlagLFG != 0 { + s.writeGroupListLfgFields(resp) + } - resp.Uint64(uint64(groupResp.Group.Id)) + resp.Uint64(groupObjectGUID(uint64(group.Id))) s.groupUpdateCounter++ resp.Uint32(s.groupUpdateCounter) - resp.Uint32(uint32(len(groupResp.Group.Members) - 1)) - for _, memberItr := range groupResp.Group.Members { - if memberItr.Guid == s.character.GUID { + resp.Uint32(uint32(len(group.Members) - 1)) + for _, memberItr := range group.Members { + if samePlayerGUID(groupRealmID, memberItr.Guid, sessionRealmID(groupRealmID), s.character.GUID) { continue } @@ -547,19 +697,19 @@ func (s *GameSession) SendGroupUpdate(ctx context.Context, groupID uint) error { } resp.String(memberItr.Name) - resp.Uint64(memberItr.Guid) + resp.Uint64(playerObjectGUIDForMember(groupRealmID, memberItr)) resp.Uint8(onlineFlag) resp.Uint8(uint8(memberItr.SubGroup)) resp.Uint8(uint8(memberItr.Flags)) resp.Uint8(uint8(memberItr.Roles)) } - resp.Uint64(groupResp.Group.Leader) - resp.Uint8(uint8(groupResp.Group.LootMethod)) - resp.Uint64(groupResp.Group.MasterLooter) - resp.Uint8(uint8(groupResp.Group.LootThreshold)) - resp.Uint8(uint8(groupResp.Group.Difficulty)) - resp.Uint8(uint8(groupResp.Group.RaidDifficulty)) + resp.Uint64(playerObjectGUIDForRealm(groupRealmID, group.Leader)) + resp.Uint8(uint8(group.LootMethod)) + resp.Uint64(playerObjectGUIDForRealm(groupRealmID, group.MasterLooter)) + resp.Uint8(uint8(group.LootThreshold)) + resp.Uint8(uint8(group.Difficulty)) + resp.Uint8(uint8(group.RaidDifficulty)) resp.Uint8(0) // heroic: m_raidDifficulty >= RAID_DIFFICULTY_10MAN_HEROIC s.gameSocket.Send(resp) @@ -567,78 +717,146 @@ func (s *GameSession) SendGroupUpdate(ctx context.Context, groupID uint) error { return nil } +func (s *GameSession) writeGroupListLfgFields(resp *packet.Writer) { + state := groupLfgDungeonStateNotDone + dungeon := uint32(0) + if s.character != nil { + status := s.character.lastLfgStatus + if status.State == events.MatchmakingLfgStateFinishedDungeon { + state = groupLfgDungeonStateFinished + } + dungeon = lfgProposalDisplayDungeon(status) + } + + resp.Uint8(state) + resp.Uint32(dungeon) +} + func (s *GameSession) HandleEventGroupMemberLeft(ctx context.Context, e *eBroadcaster.Event) error { eventData := e.Payload.(*events.GroupEventGroupMemberLeftPayload) + if s.character == nil { + return nil + } + if samePlayerGUID(eventData.RealmID, eventData.MemberGUID, sessionRealmID(eventData.RealmID), s.character.GUID) { + s.gameSocket.Send(packet.NewWriterWithSize(packet.SMsgGroupUnInvite, 0)) + return nil + } - if eventData.MemberGUID == s.character.GUID { - resp := packet.NewWriterWithSize(packet.SMsgGroupUnInvite, 0) - s.gameSocket.Send(resp) + if s.lfgDungeonActive && !s.clusterGroupPresentationBlocked() && groupMemberLeftEventIncludesSession(eventData, s.character.GUID) { + return s.SendGroupUpdateInRealm(ctx, eventData.RealmID, eventData.GroupID) + } - s.groupUpdateCounter++ + // AzerothCore applies membership updates from libsidecar and emits the client roster. + return nil +} - resp = packet.NewWriterWithSize(packet.SMsgGroupList, 0) - resp.Uint8(0x10).Uint8(0).Uint8(0).Uint8(0) - resp.Uint64(uint64(eventData.GroupID)).Uint32(s.groupUpdateCounter).Uint32(0).Uint64(0) - s.gameSocket.Send(resp) - return nil +func groupMemberLeftEventIncludesSession(eventData *events.GroupEventGroupMemberLeftPayload, playerGUID uint64) bool { + if eventData == nil || playerGUID == 0 { + return false } - return s.SendGroupUpdate(ctx, eventData.GroupID) + for _, memberGUID := range eventData.OnlineMembers { + if samePlayerGUID(eventData.RealmID, memberGUID, sessionRealmID(eventData.RealmID), playerGUID) { + return true + } + } + + return false } func (s *GameSession) HandleEventGroupDisband(ctx context.Context, e *eBroadcaster.Event) error { - eventData := e.Payload.(*events.GroupEventGroupDisbandPayload) - - s.groupUpdateCounter++ - - resp := packet.NewWriterWithSize(packet.SMsgGroupList, 0) - resp.Uint8(0x10).Uint8(0).Uint8(0).Uint8(0) - resp.Uint64(uint64(eventData.GroupID)).Uint32(s.groupUpdateCounter).Uint32(0).Uint64(0) - s.gameSocket.Send(resp) + if s.clusterGroupPresentationBlocked() { + return nil + } - resp = packet.NewWriterWithSize(packet.SMsgGroupDestroyed, 0) - s.gameSocket.Send(resp) + s.gameSocket.Send(packet.NewWriterWithSize(packet.SMsgGroupDestroyed, 0)) + // AzerothCore applies disband updates from libsidecar and emits the client roster. return nil } +func (s *GameSession) clusterGroupPresentationBlocked() bool { + return s != nil && + s.character != nil && + (s.pendingMapTransferRouting != nil || + s.activeMapTransferRouting != nil || + s.teleportingToNewMap != nil || + s.pendingRedirectID != "") +} + func (s *GameSession) HandleEventGroupMemberAdded(ctx context.Context, e *eBroadcaster.Event) error { - eventData := e.Payload.(*events.GroupEventGroupMemberAddedPayload) - return s.SendGroupUpdate(ctx, eventData.GroupID) + if s.character == nil { + return nil + } + + // AzerothCore applies this event through libsidecar and emits the roster. + return nil } func (s *GameSession) HandleEventGroupLeaderChanged(ctx context.Context, e *eBroadcaster.Event) error { - eventData := e.Payload.(*events.GroupEventGroupLeaderChangedPayload) - return s.SendGroupUpdate(ctx, eventData.GroupID) + if s.character == nil { + return nil + } + + // AzerothCore applies this event through libsidecar and emits both leader and roster packets. + return nil } func (s *GameSession) HandleEventGroupLootTypeChanged(ctx context.Context, e *eBroadcaster.Event) error { - eventData := e.Payload.(*events.GroupEventGroupLootTypeChangedPayload) - return s.SendGroupUpdate(ctx, eventData.GroupID) + if s.character == nil { + return nil + } + + // AzerothCore applies this event through libsidecar and emits the roster. + return nil } func (s *GameSession) HandleEventGroupConvertedToRaid(ctx context.Context, e *eBroadcaster.Event) error { - eventData := e.Payload.(*events.GroupEventGroupConvertedToRaidPayload) - return s.SendGroupUpdate(ctx, eventData.GroupID) + if s.character == nil { + return nil + } + + // AzerothCore applies this event through libsidecar and emits the roster. + return nil +} + +func (s *GameSession) sendGroupLeaderChanged(ctx context.Context, groupID uint, newLeaderGUID uint64) error { + groupResp, err := s.groupServiceClient.GetGroup(ctx, &pb.GetGroupRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + GroupID: uint32(groupID), + }) + if err != nil { + return NewGroupServiceUnavailableErr(err) + } + + if groupResp.Group == nil { + return nil + } + groupRealmID := groupHomeRealmIDFromPB(groupResp.Group) + + for _, member := range groupResp.Group.Members { + if !samePlayerGUID(groupRealmID, member.Guid, groupRealmID, newLeaderGUID) { + continue + } + + resp := packet.NewWriterWithSize(packet.SMsgGroupSetLeader, uint32(len(member.Name)+1)) + resp.String(member.Name) + s.gameSocket.Send(resp) + return nil + } + + return nil } func (s *GameSession) HandleEventGroupNewMessage(ctx context.Context, e *eBroadcaster.Event) error { eventData := e.Payload.(*events.GroupEventNewMessagePayload) - if eventData.SenderGUID == s.character.GUID { + if samePlayerGUID(eventData.RealmID, eventData.SenderGUID, sessionRealmID(eventData.RealmID), s.character.GUID) { return nil } - resp := packet.NewWriterWithSize(packet.SMsgMessageChat, 0) - resp.Uint8(eventData.MessageType) - resp.Uint32(eventData.Language) - resp.Uint64(eventData.SenderGUID) - resp.Uint32(0) // some flags - resp.Uint64(eventData.SenderGUID) - resp.Uint32(uint32(len(eventData.Msg) + 1)) - resp.String(eventData.Msg) - resp.Uint8(0) // chat tag - s.gameSocket.Send(resp) + s.sendAzerothCorePlayerChat(ChatType(eventData.MessageType), eventData.Language, eventData.RealmID, eventData.SenderGUID, eventData.SenderName, 0, "", eventData.Msg, eventData.SenderChatTag) return nil } diff --git a/apps/gateway/session/guild.go b/apps/gateway/session/guild.go index 9f7cbf2..b0631ee 100644 --- a/apps/gateway/session/guild.go +++ b/apps/gateway/session/guild.go @@ -12,6 +12,7 @@ import ( pbChar "github.com/walkline/ToCloud9/gen/characters/pb" pbGuild "github.com/walkline/ToCloud9/gen/guilds/pb" "github.com/walkline/ToCloud9/shared/events" + "github.com/walkline/ToCloud9/shared/wow" ) type GuildEventType uint8 @@ -39,6 +40,30 @@ const ( GuildEventTypeBankTextChanged ) +const ( + guildCommandCreate int32 = 0 + guildCommandInvite int32 = 1 + guildCommandResultSuccess int32 = 0 + guildCommandResultAlreadyInGuildS int32 = 3 + guildCommandResultAlreadyInvitedToGuild int32 = 5 + guildCommandResultPlayerNotFound int32 = 11 + guildCommandResultNotAllied int32 = 12 + petitionTurnOK uint32 = 0 + petitionTurnNeedMoreSignatures uint32 = 4 + petitionSignOK uint32 = 0 + petitionSignAlreadySigned uint32 = 1 + petitionSignAlreadyInGuild uint32 = 2 + petitionSignCantSignOwn uint32 = 3 + petitionSignNotServer uint32 = 4 + guildPetitionType uint32 = 9 + guildBankMaxTabs = 6 + guildBankWithdrawMoneyIdx = guildBankMaxTabs + guildWithdrawUnlimited uint32 = 0xFFFFFFFF + guildBankRightViewTab uint32 = 0x01 + guildRightWithdrawRepair uint32 = 0x00040000 + guildRightWithdrawGold uint32 = 0x00080000 +) + func (s *GameSession) HandleGuildRoster(ctx context.Context, p *packet.Packet) error { if s.character.GuildID == 0 { // TODO: send proper message to the client @@ -47,7 +72,7 @@ func (s *GameSession) HandleGuildRoster(ctx context.Context, p *packet.Packet) e guildResp, err := s.guildServiceClient.GetRosterInfo(ctx, &pbGuild.GetRosterInfoParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), GuildID: uint64(s.character.GuildID), }) if err != nil { @@ -63,12 +88,7 @@ func (s *GameSession) HandleGuildRoster(ctx context.Context, p *packet.Packet) e for _, rank := range guildResp.Guild.Ranks { resp.Uint32(rank.Flags) resp.Uint32(rank.GoldLimit) - - // TODO: guild bank - for i := 0; i < 6; i++ { - resp.Uint32(0) // tab flags - resp.Uint32(0) // tab withdraw limit - } + writeGuildBankTabRights(resp, rank) } for _, member := range guildResp.Guild.Members { @@ -106,7 +126,7 @@ func (s *GameSession) GuildLoginCommand(ctx context.Context) error { guildResp, err := s.guildServiceClient.GetRosterInfo(ctx, &pbGuild.GetRosterInfoParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), GuildID: uint64(s.character.GuildID), }) if err != nil { @@ -131,31 +151,42 @@ func (s *GameSession) GuildLoginCommand(ctx context.Context) error { } func (s *GameSession) HandleGuildInvite(ctx context.Context, p *packet.Packet) error { + targetName := p.Reader().String() resp, err := s.charServiceClient.CharacterOnlineByName(ctx, &pbChar.CharacterOnlineByNameRequest{ Api: root.Ver, - RealmID: root.RealmID, - CharacterName: p.Reader().String(), + RealmID: s.guildHomeRealmID(), + CharacterName: targetName, }) if err != nil { return err } if resp.Character == nil { - s.SendSysMessage("Player not found") + s.sendGuildCommandResult(guildCommandInvite, targetName, guildCommandResultPlayerNotFound) return nil } - // TODO: check fraction. + if resp.Character.GetRealmID() != s.guildHomeRealmID() { + s.sendGuildCommandResult(guildCommandInvite, targetName, guildCommandResultPlayerNotFound) + return nil + } + + if !s.guildCanInviteRace(uint8(resp.Character.GetCharRace())) { + s.sendGuildCommandResult(guildCommandInvite, resp.Character.GetCharName(), guildCommandResultNotAllied) + return nil + } _, err = s.guildServiceClient.InviteMember(ctx, &pbGuild.InviteMemberParams{ - Api: root.Ver, - RealmID: root.RealmID, - Inviter: s.character.GUID, - Invitee: resp.Character.CharGUID, - InviteeName: resp.Character.CharName, + Api: root.Ver, + RealmID: s.guildHomeRealmID(), + Inviter: s.character.GUID, + Invitee: resp.Character.CharGUID, + InviteeName: resp.Character.CharName, + InviteeRace: resp.Character.CharRace, + AllowCrossFaction: root.AllowTwoSideInteractionGuild, }) - return nil + return err } func (s *GameSession) HandleEventGuildInviteCreated(ctx context.Context, e *eBroadcaster.Event) error { @@ -215,6 +246,10 @@ func (s *GameSession) HandleEventGuildMemberAdded(_ context.Context, e *eBroadca eventData.MemberName, )) + if s.character != nil && s.character.GUID == eventData.MemberGUID { + s.character.GuildID = uint32(eventData.GuildID) + } + return nil } @@ -226,6 +261,9 @@ func (s *GameSession) HandleEventGuildMemberLeft(_ context.Context, e *eBroadcas eventData.MemberGUID, eventData.MemberName, )) + if s.character.GUID == eventData.MemberGUID { + s.character.GuildID = 0 + } return nil } @@ -286,16 +324,12 @@ func (s *GameSession) HandleEventGuildRankDeleted(_ context.Context, e *eBroadca func (s *GameSession) HandleEventGuildNewMessage(_ context.Context, e *eBroadcaster.Event) error { eventData := e.Payload.(*events.GuildEventNewMessagePayload) - resp := packet.NewWriterWithSize(packet.SMsgMessageChat, 0) - resp.Uint8(uint8(ChatTypeGuild)) - resp.Uint32(eventData.Language) - resp.Uint64(eventData.SenderGUID) - resp.Uint32(0) // some flags - resp.Uint64(eventData.SenderGUID) - resp.Uint32(uint32(len(eventData.Msg) + 1)) - resp.String(eventData.Msg) - resp.Uint8(0) // chat tag - s.gameSocket.Send(resp) + msgType := ChatTypeGuild + if eventData.ForOfficers { + msgType = ChatTypeOfficer + } + + s.sendAzerothCorePlayerChat(msgType, eventData.Language, eventData.RealmID, eventData.SenderGUID, eventData.SenderName, 0, "", eventData.Msg, eventData.SenderChatTag) return nil } @@ -303,7 +337,7 @@ func (s *GameSession) HandleEventGuildNewMessage(_ context.Context, e *eBroadcas func (s *GameSession) HandleGuildInviteAccept(ctx context.Context, _ *packet.Packet) error { inviteResp, err := s.guildServiceClient.InviteAccepted(ctx, &pbGuild.InviteAcceptedParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), Character: &pbGuild.InviteAcceptedParams_Character{ Guid: s.character.GUID, Name: s.character.Name, @@ -314,6 +348,7 @@ func (s *GameSession) HandleGuildInviteAccept(ctx context.Context, _ *packet.Pac AreaID: s.character.Zone, AccountID: uint64(s.character.AccountID), }, + AllowCrossFaction: root.AllowTwoSideInteractionGuild, }) if err != nil { return fmt.Errorf("can't accept invite err: %w", err) @@ -327,7 +362,7 @@ func (s *GameSession) HandleGuildInviteAccept(ctx context.Context, _ *packet.Pac func (s *GameSession) HandleGuildLeave(ctx context.Context, p *packet.Packet) error { _, err := s.guildServiceClient.Leave(ctx, &pbGuild.LeaveParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), Leaver: s.character.GUID, }) if err != nil { @@ -340,25 +375,21 @@ func (s *GameSession) HandleGuildLeave(ctx context.Context, p *packet.Packet) er } func (s *GameSession) HandleGuildKick(ctx context.Context, p *packet.Packet) error { - resp, err := s.charServiceClient.CharacterOnlineByName(ctx, &pbChar.CharacterOnlineByNameRequest{ - Api: root.Ver, - RealmID: root.RealmID, - CharacterName: p.Reader().String(), - }) + target, err := s.guildCharacterByName(ctx, p.Reader().String()) if err != nil { return err } - if resp.Character == nil { + if target == nil { s.SendSysMessage("Player not found") return nil } _, err = s.guildServiceClient.Kick(ctx, &pbGuild.KickParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), Kicker: s.character.GUID, - Target: resp.Character.CharGUID, + Target: target.CharGUID, }) if err != nil { return fmt.Errorf("can't kick player from the guild, err: %w", err) @@ -370,7 +401,7 @@ func (s *GameSession) HandleGuildKick(ctx context.Context, p *packet.Packet) err func (s *GameSession) HandleGuildSetMessageOfTheDay(ctx context.Context, p *packet.Packet) error { _, err := s.guildServiceClient.SetMessageOfTheDay(ctx, &pbGuild.SetMessageOfTheDayParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), ChangerGUID: s.character.GUID, MessageOfTheDay: p.Reader().String(), }) @@ -386,25 +417,21 @@ func (s *GameSession) HandleGuildSetPublicNote(ctx context.Context, p *packet.Pa targetName := reader.String() note := reader.String() - resp, err := s.charServiceClient.CharacterOnlineByName(ctx, &pbChar.CharacterOnlineByNameRequest{ - Api: root.Ver, - RealmID: root.RealmID, - CharacterName: targetName, - }) + target, err := s.guildCharacterByName(ctx, targetName) if err != nil { return err } - if resp.Character == nil { + if target == nil { s.SendSysMessage("Player not found") return nil } _, err = s.guildServiceClient.SetMemberPublicNote(ctx, &pbGuild.SetNoteParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), ChangerGUID: s.character.GUID, - TargetGUID: resp.Character.CharGUID, + TargetGUID: target.CharGUID, Note: note, }) if err != nil { @@ -419,25 +446,21 @@ func (s *GameSession) HandleGuildSetOfficerNote(ctx context.Context, p *packet.P targetName := reader.String() note := reader.String() - resp, err := s.charServiceClient.CharacterOnlineByName(ctx, &pbChar.CharacterOnlineByNameRequest{ - Api: root.Ver, - RealmID: root.RealmID, - CharacterName: targetName, - }) + target, err := s.guildCharacterByName(ctx, targetName) if err != nil { return err } - if resp.Character == nil { + if target == nil { s.SendSysMessage("Player not found") return nil } _, err = s.guildServiceClient.SetMemberOfficerNote(ctx, &pbGuild.SetNoteParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), ChangerGUID: s.character.GUID, - TargetGUID: resp.Character.CharGUID, + TargetGUID: target.CharGUID, Note: note, }) if err != nil { @@ -450,7 +473,7 @@ func (s *GameSession) HandleGuildSetOfficerNote(ctx context.Context, p *packet.P func (s *GameSession) HandleGuildSetInfoText(ctx context.Context, p *packet.Packet) error { _, err := s.guildServiceClient.SetGuildInfo(ctx, &pbGuild.SetGuildInfoParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), ChangerGUID: s.character.GUID, Info: p.Reader().String(), }) @@ -467,15 +490,27 @@ func (s *GameSession) HandleGuildRankUpdate(ctx context.Context, p *packet.Packe rights := reader.Uint32() name := reader.String() withdrawGoldLimit := reader.Uint32() + bankTabRights := make([]*pbGuild.RankUpdateParams_BankTabRight, 0, guildBankMaxTabs) + + for tabID := uint32(0); tabID < guildBankMaxTabs; tabID++ { + tabFlags := reader.Uint32() + withdrawItemLimit := reader.Uint32() + bankTabRights = append(bankTabRights, &pbGuild.RankUpdateParams_BankTabRight{ + TabID: tabID, + Flags: tabFlags, + WithdrawItemLimit: withdrawItemLimit, + }) + } _, err := s.guildServiceClient.UpdateRank(ctx, &pbGuild.RankUpdateParams{ - Api: root.Ver, - RealmID: root.RealmID, - ChangerGUID: s.character.GUID, - Rank: rankID, - RankName: name, - Rights: rights, - MoneyPerDay: withdrawGoldLimit, + Api: root.Ver, + RealmID: s.guildHomeRealmID(), + ChangerGUID: s.character.GUID, + Rank: rankID, + RankName: name, + Rights: rights, + MoneyPerDay: withdrawGoldLimit, + BankTabRights: bankTabRights, }) if err != nil { return err @@ -490,7 +525,7 @@ func (s *GameSession) HandleGuildRankAdd(ctx context.Context, p *packet.Packet) _, err := s.guildServiceClient.AddRank(ctx, &pbGuild.AddRankParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), ChangerGUID: s.character.GUID, RankName: name, }) @@ -504,7 +539,7 @@ func (s *GameSession) HandleGuildRankAdd(ctx context.Context, p *packet.Packet) func (s *GameSession) HandleGuildRankDelete(ctx context.Context, p *packet.Packet) error { _, err := s.guildServiceClient.DeleteLastRank(ctx, &pbGuild.DeleteLastRankParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), ChangerGUID: s.character.GUID, }) if err != nil { @@ -515,25 +550,21 @@ func (s *GameSession) HandleGuildRankDelete(ctx context.Context, p *packet.Packe } func (s *GameSession) HandleGuildPromote(ctx context.Context, p *packet.Packet) error { - resp, err := s.charServiceClient.CharacterOnlineByName(ctx, &pbChar.CharacterOnlineByNameRequest{ - Api: root.Ver, - RealmID: root.RealmID, - CharacterName: p.Reader().String(), - }) + target, err := s.guildCharacterByName(ctx, p.Reader().String()) if err != nil { return err } - if resp.Character == nil { + if target == nil { s.SendSysMessage("Player not found") return nil } _, err = s.guildServiceClient.PromoteMember(ctx, &pbGuild.PromoteDemoteParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), ChangerGUID: s.character.GUID, - TargetGUID: resp.Character.CharGUID, + TargetGUID: target.CharGUID, }) if err != nil { return err @@ -543,25 +574,21 @@ func (s *GameSession) HandleGuildPromote(ctx context.Context, p *packet.Packet) } func (s *GameSession) HandleGuildDemote(ctx context.Context, p *packet.Packet) error { - resp, err := s.charServiceClient.CharacterOnlineByName(ctx, &pbChar.CharacterOnlineByNameRequest{ - Api: root.Ver, - RealmID: root.RealmID, - CharacterName: p.Reader().String(), - }) + target, err := s.guildCharacterByName(ctx, p.Reader().String()) if err != nil { return err } - if resp.Character == nil { + if target == nil { s.SendSysMessage("Player not found") return nil } _, err = s.guildServiceClient.DemoteMember(ctx, &pbGuild.PromoteDemoteParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), ChangerGUID: s.character.GUID, - TargetGUID: resp.Character.CharGUID, + TargetGUID: target.CharGUID, }) if err != nil { return err @@ -574,7 +601,7 @@ func (s *GameSession) HandleGuildQuery(ctx context.Context, p *packet.Packet) er guildID := p.Reader().Uint32() guildResp, err := s.guildServiceClient.GetGuildInfo(ctx, &pbGuild.GetInfoParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), GuildID: uint64(guildID), }) if err != nil { @@ -599,6 +626,78 @@ func (s *GameSession) HandleGuildQuery(ctx context.Context, p *packet.Packet) er return nil } +func (s *GameSession) InterceptGuildCommandResult(_ context.Context, p *packet.Packet) error { + if s.gameSocket != nil { + s.gameSocket.WriteChannel() <- p + } + + reader := p.Reader() + command := reader.Int32() + name := reader.String() + result := reader.Int32() + if reader.Error() != nil { + s.pendingGuildCreate = nil + return nil + } + + if command == guildCommandCreate && result == guildCommandResultSuccess { + s.pendingGuildCreate = &pendingGuildCreateState{name: name} + return nil + } + + s.pendingGuildCreate = nil + return nil +} + +func (s *GameSession) InterceptTurnInPetitionResults(_ context.Context, p *packet.Packet) error { + if s.gameSocket != nil { + s.gameSocket.WriteChannel() <- p + } + + reader := p.Reader() + result := reader.Uint32() + if reader.Error() != nil { + s.pendingGuildCreate = nil + return nil + } + + if result == petitionTurnOK && s.pendingGuildCreate != nil && s.character != nil && s.eventsProducer != nil { + err := s.eventsProducer.GuildCreated(&events.GWEventGuildCreatedPayload{ + RealmID: s.guildHomeRealmID(), + LeaderGUID: s.character.GUID, + GuildName: s.pendingGuildCreate.name, + }) + if err != nil { + s.logger.Err(err).Msg("can't send guild created event") + } + } + + s.pendingGuildCreate = nil + return nil +} + +func (s *GameSession) sendTurnInPetitionResult(result uint32) { + resp := packet.NewWriterWithSize(packet.SMsgTurnInPetitionResults, 4) + resp.Uint32(result) + s.gameSocket.Send(resp) +} + +func (s *GameSession) guildCharacterByName(ctx context.Context, name string) (*pbChar.CharacterByNameResponse_Char, error) { + resp, err := s.charServiceClient.CharacterByName(ctx, &pbChar.CharacterByNameRequest{ + Api: root.Ver, + RealmID: s.guildHomeRealmID(), + CharacterName: name, + }) + if err != nil { + return nil, err + } + if resp == nil { + return nil, nil + } + + return resp.GetCharacter(), nil +} + func (s *GameSession) HandleGuildPermissions(ctx context.Context, p *packet.Packet) error { if s.character.GuildID == 0 { // TODO: send proper message to the client @@ -607,7 +706,7 @@ func (s *GameSession) HandleGuildPermissions(ctx context.Context, p *packet.Pack guildResp, err := s.guildServiceClient.GetRosterInfo(ctx, &pbGuild.GetRosterInfoParams{ Api: root.Ver, - RealmID: root.RealmID, + RealmID: s.guildHomeRealmID(), GuildID: uint64(s.character.GuildID), }) if err != nil { @@ -615,25 +714,13 @@ func (s *GameSession) HandleGuildPermissions(ctx context.Context, p *packet.Pack } resp := packet.NewWriterWithSize(packet.MsgGuildPermissions, 0) - for _, member := range guildResp.Guild.Members { - if member.Guid == s.character.GUID { - for _, rank := range guildResp.Guild.Ranks { - if rank.Id == member.RankID { - resp.Uint32(rank.Id) - resp.Int32(int32(rank.Flags)) - resp.Int32(int32(rank.GoldLimit)) - resp.Uint8(6) // Tabs count. - - for i := 0; i < 6; i++ { - resp.Uint32(0) // tab flags - resp.Uint32(0) // tab withdraw limit - } - - break - } - } - break - } + member, rank := currentGuildMemberRank(guildResp, s.character.GUID) + if member != nil && rank != nil { + resp.Uint32(rank.Id) + resp.Int32(int32(rank.Flags)) + resp.Int32(remainingGuildBankMoney(rank, member)) + resp.Uint8(uint8(clampGuildBankTabs(rank, guildResp.Guild.PurchasedBankTabs))) + writeGuildBankTabRemainingRights(resp, rank, member) } s.gameSocket.Send(resp) @@ -641,12 +728,190 @@ func (s *GameSession) HandleGuildPermissions(ctx context.Context, p *packet.Pack } func (s *GameSession) HandleGuildBankMoneyWithdrawn(ctx context.Context, p *packet.Packet) error { + if s.character.GuildID == 0 { + return nil + } + + guildResp, err := s.guildServiceClient.GetRosterInfo(ctx, &pbGuild.GetRosterInfoParams{ + Api: root.Ver, + RealmID: s.guildHomeRealmID(), + GuildID: uint64(s.character.GuildID), + }) + if err != nil { + return err + } + + member, rank := currentGuildMemberRank(guildResp, s.character.GUID) + if member == nil || rank == nil { + return nil + } + resp := packet.NewWriterWithSize(packet.MsgGuildBankMoneyWithdrawn, 4) - resp.Uint32(0) + resp.Int32(remainingGuildBankMoney(rank, member)) s.gameSocket.Send(resp) return nil } +func (s *GameSession) guildHomeRealmID() uint32 { + // Guilds are home-realm scoped even when the player is routed through a + // shared crossrealm map owner. + return root.RealmID +} + +func (s *GameSession) guildCanInviteRace(targetRace uint8) bool { + if root.AllowTwoSideInteractionGuild { + return true + } + if s == nil || s.character == nil { + return false + } + + return guildSameFactionByRace(s.character.Race, targetRace) +} + +func guildSameFactionByRace(leftRace, rightRace uint8) bool { + leftTeam, ok := guildRaceTeam(leftRace) + if !ok { + return false + } + rightTeam, ok := guildRaceTeam(rightRace) + if !ok { + return false + } + return leftTeam == rightTeam +} + +func guildRaceTeam(race uint8) (wow.Team, bool) { + if int(race) >= len(wow.DefaultRaces) { + return 0, false + } + raceInfo := wow.DefaultRaces[race] + if raceInfo.ID == 0 { + return 0, false + } + return raceInfo.Team, true +} + +func writeGuildBankTabRights(resp *packet.Writer, rank *pbGuild.GetRosterInfoResponse_Rank) { + var rights [guildBankMaxTabs]*pbGuild.GetRosterInfoResponse_Rank_BankTabRight + for _, right := range rank.BankTabRights { + if right.TabID < guildBankMaxTabs { + rights[right.TabID] = right + } + } + + for i := 0; i < guildBankMaxTabs; i++ { + if rights[i] == nil { + resp.Uint32(0) + resp.Uint32(0) + continue + } + + resp.Uint32(rights[i].Flags) + resp.Uint32(rights[i].WithdrawItemLimit) + } +} + +func writeGuildBankTabRemainingRights(resp *packet.Writer, rank *pbGuild.GetRosterInfoResponse_Rank, member *pbGuild.GetRosterInfoResponse_Member) { + var rights [guildBankMaxTabs]*pbGuild.GetRosterInfoResponse_Rank_BankTabRight + for _, right := range rank.BankTabRights { + if right.TabID < guildBankMaxTabs { + rights[right.TabID] = right + } + } + + for i := 0; i < guildBankMaxTabs; i++ { + if rights[i] == nil { + resp.Int32(0) + resp.Int32(0) + continue + } + + resp.Int32(int32(rights[i].Flags)) + resp.Int32(remainingGuildBankTabSlots(rights[i], member, i)) + } +} + +func currentGuildMemberRank(guildResp *pbGuild.GetRosterInfoResponse, memberGUID uint64) (*pbGuild.GetRosterInfoResponse_Member, *pbGuild.GetRosterInfoResponse_Rank) { + if guildResp == nil || guildResp.Guild == nil { + return nil, nil + } + + var member *pbGuild.GetRosterInfoResponse_Member + for _, candidate := range guildResp.Guild.Members { + if candidate.Guid == memberGUID { + member = candidate + break + } + } + if member == nil { + return nil, nil + } + + for _, rank := range guildResp.Guild.Ranks { + if rank.Id == member.RankID { + return member, rank + } + } + + return member, nil +} + +func remainingGuildBankMoney(rank *pbGuild.GetRosterInfoResponse_Rank, member *pbGuild.GetRosterInfoResponse_Member) int32 { + if rank.GoldLimit == guildWithdrawUnlimited { + return -1 + } + if rank.Flags&(guildRightWithdrawRepair|guildRightWithdrawGold) == 0 { + return 0 + } + + return remainingGuildBankLimit(rank.GoldLimit, bankWithdrawAt(member, guildBankWithdrawMoneyIdx)) +} + +func remainingGuildBankTabSlots(right *pbGuild.GetRosterInfoResponse_Rank_BankTabRight, member *pbGuild.GetRosterInfoResponse_Member, tabID int) int32 { + if right.WithdrawItemLimit == guildWithdrawUnlimited { + return -1 + } + if right.Flags&guildBankRightViewTab == 0 { + return 0 + } + + return remainingGuildBankLimit(right.WithdrawItemLimit, bankWithdrawAt(member, tabID)) +} + +func remainingGuildBankLimit(limit uint32, used uint32) int32 { + if used >= limit { + return 0 + } + + return int32(limit - used) +} + +func bankWithdrawAt(member *pbGuild.GetRosterInfoResponse_Member, index int) uint32 { + if member == nil || index < 0 || index >= len(member.BankWithdraw) { + return 0 + } + + return member.BankWithdraw[index] +} + +func clampGuildBankTabs(rank *pbGuild.GetRosterInfoResponse_Rank, purchasedTabs uint32) uint32 { + if purchasedTabs > guildBankMaxTabs { + return guildBankMaxTabs + } + if purchasedTabs != 0 { + return purchasedTabs + } + + for _, right := range rank.BankTabRights { + if right.TabID < guildBankMaxTabs && right.Flags != 0 { + return right.TabID + 1 + } + } + + return 0 +} + func buildGuildEventPacket(t GuildEventType, guid uint64, args ...string) *packet.Writer { resp := packet.NewWriterWithSize(packet.SMsgGuildEvent, 0) resp.Uint8(uint8(t)) diff --git a/apps/gateway/session/guild_bank.go b/apps/gateway/session/guild_bank.go new file mode 100644 index 0000000..fe15b66 --- /dev/null +++ b/apps/gateway/session/guild_bank.go @@ -0,0 +1,659 @@ +package session + +import ( + "context" + "fmt" + + root "github.com/walkline/ToCloud9/apps/gateway" + "github.com/walkline/ToCloud9/apps/gateway/packet" + pbGuild "github.com/walkline/ToCloud9/gen/guilds/pb" + pbWorld "github.com/walkline/ToCloud9/gen/worldserver/pb" + "github.com/walkline/ToCloud9/shared/wow/guid" +) + +const ( + guildBankMaxSlots = 98 + + guildCommandViewTab int32 = 21 + guildCommandMoveItem int32 = 22 + + guildCommandResultInternal int32 = 1 + guildCommandResultPermissions int32 = 8 + guildCommandResultPlayerNotInGuild int32 = 9 + guildCommandResultWithdrawLimit int32 = 25 + guildCommandResultNotEnoughMoney int32 = 26 + guildCommandResultBankFull int32 = 28 + guildCommandResultItemNotFound int32 = 29 + + guildBankTabCost0 uint32 = 1000000 + guildBankTabCost1 uint32 = 2500000 + guildBankTabCost2 uint32 = 5000000 + guildBankTabCost3 uint32 = 10000000 + guildBankTabCost4 uint32 = 25000000 + guildBankTabCost5 uint32 = 50000000 +) + +var guildBankTabCosts = [guildBankMaxTabs]uint32{ + guildBankTabCost0, + guildBankTabCost1, + guildBankTabCost2, + guildBankTabCost3, + guildBankTabCost4, + guildBankTabCost5, +} + +func (s *GameSession) HandleGuildBankerActivate(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + banker := reader.Uint64() + fullUpdate := reader.Uint8() != 0 + if reader.Error() != nil { + return reader.Error() + } + if ok, err := s.canInteractWithGuildBank(ctx, banker); err != nil || !ok { + return err + } + return s.sendGuildBankList(ctx, 0, fullUpdate, nil) +} + +func (s *GameSession) HandleGuildBankQueryTab(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + banker := reader.Uint64() + tabID := reader.Uint8() + fullUpdate := reader.Uint8() != 0 + if reader.Error() != nil { + return reader.Error() + } + if ok, err := s.canInteractWithGuildBank(ctx, banker); err != nil || !ok { + return err + } + return s.sendGuildBankList(ctx, tabID, fullUpdate, nil) +} + +func (s *GameSession) HandleGuildBankLogQuery(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + tabID := reader.Uint8() + if reader.Error() != nil { + return reader.Error() + } + if s.character == nil || s.character.GuildID == 0 { + return nil + } + + resp, err := s.guildServiceClient.GetGuildBankLog(ctx, &pbGuild.GetGuildBankLogParams{ + Api: root.Ver, + RealmID: root.RealmID, + GuildID: uint64(s.character.GuildID), + MemberGUID: s.character.GUID, + TabID: uint32(tabID), + }) + if err != nil { + return err + } + if !s.guildBankStatusOK(resp.Status, guildCommandViewTab) { + return nil + } + + w := packet.NewWriterWithSize(packet.MsgGuildBankLogQuery, 0) + w.Uint8(tabID) + w.Uint8(uint8(len(resp.Entries))) + for _, entry := range resp.Entries { + w.Uint8(uint8(int8(entry.EntryType))) + w.Uint64(guid.NewFromCounter(guid.Player, guid.LowType(entry.PlayerGUID)).GetRawValue()) + switch entry.EntryType { + case int32(repoGuildBankLogDepositItem), int32(repoGuildBankLogWithdrawItem): + w.Uint32(uint32(entry.ItemID)) + w.Uint32(uint32(entry.Count)) + case int32(repoGuildBankLogMoveItem), int32(repoGuildBankLogMoveItem2): + w.Uint32(uint32(entry.ItemID)) + w.Uint32(uint32(entry.Count)) + w.Uint8(uint8(entry.OtherTab)) + default: + w.Uint32(entry.Money) + } + w.Uint32(entry.TimeOffset) + } + s.gameSocket.Send(w) + return nil +} + +func (s *GameSession) HandleQueryGuildBankText(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + tabID := reader.Uint8() + if reader.Error() != nil { + return reader.Error() + } + if s.character == nil || s.character.GuildID == 0 { + return nil + } + + resp, err := s.guildServiceClient.GetGuildBankTabText(ctx, &pbGuild.GetGuildBankTabTextParams{ + Api: root.Ver, + RealmID: root.RealmID, + GuildID: uint64(s.character.GuildID), + MemberGUID: s.character.GUID, + TabID: uint32(tabID), + }) + if err != nil { + return err + } + if !s.guildBankStatusOK(resp.Status, guildCommandViewTab) { + return nil + } + + w := packet.NewWriterWithSize(packet.MsgQueryGuildBankText, uint32(1+len(resp.Text)+1)) + w.Uint8(tabID) + w.String(resp.Text) + s.gameSocket.Send(w) + return nil +} + +func (s *GameSession) HandleSetGuildBankText(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + tabID := reader.Uint8() + text := reader.String() + if reader.Error() != nil { + return reader.Error() + } + if s.character == nil || s.character.GuildID == 0 { + return nil + } + + resp, err := s.guildServiceClient.SetGuildBankTabText(ctx, &pbGuild.SetGuildBankTabTextParams{ + Api: root.Ver, + RealmID: root.RealmID, + GuildID: uint64(s.character.GuildID), + MemberGUID: s.character.GUID, + TabID: uint32(tabID), + Text: text, + }) + if err != nil { + return err + } + s.guildBankStatusOK(resp.Status, guildCommandViewTab) + return nil +} + +func (s *GameSession) HandleGuildBankUpdateTab(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + banker := reader.Uint64() + tabID := reader.Uint8() + name := reader.String() + icon := reader.String() + if reader.Error() != nil { + return reader.Error() + } + if ok, err := s.canInteractWithGuildBank(ctx, banker); err != nil || !ok { + return err + } + + resp, err := s.guildServiceClient.UpdateGuildBankTab(ctx, &pbGuild.UpdateGuildBankTabParams{ + Api: root.Ver, + RealmID: root.RealmID, + GuildID: uint64(s.character.GuildID), + MemberGUID: s.character.GUID, + TabID: uint32(tabID), + Name: name, + Icon: icon, + }) + if err != nil { + return err + } + if s.guildBankStatusOK(resp.Status, guildCommandViewTab) { + return s.sendGuildBankList(ctx, 0, true, nil) + } + return nil +} + +func (s *GameSession) HandleGuildBankBuyTab(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + banker := reader.Uint64() + tabID := reader.Uint8() + if reader.Error() != nil { + return reader.Error() + } + if ok, err := s.canInteractWithGuildBank(ctx, banker); err != nil || !ok { + return err + } + if tabID >= guildBankMaxTabs { + s.sendGuildCommandResult(guildCommandViewTab, "", guildCommandResultInternal) + return nil + } + + cost := guildBankTabCosts[tabID] + money, err := s.gameServerGRPCClient.GetMoneyForPlayer(ctx, &pbWorld.GetMoneyForPlayerRequest{Api: root.Ver, PlayerGuid: s.character.GUID}) + if err != nil { + return err + } + if money.Money < cost { + s.sendGuildCommandResult(guildCommandViewTab, "", guildCommandResultNotEnoughMoney) + return nil + } + if _, err = s.gameServerGRPCClient.ModifyMoneyForPlayer(ctx, &pbWorld.ModifyMoneyForPlayerRequest{Api: root.Ver, PlayerGuid: s.character.GUID, Value: -int32(cost)}); err != nil { + return err + } + + resp, err := s.guildServiceClient.BuyGuildBankTab(ctx, &pbGuild.BuyGuildBankTabParams{ + Api: root.Ver, + RealmID: root.RealmID, + GuildID: uint64(s.character.GuildID), + MemberGUID: s.character.GUID, + TabID: uint32(tabID), + }) + if err != nil { + return err + } + if resp.Status != pbGuild.GuildBankStatus_Ok { + _, _ = s.gameServerGRPCClient.ModifyMoneyForPlayer(ctx, &pbWorld.ModifyMoneyForPlayerRequest{Api: root.Ver, PlayerGuid: s.character.GUID, Value: int32(cost)}) + s.guildBankStatusOK(resp.Status, guildCommandViewTab) + return nil + } + return s.sendGuildBankList(ctx, 0, true, nil) +} + +func (s *GameSession) HandleGuildBankDepositMoney(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + banker := reader.Uint64() + amount := reader.Uint32() + if reader.Error() != nil { + return reader.Error() + } + if ok, err := s.canInteractWithGuildBank(ctx, banker); err != nil || !ok { + return err + } + + money, err := s.gameServerGRPCClient.GetMoneyForPlayer(ctx, &pbWorld.GetMoneyForPlayerRequest{Api: root.Ver, PlayerGuid: s.character.GUID}) + if err != nil { + return err + } + if money.Money < amount { + s.sendGuildCommandResult(guildCommandMoveItem, "", guildCommandResultNotEnoughMoney) + return nil + } + if _, err = s.gameServerGRPCClient.ModifyMoneyForPlayer(ctx, &pbWorld.ModifyMoneyForPlayerRequest{Api: root.Ver, PlayerGuid: s.character.GUID, Value: -int32(amount)}); err != nil { + return err + } + + resp, err := s.guildServiceClient.DepositGuildBankMoney(ctx, &pbGuild.DepositGuildBankMoneyParams{ + Api: root.Ver, + RealmID: root.RealmID, + GuildID: uint64(s.character.GuildID), + MemberGUID: s.character.GUID, + Amount: amount, + }) + if err != nil { + return err + } + if resp.Status != pbGuild.GuildBankStatus_Ok { + _, _ = s.gameServerGRPCClient.ModifyMoneyForPlayer(ctx, &pbWorld.ModifyMoneyForPlayerRequest{Api: root.Ver, PlayerGuid: s.character.GUID, Value: int32(amount)}) + s.guildBankStatusOK(resp.Status, guildCommandMoveItem) + return nil + } + return s.sendGuildBankList(ctx, 0, false, nil) +} + +func (s *GameSession) HandleGuildBankWithdrawMoney(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + banker := reader.Uint64() + amount := reader.Uint32() + if reader.Error() != nil { + return reader.Error() + } + if ok, err := s.canInteractWithGuildBank(ctx, banker); err != nil || !ok { + return err + } + + resp, err := s.guildServiceClient.WithdrawGuildBankMoney(ctx, &pbGuild.WithdrawGuildBankMoneyParams{ + Api: root.Ver, + RealmID: root.RealmID, + GuildID: uint64(s.character.GuildID), + MemberGUID: s.character.GUID, + Amount: amount, + }) + if err != nil { + return err + } + if !s.guildBankStatusOK(resp.Status, guildCommandMoveItem) { + return nil + } + if _, err = s.gameServerGRPCClient.ModifyMoneyForPlayer(ctx, &pbWorld.ModifyMoneyForPlayerRequest{Api: root.Ver, PlayerGuid: s.character.GUID, Value: int32(amount)}); err != nil { + if _, rollbackErr := s.guildServiceClient.RollbackGuildBankMoneyWithdraw(ctx, &pbGuild.RollbackGuildBankMoneyWithdrawParams{Api: root.Ver, RealmID: root.RealmID, GuildID: uint64(s.character.GuildID), MemberGUID: s.character.GUID, Amount: amount, LogGUID: resp.LogGUID}); rollbackErr != nil { + return rollbackErr + } + return err + } + return s.sendGuildBankList(ctx, 0, false, nil) +} + +func (s *GameSession) HandleGuildBankSwapItems(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + banker := reader.Uint64() + bankOnly := reader.Uint8() != 0 + if reader.Error() != nil { + return reader.Error() + } + if ok, err := s.canInteractWithGuildBank(ctx, banker); err != nil || !ok { + return err + } + + if bankOnly { + destTab := reader.Uint8() + destSlot := reader.Uint8() + reader.Uint32() + srcTab := reader.Uint8() + srcSlot := reader.Uint8() + reader.Uint32() + reader.Uint8() + count := reader.Uint32() + if reader.Error() != nil { + return reader.Error() + } + resp, err := s.guildServiceClient.MoveGuildBankItem(ctx, &pbGuild.MoveGuildBankItemParams{ + Api: root.Ver, + RealmID: root.RealmID, + GuildID: uint64(s.character.GuildID), + MemberGUID: s.character.GUID, + SourceTabID: uint32(srcTab), + SourceSlotID: uint32(srcSlot), + DestinationTabID: uint32(destTab), + DestinationSlotID: uint32(destSlot), + Count: count, + }) + if err != nil { + return err + } + if !s.guildBankStatusOK(resp.Status, guildCommandMoveItem) { + return nil + } + if err = s.sendGuildBankList(ctx, srcTab, false, nil); err != nil { + return err + } + if destTab != srcTab { + return s.sendGuildBankList(ctx, destTab, false, nil) + } + return nil + } + + bankTab := reader.Uint8() + bankSlot := reader.Uint8() + reader.Uint32() + autoStore := reader.Uint8() != 0 + var playerBag uint8 = 255 + var playerSlot uint8 = 255 + toChar := true + var stackCount uint32 + if autoStore { + reader.Uint32() + reader.Uint8() + stackCount = reader.Uint32() + } else { + playerBag = reader.Uint8() + playerSlot = reader.Uint8() + toChar = reader.Uint8() != 0 + stackCount = reader.Uint32() + } + if reader.Error() != nil { + return reader.Error() + } + + if toChar { + return s.withdrawGuildBankItem(ctx, bankTab, bankSlot, stackCount, !autoStore, playerBag, playerSlot) + } + return s.depositGuildBankItem(ctx, bankTab, bankSlot, stackCount, playerBag, playerSlot) +} + +func (s *GameSession) depositGuildBankItem(ctx context.Context, tabID, slotID uint8, count uint32, playerBag, playerSlot uint8) error { + taken, err := s.gameServerGRPCClient.TakePlayerItemByPos(ctx, &pbWorld.TakePlayerItemByPosRequest{ + Api: root.Ver, + PlayerGuid: s.character.GUID, + BagSlot: uint32(playerBag), + Slot: uint32(playerSlot), + Count: count, + AssignToPlayerGuid: 0, + }) + if err != nil { + return err + } + if taken.Status != pbWorld.TakePlayerItemByPosResponse_Success || taken.Item == nil { + s.sendGuildCommandResult(guildCommandMoveItem, "", guildCommandResultItemNotFound) + return nil + } + + resp, err := s.guildServiceClient.DepositGuildBankItem(ctx, &pbGuild.DepositGuildBankItemParams{ + Api: root.Ver, + RealmID: root.RealmID, + GuildID: uint64(s.character.GuildID), + MemberGUID: s.character.GUID, + TabID: uint32(tabID), + SlotID: uint32(slotID), + Item: worldItemToGuildBankItem(taken.Item, slotID), + }) + if err != nil { + return err + } + if resp.Status != pbGuild.GuildBankStatus_Ok { + _, _ = s.gameServerGRPCClient.AddExistingItemToPlayer(ctx, &pbWorld.AddExistingItemToPlayerRequest{ + Api: root.Ver, + PlayerGuid: s.character.GUID, + Item: worldTakenItemToAddItem(taken.Item), + }) + s.guildBankStatusOK(resp.Status, guildCommandMoveItem) + return nil + } + return s.sendGuildBankList(ctx, tabID, false, resp.ChangedSlots) +} + +func (s *GameSession) withdrawGuildBankItem(ctx context.Context, tabID, slotID uint8, count uint32, storeAtPos bool, playerBag, playerSlot uint8) error { + resp, err := s.guildServiceClient.WithdrawGuildBankItem(ctx, &pbGuild.WithdrawGuildBankItemParams{ + Api: root.Ver, + RealmID: root.RealmID, + GuildID: uint64(s.character.GuildID), + MemberGUID: s.character.GUID, + TabID: uint32(tabID), + SlotID: uint32(slotID), + Count: count, + }) + if err != nil { + return err + } + if !s.guildBankStatusOK(resp.Status, guildCommandMoveItem) || resp.Item == nil { + return nil + } + + addResp, err := s.gameServerGRPCClient.AddExistingItemToPlayer(ctx, &pbWorld.AddExistingItemToPlayerRequest{ + Api: root.Ver, + PlayerGuid: s.character.GUID, + Item: &pbWorld.AddExistingItemToPlayerRequest_Item{ + Guid: resp.Item.ItemGUID, + Entry: resp.Item.Entry, + Count: resp.Item.Count, + Flags: resp.Item.Flags, + Durability: resp.Item.Durability, + RandomPropertyID: resp.Item.RandomPropertyID, + Text: resp.Item.Text, + }, + StoreAtPos: storeAtPos, + BagSlot: uint32(playerBag), + Slot: uint32(playerSlot), + }) + if err != nil || addResp.Status != pbWorld.AddExistingItemToPlayerResponse_Success { + rollbackResp, rollbackErr := s.guildServiceClient.RollbackGuildBankItemWithdraw(ctx, &pbGuild.RollbackGuildBankItemWithdrawParams{ + Api: root.Ver, + RealmID: root.RealmID, + GuildID: uint64(s.character.GuildID), + MemberGUID: s.character.GUID, + TabID: uint32(tabID), + SlotID: uint32(slotID), + Item: resp.Item, + LogGUID: resp.LogGUID, + }) + if rollbackErr != nil { + return rollbackErr + } + if rollbackResp != nil && rollbackResp.Status == pbGuild.GuildBankStatus_Ok { + _ = s.sendGuildBankList(ctx, tabID, false, rollbackResp.ChangedSlots) + } + if err != nil { + return err + } + s.sendGuildCommandResult(guildCommandMoveItem, "", guildCommandResultBankFull) + return nil + } + return s.sendGuildBankList(ctx, tabID, false, resp.ChangedSlots) +} + +func (s *GameSession) sendGuildBankList(ctx context.Context, tabID uint8, fullUpdate bool, changedSlots []uint32) error { + if s.character == nil || s.character.GuildID == 0 { + return nil + } + resp, err := s.guildServiceClient.GetGuildBank(ctx, &pbGuild.GetGuildBankParams{ + Api: root.Ver, + RealmID: root.RealmID, + GuildID: uint64(s.character.GuildID), + MemberGUID: s.character.GUID, + TabID: uint32(tabID), + FullUpdate: fullUpdate, + }) + if err != nil { + return err + } + if !s.guildBankStatusOK(resp.Status, guildCommandViewTab) { + return nil + } + s.gameSocket.SendPacket(buildGuildBankListPacket(resp, changedSlots)) + return nil +} + +func buildGuildBankListPacket(resp *pbGuild.GetGuildBankResponse, changedSlots []uint32) *packet.Packet { + w := packet.NewWriterWithSize(packet.SMsgGuildBankList, 0) + w.Uint64(resp.Money) + w.Uint8(uint8(resp.TabID)) + w.Int32(resp.WithdrawalsRemaining) + w.Uint8(boolToUint8(resp.FullUpdate)) + + if resp.FullUpdate && resp.TabID == 0 { + w.Uint8(uint8(len(resp.Tabs))) + for _, tab := range resp.Tabs { + w.String(tab.Name) + w.String(tab.Icon) + } + } + + items := resp.Items + if len(changedSlots) > 0 { + itemBySlot := map[uint32]*pbGuild.GuildBankItem{} + for _, item := range resp.Items { + itemBySlot[item.Slot] = item + } + items = make([]*pbGuild.GuildBankItem, 0, len(changedSlots)) + for _, slot := range changedSlots { + if item := itemBySlot[slot]; item != nil { + items = append(items, item) + } else { + items = append(items, &pbGuild.GuildBankItem{Slot: slot}) + } + } + } + + w.Uint8(uint8(len(items))) + for _, item := range items { + w.Uint8(uint8(item.Slot)) + w.Uint32(item.Entry) + if item.Entry == 0 { + continue + } + w.Int32(int32(item.Flags)) + w.Int32(item.RandomPropertyID) + if item.RandomPropertyID != 0 { + w.Int32(item.RandomPropertySeed) + } + w.Int32(int32(item.Count)) + w.Int32(int32(item.EnchantmentID)) + w.Uint8(uint8(item.Charges)) + w.Uint8(uint8(len(item.SocketEnchants))) + for _, socket := range item.SocketEnchants { + w.Uint8(uint8(socket.SocketIndex)) + w.Int32(int32(socket.SocketEnchantID)) + } + } + return w.ToPacket() +} + +func (s *GameSession) canInteractWithGuildBank(ctx context.Context, object uint64) (bool, error) { + if s.character == nil || s.character.GuildID == 0 { + s.sendGuildCommandResult(guildCommandViewTab, "", guildCommandResultPlayerNotInGuild) + return false, nil + } + if guid.New(object).GetHigh() != guid.GameObject { + return false, fmt.Errorf("player '%d' tried to interact with non-gameobject guild bank '%d'", s.character.GUID, object) + } + const gameObjectTypeGuildBank = 34 + resp, err := s.gameServerGRPCClient.CanPlayerInteractWithGameObject(ctx, &pbWorld.CanPlayerInteractWithGameObjectRequest{ + Api: root.Ver, + PlayerGuid: s.character.GUID, + GameObjectGuid: object, + GameObjectType: gameObjectTypeGuildBank, + }) + if err != nil { + return false, err + } + return resp.CanInteract, nil +} + +func (s *GameSession) guildBankStatusOK(status pbGuild.GuildBankStatus_Status, command int32) bool { + if status == pbGuild.GuildBankStatus_Ok { + return true + } + s.sendGuildCommandResult(command, "", guildBankStatusToNativeResult(status)) + return false +} + +func guildBankStatusToNativeResult(status pbGuild.GuildBankStatus_Status) int32 { + switch status { + case pbGuild.GuildBankStatus_NotInGuild, pbGuild.GuildBankStatus_GuildNotFound: + return guildCommandResultPlayerNotInGuild + case pbGuild.GuildBankStatus_NotEnoughRights: + return guildCommandResultPermissions + case pbGuild.GuildBankStatus_NotEnoughMoney: + return guildCommandResultNotEnoughMoney + case pbGuild.GuildBankStatus_BankFull: + return guildCommandResultBankFull + case pbGuild.GuildBankStatus_WithdrawLimit: + return guildCommandResultWithdrawLimit + case pbGuild.GuildBankStatus_ItemNotFound: + return guildCommandResultItemNotFound + default: + return guildCommandResultInternal + } +} + +func worldItemToGuildBankItem(item *pbWorld.TakePlayerItemByPosResponse_Item, slotID uint8) *pbGuild.GuildBankItem { + return &pbGuild.GuildBankItem{ + ItemGUID: item.Guid, + Entry: item.Entry, + Slot: uint32(slotID), + Count: item.Count, + Flags: item.Flags, + Durability: item.Durability, + RandomPropertyID: item.RandomPropertyID, + Text: item.Text, + } +} + +func worldTakenItemToAddItem(item *pbWorld.TakePlayerItemByPosResponse_Item) *pbWorld.AddExistingItemToPlayerRequest_Item { + return &pbWorld.AddExistingItemToPlayerRequest_Item{ + Guid: item.Guid, + Entry: item.Entry, + Count: item.Count, + Flags: item.Flags, + Durability: item.Durability, + RandomPropertyID: item.RandomPropertyID, + Text: item.Text, + } +} + +const ( + repoGuildBankLogDepositItem = 1 + repoGuildBankLogWithdrawItem = 2 + repoGuildBankLogMoveItem = 3 + repoGuildBankLogMoveItem2 = 7 +) diff --git a/apps/gateway/session/guild_petition.go b/apps/gateway/session/guild_petition.go new file mode 100644 index 0000000..940a19e --- /dev/null +++ b/apps/gateway/session/guild_petition.go @@ -0,0 +1,343 @@ +package session + +import ( + "context" + + root "github.com/walkline/ToCloud9/apps/gateway" + eBroadcaster "github.com/walkline/ToCloud9/apps/gateway/events-broadcaster" + "github.com/walkline/ToCloud9/apps/gateway/packet" + pbChar "github.com/walkline/ToCloud9/gen/characters/pb" + pbGuild "github.com/walkline/ToCloud9/gen/guilds/pb" + "github.com/walkline/ToCloud9/shared/events" + wowguid "github.com/walkline/ToCloud9/shared/wow/guid" +) + +func (s *GameSession) HandleOfferGuildPetition(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + reader.Uint32() + petitionGUID := reader.Uint64() + targetClientGUID := reader.Uint64() + if reader.Error() != nil || s.character == nil { + s.forwardPacketToWorldserver(p) + return nil + } + + petition, err := s.guildPetitionForGateway(ctx, petitionGUID) + if err != nil { + return err + } + if petition == nil { + s.forwardPacketToWorldserver(p) + return nil + } + + if playerRealmIDOrDefault(s.guildHomeRealmID(), targetClientGUID) != s.guildHomeRealmID() { + return nil + } + if s.charServiceClient == nil { + s.forwardPacketToWorldserver(p) + return nil + } + + targetLowGUID := wowguid.PlayerLowGUID(targetClientGUID) + target, err := s.onlineGuildPetitionTarget(ctx, targetLowGUID) + if err != nil { + return err + } + if target == nil { + return nil + } + + if target.CharGuildID != 0 { + s.sendGuildCommandResult(guildCommandInvite, target.CharName, guildCommandResultAlreadyInGuildS) + return nil + } + + if !s.guildCanInviteRace(uint8(target.GetCharRace())) { + s.sendGuildCommandResult(guildCommandCreate, "", guildCommandResultNotAllied) + return nil + } + + resp, err := s.guildServiceClient.OfferGuildPetition(ctx, &pbGuild.OfferGuildPetitionParams{ + Api: root.Ver, + RealmID: s.guildHomeRealmID(), + OwnerGUID: s.character.GUID, + TargetGUID: target.CharGUID, + TargetName: target.CharName, + PetitionGUID: petitionGUID, + }) + if err != nil { + return err + } + + switch resp.GetStatus() { + case pbGuild.OfferGuildPetitionResponse_TargetAlreadyInGuild: + s.sendGuildCommandResult(guildCommandInvite, target.CharName, guildCommandResultAlreadyInGuildS) + case pbGuild.OfferGuildPetitionResponse_TargetAlreadyInvited: + s.sendGuildCommandResult(guildCommandInvite, target.CharName, guildCommandResultAlreadyInvitedToGuild) + } + + return nil +} + +func (s *GameSession) HandleSignGuildPetition(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + petitionGUID := reader.Uint64() + reader.Uint8() + if reader.Error() != nil || s.character == nil { + s.forwardPacketToWorldserver(p) + return nil + } + + petition, err := s.guildPetitionForGateway(ctx, petitionGUID) + if err != nil { + return err + } + if petition == nil { + s.forwardPacketToWorldserver(p) + return nil + } + + if !s.guildPetitionOwnerSameFaction(ctx, petition.GetOwnerGUID()) { + s.sendGuildCommandResult(guildCommandCreate, "", guildCommandResultNotAllied) + return nil + } + + resp, err := s.guildServiceClient.SignGuildPetition(ctx, &pbGuild.SignGuildPetitionParams{ + Api: root.Ver, + RealmID: s.guildHomeRealmID(), + SignerGUID: s.character.GUID, + SignerName: s.character.Name, + SignerAccountID: s.character.AccountID, + SignerGuildID: s.character.GuildID, + PetitionGUID: petitionGUID, + }) + if err != nil { + return err + } + + switch resp.GetStatus() { + case pbGuild.SignGuildPetitionResponse_AlreadyInGuild: + s.sendGuildCommandResult(guildCommandInvite, s.character.Name, guildCommandResultAlreadyInGuildS) + case pbGuild.SignGuildPetitionResponse_AlreadyInvited: + s.sendGuildCommandResult(guildCommandInvite, s.character.Name, guildCommandResultAlreadyInvitedToGuild) + case pbGuild.SignGuildPetitionResponse_CantSignOwn: + return nil + default: + s.sendGuildPetitionSignResult(petitionGUID, s.character.GUID, guildPetitionSignStatusToNative(resp.GetStatus())) + } + + return nil +} + +func (s *GameSession) HandleGuildPetitionShowSignatures(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + petitionGUID := reader.Uint64() + if reader.Error() != nil { + s.forwardPacketToWorldserver(p) + return nil + } + + petition, err := s.guildPetitionForGateway(ctx, petitionGUID) + if err != nil { + return err + } + if petition == nil { + s.forwardPacketToWorldserver(p) + return nil + } + + s.sendGuildPetitionSignatures(petition) + return nil +} + +func (s *GameSession) HandleGuildPetitionQuery(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + reader.Uint32() + petitionGUID := reader.Uint64() + if reader.Error() != nil { + s.forwardPacketToWorldserver(p) + return nil + } + + petition, err := s.guildPetitionForGateway(ctx, petitionGUID) + if err != nil { + return err + } + if petition == nil { + s.forwardPacketToWorldserver(p) + return nil + } + + s.gameSocket.SendPacket(buildGuildPetitionQueryResponse(petition)) + return nil +} + +func (s *GameSession) HandleEventGuildPetitionOffered(_ context.Context, e *eBroadcaster.Event) error { + payload := e.Payload.(*events.GuildEventPetitionOfferedPayload) + signatures := make([]*pbGuild.GuildPetitionSignature, len(payload.Signatures)) + for i := range payload.Signatures { + signatures[i] = &pbGuild.GuildPetitionSignature{ + PlayerGUID: payload.Signatures[i].PlayerGUID, + PlayerAccount: payload.Signatures[i].PlayerAccount, + } + } + + s.sendGuildPetitionSignatures(&pbGuild.GuildPetition{ + PetitionGUID: payload.PetitionGUID, + PetitionID: payload.PetitionID, + OwnerGUID: payload.OwnerGUID, + Name: payload.GuildName, + Type: guildPetitionType, + Signatures: signatures, + }) + return nil +} + +func (s *GameSession) HandleEventGuildPetitionSigned(_ context.Context, e *eBroadcaster.Event) error { + payload := e.Payload.(*events.GuildEventPetitionSignedPayload) + s.sendGuildPetitionSignResult(payload.PetitionGUID, payload.SignerGUID, payload.NativeStatus) + return nil +} + +func (s *GameSession) guildPetitionForGateway(ctx context.Context, petitionGUID uint64) (*pbGuild.GuildPetition, error) { + if s.guildServiceClient == nil { + return nil, nil + } + + resp, err := s.guildServiceClient.GetGuildPetition(ctx, &pbGuild.GetGuildPetitionParams{ + Api: root.Ver, + RealmID: s.guildHomeRealmID(), + PetitionGUID: petitionGUID, + }) + if err != nil { + return nil, err + } + + if resp.GetPetition() == nil || resp.GetPetition().GetType() != guildPetitionType { + return nil, nil + } + + return resp.GetPetition(), nil +} + +func (s *GameSession) onlineGuildPetitionTarget(ctx context.Context, targetLowGUID uint64) (*pbChar.ShortCharactersDataByGUIDsResponse_ShortCharData, error) { + if s.charServiceClient == nil { + return nil, nil + } + + resp, err := s.charServiceClient.ShortOnlineCharactersDataByGUIDs(ctx, &pbChar.ShortCharactersDataByGUIDsRequest{ + Api: root.Ver, + RealmID: s.guildHomeRealmID(), + GUIDs: []uint64{targetLowGUID}, + }) + if err != nil { + return nil, err + } + + for _, character := range resp.GetCharacters() { + if character.GetRealmID() == s.guildHomeRealmID() && character.GetCharGUID() == targetLowGUID && character.GetIsOnline() { + return character, nil + } + } + + return nil, nil +} + +func (s *GameSession) guildPetitionOwnerSameFaction(ctx context.Context, ownerGUID uint64) bool { + if root.AllowTwoSideInteractionGuild { + return true + } + if s == nil || s.character == nil || s.charServiceClient == nil { + return false + } + + resp, err := s.charServiceClient.CharacterByGUID(ctx, &pbChar.CharacterByGUIDRequest{ + Api: root.Ver, + RealmID: s.guildHomeRealmID(), + CharacterGUID: ownerGUID, + }) + if err != nil || resp.GetCharacter() == nil || resp.GetCharacter().GetRealmID() != s.guildHomeRealmID() { + return false + } + + return guildSameFactionByRace(s.character.Race, uint8(resp.GetCharacter().GetCharRace())) +} + +func (s *GameSession) sendGuildPetitionSignatures(petition *pbGuild.GuildPetition) { + signatures := petition.GetSignatures() + resp := packet.NewWriterWithSize(packet.SMsgPetitionShowSignatures, uint32(8+8+4+1+len(signatures)*12)) + resp.Uint64(petition.GetPetitionGUID()) + resp.Uint64(petition.GetOwnerGUID()) + resp.Uint32(petition.GetPetitionID()) + resp.Uint8(uint8(len(signatures))) + for _, signature := range signatures { + resp.Uint64(signature.GetPlayerGUID()) + resp.Uint32(0) + } + + s.gameSocket.SendPacket(resp.ToPacket()) +} + +func (s *GameSession) sendGuildPetitionSignResult(petitionGUID uint64, signerGUID uint64, status uint32) { + resp := packet.NewWriterWithSize(packet.SMsgPetitionSignResults, 8+8+4) + resp.Uint64(petitionGUID) + resp.Uint64(signerGUID) + resp.Uint32(status) + s.gameSocket.SendPacket(resp.ToPacket()) +} + +func (s *GameSession) sendGuildCommandResult(command int32, name string, result int32) { + resp := packet.NewWriterWithSize(packet.SMsgGuildCommandResult, uint32(4+len(name)+1+4)) + resp.Int32(command) + resp.String(name) + resp.Int32(result) + s.gameSocket.SendPacket(resp.ToPacket()) +} + +func (s *GameSession) forwardPacketToWorldserver(p *packet.Packet) { + if s.worldSocket != nil { + s.worldSocket.SendPacket(p) + } +} + +func guildPetitionSignStatusToNative(status pbGuild.SignGuildPetitionResponse_Status) uint32 { + switch status { + case pbGuild.SignGuildPetitionResponse_Ok: + return petitionSignOK + case pbGuild.SignGuildPetitionResponse_AlreadySigned: + return petitionSignAlreadySigned + case pbGuild.SignGuildPetitionResponse_AlreadyInGuild: + return petitionSignAlreadyInGuild + case pbGuild.SignGuildPetitionResponse_CantSignOwn: + return petitionSignCantSignOwn + default: + return petitionSignNotServer + } +} + +func buildGuildPetitionQueryResponse(petition *pbGuild.GuildPetition) *packet.Packet { + needed := petition.GetType() + resp := packet.NewWriterWithSize(packet.SMsgPetitionQueryResponse, 0) + resp.Uint32(petition.GetPetitionID()) + resp.Uint64(petition.GetOwnerGUID()) + resp.String(petition.GetName()) + resp.Uint8(0) + resp.Uint32(needed) + resp.Uint32(needed) + resp.Uint32(0) + resp.Uint32(0) + resp.Uint32(0) + resp.Uint32(0) + resp.Uint32(0) + resp.Uint16(0) + resp.Uint32(0) + resp.Uint32(0) + resp.Uint32(0) + for i := 0; i < 10; i++ { + resp.Uint8(0) + } + resp.Uint32(0) + resp.Uint32(0) + return resp.ToPacket() +} diff --git a/apps/gateway/session/handler-events.go b/apps/gateway/session/handler-events.go index 5026412..efe983c 100644 --- a/apps/gateway/session/handler-events.go +++ b/apps/gateway/session/handler-events.go @@ -21,12 +21,19 @@ var EventsHandleMap = map[eBroadcaster.EventType]EventsHandlersQueue{ eBroadcaster.EventTypeGuildRankUpdated: NewEventHandler("GuildRankUpdated", (*GameSession).HandleEventGuildRankUpdated), eBroadcaster.EventTypeGuildRankDeleted: NewEventHandler("GuildRankDeleted", (*GameSession).HandleEventGuildRankDeleted), eBroadcaster.EventTypeGuildNewMessage: NewEventHandler("GuildNewMessage", (*GameSession).HandleEventGuildNewMessage), + eBroadcaster.EventTypeGuildPetitionOffered: NewEventHandler( + "GuildPetitionOffered", (*GameSession).HandleEventGuildPetitionOffered, + ), + eBroadcaster.EventTypeGuildPetitionSigned: NewEventHandler( + "GuildPetitionSigned", (*GameSession).HandleEventGuildPetitionSigned, + ), // Mail eBroadcaster.EventTypeIncomingMail: NewEventHandler("IncomingMail", (*GameSession).HandleEventIncomingMail), // Groups eBroadcaster.EventTypeGroupInviteCreated: NewEventHandler("EventTypeGroupInviteCreated", (*GameSession).HandleEventGroupInviteCreated), + eBroadcaster.EventTypeGroupInviteDeclined: NewEventHandler("EventTypeGroupInviteDeclined", (*GameSession).HandleEventGroupInviteDeclined), eBroadcaster.EventTypeGroupCreated: NewEventHandler("EventTypeGroupCreated", (*GameSession).HandleEventGroupCreated), eBroadcaster.EventTypeGroupMemberOnlineStatusChanged: NewEventHandler("EventTypeGroupMemberOnlineStatusChanged", (*GameSession).HandleEventGroupMemberOnlineStatusChanged), eBroadcaster.EventTypeGroupMemberLeft: NewEventHandler("EventTypeGroupMemberLeft", (*GameSession).HandleEventGroupMemberLeft), @@ -38,11 +45,21 @@ var EventsHandleMap = map[eBroadcaster.EventType]EventsHandlersQueue{ eBroadcaster.EventTypeGroupNewMessage: NewEventHandler("EventTypeGroupNewMessage", (*GameSession).HandleEventGroupNewMessage), eBroadcaster.EventTypeGroupNewTargetIcon: NewEventHandler("EventTypeGroupNewTargetIcon", (*GameSession).HandleEventGroupNewTargetIcon), eBroadcaster.EventTypeGroupDifficultyChanged: NewEventHandler("EventTypeGroupDifficultyChanged", (*GameSession).HandleEventGroupDifficultyChanged), + eBroadcaster.EventTypeGroupReadyCheckStarted: NewEventHandler("EventTypeGroupReadyCheckStarted", (*GameSession).HandleEventGroupReadyCheckStarted), + eBroadcaster.EventTypeGroupReadyCheckMemberState: NewEventHandler("EventTypeGroupReadyCheckMemberState", (*GameSession).HandleEventGroupReadyCheckMemberState), + eBroadcaster.EventTypeGroupReadyCheckFinished: NewEventHandler("EventTypeGroupReadyCheckFinished", (*GameSession).HandleEventGroupReadyCheckFinished), + eBroadcaster.EventTypeGroupMemberSubGroupChanged: NewEventHandler("EventTypeGroupMemberSubGroupChanged", (*GameSession).HandleEventGroupMemberSubGroupChanged), + eBroadcaster.EventTypeGroupMemberFlagsChanged: NewEventHandler("EventTypeGroupMemberFlagsChanged", (*GameSession).HandleEventGroupMemberFlagsChanged), + eBroadcaster.EventTypeGroupMemberStateChanged: NewEventHandler("EventTypeGroupMemberStateChanged", (*GameSession).HandleEventGroupMemberStateChanged), + eBroadcaster.EventTypeGroupMemberStatesChanged: NewEventHandler("EventTypeGroupMemberStatesChanged", (*GameSession).HandleEventGroupMemberStatesChanged), // Matchmaking eBroadcaster.EventTypeMMJoinedPVPQueue: NewEventHandler("EventTypeMMJoinedPVPQueue", (*GameSession).HandleEventMMJoinedPVPQueue), eBroadcaster.EventTypeMMInvitedToBGOrArena: NewEventHandler("EventTypeMMInvitedToBGOrArena", (*GameSession).HandleEventMMInvitedToBGOrArena), eBroadcaster.EventTypeMMInviteToBGOrArenaExpired: NewEventHandler("EventTypeMMInviteToBGOrArenaExpired", (*GameSession).HandleEventMMInviteToBGOrArenaExpired), + eBroadcaster.EventTypeMMLfgStatusChanged: NewEventHandler("EventTypeMMLfgStatusChanged", (*GameSession).HandleEventMMLfgStatusChanged), + eBroadcaster.EventTypeMMLfgProposalAccepted: NewEventHandler("EventTypeMMLfgProposalAccepted", (*GameSession).HandleEventMMLfgProposalAccepted), + eBroadcaster.EventTypeMMServiceUnavailable: NewEventHandler("EventTypeMMServiceUnavailable", (*GameSession).HandleEventMMServiceUnavailable), // Friends eBroadcaster.EventTypeFriendStatusChange: NewEventHandler("FriendStatusChange", (*GameSession).HandleEventFriendStatusChange), @@ -50,6 +67,10 @@ var EventsHandleMap = map[eBroadcaster.EventType]EventsHandlersQueue{ eBroadcaster.EventTypeFriendRemoved: NewEventHandler("FriendRemoved", (*GameSession).HandleEventFriendRemoved), eBroadcaster.EventTypeFriendNoteUpdate: NewEventHandler("FriendNoteUpdate", (*GameSession).HandleEventFriendNoteUpdate), + // Arena teams + eBroadcaster.EventTypeArenaTeamInviteCreated: NewEventHandler("ArenaTeamInviteCreated", (*GameSession).HandleEventArenaTeamInviteCreated), + eBroadcaster.EventTypeArenaTeamNativeEvent: NewEventHandler("ArenaTeamNativeEvent", (*GameSession).HandleEventArenaTeamNativeEvent), + // Channels eBroadcaster.EventTypeChannelMessage: NewEventHandler("ChannelMessage", (*GameSession).HandleEventChannelMessage), eBroadcaster.EventTypeChannelJoined: NewEventHandler("ChannelJoined", (*GameSession).HandleEventChannelJoined), diff --git a/apps/gateway/session/handler.go b/apps/gateway/session/handler.go index ee5e812..5572fe8 100644 --- a/apps/gateway/session/handler.go +++ b/apps/gateway/session/handler.go @@ -14,9 +14,9 @@ import ( // Used to block worldserver friend system packets since we handle friends at gateway level // Also blocks channel packets since we handle channels at gateway level in distributed fashion var OpcodeBlacklist = map[packet.Opcode]bool{ - packet.SMsgFriendStatus: true, - packet.SMsgContactList: true, - packet.SMsgChannelList: true, + packet.SMsgFriendStatus: true, + packet.SMsgContactList: true, + packet.TC9SMsgWorldSessionReady: true, } var HandleMap = map[packet.Opcode]HandlersQueue{ @@ -26,7 +26,10 @@ var HandleMap = map[packet.Opcode]HandlersQueue{ packet.CMsgCharEnum: NewHandler("CMsgCharEnum", (*GameSession).CharactersList), packet.CMsgRealmSplit: NewHandler("CMsgRealmSplit", (*GameSession).RealmSplit), packet.CMsgReadyForAccountDataTimes: NewHandler("CMsgReadyForAccountDataTimes", (*GameSession).ReadyForAccountDataTimes), + packet.CMsgRequestAccountData: NewHandler("CMsgRequestAccountData", (*GameSession).RequestAccountData), + packet.CMsgUpdateAccountData: NewHandler("CMsgUpdateAccountData", (*GameSession).UpdateAccountData), packet.CMsgMessageChat: NewHandler("CMsgMessageChat", (*GameSession).HandleChatMessage), + packet.CMsgNameQuery: NewHandler("CMsgNameQuery", (*GameSession).HandleNameQuery), packet.CMsgGuildQuery: NewHandler("CMsgGuildQuery", (*GameSession).HandleGuildQuery), packet.CMsgWho: NewHandler("CMsgWho", (*GameSession).HandleWho), @@ -39,77 +42,142 @@ var HandleMap = map[packet.Opcode]HandlersQueue{ packet.CMsgDelIgnore: NewHandler("CMsgDelIgnore", (*GameSession).HandleDelIgnore), // Channels - packet.CMsgJoinChannel: NewHandler("CMsgJoinChannel", (*GameSession).HandleJoinChannel), - packet.CMsgLeaveChannel: NewHandler("CMsgLeaveChannel", (*GameSession).HandleLeaveChannel), - packet.CMsgChannelList: NewHandler("CMsgChannelList", (*GameSession).HandleChannelList), - packet.CMsgChannelDisplayList: NewHandler("CMsgChannelDisplayList", (*GameSession).HandleChannelList), - packet.SMsgChannelNotify: NewHandler("SMsgChannelNotify", (*GameSession).InterceptWorldserverChannelNotify), - packet.CMsgChannelPassword: NewHandler("CMsgChannelPassword", (*GameSession).HandleChannelPassword), - packet.CMsgChannelSetOwner: NewHandler("CMsgChannelSetOwner", (*GameSession).HandleChannelSetOwner), - packet.CMsgChannelModerator: NewHandler("CMsgChannelModerator", (*GameSession).HandleChannelSetModerator), - packet.CMsgChannelUnModerator: NewHandler("CMsgChannelUnModerator", (*GameSession).HandleChannelUnsetModerator), - packet.CMsgChannelMute: NewHandler("CMsgChannelMute", (*GameSession).HandleChannelMute), - packet.CMsgChannelUnmute: NewHandler("CMsgChannelUnmute", (*GameSession).HandleChannelUnmute), - packet.CMsgChannelInvite: NewHandler("CMsgChannelInvite", (*GameSession).HandleChannelInvite), - packet.CMsgChannelKick: NewHandler("CMsgChannelKick", (*GameSession).HandleChannelKick), - packet.CMsgChannelBan: NewHandler("CMsgChannelBan", (*GameSession).HandleChannelBan), - packet.CMsgChannelUnban: NewHandler("CMsgChannelUnban", (*GameSession).HandleChannelUnban), - packet.CMsgChannelAnnouncements: NewHandler("CMsgChannelAnnouncements", (*GameSession).HandleChannelAnnouncements), - packet.CMsgChannelModerate: NewHandler("CMsgChannelModerate", (*GameSession).HandleChannelModerate), - - packet.CMsgGuildInvite: NewHandler("CMsgGuildInvite", (*GameSession).HandleGuildInvite), - packet.CMsgGuildInviteAccept: NewHandler("CMsgGuildInviteAccept", (*GameSession).HandleGuildInviteAccept), - packet.CMsgGuildRoster: NewHandler("CMsgGuildRoster", (*GameSession).HandleGuildRoster), - packet.CMsgGuildLeave: NewHandler("CMsgGuildLeave", (*GameSession).HandleGuildLeave), - packet.CMsgGuildRemove: NewHandler("CMsgGuildRemove", (*GameSession).HandleGuildKick), - packet.CMsgGuildMOTD: NewHandler("CMsgGuildSMTD", (*GameSession).HandleGuildSetMessageOfTheDay), - packet.CMsgGuildSetPublicNote: NewHandler("CMsgGuildSetPublicNote", (*GameSession).HandleGuildSetPublicNote), - packet.CMsgGuildSetOfficerNote: NewHandler("CMsgGuildSetOfficerNote", (*GameSession).HandleGuildSetOfficerNote), - packet.CMsgGuildInfoText: NewHandler("CMsgGuildInfoText", (*GameSession).HandleGuildSetInfoText), - packet.CMsgGuildRank: NewHandler("CMsgGuildRank", (*GameSession).HandleGuildRankUpdate), - packet.CMsgGuildAddRank: NewHandler("CMsgGuildAddRank", (*GameSession).HandleGuildRankAdd), - packet.CMsgGuildDelRank: NewHandler("CMsgGuildDelRank", (*GameSession).HandleGuildRankDelete), - packet.CMsgGuildPromote: NewHandler("CMsgGuildPromote", (*GameSession).HandleGuildPromote), - packet.CMsgGuildDemote: NewHandler("CMsgGuildDemote", (*GameSession).HandleGuildDemote), - packet.CMsgSendMail: NewHandler("CMsgSendMail", (*GameSession).HandleSendMail), - packet.CMsgGetMailList: NewHandler("CMsgGetMailList", (*GameSession).HandleGetMailList), - packet.CMsgMailMarkAsRead: NewHandler("CMsgMailMarkAsRead", (*GameSession).HandleMailMarksAsRead), - packet.CMsgMailTakeMoney: NewHandler("CMsgMailTakeMoney", (*GameSession).HandleMailTakeMoney), - packet.CMsgMailTakeItem: NewHandler("CMsgMailTakeItem", (*GameSession).HandleMailTakeItem), - packet.CMsgMailDelete: NewHandler("CMsgMailDelete", (*GameSession).HandleDeleteMail), - packet.MsgQueryNextMailTime: NewHandler("MsgQueryNextMailTime", (*GameSession).HandleQueryNextMailTime), - packet.SMsgInitWorldStates: NewHandler("SMsgInitWorldStates", (*GameSession).InterceptInitWorldStates), - packet.SMsgLevelUpInfo: NewHandler("SMsgLevelUpInfo", (*GameSession).InterceptLevelUpInfo), - packet.CMsgPing: NewHandler("CMsgPing", (*GameSession).HandlePing), - packet.SMsgPong: NewHandler("SMsgPong", (*GameSession).InterceptPong), - packet.SMsgNewWorld: NewHandler("SMsgNewWorld", (*GameSession).InterceptNewWorld), - packet.MsgGuildPermissions: NewHandler("MsgGuildPermissions", (*GameSession).HandleGuildPermissions), - packet.MsgGuildBankMoneyWithdrawn: NewHandler("MsgGuildBankMoneyWithdrawn", (*GameSession).HandleGuildBankMoneyWithdrawn), - packet.MsgMoveWorldPortAck: NewHandler("MsgMoveWorldPortAck", (*GameSession).InterceptMoveWorldPortAck), - packet.SMsgMOTD: NewHandler("SMsgMOTD", (*GameSession).InterceptMessageOfTheDay), - packet.SMsgAccountDataTimes: NewHandler("SMsgAccountDataTimes", (*GameSession).InterceptAccountDataTimes), + packet.CMsgJoinChannel: NewHandler("CMsgJoinChannel", (*GameSession).HandleJoinChannel), + packet.CMsgLeaveChannel: NewHandler("CMsgLeaveChannel", (*GameSession).HandleLeaveChannel), + packet.CMsgChannelList: NewHandler("CMsgChannelList", (*GameSession).HandleChannelList), + packet.CMsgChannelDisplayList: NewHandler("CMsgChannelDisplayList", (*GameSession).HandleChannelList), + packet.SMsgChannelNotify: NewHandler("SMsgChannelNotify", (*GameSession).InterceptWorldserverChannelNotify), + packet.CMsgChannelPassword: NewHandler("CMsgChannelPassword", (*GameSession).HandleChannelPassword), + packet.CMsgChannelSetOwner: NewHandler("CMsgChannelSetOwner", (*GameSession).HandleChannelSetOwner), + packet.CMsgChannelModerator: NewHandler("CMsgChannelModerator", (*GameSession).HandleChannelSetModerator), + packet.CMsgChannelUnModerator: NewHandler("CMsgChannelUnModerator", (*GameSession).HandleChannelUnsetModerator), + packet.CMsgChannelMute: NewHandler("CMsgChannelMute", (*GameSession).HandleChannelMute), + packet.CMsgChannelUnmute: NewHandler("CMsgChannelUnmute", (*GameSession).HandleChannelUnmute), + packet.CMsgChannelInvite: NewHandler("CMsgChannelInvite", (*GameSession).HandleChannelInvite), + packet.CMsgChannelKick: NewHandler("CMsgChannelKick", (*GameSession).HandleChannelKick), + packet.CMsgChannelBan: NewHandler("CMsgChannelBan", (*GameSession).HandleChannelBan), + packet.CMsgChannelUnban: NewHandler("CMsgChannelUnban", (*GameSession).HandleChannelUnban), + packet.CMsgChannelAnnouncements: NewHandler("CMsgChannelAnnouncements", (*GameSession).HandleChannelAnnouncements), + packet.CMsgChannelModerate: NewHandler("CMsgChannelModerate", (*GameSession).HandleChannelModerate), + + packet.CMsgGuildInvite: NewHandler("CMsgGuildInvite", (*GameSession).HandleGuildInvite), + packet.CMsgGuildInviteAccept: NewHandler("CMsgGuildInviteAccept", (*GameSession).HandleGuildInviteAccept), + packet.CMsgGuildRoster: NewHandler("CMsgGuildRoster", (*GameSession).HandleGuildRoster), + packet.CMsgGuildLeave: NewHandler("CMsgGuildLeave", (*GameSession).HandleGuildLeave), + packet.CMsgGuildRemove: NewHandler("CMsgGuildRemove", (*GameSession).HandleGuildKick), + packet.CMsgGuildMOTD: NewHandler("CMsgGuildSMTD", (*GameSession).HandleGuildSetMessageOfTheDay), + packet.CMsgGuildSetPublicNote: NewHandler("CMsgGuildSetPublicNote", (*GameSession).HandleGuildSetPublicNote), + packet.CMsgGuildSetOfficerNote: NewHandler("CMsgGuildSetOfficerNote", (*GameSession).HandleGuildSetOfficerNote), + packet.CMsgGuildInfoText: NewHandler("CMsgGuildInfoText", (*GameSession).HandleGuildSetInfoText), + packet.CMsgGuildRank: NewHandler("CMsgGuildRank", (*GameSession).HandleGuildRankUpdate), + packet.CMsgGuildAddRank: NewHandler("CMsgGuildAddRank", (*GameSession).HandleGuildRankAdd), + packet.CMsgGuildDelRank: NewHandler("CMsgGuildDelRank", (*GameSession).HandleGuildRankDelete), + packet.CMsgGuildPromote: NewHandler("CMsgGuildPromote", (*GameSession).HandleGuildPromote), + packet.CMsgGuildDemote: NewHandler("CMsgGuildDemote", (*GameSession).HandleGuildDemote), + packet.CMsgOfferPetition: NewHandler("CMsgOfferPetition", (*GameSession).HandleOfferGuildPetition), + packet.CMsgPetitionSign: NewHandler("CMsgPetitionSign", (*GameSession).HandleSignGuildPetition), + packet.CMsgPetitionShowSignatures: NewHandler("CMsgPetitionShowSignatures", (*GameSession).HandleGuildPetitionShowSignatures), + packet.CMsgPetitionQuery: NewHandler("CMsgPetitionQuery", (*GameSession).HandleGuildPetitionQuery), + packet.CMsgTurnInPetition: NewHandler("CMsgTurnInPetition", (*GameSession).HandleArenaTeamPetitionTurnIn), + packet.SMsgGuildCommandResult: NewHandler("SMsgGuildCommandResult", (*GameSession).InterceptGuildCommandResult), + packet.SMsgTurnInPetitionResults: NewHandler("SMsgTurnInPetitionResults", (*GameSession).InterceptTurnInPetitionResults), + packet.MsgGuildPermissions: NewHandler("MsgGuildPermissions", (*GameSession).HandleGuildPermissions), + packet.MsgGuildBankMoneyWithdrawn: NewHandler("MsgGuildBankMoneyWithdrawn", (*GameSession).HandleGuildBankMoneyWithdrawn), + packet.CMsgGuildBankerActivate: NewHandler("CMsgGuildBankerActivate", (*GameSession).HandleGuildBankerActivate), + packet.CMsgGuildBankQueryTab: NewHandler("CMsgGuildBankQueryTab", (*GameSession).HandleGuildBankQueryTab), + packet.CMsgGuildBankSwapItems: NewHandler("CMsgGuildBankSwapItems", (*GameSession).HandleGuildBankSwapItems), + packet.CMsgGuildBankBuyTab: NewHandler("CMsgGuildBankBuyTab", (*GameSession).HandleGuildBankBuyTab), + packet.CMsgGuildBankUpdateTab: NewHandler("CMsgGuildBankUpdateTab", (*GameSession).HandleGuildBankUpdateTab), + packet.CMsgGuildBankDepositMoney: NewHandler("CMsgGuildBankDepositMoney", (*GameSession).HandleGuildBankDepositMoney), + packet.CMsgGuildBankWithdrawMoney: NewHandler("CMsgGuildBankWithdrawMoney", (*GameSession).HandleGuildBankWithdrawMoney), + packet.MsgGuildBankLogQuery: NewHandler("MsgGuildBankLogQuery", (*GameSession).HandleGuildBankLogQuery), + packet.MsgQueryGuildBankText: NewHandler("MsgQueryGuildBankText", (*GameSession).HandleQueryGuildBankText), + packet.CMsgSetGuildBankText: NewHandler("CMsgSetGuildBankText", (*GameSession).HandleSetGuildBankText), + packet.CMsgSendMail: NewHandler("CMsgSendMail", (*GameSession).HandleSendMail), + packet.CMsgGetMailList: NewHandler("CMsgGetMailList", (*GameSession).HandleGetMailList), + packet.CMsgMailMarkAsRead: NewHandler("CMsgMailMarkAsRead", (*GameSession).HandleMailMarksAsRead), + packet.CMsgMailTakeMoney: NewHandler("CMsgMailTakeMoney", (*GameSession).HandleMailTakeMoney), + packet.CMsgMailTakeItem: NewHandler("CMsgMailTakeItem", (*GameSession).HandleMailTakeItem), + packet.CMsgMailDelete: NewHandler("CMsgMailDelete", (*GameSession).HandleDeleteMail), + packet.MsgQueryNextMailTime: NewHandler("MsgQueryNextMailTime", (*GameSession).HandleQueryNextMailTime), + packet.SMsgInitWorldStates: NewHandler("SMsgInitWorldStates", (*GameSession).InterceptInitWorldStates), + packet.SMsgLevelUpInfo: NewHandler("SMsgLevelUpInfo", (*GameSession).InterceptLevelUpInfo), + packet.CMsgPing: NewHandler("CMsgPing", (*GameSession).HandlePing), + packet.SMsgPong: NewHandler("SMsgPong", (*GameSession).InterceptPong), + packet.SMsgNewWorld: NewHandler("SMsgNewWorld", (*GameSession).InterceptNewWorld), + packet.MsgMoveWorldPortAck: NewHandler("MsgMoveWorldPortAck", (*GameSession).InterceptMoveWorldPortAck), + packet.CMsgSetActiveMover: NewHandler("CMsgSetActiveMover", (*GameSession).HandlePlayerWorldActivePacket), + packet.CMsgWorldStateUiTimerUpdate: NewHandler("CMsgWorldStateUiTimerUpdate", (*GameSession).HandlePlayerWorldActivePacket), + packet.SMsgLogoutComplete: NewHandler("SMsgLogoutComplete", (*GameSession).InterceptLogoutComplete), + packet.SMsgTransferPending: NewHandler("SMsgTransferPending", (*GameSession).InterceptTransferPending), + packet.SMsgUpdateObject: NewHandler("SMsgUpdateObject", (*GameSession).InterceptObjectUpdate), + packet.SMsgCompressedUpdateObject: NewHandler("SMsgCompressedUpdateObject", (*GameSession).InterceptObjectUpdate), + packet.SMsgGroupDestroyed: NewHandler("SMsgGroupDestroyed", (*GameSession).InterceptGroupPresentationPacket), + packet.SMsgGroupList: NewHandler("SMsgGroupList", (*GameSession).InterceptGroupPresentationPacket), + packet.SMsgPartyMemberStats: NewHandler("SMsgPartyMemberStats", (*GameSession).InterceptPartyMemberStats), + packet.SMsgPartyMemberStatsFull: NewHandler("SMsgPartyMemberStatsFull", (*GameSession).InterceptPartyMemberStats), + packet.SMsgHealthUpdate: NewHandler("SMsgHealthUpdate", (*GameSession).InterceptDirectPlayerStateUpdate), + packet.SMsgPowerUpdate: NewHandler("SMsgPowerUpdate", (*GameSession).InterceptDirectPlayerStateUpdate), + packet.SMsgAuraUpdateAll: NewHandler("SMsgAuraUpdateAll", (*GameSession).InterceptAuraUpdate), + packet.SMsgAuraUpdate: NewHandler("SMsgAuraUpdate", (*GameSession).InterceptAuraUpdate), + packet.SMsgMOTD: NewHandler("SMsgMOTD", (*GameSession).InterceptMessageOfTheDay), + packet.SMsgAccountDataTimes: NewHandler("SMsgAccountDataTimes", (*GameSession).InterceptAccountDataTimes), packet.TC9SMsgReadyForRedirect: NewHandler("TC9SMsgReadyForRedirect", (*GameSession).HandleReadyForRedirectRequest), packet.SMsgNameQueryResponse: NewHandler("SMsgNameQueryResponse", (*GameSession).InterceptSMsgNameQueryResponse), // Groups - packet.CMsgGroupInvite: NewHandler("CMsgGroupInvite", (*GameSession).HandleGroupInvite), - packet.CMsgGroupAccept: NewHandler("CMsgGroupAccept", (*GameSession).HandleGroupInviteAccept), - packet.CMsgGroupDecline: NewHandler("CMsgGroupDecline", (*GameSession).HandleGroupInviteDeclined), - packet.CMsgGroupUnInvite: NewHandler("CMsgGroupUnInvite", (*GameSession).HandleGroupUninvite), - packet.CMsgGroupUnInviteGuid: NewHandler("CMsgGroupUnInviteGuid", (*GameSession).HandleGroupUninviteGUID), - packet.CMsgGroupDisband: NewHandler("CMsgGroupDisband", (*GameSession).HandleGroupLeave), - packet.CMsgGroupRaidConvert: NewHandler("CMsgGroupRaidConvert", (*GameSession).HandleGroupConvertToRaid), - packet.CMsgGroupSetLeader: NewHandler("CMsgGroupSetLeader", (*GameSession).HandleGroupSetLeader), - packet.MsgRaidTargetUpdate: NewHandler("MsgRaidTargetUpdate", (*GameSession).HandleSetGroupTargetIcon), - packet.CMsgLootMethod: NewHandler("CMsgLootMethod", (*GameSession).HandleSetLootMethod), - packet.MsgSetDungeonDifficulty: NewHandler("MsgSetDungeonDifficulty", (*GameSession).HandleSetDungeonDifficulty), - packet.MsgSetRaidDifficulty: NewHandler("MsgSetRaidDifficulty", (*GameSession).HandleSetRaidDifficulty), + packet.CMsgGroupInvite: NewHandler("CMsgGroupInvite", (*GameSession).HandleGroupInvite), + packet.CMsgGroupAccept: NewHandler("CMsgGroupAccept", (*GameSession).HandleGroupInviteAccept), + packet.CMsgGroupDecline: NewHandler("CMsgGroupDecline", (*GameSession).HandleGroupInviteDeclined), + packet.CMsgGroupUnInvite: NewHandler("CMsgGroupUnInvite", (*GameSession).HandleGroupUninvite), + packet.CMsgGroupUnInviteGuid: NewHandler("CMsgGroupUnInviteGuid", (*GameSession).HandleGroupUninviteGUID), + packet.CMsgGroupDisband: NewHandler("CMsgGroupDisband", (*GameSession).HandleGroupLeave), + packet.CMsgGroupRaidConvert: NewHandler("CMsgGroupRaidConvert", (*GameSession).HandleGroupConvertToRaid), + packet.CMsgGroupSetLeader: NewHandler("CMsgGroupSetLeader", (*GameSession).HandleGroupSetLeader), + packet.MsgRaidTargetUpdate: NewHandler("MsgRaidTargetUpdate", (*GameSession).HandleSetGroupTargetIcon), + packet.CMsgLootMethod: NewHandler("CMsgLootMethod", (*GameSession).HandleSetLootMethod), + packet.MsgSetDungeonDifficulty: NewHandler("MsgSetDungeonDifficulty", (*GameSession).HandleSetDungeonDifficulty), + packet.MsgSetRaidDifficulty: NewHandler("MsgSetRaidDifficulty", (*GameSession).HandleSetRaidDifficulty), + packet.MsgRaidReadyCheck: NewHandler("MsgRaidReadyCheck", (*GameSession).HandleRaidReadyCheck), + packet.MsgRaidReadyCheckConfirm: NewHandler("MsgRaidReadyCheckConfirm", (*GameSession).HandleRaidReadyCheckConfirm), + packet.MsgRaidReadyCheckFinished: NewHandler("MsgRaidReadyCheckFinished", (*GameSession).HandleRaidReadyCheckFinished), + packet.CMsgGroupChangeSubGroup: NewHandler("CMsgGroupChangeSubGroup", (*GameSession).HandleGroupChangeSubGroup), + packet.CMsgGroupSwapSubGroup: NewHandler("CMsgGroupSwapSubGroup", (*GameSession).HandleGroupSwapSubGroup), + packet.CMsgGroupAssistantLeader: NewHandler("CMsgGroupAssistantLeader", (*GameSession).HandleGroupAssistantLeader), + packet.MsgPartyAssignment: NewHandler("MsgPartyAssignment", (*GameSession).HandlePartyAssignment), + packet.CMsgResetInstances: NewHandler("CMsgResetInstances", (*GameSession).HandleResetInstances), + packet.CMsgSetSavedInstanceExtend: NewHandler("CMsgSetSavedInstanceExtend", (*GameSession).HandleSetSavedInstanceExtend), // Battlegrounds - packet.CMsgBattleMasterJoin: NewHandler("CMsgBattleMasterJoin", (*GameSession).HandleEnqueueToBattleground), - packet.CMsgBattlefieldPort: NewHandler("CMsgBattlefieldPort", (*GameSession).HandleBattlegroundPort), + packet.CMsgBattleMasterJoin: NewHandler("CMsgBattleMasterJoin", (*GameSession).HandleEnqueueToBattleground), + packet.CMsgBattlemasterJoinArena: NewHandler("CMsgBattlemasterJoinArena", (*GameSession).HandleEnqueueToArena), + packet.CMsgBattlefieldPort: NewHandler("CMsgBattlefieldPort", (*GameSession).HandleBattlegroundPort), + + // Arena teams + packet.CMsgArenaTeamQuery: NewHandler("CMsgArenaTeamQuery", (*GameSession).HandleArenaTeamQuery), + packet.CMsgArenaTeamRoster: NewHandler("CMsgArenaTeamRoster", (*GameSession).HandleArenaTeamRoster), + packet.CMsgArenaTeamInvite: NewHandler("CMsgArenaTeamInvite", (*GameSession).HandleArenaTeamInvite), + packet.CMsgArenaTeamAccept: NewHandler("CMsgArenaTeamAccept", (*GameSession).HandleArenaTeamAccept), + packet.CMsgArenaTeamDecline: NewHandler("CMsgArenaTeamDecline", (*GameSession).HandleArenaTeamDecline), + packet.CMsgArenaTeamLeave: NewHandler("CMsgArenaTeamLeave", (*GameSession).HandleArenaTeamLeave), + packet.CMsgArenaTeamRemove: NewHandler("CMsgArenaTeamRemove", (*GameSession).HandleArenaTeamRemove), + packet.CMsgArenaTeamDisband: NewHandler("CMsgArenaTeamDisband", (*GameSession).HandleArenaTeamDisband), + packet.CMsgArenaTeamLeader: NewHandler("CMsgArenaTeamLeader", (*GameSession).HandleArenaTeamLeader), + + // LFG + packet.CMsgLFGJoin: NewHandler("CMsgLFGJoin", (*GameSession).HandleLfgJoin), + packet.CMsgLFGLeave: NewHandler("CMsgLFGLeave", (*GameSession).HandleLfgLeave), + packet.CMsgLFGSetRoles: NewHandler("CMsgLFGSetRoles", (*GameSession).HandleLfgSetRoles), + packet.CMsgLFGProposalResult: NewHandler("CMsgLFGProposalResult", (*GameSession).HandleLfgProposalResult), + packet.CMsgLFGGetStatus: NewHandler("CMsgLFGGetStatus", (*GameSession).HandleLfgGetStatus), + packet.CMsgLfdPlayerLockInfoRequest: NewHandler("CMsgLfdPlayerLockInfoRequest", (*GameSession).HandleLfgPlayerLockInfoRequest), + packet.CMsgLFGTeleport: NewHandler("CMsgLFGTeleport", (*GameSession).HandleLfgTeleport), + packet.SMsgLFGTeleportDenied: NewHandler("SMsgLFGTeleportDenied", (*GameSession).InterceptLfgTeleportDenied), + packet.SMsgLFGUpdateParty: NewHandler("SMsgLFGUpdateParty", (*GameSession).InterceptLfgUpdateParty), + packet.CMsgLFGSetBootVote: NewHandler("CMsgLFGSetBootVote", (*GameSession).HandleLfgSetBootVote), + packet.CMsgLfdPartyLockInfoRequest: NewHandler("CMsgLfdPartyLockInfoRequest", (*GameSession).HandleLfgPartyLockInfoRequest), // Movements packet.MsgMoveStop: NewHandler("MsgMoveStop", (*GameSession).HandleMovement), diff --git a/apps/gateway/session/interceptors.go b/apps/gateway/session/interceptors.go index c5bb7df..42d6925 100644 --- a/apps/gateway/session/interceptors.go +++ b/apps/gateway/session/interceptors.go @@ -11,6 +11,7 @@ import ( "github.com/walkline/ToCloud9/apps/gateway/sockets" "github.com/walkline/ToCloud9/gen/characters/pb" pbServ "github.com/walkline/ToCloud9/gen/servers-registry/pb" + pbGameServ "github.com/walkline/ToCloud9/gen/worldserver/pb" "github.com/walkline/ToCloud9/shared/wow/guid" ) @@ -32,7 +33,26 @@ func (s *GameSession) InterceptInitWorldStates(ctx context.Context, p *packet.Pa s.character.Zone = uint32(zoneID) } - return nil + if s.pendingRedirectID != "" { + now := time.Now() + s.logger.Info(). + Str("redirect", s.pendingRedirectID). + Uint32("account", s.accountID). + Uint64("character", s.character.GUID). + Uint32("map", uint32(mapID)). + Uint32("zone", uint32(zoneID)). + Uint32("area", uint32(areaID)). + Dur("totalDuration", now.Sub(s.pendingRedirectAt)). + Msg("Cross-worldserver redirect reached target world") + s.pendingRedirectID = "" + s.pendingRedirectAt = time.Time{} + } + + if err := s.confirmLfgDungeonRouteEntered(ctx, uint32(mapID)); err != nil { + return err + } + + return s.reloadManagedGroupAfterMapTransfer(ctx) } func (s *GameSession) InterceptLevelUpInfo(ctx context.Context, p *packet.Packet) error { @@ -49,129 +69,342 @@ func (s *GameSession) InterceptLevelUpInfo(ctx context.Context, p *packet.Packet return nil } +func (s *GameSession) InterceptTransferPending(_ context.Context, p *packet.Packet) error { + if s.shouldSuppressRedirectWorldPortPacket(p) { + return nil + } + + s.gameSocket.SendPacket(p) + return nil +} + func (s *GameSession) InterceptNewWorld(ctx context.Context, p *packet.Packet) error { + if s.shouldSuppressRedirectWorldPortPacket(p) { + return nil + } + mapID := p.Reader().Uint32() + return s.forwardNewWorldPacket(ctx, p, mapID) +} + +func (s *GameSession) forwardNewWorldPacket(ctx context.Context, p *packet.Packet, mapID uint32) error { if s.character.ignoreNextInterceptToNewMap == nil || mapID != *s.character.ignoreNextInterceptToNewMap { s.teleportingToNewMap = &mapID + if s.pendingMapTransferRouting == nil { + routing, blocked, err := s.lfgRoutingForNativeWorldport(ctx, mapID) + if err != nil { + return err + } + if blocked { + s.teleportingToNewMap = nil + s.clearPendingMapTransferRouting() + s.sendTransferAborted(mapID, transferAbortNotFound) + return nil + } + if routing != nil { + s.setPendingMapTransferRouting(routing) + s.character.GroupMangedByGameServer = true + } + } + s.activatePendingMapTransferRouting() } s.gameSocket.SendPacket(p) return nil } -func (s *GameSession) InterceptMoveWorldPortAck(ctx context.Context, p *packet.Packet) error { - if s.worldSocket == nil { - return errors.New("can't handle InterceptMoveWorldPortAck, worldSocket is nil") +func (s *GameSession) shouldSuppressRedirectWorldPortPacket(p *packet.Packet) bool { + if s == nil || p == nil || p.Source != packet.SourceWorldServer { + return false } - s.worldSocket.SendPacket(p) - if s.teleportingToNewMap != nil && s.character.GroupMangedByGameServer { - s.character.GroupMangedByGameServer = false - if err := s.LoadGroupForPlayer(ctx); err != nil { - return err + characterGUID := uint64(0) + if s.character != nil { + characterGUID = s.character.GUID + } + + if s.pendingRedirectID != "" { + if s.logger != nil { + s.logger.Debug(). + Str("redirect", s.pendingRedirectID). + Uint32("account", s.accountID). + Uint64("character", characterGUID). + Uint16("opcode", uint16(p.Opcode)). + Msg("TC9 suppressing redirect target native worldport packet") } + return true + } + return false +} + +func (s *GameSession) InterceptMoveWorldPortAck(ctx context.Context, p *packet.Packet) error { + if s.worldSocket == nil { + return errors.New("can't handle InterceptMoveWorldPortAck, worldSocket is nil") } if s.teleportingToNewMap == nil { + s.worldSocket.SendPacket(p) return nil } mapID := *s.teleportingToNewMap s.teleportingToNewMap = nil s.character.ignoreNextInterceptToNewMap = nil + transferRouting := s.activeMapTransferRouting + s.clearActiveMapTransferRouting() + nextMapTransferRouting := cloneMapTransferRouting(transferRouting) + + realmID := root.RealmID + isCrossRealm := false + if transferRouting != nil { + realmID = transferRouting.realmID + isCrossRealm = transferRouting.isCrossRealm + } + if transferRouting != nil && transferRouting.ownerAddress != "" && shouldKeepMapTransferOnCurrentOwner(transferRouting, s.worldSocket.Address()) { + s.setCurrentMapTransferRouting(nextMapTransferRouting) + s.worldSocket.SendPacket(p) + s.logger.Debug(). + Uint64("character", s.character.GUID). + Uint32("map", mapID). + Str("worldserver", s.worldSocket.Address()). + Str("feature", transferRouting.feature.String()). + Msg("World port ack remains on owner worldserver") + return s.reloadManagedGroupAfterMapTransfer(ctx) + } + registryStarted := time.Now() serversResult, err := s.serversRegistryClient.AvailableGameServersForMapAndRealm(s.ctx, &pbServ.AvailableGameServersForMapAndRealmRequest{ - Api: root.SupportedCharServiceVer, - RealmID: root.RealmID, - MapID: mapID, + Api: root.SupportedServerRegistryVer, + RealmID: realmID, + MapID: mapID, + IsCrossRealm: isCrossRealm, }) if err != nil { + if shouldBlockLocalFallbackForMapTransfer(transferRouting) { + s.sendTransferAborted(mapID, transferAbortNotFound) + return err + } + s.worldSocket.SendPacket(p) return err } if len(serversResult.GameServers) == 0 { - return fmt.Errorf("%w, mapID %v", worldConnectErrInstanceNotFound, mapID) + if shouldBlockLocalFallbackForMapTransfer(transferRouting) { + s.sendTransferAborted(mapID, transferAbortNotFound) + return fmt.Errorf("%w, mapID %v, realmID %d", worldConnectErrInstanceNotFound, mapID, realmID) + } + s.worldSocket.SendPacket(p) + return fmt.Errorf("%w, mapID %v, realmID %d", worldConnectErrInstanceNotFound, mapID, realmID) } oldServerAddress := s.worldSocket.Address() - desiredServerAddress := serversResult.GameServers[0].Address + desiredServer := serversResult.GameServers[0] + desiredServerAddress := desiredServer.Address + desiredWorldserverID := gameServerSourceID(desiredServer) if desiredServerAddress == oldServerAddress { - return nil + s.setCurrentMapTransferRouting(nextMapTransferRouting) + s.worldSocket.SendPacket(p) + s.logger.Debug(). + Uint64("character", s.character.GUID). + Uint32("map", mapID). + Str("worldserver", oldServerAddress). + Dur("registryDuration", time.Since(registryStarted)). + Msg("World port ack remains on current worldserver") + return s.reloadManagedGroupAfterMapTransfer(ctx) } - saveAndClosePacket := packet.NewWriterWithSize(packet.TC9CMsgPrepareForRedirect, 0) + redirectID := fmt.Sprintf("%d-%d-%d", s.accountID, s.character.GUID, time.Now().UnixNano()) + redirectStarted := time.Now() + if !s.canReconnectCharacter(s.character.GUID) { + return fmt.Errorf("stale redirect for account %d, character %d, map %d: session no longer owns character", s.accountID, s.character.GUID, mapID) + } + sessionCharacterGUID := s.character.GUID + loginCharacterGUID := mapTransferLoginPlayerGUID(sessionCharacterGUID, transferRouting) + var gameServerGRPCClient pbGameServ.WorldServerServiceClient + if s.gameServerGRPCConnMgr != nil { + s.gameServerGRPCConnMgr.AddAddressMapping(desiredServer.Address, desiredServer.GrpcAddress) + gameServerGRPCClient, err = s.gameServerGRPCConnMgr.GRPCConnByGameServerAddress(desiredServer.Address) + if err != nil { + return fmt.Errorf("can't get game server grpc client for redirect target %s, err: %w", desiredServerAddress, err) + } + } + s.pendingRedirectID = redirectID + s.pendingRedirectAt = redirectStarted + s.logger.Info(). + Str("redirect", redirectID). + Uint32("account", s.accountID). + Uint64("character", sessionCharacterGUID). + Uint64("loginCharacter", loginCharacterGUID). + Uint32("map", mapID). + Str("source", oldServerAddress). + Str("target", desiredServerAddress). + Str("targetWorldserver", desiredWorldserverID). + Int("candidates", len(serversResult.GameServers)). + Dur("registryDuration", time.Since(registryStarted)). + Msg("Starting cross-worldserver redirect") + + redirectFeature := clusterTransferFeatureGeneric + if transferRouting != nil { + redirectFeature = transferRouting.feature + } + saveAndClosePacket := clusterTransferPrepareRedirectPacket(redirectFeature) + prepareStarted := time.Now() + s.logger.Info(). + Str("redirect", redirectID). + Uint32("account", s.accountID). + Uint64("character", sessionCharacterGUID). + Uint32("map", mapID). + Str("source", oldServerAddress). + Msg("Sending TC9CMsgPrepareForRedirect") s.worldSocket.Send(saveAndClosePacket) - confirmationIsSuccessfulChan := make(chan bool) - confirmationContext, cancel := context.WithTimeout(ctx, time.Second*5) + confirmationContext, cancel := context.WithTimeout(ctx, s.packetProcessTimeout) defer cancel() - go func() { - defer close(confirmationIsSuccessfulChan) - for { - select { - case <-confirmationContext.Done(): - confirmationIsSuccessfulChan <- false - return - case p, open := <-s.worldSocket.ReadChannel(): - if !open { - // If socket closed, then it also not bad, let's assume that as a good sign as well. - confirmationIsSuccessfulChan <- true - return - } - if p.Opcode == packet.TC9SMsgReadyForRedirect { - confirmationIsSuccessfulChan <- p.Reader().Uint8() == 0 - return - } - } + if err := s.waitForSourceRedirectReady(confirmationContext, s.worldSocket, redirectID, sessionCharacterGUID, mapID, oldServerAddress, desiredServerAddress); err != nil { + if s.pendingRedirectID == redirectID { + s.pendingRedirectID = "" + s.pendingRedirectAt = time.Time{} } - }() - - // Waits till new value in chan. - isReadyForRedirect := <-confirmationIsSuccessfulChan - if !isReadyForRedirect { - return fmt.Errorf("failed to redirect player with account %d, world server failed to prepare", s.accountID) + return fmt.Errorf("failed to redirect player with account %d, redirect %s, map %d, source %s, target %s after %s: %w", s.accountID, redirectID, mapID, oldServerAddress, desiredServerAddress, time.Since(prepareStarted), err) } + s.logger.Info(). + Str("redirect", redirectID). + Uint32("account", s.accountID). + Uint64("character", sessionCharacterGUID). + Uint32("map", mapID). + Str("source", oldServerAddress). + Dur("prepareDuration", time.Since(prepareStarted)). + Msg("Received TC9SMsgReadyForRedirect") s.worldSocket.Close() s.worldSocket = nil - go func(charGUID uint64) { + go func(sessionGUID uint64, targetLoginGUID uint64) { var err error var socket sockets.Socket - _, socket, err = s.connectToGameServer(context.Background(), charGUID, &mapID, nil) + connectStarted := time.Now() + if !s.canReconnectCharacter(sessionGUID) { + return + } + connectCtx, connectCancel := context.WithTimeout(s.ctx, s.packetProcessTimeout) + defer connectCancel() + socket, err = s.connectToGameServerWithAddressRetry(connectCtx, targetLoginGUID, desiredServerAddress, nil) if err != nil { - s.logger.Error().Err(err).Msg("failed to reconnect player to the world") + if !s.canReconnectCharacter(sessionGUID) { + return + } + s.logger.Error(). + Err(err). + Str("redirect", redirectID). + Uint32("account", s.accountID). + Uint64("character", sessionGUID). + Uint64("loginCharacter", targetLoginGUID). + Uint32("map", mapID). + Str("target", desiredServerAddress). + Dur("connectDuration", time.Since(connectStarted)). + Msg("failed to reconnect player to the world") resp := packet.NewWriterWithSize(packet.SMsgCharacterLoginFailed, 1) resp.Uint8(uint8(packet.LoginErrorCodeWorldServerIsDown)) s.gameSocket.Send(resp) + clearRedirect := func(session *GameSession) { + if session.pendingRedirectID == redirectID { + session.pendingRedirectID = "" + session.pendingRedirectAt = time.Time{} + } + } + select { + case s.sessionSafeFuChan <- clearRedirect: + case <-s.ctx.Done(): + } return } - s.gameSocket.SendPacket(p) - // we need to modify session in a safe thread (goroutine) - s.sessionSafeFuChan <- func(session *GameSession) { - if session.character != nil { - session.worldSocket = socket + updateSession := func(session *GameSession) { + if session.character == nil || session.character.GUID != sessionGUID || !session.canReconnectCharacter(sessionGUID) { + socket.Close() + return } + session.worldSocket = socket + session.worldserverID = desiredWorldserverID + if gameServerGRPCClient != nil { + session.gameServerGRPCClient = gameServerGRPCClient + } + session.setCurrentMapTransferRouting(transferRouting) + session.pendingRedirectID = redirectID + session.pendingRedirectAt = redirectStarted + session.resetPlayerAuraState() + if session.showGameserverConnChangeToClient { session.SendSysMessage(fmt.Sprintf("You have been redirected from %s to %s gameserver.", oldServerAddress, desiredServerAddress)) } + session.logger.Info(). + Str("redirect", redirectID). + Uint32("account", session.accountID). + Uint64("character", sessionGUID). + Uint64("loginCharacter", targetLoginGUID). + Uint32("map", mapID). + Str("source", oldServerAddress). + Str("target", desiredServerAddress). + Str("targetWorldserver", desiredWorldserverID). + Dur("connectDuration", time.Since(connectStarted)). + Dur("totalDuration", time.Since(redirectStarted)). + Msg("Cross-worldserver redirect connected to target") go func() { - time.Sleep(time.Millisecond * 500) + timer := time.NewTimer(time.Millisecond * 500) + defer timer.Stop() + + select { + case <-timer.C: + case <-session.ctx.Done(): + return + } - session.sessionSafeFuChan <- func(session *GameSession) { - session.RejoinWorldserverToSystemChannels(ctx) + rejoinSession := func(session *GameSession) { + if !session.canReconnectCharacter(sessionGUID) { + return + } + rejoinCtx, rejoinCancel := context.WithTimeout(session.ctx, session.packetProcessTimeout) + defer rejoinCancel() + if err := session.RejoinWorldserverToSystemChannels(rejoinCtx); err != nil { + session.logger.Error(). + Err(err). + Str("redirect", redirectID). + Uint64("character", sessionGUID). + Msg("failed to rejoin worldserver system channels after redirect") + } + } + select { + case session.sessionSafeFuChan <- rejoinSession: + case <-session.ctx.Done(): } }() } - }(s.character.GUID) + select { + case s.sessionSafeFuChan <- updateSession: + case <-s.ctx.Done(): + socket.Close() + } + }(sessionCharacterGUID, loginCharacterGUID) return nil } +func (s *GameSession) reloadManagedGroupAfterMapTransfer(ctx context.Context) error { + if s == nil || s.character == nil || !s.character.GroupMangedByGameServer || s.clusterGroupPresentationBlocked() { + return nil + } + + if err := s.LoadGroupForPlayer(ctx); err != nil { + return err + } + + s.character.GroupMangedByGameServer = false + return nil +} + func (s *GameSession) InterceptMessageOfTheDay(ctx context.Context, p *packet.Packet) error { if s.packetSendingControl.motdSent { return nil @@ -239,37 +472,60 @@ func (s *GameSession) InterceptSMsgNameQueryResponse(ctx context.Context, p *pac } playerData := res.Characters[0] - newPckt := packet.NewWriterWithSize(packet.SMsgNameQueryResponse, 0) - newPckt.GUID(charGUID) - - newPckt.Uint8(0) - newPckt.String(playerData.CharName) - if g.GetRealmID() == uint16(root.RealmID) { - newPckt.Uint8(0) - } else { - var name string - name, err = s.realmNamesService.NameByID(ctx, uint32(g.GetRealmID())) - if err != nil { - name = "unknown realm" - } - newPckt.String(name) - } - newPckt.Uint8(uint8(playerData.CharRace)) - newPckt.Uint8(uint8(playerData.CharGender)) - newPckt.Uint8(uint8(playerData.CharClass)) - newPckt.Uint8(0) - - s.gameSocket.Send(newPckt) + s.sendNameQueryResponse(ctx, charGUID, playerData.CharName, playerData.CharRace, playerData.CharClass, playerData.CharGender) return nil } func (s *GameSession) HandleReadyForRedirectRequest(ctx context.Context, p *packet.Packet) error { - oldConnection := s.worldSocket.Address() + if s == nil || s.character == nil { + return errors.New("can't handle worldserver-initiated redirect, character is nil") + } + if s.worldSocket == nil { + return errors.New("can't handle worldserver-initiated redirect, worldSocket is nil") + } + + status, err := redirectReadyStatus(p) + if err != nil { + return err + } + if status != 0 { + return fmt.Errorf("source worldserver rejected worldserver-initiated redirect with status %d, account %d, character %d", status, s.accountID, s.character.GUID) + } + + sessionCharacterGUID := s.character.GUID + if !s.canReconnectCharacter(sessionCharacterGUID) { + return fmt.Errorf("stale worldserver-initiated redirect for account %d, character %d: session no longer owns character", s.accountID, sessionCharacterGUID) + } - char, socket, err := s.connectToGameServer(ctx, s.character.GUID, nil, nil) + redirectID := fmt.Sprintf("%d-%d-%d", s.accountID, s.character.GUID, time.Now().UnixNano()) + redirectStarted := time.Now() + s.pendingRedirectID = redirectID + s.pendingRedirectAt = redirectStarted + oldSocket := s.worldSocket + oldConnection := oldSocket.Address() + s.logger.Info(). + Str("redirect", redirectID). + Uint32("account", s.accountID). + Uint64("character", s.character.GUID). + Str("source", oldConnection). + Msg("Starting worldserver-initiated redirect") + + char, socket, worldserverID, err := s.connectToGameServer(ctx, s.character.GUID, nil, nil) if err != nil { - return errors.New("failed to connect player to the new gameserver") + if s.pendingRedirectID == redirectID { + s.pendingRedirectID = "" + s.pendingRedirectAt = time.Time{} + } + return fmt.Errorf("failed to connect player to the new gameserver during redirect %s: %w", redirectID, err) + } + if !s.canReconnectCharacter(sessionCharacterGUID) { + socket.Close() + if s.pendingRedirectID == redirectID { + s.pendingRedirectID = "" + s.pendingRedirectAt = time.Time{} + } + return fmt.Errorf("stale worldserver-initiated redirect for account %d, character %d: session no longer owns character", s.accountID, sessionCharacterGUID) } resp := packet.NewWriterWithSize(packet.SMsgNewWorld, 0) @@ -281,12 +537,26 @@ func (s *GameSession) HandleReadyForRedirectRequest(ctx context.Context, p *pack s.gameSocket.Send(resp) if s.character != nil { + oldSocket.Close() s.worldSocket = socket + s.worldserverID = worldserverID + s.pendingRedirectID = redirectID + s.pendingRedirectAt = redirectStarted + s.resetPlayerAuraState() } if s.showGameserverConnChangeToClient { s.SendSysMessage(fmt.Sprintf("You have been redirected from %s to %s gameserver.", oldConnection, s.worldSocket.Address())) } + s.logger.Info(). + Str("redirect", redirectID). + Uint32("account", s.accountID). + Uint64("character", s.character.GUID). + Uint32("map", char.Map). + Str("source", oldConnection). + Str("target", s.worldSocket.Address()). + Dur("duration", time.Since(redirectStarted)). + Msg("Worldserver-initiated redirect connected to target") return nil } diff --git a/apps/gateway/session/lfg.go b/apps/gateway/session/lfg.go new file mode 100644 index 0000000..fd31884 --- /dev/null +++ b/apps/gateway/session/lfg.go @@ -0,0 +1,2119 @@ +package session + +import ( + "context" + "errors" + "fmt" + "sort" + "time" + + root "github.com/walkline/ToCloud9/apps/gateway" + eBroadcaster "github.com/walkline/ToCloud9/apps/gateway/events-broadcaster" + "github.com/walkline/ToCloud9/apps/gateway/packet" + pbChar "github.com/walkline/ToCloud9/gen/characters/pb" + pbGroup "github.com/walkline/ToCloud9/gen/group/pb" + pbMM "github.com/walkline/ToCloud9/gen/matchmaking/pb" + pbServ "github.com/walkline/ToCloud9/gen/servers-registry/pb" + pbGameServ "github.com/walkline/ToCloud9/gen/worldserver/pb" + "github.com/walkline/ToCloud9/shared/events" + "github.com/walkline/ToCloud9/shared/wow/guid" +) + +const ( + lfgRoleTank uint8 = 0x02 + lfgRoleHealer uint8 = 0x04 + lfgRoleDamage uint8 = 0x08 + + lfgUpdateTypeLeaderUnk1 uint8 = 1 + lfgUpdateTypeRolecheckAborted uint8 = 4 + lfgUpdateTypeJoinQueue uint8 = 5 + lfgUpdateTypeRemovedFromQueue uint8 = 7 + lfgUpdateTypeProposalFailed uint8 = 8 + lfgUpdateTypeProposalDeclined uint8 = 9 + lfgUpdateTypeGroupFound uint8 = 10 + lfgUpdateTypeAddedToQueue uint8 = 12 + lfgUpdateTypeProposalBegin uint8 = 13 + lfgUpdateTypeUpdateStatus uint8 = 14 + + lfgJoinOK uint32 = 0 + lfgJoinInternalError uint32 = 4 + + lfgRolecheckDefault uint32 = 0 + lfgRolecheckInitialited uint32 = 2 + + lfgDungeonIDMask uint32 = 0x00FFFFFF + + // Accepted proposal handling runs after every member has answered and may + // need to save, redirect, and relogin through a crossrealm dungeon owner. + lfgProposalAcceptedTimeout = 90 * time.Second + + lfgQueueUnavailableMessage = "Dungeon Finder queue was reset because the matchmaking service restarted. Please queue again." +) + +var errLfgNativeWorldportNoHandler = errors.New("source worldserver has no LFG teleport handler") + +func (s *GameSession) HandleLfgJoin(ctx context.Context, p *packet.Packet) error { + r := p.Reader() + roles := r.Uint32() + /*noPartialClear*/ _ = r.Uint8() + /*achievements*/ _ = r.Uint8() + + slotCount := r.Uint8() + dungeons := make([]uint32, 0, slotCount) + for i := uint8(0); i < slotCount; i++ { + dungeons = append(dungeons, r.Uint32()) + } + + needsCount := r.Uint8() + for i := uint8(0); i < needsCount; i++ { + _ = r.Uint8() + } + comment := r.String() + + members, memberOnline, ok, err := s.lfgMembersForJoin(ctx, uint8(roles)) + if err != nil { + return err + } + if !ok { + return nil + } + + validDungeons, result, err := s.validateLfgJoinMembers(ctx, dungeons, members, memberOnline) + if err != nil || result != pbMM.LfgJoinResult_LFG_JOIN_OK { + if err != nil { + s.sendLfgJoinResult(lfgJoinInternalError, lfgRolecheckDefault) + return err + } + s.sendLfgJoinResult(uint32(result), lfgRolecheckDefault) + return nil + } + + res, err := s.matchmakingServiceClient.JoinLfg(ctx, &pbMM.JoinLfgRequest{ + Api: root.SupportedMatchmakingServiceVer, + RealmID: root.RealmID, + LeaderGUID: s.character.GUID, + Members: members, + DungeonEntries: validDungeons, + Comment: comment, + }) + if err != nil { + s.sendLfgJoinResult(lfgJoinInternalError, lfgRolecheckDefault) + return err + } + if res.GetResult() != pbMM.LfgJoinResult_LFG_JOIN_OK { + s.sendLfgJoinResult(uint32(res.GetResult()), lfgRolecheckDefault) + return nil + } + s.trackLfgPendingJoin() + + return nil +} + +func (s *GameSession) validateLfgJoinMembers(ctx context.Context, dungeons []uint32, members []*pbMM.LfgMember, memberOnline map[uint64]bool) ([]uint32, pbMM.LfgJoinResult, error) { + validDungeons := append([]uint32(nil), dungeons...) + for _, member := range members { + if online, ok := memberOnline[lfgMemberServiceGUID(member.GetRealmID(), member.GetPlayerGUID())]; ok && !online { + return nil, pbMM.LfgJoinResult_LFG_JOIN_DISCONNECTED, nil + } + } + + placements := map[uint64]*pbGroup.MemberPlacement{} + remoteMembers := make([]uint64, 0, len(members)) + for _, member := range members { + memberRealmID := lfgPlayerRealmID(member.GetRealmID(), member.GetPlayerGUID()) + memberGUID := lfgMemberServiceGUID(member.GetRealmID(), member.GetPlayerGUID()) + if guid.SamePlayer(root.RealmID, s.character.GUID, memberRealmID, member.GetPlayerGUID()) { + continue + } + remoteMembers = append(remoteMembers, memberGUID) + } + if len(remoteMembers) > 0 { + if s.groupServiceClient == nil { + return nil, pbMM.LfgJoinResult_LFG_JOIN_PARTY_INFO_FAILED, nil + } + res, err := s.groupServiceClient.GetMemberPlacements(ctx, &pbGroup.GetMemberPlacementsRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + MemberGUIDs: remoteMembers, + }) + if err != nil { + return nil, pbMM.LfgJoinResult_LFG_JOIN_INTERNAL_ERROR, err + } + for _, placement := range res.GetPlacements() { + placements[placement.GetMemberGUID()] = placement + } + } + + localMemberIndex := -1 + for i, member := range members { + memberRealmID := lfgPlayerRealmID(member.GetRealmID(), member.GetPlayerGUID()) + if guid.SamePlayer(root.RealmID, s.character.GUID, memberRealmID, member.GetPlayerGUID()) { + localMemberIndex = i + break + } + } + if localMemberIndex < 0 { + return nil, pbMM.LfgJoinResult_LFG_JOIN_PARTY_INFO_FAILED, nil + } + + orderedMembers := make([]*pbMM.LfgMember, 0, len(members)) + orderedMembers = append(orderedMembers, members[localMemberIndex]) + for i, member := range members { + if i != localMemberIndex { + orderedMembers = append(orderedMembers, member) + } + } + + for _, member := range orderedMembers { + memberRealmID := lfgPlayerRealmID(member.GetRealmID(), member.GetPlayerGUID()) + memberLowGUID := guid.PlayerLowGUID(member.GetPlayerGUID()) + isLocalPlayer := guid.SamePlayer(root.RealmID, s.character.GUID, memberRealmID, member.GetPlayerGUID()) + client := s.gameServerGRPCClient + checkPlayerGUID := memberLowGUID + checkDungeons := validDungeons + checksCurrentWorldserver := true + lockResult := pbMM.LfgJoinResult_LFG_JOIN_NOT_MEET_REQS + if isLocalPlayer { + checkDungeons = dungeons + } else { + memberServiceGUID := lfgMemberServiceGUID(memberRealmID, member.GetPlayerGUID()) + placement := placements[memberServiceGUID] + lockResult = pbMM.LfgJoinResult_LFG_JOIN_PARTY_NOT_MEET_REQS + if placement == nil || !placement.GetFresh() || placement.GetWorldserverID() == "" { + return nil, pbMM.LfgJoinResult_LFG_JOIN_PARTY_INFO_FAILED, nil + } + if !placement.GetOnline() { + return nil, pbMM.LfgJoinResult_LFG_JOIN_DISCONNECTED, nil + } + member.WorldserverID = placement.GetWorldserverID() + if member.WorldserverID != "" && member.WorldserverID != s.worldserverID { + var err error + var server *pbServ.GameServerDetailed + client, server, err = s.lfgGameServerClientAndServerByWorldserverID(ctx, memberRealmID, member.WorldserverID) + if err != nil { + return nil, pbMM.LfgJoinResult_LFG_JOIN_INTERNAL_ERROR, err + } + if lfgGameServerIsCrossRealm(server) { + checkPlayerGUID = guid.PlayerGUIDForRealm(0, memberRealmID, memberLowGUID) + } + checksCurrentWorldserver = false + } + } + if checksCurrentWorldserver { + checkPlayerGUID = s.lfgPlayerGUIDForCurrentWorldserver(memberRealmID, memberLowGUID) + } + + lockInfo, result, err := s.validateLfgPlayerLocks(ctx, checkPlayerGUID, checkDungeons, client, lockResult) + if err != nil || result != pbMM.LfgJoinResult_LFG_JOIN_OK { + return nil, result, err + } + if isLocalPlayer && len(lockInfo.GetValidDungeonEntries()) > 0 { + validDungeons = append([]uint32(nil), lockInfo.GetValidDungeonEntries()...) + } + } + + return validDungeons, pbMM.LfgJoinResult_LFG_JOIN_OK, nil +} + +func lfgMemberServiceGUID(memberRealmID uint32, playerGUID uint64) uint64 { + return guid.PlayerGUIDForRealm(root.RealmID, lfgPlayerRealmID(memberRealmID, playerGUID), playerGUID) +} + +func lfgPlayerRealmID(memberRealmID uint32, playerGUID uint64) uint32 { + return guid.PlayerRealmIDOrDefault(lfgMemberRealmID(memberRealmID), playerGUID) +} + +func (s *GameSession) lfgPlayerGUIDForCurrentWorldserver(memberRealmID uint32, playerGUID uint64) uint64 { + playerLowGUID := guid.PlayerLowGUID(playerGUID) + if s.currentMapTransferRouting != nil && s.currentMapTransferRouting.isCrossRealm && s.currentMapTransferRouting.realmID == 0 { + return guid.PlayerGUIDForRealm(0, lfgMemberRealmID(memberRealmID), playerLowGUID) + } + return playerLowGUID +} + +func (s *GameSession) validateLfgPlayerLocks(ctx context.Context, playerGUID uint64, dungeons []uint32, client pbGameServ.WorldServerServiceClient, lockedResult pbMM.LfgJoinResult) (*pbGameServ.GetLfgPlayerLockInfoResponse, pbMM.LfgJoinResult, error) { + if client == nil { + return nil, pbMM.LfgJoinResult_LFG_JOIN_INTERNAL_ERROR, nil + } + + res, err := client.GetLfgPlayerLockInfo(ctx, &pbGameServ.GetLfgPlayerLockInfoRequest{ + Api: root.SupportedGameServerVer, + PlayerGUID: playerGUID, + DungeonEntries: dungeons, + }) + if err != nil { + return nil, pbMM.LfgJoinResult_LFG_JOIN_INTERNAL_ERROR, err + } + if res == nil { + return nil, pbMM.LfgJoinResult_LFG_JOIN_INTERNAL_ERROR, nil + } + + switch res.GetStatus() { + case pbGameServ.GetLfgPlayerLockInfoResponse_Success: + if result := lfgJoinRestrictionResult(res.GetJoinResult(), lockedResult); result != pbMM.LfgJoinResult_LFG_JOIN_OK { + return res, result, nil + } + if len(res.GetLocks()) > 0 { + return res, lockedResult, nil + } + return res, pbMM.LfgJoinResult_LFG_JOIN_OK, nil + case pbGameServ.GetLfgPlayerLockInfoResponse_PlayerNotFound: + return res, pbMM.LfgJoinResult_LFG_JOIN_PARTY_INFO_FAILED, nil + default: + return res, pbMM.LfgJoinResult_LFG_JOIN_INTERNAL_ERROR, nil + } +} + +func lfgJoinRestrictionResult(joinResult uint32, lockedResult pbMM.LfgJoinResult) pbMM.LfgJoinResult { + result := pbMM.LfgJoinResult(joinResult) + if result == pbMM.LfgJoinResult_LFG_JOIN_OK { + return pbMM.LfgJoinResult_LFG_JOIN_OK + } + if lockedResult != pbMM.LfgJoinResult_LFG_JOIN_PARTY_NOT_MEET_REQS { + return result + } + + switch result { + case pbMM.LfgJoinResult_LFG_JOIN_DESERTER: + return pbMM.LfgJoinResult_LFG_JOIN_PARTY_DESERTER + case pbMM.LfgJoinResult_LFG_JOIN_RANDOM_COOLDOWN: + return pbMM.LfgJoinResult_LFG_JOIN_PARTY_RANDOM_COOLDOWN + case pbMM.LfgJoinResult_LFG_JOIN_NOT_MEET_REQS: + return pbMM.LfgJoinResult_LFG_JOIN_PARTY_NOT_MEET_REQS + default: + return result + } +} + +func (s *GameSession) lfgGameServerClientAndServerByWorldserverID(ctx context.Context, realmID uint32, worldserverID string) (pbGameServ.WorldServerServiceClient, *pbServ.GameServerDetailed, error) { + server, err := s.lfgGameServerByWorldserverID(ctx, realmID, worldserverID) + if err != nil { + return nil, nil, err + } + if s.gameServerGRPCConnMgr == nil { + return nil, nil, fmt.Errorf("game server grpc connection manager is nil") + } + s.gameServerGRPCConnMgr.AddAddressMapping(server.GetAddress(), server.GetGrpcAddress()) + client, err := s.gameServerGRPCConnMgr.GRPCConnByGameServerAddress(server.GetAddress()) + if err != nil { + return nil, nil, fmt.Errorf("get game server grpc client for worldserver %q failed: %w", worldserverID, err) + } + return client, server, nil +} + +func (s *GameSession) lfgGameServerByWorldserverID(ctx context.Context, realmID uint32, worldserverID string) (*pbServ.GameServerDetailed, error) { + if s.serversRegistryClient == nil { + return nil, fmt.Errorf("servers registry client is nil") + } + if realmID == 0 { + realmID = root.RealmID + } + + server, err := s.lfgFindGameServerByWorldserverID(ctx, realmID, false, worldserverID) + if err != nil { + return nil, err + } + if server != nil { + return server, nil + } + + server, err = s.lfgFindGameServerByWorldserverID(ctx, 0, true, worldserverID) + if err != nil { + return nil, err + } + if server != nil { + return server, nil + } + + return nil, fmt.Errorf("worldserver %q not found in registry", worldserverID) +} + +func (s *GameSession) lfgFindGameServerByWorldserverID(ctx context.Context, realmID uint32, crossRealm bool, worldserverID string) (*pbServ.GameServerDetailed, error) { + servers, err := s.serversRegistryClient.ListGameServersForRealm(ctx, &pbServ.ListGameServersForRealmRequest{ + Api: root.SupportedServerRegistryVer, + RealmID: realmID, + IsCrossRealm: crossRealm, + }) + if err != nil { + return nil, err + } + for _, server := range servers.GetGameServers() { + if server.GetID() != worldserverID { + continue + } + return server, nil + } + + return nil, nil +} + +func lfgGameServerIsCrossRealm(server *pbServ.GameServerDetailed) bool { + return server != nil && server.GetIsCrossRealm() +} + +func (s *GameSession) lfgMembersForJoin(ctx context.Context, roles uint8) ([]*pbMM.LfgMember, map[uint64]bool, bool, error) { + members := []*pbMM.LfgMember{{ + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + Roles: uint32(roles), + Leader: true, + WorldserverID: s.worldserverID, + }} + memberOnline := map[uint64]bool{ + lfgMemberServiceGUID(root.RealmID, s.character.GUID): true, + } + + if s.groupServiceClient == nil { + return members, memberOnline, true, nil + } + + groupResp, err := s.groupServiceClient.GetGroupByMember(ctx, &pbGroup.GetGroupByMemberRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + Player: s.character.GUID, + }) + if err != nil || groupResp == nil || groupResp.Group == nil || len(groupResp.Group.Members) == 0 { + return members, memberOnline, true, nil + } + + groupRealmID := lfgMemberRealmID(groupHomeRealmIDFromPB(groupResp.Group)) + if !guid.SamePlayer(groupRealmID, groupResp.Group.GetLeader(), root.RealmID, s.character.GUID) { + return nil, nil, false, nil + } + + members = make([]*pbMM.LfgMember, 0, len(groupResp.Group.Members)) + for _, member := range groupResp.Group.Members { + memberRealmID := groupMemberRealmID(groupRealmID, member) + memberLowGUID := guid.PlayerLowGUID(member.GetGuid()) + isLocalMember := guid.SamePlayer(memberRealmID, member.GetGuid(), root.RealmID, s.character.GUID) + + memberRoles := member.GetRoles() + if isLocalMember { + memberRoles = uint32(roles) + } + members = append(members, &pbMM.LfgMember{ + RealmID: memberRealmID, + PlayerGUID: memberLowGUID, + Roles: memberRoles, + Leader: guid.SamePlayer(groupRealmID, groupResp.Group.GetLeader(), memberRealmID, memberLowGUID), + WorldserverID: lfgJoinMemberWorldserverID(memberRealmID, memberLowGUID, root.RealmID, s.character.GUID, s.worldserverID), + }) + memberOnline[lfgMemberServiceGUID(memberRealmID, memberLowGUID)] = member.GetIsOnline() + } + + return members, memberOnline, true, nil +} + +func lfgJoinMemberWorldserverID(memberRealmID uint32, memberGUID uint64, localRealmID uint32, localGUID uint64, localWorldserverID string) string { + if guid.SamePlayer(memberRealmID, memberGUID, localRealmID, localGUID) { + return localWorldserverID + } + return "" +} + +func (s *GameSession) HandleLfgLeave(ctx context.Context, p *packet.Packet) error { + if _, err := s.forwardPacketToWorldserverIfLfgDungeon(ctx, p); err != nil { + return err + } + + return s.leaveMatchmakingLfg(ctx) +} + +func (s *GameSession) leaveMatchmakingLfg(ctx context.Context) error { + if s.matchmakingServiceClient == nil || s.character == nil { + return nil + } + + _, err := s.matchmakingServiceClient.LeaveLfg(ctx, &pbMM.LeaveLfgRequest{ + Api: root.SupportedMatchmakingServiceVer, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + }) + if err == nil { + if clearErr := s.clearUnboundLfgDungeonRoute(ctx, 0); clearErr != nil && s.logger != nil { + s.logger.Error().Err(clearErr).Uint64("character", s.character.GUID).Msg("failed to clear unbound LFG dungeon route") + } + s.clearLfgDungeonTransport() + } + return err +} + +func (s *GameSession) HandleLfgSetRoles(ctx context.Context, p *packet.Packet) error { + roles := p.Reader().Uint8() + _, err := s.matchmakingServiceClient.SetLfgRoles(ctx, &pbMM.SetLfgRolesRequest{ + Api: root.SupportedMatchmakingServiceVer, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + Roles: uint32(roles), + }) + if err != nil { + return err + } + return nil +} + +func (s *GameSession) HandleLfgProposalResult(ctx context.Context, p *packet.Packet) error { + r := p.Reader() + proposalID := r.Uint32() + accept := r.Uint8() != 0 + + _, err := s.matchmakingServiceClient.AnswerLfgProposal(ctx, &pbMM.AnswerLfgProposalRequest{ + Api: root.SupportedMatchmakingServiceVer, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + ProposalID: proposalID, + Accept: accept, + }) + if err != nil { + return err + } + return nil +} + +func (s *GameSession) HandleLfgGetStatus(ctx context.Context, p *packet.Packet) error { + if s.lfgDungeonActive && s.worldSocket != nil { + s.sendNativeLfgStatusRefresh(ctx) + s.forwardLfgPacketToWorldserver(p) + return nil + } + + res, err := s.matchmakingServiceClient.LfgStatus(ctx, &pbMM.LfgStatusRequest{ + Api: root.SupportedMatchmakingServiceVer, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + }) + if err != nil { + return err + } + + if lfgProtoStateUsesNativeDungeon(res.GetStatus().GetState()) && s.worldSocket != nil { + s.lfgDungeonActive = true + status := lfgStatusPayloadFromProto(res.Status) + if err := s.persistLfgDungeonRouteFromStatus(ctx, status); err != nil { + return err + } + s.trackLfgStatus(status) + s.sendLfgStatusRefresh(status) + s.forwardLfgPacketToWorldserver(p) + return nil + } + + status := lfgStatusPayloadFromProto(res.Status) + if err := s.persistOrClearLfgDungeonRouteFromStatus(ctx, status); err != nil { + return err + } + s.trackLfgStatus(status) + s.sendLfgStatusRefresh(status) + return nil +} + +func (s *GameSession) HandleLfgTeleport(ctx context.Context, p *packet.Packet) error { + if s.gameServerGRPCClient == nil || s.character == nil { + s.forwardLfgPacketToWorldserver(p) + return nil + } + + out := p.Reader().Uint8() != 0 + out = s.normalizeLfgTeleportDirection(out) + playerGUID := s.lfgControlPlayerGUID() + dungeonEntry, transferRouting := s.lfgTeleportDungeonEntry(ctx, out) + var routing *mapTransferRouting + if !out && dungeonEntry != 0 { + routing = transferRouting + } + err := s.startLfgNativeWorldportTransport(ctx, lfgNativeWorldportRequest{ + operation: "LFG teleport", + playerGUID: playerGUID, + out: out, + dungeonEntry: dungeonEntry, + routing: routing, + }) + if errors.Is(err, errLfgNativeWorldportNoHandler) { + s.forwardLfgPacketToWorldserver(p) + return nil + } + return err +} + +func (s *GameSession) InterceptLfgTeleportDenied(ctx context.Context, p *packet.Packet) error { + s.clearPendingMapTransferRouting() + if s.teleportingToNewMap == nil { + s.clearActiveMapTransferRouting() + } + s.gameSocket.SendPacket(p) + return nil +} + +func (s *GameSession) InterceptLfgUpdateParty(ctx context.Context, p *packet.Packet) error { + s.syncNativeLfgMemberLeaveFromPartyUpdate(ctx, p) + s.gameSocket.SendPacket(p) + return nil +} + +func (s *GameSession) syncNativeLfgMemberLeaveFromPartyUpdate(ctx context.Context, p *packet.Packet) { + if s == nil || s.character == nil || s.groupServiceClient == nil || !s.lfgDungeonActive || p == nil || p.Source != packet.SourceWorldServer { + return + } + + r := p.Reader() + updateType := r.Uint8() + if err := r.Error(); err != nil { + if s.logger != nil { + s.logger.Warn().Err(err).Msg("failed to parse native LFG party update") + } + return + } + if updateType != lfgUpdateTypeLeaderUnk1 { + return + } + + _, err := s.groupServiceClient.Leave(ctx, &pbGroup.GroupLeaveParams{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + Player: s.character.GUID, + }) + if err != nil && s.logger != nil { + s.logger.Warn().Err(err).Uint64("playerGUID", s.character.GUID).Msg("failed to mirror native LFG member removal to groupservice") + } +} + +type lfgNativeWorldportRequest struct { + operation string + playerGUID uint64 + out bool + dungeonEntry uint32 + routing *mapTransferRouting +} + +func (s *GameSession) startLfgNativeWorldportTransport(ctx context.Context, req lfgNativeWorldportRequest) error { + if s.gameServerGRPCClient == nil { + return fmt.Errorf("source worldserver grpc client is nil") + } + + return s.startClusterNativeWorldportTransport(ctx, clusterNativeWorldportTransport{ + feature: clusterTransferFeatureLFG, + operation: req.operation, + playerGUID: req.playerGUID, + routing: req.routing, + reloadManagedGroupAfterTransfer: req.routing != nil, + start: func(ctx context.Context) error { + res, err := s.gameServerGRPCClient.TeleportLfgPlayer(ctx, &pbGameServ.TeleportLfgPlayerRequest{ + Api: root.SupportedGameServerVer, + PlayerGUID: req.playerGUID, + Out: req.out, + DungeonEntry: req.dungeonEntry, + }) + if err != nil { + return err + } + return lfgNativeWorldportResponseError(req, res) + }, + }) +} + +func lfgNativeWorldportResponseError(req lfgNativeWorldportRequest, res *pbGameServ.TeleportLfgPlayerResponse) error { + if res == nil { + return fmt.Errorf("%s for player %d returned nil response", req.operation, req.playerGUID) + } + + switch res.GetStatus() { + case pbGameServ.TeleportLfgPlayerResponse_Success: + return nil + case pbGameServ.TeleportLfgPlayerResponse_NoHandler: + return fmt.Errorf("%w for player %d", errLfgNativeWorldportNoHandler, req.playerGUID) + case pbGameServ.TeleportLfgPlayerResponse_PlayerNotFound: + return fmt.Errorf("source worldserver cannot find LFG player %d", req.playerGUID) + default: + return fmt.Errorf("%s for player %d returned status %s", req.operation, req.playerGUID, res.GetStatus().String()) + } +} + +func (s *GameSession) HandleLfgSetBootVote(ctx context.Context, p *packet.Packet) error { + if s.gameServerGRPCClient == nil || s.character == nil { + s.forwardLfgPacketToWorldserver(p) + return nil + } + + agree := p.Reader().Uint8() != 0 + playerGUID := s.lfgControlPlayerGUID() + res, err := s.gameServerGRPCClient.SetLfgBootVote(ctx, &pbGameServ.SetLfgBootVoteRequest{ + Api: root.SupportedGameServerVer, + PlayerGUID: playerGUID, + Agree: agree, + }) + if err != nil { + return err + } + if res == nil { + return fmt.Errorf("lfg boot vote for player %d returned nil response", playerGUID) + } + + switch res.GetStatus() { + case pbGameServ.SetLfgBootVoteResponse_Success: + return nil + case pbGameServ.SetLfgBootVoteResponse_NoHandler: + s.forwardLfgPacketToWorldserver(p) + return nil + case pbGameServ.SetLfgBootVoteResponse_PlayerNotFound: + return fmt.Errorf("lfg boot vote player %d not found", playerGUID) + default: + return fmt.Errorf("lfg boot vote for player %d returned status %s", playerGUID, res.GetStatus().String()) + } +} + +func (s *GameSession) HandleLfgPlayerLockInfoRequest(ctx context.Context, p *packet.Packet) error { + if s.gameServerGRPCClient == nil || s.character == nil { + s.forwardLfgPacketToWorldserver(p) + return nil + } + + playerGUID := s.lfgControlPlayerGUID() + res, err := s.getLfgPlayerInfo(ctx, playerGUID) + if err != nil { + return err + } + if res.GetStatus() == pbGameServ.GetLfgPlayerInfoResponse_PlayerNotFound { + localGUID := guid.PlayerLowGUID(playerGUID) + if localGUID != 0 && localGUID != playerGUID { + localRes, localErr := s.getLfgPlayerInfo(ctx, localGUID) + if localErr != nil { + return localErr + } + if localRes.GetStatus() != pbGameServ.GetLfgPlayerInfoResponse_PlayerNotFound { + playerGUID = localGUID + res = localRes + } + } + } + + switch res.GetStatus() { + case pbGameServ.GetLfgPlayerInfoResponse_Success: + s.sendLfgPlayerInfo(res) + return nil + case pbGameServ.GetLfgPlayerInfoResponse_NoHandler: + s.forwardLfgPacketToWorldserver(p) + return nil + case pbGameServ.GetLfgPlayerInfoResponse_PlayerNotFound: + return fmt.Errorf("lfg player info player %d not found", playerGUID) + default: + return fmt.Errorf("lfg player info for player %d returned status %s", playerGUID, res.GetStatus().String()) + } +} + +func (s *GameSession) getLfgPlayerInfo(ctx context.Context, playerGUID uint64) (*pbGameServ.GetLfgPlayerInfoResponse, error) { + res, err := s.gameServerGRPCClient.GetLfgPlayerInfo(ctx, &pbGameServ.GetLfgPlayerInfoRequest{ + Api: root.SupportedGameServerVer, + PlayerGUID: playerGUID, + }) + if err != nil { + return nil, err + } + if res == nil { + return nil, fmt.Errorf("lfg player info for player %d returned nil response", playerGUID) + } + return res, nil +} + +func (s *GameSession) HandleLfgPartyLockInfoRequest(ctx context.Context, p *packet.Packet) error { + if s.groupServiceClient == nil || s.gameServerGRPCClient == nil { + s.forwardLfgPacketToWorldserver(p) + return nil + } + + groupResp, err := s.groupServiceClient.GetGroupByMember(ctx, &pbGroup.GetGroupByMemberRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + Player: s.character.GUID, + }) + if err != nil { + return err + } + if groupResp == nil || groupResp.Group == nil { + s.forwardLfgPacketToWorldserver(p) + return nil + } + + groupRealmID := lfgMemberRealmID(groupHomeRealmIDFromPB(groupResp.Group)) + memberGUIDs := make([]uint64, 0, len(groupResp.Group.Members)) + memberRealms := make(map[uint64]uint32, len(groupResp.Group.Members)) + memberLows := make(map[uint64]uint64, len(groupResp.Group.Members)) + for _, member := range groupResp.Group.Members { + memberRealmID := groupMemberRealmID(groupRealmID, member) + memberLowGUID := guid.PlayerLowGUID(member.GetGuid()) + if guid.SamePlayer(memberRealmID, member.GetGuid(), root.RealmID, s.character.GUID) || !member.GetIsOnline() { + continue + } + memberServiceGUID := lfgMemberServiceGUID(memberRealmID, memberLowGUID) + memberGUIDs = append(memberGUIDs, memberServiceGUID) + memberRealms[memberServiceGUID] = memberRealmID + memberLows[memberServiceGUID] = memberLowGUID + } + if len(memberGUIDs) == 0 { + s.sendLfgPartyInfo(nil) + return nil + } + + placements := map[uint64]*pbGroup.MemberPlacement{} + placementResp, err := s.groupServiceClient.GetMemberPlacements(ctx, &pbGroup.GetMemberPlacementsRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + MemberGUIDs: memberGUIDs, + }) + if err != nil { + return err + } + for _, placement := range placementResp.GetPlacements() { + placements[placement.GetMemberGUID()] = placement + } + + memberLocks := make([]lfgPartyMemberLocks, 0, len(memberGUIDs)) + for _, memberServiceGUID := range memberGUIDs { + memberRealmID := memberRealms[memberServiceGUID] + memberLowGUID := memberLows[memberServiceGUID] + client := s.gameServerGRPCClient + checkPlayerGUID := s.lfgPlayerGUIDForCurrentWorldserver(memberRealmID, memberLowGUID) + if placement := placements[memberServiceGUID]; placement != nil { + if !placement.GetFresh() { + if memberRealmID != root.RealmID { + continue + } + } else if !placement.GetOnline() { + continue + } else if placement.GetWorldserverID() != "" && placement.GetWorldserverID() != s.worldserverID { + var server *pbServ.GameServerDetailed + client, server, err = s.lfgGameServerClientAndServerByWorldserverID(ctx, memberRealmID, placement.GetWorldserverID()) + if err != nil { + return err + } + if lfgGameServerIsCrossRealm(server) { + checkPlayerGUID = guid.PlayerGUIDForRealm(0, memberRealmID, memberLowGUID) + } + } else if placement.GetWorldserverID() == "" && memberRealmID != root.RealmID { + continue + } + } else if memberRealmID != root.RealmID { + continue + } + + res, err := s.lfgPlayerLockInfo(ctx, checkPlayerGUID, nil, client) + if err != nil { + return err + } + if res == nil || res.GetStatus() == pbGameServ.GetLfgPlayerLockInfoResponse_PlayerNotFound { + continue + } + if res.GetStatus() != pbGameServ.GetLfgPlayerLockInfoResponse_Success { + return fmt.Errorf("lfg party lock info for player %d returned status %s", memberLowGUID, res.GetStatus().String()) + } + + memberLocks = append(memberLocks, lfgPartyMemberLocks{ + realmID: memberRealmID, + playerGUID: memberLowGUID, + locks: res.GetLocks(), + }) + } + + s.sendLfgPartyInfo(memberLocks) + return nil +} + +type lfgPartyMemberLocks struct { + realmID uint32 + playerGUID uint64 + locks []*pbGameServ.LfgDungeonLock +} + +func (s *GameSession) lfgPlayerLockInfo(ctx context.Context, playerGUID uint64, dungeons []uint32, client pbGameServ.WorldServerServiceClient) (*pbGameServ.GetLfgPlayerLockInfoResponse, error) { + if client == nil { + return nil, nil + } + + return client.GetLfgPlayerLockInfo(ctx, &pbGameServ.GetLfgPlayerLockInfoRequest{ + Api: root.SupportedGameServerVer, + PlayerGUID: playerGUID, + DungeonEntries: dungeons, + }) +} + +func (s *GameSession) sendLfgPartyInfo(members []lfgPartyMemberLocks) { + resp := packet.NewWriter(packet.SMsgLFGPartyInfo) + resp.Uint8(uint8(len(members))) + for _, member := range members { + resp.Uint64(playerObjectGUIDForRealm(lfgMemberRealmID(member.realmID), member.playerGUID)) + resp.Uint32(uint32(len(member.locks))) + for _, lock := range member.locks { + resp.Uint32(lock.GetDungeonEntry()) + resp.Uint32(lock.GetLockStatus()) + } + } + s.gameSocket.Send(resp) +} + +func (s *GameSession) sendLfgPlayerInfo(info *pbGameServ.GetLfgPlayerInfoResponse) { + resp := packet.NewWriter(packet.SMsgLFGPlayerInfo) + resp.Uint8(uint8(len(info.GetRandomDungeons()))) + for _, dungeon := range info.GetRandomDungeons() { + resp.Uint32(dungeon.GetDungeonEntry()) + resp.Uint8(boolToUint8(dungeon.GetDone())) + resp.Uint32(dungeon.GetRewardMoney()) + resp.Uint32(dungeon.GetRewardXP()) + resp.Uint32(dungeon.GetRewardUnknown1()) + resp.Uint32(dungeon.GetRewardUnknown2()) + resp.Uint8(uint8(len(dungeon.GetRewardItems()))) + for _, item := range dungeon.GetRewardItems() { + resp.Uint32(item.GetItemID()) + resp.Uint32(item.GetDisplayID()) + resp.Uint32(item.GetCount()) + } + } + resp.Uint32(uint32(len(info.GetLocks()))) + for _, lock := range info.GetLocks() { + resp.Uint32(lock.GetDungeonEntry()) + resp.Uint32(lock.GetLockStatus()) + } + s.gameSocket.Send(resp) +} + +func (s *GameSession) forwardLfgPacketToWorldserver(p *packet.Packet) { + if s.worldSocket != nil && p != nil { + s.worldSocket.WriteChannel() <- p + } +} + +func (s *GameSession) forwardPacketToWorldserverIfLfgDungeon(ctx context.Context, p *packet.Packet) (bool, error) { + if s.worldSocket == nil || s.character == nil || p == nil { + return false, nil + } + + if s.lfgDungeonActive { + s.forwardLfgPacketToWorldserver(p) + return true, nil + } + + if s.matchmakingServiceClient == nil { + return false, nil + } + + status, err := s.matchmakingServiceClient.LfgStatus(ctx, &pbMM.LfgStatusRequest{ + Api: root.SupportedMatchmakingServiceVer, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + }) + if err != nil || status == nil || !lfgProtoStateUsesNativeDungeon(status.GetStatus().GetState()) { + return false, nil + } + + s.lfgDungeonActive = true + s.forwardLfgPacketToWorldserver(p) + return true, nil +} + +func (s *GameSession) HandleEventMMLfgStatusChanged(ctx context.Context, e *eBroadcaster.Event) error { + eventData := e.Payload.(*events.MatchmakingEventLfgStatusChangedPayload) + previousStatus := s.currentLfgMatchmakingStatus() + wasDungeonActive := s.lfgDungeonActive + s.sendLfgStatus(previousStatus, eventData.Status, wasDungeonActive) + if err := s.persistOrClearLfgDungeonRouteFromStatus(ctx, eventData.Status); err != nil { + return err + } + s.trackLfgStatus(eventData.Status) + return nil +} + +func (s *GameSession) HandleEventMMLfgProposalAccepted(ctx context.Context, e *eBroadcaster.Event) error { + eventData := e.Payload.(*events.MatchmakingEventLfgProposalAcceptedPayload) + if eventData == nil || eventData.LeaderWorldserverID == "" || s.character == nil { + return nil + } + if !lfgProposalAcceptedTargetsPlayer(eventData, s.character.GUID) { + return nil + } + + opCtx, cancel := s.lfgProposalAcceptedOperationContext(ctx) + defer cancel() + + targetServer, dungeonInfo, err := s.lfgProposalAcceptedTargetServer(opCtx, eventData) + if err != nil { + return err + } + dungeonMapID := dungeonInfo.GetMapID() + effectiveDungeonEntry := lfgEffectiveDungeonEntry(eventData.DungeonEntry, dungeonInfo) + targetWorldserverID := targetServer.GetId() + if targetWorldserverID == "" { + return fmt.Errorf("can't redirect LFG member %d: target worldserver is empty", s.character.GUID) + } + + transferRouting := &mapTransferRouting{ + realmID: lfgMaterializationRealmID(eventData), + isCrossRealm: eventData.CrossRealm || targetServer.GetIsCrossRealm(), + feature: clusterTransferFeatureLFG, + } + s.rememberLfgDungeonTransport(effectiveDungeonEntry, transferRouting, dungeonMapID, dungeonInfo.GetDifficulty()) + if err := s.recordLfgDungeonRoute(opCtx, effectiveDungeonEntry, dungeonMapID, dungeonInfo.GetDifficulty(), transferRouting, true); err != nil { + return err + } + + loginPlayerGUID := mapTransferLoginPlayerGUID(s.character.GUID, transferRouting) + if s.worldserverID == targetWorldserverID { + return s.startClusterOwnerNativeWorldportTransport(opCtx, clusterOwnerNativeWorldportTransport{ + feature: clusterTransferFeatureLFG, + operation: "accepted LFG dungeon transport", + sessionPlayerGUID: s.character.GUID, + loginPlayerGUID: loginPlayerGUID, + targetWorldserverID: targetWorldserverID, + routing: transferRouting, + reloadManagedGroupAfterTransfer: true, + }) + } + if s.worldSocket == nil { + return fmt.Errorf("can't redirect LFG member %d to worldserver %q: world socket is nil", s.character.GUID, targetWorldserverID) + } + targetAddress := targetServer.GetAddress() + if targetAddress == "" { + return fmt.Errorf("worldserver %q has empty address", targetWorldserverID) + } + if s.worldSocket.Address() == targetAddress { + return s.startClusterOwnerNativeWorldportTransport(opCtx, clusterOwnerNativeWorldportTransport{ + feature: clusterTransferFeatureLFG, + operation: "accepted LFG dungeon transport", + sessionPlayerGUID: s.character.GUID, + loginPlayerGUID: loginPlayerGUID, + targetAddress: targetAddress, + targetWorldserverID: targetWorldserverID, + routing: transferRouting, + reloadManagedGroupAfterTransfer: true, + }) + } + + var targetGRPCClient pbGameServ.WorldServerServiceClient + if s.gameServerGRPCConnMgr != nil { + s.gameServerGRPCConnMgr.AddAddressMapping(targetAddress, targetServer.GetGrpcAddress()) + targetGRPCClient, err = s.gameServerGRPCConnMgr.GRPCConnByGameServerAddress(targetAddress) + if err != nil { + return fmt.Errorf("can't resolve LFG target worldserver %q grpc client: %w", targetWorldserverID, err) + } + } + + return s.startClusterOwnerNativeWorldportTransport(opCtx, clusterOwnerNativeWorldportTransport{ + feature: clusterTransferFeatureLFG, + operation: "accepted LFG dungeon transport", + sessionPlayerGUID: s.character.GUID, + loginPlayerGUID: loginPlayerGUID, + targetAddress: targetAddress, + targetWorldserverID: targetWorldserverID, + routing: transferRouting, + reloadManagedGroupAfterTransfer: true, + onOwnerPlaced: func(context.Context) error { + if targetGRPCClient != nil { + s.gameServerGRPCClient = targetGRPCClient + } + return nil + }, + }) +} + +func (s *GameSession) lfgProposalAcceptedOperationContext(parent context.Context) (context.Context, context.CancelFunc) { + base := s.ctx + if base == nil { + base = parent + } + if base == nil { + base = context.Background() + } + return context.WithTimeout(base, lfgProposalAcceptedTimeout) +} + +func (s *GameSession) lfgProposalAcceptedTargetServer(ctx context.Context, eventData *events.MatchmakingEventLfgProposalAcceptedPayload) (*pbServ.Server, *pbGameServ.GetLfgDungeonInfoResponse, error) { + if s.gameServerGRPCClient == nil { + return nil, nil, fmt.Errorf("can't resolve LFG dungeon owner: worldserver grpc client is nil") + } + if s.serversRegistryClient == nil { + return nil, nil, fmt.Errorf("can't resolve LFG dungeon owner: servers registry client is nil") + } + + dungeonInfo, err := s.gameServerGRPCClient.GetLfgDungeonInfo(ctx, &pbGameServ.GetLfgDungeonInfoRequest{ + Api: root.SupportedGameServerVer, + DungeonEntry: eventData.DungeonEntry, + }) + if err != nil { + return nil, nil, fmt.Errorf("get LFG dungeon info for proposal %d failed: %w", eventData.ProposalID, err) + } + if dungeonInfo == nil { + return nil, nil, fmt.Errorf("get LFG dungeon info for proposal %d returned nil response", eventData.ProposalID) + } + if dungeonInfo.GetStatus() != pbGameServ.GetLfgDungeonInfoResponse_Success { + return nil, nil, fmt.Errorf("get LFG dungeon info for proposal %d returned status %s", eventData.ProposalID, dungeonInfo.GetStatus()) + } + + servers, err := s.serversRegistryClient.AvailableGameServersForMapAndRealm(ctx, &pbServ.AvailableGameServersForMapAndRealmRequest{ + Api: root.SupportedServerRegistryVer, + RealmID: lfgMaterializationRealmID(eventData), + MapID: dungeonInfo.GetMapID(), + IsCrossRealm: eventData.CrossRealm, + }) + if err != nil { + return nil, nil, fmt.Errorf("list game servers for LFG dungeon map %d in realm %d crossrealm=%t failed: %w", dungeonInfo.GetMapID(), lfgMaterializationRealmID(eventData), eventData.CrossRealm, err) + } + if servers == nil { + return nil, nil, fmt.Errorf("list game servers for LFG dungeon map %d in realm %d crossrealm=%t returned nil response", dungeonInfo.GetMapID(), lfgMaterializationRealmID(eventData), eventData.CrossRealm) + } + + target := chooseLFGProposalTargetServer(servers.GetGameServers(), eventData.LeaderWorldserverID) + if target == nil { + return nil, nil, fmt.Errorf("no worldserver available for LFG dungeon map %d in realm %d crossrealm=%t", dungeonInfo.GetMapID(), lfgMaterializationRealmID(eventData), eventData.CrossRealm) + } + return target, dungeonInfo, nil +} + +func chooseLFGProposalTargetServer(servers []*pbServ.Server, preferredWorldserverID string) *pbServ.Server { + for _, server := range servers { + if server.GetId() == preferredWorldserverID { + return server + } + } + + candidates := make([]*pbServ.Server, 0, len(servers)) + for _, server := range servers { + if server != nil && server.GetId() != "" { + candidates = append(candidates, server) + } + } + sort.Slice(candidates, func(i, j int) bool { + return candidates[i].GetId() < candidates[j].GetId() + }) + if len(candidates) == 0 { + return nil + } + return candidates[0] +} + +func lfgProposalAcceptedTargetsPlayer(payload *events.MatchmakingEventLfgProposalAcceptedPayload, playerGUID uint64) bool { + if payload != nil && len(payload.Members) > 0 { + for _, member := range payload.Members { + if lfgMemberRealmID(member.RealmID) == root.RealmID && uint64(member.PlayerGUID) == playerGUID { + return true + } + } + return false + } + for _, guid := range payload.PlayersGUID { + if uint64(guid) == playerGUID { + return true + } + } + return false +} + +func lfgMaterializationRealmID(payload *events.MatchmakingEventLfgProposalAcceptedPayload) uint32 { + if payload != nil && payload.CrossRealm { + return 0 + } + if payload == nil { + return root.RealmID + } + return payload.RealmID +} + +func (s *GameSession) lfgControlPlayerGUID() uint64 { + if s.character == nil { + return 0 + } + if s.lfgUsesCrossrealmOwner() { + return guid.PlayerGUIDForRealm(0, root.RealmID, s.character.GUID) + } + return s.character.GUID +} + +func (s *GameSession) lfgUsesCrossrealmOwner() bool { + return mapTransferRoutingUsesCrossrealmOwner(s.currentMapTransferRouting) || + mapTransferRoutingUsesCrossrealmOwner(s.activeMapTransferRouting) || + mapTransferRoutingUsesCrossrealmOwner(s.pendingMapTransferRouting) +} + +func (s *GameSession) normalizeLfgTeleportDirection(out bool) bool { + if out || !s.lfgDungeonActive || !s.lfgUsesCrossrealmOwner() { + return out + } + + if s.logger != nil { + s.logger.Debug(). + Uint32("accountID", s.accountID). + Uint64("playerGUID", s.character.GUID). + Msg("treating stale LFG dungeon enter request as dungeon exit") + } + return true +} + +func (s *GameSession) sendLfgStatus(previousStatus, status events.MatchmakingLfgStatusPayload, wasDungeonActive bool) { + s.lfgDungeonActive = lfgStateUsesNativeDungeon(status.State) + + switch status.State { + case events.MatchmakingLfgStateNone: + s.sendLfgNoneStatus(previousStatus, wasDungeonActive) + case events.MatchmakingLfgStateRoleCheck: + s.sendLfgRoleCheckUpdate(status) + case events.MatchmakingLfgStateQueued: + s.sendLfgJoinResult(lfgJoinOK, lfgRolecheckDefault) + if s.lfgQueuedStatusIsParty(status) { + s.sendLfgUpdateParty(lfgUpdateTypeAddedToQueue, true, true, status.SelectedDungeons) + } else { + s.sendLfgUpdatePlayer(lfgUpdateTypeJoinQueue, true, status.SelectedDungeons) + } + s.sendLfgQueueStatus(status) + case events.MatchmakingLfgStateProposal: + if status.ProposalState == events.MatchmakingLfgProposalFailed { + s.sendLfgProposalUpdate(status) + s.sendLfgProposalFailureStatus(status) + return + } + s.lastLfgProposalSuccessID = 0 + if s.lfgQueuedStatusIsParty(status) { + s.sendLfgUpdateParty(lfgUpdateTypeProposalBegin, true, false, status.SelectedDungeons) + } else { + s.sendLfgUpdatePlayer(lfgUpdateTypeProposalBegin, false, status.SelectedDungeons) + } + s.sendLfgProposalUpdate(status) + case events.MatchmakingLfgStateDungeon: + if status.ProposalID == 0 || s.lastLfgProposalSuccessID == status.ProposalID { + return + } + s.lastLfgProposalSuccessID = status.ProposalID + s.sendLfgProposalUpdate(status) + if s.lfgQueuedStatusIsParty(status) { + s.sendLfgUpdateParty(lfgUpdateTypeGroupFound, false, false, nil) + } else { + s.sendLfgUpdatePlayer(lfgUpdateTypeGroupFound, false, nil) + } + s.sendLfgUpdatePlayer(lfgUpdateTypeRemovedFromQueue, false, nil) + s.sendLfgUpdateParty(lfgUpdateTypeRemovedFromQueue, false, false, nil) + s.sendLfgStatusRefresh(status) + case events.MatchmakingLfgStateFinishedDungeon: + return + } +} + +func (s *GameSession) currentLfgMatchmakingStatus() events.MatchmakingLfgStatusPayload { + if s.character == nil { + return events.MatchmakingLfgStatusPayload{} + } + return s.character.lastLfgStatus +} + +func (s *GameSession) sendLfgNoneStatus(previousStatus events.MatchmakingLfgStatusPayload, wasDungeonActive bool) { + if wasDungeonActive { + return + } + + switch previousStatus.State { + case events.MatchmakingLfgStateDungeon, events.MatchmakingLfgStateFinishedDungeon: + return + case events.MatchmakingLfgStateRoleCheck: + s.sendLfgUpdateParty(lfgUpdateTypeRolecheckAborted, false, false, nil) + case events.MatchmakingLfgStateQueued: + if s.lfgQueuedStatusIsParty(previousStatus) { + s.sendLfgUpdateParty(lfgUpdateTypeRemovedFromQueue, false, false, nil) + } else { + s.sendLfgUpdatePlayer(lfgUpdateTypeRemovedFromQueue, false, nil) + } + case events.MatchmakingLfgStateProposal, events.MatchmakingLfgStateBoot: + s.sendLfgUpdateParty(lfgUpdateTypeProposalFailed, false, false, nil) + default: + s.sendLfgUpdatePlayer(lfgUpdateTypeRemovedFromQueue, false, nil) + s.sendLfgUpdateParty(lfgUpdateTypeRolecheckAborted, false, false, nil) + } +} + +func (s *GameSession) sendLfgProposalFailureStatus(status events.MatchmakingLfgStatusPayload) { + localLeader, ok := s.lfgLocalQueueLeader(status) + if !ok { + s.sendLfgUpdateParty(lfgProposalFailureUpdateType(status), false, false, nil) + return + } + + removedLeaders := lfgProposalRemovedQueueLeaders(status) + if _, removed := removedLeaders[localLeader]; removed { + updateType := lfgUpdateTypeRemovedFromQueue + if s.lfgLocalProposalMemberFailed(status) { + updateType = lfgProposalFailureUpdateType(status) + } + if s.lfgQueuedStatusIsParty(status) { + s.sendLfgUpdateParty(updateType, false, false, nil) + } else { + s.sendLfgUpdatePlayer(updateType, false, nil) + } + return + } + + if s.lfgQueuedStatusIsParty(status) { + s.sendLfgUpdateParty(lfgUpdateTypeAddedToQueue, true, true, status.SelectedDungeons) + return + } + s.sendLfgUpdatePlayer(lfgUpdateTypeAddedToQueue, true, status.SelectedDungeons) +} + +func (s *GameSession) trackLfgPendingJoin() { + if s.character == nil { + return + } + + s.character.lfgPendingJoin = true + s.clearLfgDungeonTransport() +} + +func (s *GameSession) trackLfgStatus(status events.MatchmakingLfgStatusPayload) { + if s.character == nil { + return + } + + s.character.lfgPendingJoin = false + if lfgStateUsesNativeDungeon(status.State) { + s.rememberLfgDungeonTransport(status.DungeonEntry, lfgStatusTransportRouting(status)) + s.character.lfgMatchmakingActive = false + s.character.lastLfgStatus = status + return + } + if status.State == events.MatchmakingLfgStateNone { + s.clearLfgDungeonTransport() + } + if lfgMatchmakingStatusIsActive(status.State) { + s.character.lfgMatchmakingActive = true + s.character.lastLfgStatus = status + return + } + + s.clearLfgMatchmakingTracking() +} + +func (s *GameSession) sendNativeLfgStatusRefresh(ctx context.Context) { + if s.gameSocket == nil { + return + } + + if status, ok := s.currentNativeLfgStatus(); ok { + s.sendLfgStatusRefresh(status) + return + } + + if s.matchmakingServiceClient == nil || s.character == nil { + return + } + + res, err := s.matchmakingServiceClient.LfgStatus(ctx, &pbMM.LfgStatusRequest{ + Api: root.SupportedMatchmakingServiceVer, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + }) + if err != nil { + return + } + + status := lfgStatusPayloadFromProto(res.GetStatus()) + if !lfgStateUsesNativeDungeon(status.State) { + return + } + + s.trackLfgStatus(status) + s.sendLfgStatusRefresh(status) +} + +func (s *GameSession) currentNativeLfgStatus() (events.MatchmakingLfgStatusPayload, bool) { + if s.character == nil { + return events.MatchmakingLfgStatusPayload{}, false + } + + status := s.character.lastLfgStatus + if !lfgStateUsesNativeDungeon(status.State) || len(status.SelectedDungeons) == 0 { + return events.MatchmakingLfgStatusPayload{}, false + } + + return status, true +} + +func (s *GameSession) clearLfgMatchmakingTracking() { + if s.character == nil { + return + } + + s.character.lfgPendingJoin = false + s.character.lfgMatchmakingActive = false + s.character.lastLfgStatus = events.MatchmakingLfgStatusPayload{} +} + +func (s *GameSession) rememberLfgDungeonTransport(dungeonEntry uint32, routing *mapTransferRouting, mapAndDifficulty ...uint32) { + if s.character == nil || dungeonEntry == 0 { + return + } + + var mapID uint32 + var difficulty uint32 + if len(mapAndDifficulty) > 0 { + mapID = mapAndDifficulty[0] + } + if len(mapAndDifficulty) > 1 { + difficulty = mapAndDifficulty[1] + } + if current := s.character.lfgDungeonTransport; current != nil { + if routing == nil { + routing = current.routing + } + if mapID == 0 && current.dungeonEntry == dungeonEntry { + mapID = current.mapID + } + if difficulty == 0 && current.dungeonEntry == dungeonEntry { + difficulty = current.difficulty + } + } + + s.character.lfgDungeonTransport = &lfgDungeonTransportState{ + dungeonEntry: dungeonEntry, + mapID: mapID, + difficulty: difficulty, + routing: cloneMapTransferRouting(routing), + } +} + +func (s *GameSession) persistOrClearLfgDungeonRouteFromStatus(ctx context.Context, status events.MatchmakingLfgStatusPayload) error { + if status.State == events.MatchmakingLfgStateNone { + if s != nil && s.character != nil && s.character.lfgDungeonTransport != nil && mapTransferRoutingUsesCrossrealmOwner(s.character.lfgDungeonTransport.routing) { + return nil + } + return s.clearUnboundLfgDungeonRoute(ctx, 0) + } + return s.persistLfgDungeonRouteFromStatus(ctx, status) +} + +func (s *GameSession) persistLfgDungeonRouteFromStatus(ctx context.Context, status events.MatchmakingLfgStatusPayload) error { + if s == nil || s.character == nil || status.DungeonEntry == 0 { + return nil + } + routing := lfgStatusTransportRouting(status) + if !mapTransferRoutingUsesCrossrealmOwner(routing) { + return nil + } + info, err := s.resolveLfgDungeonInfo(ctx, status.DungeonEntry) + if err != nil || info == nil { + return err + } + dungeonEntry := lfgEffectiveDungeonEntry(status.DungeonEntry, info) + s.rememberLfgDungeonTransport(dungeonEntry, routing, info.GetMapID(), info.GetDifficulty()) + return s.recordLfgDungeonRoute(ctx, dungeonEntry, info.GetMapID(), info.GetDifficulty(), routing, true) +} + +func lfgEffectiveDungeonEntry(requestedDungeonEntry uint32, dungeonInfo *pbGameServ.GetLfgDungeonInfoResponse) uint32 { + if dungeonInfo != nil && dungeonInfo.GetDungeonEntry() != 0 { + return dungeonInfo.GetDungeonEntry() + } + return requestedDungeonEntry +} + +func (s *GameSession) recordLfgDungeonRoute(ctx context.Context, dungeonEntry, mapID, difficulty uint32, routing *mapTransferRouting, requiresBoundInstance bool) error { + if s == nil || s.character == nil || s.charServiceClient == nil || dungeonEntry == 0 || mapID == 0 || !mapTransferRoutingUsesCrossrealmOwner(routing) { + return nil + } + _, err := s.charServiceClient.RecordLfgDungeonRoute(ctx, &pbChar.RecordLfgDungeonRouteRequest{ + Api: root.SupportedCharServiceVer, + Route: &pbChar.LfgDungeonRoute{ + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + DungeonEntry: dungeonEntry, + MapID: mapID, + Difficulty: difficulty, + OwnerRealmID: routing.realmID, + IsCrossRealm: routing.isCrossRealm, + RequiresBoundInstance: requiresBoundInstance, + }, + }) + return err +} + +func (s *GameSession) clearUnboundLfgDungeonRoute(ctx context.Context, mapID uint32) error { + if s == nil || s.character == nil || s.charServiceClient == nil { + return nil + } + _, err := s.charServiceClient.ClearUnboundLfgDungeonRoute(ctx, &pbChar.ClearUnboundLfgDungeonRouteRequest{ + Api: root.SupportedCharServiceVer, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + MapID: mapID, + }) + return err +} + +func (s *GameSession) confirmLfgDungeonRouteEntered(ctx context.Context, mapID uint32) error { + if s == nil || s.character == nil || s.charServiceClient == nil || mapID == 0 { + return nil + } + if s.currentMapTransferRouting == nil || s.currentMapTransferRouting.feature != clusterTransferFeatureLFG || !mapTransferRoutingUsesCrossrealmOwner(s.currentMapTransferRouting) { + return nil + } + res, err := s.charServiceClient.ConfirmLfgDungeonRouteEntered(ctx, &pbChar.ConfirmLfgDungeonRouteEnteredRequest{ + Api: root.SupportedCharServiceVer, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + MapID: mapID, + Difficulty: s.currentLfgDungeonRouteDifficulty(mapID), + }) + if err != nil || res == nil || res.GetRoute() == nil { + return err + } + route := res.GetRoute() + s.rememberLfgDungeonTransport(route.GetDungeonEntry(), lfgRouteMapTransferRouting(route), route.GetMapID(), route.GetDifficulty()) + return nil +} + +func (s *GameSession) currentLfgDungeonRouteDifficulty(mapID uint32) uint32 { + if s == nil || s.character == nil || s.character.lfgDungeonTransport == nil { + return 0 + } + transport := s.character.lfgDungeonTransport + if transport.mapID != 0 && mapID != 0 && transport.mapID != mapID { + return 0 + } + return transport.difficulty +} + +func (s *GameSession) clearLfgDungeonTransport() { + if s.character == nil { + return + } + + s.character.lfgDungeonTransport = nil +} + +func (s *GameSession) lfgTeleportDungeonEntry(ctx context.Context, out bool) (uint32, *mapTransferRouting) { + if out || s.character == nil { + return 0, nil + } + + if transport := s.character.lfgDungeonTransport; transport != nil && transport.dungeonEntry != 0 { + return transport.dungeonEntry, cloneMapTransferRouting(transport.routing) + } + + if s.matchmakingServiceClient == nil { + return 0, nil + } + + status, err := s.matchmakingServiceClient.LfgStatus(ctx, &pbMM.LfgStatusRequest{ + Api: root.SupportedMatchmakingServiceVer, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + }) + if err != nil || status == nil { + return 0, nil + } + + payload := lfgStatusPayloadFromProto(status.GetStatus()) + if !lfgStateUsesNativeDungeon(payload.State) || payload.DungeonEntry == 0 { + return 0, nil + } + + routing := lfgStatusTransportRouting(payload) + s.rememberLfgDungeonTransport(payload.DungeonEntry, routing) + return payload.DungeonEntry, routing +} + +func (s *GameSession) lfgRoutingForNativeWorldport(ctx context.Context, mapID uint32) (*mapTransferRouting, bool, error) { + if s == nil || s.character == nil || mapID == 0 { + return nil, false, nil + } + + transport := s.character.lfgDungeonTransport + if transport != nil && transport.dungeonEntry != 0 && mapTransferRoutingUsesCrossrealmOwner(transport.routing) { + dungeonMapID := s.resolveLfgDungeonMapID(ctx, transport) + if dungeonMapID == mapID { + return cloneMapTransferRouting(transport.routing), false, nil + } + } + + route, blocked, err := s.lfgPersistentRouteForMap(ctx, s.character.GUID, mapID) + if err != nil || route == nil || blocked { + return nil, blocked, err + } + routing := lfgRouteMapTransferRouting(route) + s.rememberLfgDungeonTransport(route.GetDungeonEntry(), routing, route.GetMapID(), route.GetDifficulty()) + return routing, false, nil +} + +func (s *GameSession) resolveLfgDungeonMapID(ctx context.Context, transport *lfgDungeonTransportState) uint32 { + if s == nil || transport == nil || transport.dungeonEntry == 0 { + return 0 + } + if transport.mapID != 0 { + return transport.mapID + } + dungeonInfo, err := s.resolveLfgDungeonInfo(ctx, transport.dungeonEntry) + if err != nil || dungeonInfo == nil || dungeonInfo.GetMapID() == 0 { + if s.logger != nil { + event := s.logger.Debug(). + Uint32("dungeonEntry", transport.dungeonEntry) + if dungeonInfo != nil { + event = event.Str("status", dungeonInfo.GetStatus().String()) + } + event.Err(err).Msg("TC9 could not resolve LFG dungeon map for native worldport routing") + } + return 0 + } + + transport.mapID = dungeonInfo.GetMapID() + transport.difficulty = dungeonInfo.GetDifficulty() + return transport.mapID +} + +func (s *GameSession) resolveLfgDungeonInfo(ctx context.Context, dungeonEntry uint32) (*pbGameServ.GetLfgDungeonInfoResponse, error) { + if s == nil || dungeonEntry == 0 || s.gameServerGRPCClient == nil { + return nil, nil + } + + if ctx == nil { + ctx = context.Background() + } + dungeonInfo, err := s.gameServerGRPCClient.GetLfgDungeonInfo(ctx, &pbGameServ.GetLfgDungeonInfoRequest{ + Api: root.SupportedGameServerVer, + DungeonEntry: dungeonEntry, + }) + if err != nil || dungeonInfo == nil || dungeonInfo.GetStatus() != pbGameServ.GetLfgDungeonInfoResponse_Success { + return nil, err + } + return dungeonInfo, nil +} + +func (s *GameSession) lfgPersistentRouteForMap(ctx context.Context, playerGUID uint64, mapID uint32) (*pbChar.LfgDungeonRoute, bool, error) { + if s == nil || s.charServiceClient == nil || playerGUID == 0 || mapID == 0 || !lfgLoginMapMayNeedPersistentRoute(mapID) { + return nil, false, nil + } + res, err := s.charServiceClient.GetLfgDungeonRoute(ctx, &pbChar.GetLfgDungeonRouteRequest{ + Api: root.SupportedCharServiceVer, + RealmID: root.RealmID, + PlayerGUID: playerGUID, + MapID: mapID, + }) + if err != nil || res == nil || !res.GetFound() || res.GetRoute() == nil { + return nil, false, err + } + return res.GetRoute(), !res.GetAvailable(), nil +} + +const ( + lfgWorldMapEasternKingdoms uint32 = 0 + lfgWorldMapKalimdor uint32 = 1 + lfgWorldMapOutland uint32 = 530 + lfgWorldMapNorthrend uint32 = 571 +) + +func lfgLoginMapMayNeedPersistentRoute(mapID uint32) bool { + switch mapID { + case lfgWorldMapEasternKingdoms, lfgWorldMapKalimdor, lfgWorldMapOutland, lfgWorldMapNorthrend: + return false + default: + return true + } +} + +func lfgRouteMapTransferRouting(route *pbChar.LfgDungeonRoute) *mapTransferRouting { + if route == nil || !route.GetIsCrossRealm() { + return nil + } + return &mapTransferRouting{ + realmID: route.GetOwnerRealmID(), + isCrossRealm: route.GetIsCrossRealm(), + feature: clusterTransferFeatureLFG, + } +} + +func lfgStatusTransportRouting(status events.MatchmakingLfgStatusPayload) *mapTransferRouting { + if !lfgStateUsesNativeDungeon(status.State) || status.DungeonEntry == 0 || !lfgStatusHasMixedRealms(status) { + return nil + } + + return &mapTransferRouting{ + realmID: 0, + isCrossRealm: true, + feature: clusterTransferFeatureLFG, + } +} + +func lfgStatusHasMixedRealms(status events.MatchmakingLfgStatusPayload) bool { + var realmID uint32 + for _, member := range status.QueuedMembers { + if lfgStatusMemberIntroducesForeignRealm(&realmID, member.RealmID) { + return true + } + } + for _, member := range status.ProposalMembers { + if lfgStatusMemberIntroducesForeignRealm(&realmID, member.RealmID) { + return true + } + } + return false +} + +func lfgStatusMemberIntroducesForeignRealm(current *uint32, memberRealmID uint32) bool { + if current == nil || memberRealmID == 0 { + return false + } + if *current == 0 { + *current = memberRealmID + return false + } + return *current != memberRealmID +} + +func lfgMatchmakingStatusIsActive(state events.MatchmakingLfgState) bool { + switch state { + case events.MatchmakingLfgStateRoleCheck, + events.MatchmakingLfgStateQueued, + events.MatchmakingLfgStateProposal, + events.MatchmakingLfgStateBoot: + return true + default: + return false + } +} + +func (s *GameSession) clearLfgAfterMatchmakingUnavailable() bool { + if s.character == nil || s.lfgDungeonActive { + return false + } + if !s.character.lfgPendingJoin && !s.character.lfgMatchmakingActive { + return false + } + + status := s.character.lastLfgStatus + s.sendLfgJoinResult(lfgJoinInternalError, lfgRolecheckDefault) + switch status.State { + case events.MatchmakingLfgStateRoleCheck: + s.sendLfgUpdateParty(lfgUpdateTypeRolecheckAborted, false, false, nil) + case events.MatchmakingLfgStateQueued: + if s.lfgQueuedStatusIsParty(status) { + s.sendLfgUpdateParty(lfgUpdateTypeRemovedFromQueue, false, false, nil) + } else { + s.sendLfgUpdatePlayer(lfgUpdateTypeRemovedFromQueue, false, nil) + } + case events.MatchmakingLfgStateProposal, events.MatchmakingLfgStateBoot: + s.sendLfgUpdateParty(lfgUpdateTypeProposalFailed, false, false, nil) + if status.ProposalID != 0 { + status.ProposalState = events.MatchmakingLfgProposalFailed + s.sendLfgProposalUpdate(status) + } + default: + s.sendLfgUpdatePlayer(lfgUpdateTypeRemovedFromQueue, false, nil) + s.sendLfgUpdateParty(lfgUpdateTypeRolecheckAborted, false, false, nil) + } + s.clearLfgMatchmakingTracking() + return true +} + +func (s *GameSession) sendLfgStatusRefresh(status events.MatchmakingLfgStatusPayload) { + s.lfgDungeonActive = lfgStateUsesNativeDungeon(status.State) + + dungeons := status.SelectedDungeons + if s.lfgStatusRefreshUsesParty(status) { + join := status.State != events.MatchmakingLfgStateRoleCheck && status.State != events.MatchmakingLfgStateNone + queued := status.State == events.MatchmakingLfgStateQueued + s.sendLfgUpdateParty(lfgUpdateTypeUpdateStatus, join, queued, dungeons) + s.sendLfgUpdatePlayer(lfgUpdateTypeUpdateStatus, false, nil) + return + } + + queued := status.State == events.MatchmakingLfgStateQueued + s.sendLfgUpdatePlayer(lfgUpdateTypeUpdateStatus, queued, dungeons) + s.sendLfgUpdateParty(lfgUpdateTypeUpdateStatus, false, false, nil) +} + +func (s *GameSession) lfgStatusRefreshUsesParty(status events.MatchmakingLfgStatusPayload) bool { + if lfgStateUsesNativeDungeon(status.State) { + return true + } + + return s.lfgQueuedStatusIsParty(status) +} + +type lfgStatusPlayerKey struct { + realmID uint32 + playerGUID guid.LowType +} + +func (s *GameSession) lfgQueuedStatusIsParty(status events.MatchmakingLfgStatusPayload) bool { + local, found := s.lfgLocalQueuedMember(status) + if !found { + return false + } + + leader := lfgQueuedMemberLeaderKey(status.QueuedMembers, local) + if leader.playerGUID == 0 { + return false + } + + groupSize := 0 + for _, member := range status.QueuedMembers { + memberLeader := lfgQueuedMemberLeaderKey(status.QueuedMembers, member) + if memberLeader == leader { + groupSize++ + } + } + + return groupSize > 1 +} + +func (s *GameSession) lfgLocalQueuedMember(status events.MatchmakingLfgStatusPayload) (events.MatchmakingLfgMember, bool) { + if s.character == nil { + return events.MatchmakingLfgMember{}, false + } + for _, member := range status.QueuedMembers { + if lfgMemberRealmID(member.RealmID) == root.RealmID && uint64(member.PlayerGUID) == s.character.GUID { + return member, true + } + } + return events.MatchmakingLfgMember{}, false +} + +func (s *GameSession) lfgLocalQueueLeader(status events.MatchmakingLfgStatusPayload) (lfgStatusPlayerKey, bool) { + local, found := s.lfgLocalQueuedMember(status) + if !found { + return lfgStatusPlayerKey{}, false + } + leader := lfgQueuedMemberLeaderKey(status.QueuedMembers, local) + if leader.playerGUID == 0 { + return lfgStatusPlayerKey{}, false + } + return leader, true +} + +func (s *GameSession) lfgLocalProposalMemberFailed(status events.MatchmakingLfgStatusPayload) bool { + if s.character == nil { + return false + } + local := lfgStatusPlayerKey{realmID: root.RealmID, playerGUID: guid.LowType(s.character.GUID)} + allAcceptedFailure := lfgProposalFailure(status) == events.MatchmakingLfgProposalFailureFailed && + !lfgProposalHasUnacceptedMember(status) + for _, member := range status.ProposalMembers { + if lfgProposalMemberKey(member) != local { + continue + } + if allAcceptedFailure { + return true + } + switch lfgProposalFailure(status) { + case events.MatchmakingLfgProposalFailureDeclined: + return member.Answered && !member.Accepted + case events.MatchmakingLfgProposalFailureFailed: + return !member.Accepted + default: + return false + } + } + return allAcceptedFailure +} + +func lfgProposalFailure(status events.MatchmakingLfgStatusPayload) events.MatchmakingLfgProposalFailure { + if status.ProposalFailure != events.MatchmakingLfgProposalFailureNone { + return status.ProposalFailure + } + for _, member := range status.ProposalMembers { + if member.Answered && !member.Accepted { + return events.MatchmakingLfgProposalFailureDeclined + } + } + return events.MatchmakingLfgProposalFailureFailed +} + +func lfgProposalFailureUpdateType(status events.MatchmakingLfgStatusPayload) uint8 { + if lfgProposalFailure(status) == events.MatchmakingLfgProposalFailureDeclined { + return lfgUpdateTypeProposalDeclined + } + return lfgUpdateTypeProposalFailed +} + +func lfgProposalRemovedQueueLeaders(status events.MatchmakingLfgStatusPayload) map[lfgStatusPlayerKey]struct{} { + res := map[lfgStatusPlayerKey]struct{}{} + failure := lfgProposalFailure(status) + for _, member := range status.ProposalMembers { + queueLeader := lfgProposalMemberQueueLeader(status, member) + switch failure { + case events.MatchmakingLfgProposalFailureDeclined: + if member.Answered && !member.Accepted { + res[queueLeader] = struct{}{} + } + case events.MatchmakingLfgProposalFailureFailed: + if !member.Accepted { + res[queueLeader] = struct{}{} + } + } + } + if failure == events.MatchmakingLfgProposalFailureFailed && len(res) == 0 { + for _, member := range status.QueuedMembers { + res[lfgQueuedMemberLeaderKey(status.QueuedMembers, member)] = struct{}{} + } + } + return res +} + +func lfgProposalHasUnacceptedMember(status events.MatchmakingLfgStatusPayload) bool { + for _, member := range status.ProposalMembers { + if !member.Accepted { + return true + } + } + return false +} + +func lfgProposalMemberQueueLeader(status events.MatchmakingLfgStatusPayload, proposalMember events.MatchmakingLfgProposalMember) lfgStatusPlayerKey { + proposalKey := lfgProposalMemberKey(proposalMember) + for _, queuedMember := range status.QueuedMembers { + if lfgQueuedMemberKey(queuedMember) == proposalKey { + return lfgQueuedMemberLeaderKey(status.QueuedMembers, queuedMember) + } + } + return proposalKey +} + +func lfgQueuedMemberLeaderKey(members []events.MatchmakingLfgMember, member events.MatchmakingLfgMember) lfgStatusPlayerKey { + leaderRealmID := lfgMemberRealmID(member.QueueLeaderRealmID) + leaderGUID := member.QueueLeaderGUID + if leaderGUID == 0 { + leaderRealmID, leaderGUID = lfgInferQueueLeader(members, member) + } + return lfgStatusPlayerKey{realmID: leaderRealmID, playerGUID: leaderGUID} +} + +func lfgQueuedMemberKey(member events.MatchmakingLfgMember) lfgStatusPlayerKey { + return lfgStatusPlayerKey{realmID: lfgMemberRealmID(member.RealmID), playerGUID: member.PlayerGUID} +} + +func lfgProposalMemberKey(member events.MatchmakingLfgProposalMember) lfgStatusPlayerKey { + return lfgStatusPlayerKey{realmID: lfgMemberRealmID(member.RealmID), playerGUID: member.PlayerGUID} +} + +func lfgInferQueueLeader(members []events.MatchmakingLfgMember, member events.MatchmakingLfgMember) (uint32, guid.LowType) { + if member.Leader { + return lfgMemberRealmID(member.RealmID), member.PlayerGUID + } + + var leader events.MatchmakingLfgMember + leaderCount := 0 + for _, candidate := range members { + if !candidate.Leader { + continue + } + leader = candidate + leaderCount++ + } + if leaderCount == 1 { + return lfgMemberRealmID(leader.RealmID), leader.PlayerGUID + } + + return lfgMemberRealmID(member.RealmID), member.PlayerGUID +} + +func (s *GameSession) sendLfgUpdatePlayer(updateType uint8, queued bool, dungeons []uint32) { + resp := packet.NewWriter(packet.SMsgLFGUpdatePlayer) + resp.Uint8(updateType) + resp.Uint8(boolToUint8(len(dungeons) > 0)) + if len(dungeons) > 0 { + resp.Uint8(boolToUint8(queued)) + resp.Uint8(0) + resp.Uint8(0) + resp.Uint8(uint8(len(dungeons))) + for _, dungeon := range dungeons { + resp.Uint32(lfgDungeonID(dungeon)) + } + resp.String("") + } + s.gameSocket.Send(resp) +} + +func (s *GameSession) sendLfgUpdateParty(updateType uint8, join bool, queued bool, dungeons []uint32) { + resp := packet.NewWriter(packet.SMsgLFGUpdateParty) + resp.Uint8(updateType) + resp.Uint8(boolToUint8(len(dungeons) > 0)) + if len(dungeons) > 0 { + resp.Uint8(boolToUint8(join)) + resp.Uint8(boolToUint8(queued)) + resp.Uint8(0) + resp.Uint8(0) + for i := 0; i < 3; i++ { + resp.Uint8(0) + } + resp.Uint8(uint8(len(dungeons))) + for _, dungeon := range dungeons { + resp.Uint32(lfgDungeonID(dungeon)) + } + resp.String("") + } + s.gameSocket.Send(resp) +} + +func (s *GameSession) sendLfgRoleCheckUpdate(status events.MatchmakingLfgStatusPayload) { + resp := packet.NewWriter(packet.SMsgLFGRoleCheckUpdate) + resp.Uint32(lfgRolecheckInitialited) + resp.Uint8(1) + resp.Uint8(uint8(len(status.SelectedDungeons))) + for _, dungeon := range status.SelectedDungeons { + resp.Uint32(dungeon) + } + + members := lfgMembersLeaderFirst(status.QueuedMembers) + resp.Uint8(uint8(len(members))) + for _, member := range members { + resp.Uint64(playerObjectGUIDForRealm(lfgMemberRealmID(member.RealmID), uint64(member.PlayerGUID))) + resp.Uint8(boolToUint8(member.Roles > 0)) + resp.Uint32(uint32(member.Roles)) + resp.Uint8(s.lfgMemberLevel(member.RealmID, member.PlayerGUID)) + } + s.gameSocket.Send(resp) +} + +func lfgMembersLeaderFirst(members []events.MatchmakingLfgMember) []events.MatchmakingLfgMember { + res := append([]events.MatchmakingLfgMember(nil), members...) + for i, member := range res { + if member.Leader { + res[0], res[i] = res[i], res[0] + break + } + } + return res +} + +func lfgMemberRealmID(realmID uint32) uint32 { + if realmID == 0 { + return root.RealmID + } + return realmID +} + +func (s *GameSession) lfgMemberLevel(realmID uint32, playerGUID guid.LowType) uint8 { + if lfgMemberRealmID(realmID) == root.RealmID && uint64(playerGUID) == s.character.GUID { + return s.character.Level + } + return 0 +} + +func (s *GameSession) sendLfgJoinResult(result uint32, state uint32) { + resp := packet.NewWriter(packet.SMsgLFGJoinResult) + resp.Uint32(result) + resp.Uint32(state) + s.gameSocket.Send(resp) +} + +func (s *GameSession) sendLfgQueueStatus(status events.MatchmakingLfgStatusPayload) { + dungeon := uint32(0) + if len(status.SelectedDungeons) > 0 { + dungeon = lfgDungeonID(status.SelectedDungeons[0]) + } + + resp := packet.NewWriter(packet.SMsgLFGQueueStatus) + resp.Uint32(dungeon) + resp.Int32(-1) + resp.Int32(-1) + resp.Int32(-1) + resp.Int32(-1) + resp.Int32(-1) + resp.Uint8(status.TanksNeeded) + resp.Uint8(status.HealersNeeded) + resp.Uint8(status.DamageNeeded) + resp.Uint32(lfgQueueStatusQueuedTimeSeconds(status)) + s.gameSocket.Send(resp) +} + +func lfgDungeonID(entry uint32) uint32 { + return entry & lfgDungeonIDMask +} + +func lfgQueueStatusQueuedTimeSeconds(status events.MatchmakingLfgStatusPayload) uint32 { + // The payload field is historic; the client packet expects elapsed seconds. + if status.QueuedTimeMilliseconds == 0 { + return 1 + } + return status.QueuedTimeMilliseconds +} + +func (s *GameSession) sendLfgProposalUpdate(status events.MatchmakingLfgStatusPayload) { + resp := packet.NewWriter(packet.SMsgLFGProposalUpdate) + resp.Uint32(lfgProposalDisplayDungeon(status)) + resp.Uint8(uint8(status.ProposalState)) + resp.Uint32(status.ProposalID) + resp.Uint32(0) + resp.Uint8(0) + resp.Uint8(uint8(len(status.ProposalMembers))) + + for _, member := range status.ProposalMembers { + resp.Uint32(uint32(member.AssignedRole)) + resp.Uint8(boolToUint8(lfgMemberRealmID(member.RealmID) == root.RealmID && uint64(member.PlayerGUID) == s.character.GUID)) + resp.Uint8(0) + resp.Uint8(0) + resp.Uint8(boolToUint8(member.Answered)) + resp.Uint8(boolToUint8(member.Accepted)) + } + s.gameSocket.Send(resp) +} + +func lfgProposalDisplayDungeon(status events.MatchmakingLfgStatusPayload) uint32 { + if status.DungeonEntry == 0 { + return 0 + } + for _, entry := range status.SelectedDungeons { + if entry&lfgDungeonIDMask == status.DungeonEntry&lfgDungeonIDMask { + return entry + } + } + return status.DungeonEntry +} + +func boolToUint8(v bool) uint8 { + if v { + return 1 + } + return 0 +} + +func lfgStateUsesNativeDungeon(state events.MatchmakingLfgState) bool { + return state == events.MatchmakingLfgStateDungeon || state == events.MatchmakingLfgStateFinishedDungeon +} + +func lfgProtoStateUsesNativeDungeon(state pbMM.LfgState) bool { + return state == pbMM.LfgState_LFG_STATE_DUNGEON || state == pbMM.LfgState_LFG_STATE_FINISHED_DUNGEON +} + +func lfgStatusPayloadFromProto(status *pbMM.LfgStatusData) events.MatchmakingLfgStatusPayload { + if status == nil { + return events.MatchmakingLfgStatusPayload{State: events.MatchmakingLfgStateNone} + } + + queuedMembers := make([]events.MatchmakingLfgMember, 0, len(status.QueuedMembers)) + for _, member := range status.QueuedMembers { + queuedMembers = append(queuedMembers, events.MatchmakingLfgMember{ + RealmID: lfgMemberRealmID(member.RealmID), + PlayerGUID: guid.LowType(member.PlayerGUID), + Roles: uint8(member.Roles), + Leader: member.Leader, + QueueLeaderRealmID: lfgMemberRealmID(member.QueueLeaderRealmID), + QueueLeaderGUID: guid.LowType(member.QueueLeaderGUID), + }) + } + + proposalMembers := make([]events.MatchmakingLfgProposalMember, 0, len(status.ProposalMembers)) + for _, member := range status.ProposalMembers { + proposalMembers = append(proposalMembers, events.MatchmakingLfgProposalMember{ + RealmID: lfgMemberRealmID(member.RealmID), + PlayerGUID: guid.LowType(member.PlayerGUID), + SelectedRoles: uint8(member.SelectedRoles), + AssignedRole: uint8(member.AssignedRole), + Answered: member.Answered, + Accepted: member.Accepted, + }) + } + + return events.MatchmakingLfgStatusPayload{ + State: events.MatchmakingLfgState(status.State), + ProposalID: status.ProposalID, + ProposalState: events.MatchmakingLfgProposalState(status.ProposalState), + DungeonEntry: status.DungeonEntry, + SelectedDungeons: status.SelectedDungeons, + QueuedMembers: queuedMembers, + ProposalMembers: proposalMembers, + QueuedTimeMilliseconds: status.QueuedTimeMilliseconds, + TanksNeeded: uint8(status.TanksNeeded), + HealersNeeded: uint8(status.HealersNeeded), + DamageNeeded: uint8(status.DamageNeeded), + } +} diff --git a/apps/gateway/session/mail.go b/apps/gateway/session/mail.go index 426f456..0d4ec8c 100644 --- a/apps/gateway/session/mail.go +++ b/apps/gateway/session/mail.go @@ -191,7 +191,7 @@ func (s *GameSession) HandleSendMail(ctx context.Context, p *packet.Packet) erro Flags: item.Flags, Durability: int32(item.Durability), Charges: 0, - RandomPropertyID: item.RandomPropertyID, + RandomPropertyID: uint32(item.RandomPropertyID), PropertySeed: 0, Text: item.Text, } @@ -489,7 +489,7 @@ func (s *GameSession) HandleMailTakeItem(ctx context.Context, p *packet.Packet) Count: item.Count, Flags: item.Flags, Durability: uint32(item.Durability), - RandomPropertyID: item.RandomPropertyID, + RandomPropertyID: int32(item.RandomPropertyID), Text: item.Text, }, }) diff --git a/apps/gateway/session/movements.go b/apps/gateway/session/movements.go index f3d8461..2f29b52 100644 --- a/apps/gateway/session/movements.go +++ b/apps/gateway/session/movements.go @@ -23,6 +23,8 @@ func (s *GameSession) HandleMovement(ctx context.Context, p *packet.Packet) erro return nil } + s.markPlayerWorldActive() + r := p.Reader() if r.ReadGUID() != s.character.GUID { return nil diff --git a/apps/gateway/session/name_query.go b/apps/gateway/session/name_query.go new file mode 100644 index 0000000..ee959d0 --- /dev/null +++ b/apps/gateway/session/name_query.go @@ -0,0 +1,142 @@ +package session + +import ( + "context" + + root "github.com/walkline/ToCloud9/apps/gateway" + "github.com/walkline/ToCloud9/apps/gateway/packet" + pbChar "github.com/walkline/ToCloud9/gen/characters/pb" + pbGroup "github.com/walkline/ToCloud9/gen/group/pb" + "github.com/walkline/ToCloud9/shared/wow/guid" +) + +func (s *GameSession) HandleNameQuery(ctx context.Context, p *packet.Packet) error { + reader := p.Reader() + queryGUID := reader.Uint64() + if err := reader.Error(); err != nil { + return err + } + + memberGUID := playerDBGUIDFromClientGUID(queryGUID) + if memberGUID == 0 || s.character == nil || s.groupServiceClient == nil || s.charServiceClient == nil { + if s.sendForeignNameQueryResponse(ctx, queryGUID) { + return nil + } + s.forwardNameQueryToWorld(p) + return nil + } + + groupResp, err := s.groupServiceClient.GetGroupByMember(ctx, &pbGroup.GetGroupByMemberRequest{ + Api: root.SupportedGroupServiceVer, + RealmID: root.RealmID, + Player: s.character.GUID, + }) + if err != nil || groupResp == nil || groupResp.Group == nil { + if s.sendForeignNameQueryResponse(ctx, queryGUID) { + return nil + } + s.forwardNameQueryToWorld(p) + return nil + } + + groupRealmID := groupHomeRealmIDFromPB(groupResp.Group) + var member *pbGroup.GetGroupResponse_GroupMember + for _, candidate := range groupResp.Group.Members { + if samePlayerGUID(groupRealmID, candidate.Guid, root.RealmID, memberGUID) { + member = candidate + break + } + } + if member == nil || member.Name == "" { + if s.sendForeignNameQueryResponse(ctx, queryGUID) { + return nil + } + s.forwardNameQueryToWorld(p) + return nil + } + + memberRealmID := groupMemberRealmID(groupRealmID, member) + charResp, err := s.charServiceClient.CharacterByName(ctx, &pbChar.CharacterByNameRequest{ + Api: root.Ver, + RealmID: memberRealmID, + CharacterName: member.Name, + }) + if err != nil || charResp == nil || charResp.Character == nil || + !samePlayerGUID(memberRealmID, charResp.Character.CharGUID, groupRealmID, member.Guid) { + if s.sendForeignNameQueryResponse(ctx, queryGUID) { + return nil + } + s.forwardNameQueryToWorld(p) + return nil + } + + s.sendNameQueryResponse(ctx, queryGUID, charResp.Character.CharName, charResp.Character.CharRace, charResp.Character.CharClass, charResp.Character.CharGender) + return nil +} + +func (s *GameSession) sendForeignNameQueryResponse(ctx context.Context, queryGUID uint64) bool { + if s.charServiceClient == nil { + return false + } + + realmID := realmIDFromClientPlayerGUID(queryGUID) + if realmID == root.RealmID { + return false + } + + resp, err := s.charServiceClient.CharacterByGUID(ctx, &pbChar.CharacterByGUIDRequest{ + Api: root.Ver, + RealmID: realmID, + CharacterGUID: guid.PlayerLowGUID(queryGUID), + }) + if err != nil || resp == nil || resp.Character == nil { + return false + } + + s.sendNameQueryResponse(ctx, queryGUID, resp.Character.CharName, resp.Character.CharRace, resp.Character.CharClass, resp.Character.CharGender) + return true +} + +func (s *GameSession) forwardNameQueryToWorld(p *packet.Packet) { + if s.worldSocket != nil { + s.worldSocket.SendPacket(p) + } +} + +func (s *GameSession) sendNameQueryResponse(ctx context.Context, queryGUID uint64, name string, race, class, gender uint32) { + resp := packet.NewWriterWithSize(packet.SMsgNameQueryResponse, 0) + resp.GUID(queryGUID) + resp.Uint8(0) + resp.String(name) + + if realmIDFromClientPlayerGUID(queryGUID) == root.RealmID { + resp.Uint8(0) + } else { + realmName := "unknown realm" + if s.realmNamesService != nil { + if name, err := s.realmNamesService.NameByID(ctx, realmIDFromClientPlayerGUID(queryGUID)); err == nil { + realmName = name + } + } + resp.String(realmName) + } + + resp.Uint8(uint8(race)) + resp.Uint8(uint8(gender)) + resp.Uint8(uint8(class)) + resp.Uint8(0) + s.gameSocket.Send(resp) +} + +func realmIDFromClientPlayerGUID(playerGUID uint64) uint32 { + if playerGUID>>48 == 0 { + if realmID := uint32((playerGUID >> 32) & 0xffff); realmID != 0 { + return realmID + } + } + if realmID := uint32(guid.New(playerGUID).GetRealmID()); realmID != 0 { + return realmID + } + + return root.RealmID +} diff --git a/apps/gateway/session/object-update-player-state.go b/apps/gateway/session/object-update-player-state.go new file mode 100644 index 0000000..83ea760 --- /dev/null +++ b/apps/gateway/session/object-update-player-state.go @@ -0,0 +1,552 @@ +package session + +import ( + "bytes" + "compress/zlib" + "context" + "encoding/binary" + "fmt" + "io" + "time" + + "github.com/walkline/ToCloud9/apps/gateway/packet" + "github.com/walkline/ToCloud9/apps/gateway/service" + "github.com/walkline/ToCloud9/shared/groupstatetrace" + "github.com/walkline/ToCloud9/shared/wow/guid" +) + +const ( + objectUpdateTypeValues = 0 + objectUpdateTypeMovement = 1 + objectUpdateTypeCreateObject = 2 + objectUpdateTypeCreateObject2 = 3 + objectUpdateTypeOutOfRangeObjects = 4 + + objectUpdateFlagSelf = 0x0001 + objectUpdateFlagTransport = 0x0002 + objectUpdateFlagHasTarget = 0x0004 + objectUpdateFlagUnknown = 0x0008 + objectUpdateFlagLowGUID = 0x0010 + objectUpdateFlagLiving = 0x0020 + objectUpdateFlagStationaryPosition = 0x0040 + objectUpdateFlagVehicle = 0x0080 + objectUpdateFlagPosition = 0x0100 + objectUpdateFlagRotation = 0x0200 + + movementFlagOnTransport = 0x00000200 + movementFlagFalling = 0x00001000 + movementFlagSwimming = 0x00200000 + movementFlagFlying = 0x02000000 + movementFlagSplineElevation = 0x04000000 + movementFlagSplineEnabled = 0x08000000 + + movementFlag2AlwaysAllowPitching = 0x00000020 + movementFlag2InterpolatedMovement = 0x00000400 + objectTypeIDPlayer uint8 = 4 + + moveSplineFlagFinalPoint = 0x00008000 + moveSplineFlagFinalTarget = 0x00010000 + moveSplineFlagFinalAngle = 0x00020000 + + unitFieldBytes0 = 0x0017 + unitFieldHealth = 0x0018 + unitFieldPower1 = 0x0019 + unitFieldMaxHealth = 0x0020 + unitFieldMaxPower1 = 0x0021 + unitFieldLevel = 0x0036 + unitPowerFieldMax = 7 +) + +func (s *GameSession) InterceptObjectUpdate(_ context.Context, p *packet.Packet) error { + s.gameSocket.SendPacket(p) + + if p.Source != packet.SourceWorldServer || s.character == nil || s.playerStateUpdatesBarrier == nil { + return nil + } + + if s.pendingRedirectID != "" { + return nil + } + if !s.playerWorldActive { + return nil + } + + payload := p.Data + var err error + if p.Opcode == packet.SMsgCompressedUpdateObject { + payload, err = decompressObjectUpdatePayload(p.Data) + if err != nil { + s.logger.Debug().Err(err).Msg("can't decompress object update packet for player state extraction") + return nil + } + } + + snapshots, err := extractPlayerStateSnapshotsFromObjectUpdate(payload) + if err != nil { + s.logger.Debug().Err(err).Msg("can't fully parse object update packet for player state extraction") + } + + currentMemberGUID := currentCharacterMemberGUID(s.character.GUID) + + for _, snapshot := range snapshots { + if snapshot.MemberGUID != currentMemberGUID { + continue + } + + s.fillPlayerStateSnapshotSessionFields(&snapshot) + if event := groupstatetrace.Event(s.logger, "gateway.object_update.snapshot", snapshot.MemberGUID); event != nil { + traceSessionPlayerStateSnapshot(event, snapshot). + Uint32("accountID", s.accountID). + Str("opcode", p.Opcode.String()). + Msg(groupstatetrace.Message) + } + s.playerStateUpdatesBarrier.Update(snapshot) + } + + return nil +} + +func (s *GameSession) fillPlayerStateSnapshotSessionFields(snapshot *service.PlayerStateSnapshot) { + online := true + level := s.character.Level + classID := s.character.Class + zoneID := s.character.Zone + mapID := s.character.Map + + snapshot.SourceWorldserverID = s.currentWorldserverSourceID() + if snapshot.Online == nil { + snapshot.Online = &online + } + if snapshot.Level == nil { + snapshot.Level = &level + } + if snapshot.Class == nil { + snapshot.Class = &classID + } + if snapshot.ZoneID == nil { + snapshot.ZoneID = &zoneID + } + if snapshot.MapID == nil { + snapshot.MapID = &mapID + } + if snapshot.TimestampMs == 0 { + snapshot.TimestampMs = uint64(time.Now().UnixMilli()) + } +} + +func decompressObjectUpdatePayload(data []byte) ([]byte, error) { + if len(data) < 4 { + return nil, fmt.Errorf("compressed object update too short: %d", len(data)) + } + + expectedSize := binary.LittleEndian.Uint32(data[:4]) + zr, err := zlib.NewReader(bytes.NewReader(data[4:])) + if err != nil { + return nil, err + } + defer zr.Close() + + payload, err := io.ReadAll(zr) + if err != nil { + return nil, err + } + + if expectedSize != 0 && uint32(len(payload)) != expectedSize { + return payload, fmt.Errorf("decompressed object update size mismatch: got %d, expected %d", len(payload), expectedSize) + } + + return payload, nil +} + +func extractPlayerStateSnapshotsFromObjectUpdate(data []byte) ([]service.PlayerStateSnapshot, error) { + r := objectUpdateReader{data: data} + blockCount, err := r.u32() + if err != nil { + return nil, err + } + + snapshots := make([]service.PlayerStateSnapshot, 0) + for i := uint32(0); i < blockCount; i++ { + updateType, err := r.u8() + if err != nil { + return snapshots, err + } + + switch updateType { + case objectUpdateTypeValues: + rawGUID, err := r.packedGUID() + if err != nil { + return snapshots, err + } + snapshot, err := r.valuesUpdate(rawGUID, true) + if err != nil { + return snapshots, err + } + if snapshot.MemberGUID != 0 { + snapshots = append(snapshots, snapshot) + } + case objectUpdateTypeMovement: + if _, err := r.packedGUID(); err != nil { + return snapshots, err + } + if err := r.skipMovementUpdate(); err != nil { + return snapshots, err + } + case objectUpdateTypeCreateObject, objectUpdateTypeCreateObject2: + rawGUID, err := r.packedGUID() + if err != nil { + return snapshots, err + } + objectTypeID, err := r.u8() + if err != nil { + return snapshots, err + } + if err := r.skipMovementUpdate(); err != nil { + return snapshots, err + } + snapshot, err := r.valuesUpdate(rawGUID, objectTypeID == objectTypeIDPlayer) + if err != nil { + return snapshots, err + } + if snapshot.MemberGUID != 0 { + snapshots = append(snapshots, snapshot) + } + case objectUpdateTypeOutOfRangeObjects: + count, err := r.u32() + if err != nil { + return snapshots, err + } + for j := uint32(0); j < count; j++ { + if _, err := r.packedGUID(); err != nil { + return snapshots, err + } + } + default: + return snapshots, fmt.Errorf("unsupported object update type %d", updateType) + } + } + + return snapshots, nil +} + +type objectUpdateReader struct { + data []byte + pos int +} + +func (r *objectUpdateReader) u8() (uint8, error) { + if r.pos+1 > len(r.data) { + return 0, io.ErrUnexpectedEOF + } + v := r.data[r.pos] + r.pos++ + return v, nil +} + +func (r *objectUpdateReader) u16() (uint16, error) { + if r.pos+2 > len(r.data) { + return 0, io.ErrUnexpectedEOF + } + v := binary.LittleEndian.Uint16(r.data[r.pos:]) + r.pos += 2 + return v, nil +} + +func (r *objectUpdateReader) u32() (uint32, error) { + if r.pos+4 > len(r.data) { + return 0, io.ErrUnexpectedEOF + } + v := binary.LittleEndian.Uint32(r.data[r.pos:]) + r.pos += 4 + return v, nil +} + +func (r *objectUpdateReader) skip(n int) error { + if n < 0 || r.pos+n > len(r.data) { + return io.ErrUnexpectedEOF + } + r.pos += n + return nil +} + +func (r *objectUpdateReader) packedGUID() (uint64, error) { + mask, err := r.u8() + if err != nil { + return 0, err + } + + var raw uint64 + for i := 0; i < 8; i++ { + if mask&(1< uint32((len(r.data)-r.pos)/12) { + return io.ErrUnexpectedEOF + } + + if err := r.skip(int(nodes) * 12); err != nil { + return err + } + + if err := r.skip(1); err != nil { // spline mode + return err + } + + return r.skip(12) // final destination +} + +func (r *objectUpdateReader) valuesUpdate(rawGUID uint64, canExtract bool) (service.PlayerStateSnapshot, error) { + maskBlockCount, err := r.u8() + if err != nil { + return service.PlayerStateSnapshot{}, err + } + + updateMask := make([]uint32, maskBlockCount) + for i := range updateMask { + updateMask[i], err = r.u32() + if err != nil { + return service.PlayerStateSnapshot{}, err + } + } + + snapshot := service.PlayerStateSnapshot{} + if canExtract { + snapshot.MemberGUID = playerDBGUIDFromObjectUpdateGUID(rawGUID) + } + + for blockIdx, block := range updateMask { + for bit := 0; bit < 32; bit++ { + if block&(1<> 8) & 0xff) + powerType := uint8((value >> 24) & 0xff) + snapshot.Class = &classID + snapshot.PowerType = &powerType + case field == unitFieldHealth: + snapshot.Health = &value + dead := value == 0 + snapshot.Dead = &dead + case field >= unitFieldPower1 && field < unitFieldPower1+unitPowerFieldMax: + powerType := uint8(field - unitFieldPower1) + if snapshot.PowerType != nil && *snapshot.PowerType != powerType { + return + } + snapshot.PowerType = &powerType + snapshot.Power = &value + case field == unitFieldMaxHealth: + snapshot.MaxHealth = &value + case field >= unitFieldMaxPower1 && field < unitFieldMaxPower1+unitPowerFieldMax: + powerType := uint8(field - unitFieldMaxPower1) + if snapshot.PowerType != nil && *snapshot.PowerType != powerType { + return + } + snapshot.PowerType = &powerType + snapshot.MaxPower = &value + case field == unitFieldLevel: + level := uint8(value) + snapshot.Level = &level + } +} + +func playerDBGUIDFromObjectUpdateGUID(raw uint64) uint64 { + g := guid.New(raw) + if raw == 0 || g.GetHigh() != guid.Player { + return 0 + } + + if raw>>32 == 0 { + return raw + } + + return raw & 0xffffffff +} + +func currentCharacterMemberGUID(characterGUID uint64) uint64 { + currentMemberGUID := playerDBGUIDFromObjectUpdateGUID(characterGUID) + if currentMemberGUID == 0 { + return characterGUID + } + + return currentMemberGUID +} diff --git a/apps/gateway/session/player-state-packets.go b/apps/gateway/session/player-state-packets.go new file mode 100644 index 0000000..fcd27a2 --- /dev/null +++ b/apps/gateway/session/player-state-packets.go @@ -0,0 +1,271 @@ +package session + +import ( + "context" + "fmt" + + "github.com/walkline/ToCloud9/apps/gateway/packet" + "github.com/walkline/ToCloud9/apps/gateway/service" + "github.com/walkline/ToCloud9/shared/groupstatetrace" + "github.com/walkline/ToCloud9/shared/wow" +) + +const ( + groupUpdateFlagPosition uint32 = 0x00000100 +) + +func (s *GameSession) InterceptDirectPlayerStateUpdate(_ context.Context, p *packet.Packet) error { + s.gameSocket.SendPacket(p) + + if !s.canObserveOwnPlayerStateFromWorld(p) { + return nil + } + + snapshot, err := extractDirectPlayerStateSnapshot(p.Opcode, p.Data) + if err != nil { + s.logger.Debug().Err(err).Str("opcode", p.Opcode.String()).Msg("can't parse direct player state update") + return nil + } + + s.publishOwnPlayerStateSnapshot(snapshot, "gateway.direct_packet.snapshot", false) + return nil +} + +func (s *GameSession) InterceptPartyMemberStats(_ context.Context, p *packet.Packet) error { + if s.shouldSuppressWorldGroupPresentation(p) { + return nil + } + + s.gameSocket.SendPacket(p) + + if !s.canPublishOwnPlayerStateFromWorld(p) { + return nil + } + + snapshot, err := extractPlayerStateSnapshotFromPartyMemberStats(p.Data, p.Opcode == packet.SMsgPartyMemberStatsFull) + if err != nil { + s.logger.Debug().Err(err).Str("opcode", p.Opcode.String()).Msg("can't parse party member stats for player state") + return nil + } + + s.publishOwnPlayerStateSnapshot(snapshot, "gateway.party_stats.snapshot", true) + return nil +} + +func (s *GameSession) canPublishOwnPlayerStateFromWorld(p *packet.Packet) bool { + if !s.canObserveOwnPlayerStateFromWorld(p) { + return false + } + return s.playerWorldActive +} + +func (s *GameSession) canObserveOwnPlayerStateFromWorld(p *packet.Packet) bool { + if p.Source != packet.SourceWorldServer || s.character == nil || s.playerStateUpdatesBarrier == nil { + return false + } + if s.pendingRedirectID != "" { + return false + } + return true +} + +func (s *GameSession) publishOwnPlayerStateSnapshot(snapshot service.PlayerStateSnapshot, traceStage string, flushIfComplete bool) { + if snapshot.MemberGUID == 0 { + return + } + + if snapshot.MemberGUID != currentCharacterMemberGUID(s.character.GUID) { + return + } + + s.fillPlayerStateSnapshotSessionFields(&snapshot) + if traceStage == "gateway.direct_packet.snapshot" && s.shouldDropInactiveDirectPowerSnapshot(snapshot) { + if event := groupstatetrace.Event(s.logger, "gateway.direct_packet.drop_inactive_power", snapshot.MemberGUID); event != nil { + expectedPowerType, _ := wow.FixedPrimaryPowerTypeForClass(*snapshot.Class) + traceSessionPlayerStateSnapshot(event, snapshot). + Uint8("expectedPowerType", expectedPowerType). + Uint32("accountID", s.accountID). + Msg(groupstatetrace.Message) + } + return + } + if event := groupstatetrace.Event(s.logger, traceStage, snapshot.MemberGUID); event != nil { + traceSessionPlayerStateSnapshot(event, snapshot). + Uint32("accountID", s.accountID). + Msg(groupstatetrace.Message) + } + if flushIfComplete && snapshot.IsComplete() { + s.playerStateUpdatesBarrier.UpdateAndFlush(snapshot) + return + } + + s.playerStateUpdatesBarrier.Update(snapshot) +} + +func (s *GameSession) InterceptGroupPresentationPacket(_ context.Context, p *packet.Packet) error { + if s.shouldSuppressWorldGroupPresentation(p) { + return nil + } + + s.gameSocket.SendPacket(p) + return nil +} + +func (s *GameSession) shouldSuppressWorldGroupPresentation(p *packet.Packet) bool { + if s == nil || p == nil || p.Source != packet.SourceWorldServer { + return false + } + if s.clusterGroupPresentationBlocked() { + return true + } + + switch p.Opcode { + case packet.SMsgGroupDestroyed, packet.SMsgGroupList: + return mapTransferRoutingUsesCrossrealmOwner(s.currentMapTransferRouting) + default: + return false + } +} + +func (s *GameSession) shouldDropInactiveDirectPowerSnapshot(snapshot service.PlayerStateSnapshot) bool { + if snapshot.Class == nil || snapshot.PowerType == nil || snapshot.Power == nil { + return false + } + + expectedPowerType, fixed := wow.FixedPrimaryPowerTypeForClass(*snapshot.Class) + return fixed && *snapshot.PowerType != expectedPowerType +} + +func extractDirectPlayerStateSnapshot(opcode packet.Opcode, data []byte) (service.PlayerStateSnapshot, error) { + r := packet.NewReaderWithData(data) + rawGUID := r.ReadGUID() + memberGUID := playerDBGUIDFromObjectUpdateGUID(rawGUID) + if memberGUID == 0 { + return service.PlayerStateSnapshot{}, nil + } + + snapshot := service.PlayerStateSnapshot{MemberGUID: memberGUID} + switch opcode { + case packet.SMsgHealthUpdate: + health := r.Uint32() + dead := health == 0 + snapshot.Health = &health + snapshot.Dead = &dead + case packet.SMsgPowerUpdate: + powerType := r.Uint8() + power := r.Uint32() + snapshot.PowerType = &powerType + snapshot.Power = &power + default: + return service.PlayerStateSnapshot{}, fmt.Errorf("unsupported direct player state opcode %s", opcode.String()) + } + + if err := r.Error(); err != nil { + return service.PlayerStateSnapshot{}, err + } + + return snapshot, nil +} + +func extractPlayerStateSnapshotFromPartyMemberStats(data []byte, full bool) (service.PlayerStateSnapshot, error) { + r := packet.NewReaderWithData(data) + if full { + _ = r.Uint8() + } + + rawGUID := r.ReadGUID() + memberGUID := playerDBGUIDFromObjectUpdateGUID(rawGUID) + if memberGUID == 0 { + return service.PlayerStateSnapshot{}, nil + } + + updateMask := r.Uint32() + snapshot := service.PlayerStateSnapshot{MemberGUID: memberGUID} + + if updateMask&groupUpdateFlagStatus != 0 { + status := r.Uint16() + online := status&memberStatusOnline != 0 + dead := status&memberStatusDead != 0 + ghost := status&memberStatusGhost != 0 + snapshot.Online = &online + snapshot.Dead = &dead + snapshot.Ghost = &ghost + } + if updateMask&groupUpdateFlagCurHP != 0 { + health := r.Uint32() + snapshot.Health = &health + } + if updateMask&groupUpdateFlagMaxHP != 0 { + maxHealth := r.Uint32() + snapshot.MaxHealth = &maxHealth + } + if updateMask&groupUpdateFlagPowerType != 0 { + powerType := r.Uint8() + snapshot.PowerType = &powerType + } + if updateMask&groupUpdateFlagCurPower != 0 { + power := uint32(r.Uint16()) + snapshot.Power = &power + } + if updateMask&groupUpdateFlagMaxPower != 0 { + maxPower := uint32(r.Uint16()) + snapshot.MaxPower = &maxPower + } + if snapshot.PowerType == nil && (snapshot.Power != nil || snapshot.MaxPower != nil) { + powerType := uint8(0) + snapshot.PowerType = &powerType + } + if updateMask&groupUpdateFlagLevel != 0 { + level := uint8(r.Uint16()) + snapshot.Level = &level + } + if updateMask&groupUpdateFlagZone != 0 { + zoneID := uint32(r.Uint16()) + snapshot.ZoneID = &zoneID + } + if updateMask&groupUpdateFlagPosition != 0 { + _ = r.Uint16() + _ = r.Uint16() + } + if updateMask&groupUpdateFlagAuras != 0 { + auras, err := readPlayerAurasFromStats(r) + if err != nil { + return service.PlayerStateSnapshot{}, err + } + snapshot.AurasKnown = true + snapshot.Auras = auras + } + + if err := r.Error(); err != nil { + return service.PlayerStateSnapshot{}, err + } + + return snapshot, nil +} + +func readPlayerAurasFromStats(r *packet.Reader) ([]service.PlayerAuraSnapshot, error) { + auraMask := r.Uint64() + if err := r.Error(); err != nil { + return nil, err + } + + auras := make([]service.PlayerAuraSnapshot, 0) + for slot := 0; slot < maxGroupAuraSlots; slot++ { + if auraMask&(uint64(1)< 0 { + worldserverConnectRetryWait = wait + } + if maxWait > 0 { + worldserverConnectRetryMax = maxWait + } + if worldserverConnectRetryMax > 0 && worldserverConnectRetryWait > worldserverConnectRetryMax { + worldserverConnectRetryMax = worldserverConnectRetryWait + } +} + // GameSession represents session of the player, holds world and game sockets, routes and handles packets. type GameSession struct { ctx context.Context + cancel context.CancelFunc logger *zerolog.Logger gameSocket sockets.Socket @@ -54,22 +82,45 @@ type GameSession struct { eventsProducer events.GatewayProducer eventsBroadcaster eBroadcaster.Broadcaster chatChannelsEventsBroadcaster *eBroadcaster.ChatChannelsService + sessionRegistry GameSessionRegistry charsUpdsBarrier *service.CharactersUpdatesBarrier + playerStateUpdatesBarrier *service.PlayerStateUpdatesBarrier realmNamesService *service.RealmNamesService gameServerGRPCConnMgr conn.GameServerGRPCConnMgr groupUpdateCounter uint32 - packetProcessTimeout time.Duration + packetProcessTimeout time.Duration + worldAuthAttemptTimeout time.Duration + worldAuthSessionReadyDelay time.Duration authPacket *packet.Packet pingToWorldServerStarted time.Time - accountID uint32 - character *LoggedInCharacter - - teleportingToNewMap *uint32 + accountID uint32 + character *LoggedInCharacter + playerWorldActive bool + characterLoggedInPublished bool + worldserverID string + lastLfgProposalSuccessID uint32 + lfgDungeonActive bool + + teleportingToNewMap *uint32 + pendingMapTransferRouting *mapTransferRouting + activeMapTransferRouting *mapTransferRouting + currentMapTransferRouting *mapTransferRouting + pendingGuildCreate *pendingGuildCreateState + pendingRedirectID string + pendingRedirectAt time.Time + playerAuraState map[uint8]service.PlayerAuraSnapshot + observedPlayerAuraStates map[uint64]map[uint8]service.PlayerAuraSnapshot + renderedGroupMemberAuras map[groupMemberAuraRenderKey]map[uint8]events.GroupMemberAuraState + + worldLoginTimingMu sync.Mutex + pendingWorldLoginStarted time.Time + pendingWorldLoginGUID uint64 + pendingWorldLoginAddress string packetSendingControl PacketSendingControl @@ -83,6 +134,15 @@ type GameSession struct { showGameserverConnChangeToClient bool } +type lfgDungeonTransportState struct { + dungeonEntry uint32 + mapID uint32 + difficulty uint32 + routing *mapTransferRouting +} + +type worldPreLoginHook func(sockets.Socket) error + type GameSessionParams struct { CharServiceClient pbChar.CharactersServiceClient ServersRegistryClient pbServ.ServersRegistryServiceClient @@ -96,9 +156,13 @@ type GameSessionParams struct { RealmNamesService *service.RealmNamesService EventsBroadcaster eBroadcaster.Broadcaster ChatChannelsEventBroadcaster *eBroadcaster.ChatChannelsService + SessionRegistry GameSessionRegistry GameServerGRPCConnMgr conn.GameServerGRPCConnMgr PacketProcessTimeout time.Duration + WorldAuthAttemptTimeout time.Duration + WorldAuthSessionReadyDelay time.Duration ShowGameserverConnChangeToClient bool + PlayerStateUpdatesBarrier *service.PlayerStateUpdatesBarrier } func NewGameSession( @@ -106,14 +170,24 @@ func NewGameSession( gameSocket sockets.Socket, accountID uint32, authPacket *packet.Packet, params GameSessionParams, ) *GameSession { - const defaultPacketProcessingTimeout = time.Second * 5 + sessionCtx, sessionCancel := context.WithCancel(ctx) + packetProcessTimeout := params.PacketProcessTimeout if packetProcessTimeout == 0 { packetProcessTimeout = defaultPacketProcessingTimeout } + worldAuthReadyDelay := params.WorldAuthSessionReadyDelay + if worldAuthReadyDelay == 0 { + worldAuthReadyDelay = worldAuthSessionReadyDelay + } + worldAuthTimeout := params.WorldAuthAttemptTimeout + if worldAuthTimeout == 0 { + worldAuthTimeout = worldAuthAttemptTimeout + } s := &GameSession{ - ctx: ctx, + ctx: sessionCtx, + cancel: sessionCancel, logger: logger, gameSocket: gameSocket, authPacket: authPacket, @@ -129,15 +203,22 @@ func NewGameSession( eventsProducer: params.EventsProducer, eventsBroadcaster: params.EventsBroadcaster, chatChannelsEventsBroadcaster: params.ChatChannelsEventBroadcaster, + sessionRegistry: params.SessionRegistry, charsUpdsBarrier: params.CharsUpdsBarrier, + playerStateUpdatesBarrier: params.PlayerStateUpdatesBarrier, realmNamesService: params.RealmNamesService, gameServerGRPCConnMgr: params.GameServerGRPCConnMgr, showGameserverConnChangeToClient: params.ShowGameserverConnChangeToClient, - sessionSafeFuChan: make(chan func(*GameSession), 100), - packetProcessTimeout: packetProcessTimeout, - channelMembership: NewChannelMembership(0, params.ChatChannelsEventBroadcaster), - worldserverChannelBuffer: make([]WorldserverChannelInfo, 0), + sessionSafeFuChan: make(chan func(*GameSession), 100), + packetProcessTimeout: packetProcessTimeout, + worldAuthAttemptTimeout: worldAuthTimeout, + worldAuthSessionReadyDelay: worldAuthReadyDelay, + channelMembership: NewChannelMembership(0, params.ChatChannelsEventBroadcaster), + worldserverChannelBuffer: make([]WorldserverChannelInfo, 0), + } + if s.sessionRegistry != nil { + s.sessionRegistry.RegisterAccount(s) } return s } @@ -145,9 +226,20 @@ func NewGameSession( // HandlePackets handles game and world packets, as well as general events (like messages). // Has infinite loop that can be broken with ctx or by closing gameSocket read channel. func (s *GameSession) HandlePackets(ctx context.Context) { - c, cancel := context.WithCancel(ctx) + c, cancel := context.WithCancel(s.ctx) + if ctx != nil { + go func() { + select { + case <-ctx.Done(): + cancel() + case <-c.Done(): + } + }() + } defer cancel() + defer s.cancelSession() defer s.logger.Debug().Msg("Stopped to handle packets") + defer s.unregisterFromSessionRegistry() defer func() { if s.character != nil { @@ -217,6 +309,8 @@ func (s *GameSession) HandlePackets(ctx context.Context) { break } + s.observeWorldLoginVerify(p) + handler, found := HandleMap[p.Opcode] if !found { if s.gameSocket != nil { @@ -243,11 +337,31 @@ func (s *GameSession) HandlePackets(ctx context.Context) { } } +func (s *GameSession) cancelSession() { + if s.cancel != nil { + s.cancel() + } +} + +func (s *GameSession) CanRestartConnectionAuthSession() bool { + return s != nil && s.character == nil && s.worldSocket == nil +} + +func (s *GameSession) StopForConnectionAuthSession() { + s.cancelSession() +} + func (s *GameSession) Login(ctx context.Context, p *packet.Packet) error { // Reset sending control for new login. s.packetSendingControl = PacketSendingControl{} - - char, socket, err := s.connectToGameServer(ctx, p.Reader().Uint64(), nil, nil) + s.playerWorldActive = false + s.characterLoggedInPublished = false + s.worldserverID = "" + s.pendingRedirectID = "" + s.pendingRedirectAt = time.Time{} + s.resetPlayerAuraState() + + char, socket, worldserverID, err := s.connectToGameServer(ctx, p.Reader().Uint64(), nil, nil) if err != nil { code := packet.LoginErrorCodeLoginFailed switch { @@ -288,29 +402,13 @@ func (s *GameSession) Login(ctx context.Context, p *packet.Packet) error { PetLevel: char.PetLevel, Banned: char.Banned, AccountID: char.AccountID, + ExtraFlags: char.ExtraFlags, GroupMangedByGameServer: false, } s.worldSocket = socket - - err = s.eventsProducer.CharacterLoggedIn(&events.GWEventCharacterLoggedInPayload{ - RealmID: root.RealmID, - GatewayID: root.RetrievedGatewayID, - CharGUID: char.GUID, - CharName: char.Name, - CharRace: uint8(char.Race), - CharClass: uint8(char.Class), - CharGender: uint8(char.Gender), - CharLevel: uint8(char.Level), - CharZone: char.Zone, - CharMap: char.Map, - CharPosX: char.PositionX, - CharPosY: char.PositionY, - CharPosZ: char.PositionZ, - CharGuildID: char.GuildID, - AccountID: char.AccountID, - }) - if err != nil { - s.logger.Err(err).Msg("can't send login event") + s.worldserverID = worldserverID + if s.sessionRegistry != nil { + s.sessionRegistry.RegisterCharacter(s, s.character.GUID) } s.eventsChan = s.eventsBroadcaster.RegisterCharacter(char.GUID) @@ -356,13 +454,12 @@ func (s *GameSession) ReadyForAccountDataTimes(ctx context.Context, p *packet.Pa return err } - globalCacheMask := uint32(0x15) resp := packet.NewWriterWithSize(packet.SMsgAccountDataTimes, 4+1+4+8*4) resp.Uint32(uint32(time.Now().Unix())) resp.Uint8(1) - resp.Uint32(globalCacheMask) + resp.Uint32(globalAccountDataMask) for i := uint32(0); i < 8; i++ { - if globalCacheMask&(uint32(1)< 0 { + if globalAccountDataMask&(uint32(1)< 0 { found := false for _, data := range accountData.AccountData { if data.Type == i { @@ -404,91 +501,519 @@ func (s *GameSession) InterceptPong(ctx context.Context, p *packet.Packet) error return nil } -func (s *GameSession) connectToGameServer(ctx context.Context, characterGUID uint64, mapID *uint32, preLoginHook func(sockets.Socket)) (*pbChar.LogInCharacter, sockets.Socket, error) { +func (s *GameSession) connectToGameServer(ctx context.Context, characterGUID uint64, mapID *uint32, preLoginHook worldPreLoginHook) (*pbChar.LogInCharacter, sockets.Socket, string, error) { + if _, ok := ctx.Deadline(); !ok { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, s.packetProcessTimeout) + defer cancel() + } + + charLookupStarted := time.Now() r, err := s.charServiceClient.CharactersToLoginByGUID(ctx, &pbChar.CharactersToLoginByGUIDRequest{ Api: root.SupportedCharServiceVer, CharacterGUID: characterGUID, RealmID: root.RealmID, + AccountID: s.accountID, }) if err != nil { - return nil, nil, fmt.Errorf("can't get characters to login, err: %w", err) + return nil, nil, "", fmt.Errorf("can't get characters to login, err: %w", err) } if r.Character == nil { - return nil, nil, fmt.Errorf("char id: %q, err: %w", characterGUID, worldConnectErrCharacterNotFound) + return nil, nil, "", fmt.Errorf("char id: %q, err: %w", characterGUID, worldConnectErrCharacterNotFound) } + if r.Character.AccountID != s.accountID { + s.logger.Error(). + Uint32("sessionAccount", s.accountID). + Uint32("characterAccount", r.Character.AccountID). + Uint64("character", characterGUID). + Msg("Blocked cross-account character login") + return nil, nil, "", fmt.Errorf("char id: %q, err: %w", characterGUID, worldConnectErrCharacterNotFound) + } + s.logger.Debug(). + Uint64("character", characterGUID). + Dur("duration", time.Since(charLookupStarted)). + Msg("Resolved character for worldserver login") mapIDToLogin := r.Character.Map if mapID != nil { mapIDToLogin = *mapID } + persistentLfgRoute, persistentLfgBlocked, err := s.lfgPersistentRouteForMap(ctx, characterGUID, mapIDToLogin) + if err != nil { + return nil, nil, "", fmt.Errorf("can't resolve persisted LFG dungeon route, err: %w", err) + } + if persistentLfgBlocked { + return r.Character, nil, "", fmt.Errorf("%w, persisted LFG route for mapID %v has no bound instance", worldConnectErrInstanceNotFound, mapIDToLogin) + } - serversResult, err := s.serversRegistryClient.AvailableGameServersForMapAndRealm(s.ctx, &pbServ.AvailableGameServersForMapAndRealmRequest{ - Api: root.SupportedCharServiceVer, - RealmID: root.RealmID, - MapID: mapIDToLogin, - }) + var lastErr error + for attempt := 1; ctx.Err() == nil; attempt++ { + registryLookupStarted := time.Now() + loginCharacterGUID := characterGUID + transferRouting := (*mapTransferRouting)(nil) + lookupRealmID := root.RealmID + lookupCrossRealm := false + var serversResult *pbServ.AvailableGameServersForMapAndRealmResponse + var err error + if persistentLfgRoute != nil { + transferRouting = lfgRouteMapTransferRouting(persistentLfgRoute) + loginCharacterGUID = mapTransferLoginPlayerGUID(characterGUID, transferRouting) + lookupRealmID = persistentLfgRoute.GetOwnerRealmID() + lookupCrossRealm = persistentLfgRoute.GetIsCrossRealm() + serversResult, err = s.serversRegistryClient.AvailableGameServersForMapAndRealm(ctx, &pbServ.AvailableGameServersForMapAndRealmRequest{ + Api: root.SupportedServerRegistryVer, + RealmID: lookupRealmID, + MapID: mapIDToLogin, + IsCrossRealm: lookupCrossRealm, + }) + } else { + serversResult, err = s.serversRegistryClient.AvailableGameServersForMapAndRealm(ctx, &pbServ.AvailableGameServersForMapAndRealmRequest{ + Api: root.SupportedServerRegistryVer, + RealmID: root.RealmID, + MapID: mapIDToLogin, + }) + } - if err != nil { - return nil, nil, fmt.Errorf("can't get available game servers for map, err: %w", err) + if err != nil { + return nil, nil, "", fmt.Errorf("can't get available game servers for map, err: %w", err) + } + if len(serversResult.GameServers) == 0 && persistentLfgRoute == nil { + crossrealmResult, err := s.serversRegistryClient.AvailableGameServersForMapAndRealm(ctx, &pbServ.AvailableGameServersForMapAndRealmRequest{ + Api: root.SupportedServerRegistryVer, + RealmID: 0, + MapID: mapIDToLogin, + IsCrossRealm: true, + }) + if err != nil { + return nil, nil, "", fmt.Errorf("can't get available crossrealm game servers for map, err: %w", err) + } + if len(crossrealmResult.GetGameServers()) > 0 { + serversResult = crossrealmResult + transferRouting = &mapTransferRouting{realmID: 0, isCrossRealm: true} + loginCharacterGUID = mapTransferLoginPlayerGUID(characterGUID, transferRouting) + lookupRealmID = 0 + lookupCrossRealm = true + s.logger.Warn(). + Uint64("character", characterGUID). + Uint64("loginCharacter", loginCharacterGUID). + Uint32("map", mapIDToLogin). + Uint32("homeRealm", root.RealmID). + Msg("Falling back to crossrealm worldserver for persisted character map") + } + } + + if len(serversResult.GameServers) == 0 { + lastErr = fmt.Errorf("%w, mapID %v, realmID %d", worldConnectErrInstanceNotFound, mapIDToLogin, lookupRealmID) + retryEvent := s.logger.Debug() + if attempt == 1 { + retryEvent = s.logger.Warn() + } + retryEvent. + Uint64("character", characterGUID). + Uint32("map", mapIDToLogin). + Uint32("realm", lookupRealmID). + Bool("crossrealm", lookupCrossRealm). + Int("attempt", attempt). + Dur("duration", time.Since(registryLookupStarted)). + Msg("No worldserver available for map, retrying") + if !waitForWorldserverConnectRetry(ctx, attempt) { + break + } + continue + } + + attemptedCandidate := false + for candidateIndex, server := range serversResult.GameServers { + if server == nil || server.Address == "" { + continue + } + attemptedCandidate = true + + s.logger.Debug(). + Uint64("character", characterGUID). + Uint32("map", mapIDToLogin). + Uint32("realm", lookupRealmID). + Bool("crossrealm", lookupCrossRealm). + Str("address", server.Address). + Int("candidate", candidateIndex+1). + Int("candidates", len(serversResult.GameServers)). + Int("attempt", attempt). + Dur("duration", time.Since(registryLookupStarted)). + Msg("Resolved worldserver candidate for map") + + s.gameServerGRPCConnMgr.AddAddressMapping(server.Address, server.GrpcAddress) + + gameServerGRPCClient, err := s.gameServerGRPCConnMgr.GRPCConnByGameServerAddress(server.Address) + if err != nil { + return nil, nil, "", fmt.Errorf("can't get game server grpc client, err: %w", err) + } + + socket, err := s.connectToGameServerWithAddress(ctx, loginCharacterGUID, server.Address, preLoginHook) + if err == nil { + s.gameServerGRPCClient = gameServerGRPCClient + s.setCurrentMapTransferRouting(transferRouting) + return r.Character, socket, gameServerSourceID(server), nil + } + + lastErr = err + if !isRetryableWorldConnectError(err) { + return r.Character, nil, "", err + } + + retryEvent := s.logger.Debug() + if attempt == 1 { + retryEvent = s.logger.Warn() + } + retryEvent. + Err(err). + Uint64("character", characterGUID). + Uint64("loginCharacter", loginCharacterGUID). + Uint32("map", mapIDToLogin). + Uint32("realm", lookupRealmID). + Bool("crossrealm", lookupCrossRealm). + Str("address", server.Address). + Int("candidate", candidateIndex+1). + Int("candidates", len(serversResult.GameServers)). + Int("attempt", attempt). + Msg("Worldserver login candidate failed") + } + if !attemptedCandidate { + lastErr = fmt.Errorf("%w, mapID %v, realmID %d", worldConnectErrInstanceNotFound, mapIDToLogin, lookupRealmID) + } + + if !waitForWorldserverConnectRetry(ctx, attempt) { + break + } } - if len(serversResult.GameServers) == 0 { - return nil, nil, fmt.Errorf("%w, mapID %v", worldConnectErrInstanceNotFound, mapIDToLogin) + if lastErr != nil { + return r.Character, nil, "", lastErr } - s.gameServerGRPCConnMgr.AddAddressMapping(serversResult.GameServers[0].Address, serversResult.GameServers[0].GrpcAddress) + return r.Character, nil, "", ctx.Err() +} + +func gameServerSourceID(server *pbServ.Server) string { + if server == nil { + return "" + } + if server.Id != "" { + return server.Id + } + return server.Address +} + +func (s *GameSession) canonicalWorldserverIDForAddress(ctx context.Context, address string) string { + if address == "" || s.serversRegistryClient == nil { + return address + } - s.gameServerGRPCClient, err = s.gameServerGRPCConnMgr.GRPCConnByGameServerAddress(serversResult.GameServers[0].Address) + resp, err := s.serversRegistryClient.ListAllGameServers(ctx, &pbServ.ListAllGameServersRequest{ + Api: root.SupportedServerRegistryVer, + }) if err != nil { - return nil, nil, fmt.Errorf("can't get game server grpc client, err: %w", err) + if s.logger != nil { + s.logger.Debug().Err(err).Str("address", address).Msg("can't resolve canonical worldserver id") + } + return address + } + + for _, server := range resp.GameServers { + if server.Address == address && server.ID != "" { + return server.ID + } } - socket, err := s.connectToGameServerWithAddress(ctx, characterGUID, serversResult.GameServers[0].Address, preLoginHook) - return r.Character, socket, err + return address } -func (s *GameSession) connectToGameServerWithAddress(ctx context.Context, characterGUID uint64, gameserverAddress string, preLoginHook func(sockets.Socket)) (sockets.Socket, error) { +func (s *GameSession) connectToGameServerWithAddress(ctx context.Context, characterGUID uint64, gameserverAddress string, preLoginHook worldPreLoginHook) (sockets.Socket, error) { + connectStarted := time.Now() s.logger.Debug(). Str("address", gameserverAddress). + Uint64("character", characterGUID). Msg("Connecting to the world server") socket, err := WorldSocketCreator(s.logger, gameserverAddress) if err != nil { - return nil, fmt.Errorf("can't connect to the world server, err: %w", err) + return nil, fmt.Errorf("%w: %v", worldConnectErrSocketDialFailed, err) } + s.logger.Debug(). + Str("address", gameserverAddress). + Uint64("character", characterGUID). + Dur("duration", time.Since(connectStarted)). + Msg("Connected TCP socket to world server") go socket.ListenAndProcess(s.ctx) + authStarted := time.Now() socket.SendPacket(s.authPacket) - - select { - case p, open := <-socket.ReadChannel(): - if !open { - return nil, fmt.Errorf("world socket closed") - } - if p.Opcode != packet.SMsgAuthChallenge { - socket.WriteChannel() <- p + authTimeout := s.worldAuthAttemptTimeout + if authTimeout == 0 { + authTimeout = worldAuthAttemptTimeout + } + if deadline, ok := ctx.Deadline(); ok { + if remaining := time.Until(deadline); remaining < authTimeout { + authTimeout = remaining } - case <-ctx.Done(): - return nil, ctx.Err() + } + authCtx, cancel := context.WithTimeout(ctx, authTimeout) + defer cancel() + if err := s.waitForWorldAuthResponse(authCtx, socket, characterGUID, gameserverAddress, authStarted); err != nil { + socket.Close() + return nil, err } - // we need give some time to add session on the world side - time.Sleep(time.Millisecond * 200) + worldAuthReadyDelay := s.worldAuthSessionReadyDelay + if worldAuthReadyDelay == 0 { + worldAuthReadyDelay = worldAuthSessionReadyDelay + } + if err := s.waitForWorldAuthSessionReady(ctx, socket, characterGUID, gameserverAddress, worldAuthReadyDelay); err != nil { + socket.Close() + return nil, err + } if preLoginHook != nil { - preLoginHook(socket) + if err := preLoginHook(socket); err != nil { + socket.Close() + return nil, fmt.Errorf("pre-login hook failed for character %d on %s: %w", characterGUID, gameserverAddress, err) + } } resp := packet.NewWriterWithSize(packet.CMsgPlayerLogin, 8) resp.Uint64(characterGUID) socket.Send(resp) + s.markWorldLoginSent(characterGUID, gameserverAddress) + s.logger.Debug(). + Str("address", gameserverAddress). + Uint64("character", characterGUID). + Dur("duration", time.Since(connectStarted)). + Msg("Sent CMsgPlayerLogin to world server") return socket, nil } +func (s *GameSession) connectToGameServerWithAddressRetry(ctx context.Context, characterGUID uint64, gameserverAddress string, preLoginHook worldPreLoginHook) (sockets.Socket, error) { + var lastErr error + for attempt := 1; ctx.Err() == nil; attempt++ { + socket, err := s.connectToGameServerWithAddress(ctx, characterGUID, gameserverAddress, preLoginHook) + if err == nil { + return socket, nil + } + if !isRetryableWorldConnectError(err) { + return nil, err + } + + lastErr = err + event := s.logger.Debug() + if attempt == 1 { + event = s.logger.Warn() + } + event. + Err(err). + Uint64("character", characterGUID). + Str("address", gameserverAddress). + Int("attempt", attempt). + Msg("Worldserver redirect login attempt failed, retrying") + + if !waitForWorldserverConnectRetry(ctx, attempt) { + break + } + } + + if lastErr != nil { + return nil, lastErr + } + return nil, ctx.Err() +} + +func (s *GameSession) markWorldLoginSent(characterGUID uint64, gameserverAddress string) { + s.worldLoginTimingMu.Lock() + defer s.worldLoginTimingMu.Unlock() + + s.pendingWorldLoginStarted = time.Now() + s.pendingWorldLoginGUID = characterGUID + s.pendingWorldLoginAddress = gameserverAddress +} + +func (s *GameSession) observeWorldLoginVerify(p *packet.Packet) { + if p.Opcode != packet.SMsgLoginVerifyWorld { + return + } + + s.markPlayerWorldActive() + + s.worldLoginTimingMu.Lock() + started := s.pendingWorldLoginStarted + characterGUID := s.pendingWorldLoginGUID + address := s.pendingWorldLoginAddress + s.pendingWorldLoginStarted = time.Time{} + s.pendingWorldLoginGUID = 0 + s.pendingWorldLoginAddress = "" + s.worldLoginTimingMu.Unlock() + + if started.IsZero() { + return + } + + duration := time.Since(started) + event := s.logger.Debug() + if duration >= slowWorldLoginVerifyAfter { + event = s.logger.Warn() + } + + event. + Str("address", address). + Uint64("character", characterGUID). + Dur("duration", duration). + Msg("Worldserver login verify reached client") +} + +func (s *GameSession) waitForWorldAuthResponse(ctx context.Context, socket sockets.Socket, characterGUID uint64, gameserverAddress string, started time.Time) error { + challengeReceived := false + for { + select { + case p, open := <-socket.ReadChannel(): + if !open { + return fmt.Errorf("%w, address: %s", worldConnectErrAuthSocketClosed, gameserverAddress) + } + + switch p.Opcode { + case packet.SMsgAuthChallenge: + challengeReceived = true + s.logger.Debug(). + Str("address", gameserverAddress). + Uint64("character", characterGUID). + Dur("duration", time.Since(started)). + Msg("Received worldserver auth challenge") + case packet.SMsgAuthResponse: + authStatus := uint8(0) + if p.Size > 0 { + authStatus = p.Reader().Uint8() + if authStatus != worldAuthResponseOK { + return fmt.Errorf("world auth response failed with status %d, address: %s", authStatus, gameserverAddress) + } + } + s.logger.Debug(). + Str("address", gameserverAddress). + Uint64("character", characterGUID). + Uint8("status", authStatus). + Bool("challengeReceived", challengeReceived). + Dur("duration", time.Since(started)). + Msg("Received worldserver auth response") + return nil + default: + return fmt.Errorf("unexpected world packet %s before auth response, address: %s", p.Opcode.String(), gameserverAddress) + } + case <-ctx.Done(): + return fmt.Errorf("%w from %s: %v", worldConnectErrAuthTimeout, gameserverAddress, ctx.Err()) + } + } +} + +func isRetryableWorldConnectError(err error) bool { + return errors.Is(err, worldConnectErrInstanceNotFound) || + errors.Is(err, worldConnectErrAuthTimeout) || + errors.Is(err, worldConnectErrAuthSocketClosed) || + errors.Is(err, worldConnectErrSocketDialFailed) +} + +func waitForWorldserverConnectRetry(ctx context.Context, attempt int) bool { + if worldserverConnectRetryWait <= 0 { + return ctx.Err() == nil + } + + timer := time.NewTimer(worldserverConnectRetryDelay(attempt)) + defer timer.Stop() + + select { + case <-ctx.Done(): + return false + case <-timer.C: + return ctx.Err() == nil + } +} + +func worldserverConnectRetryDelay(attempt int) time.Duration { + if worldserverConnectRetryWait <= 0 { + return 0 + } + if attempt < 1 { + attempt = 1 + } + + delay := worldserverConnectRetryWait + for i := 1; i < attempt; i++ { + if worldserverConnectRetryMax > 0 && delay >= worldserverConnectRetryMax { + return worldserverConnectRetryMax + } + delay *= 2 + if worldserverConnectRetryMax > 0 && delay > worldserverConnectRetryMax { + return worldserverConnectRetryMax + } + } + + return delay +} + +func (s *GameSession) waitForWorldAuthSessionReady(ctx context.Context, socket sockets.Socket, characterGUID uint64, gameserverAddress string, fallbackDelay time.Duration) error { + if fallbackDelay <= 0 { + if ctx.Err() != nil { + return fmt.Errorf("%w from %s: %v", worldConnectErrAuthTimeout, gameserverAddress, ctx.Err()) + } + return nil + } + + timer := time.NewTimer(fallbackDelay) + defer timer.Stop() + + for { + select { + case p, open := <-socket.ReadChannel(): + if !open { + return fmt.Errorf("%w, address: %s", worldConnectErrAuthSocketClosed, gameserverAddress) + } + + if p.Opcode != packet.TC9SMsgWorldSessionReady { + return fmt.Errorf("unexpected world packet %s before world session ready ack, address: %s", p.Opcode.String(), gameserverAddress) + } + + s.logger.Debug(). + Str("address", gameserverAddress). + Uint64("character", characterGUID). + Msg("Received TC9 world session ready ack") + return nil + case <-timer.C: + s.logger.Debug(). + Str("address", gameserverAddress). + Uint64("character", characterGUID). + Dur("fallbackDelay", fallbackDelay). + Msg("World session ready ack not observed before fallback delay") + return nil + case <-ctx.Done(): + return fmt.Errorf("%w from %s: %v", worldConnectErrAuthTimeout, gameserverAddress, ctx.Err()) + } + } +} + +func waitForWorldAuthSessionReady(ctx context.Context, delay time.Duration) bool { + if delay <= 0 { + return ctx.Err() == nil + } + + timer := time.NewTimer(delay) + defer timer.Stop() + + select { + case <-ctx.Done(): + return false + case <-timer.C: + return ctx.Err() == nil + } +} + func (s *GameSession) processWorldPacketsInPlace(ctx context.Context, f func(*packet.Packet) (stopProcessing bool, err error)) error { if s.worldSocket == nil { return nil @@ -515,97 +1040,230 @@ func (s *GameSession) processWorldPacketsInPlace(ctx context.Context, f func(*pa } func (s *GameSession) onWorldSocketClosed() { + if s.character == nil { + return + } + if s.pendingRedirectID != "" { + if s.logger != nil { + s.logger.Debug(). + Str("redirect", s.pendingRedirectID). + Uint32("account", s.accountID). + Uint64("character", s.character.GUID). + Msg("TC9 ignoring source world socket close during cross-worldserver redirect") + } + return + } + + character := *s.character + if !s.canReconnectCharacter(character.GUID) { + return + } + + go func(character LoggedInCharacter) { + if !s.canReconnectCharacter(character.GUID) { + return + } - go func(charGUID uint64) { s.SendSysMessage("Lost connection with world server...") - time.Sleep(time.Second * 2) // giving some time to recover + if !s.waitForReconnectDelay(time.Second * 2) { + return + } + if !s.canReconnectCharacter(character.GUID) { + return + } s.SendSysMessage("Trying to recover...") - time.Sleep(time.Second * 1) // giving some time to recover + if !s.waitForReconnectDelay(time.Second) { + return + } var err error var char *pbChar.LogInCharacter var socket sockets.Socket + var worldserverID string for i := 0; i < 3; i++ { - char, socket, err = s.connectToGameServer(context.TODO(), charGUID, nil, func(_ sockets.Socket) { - _, err := s.charServiceClient.SavePlayerPosition(context.TODO(), &pbChar.SavePlayerPositionRequest{ + if !s.canReconnectCharacter(character.GUID) { + return + } + + char, socket, worldserverID, err = s.connectToGameServer(s.ctx, character.GUID, nil, func(_ sockets.Socket) error { + saveCtx, cancel := context.WithTimeout(s.ctx, s.packetProcessTimeout) + defer cancel() + _, err := s.charServiceClient.SavePlayerPosition(saveCtx, &pbChar.SavePlayerPositionRequest{ Api: root.SupportedCharServiceVer, RealmID: root.RealmID, - CharGUID: s.character.GUID, - MapID: s.character.Map, - X: s.character.PositionX, - Y: s.character.PositionY, - Z: s.character.PositionZ, - O: s.character.PositionO, + CharGUID: character.GUID, + MapID: character.Map, + X: character.PositionX, + Y: character.PositionY, + Z: character.PositionZ, + O: character.PositionO, }) if err != nil { s.logger.Error().Err(err).Msg("can't save player position") + return err } + return nil }) if err != nil { s.logger.Error().Err(err).Msg("failed to reconnect player to the world") } else { break } - time.Sleep(time.Second * 5) // giving some time to recover + if !s.waitForReconnectDelay(time.Second * 5) { + return + } } if err != nil { + if !s.canReconnectCharacter(character.GUID) { + return + } + s.SendSysMessage("Failed :( Returning to the characters screen.") - time.Sleep(time.Second * 2) // giving some time for player to read the message + if !s.waitForReconnectDelay(time.Second * 2) { + return + } resp := packet.NewWriterWithSize(packet.SMsgCharacterLoginFailed, 1) resp.Uint8(uint8(packet.LoginErrorCodeWorldServerIsDown)) s.gameSocket.Send(resp) return } + if !s.canReconnectCharacter(character.GUID) { + socket.Close() + return + } - resp := packet.NewWriterWithSize(packet.SMsgNewWorld, 0) - resp.Uint32(char.Map) - resp.Float32(s.character.PositionX) - resp.Float32(s.character.PositionY) - resp.Float32(s.character.PositionZ) - resp.Float32(0.0) - s.gameSocket.Send(resp) + s.gameSocket.Send(recoveredNewWorldPacket(char, character)) + + // We need to modify the session in the packet handler goroutine. + updateSession := func(session *GameSession) { + if !session.canReconnectCharacter(character.GUID) { + socket.Close() + return + } - // we need to modify session in a safe thread (goroutine) - s.sessionSafeFuChan <- func(session *GameSession) { - if session.character != nil { + if session.character != nil && session.character.GUID == character.GUID { session.worldSocket = socket + session.worldserverID = worldserverID + session.resetPlayerAuraState() } if session.showGameserverConnChangeToClient { - session.SendSysMessage(fmt.Sprintf("Connection recovered! New gameserver: %s. Sorry for inconvenience.", s.worldSocket.Address())) + session.SendSysMessage(fmt.Sprintf("Connection recovered! New gameserver: %s. Sorry for inconvenience.", socket.Address())) } else { session.SendSysMessage("Connection recovered! Sorry for inconvenience.") } } - }(s.character.GUID) + select { + case s.sessionSafeFuChan <- updateSession: + case <-s.ctx.Done(): + socket.Close() + return + } + }(character) +} + +func recoveredNewWorldPacket(char *pbChar.LogInCharacter, character LoggedInCharacter) *packet.Writer { + resp := packet.NewWriterWithSize(packet.SMsgNewWorld, 0) + resp.Uint32(char.Map) + resp.Float32(character.PositionX) + resp.Float32(character.PositionY) + resp.Float32(character.PositionZ) + resp.Float32(character.PositionO) + return resp +} + +func (s *GameSession) waitForReconnectDelay(delay time.Duration) bool { + if delay <= 0 { + return s.ctx == nil || s.ctx.Err() == nil + } + + timer := time.NewTimer(delay) + defer timer.Stop() + + select { + case <-timer.C: + return s.ctx == nil || s.ctx.Err() == nil + case <-s.ctx.Done(): + return false + } +} + +func (s *GameSession) canReconnectCharacter(characterGUID uint64) bool { + if s.ctx != nil && s.ctx.Err() != nil { + return false + } + if s.gameSocket == nil { + return false + } + + return s.isActiveAccountSession() && s.isActiveCharacterSession(characterGUID) } func (s *GameSession) onLoggedOut() { if s.character == nil { + s.playerWorldActive = false + s.characterLoggedInPublished = false return } + ownsCharacterSession := s.isActiveCharacterSession(s.character.GUID) + if ownsCharacterSession && s.sessionRegistry != nil { + s.sessionRegistry.UnregisterCharacter(s, s.character.GUID) + } - err := s.eventsProducer.CharacterLoggedOut(&events.GWEventCharacterLoggedOutPayload{ - RealmID: root.RealmID, - GatewayID: root.RetrievedGatewayID, - CharGUID: s.character.GUID, - CharName: s.character.Name, - CharGuildID: s.character.GuildID, - AccountID: s.character.AccountID, - }) - if err != nil { - s.logger.Err(err).Msg("can't send logout event") + if ownsCharacterSession { + s.publishOfflinePlayerStateSnapshot() } - s.eventsBroadcaster.UnregisterCharacter(s.character.GUID) - s.chatChannelsEventsBroadcaster.DisconnectPlayer(s.character.GUID) - s.channelMembership.events = nil + if ownsCharacterSession && s.eventsProducer != nil { + err := s.eventsProducer.CharacterLoggedOut(&events.GWEventCharacterLoggedOutPayload{ + RealmID: root.RealmID, + GatewayID: root.RetrievedGatewayID, + CharGUID: s.character.GUID, + CharName: s.character.Name, + CharGuildID: s.character.GuildID, + AccountID: s.character.AccountID, + }) + if err != nil { + s.logger.Err(err).Msg("can't send logout event") + } + } + + if ownsCharacterSession && s.eventsBroadcaster != nil { + s.eventsBroadcaster.UnregisterCharacter(s.character.GUID) + } + s.eventsChan = nil + if ownsCharacterSession && s.chatChannelsEventsBroadcaster != nil { + s.chatChannelsEventsBroadcaster.DisconnectPlayer(s.character.GUID) + } + if s.channelMembership != nil { + s.channelMembership.events = nil + } + s.resetPlayerAuraState() s.character = nil + s.playerWorldActive = false + s.characterLoggedInPublished = false + s.worldserverID = "" +} + +func (s *GameSession) isActiveAccountSession() bool { + if s.sessionRegistry == nil { + return true + } + + return s.sessionRegistry.IsAccountSession(s) +} + +func (s *GameSession) isActiveCharacterSession(characterGUID uint64) bool { + if s.sessionRegistry == nil { + return true + } + + return s.sessionRegistry.IsCharacterSession(s, characterGUID) } var WorldSocketCreator = worldsocket.NewWorldSocketWithAddress @@ -618,6 +1276,10 @@ type PacketSendingControl struct { accountDataTimesPerCharSent bool } +type pendingGuildCreateState struct { + name string +} + // LoggedInCharacter represents a character that is logged in and bound to the session. // Some values are cached values and can be not actual values from gameserver. type LoggedInCharacter struct { @@ -646,12 +1308,18 @@ type LoggedInCharacter struct { PetLevel uint32 Banned bool AccountID uint32 + ExtraFlags uint32 // GroupMangedByGameServer tracks cases when player joined e.g. battleground // and the group is managed by game server but not group server. GroupMangedByGameServer bool ignoreNextInterceptToNewMap *uint32 + pvpQueueSlotsByType map[uint32]uint8 + lfgPendingJoin bool + lfgMatchmakingActive bool + lastLfgStatus events.MatchmakingLfgStatusPayload + lfgDungeonTransport *lfgDungeonTransportState // bgInviteOrderingFix handles race conditions between Invite and JoinToQueue events // for battleground queuing. It contains state to ensure correct event ordering: diff --git a/apps/gateway/session/social.go b/apps/gateway/session/social.go index bbbd8f0..c47cc26 100644 --- a/apps/gateway/session/social.go +++ b/apps/gateway/session/social.go @@ -3,33 +3,35 @@ package session import ( "context" "fmt" + "strings" root "github.com/walkline/ToCloud9/apps/gateway" eBroadcaster "github.com/walkline/ToCloud9/apps/gateway/events-broadcaster" "github.com/walkline/ToCloud9/apps/gateway/packet" pbChar "github.com/walkline/ToCloud9/gen/characters/pb" + "github.com/walkline/ToCloud9/shared/authidentity" "github.com/walkline/ToCloud9/shared/events" ) // FriendsResult enum values matching AzerothCore protocol const ( - FriendResultDBError = 0x00 - FriendResultListFull = 0x01 - FriendResultOnline = 0x02 - FriendResultOffline = 0x03 - FriendResultNotFound = 0x04 - FriendResultRemoved = 0x05 - FriendResultAddedOnline = 0x06 - FriendResultAddedOffline = 0x07 - FriendResultAlready = 0x08 - FriendResultSelf = 0x09 - FriendResultEnemy = 0x0A - FriendResultIgnoreSelf = 0x0B - FriendResultIgnoreNotFound = 0x0C - FriendResultIgnoreAlready = 0x0D - FriendResultIgnoreAdded = 0x0E - FriendResultIgnoreRemoved = 0x0F - FriendResultIgnoreFull = 0x10 + FriendResultDBError = 0x00 + FriendResultListFull = 0x01 + FriendResultOnline = 0x02 + FriendResultOffline = 0x03 + FriendResultNotFound = 0x04 + FriendResultRemoved = 0x05 + FriendResultAddedOnline = 0x06 + FriendResultAddedOffline = 0x07 + FriendResultAlready = 0x08 + FriendResultSelf = 0x09 + FriendResultEnemy = 0x0A + FriendResultIgnoreSelf = 0x0B + FriendResultIgnoreNotFound = 0x0C + FriendResultIgnoreAlready = 0x0D + FriendResultIgnoreAdded = 0x0E + FriendResultIgnoreRemoved = 0x0F + FriendResultIgnoreFull = 0x10 ) // HandleContactList handles CMsgContactList (0x066) @@ -57,10 +59,10 @@ func (s *GameSession) HandleContactList(ctx context.Context, p *packet.Packet) e // Friends for _, friend := range resp.Friends { w.Uint64(friend.Guid) - w.Uint32(0x01) // SOCIAL_FLAG_FRIEND - w.String(friend.Note) // note comes BEFORE status! + w.Uint32(0x01) // SOCIAL_FLAG_FRIEND + w.String(friend.Note) // note comes BEFORE status! w.Uint8(uint8(friend.Status)) // 0=offline, 1=online - if friend.Status > 0 { // if online + if friend.Status > 0 { // if online w.Uint32(friend.Area) w.Uint32(friend.Level) w.Uint32(friend.ClassID) @@ -88,6 +90,36 @@ func (s *GameSession) HandleAddFriend(ctx context.Context, p *packet.Packet) err s.logger.Debug().Str("friendName", friendName).Msg("Handling add friend") + if strings.Contains(friendName, "@") { + if !authidentity.IsValidEmail(friendName) { + s.SendFriendStatus(FriendResultNotFound, 0, "", 0, 0, 0, 0) + return nil + } + friendName = authidentity.NormalizeLoginIdentity(friendName) + friendResp, err := s.charServiceClient.AddRealIDFriendByEmail(ctx, &pbChar.AddRealIDFriendByEmailRequest{ + Api: root.Ver, + RealmID: root.RealmID, + PlayerGUID: s.character.GUID, + AccountID: s.character.AccountID, + Email: friendName, + Note: note, + }) + if err != nil { + return fmt.Errorf("failed to add real id friend: %w", err) + } + + s.SendFriendStatus( + friendResp.Result, + friendResp.FriendGUID, + note, + uint8(friendResp.Status), + friendResp.Area, + friendResp.Level, + friendResp.ClassID, + ) + return nil + } + // Resolve friend name to GUID charResp, err := s.charServiceClient.CharacterByName(ctx, &pbChar.CharacterByNameRequest{ Api: root.Ver, @@ -314,7 +346,7 @@ func (s *GameSession) HandleWho(ctx context.Context, p *packet.Packet) error { strCount := r.Uint32() if strCount > 4 { - return fmt.Errorf("zoneCount is invalid - %d, should be <= 4", zonesCount) + return fmt.Errorf("strCount is invalid - %d, should be <= 4", strCount) } strs := make([]string, strCount) @@ -332,6 +364,7 @@ func (s *GameSession) HandleWho(ctx context.Context, p *packet.Packet) error { GuildName: guildName, RaceMask: raceMask, ClassMask: classMask, + Zones: zones, Strings: strs, }) if err != nil { diff --git a/apps/gateway/sockets/gamesocket/game-socket.go b/apps/gateway/sockets/gamesocket/game-socket.go index a4a5ee6..61f6d7a 100644 --- a/apps/gateway/sockets/gamesocket/game-socket.go +++ b/apps/gateway/sockets/gamesocket/game-socket.go @@ -8,6 +8,7 @@ import ( "encoding/binary" "fmt" "net" + "time" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -23,6 +24,18 @@ import ( // useEncryption used to disable encryption during testing var useEncryption = true +var authSessionKeyRefreshDelay = 100 * time.Millisecond +var authSessionKeyRefreshAttempts = 30 + +func ConfigureAuthSessionKeyRefresh(delay time.Duration, attempts int) { + if attempts > 0 { + authSessionKeyRefreshAttempts = attempts + } + if delay >= 0 { + authSessionKeyRefreshDelay = delay + } +} + // GameSocket socket between game client and gateway type GameSocket struct { logger zerolog.Logger @@ -89,6 +102,7 @@ func (s *GameSocket) Handshake() error { func (s *GameSocket) ListenAndProcess(ctx context.Context) error { newCtx, cancel := context.WithCancel(ctx) defer cancel() + defer close(s.readChan) s.ctx = newCtx @@ -199,19 +213,11 @@ func (s *GameSocket) AuthSession(p *packet.Packet) error { return fmt.Errorf("can't process AuthSession, err: %w", reader.Error()) } - accountObj, err := s.accountRepo.AccountByUserName(context.TODO(), account) + accountObj, err := s.accountForAuthSession(context.TODO(), account, localChallenge, theirDigest) if err != nil { return err } - if useEncryption { - t := []byte{0, 0, 0, 0} - ourDigest := sha1.Sum(slices.AppendBytes([]byte(account), t, localChallenge, s.authSeed, accountObj.SessionKeyAuth)) - if !slices.SameBytes(ourDigest[:], theirDigest) { - return fmt.Errorf("authentication failed, account: %s (%d)", account, accountObj.ID) - } - } - s.accountID = accountObj.ID s.logger = s.logger.With().Uint32("account", accountObj.ID).Logger() @@ -250,6 +256,60 @@ func (s *GameSocket) AuthSession(p *packet.Packet) error { return nil } +func (s *GameSocket) accountForAuthSession(ctx context.Context, account string, localChallenge []byte, theirDigest []byte) (*repo.Account, error) { + accountObj, err := s.accountRepo.AccountByUserName(ctx, account) + if err != nil { + return nil, err + } + if accountObj == nil { + return nil, fmt.Errorf("account %q not found", account) + } + + if !useEncryption || authSessionDigestMatches(account, localChallenge, s.authSeed, accountObj.SessionKeyAuth, theirDigest) { + return accountObj, nil + } + + var refreshedAccount *repo.Account + for attempt := 0; attempt < authSessionKeyRefreshAttempts; attempt++ { + if authSessionKeyRefreshDelay > 0 { + timer := time.NewTimer(authSessionKeyRefreshDelay) + select { + case <-timer.C: + case <-ctx.Done(): + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } + return nil, ctx.Err() + } + } + + refreshedAccount, err = s.accountRepo.AccountByUserName(ctx, account) + if err != nil { + return nil, err + } + if refreshedAccount == nil { + return nil, fmt.Errorf("account %q not found", account) + } + if authSessionDigestMatches(account, localChallenge, s.authSeed, refreshedAccount.SessionKeyAuth, theirDigest) { + return refreshedAccount, nil + } + } + + if refreshedAccount != nil { + return nil, fmt.Errorf("authentication failed, account: %s (%d)", account, refreshedAccount.ID) + } + return nil, fmt.Errorf("authentication failed, account: %s", account) +} + +func authSessionDigestMatches(account string, localChallenge []byte, authSeed []byte, sessionKey []byte, theirDigest []byte) bool { + t := []byte{0, 0, 0, 0} + ourDigest := sha1.Sum(slices.AppendBytes([]byte(account), t, localChallenge, authSeed, sessionKey)) + return slices.SameBytes(ourDigest[:], theirDigest) +} + func (s *GameSocket) Address() string { return s.conn.RemoteAddr().String() } @@ -361,6 +421,14 @@ func (s *GameSocket) processPacket(p *packet.Packet) error { if s.session == nil { return s.AuthSession(p) } + if s.session.CanRestartConnectionAuthSession() { + s.logger.Debug(). + Uint32("account", s.accountID). + Msg("Restarting character-select gateway session from client auth session") + s.session.StopForConnectionAuthSession() + s.session = nil + return s.AuthSession(p) + } default: s.readChan <- p } diff --git a/apps/gateway/sockets/packets-reader.go b/apps/gateway/sockets/packets-reader.go index c71fadf..26b85bc 100644 --- a/apps/gateway/sockets/packets-reader.go +++ b/apps/gateway/sockets/packets-reader.go @@ -63,7 +63,7 @@ func (p *PacketsReader) Next() bool { p.err = fmt.Errorf("packet size too large: %d", pack.Size) return false } - + if p.opcodeSize == 2 { pack.Opcode = packet.Opcode(binary.LittleEndian.Uint16(p.headerBuffer[2:])) } else { diff --git a/apps/gateway/sockets/socketmock/socket.go b/apps/gateway/sockets/socketmock/socket.go index 9f95b39..7e2fb04 100644 --- a/apps/gateway/sockets/socketmock/socket.go +++ b/apps/gateway/sockets/socketmock/socket.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.20.2. DO NOT EDIT. +// Code generated by mockery v2.53.6. DO NOT EDIT. package mocks @@ -14,10 +14,14 @@ type Socket struct { mock.Mock } -// Address provides a mock function with given fields: +// Address provides a mock function with no fields func (_m *Socket) Address() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Address") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -28,7 +32,7 @@ func (_m *Socket) Address() string { return r0 } -// Close provides a mock function with given fields: +// Close provides a mock function with no fields func (_m *Socket) Close() { _m.Called() } @@ -37,6 +41,10 @@ func (_m *Socket) Close() { func (_m *Socket) ListenAndProcess(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for ListenAndProcess") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -47,10 +55,14 @@ func (_m *Socket) ListenAndProcess(ctx context.Context) error { return r0 } -// ReadChannel provides a mock function with given fields: +// ReadChannel provides a mock function with no fields func (_m *Socket) ReadChannel() <-chan *packet.Packet { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ReadChannel") + } + var r0 <-chan *packet.Packet if rf, ok := ret.Get(0).(func() <-chan *packet.Packet); ok { r0 = rf() @@ -73,10 +85,14 @@ func (_m *Socket) SendPacket(_a0 *packet.Packet) { _m.Called(_a0) } -// WriteChannel provides a mock function with given fields: +// WriteChannel provides a mock function with no fields func (_m *Socket) WriteChannel() chan<- *packet.Packet { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for WriteChannel") + } + var r0 chan<- *packet.Packet if rf, ok := ret.Get(0).(func() chan<- *packet.Packet); ok { r0 = rf() @@ -89,13 +105,12 @@ func (_m *Socket) WriteChannel() chan<- *packet.Packet { return r0 } -type mockConstructorTestingTNewSocket interface { +// NewSocket creates a new instance of Socket. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSocket(t interface { mock.TestingT Cleanup(func()) -} - -// NewSocket creates a new instance of Socket. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewSocket(t mockConstructorTestingTNewSocket) *Socket { +}) *Socket { mock := &Socket{} mock.Mock.Test(t) From d9d1a4ea425d1c87a17005280bd92e329cb3f48a Mon Sep 17 00:00:00 2001 From: VG-Prog Date: Fri, 22 May 2026 21:48:13 +0200 Subject: [PATCH 05/11] feat(Cluster/Social): Add realm chat and character services Extend character, chat, and mail services for clustered realm identity, Real ID/account lookups, channel membership and moderation, crossrealm whisper policy, online-state broadcasts, and arena-team persistence used by gateway routing. --- apps/charserver/cmd/charserver/main.go | 16 +- apps/charserver/config/config.go | 6 + apps/charserver/repo/accounts.go | 31 + apps/charserver/repo/accounts_mysql.go | 254 ++++ apps/charserver/repo/arena_team.go | 1238 +++++++++++++++++ apps/charserver/repo/characters.go | 32 +- apps/charserver/repo/characters_mysql.go | 427 +++++- apps/charserver/repo/online-characters.go | 2 +- .../repo/online-characters_inmem.go | 145 +- apps/charserver/server/arena_team.go | 751 ++++++++++ apps/charserver/server/charserver.go | 295 +++- .../charserver/service/characters-listener.go | 25 +- apps/charserver/service/friends-cache.go | 159 ++- apps/charserver/service/friends.go | 539 ++++++- .../service/serversregistry-listener.go | 11 +- apps/chatserver/cmd/chatserver/main.go | 2 +- apps/chatserver/repo/channels.go | 14 +- apps/chatserver/repo/characters.go | 7 + apps/chatserver/repo/characters_inmem.go | 119 +- apps/chatserver/sender/sender.go | 22 +- apps/chatserver/server/chat-channels.go | 197 ++- apps/chatserver/server/chat.go | 124 +- apps/chatserver/service/channel-manager.go | 303 ++-- apps/chatserver/service/channels-listener.go | 12 +- .../chatserver/service/characters-listener.go | 57 +- .../service/serversregistry-listener.go | 4 +- apps/mailserver/cmd/mailserver/main.go | 14 +- 27 files changed, 4410 insertions(+), 396 deletions(-) create mode 100644 apps/charserver/repo/accounts.go create mode 100644 apps/charserver/repo/accounts_mysql.go create mode 100644 apps/charserver/repo/arena_team.go create mode 100644 apps/charserver/server/arena_team.go diff --git a/apps/charserver/cmd/charserver/main.go b/apps/charserver/cmd/charserver/main.go index b35fcc5..3af54dd 100644 --- a/apps/charserver/cmd/charserver/main.go +++ b/apps/charserver/cmd/charserver/main.go @@ -56,6 +56,13 @@ func main() { charDB.SetDBForRealm(realmID, cdb) } + authDB, err := sql.Open("mysql", conf.AuthDBConnection) + if err != nil { + log.Fatal().Err(err).Msg("can't connect to auth db") + } + defer authDB.Close() + configureDBConn(authDB) + wdb, err := sql.Open("mysql", conf.WorldDBConnection) if err != nil { log.Fatal().Err(err).Msg("can't connect to world db") @@ -69,13 +76,18 @@ func main() { } charRepo := repo.NewCharactersMYSQL(charDB) + arenaTeamsRepo := repo.NewArenaTeamsMYSQL(charDB) + accountRepo, err := repo.NewAccountsMySQL(authDB) + if err != nil { + log.Fatal().Err(err).Msg("can't create account repo") + } onlineCharsRepo := repo.NewCharactersOnlineInMem() // Friends initialization friendsOnlineCache := service.NewOnlinePlayersCache() friendsEventsProducer := events.NewFriendsServiceProducerNatsJSON(nc, charserver.Ver) - friendsService := service.NewFriendsService(charRepo, friendsOnlineCache, friendsEventsProducer) + friendsService := service.NewFriendsService(charRepo, accountRepo, friendsOnlineCache, friendsEventsProducer, service.WithRealIDCrossFaction(conf.RealIDCrossFaction)) friendsOnlineCache.SetFriendsService(friendsService) // Composite handlers to call both onlineCharsRepo and friendsOnlineCache @@ -109,7 +121,7 @@ func main() { defer srHandler.Stop() grpcServer := grpc.NewServer() - pb.RegisterCharactersServiceServer(grpcServer, server.NewCharServer(charRepo, onlineCharsRepo, onlineCharsRepo, itemsTemplate, friendsService)) + pb.RegisterCharactersServiceServer(grpcServer, server.NewCharServer(charRepo, arenaTeamsRepo, onlineCharsRepo, onlineCharsRepo, itemsTemplate, friendsService, events.NewCharactersServiceProducerNatsJSON(nc, charserver.Ver))) log.Info().Str("address", lis.Addr().String()).Msg("🚀 Characters Server Started!") diff --git a/apps/charserver/config/config.go b/apps/charserver/config/config.go index ef46335..0051e44 100644 --- a/apps/charserver/config/config.go +++ b/apps/charserver/config/config.go @@ -14,9 +14,15 @@ type Config struct { // CharDBConnection is connection string to the characters database CharDBConnection map[uint32]string `yaml:"charactersDB" env:"CHAR_DB_CONNECTION" env-separator:";" env-default:"1:trinity:trinity@tcp(127.0.0.1:3306)/characters"` + // AuthDBConnection is connection string to the auth database + AuthDBConnection string `yaml:"authDB" env:"AUTH_DB_CONNECTION" env-default:"trinity:trinity@tcp(127.0.0.1:3306)/auth"` + // WorldDBConnection is connection string to the world database WorldDBConnection string `yaml:"worldDB" env:"WORLD_DB_CONNECTION" env-default:"trinity:trinity@tcp(127.0.0.1:3306)/world"` + // RealIDCrossFaction allows Real ID account friends to appear across factions. + RealIDCrossFaction bool `yaml:"realIDCrossFaction" env:"REAL_ID_CROSS_FACTION" env-default:"true"` + // NatsURL is nats connection url NatsURL string `yaml:"natsUrl" env:"NATS_URL" env-default:"nats://nats:4222"` } diff --git a/apps/charserver/repo/accounts.go b/apps/charserver/repo/accounts.go new file mode 100644 index 0000000..dd27d4f --- /dev/null +++ b/apps/charserver/repo/accounts.go @@ -0,0 +1,31 @@ +package repo + +import "context" + +const ( + RealIDFriendStatusPending uint8 = 1 + RealIDFriendStatusAccepted uint8 = 2 +) + +type Account struct { + ID uint32 + Username string + Email string +} + +type RealIDFriendRelation struct { + AccountID uint32 + FriendAccountID uint32 + RequesterAccountID uint32 + Status uint8 + Note string +} + +type Accounts interface { + AccountByEmail(ctx context.Context, email string) (*Account, error) + RequestRealIDFriend(ctx context.Context, requesterAccountID uint32, addresseeAccountID uint32, note string) (*RealIDFriendRelation, error) + AcceptRealIDFriend(ctx context.Context, accountID uint32, requesterAccountID uint32, note string) (*RealIDFriendRelation, error) + RemoveRealIDFriend(ctx context.Context, accountID uint32, friendAccountID uint32) error + UpdateRealIDFriendNote(ctx context.Context, accountID uint32, friendAccountID uint32, note string) error + AcceptedRealIDFriends(ctx context.Context, accountID uint32) ([]*RealIDFriendRelation, error) +} diff --git a/apps/charserver/repo/accounts_mysql.go b/apps/charserver/repo/accounts_mysql.go new file mode 100644 index 0000000..b3d2579 --- /dev/null +++ b/apps/charserver/repo/accounts_mysql.go @@ -0,0 +1,254 @@ +package repo + +import ( + "context" + "database/sql" + + "github.com/walkline/ToCloud9/shared/authidentity" +) + +type accountsMySQL struct { + db *sql.DB + + accountByEmailStmt *sql.Stmt + relationByPairStmt *sql.Stmt + insertRelationStmt *sql.Stmt + updateRelationStatusStmt *sql.Stmt + acceptRelationStmt *sql.Stmt + updateRelationNoteStmt *sql.Stmt + deleteRelationStmt *sql.Stmt + acceptedRelationsStmt *sql.Stmt +} + +func NewAccountsMySQL(db *sql.DB) (Accounts, error) { + stmts := &accountsMySQL{db: db} + var err error + + if stmts.accountByEmailStmt, err = db.Prepare("SELECT id, username, email FROM account WHERE UPPER(email) = UPPER(?) OR UPPER(reg_mail) = UPPER(?) LIMIT 1"); err != nil { + return nil, err + } + if stmts.relationByPairStmt, err = db.Prepare(`SELECT account_id_low, account_id_high, requester_account_id, status, note_low, note_high + FROM tc9_realid_friends WHERE account_id_low = ? AND account_id_high = ?`); err != nil { + return nil, err + } + if stmts.insertRelationStmt, err = db.Prepare(`INSERT INTO tc9_realid_friends + (account_id_low, account_id_high, requester_account_id, status, note_low, note_high, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()) + ON DUPLICATE KEY UPDATE updated_at = updated_at`); err != nil { + return nil, err + } + if stmts.updateRelationStatusStmt, err = db.Prepare(`UPDATE tc9_realid_friends + SET status = ?, note_low = CASE WHEN account_id_low = ? THEN ? ELSE note_low END, + note_high = CASE WHEN account_id_high = ? THEN ? ELSE note_high END, + updated_at = UNIX_TIMESTAMP() + WHERE account_id_low = ? AND account_id_high = ?`); err != nil { + return nil, err + } + if stmts.acceptRelationStmt, err = db.Prepare(`UPDATE tc9_realid_friends + SET status = ?, note_low = CASE WHEN account_id_low = ? THEN ? ELSE note_low END, + note_high = CASE WHEN account_id_high = ? THEN ? ELSE note_high END, + updated_at = UNIX_TIMESTAMP() + WHERE account_id_low = ? AND account_id_high = ? AND requester_account_id = ? AND status = ?`); err != nil { + return nil, err + } + if stmts.updateRelationNoteStmt, err = db.Prepare(`UPDATE tc9_realid_friends + SET note_low = CASE WHEN account_id_low = ? THEN ? ELSE note_low END, + note_high = CASE WHEN account_id_high = ? THEN ? ELSE note_high END, + updated_at = UNIX_TIMESTAMP() + WHERE account_id_low = ? AND account_id_high = ?`); err != nil { + return nil, err + } + if stmts.deleteRelationStmt, err = db.Prepare("DELETE FROM tc9_realid_friends WHERE account_id_low = ? AND account_id_high = ?"); err != nil { + return nil, err + } + if stmts.acceptedRelationsStmt, err = db.Prepare(`SELECT account_id_low, account_id_high, requester_account_id, status, note_low, note_high + FROM tc9_realid_friends + WHERE status = ? AND (account_id_low = ? OR account_id_high = ?) + ORDER BY updated_at DESC`); err != nil { + return nil, err + } + + return stmts, nil +} + +func (a *accountsMySQL) AccountByEmail(ctx context.Context, email string) (*Account, error) { + if !authidentity.IsValidEmail(email) { + return nil, nil + } + email = authidentity.NormalizeLoginIdentity(email) + + account := &Account{} + if err := a.accountByEmailStmt.QueryRowContext(ctx, email, email).Scan(&account.ID, &account.Username, &account.Email); err != nil { + if err == sql.ErrNoRows { + return nil, nil + } + return nil, err + } + return account, nil +} + +func (a *accountsMySQL) RequestRealIDFriend(ctx context.Context, requesterAccountID uint32, addresseeAccountID uint32, note string) (*RealIDFriendRelation, error) { + low, high := realIDPair(requesterAccountID, addresseeAccountID) + + tx, err := a.db.BeginTx(ctx, nil) + if err != nil { + return nil, err + } + defer func() { + if tx != nil { + _ = tx.Rollback() + } + }() + + relation, err := a.realIDRelationByPairTx(ctx, tx, low, high, requesterAccountID) + if err != nil { + return nil, err + } + + if relation == nil { + noteLow, noteHigh := "", "" + if requesterAccountID == low { + noteLow = note + } else { + noteHigh = note + } + if _, err = tx.StmtContext(ctx, a.insertRelationStmt).ExecContext(ctx, low, high, requesterAccountID, RealIDFriendStatusPending, noteLow, noteHigh); err != nil { + return nil, err + } + relation, err = a.realIDRelationByPairTx(ctx, tx, low, high, requesterAccountID) + if err != nil { + return nil, err + } + } + + if relation != nil && relation.Status == RealIDFriendStatusPending && relation.RequesterAccountID != requesterAccountID { + if _, err = tx.StmtContext(ctx, a.updateRelationStatusStmt).ExecContext(ctx, RealIDFriendStatusAccepted, requesterAccountID, note, requesterAccountID, note, low, high); err != nil { + return nil, err + } + relation.Status = RealIDFriendStatusAccepted + relation.Note = note + } else if relation != nil && note != "" { + if _, err = tx.StmtContext(ctx, a.updateRelationNoteStmt).ExecContext(ctx, requesterAccountID, note, requesterAccountID, note, low, high); err != nil { + return nil, err + } + relation.Note = note + } + + if err = tx.Commit(); err != nil { + return nil, err + } + tx = nil + return relation, nil +} + +func (a *accountsMySQL) AcceptRealIDFriend(ctx context.Context, accountID uint32, requesterAccountID uint32, note string) (*RealIDFriendRelation, error) { + low, high := realIDPair(accountID, requesterAccountID) + + relation, err := a.realIDRelationByPair(ctx, low, high, accountID) + if err != nil || relation == nil { + return relation, err + } + if relation.RequesterAccountID != requesterAccountID { + return nil, nil + } + if relation.Status == RealIDFriendStatusAccepted { + return relation, nil + } + if relation.Status != RealIDFriendStatusPending || accountID == requesterAccountID { + return nil, nil + } + + if _, err := a.acceptRelationStmt.ExecContext(ctx, RealIDFriendStatusAccepted, accountID, note, accountID, note, low, high, requesterAccountID, RealIDFriendStatusPending); err != nil { + return nil, err + } + return a.realIDRelationByPair(ctx, low, high, accountID) +} + +func (a *accountsMySQL) RemoveRealIDFriend(ctx context.Context, accountID uint32, friendAccountID uint32) error { + low, high := realIDPair(accountID, friendAccountID) + _, err := a.deleteRelationStmt.ExecContext(ctx, low, high) + return err +} + +func (a *accountsMySQL) UpdateRealIDFriendNote(ctx context.Context, accountID uint32, friendAccountID uint32, note string) error { + low, high := realIDPair(accountID, friendAccountID) + _, err := a.updateRelationNoteStmt.ExecContext(ctx, accountID, note, accountID, note, low, high) + return err +} + +func (a *accountsMySQL) AcceptedRealIDFriends(ctx context.Context, accountID uint32) ([]*RealIDFriendRelation, error) { + rows, err := a.acceptedRelationsStmt.QueryContext(ctx, RealIDFriendStatusAccepted, accountID, accountID) + if err != nil { + return nil, err + } + defer rows.Close() + + var result []*RealIDFriendRelation + for rows.Next() { + relation, err := scanRealIDRelation(rows, accountID) + if err != nil { + return nil, err + } + result = append(result, relation) + } + return result, rows.Err() +} + +func (a *accountsMySQL) realIDRelationByPair(ctx context.Context, low uint32, high uint32, accountID uint32) (*RealIDFriendRelation, error) { + return scanRealIDRelationRow(a.relationByPairStmt.QueryRowContext(ctx, low, high), accountID) +} + +func (a *accountsMySQL) realIDRelationByPairTx(ctx context.Context, tx *sql.Tx, low uint32, high uint32, accountID uint32) (*RealIDFriendRelation, error) { + return scanRealIDRelationRow(tx.StmtContext(ctx, a.relationByPairStmt).QueryRowContext(ctx, low, high), accountID) +} + +func scanRealIDRelationRow(row *sql.Row, accountID uint32) (*RealIDFriendRelation, error) { + var low, high, requester uint32 + var status uint8 + var noteLow, noteHigh string + if err := row.Scan(&low, &high, &requester, &status, ¬eLow, ¬eHigh); err != nil { + if err == sql.ErrNoRows { + return nil, nil + } + return nil, err + } + return realIDRelationFromRow(accountID, low, high, requester, status, noteLow, noteHigh), nil +} + +type realIDRelationScanner interface { + Scan(dest ...interface{}) error +} + +func scanRealIDRelation(row realIDRelationScanner, accountID uint32) (*RealIDFriendRelation, error) { + var low, high, requester uint32 + var status uint8 + var noteLow, noteHigh string + if err := row.Scan(&low, &high, &requester, &status, ¬eLow, ¬eHigh); err != nil { + return nil, err + } + return realIDRelationFromRow(accountID, low, high, requester, status, noteLow, noteHigh), nil +} + +func realIDRelationFromRow(accountID uint32, low uint32, high uint32, requester uint32, status uint8, noteLow string, noteHigh string) *RealIDFriendRelation { + friendAccountID := low + note := noteHigh + if accountID == low { + friendAccountID = high + note = noteLow + } + + return &RealIDFriendRelation{ + AccountID: accountID, + FriendAccountID: friendAccountID, + RequesterAccountID: requester, + Status: status, + Note: note, + } +} + +func realIDPair(a uint32, b uint32) (uint32, uint32) { + if a < b { + return a, b + } + return b, a +} diff --git a/apps/charserver/repo/arena_team.go b/apps/charserver/repo/arena_team.go new file mode 100644 index 0000000..d9eb7de --- /dev/null +++ b/apps/charserver/repo/arena_team.go @@ -0,0 +1,1238 @@ +package repo + +import ( + "context" + "database/sql" + "errors" + "fmt" + "unicode/utf8" + + shrepo "github.com/walkline/ToCloud9/shared/repo" +) + +var ( + ErrArenaTeamNotFound = errors.New("arena team not found") + ErrArenaTeamMemberMismatch = errors.New("arena team member mismatch") + ErrArenaTeamPartySize = errors.New("invalid arena team party size") + ErrArenaTeamNotOwner = errors.New("arena petition is not owned by captain") + ErrArenaTeamInvalidType = errors.New("invalid arena team type") + ErrArenaTeamNameExists = errors.New("arena team name exists") + ErrArenaTeamAlreadyInTeam = errors.New("arena team member already belongs to bracket") + ErrArenaTeamNotEnoughSigns = errors.New("arena petition has insufficient signatures") + ErrArenaTeamRosterFull = errors.New("arena team roster is full") + ErrArenaTeamInvalidName = errors.New("invalid arena team name") + ErrArenaTeamPermission = errors.New("arena team permission denied") + ErrArenaTeamLeaderLeave = errors.New("arena team leader cannot leave") +) + +const ( + minArenaTeamNameRunes = 2 + maxArenaTeamNameRunes = 24 +) + +type ArenaTeamQueueData struct { + ArenaTeamID uint32 + TeamRating uint32 + MatchmakerRating uint32 + PreviousOpponentsTeamID uint32 +} + +type ArenaTeamCreateFromPetitionRequest struct { + RealmID uint32 + CaptainGUID uint64 + PetitionGUID uint64 + ArenaType uint8 + BackgroundColor uint32 + EmblemStyle uint8 + EmblemColor uint32 + BorderStyle uint8 + BorderColor uint32 + StartRating uint32 + StartPersonalRating uint32 + StartMatchmakerRating uint32 +} + +type ArenaTeamCreateFromPetitionResult struct { + ArenaTeamID uint32 +} + +type ArenaTeamPetitionDetails struct { + PetitionGUID uint64 + PetitionID uint32 + OwnerGUID uint64 + Name string + ArenaType uint8 + Signatures uint32 +} + +type ArenaTeamDetails struct { + ArenaTeamID uint32 + Name string + CaptainGUID uint64 + Type uint8 + Rating uint32 + WeekGames uint32 + WeekWins uint32 + SeasonGames uint32 + SeasonWins uint32 + Rank uint32 + BackgroundColor uint32 + EmblemStyle uint8 + EmblemColor uint32 + BorderStyle uint8 + BorderColor uint32 + Members []ArenaTeamDetailsMember +} + +type ArenaTeamDetailsMember struct { + PlayerGUID uint64 + Name string + Level uint8 + Class uint8 + WeekGames uint32 + WeekWins uint32 + SeasonGames uint32 + SeasonWins uint32 + PersonalRating uint32 + MatchmakerRating uint32 + MaxMMR uint32 +} + +type ArenaTeamSaveStatsRequest struct { + RealmID uint32 + ArenaTeamID uint32 + Rating uint32 + WeekGames uint32 + WeekWins uint32 + SeasonGames uint32 + SeasonWins uint32 + Rank uint32 + Slot uint32 + Members []ArenaTeamSaveStatsMember +} + +type ArenaTeamSaveStatsMember struct { + PlayerGUID uint64 + PersonalRating uint32 + WeekGames uint32 + WeekWins uint32 + SeasonGames uint32 + SeasonWins uint32 + MatchmakerRating uint32 + MaxMMR uint32 + SaveArenaStats bool +} + +type ArenaTeams interface { + QueueDataForRatedArena(ctx context.Context, realmID uint32, leaderGUID uint64, playerGUIDs []uint64, arenaType uint8, startMatchmakerRating uint32) (*ArenaTeamQueueData, error) + GetTeam(ctx context.Context, realmID uint32, arenaTeamID uint32) (*ArenaTeamDetails, error) + GetPetition(ctx context.Context, realmID uint32, petitionGUID uint64) (*ArenaTeamPetitionDetails, error) + MemberTeamForType(ctx context.Context, realmID uint32, playerGUID uint64, arenaType uint8) (uint32, bool, error) + CreateFromPetition(ctx context.Context, request ArenaTeamCreateFromPetitionRequest) (*ArenaTeamCreateFromPetitionResult, error) + AddMember(ctx context.Context, realmID uint32, arenaTeamID uint32, playerGUID uint64, personalRating uint32) error + RemoveMember(ctx context.Context, realmID uint32, arenaTeamID uint32, playerGUID uint64, actorGUID uint64) error + Disband(ctx context.Context, realmID uint32, arenaTeamID uint32, actorGUID uint64) error + SetCaptain(ctx context.Context, realmID uint32, arenaTeamID uint32, captainGUID uint64, actorGUID uint64) error + SetName(ctx context.Context, realmID uint32, arenaTeamID uint32, name string, actorGUID uint64) error + SaveStats(ctx context.Context, request ArenaTeamSaveStatsRequest) error + DeleteAll(ctx context.Context, realmID uint32) error + ValidateCharacterDelete(ctx context.Context, realmID uint32, playerGUID uint64) error + RemovePlayerFromTeams(ctx context.Context, realmID uint32, playerGUID uint64) error +} + +type ArenaTeamsMYSQL struct { + db shrepo.CharactersDB +} + +func NewArenaTeamsMYSQL(db shrepo.CharactersDB) ArenaTeams { + return &ArenaTeamsMYSQL{db: db} +} + +func (r *ArenaTeamsMYSQL) dbForRealm(realmID uint32) (*sql.DB, error) { + db := r.db.DBByRealm(realmID) + if db == nil { + return nil, fmt.Errorf("characters db not configured for realm %d", realmID) + } + return db, nil +} + +func (r *ArenaTeamsMYSQL) QueueDataForRatedArena(ctx context.Context, realmID uint32, leaderGUID uint64, playerGUIDs []uint64, arenaType uint8, startMatchmakerRating uint32) (*ArenaTeamQueueData, error) { + slot, ok := arenaSlotByType(arenaType) + if !ok { + return nil, ErrArenaTeamInvalidType + } + + if len(playerGUIDs) != int(arenaType) { + return nil, ErrArenaTeamPartySize + } + + db, err := r.dbForRealm(realmID) + if err != nil { + return nil, err + } + + teamID, teamRating, err := r.teamForLeader(ctx, db, leaderGUID, arenaType) + if err != nil { + return nil, err + } + + mmr, err := r.averageSelectedMembersMMR(ctx, db, teamID, playerGUIDs, slot, startMatchmakerRating) + if err != nil { + return nil, err + } + + return &ArenaTeamQueueData{ + ArenaTeamID: teamID, + TeamRating: teamRating, + MatchmakerRating: mmr, + PreviousOpponentsTeamID: 0, + }, nil +} + +func (r *ArenaTeamsMYSQL) GetTeam(ctx context.Context, realmID uint32, arenaTeamID uint32) (*ArenaTeamDetails, error) { + db, err := r.dbForRealm(realmID) + if err != nil { + return nil, err + } + + team := ArenaTeamDetails{} + err = db.QueryRowContext(ctx, ` +SELECT arenaTeamId, name, captainGuid, type, rating, weekGames, weekWins, seasonGames, seasonWins, `+"`rank`"+`, backgroundColor, emblemStyle, emblemColor, borderStyle, borderColor +FROM arena_team +WHERE arenaTeamId = ? +LIMIT 1`, arenaTeamID).Scan( + &team.ArenaTeamID, + &team.Name, + &team.CaptainGUID, + &team.Type, + &team.Rating, + &team.WeekGames, + &team.WeekWins, + &team.SeasonGames, + &team.SeasonWins, + &team.Rank, + &team.BackgroundColor, + &team.EmblemStyle, + &team.EmblemColor, + &team.BorderStyle, + &team.BorderColor, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrArenaTeamNotFound + } + return nil, err + } + slot, ok := arenaSlotByType(team.Type) + if !ok { + return nil, ErrArenaTeamInvalidType + } + + rows, err := db.QueryContext(ctx, ` +SELECT atm.guid, COALESCE(c.name, ''), COALESCE(c.level, 0), COALESCE(c.class, 0), atm.weekGames, atm.weekWins, atm.seasonGames, atm.seasonWins, atm.personalRating, + COALESCE(NULLIF(cas.matchMakerRating, 0), 0), COALESCE(cas.maxMMR, 0) +FROM arena_team_member AS atm +LEFT JOIN characters AS c ON c.guid = atm.guid AND c.deleteInfos_Name IS NULL +LEFT JOIN character_arena_stats AS cas ON cas.guid = atm.guid AND cas.slot = ? +WHERE atm.arenaTeamId = ? +ORDER BY atm.guid ASC`, slot, arenaTeamID) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + member := ArenaTeamDetailsMember{} + if err = rows.Scan( + &member.PlayerGUID, + &member.Name, + &member.Level, + &member.Class, + &member.WeekGames, + &member.WeekWins, + &member.SeasonGames, + &member.SeasonWins, + &member.PersonalRating, + &member.MatchmakerRating, + &member.MaxMMR, + ); err != nil { + return nil, err + } + team.Members = append(team.Members, member) + } + if err = rows.Err(); err != nil { + return nil, err + } + + return &team, nil +} + +func (r *ArenaTeamsMYSQL) GetPetition(ctx context.Context, realmID uint32, petitionGUID uint64) (*ArenaTeamPetitionDetails, error) { + db, err := r.dbForRealm(realmID) + if err != nil { + return nil, err + } + + petitionGuidLow := petitionGUID + if petitionGuidLow > 0xFFFFFFFF { + petitionGuidLow &= 0xFFFFFFFF + } + + petition, err := arenaPetitionByGUID(ctx, db, petitionGuidLow) + if err != nil { + return nil, err + } + if petition == nil { + return nil, ErrArenaTeamNotFound + } + if _, ok := arenaSlotByType(petition.petitionType); !ok { + return nil, ErrArenaTeamInvalidType + } + + signatures, err := arenaPetitionSignatureCount(ctx, db, petition.petitionID) + if err != nil { + return nil, err + } + + return &ArenaTeamPetitionDetails{ + PetitionGUID: petition.petitionGUID, + PetitionID: petition.petitionID, + OwnerGUID: petition.ownerGUID, + Name: petition.name, + ArenaType: petition.petitionType, + Signatures: signatures, + }, nil +} + +func (r *ArenaTeamsMYSQL) MemberTeamForType(ctx context.Context, realmID uint32, playerGUID uint64, arenaType uint8) (uint32, bool, error) { + if _, ok := arenaSlotByType(arenaType); !ok { + return 0, false, ErrArenaTeamInvalidType + } + + db, err := r.dbForRealm(realmID) + if err != nil { + return 0, false, err + } + + var arenaTeamID uint32 + err = db.QueryRowContext(ctx, ` +SELECT atm.arenaTeamId +FROM arena_team_member AS atm +INNER JOIN arena_team AS at ON at.arenaTeamId = atm.arenaTeamId +WHERE atm.guid = ? AND at.type = ? +LIMIT 1`, playerGUID, arenaType).Scan(&arenaTeamID) + if errors.Is(err, sql.ErrNoRows) { + return 0, false, nil + } + if err != nil { + return 0, false, err + } + return arenaTeamID, true, nil +} + +func (r *ArenaTeamsMYSQL) teamForLeader(ctx context.Context, db *sql.DB, leaderGUID uint64, arenaType uint8) (uint32, uint32, error) { + row := db.QueryRowContext(ctx, ` +SELECT at.arenaTeamId, at.rating +FROM arena_team AS at +INNER JOIN arena_team_member AS atm ON atm.arenaTeamId = at.arenaTeamId +WHERE atm.guid = ? AND at.type = ? +LIMIT 1`, leaderGUID, arenaType) + + var teamID uint32 + var teamRating uint32 + if err := row.Scan(&teamID, &teamRating); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return 0, 0, ErrArenaTeamNotFound + } + return 0, 0, err + } + + if teamRating == 0 { + teamRating = 1 + } + + return teamID, teamRating, nil +} + +func (r *ArenaTeamsMYSQL) averageSelectedMembersMMR(ctx context.Context, db *sql.DB, teamID uint32, playerGUIDs []uint64, slot uint8, startMatchmakerRating uint32) (uint32, error) { + expected := make(map[uint64]struct{}, len(playerGUIDs)) + for _, playerGUID := range playerGUIDs { + expected[playerGUID] = struct{}{} + } + + rows, err := db.QueryContext(ctx, ` +SELECT atm.guid, COALESCE(NULLIF(cas.matchMakerRating, 0), ?) AS matchMakerRating +FROM arena_team_member AS atm +LEFT JOIN character_arena_stats AS cas ON cas.guid = atm.guid AND cas.slot = ? +WHERE atm.arenaTeamId = ?`, startMatchmakerRating, slot, teamID) + if err != nil { + return 0, err + } + defer rows.Close() + + var total uint32 + var count uint32 + for rows.Next() { + var memberGUID uint64 + var memberMMR uint32 + if err := rows.Scan(&memberGUID, &memberMMR); err != nil { + return 0, err + } + + if _, ok := expected[memberGUID]; !ok { + continue + } + + total += memberMMR + count++ + delete(expected, memberGUID) + } + if err := rows.Err(); err != nil { + return 0, err + } + + if len(expected) > 0 || count != uint32(len(playerGUIDs)) { + return 0, ErrArenaTeamMemberMismatch + } + + if count == 0 { + return startMatchmakerRating, nil + } + + return total / count, nil +} + +func arenaSlotByType(arenaType uint8) (uint8, bool) { + switch arenaType { + case 2: + return 0, true + case 3: + return 1, true + case 5: + return 2, true + default: + return 0, false + } +} + +func (r *ArenaTeamsMYSQL) CreateFromPetition(ctx context.Context, request ArenaTeamCreateFromPetitionRequest) (*ArenaTeamCreateFromPetitionResult, error) { + _, ok := arenaSlotByType(request.ArenaType) + if !ok { + return nil, ErrArenaTeamInvalidType + } + + db, err := r.dbForRealm(request.RealmID) + if err != nil { + return nil, err + } + + petitionGuidLow := uint64(request.PetitionGUID) + if petitionGuidLow > 0xFFFFFFFF { + petitionGuidLow = petitionGuidLow & 0xFFFFFFFF + } + + lockName := fmt.Sprintf("tc9_arena_team_create:%d", request.RealmID) + var locked int + if err := db.QueryRowContext(ctx, "SELECT GET_LOCK(?, 5)", lockName).Scan(&locked); err != nil { + return nil, err + } + if locked != 1 { + return nil, errors.New("cannot acquire arena team creation lock") + } + defer func() { + _, _ = db.ExecContext(context.Background(), "SELECT RELEASE_LOCK(?)", lockName) + }() + + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return nil, err + } + defer func() { + _ = tx.Rollback() + }() + + petition, err := arenaPetitionForUpdate(ctx, tx, petitionGuidLow) + if err != nil { + return nil, err + } + if petition == nil { + return nil, ErrArenaTeamNotFound + } + if petition.ownerGUID != request.CaptainGUID { + return nil, ErrArenaTeamNotOwner + } + if petition.petitionType != request.ArenaType { + return nil, ErrArenaTeamInvalidType + } + if !isValidArenaTeamName(petition.name) { + return nil, ErrArenaTeamInvalidName + } + + exists, err := arenaTeamNameExists(ctx, tx, petition.name) + if err != nil { + return nil, err + } + if exists { + return nil, ErrArenaTeamNameExists + } + + signatures, err := arenaPetitionSignatures(ctx, tx, petition.petitionID) + if err != nil { + return nil, err + } + if len(signatures) < int(request.ArenaType)-1 { + return nil, ErrArenaTeamNotEnoughSigns + } + + memberGUIDs := arenaMemberGUIDsForCreate(request.CaptainGUID, signatures, request.ArenaType) + + if err = ensureArenaMembersAvailable(ctx, tx, memberGUIDs, request.ArenaType); err != nil { + return nil, err + } + + nextID, err := nextArenaTeamID(ctx, tx) + if err != nil { + return nil, err + } + + _, err = tx.ExecContext(ctx, ` +INSERT INTO arena_team (arenaTeamId, name, captainGuid, type, rating, backgroundColor, emblemStyle, emblemColor, borderStyle, borderColor) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + nextID, + petition.name, + request.CaptainGUID, + request.ArenaType, + request.StartRating, + request.BackgroundColor, + request.EmblemStyle, + request.EmblemColor, + request.BorderStyle, + request.BorderColor, + ) + if err != nil { + return nil, err + } + + for _, memberGUID := range memberGUIDs { + _, err = tx.ExecContext(ctx, ` +INSERT INTO arena_team_member (arenaTeamId, guid, personalRating) +VALUES (?, ?, ?)`, nextID, memberGUID, request.StartPersonalRating) + if err != nil { + return nil, err + } + + if err = removeArenaPetitionsAndSigns(ctx, tx, memberGUID, request.ArenaType); err != nil { + return nil, err + } + } + + _, err = tx.ExecContext(ctx, "DELETE FROM petition_sign WHERE petition_id = ?", petition.petitionID) + if err != nil { + return nil, err + } + _, err = tx.ExecContext(ctx, "DELETE FROM petition WHERE petition_id = ?", petition.petitionID) + if err != nil { + return nil, err + } + + if err = tx.Commit(); err != nil { + return nil, err + } + + return &ArenaTeamCreateFromPetitionResult{ArenaTeamID: nextID}, nil +} + +func (r *ArenaTeamsMYSQL) AddMember(ctx context.Context, realmID uint32, arenaTeamID uint32, playerGUID uint64, personalRating uint32) error { + db, err := r.dbForRealm(realmID) + if err != nil { + return err + } + + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer func() { + _ = tx.Rollback() + }() + + arenaType, err := arenaTeamTypeForUpdate(ctx, tx, arenaTeamID) + if err != nil { + return err + } + + existingTeamID, found, err := arenaMemberTeamForType(ctx, tx, playerGUID, arenaType) + if err != nil { + return err + } + if found { + if existingTeamID == arenaTeamID { + return tx.Commit() + } + return ErrArenaTeamAlreadyInTeam + } + + memberCount, err := arenaTeamMemberCount(ctx, tx, arenaTeamID) + if err != nil { + return err + } + if memberCount >= uint32(arenaType)*2 { + return ErrArenaTeamRosterFull + } + + if _, err = tx.ExecContext(ctx, ` +INSERT INTO arena_team_member (arenaTeamId, guid, personalRating) +VALUES (?, ?, ?)`, arenaTeamID, playerGUID, personalRating); err != nil { + return err + } + + if err = removeArenaPetitionsAndSigns(ctx, tx, playerGUID, arenaType); err != nil { + return err + } + + return tx.Commit() +} + +func (r *ArenaTeamsMYSQL) RemoveMember(ctx context.Context, realmID uint32, arenaTeamID uint32, playerGUID uint64, actorGUID uint64) error { + db, err := r.dbForRealm(realmID) + if err != nil { + return err + } + + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer func() { + _ = tx.Rollback() + }() + + header, err := arenaTeamHeaderForUpdate(ctx, tx, arenaTeamID) + if err != nil { + return err + } + if actorGUID != 0 { + if playerGUID == header.captainGUID { + return ErrArenaTeamLeaderLeave + } + if actorGUID != playerGUID && actorGUID != header.captainGUID { + return ErrArenaTeamPermission + } + } + + res, err := tx.ExecContext(ctx, "DELETE FROM arena_team_member WHERE arenaTeamId = ? AND guid = ?", arenaTeamID, playerGUID) + if err != nil { + return err + } + if rows, rowErr := res.RowsAffected(); rowErr == nil && rows == 0 { + return ErrArenaTeamMemberMismatch + } + + return tx.Commit() +} + +func (r *ArenaTeamsMYSQL) Disband(ctx context.Context, realmID uint32, arenaTeamID uint32, actorGUID uint64) error { + db, err := r.dbForRealm(realmID) + if err != nil { + return err + } + + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer func() { + _ = tx.Rollback() + }() + + header, err := arenaTeamHeaderForUpdate(ctx, tx, arenaTeamID) + if err != nil { + return err + } + if actorGUID != 0 && actorGUID != header.captainGUID { + return ErrArenaTeamPermission + } + + if _, err = tx.ExecContext(ctx, "DELETE FROM arena_team_member WHERE arenaTeamId = ?", arenaTeamID); err != nil { + return err + } + if _, err = tx.ExecContext(ctx, "DELETE FROM arena_team WHERE arenaTeamId = ?", arenaTeamID); err != nil { + return err + } + + return tx.Commit() +} + +func (r *ArenaTeamsMYSQL) SetCaptain(ctx context.Context, realmID uint32, arenaTeamID uint32, captainGUID uint64, actorGUID uint64) error { + db, err := r.dbForRealm(realmID) + if err != nil { + return err + } + + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer func() { + _ = tx.Rollback() + }() + + header, err := arenaTeamHeaderForUpdate(ctx, tx, arenaTeamID) + if err != nil { + return err + } + if actorGUID != 0 && actorGUID != header.captainGUID { + return ErrArenaTeamPermission + } + + isMember, err := arenaTeamHasMember(ctx, tx, arenaTeamID, captainGUID) + if err != nil { + return err + } + if !isMember { + return ErrArenaTeamMemberMismatch + } + + if _, err = tx.ExecContext(ctx, "UPDATE arena_team SET captainGuid = ? WHERE arenaTeamId = ?", captainGUID, arenaTeamID); err != nil { + return err + } + + return tx.Commit() +} + +func (r *ArenaTeamsMYSQL) SetName(ctx context.Context, realmID uint32, arenaTeamID uint32, name string, actorGUID uint64) error { + if !isValidArenaTeamName(name) { + return ErrArenaTeamInvalidName + } + + db, err := r.dbForRealm(realmID) + if err != nil { + return err + } + + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer func() { + _ = tx.Rollback() + }() + + header, err := arenaTeamHeaderForUpdate(ctx, tx, arenaTeamID) + if err != nil { + return err + } + if actorGUID != 0 && actorGUID != header.captainGUID { + return ErrArenaTeamPermission + } + + var existingTeamID uint32 + err = tx.QueryRowContext(ctx, "SELECT arenaTeamId FROM arena_team WHERE name = ? LIMIT 1", name).Scan(&existingTeamID) + if err != nil && !errors.Is(err, sql.ErrNoRows) { + return err + } + if err == nil && existingTeamID != arenaTeamID { + return ErrArenaTeamNameExists + } + + if _, err = tx.ExecContext(ctx, "UPDATE arena_team SET name = ? WHERE arenaTeamId = ?", name, arenaTeamID); err != nil { + return err + } + + return tx.Commit() +} + +func (r *ArenaTeamsMYSQL) SaveStats(ctx context.Context, request ArenaTeamSaveStatsRequest) error { + db, err := r.dbForRealm(request.RealmID) + if err != nil { + return err + } + + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer func() { + _ = tx.Rollback() + }() + + arenaType, err := arenaTeamTypeForUpdate(ctx, tx, request.ArenaTeamID) + if err != nil { + return err + } + rank, err := arenaTeamRankForRating(ctx, tx, request.ArenaTeamID, arenaType, request.Rating) + if err != nil { + return err + } + + if _, err = tx.ExecContext(ctx, ` +UPDATE arena_team +SET rating = ?, weekGames = ?, weekWins = ?, seasonGames = ?, seasonWins = ?, `+"`rank`"+` = ? +WHERE arenaTeamId = ?`, + request.Rating, + request.WeekGames, + request.WeekWins, + request.SeasonGames, + request.SeasonWins, + rank, + request.ArenaTeamID, + ); err != nil { + return err + } + + for _, member := range request.Members { + isMember, err := arenaTeamHasMember(ctx, tx, request.ArenaTeamID, member.PlayerGUID) + if err != nil { + return err + } + if !isMember { + return ErrArenaTeamMemberMismatch + } + + if _, err = tx.ExecContext(ctx, ` +UPDATE arena_team_member +SET personalRating = ?, weekGames = ?, weekWins = ?, seasonGames = ?, seasonWins = ? +WHERE arenaTeamId = ? AND guid = ?`, + member.PersonalRating, + member.WeekGames, + member.WeekWins, + member.SeasonGames, + member.SeasonWins, + request.ArenaTeamID, + member.PlayerGUID, + ); err != nil { + return err + } + + if member.SaveArenaStats { + if _, err = tx.ExecContext(ctx, ` +REPLACE INTO character_arena_stats (guid, slot, matchMakerRating, maxMMR) +VALUES (?, ?, ?, ?)`, + member.PlayerGUID, + request.Slot, + member.MatchmakerRating, + member.MaxMMR, + ); err != nil { + return err + } + } + } + + return tx.Commit() +} + +func (r *ArenaTeamsMYSQL) DeleteAll(ctx context.Context, realmID uint32) error { + db, err := r.dbForRealm(realmID) + if err != nil { + return err + } + + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer func() { + _ = tx.Rollback() + }() + + if _, err = tx.ExecContext(ctx, "DELETE FROM arena_team_member"); err != nil { + return err + } + if _, err = tx.ExecContext(ctx, "DELETE FROM arena_team"); err != nil { + return err + } + if _, err = tx.ExecContext(ctx, "DELETE FROM character_arena_stats"); err != nil { + return err + } + + return tx.Commit() +} + +func (r *ArenaTeamsMYSQL) ValidateCharacterDelete(ctx context.Context, realmID uint32, playerGUID uint64) error { + db, err := r.dbForRealm(realmID) + if err != nil { + return err + } + + var arenaTeamID uint32 + err = db.QueryRowContext(ctx, "SELECT arenaTeamId FROM arena_team WHERE captainGuid = ? LIMIT 1", playerGUID).Scan(&arenaTeamID) + if errors.Is(err, sql.ErrNoRows) { + return nil + } + if err != nil { + return err + } + + return ErrArenaTeamLeaderLeave +} + +func (r *ArenaTeamsMYSQL) RemovePlayerFromTeams(ctx context.Context, realmID uint32, playerGUID uint64) error { + db, err := r.dbForRealm(realmID) + if err != nil { + return err + } + + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer func() { + _ = tx.Rollback() + }() + + var arenaTeamID uint32 + err = tx.QueryRowContext(ctx, "SELECT arenaTeamId FROM arena_team WHERE captainGuid = ? LIMIT 1 FOR UPDATE", playerGUID).Scan(&arenaTeamID) + if err == nil { + return ErrArenaTeamLeaderLeave + } + if err != nil && !errors.Is(err, sql.ErrNoRows) { + return err + } + + if _, err = tx.ExecContext(ctx, "DELETE FROM arena_team_member WHERE guid = ?", playerGUID); err != nil { + return err + } + + return tx.Commit() +} + +type arenaPetition struct { + petitionID uint32 + ownerGUID uint64 + petitionGUID uint64 + name string + petitionType uint8 +} + +type arenaPetitionSignature struct { + playerGUID uint64 + playerAccount uint32 +} + +func arenaMemberGUIDsForCreate(captainGUID uint64, signatures []arenaPetitionSignature, arenaType uint8) []uint64 { + memberGUIDs := make([]uint64, 0, len(signatures)+1) + memberGUIDs = append(memberGUIDs, captainGUID) + + maxMembers := int(arenaType) * 2 + for _, signature := range signatures { + if len(memberGUIDs) >= maxMembers { + break + } + memberGUIDs = append(memberGUIDs, signature.playerGUID) + } + + return memberGUIDs +} + +func arenaPetitionForUpdate(ctx context.Context, tx *sql.Tx, petitionGuidLow uint64) (*arenaPetition, error) { + row := tx.QueryRowContext(ctx, ` +SELECT petition_id, ownerguid, petitionguid, name, type +FROM petition +WHERE petitionguid = ? +FOR UPDATE`, petitionGuidLow) + + petition := arenaPetition{} + err := row.Scan(&petition.petitionID, &petition.ownerGUID, &petition.petitionGUID, &petition.name, &petition.petitionType) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, nil + } + return nil, err + } + + return &petition, nil +} + +func arenaPetitionByGUID(ctx context.Context, db *sql.DB, petitionGuidLow uint64) (*arenaPetition, error) { + row := db.QueryRowContext(ctx, ` +SELECT petition_id, ownerguid, petitionguid, name, type +FROM petition +WHERE petitionguid = ?`, petitionGuidLow) + + petition := arenaPetition{} + err := row.Scan(&petition.petitionID, &petition.ownerGUID, &petition.petitionGUID, &petition.name, &petition.petitionType) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, nil + } + return nil, err + } + + return &petition, nil +} + +func arenaPetitionSignatureCount(ctx context.Context, db *sql.DB, petitionID uint32) (uint32, error) { + var count uint32 + err := db.QueryRowContext(ctx, "SELECT COUNT(DISTINCT player_account) FROM petition_sign WHERE petition_id = ?", petitionID).Scan(&count) + return count, err +} + +func arenaTeamNameExists(ctx context.Context, tx *sql.Tx, name string) (bool, error) { + var exists uint8 + err := tx.QueryRowContext(ctx, "SELECT 1 FROM arena_team WHERE name = ? LIMIT 1", name).Scan(&exists) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return false, nil + } + return false, err + } + return exists == 1, nil +} + +func arenaPetitionSignatures(ctx context.Context, tx *sql.Tx, petitionID uint32) ([]arenaPetitionSignature, error) { + rows, err := tx.QueryContext(ctx, ` +SELECT playerguid, player_account +FROM petition_sign +WHERE petition_id = ? +ORDER BY playerguid ASC`, petitionID) + if err != nil { + return nil, err + } + defer rows.Close() + + var signatures []arenaPetitionSignature + seenAccounts := map[uint32]struct{}{} + for rows.Next() { + var signature arenaPetitionSignature + if err = rows.Scan(&signature.playerGUID, &signature.playerAccount); err != nil { + return nil, err + } + if _, ok := seenAccounts[signature.playerAccount]; ok { + continue + } + seenAccounts[signature.playerAccount] = struct{}{} + signatures = append(signatures, signature) + } + + return signatures, rows.Err() +} + +func ensureArenaMembersAvailable(ctx context.Context, tx *sql.Tx, memberGUIDs []uint64, arenaType uint8) error { + for _, memberGUID := range memberGUIDs { + var existing uint8 + err := tx.QueryRowContext(ctx, ` +SELECT 1 +FROM arena_team_member AS atm +INNER JOIN arena_team AS at ON at.arenaTeamId = atm.arenaTeamId +WHERE atm.guid = ? AND at.type = ? +LIMIT 1`, memberGUID, arenaType).Scan(&existing) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + continue + } + return err + } + return ErrArenaTeamAlreadyInTeam + } + return nil +} + +func arenaTeamTypeForUpdate(ctx context.Context, tx *sql.Tx, arenaTeamID uint32) (uint8, error) { + header, err := arenaTeamHeaderForUpdate(ctx, tx, arenaTeamID) + if err != nil { + return 0, err + } + return header.arenaType, nil +} + +func arenaTeamRankForRating(ctx context.Context, tx *sql.Tx, arenaTeamID uint32, arenaType uint8, rating uint32) (uint32, error) { + var higherRated uint32 + err := tx.QueryRowContext(ctx, ` +SELECT COUNT(*) +FROM arena_team +WHERE type = ? AND arenaTeamId <> ? AND rating > ?`, arenaType, arenaTeamID, rating).Scan(&higherRated) + if err != nil { + return 0, err + } + return higherRated + 1, nil +} + +type arenaTeamHeader struct { + arenaType uint8 + captainGUID uint64 + name string +} + +func arenaTeamHeaderForUpdate(ctx context.Context, tx *sql.Tx, arenaTeamID uint32) (*arenaTeamHeader, error) { + header := arenaTeamHeader{} + err := tx.QueryRowContext(ctx, ` +SELECT type, captainGuid, name +FROM arena_team +WHERE arenaTeamId = ? +FOR UPDATE`, arenaTeamID).Scan(&header.arenaType, &header.captainGUID, &header.name) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrArenaTeamNotFound + } + return nil, err + } + if _, ok := arenaSlotByType(header.arenaType); !ok { + return nil, ErrArenaTeamInvalidType + } + return &header, nil +} + +func arenaMemberTeamForType(ctx context.Context, tx *sql.Tx, playerGUID uint64, arenaType uint8) (uint32, bool, error) { + var arenaTeamID uint32 + err := tx.QueryRowContext(ctx, ` +SELECT atm.arenaTeamId +FROM arena_team_member AS atm +INNER JOIN arena_team AS at ON at.arenaTeamId = atm.arenaTeamId +WHERE atm.guid = ? AND at.type = ? +LIMIT 1 +FOR UPDATE`, playerGUID, arenaType).Scan(&arenaTeamID) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return 0, false, nil + } + return 0, false, err + } + return arenaTeamID, true, nil +} + +func arenaTeamMemberCount(ctx context.Context, tx *sql.Tx, arenaTeamID uint32) (uint32, error) { + var count uint32 + err := tx.QueryRowContext(ctx, "SELECT COUNT(*) FROM arena_team_member WHERE arenaTeamId = ?", arenaTeamID).Scan(&count) + return count, err +} + +func arenaTeamHasMember(ctx context.Context, tx *sql.Tx, arenaTeamID uint32, playerGUID uint64) (bool, error) { + var exists uint8 + err := tx.QueryRowContext(ctx, "SELECT 1 FROM arena_team_member WHERE arenaTeamId = ? AND guid = ? LIMIT 1", arenaTeamID, playerGUID).Scan(&exists) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return false, nil + } + return false, err + } + return exists == 1, nil +} + +func nextArenaTeamID(ctx context.Context, tx *sql.Tx) (uint32, error) { + var nextID uint32 + err := tx.QueryRowContext(ctx, "SELECT COALESCE(MAX(arenaTeamId), 0) + 1 FROM arena_team").Scan(&nextID) + return nextID, err +} + +func isValidArenaTeamName(name string) bool { + if !utf8.ValidString(name) { + return false + } + + runes := []rune(name) + if len(runes) < minArenaTeamNameRunes || len(runes) > maxArenaTeamNameRunes { + return false + } + + family := arenaTeamNameFamilyNone + for _, r := range runes { + if isArenaTeamNameNumericOrSpace(r) { + continue + } + + current := arenaTeamNameFamilyForRune(r) + if current == arenaTeamNameFamilyNone { + return false + } + if family != arenaTeamNameFamilyNone && family != current { + return false + } + family = current + } + + return true +} + +type arenaTeamNameFamily uint8 + +const ( + arenaTeamNameFamilyNone arenaTeamNameFamily = iota + arenaTeamNameFamilyLatin + arenaTeamNameFamilyCyrillic + arenaTeamNameFamilyEastAsian +) + +func arenaTeamNameFamilyForRune(r rune) arenaTeamNameFamily { + switch { + case isArenaTeamNameExtendedLatin(r): + return arenaTeamNameFamilyLatin + case isArenaTeamNameCyrillic(r): + return arenaTeamNameFamilyCyrillic + case isArenaTeamNameEastAsian(r): + return arenaTeamNameFamilyEastAsian + default: + return arenaTeamNameFamilyNone + } +} + +func isArenaTeamNameNumericOrSpace(r rune) bool { + return (r >= '0' && r <= '9') || r == ' ' +} + +func isArenaTeamNameBasicLatin(r rune) bool { + return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') +} + +func isArenaTeamNameExtendedLatin(r rune) bool { + return isArenaTeamNameBasicLatin(r) || + (r >= 0x00C0 && r <= 0x00D6) || + (r >= 0x00D8 && r <= 0x00DE) || + r == 0x00DF || + (r >= 0x00E0 && r <= 0x00F6) || + (r >= 0x00F8 && r <= 0x00FE) || + (r >= 0x0100 && r <= 0x012F) || + r == 0x1E9E +} + +func isArenaTeamNameCyrillic(r rune) bool { + return (r >= 0x0410 && r <= 0x044F) || + r == 0x0401 || + r == 0x0451 +} + +func isArenaTeamNameEastAsian(r rune) bool { + return (r >= 0x1100 && r <= 0x11F9) || + (r >= 0x3041 && r <= 0x30FF) || + (r >= 0x3131 && r <= 0x318E) || + (r >= 0x31F0 && r <= 0x31FF) || + (r >= 0x3400 && r <= 0x4DB5) || + (r >= 0x4E00 && r <= 0x9FC3) || + (r >= 0xAC00 && r <= 0xD7A3) || + (r >= 0xFF01 && r <= 0xFFEE) +} + +func removeArenaPetitionsAndSigns(ctx context.Context, tx *sql.Tx, memberGUID uint64, arenaType uint8) error { + if _, err := tx.ExecContext(ctx, "DELETE FROM petition_sign WHERE playerguid = ? AND type = ?", memberGUID, arenaType); err != nil { + return err + } + + rows, err := tx.QueryContext(ctx, "SELECT petition_id FROM petition WHERE ownerguid = ? AND type = ?", memberGUID, arenaType) + if err != nil { + return err + } + + var petitionIDs []uint32 + for rows.Next() { + var petitionID uint32 + if err = rows.Scan(&petitionID); err != nil { + _ = rows.Close() + return err + } + petitionIDs = append(petitionIDs, petitionID) + } + if err = rows.Err(); err != nil { + _ = rows.Close() + return err + } + if err = rows.Close(); err != nil { + return err + } + + for _, petitionID := range petitionIDs { + if _, err = tx.ExecContext(ctx, "DELETE FROM petition_sign WHERE petition_id = ?", petitionID); err != nil { + return err + } + } + _, err = tx.ExecContext(ctx, "DELETE FROM petition WHERE ownerguid = ? AND type = ?", memberGUID, arenaType) + return err +} diff --git a/apps/charserver/repo/characters.go b/apps/charserver/repo/characters.go index 27a15d6..a225318 100644 --- a/apps/charserver/repo/characters.go +++ b/apps/charserver/repo/characters.go @@ -29,6 +29,7 @@ type LogInCharacter struct { GuildID uint32 PlayerFlags uint32 + ExtraFlags uint32 AtLoginFlags uint16 PetEntry uint32 @@ -53,18 +54,39 @@ const ( ) type FriendEntry struct { - PlayerGUID uint64 - FriendGUID uint64 - Flags uint8 - Note string + PlayerRealmID uint32 + PlayerGUID uint64 + FriendGUID uint64 + Flags uint8 + Note string +} + +type LfgDungeonRoute struct { + RealmID uint32 + PlayerGUID uint64 + DungeonEntry uint32 + MapID uint32 + Difficulty uint8 + OwnerRealmID uint32 + IsCrossRealm bool + RequiresBoundInstance bool + InstanceID uint32 + BoundInstanceID uint32 } type Characters interface { ListCharactersToLogIn(ctx context.Context, realmID, accountID uint32) ([]LogInCharacter, error) - CharacterToLogInByGUID(ctx context.Context, realmID uint32, charGUID uint64) (*LogInCharacter, error) + CharacterToLogInByGUID(ctx context.Context, realmID, accountID uint32, charGUID uint64) (*LogInCharacter, error) CharacterByName(ctx context.Context, realmID uint32, name string) (*Character, error) + CharacterByGUID(ctx context.Context, realmID uint32, charGUID uint64) (*Character, error) + DisplayCharacterByAccount(ctx context.Context, accountID uint32) (*Character, error) AccountDataForAccountID(ctx context.Context, realmID, accountID uint32) ([]AccountData, error) + UpdateAccountDataForAccountID(ctx context.Context, realmID, accountID uint32, data AccountData) error SaveCharacterPosition(ctx context.Context, realmID uint32, charGUID uint64, mapID uint32, x, y, z, o float32) error + RecordLfgDungeonRoute(ctx context.Context, route LfgDungeonRoute) error + ConfirmLfgDungeonRouteEntered(ctx context.Context, realmID uint32, playerGUID uint64, mapID uint32, difficulty uint8, instanceID uint32) (*LfgDungeonRoute, error) + ClearUnboundLfgDungeonRoute(ctx context.Context, realmID uint32, playerGUID uint64, mapID uint32) error + LfgDungeonRouteForPlayer(ctx context.Context, realmID uint32, playerGUID uint64, mapID uint32) (*LfgDungeonRoute, error) // Friends and social methods GetFriendsForPlayer(ctx context.Context, realmID uint32, playerGUID uint64) ([]*FriendEntry, error) diff --git a/apps/charserver/repo/characters_mysql.go b/apps/charserver/repo/characters_mysql.go index 65390ab..4c54fd2 100644 --- a/apps/charserver/repo/characters_mysql.go +++ b/apps/charserver/repo/characters_mysql.go @@ -2,6 +2,7 @@ package repo import ( "context" + "database/sql" "fmt" "strconv" "strings" @@ -15,21 +16,89 @@ type CharactersPreparedStatements uint32 func (s CharactersPreparedStatements) Stmt() string { switch s { case StmtListCharactersToLogin: - return `SELECT c.guid, c.account, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, - IFNULL(gm.guildid, 0), c.playerFlags, c.at_login, IFNULL(cp.entry, 0), IFNULL(cp.modelid, 0), IFNULL(cp.level, 0), c.equipmentCache, IFNULL(cb.guid, 0) - FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid - LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY c.guid` + return `SELECT c.guid, c.account, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, + IFNULL(gm.guildid, 0), c.playerFlags, c.extra_flags, c.at_login, IFNULL(cp.entry, 0), IFNULL(cp.modelid, 0), IFNULL(cp.level, 0), c.equipmentCache, IFNULL(cb.guid, 0) + FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid + LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY COALESCE(c.order, c.guid)` case StmtCharacterToLogin: - return `SELECT c.guid, c.account, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, - IFNULL(gm.guildid, 0), c.playerFlags, c.at_login, IFNULL(cp.entry, 0), IFNULL(cp.modelid, 0), IFNULL(cp.level, 0), c.equipmentCache, IFNULL(cb.guid, 0) - FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid - LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.guid = ? AND c.deleteInfos_Name IS NULL` + return `SELECT c.guid, c.account, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, + IFNULL(gm.guildid, 0), c.playerFlags, c.extra_flags, c.at_login, IFNULL(cp.entry, 0), IFNULL(cp.modelid, 0), IFNULL(cp.level, 0), c.equipmentCache, IFNULL(cb.guid, 0) + FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid + LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.guid = ? AND c.account = ? AND c.deleteInfos_Name IS NULL` case StmtSelectAccountData: return "SELECT type, time, data FROM account_data WHERE accountId = ?" + case StmtReplaceAccountData: + return "REPLACE INTO account_data (accountId, type, time, data) VALUES (?, ?, ?, ?)" case StmtSelectCharacterWithName: return "SELECT c.guid, account, race, class, gender, level, zone, map, position_x, position_y, position_z, IFNULL(gm.guildid, 0) FROM characters AS c LEFT JOIN guild_member AS gm ON c.guid = gm.guid WHERE name = ?" + case StmtSelectCharacterWithGUID: + return "SELECT c.guid, c.name, account, race, class, gender, level, zone, map, position_x, position_y, position_z, IFNULL(gm.guildid, 0) FROM characters AS c LEFT JOIN guild_member AS gm ON c.guid = gm.guid WHERE c.guid = ? AND c.deleteInfos_Name IS NULL" + case StmtSelectDisplayCharacterByAccount: + return "SELECT c.guid, c.name, account, race, class, gender, level, zone, map, position_x, position_y, position_z, IFNULL(gm.guildid, 0) FROM characters AS c LEFT JOIN guild_member AS gm ON c.guid = gm.guid WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY c.guid LIMIT 1" case StmtUpdateCharacterPosition: return "UPDATE characters SET map = ?, position_x = ?, position_y = ?, position_z = ?, orientation = ? WHERE guid = ?" + case StmtUpsertLfgDungeonRoute: + return `INSERT INTO tc9_lfg_dungeon_routes + (realmId, playerGuid, dungeonEntry, mapId, difficulty, ownerRealmId, isCrossRealm, requiresBoundInstance, instanceId, createdAt, updatedAt) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()) + ON DUPLICATE KEY UPDATE + dungeonEntry = VALUES(dungeonEntry), + ownerRealmId = VALUES(ownerRealmId), + isCrossRealm = VALUES(isCrossRealm), + requiresBoundInstance = VALUES(requiresBoundInstance), + instanceId = VALUES(instanceId), + updatedAt = UNIX_TIMESTAMP()` + case StmtConfirmLfgDungeonRouteEntered: + return `UPDATE tc9_lfg_dungeon_routes AS route + LEFT JOIN ( + SELECT ci.guid, MAX(ci.instance) AS instance, i.map, i.difficulty + FROM character_instance AS ci + JOIN instance AS i ON i.id = ci.instance + WHERE ci.guid = ? AND i.map = ? + GROUP BY ci.guid, i.map, i.difficulty + ) AS bind ON bind.guid = route.playerGuid + AND bind.map = route.mapId + AND bind.difficulty = route.difficulty + SET route.requiresBoundInstance = 1, + route.instanceId = IFNULL(bind.instance, route.instanceId), + route.updatedAt = UNIX_TIMESTAMP() + WHERE route.realmId = ? AND route.playerGuid = ? AND route.mapId = ? AND route.difficulty = ?` + case StmtClearUnboundLfgDungeonRoute: + return `DELETE FROM tc9_lfg_dungeon_routes + WHERE realmId = ? AND playerGuid = ? AND (? = 0 OR mapId = ?) AND requiresBoundInstance = 0 AND instanceId = 0` + case StmtSelectLfgDungeonRoute: + return `SELECT route.realmId, route.playerGuid, route.dungeonEntry, route.mapId, route.difficulty, + route.ownerRealmId, route.isCrossRealm, route.requiresBoundInstance, route.instanceId, + IFNULL(bind.instance, 0) AS boundInstanceId + FROM tc9_lfg_dungeon_routes AS route + LEFT JOIN ( + SELECT ci.guid, MAX(ci.instance) AS instance, i.map, i.difficulty + FROM character_instance AS ci + JOIN instance AS i ON i.id = ci.instance + WHERE ci.guid = ? + GROUP BY ci.guid, i.map, i.difficulty + ) AS bind ON bind.guid = route.playerGuid + AND bind.map = route.mapId + AND bind.difficulty = route.difficulty + WHERE route.realmId = ? AND route.playerGuid = ? AND (? = 0 OR route.mapId = ?) + ORDER BY route.updatedAt DESC` + case StmtSelectLfgDungeonBoundInstance: + return `SELECT IFNULL(MAX(ci.instance), 0) + FROM character_instance AS ci + JOIN instance AS i ON i.id = ci.instance + WHERE ci.guid = ? AND i.map = ? AND i.difficulty = ?` + case StmtUpdateLfgDungeonRouteInstance: + return `UPDATE tc9_lfg_dungeon_routes + SET requiresBoundInstance = 1, instanceId = ?, updatedAt = UNIX_TIMESTAMP() + WHERE realmId = ? AND playerGuid = ? AND mapId = ? AND difficulty = ?` + case StmtSelectLfgDungeonRouteByInstance: + return `SELECT route.realmId, route.playerGuid, route.dungeonEntry, route.mapId, route.difficulty, + route.ownerRealmId, route.isCrossRealm, route.requiresBoundInstance, route.instanceId, + route.instanceId AS boundInstanceId + FROM tc9_lfg_dungeon_routes AS route + WHERE route.mapId = ? AND route.difficulty = ? AND route.instanceId = ? AND route.requiresBoundInstance = 1 AND route.dungeonEntry <> 0 + ORDER BY route.updatedAt DESC + LIMIT 1` case StmtGetFriendsForPlayer: return "SELECT friend, flags, note FROM character_social WHERE guid = ?" case StmtAddFriend: @@ -57,8 +126,18 @@ const ( StmtListCharactersToLogin CharactersPreparedStatements = iota StmtCharacterToLogin StmtSelectAccountData + StmtReplaceAccountData + StmtSelectCharacterWithGUID StmtSelectCharacterWithName + StmtSelectDisplayCharacterByAccount StmtUpdateCharacterPosition + StmtUpsertLfgDungeonRoute + StmtConfirmLfgDungeonRouteEntered + StmtClearUnboundLfgDungeonRoute + StmtSelectLfgDungeonRoute + StmtSelectLfgDungeonBoundInstance + StmtUpdateLfgDungeonRouteInstance + StmtSelectLfgDungeonRouteByInstance StmtGetFriendsForPlayer StmtAddFriend StmtRemoveFriend @@ -75,9 +154,19 @@ type CharactersMYSQL struct { func NewCharactersMYSQL(db shrepo.CharactersDB) Characters { db.SetPreparedStatement(StmtListCharactersToLogin) db.SetPreparedStatement(StmtSelectAccountData) + db.SetPreparedStatement(StmtReplaceAccountData) db.SetPreparedStatement(StmtCharacterToLogin) + db.SetPreparedStatement(StmtSelectCharacterWithGUID) db.SetPreparedStatement(StmtSelectCharacterWithName) + db.SetPreparedStatement(StmtSelectDisplayCharacterByAccount) db.SetPreparedStatement(StmtUpdateCharacterPosition) + db.SetPreparedStatement(StmtUpsertLfgDungeonRoute) + db.SetPreparedStatement(StmtConfirmLfgDungeonRouteEntered) + db.SetPreparedStatement(StmtClearUnboundLfgDungeonRoute) + db.SetPreparedStatement(StmtSelectLfgDungeonRoute) + db.SetPreparedStatement(StmtSelectLfgDungeonBoundInstance) + db.SetPreparedStatement(StmtUpdateLfgDungeonRouteInstance) + db.SetPreparedStatement(StmtSelectLfgDungeonRouteByInstance) db.SetPreparedStatement(StmtGetFriendsForPlayer) db.SetPreparedStatement(StmtAddFriend) db.SetPreparedStatement(StmtRemoveFriend) @@ -108,7 +197,7 @@ func (c CharactersMYSQL) ListCharactersToLogIn(ctx context.Context, realmID, acc err = rows.Scan( &item.GUID, &item.AccountID, &item.Name, &item.Race, &item.Class, &item.Gender, &item.Skin, &item.Face, &item.HairStyle, &item.HairColor, &item.FacialStyle, &item.Level, &item.Zone, &item.Map, &item.PositionX, &item.PositionY, &item.PositionZ, - &item.GuildID, &item.PlayerFlags, &item.AtLoginFlags, &item.PetEntry, &item.PetModelID, &item.PetLevel, + &item.GuildID, &item.PlayerFlags, &item.ExtraFlags, &item.AtLoginFlags, &item.PetEntry, &item.PetModelID, &item.PetLevel, &equipmentCache, &bannedGuid, ) if err != nil { @@ -139,9 +228,9 @@ func (c CharactersMYSQL) ListCharactersToLogIn(ctx context.Context, realmID, acc return result, nil } -func (c CharactersMYSQL) CharacterToLogInByGUID(ctx context.Context, realmID uint32, charGUID uint64) (*LogInCharacter, error) { +func (c CharactersMYSQL) CharacterToLogInByGUID(ctx context.Context, realmID, accountID uint32, charGUID uint64) (*LogInCharacter, error) { const currentPetSlot = 0 - rows, err := c.db.PreparedStatement(realmID, StmtCharacterToLogin).QueryContext(ctx, currentPetSlot, charGUID) + rows, err := c.db.PreparedStatement(realmID, StmtCharacterToLogin).QueryContext(ctx, currentPetSlot, charGUID, accountID) if err != nil { return nil, err } @@ -156,7 +245,7 @@ func (c CharactersMYSQL) CharacterToLogInByGUID(ctx context.Context, realmID uin err = rows.Scan( &item.GUID, &item.AccountID, &item.Name, &item.Race, &item.Class, &item.Gender, &item.Skin, &item.Face, &item.HairStyle, &item.HairColor, &item.FacialStyle, &item.Level, &item.Zone, &item.Map, &item.PositionX, &item.PositionY, &item.PositionZ, - &item.GuildID, &item.PlayerFlags, &item.AtLoginFlags, &item.PetEntry, &item.PetModelID, &item.PetLevel, + &item.GuildID, &item.PlayerFlags, &item.ExtraFlags, &item.AtLoginFlags, &item.PetEntry, &item.PetModelID, &item.PetLevel, &equipmentCache, &bannedGuid, ) if err != nil { @@ -208,6 +297,7 @@ func (c CharactersMYSQL) CharacterByName(ctx context.Context, realmID uint32, na result := []Character{} for rows.Next() { item := Character{ + RealmID: realmID, CharName: string(nameRunes), } if err = rows.Scan( @@ -227,6 +317,71 @@ func (c CharactersMYSQL) CharacterByName(ctx context.Context, realmID uint32, na } +func (c CharactersMYSQL) CharacterByGUID(ctx context.Context, realmID uint32, charGUID uint64) (*Character, error) { + rows, err := c.db.PreparedStatement(realmID, StmtSelectCharacterWithGUID).QueryContext(ctx, charGUID) + if err != nil { + return nil, err + } + defer rows.Close() + + result := []Character{} + for rows.Next() { + item := Character{ + RealmID: realmID, + } + if err = rows.Scan( + &item.CharGUID, &item.CharName, &item.AccountID, &item.CharRace, &item.CharClass, &item.CharGender, &item.CharLevel, + &item.CharZone, &item.CharMap, &item.CharPosX, &item.CharPosY, &item.CharPosZ, &item.CharGuildID, + ); err != nil { + return nil, err + } + result = append(result, item) + } + + if len(result) == 0 { + return nil, nil + } + + return &result[0], nil +} + +func (c CharactersMYSQL) DisplayCharacterByAccount(ctx context.Context, accountID uint32) (*Character, error) { + for _, realmID := range c.db.RealmIDs() { + rows, err := c.db.PreparedStatement(realmID, StmtSelectDisplayCharacterByAccount).QueryContext(ctx, accountID) + if err != nil { + return nil, err + } + + var result *Character + if rows.Next() { + item := Character{ + RealmID: realmID, + } + if err = rows.Scan( + &item.CharGUID, &item.CharName, &item.AccountID, &item.CharRace, &item.CharClass, &item.CharGender, &item.CharLevel, + &item.CharZone, &item.CharMap, &item.CharPosX, &item.CharPosY, &item.CharPosZ, &item.CharGuildID, + ); err != nil { + _ = rows.Close() + return nil, err + } + result = &item + } + + if err = rows.Err(); err != nil { + _ = rows.Close() + return nil, err + } + if err = rows.Close(); err != nil { + return nil, err + } + if result != nil { + return result, nil + } + } + + return nil, nil +} + func (c CharactersMYSQL) AccountDataForAccountID(ctx context.Context, realmID, accountID uint32) ([]AccountData, error) { rows, err := c.db.PreparedStatement(realmID, StmtSelectAccountData).QueryContext(ctx, accountID) if err != nil { @@ -246,6 +401,11 @@ func (c CharactersMYSQL) AccountDataForAccountID(ctx context.Context, realmID, a return result, nil } +func (c CharactersMYSQL) UpdateAccountDataForAccountID(ctx context.Context, realmID, accountID uint32, data AccountData) error { + _, err := c.db.PreparedStatement(realmID, StmtReplaceAccountData).ExecContext(ctx, accountID, data.Type, data.Time, data.Data) + return err +} + func (c CharactersMYSQL) SaveCharacterPosition(ctx context.Context, realmID uint32, charGUID uint64, mapID uint32, x, y, z, o float32) error { _, err := c.db.PreparedStatement(realmID, StmtUpdateCharacterPosition).ExecContext(ctx, mapID, x, y, z, o, charGUID) if err != nil { @@ -255,6 +415,247 @@ func (c CharactersMYSQL) SaveCharacterPosition(ctx context.Context, realmID uint return nil } +func (c CharactersMYSQL) RecordLfgDungeonRoute(ctx context.Context, route LfgDungeonRoute) error { + if route.RealmID == 0 { + route.RealmID = 1 + } + if route.PlayerGUID == 0 || route.MapID == 0 || route.DungeonEntry == 0 { + return nil + } + if !route.RequiresBoundInstance { + existing, err := c.LfgDungeonRouteForPlayer(ctx, route.RealmID, route.PlayerGUID, route.MapID) + if err != nil { + return err + } + if existing != nil && existing.Difficulty == route.Difficulty && existing.RequiresBoundInstance && existing.BoundInstanceID != 0 { + route.RequiresBoundInstance = true + route.InstanceID = existing.InstanceID + if route.InstanceID == 0 { + route.InstanceID = existing.BoundInstanceID + } + } + } + + _, err := c.db.PreparedStatement(route.RealmID, StmtUpsertLfgDungeonRoute).ExecContext( + ctx, + route.RealmID, + route.PlayerGUID, + route.DungeonEntry, + route.MapID, + route.Difficulty, + route.OwnerRealmID, + route.IsCrossRealm, + route.RequiresBoundInstance, + route.InstanceID, + ) + return err +} + +func (c CharactersMYSQL) ConfirmLfgDungeonRouteEntered(ctx context.Context, realmID uint32, playerGUID uint64, mapID uint32, difficulty uint8, instanceID uint32) (*LfgDungeonRoute, error) { + if playerGUID == 0 || mapID == 0 { + return nil, nil + } + var err error + if instanceID != 0 { + _, err = c.db.PreparedStatement(realmID, StmtUpdateLfgDungeonRouteInstance).ExecContext(ctx, instanceID, realmID, playerGUID, mapID, difficulty) + } else { + _, err = c.db.PreparedStatement(realmID, StmtConfirmLfgDungeonRouteEntered).ExecContext(ctx, playerGUID, mapID, realmID, playerGUID, mapID, difficulty) + } + if err != nil { + return nil, err + } + route, err := c.LfgDungeonRouteForPlayer(ctx, realmID, playerGUID, mapID) + if err != nil || route != nil || instanceID == 0 { + return route, err + } + + template, err := c.lfgDungeonRouteTemplateForInstance(ctx, realmID, mapID, difficulty, instanceID) + if err != nil || template == nil { + return nil, err + } + + template.RealmID = realmID + template.PlayerGUID = playerGUID + template.RequiresBoundInstance = true + template.InstanceID = instanceID + template.BoundInstanceID = instanceID + if err = c.RecordLfgDungeonRoute(ctx, *template); err != nil { + return nil, err + } + return c.LfgDungeonRouteForPlayer(ctx, realmID, playerGUID, mapID) +} + +func (c CharactersMYSQL) ClearUnboundLfgDungeonRoute(ctx context.Context, realmID uint32, playerGUID uint64, mapID uint32) error { + if playerGUID == 0 { + return nil + } + _, err := c.db.PreparedStatement(realmID, StmtClearUnboundLfgDungeonRoute).ExecContext(ctx, realmID, playerGUID, mapID, mapID) + return err +} + +func (c CharactersMYSQL) LfgDungeonRouteForPlayer(ctx context.Context, realmID uint32, playerGUID uint64, mapID uint32) (*LfgDungeonRoute, error) { + if playerGUID == 0 { + return nil, nil + } + + rows, err := c.db.PreparedStatement(realmID, StmtSelectLfgDungeonRoute).QueryContext(ctx, playerGUID, realmID, playerGUID, mapID, mapID) + if err != nil { + return nil, err + } + defer rows.Close() + + var routes []LfgDungeonRoute + for rows.Next() { + route := LfgDungeonRoute{} + if err = rows.Scan( + &route.RealmID, + &route.PlayerGUID, + &route.DungeonEntry, + &route.MapID, + &route.Difficulty, + &route.OwnerRealmID, + &route.IsCrossRealm, + &route.RequiresBoundInstance, + &route.InstanceID, + &route.BoundInstanceID, + ); err != nil { + return nil, err + } + routes = append(routes, route) + } + if err = rows.Err(); err != nil { + return nil, err + } + if len(routes) == 0 { + return nil, nil + } + + for _, route := range routes { + if route.RequiresBoundInstance && route.BoundInstanceID == 0 { + route.BoundInstanceID, err = c.findLfgDungeonRouteBoundInstance(ctx, realmID, route) + if err != nil { + return nil, err + } + } + if route.RequiresBoundInstance && route.BoundInstanceID != 0 && route.InstanceID != route.BoundInstanceID { + _, err = c.db.PreparedStatement(realmID, StmtUpdateLfgDungeonRouteInstance).ExecContext(ctx, route.BoundInstanceID, route.RealmID, route.PlayerGUID, route.MapID, route.Difficulty) + if err != nil { + return nil, err + } + route.InstanceID = route.BoundInstanceID + } + return &route, nil + } + + return nil, nil +} + +func (c CharactersMYSQL) findLfgDungeonRouteBoundInstance(ctx context.Context, requestRealmID uint32, route LfgDungeonRoute) (uint32, error) { + for _, realmID := range c.lfgDungeonRouteBoundInstanceRealmIDs(requestRealmID, route) { + instanceID, err := c.lfgDungeonBoundInstanceInRealm(ctx, realmID, route.PlayerGUID, route.MapID, route.Difficulty) + if err != nil { + return 0, err + } + if instanceID != 0 { + return instanceID, nil + } + } + + return 0, nil +} + +func (c CharactersMYSQL) lfgDungeonRouteBoundInstanceRealmIDs(requestRealmID uint32, route LfgDungeonRoute) []uint32 { + configuredRealms := c.db.RealmIDs() + configuredRealmSet := make(map[uint32]struct{}, len(configuredRealms)) + for _, realmID := range configuredRealms { + configuredRealmSet[realmID] = struct{}{} + } + + realmIDSet := make(map[uint32]struct{}) + realmIDs := make([]uint32, 0, 4) + addRealm := func(realmID uint32) { + if realmID == 0 { + return + } + if _, ok := realmIDSet[realmID]; ok { + return + } + realmIDSet[realmID] = struct{}{} + realmIDs = append(realmIDs, realmID) + } + + addRealm(route.RealmID) + addRealm(requestRealmID) + if _, ok := configuredRealmSet[route.OwnerRealmID]; ok { + addRealm(route.OwnerRealmID) + } + + if route.IsCrossRealm && route.OwnerRealmID == 0 { + for _, realmID := range configuredRealms { + addRealm(realmID) + } + } + + return realmIDs +} + +func (c CharactersMYSQL) lfgDungeonBoundInstanceInRealm(ctx context.Context, realmID uint32, playerGUID uint64, mapID uint32, difficulty uint8) (uint32, error) { + var instanceID uint32 + err := c.db.PreparedStatement(realmID, StmtSelectLfgDungeonBoundInstance).QueryRowContext(ctx, playerGUID, mapID, difficulty).Scan(&instanceID) + if err != nil { + if err == sql.ErrNoRows { + return 0, nil + } + return 0, err + } + return instanceID, nil +} + +func (c CharactersMYSQL) lfgDungeonRouteTemplateForInstance(ctx context.Context, requestRealmID uint32, mapID uint32, difficulty uint8, instanceID uint32) (*LfgDungeonRoute, error) { + route, err := c.lfgDungeonRouteTemplateForInstanceID(ctx, requestRealmID, mapID, difficulty, instanceID) + if err != nil || route != nil || instanceID == 0 { + return route, err + } + return c.lfgDungeonRouteTemplateForInstanceID(ctx, requestRealmID, mapID, difficulty, 0) +} + +func (c CharactersMYSQL) lfgDungeonRouteTemplateForInstanceID(ctx context.Context, requestRealmID uint32, mapID uint32, difficulty uint8, instanceID uint32) (*LfgDungeonRoute, error) { + seen := make(map[uint32]struct{}) + realmIDs := append([]uint32{requestRealmID}, c.db.RealmIDs()...) + for _, realmID := range realmIDs { + if realmID == 0 { + continue + } + if _, ok := seen[realmID]; ok { + continue + } + seen[realmID] = struct{}{} + + route := LfgDungeonRoute{} + err := c.db.PreparedStatement(realmID, StmtSelectLfgDungeonRouteByInstance).QueryRowContext(ctx, mapID, difficulty, instanceID).Scan( + &route.RealmID, + &route.PlayerGUID, + &route.DungeonEntry, + &route.MapID, + &route.Difficulty, + &route.OwnerRealmID, + &route.IsCrossRealm, + &route.RequiresBoundInstance, + &route.InstanceID, + &route.BoundInstanceID, + ) + if err != nil { + if err == sql.ErrNoRows { + continue + } + return nil, err + } + return &route, nil + } + + return nil, nil +} + func (c CharactersMYSQL) GetFriendsForPlayer(ctx context.Context, realmID uint32, playerGUID uint64) ([]*FriendEntry, error) { rows, err := c.db.PreparedStatement(realmID, StmtGetFriendsForPlayer).QueryContext(ctx, playerGUID) if err != nil { @@ -264,7 +665,7 @@ func (c CharactersMYSQL) GetFriendsForPlayer(ctx context.Context, realmID uint32 var result []*FriendEntry for rows.Next() { - entry := &FriendEntry{PlayerGUID: playerGUID} + entry := &FriendEntry{PlayerRealmID: realmID, PlayerGUID: playerGUID} err = rows.Scan(&entry.FriendGUID, &entry.Flags, &entry.Note) if err != nil { return nil, err diff --git a/apps/charserver/repo/online-characters.go b/apps/charserver/repo/online-characters.go index febdb38..275f58d 100644 --- a/apps/charserver/repo/online-characters.go +++ b/apps/charserver/repo/online-characters.go @@ -27,7 +27,7 @@ type Character struct { type CharactersOnline interface { Add(context.Context, *Character) error Remove(ctx context.Context, realmID uint32, guid uint64) error - RemoveAllWithGatewayID(ctx context.Context, realmID uint32, gatewayID string) ([]uint64, error) + RemoveAllWithGatewayID(ctx context.Context, realmID uint32, gatewayID string, eventTimeUnixNano uint64) ([]uint64, error) OneByRealmAndGUID(ctx context.Context, realmID uint32, guid uint64) (*Character, error) OneByRealmAndName(ctx context.Context, realmID uint32, name string) (*Character, error) diff --git a/apps/charserver/repo/online-characters_inmem.go b/apps/charserver/repo/online-characters_inmem.go index b472b00..d058990 100644 --- a/apps/charserver/repo/online-characters_inmem.go +++ b/apps/charserver/repo/online-characters_inmem.go @@ -4,46 +4,36 @@ import ( "context" "strings" "sync" + "time" "github.com/walkline/ToCloud9/shared/events" ) type charactersOnlineInMem struct { - guidStorage map[uint32]map[uint64]*Character - nameStorage map[uint32]map[string]*Character - m sync.RWMutex + guidStorage map[uint32]map[uint64]*Character + nameStorage map[uint32]map[string]*Character + lifecycleEventTimes map[uint32]map[uint64]uint64 + m sync.RWMutex } func NewCharactersOnlineInMem() CharactersOnline { return &charactersOnlineInMem{ - guidStorage: map[uint32]map[uint64]*Character{}, - nameStorage: map[uint32]map[string]*Character{}, + guidStorage: map[uint32]map[uint64]*Character{}, + nameStorage: map[uint32]map[string]*Character{}, + lifecycleEventTimes: map[uint32]map[uint64]uint64{}, } } func (c *charactersOnlineInMem) Add(_ context.Context, character *Character) error { c.m.Lock() - if c.guidStorage[character.RealmID] == nil { - c.guidStorage[character.RealmID] = map[uint64]*Character{} - c.nameStorage[character.RealmID] = map[string]*Character{} - } - c.guidStorage[character.RealmID][character.CharGUID] = character - c.nameStorage[character.RealmID][strings.ToUpper(character.CharName)] = character + c.addLocked(character, 0) c.m.Unlock() return nil } func (c *charactersOnlineInMem) Remove(ctx context.Context, realmID uint32, guid uint64) error { - char, err := c.OneByRealmAndGUID(ctx, realmID, guid) - if err != nil { - return err - } - if char == nil { - return nil - } c.m.Lock() - delete(c.guidStorage[realmID], guid) - delete(c.nameStorage[realmID], char.CharName) + c.removeLocked(realmID, guid, 0) c.m.Unlock() return nil } @@ -112,7 +102,8 @@ func (c *charactersOnlineInMem) AllGUIDsByRealm(ctx context.Context, realmID uin } func (c *charactersOnlineInMem) HandleCharacterLoggedIn(payload events.GWEventCharacterLoggedInPayload) error { - return c.Add(context.TODO(), &Character{ + c.m.Lock() + c.addLocked(&Character{ RealmID: payload.RealmID, GatewayID: payload.GatewayID, CharGUID: payload.CharGUID, @@ -128,11 +119,16 @@ func (c *charactersOnlineInMem) HandleCharacterLoggedIn(payload events.GWEventCh CharPosZ: payload.CharPosZ, CharGuildID: payload.CharGuildID, AccountID: payload.AccountID, - }) + }, payload.EventTimeUnixNano) + c.m.Unlock() + return nil } func (c *charactersOnlineInMem) HandleCharacterLoggedOut(payload events.GWEventCharacterLoggedOutPayload) error { - return c.Remove(context.TODO(), payload.RealmID, payload.CharGUID) + c.m.Lock() + c.removeLocked(payload.RealmID, payload.CharGUID, payload.EventTimeUnixNano) + c.m.Unlock() + return nil } func (c *charactersOnlineInMem) HandleCharactersUpdates(payload events.GWEventCharactersUpdatesPayload) error { @@ -140,6 +136,16 @@ func (c *charactersOnlineInMem) HandleCharactersUpdates(payload events.GWEventCh for _, update := range payload.Updates { member := c.guidStorage[payload.RealmID][update.ID] if member != nil { + if payload.GatewayID != "" && member.GatewayID != "" && payload.GatewayID != member.GatewayID { + continue + } + eventTimeUnixNano := update.EventTimeUnixNano + if eventTimeUnixNano == 0 { + eventTimeUnixNano = payload.EventTimeUnixNano + } + if eventTimeUnixNano != 0 && c.lifecycleEventTimeLocked(payload.RealmID, update.ID) > eventTimeUnixNano { + continue + } applyCharUpdate(member, update) } } @@ -147,28 +153,103 @@ func (c *charactersOnlineInMem) HandleCharactersUpdates(payload events.GWEventCh return nil } -func (c *charactersOnlineInMem) RemoveAllWithGatewayID(ctx context.Context, realmID uint32, gatewayID string) ([]uint64, error) { +func (c *charactersOnlineInMem) RemoveAllWithGatewayID(ctx context.Context, realmID uint32, gatewayID string, eventTimeUnixNano uint64) ([]uint64, error) { charsToDelete := make([]uint64, 0, 20) c.m.Lock() storage := c.guidStorage[realmID] namesStorage := c.nameStorage[realmID] + if eventTimeUnixNano == 0 { + eventTimeUnixNano = uint64(time.Now().UnixNano()) + } for guid, char := range storage { - if char.GatewayID == gatewayID { + if char.GatewayID == gatewayID && c.lifecycleEventTimeLocked(realmID, guid) <= eventTimeUnixNano { charsToDelete = append(charsToDelete, guid) } } for _, guid := range charsToDelete { - delete(namesStorage, storage[guid].CharName) + delete(namesStorage, characterNameKey(storage[guid].CharName)) delete(storage, guid) + c.rememberLifecycleEventTimeLocked(realmID, guid, eventTimeUnixNano) } c.m.Unlock() return charsToDelete, nil } +func (c *charactersOnlineInMem) addLocked(character *Character, eventTimeUnixNano uint64) { + if character == nil { + return + } + if !c.shouldApplyLifecycleEventLocked(character.RealmID, character.CharGUID, eventTimeUnixNano) { + return + } + + c.ensureRealmStorageLocked(character.RealmID) + if previous := c.guidStorage[character.RealmID][character.CharGUID]; previous != nil { + delete(c.nameStorage[character.RealmID], characterNameKey(previous.CharName)) + } + + c.guidStorage[character.RealmID][character.CharGUID] = character + c.nameStorage[character.RealmID][characterNameKey(character.CharName)] = character + c.rememberLifecycleEventTimeLocked(character.RealmID, character.CharGUID, eventTimeUnixNano) +} + +func (c *charactersOnlineInMem) removeLocked(realmID uint32, guid uint64, eventTimeUnixNano uint64) { + if !c.shouldApplyLifecycleEventLocked(realmID, guid, eventTimeUnixNano) { + return + } + + storage := c.guidStorage[realmID] + if storage != nil { + if char := storage[guid]; char != nil { + delete(c.nameStorage[realmID], characterNameKey(char.CharName)) + } + delete(storage, guid) + } + c.rememberLifecycleEventTimeLocked(realmID, guid, eventTimeUnixNano) +} + +func (c *charactersOnlineInMem) ensureRealmStorageLocked(realmID uint32) { + if c.guidStorage[realmID] == nil { + c.guidStorage[realmID] = map[uint64]*Character{} + } + if c.nameStorage[realmID] == nil { + c.nameStorage[realmID] = map[string]*Character{} + } +} + +func (c *charactersOnlineInMem) shouldApplyLifecycleEventLocked(realmID uint32, guid uint64, eventTimeUnixNano uint64) bool { + return eventTimeUnixNano == 0 || c.lifecycleEventTimeLocked(realmID, guid) <= eventTimeUnixNano +} + +func (c *charactersOnlineInMem) lifecycleEventTimeLocked(realmID uint32, guid uint64) uint64 { + realmEvents := c.lifecycleEventTimes[realmID] + if realmEvents == nil { + return 0 + } + return realmEvents[guid] +} + +func (c *charactersOnlineInMem) rememberLifecycleEventTimeLocked(realmID uint32, guid uint64, eventTimeUnixNano uint64) { + if eventTimeUnixNano == 0 { + return + } + if c.lifecycleEventTimes == nil { + c.lifecycleEventTimes = map[uint32]map[uint64]uint64{} + } + if c.lifecycleEventTimes[realmID] == nil { + c.lifecycleEventTimes[realmID] = map[uint64]uint64{} + } + c.lifecycleEventTimes[realmID][guid] = eventTimeUnixNano +} + +func characterNameKey(name string) string { + return strings.ToUpper(name) +} + func applyCharUpdate(c *Character, upd *events.CharacterUpdate) { if upd.Zone != nil { c.CharZone = *upd.Zone @@ -215,12 +296,14 @@ func (c *charactersOnlineInMem) WhoRequest(_ context.Context, requesterRealmID u } showZones := true - for zone := range query.Zones { - if char.CharZone == uint32(zone) { - showZones = true - break - } + if len(query.Zones) > 0 { showZones = false + for _, zone := range query.Zones { + if char.CharZone == zone { + showZones = true + break + } + } } if !showZones { diff --git a/apps/charserver/server/arena_team.go b/apps/charserver/server/arena_team.go new file mode 100644 index 0000000..8ec5ee4 --- /dev/null +++ b/apps/charserver/server/arena_team.go @@ -0,0 +1,751 @@ +package server + +import ( + "context" + "errors" + "time" + + "github.com/rs/zerolog/log" + + "github.com/walkline/ToCloud9/apps/charserver/repo" + "github.com/walkline/ToCloud9/gen/characters/pb" + "github.com/walkline/ToCloud9/shared/events" +) + +const ( + arenaTeamNativeEventJoin uint8 = 3 + arenaTeamNativeEventLeave uint8 = 4 + arenaTeamNativeEventRemove uint8 = 5 + arenaTeamNativeEventLeaderChanged uint8 = 7 + arenaTeamNativeEventDisbanded uint8 = 8 +) + +func (c *CharServer) ArenaTeamQueueDataForRatedArena(ctx context.Context, request *pb.ArenaTeamQueueDataForRatedArenaRequest) (*pb.ArenaTeamQueueDataForRatedArenaResponse, error) { + defer func(t time.Time) { + log.Debug(). + Uint32("realmID", request.GetRealmID()). + Uint64("leaderGUID", request.GetLeaderGUID()). + Uint32("arenaType", request.GetArenaType()). + Int("playerCount", len(request.GetPlayerGUIDs())). + Str("timeTook", time.Since(t).String()). + Msg("Handled rated arena team queue data request") + }(time.Now()) + + if c.arenaTeams == nil { + return &pb.ArenaTeamQueueDataForRatedArenaResponse{ + Api: ver, + Status: pb.ArenaTeamQueueDataForRatedArenaResponse_Failed, + }, nil + } + + data, err := c.arenaTeams.QueueDataForRatedArena( + ctx, + request.GetRealmID(), + request.GetLeaderGUID(), + request.GetPlayerGUIDs(), + uint8(request.GetArenaType()), + request.GetStartMatchmakerRating(), + ) + if err != nil { + status, handled := arenaTeamQueueStatusFromError(err) + if handled { + return &pb.ArenaTeamQueueDataForRatedArenaResponse{ + Api: ver, + Status: status, + }, nil + } + return nil, err + } + + return &pb.ArenaTeamQueueDataForRatedArenaResponse{ + Api: ver, + Status: pb.ArenaTeamQueueDataForRatedArenaResponse_Ok, + ArenaTeamID: data.ArenaTeamID, + TeamRating: data.TeamRating, + MatchmakerRating: data.MatchmakerRating, + PreviousOpponentsTeamID: data.PreviousOpponentsTeamID, + }, nil +} + +func (c *CharServer) CreateArenaTeamFromPetition(ctx context.Context, request *pb.CreateArenaTeamFromPetitionRequest) (*pb.CreateArenaTeamFromPetitionResponse, error) { + defer func(t time.Time) { + log.Debug(). + Uint32("realmID", request.GetRealmID()). + Uint64("captainGUID", request.GetCaptainGUID()). + Uint64("petitionGUID", request.GetPetitionGUID()). + Uint32("arenaType", request.GetArenaType()). + Str("timeTook", time.Since(t).String()). + Msg("Handled arena team create from petition request") + }(time.Now()) + + if c.arenaTeams == nil { + return &pb.CreateArenaTeamFromPetitionResponse{ + Api: ver, + Status: pb.CreateArenaTeamFromPetitionResponse_Failed, + }, nil + } + + result, err := c.arenaTeams.CreateFromPetition(ctx, repo.ArenaTeamCreateFromPetitionRequest{ + RealmID: request.GetRealmID(), + CaptainGUID: request.GetCaptainGUID(), + PetitionGUID: request.GetPetitionGUID(), + ArenaType: uint8(request.GetArenaType()), + BackgroundColor: request.GetBackgroundColor(), + EmblemStyle: uint8(request.GetEmblemStyle()), + EmblemColor: request.GetEmblemColor(), + BorderStyle: uint8(request.GetBorderStyle()), + BorderColor: request.GetBorderColor(), + StartRating: request.GetStartRating(), + StartPersonalRating: request.GetStartPersonalRating(), + StartMatchmakerRating: request.GetStartMatchmakerRating(), + }) + if err != nil { + status, handled := arenaTeamCreateStatusFromError(err) + if handled { + return &pb.CreateArenaTeamFromPetitionResponse{ + Api: ver, + Status: status, + }, nil + } + return nil, err + } + + return &pb.CreateArenaTeamFromPetitionResponse{ + Api: ver, + Status: pb.CreateArenaTeamFromPetitionResponse_Ok, + ArenaTeamID: result.ArenaTeamID, + }, nil +} + +func (c *CharServer) GetArenaTeamPetition(ctx context.Context, request *pb.GetArenaTeamPetitionRequest) (*pb.GetArenaTeamPetitionResponse, error) { + defer func(t time.Time) { + log.Debug(). + Uint32("realmID", request.GetRealmID()). + Uint64("petitionGUID", request.GetPetitionGUID()). + Str("timeTook", time.Since(t).String()). + Msg("Handled arena team petition lookup request") + }(time.Now()) + + if c.arenaTeams == nil { + return &pb.GetArenaTeamPetitionResponse{Api: ver, Status: pb.GetArenaTeamPetitionResponse_Failed}, nil + } + + petition, err := c.arenaTeams.GetPetition(ctx, request.GetRealmID(), request.GetPetitionGUID()) + if err != nil { + switch { + case errors.Is(err, repo.ErrArenaTeamNotFound): + return &pb.GetArenaTeamPetitionResponse{Api: ver, Status: pb.GetArenaTeamPetitionResponse_NotFound}, nil + case errors.Is(err, repo.ErrArenaTeamInvalidType): + return &pb.GetArenaTeamPetitionResponse{Api: ver, Status: pb.GetArenaTeamPetitionResponse_NotArena}, nil + default: + return nil, err + } + } + + return &pb.GetArenaTeamPetitionResponse{ + Api: ver, + Status: pb.GetArenaTeamPetitionResponse_Ok, + Petition: &pb.ArenaTeamPetitionData{ + PetitionGUID: petition.PetitionGUID, + PetitionID: petition.PetitionID, + OwnerGUID: petition.OwnerGUID, + Name: petition.Name, + ArenaType: uint32(petition.ArenaType), + Signatures: petition.Signatures, + }, + }, nil +} + +func (c *CharServer) GetArenaTeam(ctx context.Context, request *pb.GetArenaTeamRequest) (*pb.GetArenaTeamResponse, error) { + defer arenaTeamMutationLog("get team", request.GetRealmID(), request.GetArenaTeamID(), time.Now()) + + if c.arenaTeams == nil { + return &pb.GetArenaTeamResponse{Api: ver, Status: pb.GetArenaTeamResponse_Failed}, nil + } + + team, err := c.arenaTeams.GetTeam(ctx, request.GetRealmID(), request.GetArenaTeamID()) + if err != nil { + if errors.Is(err, repo.ErrArenaTeamNotFound) { + return &pb.GetArenaTeamResponse{Api: ver, Status: pb.GetArenaTeamResponse_NotFound}, nil + } + return nil, err + } + + return &pb.GetArenaTeamResponse{ + Api: ver, + Status: pb.GetArenaTeamResponse_Ok, + Team: c.arenaTeamData(ctx, request.GetRealmID(), team), + }, nil +} + +func (c *CharServer) InviteArenaTeamMember(ctx context.Context, request *pb.InviteArenaTeamMemberRequest) (*pb.InviteArenaTeamMemberResponse, error) { + defer arenaTeamMutationLog("invite member", request.GetRealmID(), request.GetArenaTeamID(), time.Now()) + + if c.arenaTeams == nil { + return &pb.InviteArenaTeamMemberResponse{Api: ver, Status: pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_FAILED}, nil + } + + team, err := c.arenaTeams.GetTeam(ctx, request.GetRealmID(), request.GetArenaTeamID()) + if err != nil { + status, handled := arenaTeamMutationStatusFromError(err) + if handled { + return &pb.InviteArenaTeamMemberResponse{Api: ver, Status: status}, nil + } + return nil, err + } + + if !arenaTeamHasMemberData(team, request.GetInviterGUID()) || team.CaptainGUID != request.GetInviterGUID() { + return &pb.InviteArenaTeamMemberResponse{Api: ver, Status: pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_PERMISSION_DENIED}, nil + } + if arenaTeamHasMemberData(team, request.GetTargetGUID()) { + return &pb.InviteArenaTeamMemberResponse{Api: ver, Status: pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_ALREADY_IN_TEAM}, nil + } + if ignoring, err := c.arenaTeamTargetIgnoresInviter(ctx, request.GetRealmID(), request.GetTargetGUID(), request.GetInviterGUID()); err != nil { + return nil, err + } else if ignoring { + return &pb.InviteArenaTeamMemberResponse{Api: ver, Status: pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_IGNORING_YOU}, nil + } + if _, found, err := c.arenaTeams.MemberTeamForType(ctx, request.GetRealmID(), request.GetTargetGUID(), team.Type); err != nil { + return nil, err + } else if found { + return &pb.InviteArenaTeamMemberResponse{Api: ver, Status: pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_ALREADY_IN_TEAM}, nil + } + if len(team.Members) >= int(team.Type)*2 { + return &pb.InviteArenaTeamMemberResponse{Api: ver, Status: pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_ROSTER_FULL}, nil + } + + inviteKey := arenaTeamInviteKey{realmID: request.GetRealmID(), playerGUID: request.GetTargetGUID()} + c.arenaInvitesMu.Lock() + if _, ok := c.arenaInvites[inviteKey]; ok { + c.arenaInvitesMu.Unlock() + return &pb.InviteArenaTeamMemberResponse{Api: ver, Status: pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_ALREADY_INVITED}, nil + } + c.arenaInvites[inviteKey] = arenaTeamInvite{ + arenaTeamID: request.GetArenaTeamID(), + inviterGUID: request.GetInviterGUID(), + inviterName: request.GetInviterName(), + teamName: team.Name, + } + c.arenaInvitesMu.Unlock() + + if c.eventsProducer != nil { + if err = c.eventsProducer.ArenaTeamInviteCreated(&events.CharEventArenaTeamInviteCreatedPayload{ + RealmID: request.GetRealmID(), + TargetGUID: request.GetTargetGUID(), + TargetName: request.GetTargetName(), + InviterGUID: request.GetInviterGUID(), + InviterName: request.GetInviterName(), + ArenaTeamID: request.GetArenaTeamID(), + TeamName: team.Name, + }); err != nil { + return nil, err + } + } + + return &pb.InviteArenaTeamMemberResponse{ + Api: ver, + Status: pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK, + Team: c.arenaTeamData(ctx, request.GetRealmID(), team), + }, nil +} + +func (c *CharServer) AcceptArenaTeamInvite(ctx context.Context, request *pb.AcceptArenaTeamInviteRequest) (*pb.AcceptArenaTeamInviteResponse, error) { + if c.arenaTeams == nil { + return &pb.AcceptArenaTeamInviteResponse{Api: ver, Status: pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_FAILED}, nil + } + + key := arenaTeamInviteKey{realmID: request.GetRealmID(), playerGUID: request.GetPlayerGUID()} + c.arenaInvitesMu.Lock() + invite, ok := c.arenaInvites[key] + if ok { + delete(c.arenaInvites, key) + } + c.arenaInvitesMu.Unlock() + if !ok { + return &pb.AcceptArenaTeamInviteResponse{Api: ver, Status: pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_NOT_FOUND}, nil + } + + team, err := c.arenaTeams.GetTeam(ctx, request.GetRealmID(), invite.arenaTeamID) + if err != nil { + return nil, err + } + + personalRating := request.GetPersonalRating() + if personalRating == 0 { + personalRating = team.Rating + } + + if err := c.arenaTeams.AddMember(ctx, request.GetRealmID(), invite.arenaTeamID, request.GetPlayerGUID(), personalRating); err != nil { + status, handled := arenaTeamMutationStatusFromError(err) + if handled { + return &pb.AcceptArenaTeamInviteResponse{Api: ver, Status: status}, nil + } + return nil, err + } + + team, err = c.arenaTeams.GetTeam(ctx, request.GetRealmID(), invite.arenaTeamID) + if err != nil { + return nil, err + } + + c.publishArenaTeamNativeEvent( + request.GetRealmID(), + team, + arenaTeamNativeEventJoin, + request.GetPlayerGUID(), + request.GetPlayerName(), + team.Name, + ) + + return &pb.AcceptArenaTeamInviteResponse{ + Api: ver, + Status: pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK, + Team: c.arenaTeamData(ctx, request.GetRealmID(), team), + }, nil +} + +func (c *CharServer) DeclineArenaTeamInvite(_ context.Context, request *pb.DeclineArenaTeamInviteRequest) (*pb.ArenaTeamMutationResponse, error) { + key := arenaTeamInviteKey{realmID: request.GetRealmID(), playerGUID: request.GetPlayerGUID()} + c.arenaInvitesMu.Lock() + delete(c.arenaInvites, key) + c.arenaInvitesMu.Unlock() + + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK), nil +} + +func (c *CharServer) AddArenaTeamMember(ctx context.Context, request *pb.AddArenaTeamMemberRequest) (*pb.ArenaTeamMutationResponse, error) { + defer arenaTeamMutationLog("add member", request.GetRealmID(), request.GetArenaTeamID(), time.Now()) + + if c.arenaTeams == nil { + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_FAILED), nil + } + + if err := c.arenaTeams.AddMember(ctx, request.GetRealmID(), request.GetArenaTeamID(), request.GetPlayerGUID(), request.GetPersonalRating()); err != nil { + return arenaTeamMutationError(err) + } + + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK), nil +} + +func (c *CharServer) RemoveArenaTeamMember(ctx context.Context, request *pb.RemoveArenaTeamMemberRequest) (*pb.ArenaTeamMutationResponse, error) { + defer arenaTeamMutationLog("remove member", request.GetRealmID(), request.GetArenaTeamID(), time.Now()) + + if c.arenaTeams == nil { + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_FAILED), nil + } + + team, err := c.arenaTeams.GetTeam(ctx, request.GetRealmID(), request.GetArenaTeamID()) + if err != nil { + return arenaTeamMutationError(err) + } + + if err := c.arenaTeams.RemoveMember(ctx, request.GetRealmID(), request.GetArenaTeamID(), request.GetPlayerGUID(), request.GetActorGUID()); err != nil { + return arenaTeamMutationError(err) + } + + if request.GetActorGUID() != 0 { + targetName := arenaTeamMemberName(team, request.GetPlayerGUID()) + actorName := arenaTeamMemberName(team, request.GetActorGUID()) + if request.GetActorGUID() == request.GetPlayerGUID() { + c.publishArenaTeamNativeEvent( + request.GetRealmID(), + team, + arenaTeamNativeEventLeave, + request.GetPlayerGUID(), + targetName, + team.Name, + ) + } else { + c.publishArenaTeamNativeEvent( + request.GetRealmID(), + team, + arenaTeamNativeEventRemove, + 0, + targetName, + team.Name, + actorName, + ) + } + } + + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK), nil +} + +func (c *CharServer) DisbandArenaTeam(ctx context.Context, request *pb.DisbandArenaTeamRequest) (*pb.ArenaTeamMutationResponse, error) { + defer arenaTeamMutationLog("disband", request.GetRealmID(), request.GetArenaTeamID(), time.Now()) + + if c.arenaTeams == nil { + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_FAILED), nil + } + + team, err := c.arenaTeams.GetTeam(ctx, request.GetRealmID(), request.GetArenaTeamID()) + if err != nil { + return arenaTeamMutationError(err) + } + + if err := c.arenaTeams.Disband(ctx, request.GetRealmID(), request.GetArenaTeamID(), request.GetActorGUID()); err != nil { + return arenaTeamMutationError(err) + } + + if request.GetActorGUID() != 0 { + c.publishArenaTeamNativeEvent( + request.GetRealmID(), + team, + arenaTeamNativeEventDisbanded, + 0, + arenaTeamMemberName(team, request.GetActorGUID()), + team.Name, + ) + } + + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK), nil +} + +func (c *CharServer) SetArenaTeamCaptain(ctx context.Context, request *pb.SetArenaTeamCaptainRequest) (*pb.ArenaTeamMutationResponse, error) { + defer arenaTeamMutationLog("set captain", request.GetRealmID(), request.GetArenaTeamID(), time.Now()) + + if c.arenaTeams == nil { + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_FAILED), nil + } + + team, err := c.arenaTeams.GetTeam(ctx, request.GetRealmID(), request.GetArenaTeamID()) + if err != nil { + return arenaTeamMutationError(err) + } + + if err := c.arenaTeams.SetCaptain(ctx, request.GetRealmID(), request.GetArenaTeamID(), request.GetCaptainGUID(), request.GetActorGUID()); err != nil { + return arenaTeamMutationError(err) + } + + if request.GetActorGUID() != 0 { + c.publishArenaTeamNativeEvent( + request.GetRealmID(), + team, + arenaTeamNativeEventLeaderChanged, + 0, + arenaTeamMemberName(team, request.GetActorGUID()), + arenaTeamMemberName(team, request.GetCaptainGUID()), + team.Name, + ) + } + + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK), nil +} + +func (c *CharServer) SetArenaTeamName(ctx context.Context, request *pb.SetArenaTeamNameRequest) (*pb.ArenaTeamMutationResponse, error) { + defer arenaTeamMutationLog("set name", request.GetRealmID(), request.GetArenaTeamID(), time.Now()) + + if c.arenaTeams == nil { + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_FAILED), nil + } + + if err := c.arenaTeams.SetName(ctx, request.GetRealmID(), request.GetArenaTeamID(), request.GetName(), request.GetActorGUID()); err != nil { + return arenaTeamMutationError(err) + } + + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK), nil +} + +func (c *CharServer) SaveArenaTeamStats(ctx context.Context, request *pb.SaveArenaTeamStatsRequest) (*pb.ArenaTeamMutationResponse, error) { + defer arenaTeamMutationLog("save stats", request.GetRealmID(), request.GetArenaTeamID(), time.Now()) + + if c.arenaTeams == nil { + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_FAILED), nil + } + + members := make([]repo.ArenaTeamSaveStatsMember, 0, len(request.GetMembers())) + for _, member := range request.GetMembers() { + members = append(members, repo.ArenaTeamSaveStatsMember{ + PlayerGUID: member.GetPlayerGUID(), + PersonalRating: member.GetPersonalRating(), + WeekGames: member.GetWeekGames(), + WeekWins: member.GetWeekWins(), + SeasonGames: member.GetSeasonGames(), + SeasonWins: member.GetSeasonWins(), + MatchmakerRating: member.GetMatchmakerRating(), + MaxMMR: member.GetMaxMMR(), + SaveArenaStats: member.GetSaveArenaStats(), + }) + } + + if err := c.arenaTeams.SaveStats(ctx, repo.ArenaTeamSaveStatsRequest{ + RealmID: request.GetRealmID(), + ArenaTeamID: request.GetArenaTeamID(), + Rating: request.GetRating(), + WeekGames: request.GetWeekGames(), + WeekWins: request.GetWeekWins(), + SeasonGames: request.GetSeasonGames(), + SeasonWins: request.GetSeasonWins(), + Rank: request.GetRank(), + Slot: request.GetSlot(), + Members: members, + }); err != nil { + return arenaTeamMutationError(err) + } + + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK), nil +} + +func (c *CharServer) DeleteAllArenaTeams(ctx context.Context, request *pb.DeleteAllArenaTeamsRequest) (*pb.ArenaTeamMutationResponse, error) { + defer arenaTeamMutationLog("delete all", request.GetRealmID(), 0, time.Now()) + + if c.arenaTeams == nil { + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_FAILED), nil + } + + if err := c.arenaTeams.DeleteAll(ctx, request.GetRealmID()); err != nil { + return arenaTeamMutationError(err) + } + + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK), nil +} + +func (c *CharServer) ValidateArenaTeamCharacterDelete(ctx context.Context, request *pb.ValidateArenaTeamCharacterDeleteRequest) (*pb.ArenaTeamMutationResponse, error) { + defer arenaTeamMutationLog("validate character delete", request.GetRealmID(), 0, time.Now()) + + if c.arenaTeams == nil { + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_FAILED), nil + } + + if err := c.arenaTeams.ValidateCharacterDelete(ctx, request.GetRealmID(), request.GetPlayerGUID()); err != nil { + return arenaTeamMutationError(err) + } + + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK), nil +} + +func (c *CharServer) RemovePlayerFromArenaTeams(ctx context.Context, request *pb.RemovePlayerFromArenaTeamsRequest) (*pb.ArenaTeamMutationResponse, error) { + defer arenaTeamMutationLog("remove player from all teams", request.GetRealmID(), 0, time.Now()) + + if c.arenaTeams == nil { + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_FAILED), nil + } + + if err := c.arenaTeams.RemovePlayerFromTeams(ctx, request.GetRealmID(), request.GetPlayerGUID()); err != nil { + return arenaTeamMutationError(err) + } + + c.arenaInvitesMu.Lock() + for key, invite := range c.arenaInvites { + if key.realmID == request.GetRealmID() && (key.playerGUID == request.GetPlayerGUID() || invite.inviterGUID == request.GetPlayerGUID()) { + delete(c.arenaInvites, key) + } + } + c.arenaInvitesMu.Unlock() + + return arenaTeamMutationResponse(pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK), nil +} + +func arenaTeamQueueStatusFromError(err error) (pb.ArenaTeamQueueDataForRatedArenaResponse_Status, bool) { + switch { + case errors.Is(err, repo.ErrArenaTeamNotFound): + return pb.ArenaTeamQueueDataForRatedArenaResponse_NotFound, true + case errors.Is(err, repo.ErrArenaTeamMemberMismatch): + return pb.ArenaTeamQueueDataForRatedArenaResponse_MemberMismatch, true + case errors.Is(err, repo.ErrArenaTeamPartySize): + return pb.ArenaTeamQueueDataForRatedArenaResponse_InvalidPartySize, true + case errors.Is(err, repo.ErrArenaTeamInvalidType): + return pb.ArenaTeamQueueDataForRatedArenaResponse_InvalidType, true + default: + return pb.ArenaTeamQueueDataForRatedArenaResponse_Failed, false + } +} + +func arenaTeamMutationResponse(status pb.ArenaTeamMutationStatus) *pb.ArenaTeamMutationResponse { + return &pb.ArenaTeamMutationResponse{ + Api: ver, + Status: status, + } +} + +func arenaTeamMutationError(err error) (*pb.ArenaTeamMutationResponse, error) { + status, handled := arenaTeamMutationStatusFromError(err) + if handled { + return arenaTeamMutationResponse(status), nil + } + return nil, err +} + +func arenaTeamMutationStatusFromError(err error) (pb.ArenaTeamMutationStatus, bool) { + switch { + case errors.Is(err, repo.ErrArenaTeamNotFound): + return pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_NOT_FOUND, true + case errors.Is(err, repo.ErrArenaTeamMemberMismatch): + return pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_MEMBER_MISMATCH, true + case errors.Is(err, repo.ErrArenaTeamInvalidType): + return pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_INVALID_TYPE, true + case errors.Is(err, repo.ErrArenaTeamNameExists): + return pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_NAME_EXISTS, true + case errors.Is(err, repo.ErrArenaTeamAlreadyInTeam): + return pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_ALREADY_IN_TEAM, true + case errors.Is(err, repo.ErrArenaTeamRosterFull): + return pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_ROSTER_FULL, true + case errors.Is(err, repo.ErrArenaTeamInvalidName): + return pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_INVALID_NAME, true + case errors.Is(err, repo.ErrArenaTeamPermission): + return pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_PERMISSION_DENIED, true + case errors.Is(err, repo.ErrArenaTeamLeaderLeave): + return pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_LEADER_LEAVE, true + default: + return pb.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_FAILED, false + } +} + +func (c *CharServer) arenaTeamData(ctx context.Context, realmID uint32, team *repo.ArenaTeamDetails) *pb.ArenaTeamData { + if team == nil { + return nil + } + + online := map[uint64]struct{}{} + if c.onlineChars != nil && len(team.Members) > 0 { + guids := make([]uint64, 0, len(team.Members)) + for _, member := range team.Members { + guids = append(guids, member.PlayerGUID) + } + chars, err := c.onlineChars.CharactersByRealmAndGUIDs(ctx, realmID, guids) + if err == nil { + for _, char := range chars { + online[char.CharGUID] = struct{}{} + } + } + } + + members := make([]*pb.ArenaTeamMemberData, 0, len(team.Members)) + for _, member := range team.Members { + _, isOnline := online[member.PlayerGUID] + members = append(members, &pb.ArenaTeamMemberData{ + PlayerGUID: member.PlayerGUID, + Name: member.Name, + Online: isOnline, + Level: uint32(member.Level), + Class: uint32(member.Class), + WeekGames: member.WeekGames, + WeekWins: member.WeekWins, + SeasonGames: member.SeasonGames, + SeasonWins: member.SeasonWins, + PersonalRating: member.PersonalRating, + MatchmakerRating: member.MatchmakerRating, + MaxMMR: member.MaxMMR, + }) + } + + return &pb.ArenaTeamData{ + ArenaTeamID: team.ArenaTeamID, + Name: team.Name, + CaptainGUID: team.CaptainGUID, + Type: uint32(team.Type), + Rating: team.Rating, + WeekGames: team.WeekGames, + WeekWins: team.WeekWins, + SeasonGames: team.SeasonGames, + SeasonWins: team.SeasonWins, + Rank: team.Rank, + BackgroundColor: team.BackgroundColor, + EmblemStyle: uint32(team.EmblemStyle), + EmblemColor: team.EmblemColor, + BorderStyle: uint32(team.BorderStyle), + BorderColor: team.BorderColor, + Members: members, + } +} + +func arenaTeamHasMemberData(team *repo.ArenaTeamDetails, playerGUID uint64) bool { + if team == nil { + return false + } + for _, member := range team.Members { + if member.PlayerGUID == playerGUID { + return true + } + } + return false +} + +func arenaTeamMemberName(team *repo.ArenaTeamDetails, playerGUID uint64) string { + if team == nil { + return "" + } + for _, member := range team.Members { + if member.PlayerGUID == playerGUID { + return member.Name + } + } + return "" +} + +func (c *CharServer) arenaTeamTargetIgnoresInviter(ctx context.Context, realmID uint32, targetGUID uint64, inviterGUID uint64) (bool, error) { + if c.friendsService == nil { + return false, nil + } + + friends, err := c.friendsService.GetFriendsList(ctx, realmID, targetGUID) + if err != nil { + return false, err + } + if friends == nil { + return false, nil + } + for _, ignoredGUID := range friends.Ignored { + if ignoredGUID == inviterGUID { + return true, nil + } + } + return false, nil +} + +func (c *CharServer) publishArenaTeamNativeEvent(realmID uint32, team *repo.ArenaTeamDetails, event uint8, eventGUID uint64, args ...string) { + if c.eventsProducer == nil || team == nil || len(team.Members) == 0 { + return + } + + receiverGUIDs := make([]uint64, 0, len(team.Members)) + for _, member := range team.Members { + receiverGUIDs = append(receiverGUIDs, member.PlayerGUID) + } + + if err := c.eventsProducer.ArenaTeamNativeEvent(&events.CharEventArenaTeamNativeEventPayload{ + RealmID: realmID, + ReceiverGUIDs: receiverGUIDs, + ArenaTeamID: team.ArenaTeamID, + Event: event, + EventGUID: eventGUID, + Args: args, + }); err != nil { + log.Error(). + Err(err). + Uint32("realmID", realmID). + Uint32("arenaTeamID", team.ArenaTeamID). + Uint8("event", event). + Msg("Failed to publish arena team native event") + } +} + +func arenaTeamMutationLog(action string, realmID uint32, arenaTeamID uint32, start time.Time) { + log.Debug(). + Str("action", action). + Uint32("realmID", realmID). + Uint32("arenaTeamID", arenaTeamID). + Str("timeTook", time.Since(start).String()). + Msg("Handled arena team mutation request") +} + +func arenaTeamCreateStatusFromError(err error) (pb.CreateArenaTeamFromPetitionResponse_Status, bool) { + switch { + case errors.Is(err, repo.ErrArenaTeamNotFound): + return pb.CreateArenaTeamFromPetitionResponse_NotFound, true + case errors.Is(err, repo.ErrArenaTeamNotOwner): + return pb.CreateArenaTeamFromPetitionResponse_NotOwner, true + case errors.Is(err, repo.ErrArenaTeamInvalidType): + return pb.CreateArenaTeamFromPetitionResponse_InvalidType, true + case errors.Is(err, repo.ErrArenaTeamNameExists): + return pb.CreateArenaTeamFromPetitionResponse_NameExists, true + case errors.Is(err, repo.ErrArenaTeamAlreadyInTeam): + return pb.CreateArenaTeamFromPetitionResponse_AlreadyInTeam, true + case errors.Is(err, repo.ErrArenaTeamNotEnoughSigns): + return pb.CreateArenaTeamFromPetitionResponse_NotEnoughSignatures, true + case errors.Is(err, repo.ErrArenaTeamInvalidName): + return pb.CreateArenaTeamFromPetitionResponse_InvalidName, true + default: + return pb.CreateArenaTeamFromPetitionResponse_Failed, false + } +} diff --git a/apps/charserver/server/charserver.go b/apps/charserver/server/charserver.go index 7d84642..b26d637 100644 --- a/apps/charserver/server/charserver.go +++ b/apps/charserver/server/charserver.go @@ -2,6 +2,7 @@ package server import ( "context" + "sync" "time" "github.com/rs/zerolog/log" @@ -9,6 +10,7 @@ import ( "github.com/walkline/ToCloud9/apps/charserver/repo" "github.com/walkline/ToCloud9/apps/charserver/service" "github.com/walkline/ToCloud9/gen/characters/pb" + "github.com/walkline/ToCloud9/shared/events" ) const ( @@ -18,22 +20,41 @@ const ( type CharServer struct { pb.UnimplementedCharactersServiceServer repo repo.Characters + arenaTeams repo.ArenaTeams whoHandler repo.WhoHandler itemsTemplate repo.ItemsTemplate onlineChars repo.CharactersOnline friendsService service.FriendsService + eventsProducer events.CharactersServiceProducer + arenaInvitesMu sync.Mutex + arenaInvites map[arenaTeamInviteKey]arenaTeamInvite } -func NewCharServer(repo repo.Characters, onlineChars repo.CharactersOnline, whoHandler repo.WhoHandler, itemsTemplate repo.ItemsTemplate, friendsService service.FriendsService) pb.CharactersServiceServer { +func NewCharServer(repo repo.Characters, arenaTeams repo.ArenaTeams, onlineChars repo.CharactersOnline, whoHandler repo.WhoHandler, itemsTemplate repo.ItemsTemplate, friendsService service.FriendsService, eventsProducer events.CharactersServiceProducer) pb.CharactersServiceServer { return &CharServer{ repo: repo, + arenaTeams: arenaTeams, whoHandler: whoHandler, itemsTemplate: itemsTemplate, onlineChars: onlineChars, friendsService: friendsService, + eventsProducer: eventsProducer, + arenaInvites: make(map[arenaTeamInviteKey]arenaTeamInvite), } } +type arenaTeamInviteKey struct { + realmID uint32 + playerGUID uint64 +} + +type arenaTeamInvite struct { + arenaTeamID uint32 + inviterGUID uint64 + inviterName string + teamName string +} + func (c *CharServer) CharactersToLoginForAccount(ctx context.Context, request *pb.CharactersToLoginForAccountRequest) (*pb.CharactersToLoginForAccountResponse, error) { defer func(t time.Time) { log.Debug(). @@ -95,6 +116,7 @@ func (c *CharServer) CharactersToLoginForAccount(ctx context.Context, request *p Equipments: equipments, Banned: char.Banned, AccountID: char.AccountID, + ExtraFlags: char.ExtraFlags, } result = append(result, item) } @@ -134,16 +156,41 @@ func (c *CharServer) AccountDataForAccount(ctx context.Context, request *pb.Acco }, nil } +func (c *CharServer) UpdateAccountDataForAccount(ctx context.Context, request *pb.UpdateAccountDataForAccountRequest) (*pb.UpdateAccountDataForAccountResponse, error) { + defer func(t time.Time) { + log.Debug(). + Uint32("accountID", request.AccountID). + Uint32("realmID", request.RealmID). + Uint32("type", request.Type). + Str("timeTook", time.Since(t).String()). + Msg("Handled account data update") + }(time.Now()) + + err := c.repo.UpdateAccountDataForAccountID(ctx, request.RealmID, request.AccountID, repo.AccountData{ + Type: uint8(request.Type), + Time: request.Time, + Data: request.Data, + }) + if err != nil { + return nil, err + } + + return &pb.UpdateAccountDataForAccountResponse{ + Api: ver, + }, nil +} + func (c *CharServer) CharactersToLoginByGUID(ctx context.Context, request *pb.CharactersToLoginByGUIDRequest) (*pb.CharactersToLoginByGUIDResponse, error) { defer func(t time.Time) { log.Debug(). Uint64("characterID", request.CharacterGUID). + Uint32("accountID", request.AccountID). Uint32("realmID", request.RealmID). Str("timeTook", time.Since(t).String()). Msg("Handled characters to login by GUID") }(time.Now()) - char, err := c.repo.CharacterToLogInByGUID(ctx, request.RealmID, request.CharacterGUID) + char, err := c.repo.CharacterToLogInByGUID(ctx, request.RealmID, request.AccountID, request.CharacterGUID) if err != nil { return nil, err } @@ -194,6 +241,7 @@ func (c *CharServer) CharactersToLoginByGUID(ctx context.Context, request *pb.Ch Equipments: equipments, Banned: char.Banned, AccountID: char.AccountID, + ExtraFlags: char.ExtraFlags, } } @@ -203,6 +251,83 @@ func (c *CharServer) CharactersToLoginByGUID(ctx context.Context, request *pb.Ch }, nil } +func (c *CharServer) RecordLfgDungeonRoute(ctx context.Context, request *pb.RecordLfgDungeonRouteRequest) (*pb.RecordLfgDungeonRouteResponse, error) { + route := request.GetRoute() + if route == nil { + return &pb.RecordLfgDungeonRouteResponse{Api: ver}, nil + } + + err := c.repo.RecordLfgDungeonRoute(ctx, repo.LfgDungeonRoute{ + RealmID: route.GetRealmID(), + PlayerGUID: route.GetPlayerGUID(), + DungeonEntry: route.GetDungeonEntry(), + MapID: route.GetMapID(), + Difficulty: uint8(route.GetDifficulty()), + OwnerRealmID: route.GetOwnerRealmID(), + IsCrossRealm: route.GetIsCrossRealm(), + RequiresBoundInstance: route.GetRequiresBoundInstance(), + InstanceID: route.GetInstanceID(), + }) + if err != nil { + return nil, err + } + return &pb.RecordLfgDungeonRouteResponse{Api: ver}, nil +} + +func (c *CharServer) ConfirmLfgDungeonRouteEntered(ctx context.Context, request *pb.ConfirmLfgDungeonRouteEnteredRequest) (*pb.ConfirmLfgDungeonRouteEnteredResponse, error) { + route, err := c.repo.ConfirmLfgDungeonRouteEntered(ctx, request.GetRealmID(), request.GetPlayerGUID(), request.GetMapID(), uint8(request.GetDifficulty()), request.GetInstanceID()) + if err != nil { + return nil, err + } + return &pb.ConfirmLfgDungeonRouteEnteredResponse{ + Api: ver, + Route: lfgDungeonRouteToProto(route), + }, nil +} + +func (c *CharServer) ClearUnboundLfgDungeonRoute(ctx context.Context, request *pb.ClearUnboundLfgDungeonRouteRequest) (*pb.ClearUnboundLfgDungeonRouteResponse, error) { + if err := c.repo.ClearUnboundLfgDungeonRoute(ctx, request.GetRealmID(), request.GetPlayerGUID(), request.GetMapID()); err != nil { + return nil, err + } + return &pb.ClearUnboundLfgDungeonRouteResponse{Api: ver}, nil +} + +func (c *CharServer) GetLfgDungeonRoute(ctx context.Context, request *pb.GetLfgDungeonRouteRequest) (*pb.GetLfgDungeonRouteResponse, error) { + route, err := c.repo.LfgDungeonRouteForPlayer(ctx, request.GetRealmID(), request.GetPlayerGUID(), request.GetMapID()) + if err != nil { + return nil, err + } + if route == nil { + return &pb.GetLfgDungeonRouteResponse{Api: ver}, nil + } + + available := !route.RequiresBoundInstance || route.BoundInstanceID != 0 + return &pb.GetLfgDungeonRouteResponse{ + Api: ver, + Found: true, + Available: available, + Route: lfgDungeonRouteToProto(route), + }, nil +} + +func lfgDungeonRouteToProto(route *repo.LfgDungeonRoute) *pb.LfgDungeonRoute { + if route == nil { + return nil + } + return &pb.LfgDungeonRoute{ + RealmID: route.RealmID, + PlayerGUID: route.PlayerGUID, + DungeonEntry: route.DungeonEntry, + MapID: route.MapID, + Difficulty: uint32(route.Difficulty), + OwnerRealmID: route.OwnerRealmID, + IsCrossRealm: route.IsCrossRealm, + RequiresBoundInstance: route.RequiresBoundInstance, + InstanceID: route.InstanceID, + BoundInstanceID: route.BoundInstanceID, + } +} + func (c *CharServer) WhoQuery(ctx context.Context, request *pb.WhoQueryRequest) (*pb.WhoQueryResponse, error) { defer func(t time.Time) { log.Debug(). @@ -351,6 +476,73 @@ func (c *CharServer) CharacterByName(ctx context.Context, request *pb.CharacterB }, nil } +func (c *CharServer) CharacterByGUID(ctx context.Context, request *pb.CharacterByGUIDRequest) (*pb.CharacterByGUIDResponse, error) { + defer func(t time.Time) { + log.Debug(). + Uint64("guid", request.CharacterGUID). + Uint32("realmID", request.RealmID). + Str("timeTook", time.Since(t).String()). + Msg("Handled character by guid") + }(time.Now()) + + chars, err := c.onlineChars.CharactersByRealmAndGUIDs(ctx, request.RealmID, []uint64{request.CharacterGUID}) + if err != nil { + return nil, err + } + + if len(chars) > 0 { + char := chars[0] + return &pb.CharacterByGUIDResponse{ + Api: ver, + Character: &pb.CharacterByNameResponse_Char{ + RealmID: char.RealmID, + IsOnline: true, + GatewayID: char.GatewayID, + CharGUID: char.CharGUID, + CharName: char.CharName, + CharRace: uint32(char.CharRace), + CharClass: uint32(char.CharClass), + CharGender: uint32(char.CharGender), + CharLvl: uint32(char.CharLevel), + CharZone: char.CharZone, + CharMap: char.CharMap, + CharGuildID: uint64(char.CharGuildID), + AccountID: char.AccountID, + }, + }, nil + } + + char, err := c.repo.CharacterByGUID(ctx, request.RealmID, request.CharacterGUID) + if err != nil { + return nil, err + } + if char == nil { + return &pb.CharacterByGUIDResponse{ + Api: ver, + Character: nil, + }, nil + } + + return &pb.CharacterByGUIDResponse{ + Api: ver, + Character: &pb.CharacterByNameResponse_Char{ + RealmID: char.RealmID, + IsOnline: false, + GatewayID: "", + CharGUID: char.CharGUID, + CharName: char.CharName, + CharRace: uint32(char.CharRace), + CharClass: uint32(char.CharClass), + CharGender: uint32(char.CharGender), + CharLvl: uint32(char.CharLevel), + CharZone: char.CharZone, + CharMap: char.CharMap, + CharGuildID: uint64(char.CharGuildID), + AccountID: char.AccountID, + }, + }, nil +} + func (c *CharServer) ShortOnlineCharactersDataByGUIDs(ctx context.Context, request *pb.ShortCharactersDataByGUIDsRequest) (*pb.ShortCharactersDataByGUIDsResponse, error) { defer func(t time.Time) { log.Debug(). @@ -418,6 +610,7 @@ func (c *CharServer) GetFriendsList(ctx context.Context, request *pb.GetFriendsL friends := make([]*pb.GetFriendsListResponse_Friend, 0, len(friendsList.Friends)) for _, friend := range friendsList.Friends { friends = append(friends, &pb.GetFriendsListResponse_Friend{ + RealmID: friend.RealmID, Guid: friend.GUID, Note: friend.Note, Status: uint32(friend.Status), @@ -457,12 +650,92 @@ func (c *CharServer) AddFriend(ctx context.Context, request *pb.AddFriendRequest } return &pb.AddFriendResponse{ - Api: ver, - Result: result.Result, - Status: uint32(result.Status), - Area: result.Area, - Level: result.Level, - ClassID: result.ClassID, + Api: ver, + Result: result.Result, + Status: uint32(result.Status), + Area: result.Area, + Level: result.Level, + ClassID: result.ClassID, + FriendGUID: result.FriendGUID, + Pending: result.Pending, + Accepted: result.Accepted, + }, nil +} + +func (c *CharServer) AddRealIDFriendByEmail(ctx context.Context, request *pb.AddRealIDFriendByEmailRequest) (*pb.AddRealIDFriendByEmailResponse, error) { + defer func(t time.Time) { + log.Debug(). + Uint64("playerGUID", request.PlayerGUID). + Uint32("accountID", request.AccountID). + Uint32("realmID", request.RealmID). + Str("timeTook", time.Since(t).String()). + Msg("Handled add real id friend by email") + }(time.Now()) + + result, err := c.friendsService.AddRealIDFriendByEmail(ctx, request.RealmID, request.PlayerGUID, request.AccountID, request.Email, request.Note) + if err != nil { + return nil, err + } + + return &pb.AddRealIDFriendByEmailResponse{ + Api: ver, + Result: result.Result, + Status: uint32(result.Status), + Area: result.Area, + Level: result.Level, + ClassID: result.ClassID, + FriendGUID: result.FriendGUID, + Pending: result.Pending, + Accepted: result.Accepted, + }, nil +} + +func (c *CharServer) AcceptRealIDFriend(ctx context.Context, request *pb.AcceptRealIDFriendRequest) (*pb.AcceptRealIDFriendResponse, error) { + defer func(t time.Time) { + log.Debug(). + Uint64("playerGUID", request.PlayerGUID). + Uint32("accountID", request.AccountID). + Uint32("requesterAccountID", request.RequesterAccountID). + Uint32("realmID", request.RealmID). + Str("timeTook", time.Since(t).String()). + Msg("Handled accept real id friend") + }(time.Now()) + + result, err := c.friendsService.AcceptRealIDFriend(ctx, request.RealmID, request.PlayerGUID, request.AccountID, request.RequesterAccountID, request.Note) + if err != nil { + return nil, err + } + + return &pb.AcceptRealIDFriendResponse{ + Api: ver, + Result: result.Result, + Status: uint32(result.Status), + Area: result.Area, + Level: result.Level, + ClassID: result.ClassID, + FriendGUID: result.FriendGUID, + Pending: result.Pending, + Accepted: result.Accepted, + }, nil +} + +func (c *CharServer) AreRealIDFriends(ctx context.Context, request *pb.AreRealIDFriendsRequest) (*pb.AreRealIDFriendsResponse, error) { + defer func(t time.Time) { + log.Debug(). + Uint32("accountID", request.AccountID). + Uint32("friendAccountID", request.FriendAccountID). + Str("timeTook", time.Since(t).String()). + Msg("Handled are real id friends") + }(time.Now()) + + accepted, err := c.friendsService.AreRealIDFriends(ctx, request.AccountID, request.FriendAccountID) + if err != nil { + return nil, err + } + + return &pb.AreRealIDFriendsResponse{ + Api: ver, + Accepted: accepted, }, nil } @@ -581,8 +854,8 @@ func (c *CharServer) GetOnlineCharacters(ctx context.Context, request *pb.GetOnl } return &pb.GetOnlineCharactersResponse{ - Api: ver, - CharacterGUIDs: guids, - TotalCount: uint32(len(guids)), + Api: ver, + CharacterGUIDs: guids, + TotalCount: uint32(len(guids)), }, nil } diff --git a/apps/charserver/service/characters-listener.go b/apps/charserver/service/characters-listener.go index 8bc7403..7f8a7ec 100644 --- a/apps/charserver/service/characters-listener.go +++ b/apps/charserver/service/characters-listener.go @@ -1,8 +1,6 @@ package service import ( - "context" - "github.com/nats-io/nats.go" "github.com/rs/zerolog/log" @@ -32,25 +30,7 @@ func (c *CharactersListener) Listen() error { return } - err = c.charRepo.Add(context.TODO(), &repo.Character{ - RealmID: loggedInP.RealmID, - GatewayID: loggedInP.GatewayID, - CharGUID: loggedInP.CharGUID, - CharName: loggedInP.CharName, - CharRace: loggedInP.CharRace, - CharClass: loggedInP.CharClass, - CharGender: loggedInP.CharGender, - CharLevel: loggedInP.CharLevel, - CharZone: loggedInP.CharZone, - CharMap: loggedInP.CharMap, - CharPosX: loggedInP.CharPosX, - CharPosY: loggedInP.CharPosY, - CharPosZ: loggedInP.CharPosZ, - CharGuildID: loggedInP.CharGuildID, - AccountID: loggedInP.AccountID, - }) - - if err != nil { + if err = c.charRepo.HandleCharacterLoggedIn(loggedInP); err != nil { log.Error().Err(err).Msg("can't add character in GWEventCharacterLoggedIn event") return } @@ -69,8 +49,7 @@ func (c *CharactersListener) Listen() error { return } - err = c.charRepo.Remove(context.TODO(), loggedOutP.RealmID, loggedOutP.CharGUID) - if err != nil { + if err = c.charRepo.HandleCharacterLoggedOut(loggedOutP); err != nil { log.Error().Err(err).Msg("can't remove character in GWEventCharacterLoggedOut event") return } diff --git a/apps/charserver/service/friends-cache.go b/apps/charserver/service/friends-cache.go index 178a89e..f4b2149 100644 --- a/apps/charserver/service/friends-cache.go +++ b/apps/charserver/service/friends-cache.go @@ -6,13 +6,16 @@ import ( "time" "github.com/walkline/ToCloud9/shared/events" + wowguid "github.com/walkline/ToCloud9/shared/wow/guid" ) //go:generate mockery --name=OnlinePlayersCache type OnlinePlayersCache interface { - PlayerLoggedIn(playerGUID uint64, level, class, area uint32) - PlayerLoggedOut(playerGUID uint64) - GetOnlineInfo(playerGUID uint64) (OnlinePlayerInfo, bool) + PlayerLoggedIn(realmID uint32, playerGUID uint64, accountID uint32, name string, race, level, class, area uint32) + PlayerLoggedOut(realmID uint32, playerGUID uint64) + GetOnlineInfo(realmID uint32, playerGUID uint64) (OnlinePlayerInfo, bool) + GetOnlineInfoForAccount(accountID uint32) (OnlinePlayerInfo, bool) + GetOnlineInfosForAccount(accountID uint32) []OnlinePlayerInfo SetFriendsService(friendsService FriendsService) HandleCharacterLoggedIn(payload events.GWEventCharacterLoggedInPayload) error @@ -24,8 +27,15 @@ type onlinePlayersCacheImpl struct { // cacheMutex guards onlineInfoByGUID map cacheMutex sync.RWMutex - // onlineInfoByGUID maps player GUID to their online info - onlineInfoByGUID map[uint64]*OnlinePlayerInfo + // onlineInfoByGUID maps realm-scoped player keys to their online info. + onlineInfoByGUID map[onlinePlayerKey]*OnlinePlayerInfo + + // onlineInfoByAccount maps account ids to their active characters. + onlineInfoByAccount map[uint32]map[onlinePlayerKey]*OnlinePlayerInfo + + // lifecycleEventTimes stores the newest login/logout event time per player + // so delayed cross-subject NATS delivery cannot resurrect stale presence. + lifecycleEventTimes map[onlinePlayerKey]uint64 // friendsService is used to notify friends about status changes friendsService FriendsService @@ -33,7 +43,9 @@ type onlinePlayersCacheImpl struct { func NewOnlinePlayersCache() OnlinePlayersCache { return &onlinePlayersCacheImpl{ - onlineInfoByGUID: make(map[uint64]*OnlinePlayerInfo), + onlineInfoByGUID: make(map[onlinePlayerKey]*OnlinePlayerInfo), + onlineInfoByAccount: make(map[uint32]map[onlinePlayerKey]*OnlinePlayerInfo), + lifecycleEventTimes: make(map[onlinePlayerKey]uint64), } } @@ -41,40 +53,118 @@ func (o *onlinePlayersCacheImpl) SetFriendsService(friendsService FriendsService o.friendsService = friendsService } -func (o *onlinePlayersCacheImpl) PlayerLoggedIn(playerGUID uint64, level, class, area uint32) { +type onlinePlayerKey struct { + realmID uint32 + guid uint64 +} + +func (o *onlinePlayersCacheImpl) PlayerLoggedIn(realmID uint32, playerGUID uint64, accountID uint32, name string, race, level, class, area uint32) { + o.playerLoggedInAt(realmID, playerGUID, accountID, name, race, level, class, area, 0) +} + +func (o *onlinePlayersCacheImpl) playerLoggedInAt(realmID uint32, playerGUID uint64, accountID uint32, name string, race, level, class, area uint32, eventTimeUnixNano uint64) bool { o.cacheMutex.Lock() defer o.cacheMutex.Unlock() - o.onlineInfoByGUID[playerGUID] = &OnlinePlayerInfo{ - GUID: playerGUID, - Level: level, - Class: class, - Area: area, - Status: 1, // online + playerGUID = wowguid.PlayerLowGUID(playerGUID) + key := onlinePlayerKey{realmID: realmID, guid: playerGUID} + if !o.shouldApplyLifecycleEventLocked(key, eventTimeUnixNano) { + return false + } + if previous := o.onlineInfoByGUID[key]; previous != nil && previous.AccountID != accountID { + delete(o.onlineInfoByAccount[previous.AccountID], key) + if len(o.onlineInfoByAccount[previous.AccountID]) == 0 { + delete(o.onlineInfoByAccount, previous.AccountID) + } + } + + info := &OnlinePlayerInfo{ + RealmID: realmID, + AccountID: accountID, + GUID: playerGUID, + Name: name, + Race: race, + Level: level, + Class: class, + Area: area, + Status: 1, // online } + o.onlineInfoByGUID[key] = info + if o.onlineInfoByAccount[accountID] == nil { + o.onlineInfoByAccount[accountID] = make(map[onlinePlayerKey]*OnlinePlayerInfo) + } + o.onlineInfoByAccount[accountID][key] = info + o.rememberLifecycleEventTimeLocked(key, eventTimeUnixNano) + return true +} + +func (o *onlinePlayersCacheImpl) PlayerLoggedOut(realmID uint32, playerGUID uint64) { + o.playerLoggedOutAt(realmID, playerGUID, 0) } -func (o *onlinePlayersCacheImpl) PlayerLoggedOut(playerGUID uint64) { +func (o *onlinePlayersCacheImpl) playerLoggedOutAt(realmID uint32, playerGUID uint64, eventTimeUnixNano uint64) bool { o.cacheMutex.Lock() defer o.cacheMutex.Unlock() - delete(o.onlineInfoByGUID, playerGUID) + playerGUID = wowguid.PlayerLowGUID(playerGUID) + key := onlinePlayerKey{realmID: realmID, guid: playerGUID} + if !o.shouldApplyLifecycleEventLocked(key, eventTimeUnixNano) { + return false + } + + info := o.onlineInfoByGUID[key] + delete(o.onlineInfoByGUID, key) + if info != nil { + delete(o.onlineInfoByAccount[info.AccountID], key) + if len(o.onlineInfoByAccount[info.AccountID]) == 0 { + delete(o.onlineInfoByAccount, info.AccountID) + } + } + o.rememberLifecycleEventTimeLocked(key, eventTimeUnixNano) + return true } -func (o *onlinePlayersCacheImpl) GetOnlineInfo(playerGUID uint64) (OnlinePlayerInfo, bool) { +func (o *onlinePlayersCacheImpl) GetOnlineInfo(realmID uint32, playerGUID uint64) (OnlinePlayerInfo, bool) { o.cacheMutex.RLock() defer o.cacheMutex.RUnlock() - info, ok := o.onlineInfoByGUID[playerGUID] + playerGUID = wowguid.PlayerLowGUID(playerGUID) + info, ok := o.onlineInfoByGUID[onlinePlayerKey{realmID: realmID, guid: playerGUID}] if !ok { return OnlinePlayerInfo{}, false } return *info, true } +func (o *onlinePlayersCacheImpl) GetOnlineInfoForAccount(accountID uint32) (OnlinePlayerInfo, bool) { + o.cacheMutex.RLock() + defer o.cacheMutex.RUnlock() + + infos := o.onlineInfoByAccount[accountID] + for _, info := range infos { + return *info, true + } + return OnlinePlayerInfo{}, false +} + +func (o *onlinePlayersCacheImpl) GetOnlineInfosForAccount(accountID uint32) []OnlinePlayerInfo { + o.cacheMutex.RLock() + defer o.cacheMutex.RUnlock() + + infos := o.onlineInfoByAccount[accountID] + result := make([]OnlinePlayerInfo, 0, len(infos)) + for _, info := range infos { + result = append(result, *info) + } + return result +} + // HandleCharacterLoggedIn handles character login event from gateway func (o *onlinePlayersCacheImpl) HandleCharacterLoggedIn(payload events.GWEventCharacterLoggedInPayload) error { - o.PlayerLoggedIn(payload.CharGUID, uint32(payload.CharLevel), uint32(payload.CharClass), payload.CharZone) + playerGUID := wowguid.PlayerLowGUID(payload.CharGUID) + if !o.playerLoggedInAt(payload.RealmID, playerGUID, payload.AccountID, payload.CharName, uint32(payload.CharRace), uint32(payload.CharLevel), uint32(payload.CharClass), payload.CharZone, payload.EventTimeUnixNano) { + return nil + } // Notify friends service about login if o.friendsService != nil { @@ -83,7 +173,7 @@ func (o *onlinePlayersCacheImpl) HandleCharacterLoggedIn(payload events.GWEventC return o.friendsService.NotifyStatusChange( ctx, payload.RealmID, - payload.CharGUID, + playerGUID, 1, // online payload.CharZone, uint32(payload.CharLevel), @@ -96,7 +186,10 @@ func (o *onlinePlayersCacheImpl) HandleCharacterLoggedIn(payload events.GWEventC // HandleCharacterLoggedOut handles character logout event from gateway func (o *onlinePlayersCacheImpl) HandleCharacterLoggedOut(payload events.GWEventCharacterLoggedOutPayload) error { - o.PlayerLoggedOut(payload.CharGUID) + playerGUID := wowguid.PlayerLowGUID(payload.CharGUID) + if !o.playerLoggedOutAt(payload.RealmID, playerGUID, payload.EventTimeUnixNano) { + return nil + } // Notify friends service about logout if o.friendsService != nil { @@ -105,7 +198,7 @@ func (o *onlinePlayersCacheImpl) HandleCharacterLoggedOut(payload events.GWEvent return o.friendsService.NotifyStatusChange( ctx, payload.RealmID, - payload.CharGUID, + playerGUID, 0, // offline 0, 0, 0, ) @@ -120,10 +213,18 @@ func (o *onlinePlayersCacheImpl) HandleCharactersUpdates(payload events.GWEventC defer o.cacheMutex.Unlock() for _, update := range payload.Updates { - info, exists := o.onlineInfoByGUID[update.ID] + key := onlinePlayerKey{realmID: payload.RealmID, guid: wowguid.PlayerLowGUID(update.ID)} + info, exists := o.onlineInfoByGUID[key] if !exists { continue } + eventTimeUnixNano := update.EventTimeUnixNano + if eventTimeUnixNano == 0 { + eventTimeUnixNano = payload.EventTimeUnixNano + } + if eventTimeUnixNano != 0 && o.lifecycleEventTimes[key] > eventTimeUnixNano { + continue + } // Update level if update.Lvl != nil { @@ -140,3 +241,17 @@ func (o *onlinePlayersCacheImpl) HandleCharactersUpdates(payload events.GWEventC return nil } + +func (o *onlinePlayersCacheImpl) shouldApplyLifecycleEventLocked(key onlinePlayerKey, eventTimeUnixNano uint64) bool { + return eventTimeUnixNano == 0 || o.lifecycleEventTimes[key] <= eventTimeUnixNano +} + +func (o *onlinePlayersCacheImpl) rememberLifecycleEventTimeLocked(key onlinePlayerKey, eventTimeUnixNano uint64) { + if eventTimeUnixNano == 0 { + return + } + if o.lifecycleEventTimes == nil { + o.lifecycleEventTimes = map[onlinePlayerKey]uint64{} + } + o.lifecycleEventTimes[key] = eventTimeUnixNano +} diff --git a/apps/charserver/service/friends.go b/apps/charserver/service/friends.go index 24714eb..6a39e53 100644 --- a/apps/charserver/service/friends.go +++ b/apps/charserver/service/friends.go @@ -3,10 +3,13 @@ package service import ( "context" "errors" + "sort" "github.com/walkline/ToCloud9/apps/charserver" "github.com/walkline/ToCloud9/apps/charserver/repo" + "github.com/walkline/ToCloud9/shared/authidentity" "github.com/walkline/ToCloud9/shared/events" + wowguid "github.com/walkline/ToCloud9/shared/wow/guid" ) const ( @@ -16,23 +19,23 @@ const ( // FriendsResult enum values matching AzerothCore protocol const ( - FriendResultDBError = 0x00 - FriendResultListFull = 0x01 - FriendResultOnline = 0x02 - FriendResultOffline = 0x03 - FriendResultNotFound = 0x04 - FriendResultRemoved = 0x05 - FriendResultAddedOnline = 0x06 - FriendResultAddedOffline = 0x07 - FriendResultAlready = 0x08 - FriendResultSelf = 0x09 - FriendResultEnemy = 0x0A - FriendResultIgnoreSelf = 0x0B - FriendResultIgnoreNotFound = 0x0C - FriendResultIgnoreAlready = 0x0D - FriendResultIgnoreAdded = 0x0E - FriendResultIgnoreRemoved = 0x0F - FriendResultIgnoreFull = 0x10 + FriendResultDBError = 0x00 + FriendResultListFull = 0x01 + FriendResultOnline = 0x02 + FriendResultOffline = 0x03 + FriendResultNotFound = 0x04 + FriendResultRemoved = 0x05 + FriendResultAddedOnline = 0x06 + FriendResultAddedOffline = 0x07 + FriendResultAlready = 0x08 + FriendResultSelf = 0x09 + FriendResultEnemy = 0x0A + FriendResultIgnoreSelf = 0x0B + FriendResultIgnoreNotFound = 0x0C + FriendResultIgnoreAlready = 0x0D + FriendResultIgnoreAdded = 0x0E + FriendResultIgnoreRemoved = 0x0F + FriendResultIgnoreFull = 0x10 ) var ( @@ -45,14 +48,19 @@ var ( ) type OnlinePlayerInfo struct { - GUID uint64 - Level uint32 - Class uint32 - Area uint32 - Status uint8 + RealmID uint32 + AccountID uint32 + GUID uint64 + Name string + Race uint32 + Level uint32 + Class uint32 + Area uint32 + Status uint8 } type FriendInfo struct { + RealmID uint32 GUID uint64 Note string Status uint8 @@ -67,17 +75,23 @@ type FriendsList struct { } type AddFriendResult struct { - Result uint32 - Status uint8 - Area uint32 - Level uint32 - ClassID uint32 + Result uint32 + FriendGUID uint64 + Status uint8 + Area uint32 + Level uint32 + ClassID uint32 + Pending bool + Accepted bool } //go:generate mockery --name=FriendsService type FriendsService interface { GetFriendsList(ctx context.Context, realmID uint32, playerGUID uint64) (*FriendsList, error) AddFriend(ctx context.Context, realmID uint32, playerGUID, friendGUID uint64, friendName, note string) (*AddFriendResult, error) + AddRealIDFriendByEmail(ctx context.Context, realmID uint32, playerGUID uint64, accountID uint32, email, note string) (*AddFriendResult, error) + AcceptRealIDFriend(ctx context.Context, realmID uint32, playerGUID uint64, accountID uint32, requesterAccountID uint32, note string) (*AddFriendResult, error) + AreRealIDFriends(ctx context.Context, accountID uint32, friendAccountID uint32) (bool, error) RemoveFriend(ctx context.Context, realmID uint32, playerGUID, friendGUID uint64) error SetFriendNote(ctx context.Context, realmID uint32, playerGUID, friendGUID uint64, note string) error AddIgnore(ctx context.Context, realmID uint32, playerGUID, ignoredGUID uint64) (uint32, error) @@ -89,16 +103,33 @@ type FriendsService interface { type friendsServiceImpl struct { charRepo repo.Characters + accountRepo repo.Accounts onlineCache OnlinePlayersCache eventsProducer events.FriendsServiceProducer + + realIDCrossFaction bool +} + +type FriendsServiceOption func(*friendsServiceImpl) + +func WithRealIDCrossFaction(enabled bool) FriendsServiceOption { + return func(f *friendsServiceImpl) { + f.realIDCrossFaction = enabled + } } -func NewFriendsService(charRepo repo.Characters, onlineCache OnlinePlayersCache, eventsProducer events.FriendsServiceProducer) FriendsService { - return &friendsServiceImpl{ - charRepo: charRepo, - onlineCache: onlineCache, - eventsProducer: eventsProducer, +func NewFriendsService(charRepo repo.Characters, accountRepo repo.Accounts, onlineCache OnlinePlayersCache, eventsProducer events.FriendsServiceProducer, opts ...FriendsServiceOption) FriendsService { + service := &friendsServiceImpl{ + charRepo: charRepo, + accountRepo: accountRepo, + onlineCache: onlineCache, + eventsProducer: eventsProducer, + realIDCrossFaction: true, } + for _, opt := range opts { + opt(service) + } + return service } func (f *friendsServiceImpl) GetFriendsList(ctx context.Context, realmID uint32, playerGUID uint64) (*FriendsList, error) { @@ -111,16 +142,18 @@ func (f *friendsServiceImpl) GetFriendsList(ctx context.Context, realmID uint32, Friends: make([]*FriendInfo, 0), Ignored: make([]uint64, 0), } + seenFriendGUIDs := make(map[uint64]struct{}) for _, entry := range entries { if entry.Flags == repo.SocialFlagFriend { friend := &FriendInfo{ - GUID: entry.FriendGUID, - Note: entry.Note, + RealmID: realmID, + GUID: entry.FriendGUID, + Note: entry.Note, } // Check if friend is online - if onlineInfo, ok := f.onlineCache.GetOnlineInfo(entry.FriendGUID); ok { + if onlineInfo, ok := f.onlineCache.GetOnlineInfo(realmID, entry.FriendGUID); ok { friend.Status = 1 // online friend.Area = onlineInfo.Area friend.Level = onlineInfo.Level @@ -130,17 +163,50 @@ func (f *friendsServiceImpl) GetFriendsList(ctx context.Context, realmID uint32, } result.Friends = append(result.Friends, friend) + seenFriendGUIDs[friend.GUID] = struct{}{} } else if entry.Flags == repo.SocialFlagIgnore { result.Ignored = append(result.Ignored, entry.FriendGUID) } } + if f.accountRepo != nil { + player, err := f.charRepo.CharacterByGUID(ctx, realmID, playerGUID) + if err != nil { + return nil, err + } + if player != nil { + realIDFriends, err := f.accountRepo.AcceptedRealIDFriends(ctx, player.AccountID) + if err != nil { + return nil, err + } + for _, realIDFriend := range realIDFriends { + friend, err := f.realIDFriendInfo(ctx, realmID, player, realIDFriend) + if err != nil { + return nil, err + } + if friend != nil { + if _, ok := seenFriendGUIDs[friend.GUID]; ok { + continue + } + result.Friends = append(result.Friends, friend) + seenFriendGUIDs[friend.GUID] = struct{}{} + } + } + } + } + return result, nil } func (f *friendsServiceImpl) AddFriend(ctx context.Context, realmID uint32, playerGUID, friendGUID uint64, friendName, note string) (*AddFriendResult, error) { + friendRealmID := wowguid.PlayerRealmIDOrDefault(realmID, friendGUID) + friendLowGUID := wowguid.PlayerLowGUID(friendGUID) + if friendRealmID != realmID { + return &AddFriendResult{Result: FriendResultNotFound}, nil + } + // Cannot add self - if playerGUID == friendGUID { + if playerGUID == friendLowGUID { return &AddFriendResult{Result: FriendResultSelf}, nil } @@ -153,7 +219,7 @@ func (f *friendsServiceImpl) AddFriend(ctx context.Context, realmID uint32, play friendCount := 0 for _, entry := range entries { if entry.Flags == repo.SocialFlagFriend { - if entry.FriendGUID == friendGUID { + if entry.FriendGUID == friendLowGUID { return &AddFriendResult{Result: FriendResultAlready}, nil } friendCount++ @@ -166,14 +232,14 @@ func (f *friendsServiceImpl) AddFriend(ctx context.Context, realmID uint32, play } // Add friend - err = f.charRepo.AddFriend(ctx, realmID, playerGUID, friendGUID, note) + err = f.charRepo.AddFriend(ctx, realmID, playerGUID, friendLowGUID, note) if err != nil { return &AddFriendResult{Result: FriendResultDBError}, err } // Get friend's online status - result := &AddFriendResult{} - if onlineInfo, ok := f.onlineCache.GetOnlineInfo(friendGUID); ok { + result := &AddFriendResult{FriendGUID: friendLowGUID, Accepted: true} + if onlineInfo, ok := f.onlineCache.GetOnlineInfo(realmID, friendLowGUID); ok { result.Result = FriendResultAddedOnline result.Status = 1 // online result.Area = onlineInfo.Area @@ -189,7 +255,7 @@ func (f *friendsServiceImpl) AddFriend(ctx context.Context, realmID uint32, play ServiceID: charserver.ServiceID, RealmID: realmID, PlayerGUID: playerGUID, - FriendGUID: friendGUID, + FriendGUID: friendLowGUID, FriendName: friendName, Note: note, }) @@ -197,9 +263,109 @@ func (f *friendsServiceImpl) AddFriend(ctx context.Context, realmID uint32, play return result, nil } -func (f *friendsServiceImpl) RemoveFriend(ctx context.Context, realmID uint32, playerGUID, friendGUID uint64) error { - err := f.charRepo.RemoveFriend(ctx, realmID, playerGUID, friendGUID) +func (f *friendsServiceImpl) AddRealIDFriendByEmail(ctx context.Context, realmID uint32, playerGUID uint64, accountID uint32, email, note string) (*AddFriendResult, error) { + if f.accountRepo == nil { + return &AddFriendResult{Result: FriendResultNotFound}, nil + } + + if !authidentity.IsValidEmail(email) { + return &AddFriendResult{Result: FriendResultNotFound}, nil + } + email = authidentity.NormalizeLoginIdentity(email) + + player, err := f.charRepo.CharacterByGUID(ctx, realmID, playerGUID) + if err != nil { + return &AddFriendResult{Result: FriendResultDBError}, err + } + if player == nil { + return &AddFriendResult{Result: FriendResultNotFound}, nil + } + accountID = player.AccountID + + target, err := f.accountRepo.AccountByEmail(ctx, email) + if err != nil { + return &AddFriendResult{Result: FriendResultDBError}, err + } + if target == nil { + return &AddFriendResult{Result: FriendResultNotFound}, nil + } + if target.ID == accountID { + return &AddFriendResult{Result: FriendResultSelf}, nil + } + + if full, err := f.realIDFriendListFull(ctx, realmID, playerGUID, accountID); err != nil { + return &AddFriendResult{Result: FriendResultDBError}, err + } else if full { + return &AddFriendResult{Result: FriendResultListFull}, nil + } + + relation, err := f.accountRepo.RequestRealIDFriend(ctx, accountID, target.ID, note) + if err != nil { + return &AddFriendResult{Result: FriendResultDBError}, err + } + + result, err := f.realIDAddResult(ctx, realmID, player, relation) if err != nil { + return &AddFriendResult{Result: FriendResultDBError}, err + } + return result, nil +} + +func (f *friendsServiceImpl) AcceptRealIDFriend(ctx context.Context, realmID uint32, playerGUID uint64, accountID uint32, requesterAccountID uint32, note string) (*AddFriendResult, error) { + if f.accountRepo == nil { + return &AddFriendResult{Result: FriendResultNotFound}, nil + } + + player, err := f.charRepo.CharacterByGUID(ctx, realmID, playerGUID) + if err != nil { + return &AddFriendResult{Result: FriendResultDBError}, err + } + if player == nil { + return &AddFriendResult{Result: FriendResultNotFound}, nil + } + accountID = player.AccountID + + relation, err := f.accountRepo.AcceptRealIDFriend(ctx, accountID, requesterAccountID, note) + if err != nil { + return &AddFriendResult{Result: FriendResultDBError}, err + } + if relation == nil || relation.Status != repo.RealIDFriendStatusAccepted { + return &AddFriendResult{Result: FriendResultNotFound}, nil + } + + return f.realIDAddResult(ctx, realmID, player, relation) +} + +func (f *friendsServiceImpl) AreRealIDFriends(ctx context.Context, accountID uint32, friendAccountID uint32) (bool, error) { + if f.accountRepo == nil || accountID == 0 || friendAccountID == 0 || accountID == friendAccountID { + return false, nil + } + + relations, err := f.accountRepo.AcceptedRealIDFriends(ctx, accountID) + if err != nil { + return false, err + } + + for _, relation := range relations { + if relation.FriendAccountID == friendAccountID && relation.Status == repo.RealIDFriendStatusAccepted { + return true, nil + } + } + + return false, nil +} + +func (f *friendsServiceImpl) RemoveFriend(ctx context.Context, realmID uint32, playerGUID, friendGUID uint64) error { + friendRealmID := wowguid.PlayerRealmIDOrDefault(realmID, friendGUID) + friendLowGUID := wowguid.PlayerLowGUID(friendGUID) + + if friendRealmID == realmID { + if err := f.charRepo.RemoveFriend(ctx, realmID, playerGUID, friendLowGUID); err != nil { + return err + } + } + + if err := f.removeRealIDFriendForCharacterGUID(ctx, realmID, playerGUID, friendRealmID, friendLowGUID); err != nil { return err } @@ -208,15 +374,23 @@ func (f *friendsServiceImpl) RemoveFriend(ctx context.Context, realmID uint32, p ServiceID: charserver.ServiceID, RealmID: realmID, PlayerGUID: playerGUID, - FriendGUID: friendGUID, + FriendGUID: wowguid.PlayerGUIDForRealm(realmID, friendRealmID, friendLowGUID), }) return nil } func (f *friendsServiceImpl) SetFriendNote(ctx context.Context, realmID uint32, playerGUID, friendGUID uint64, note string) error { - err := f.charRepo.UpdateFriendNote(ctx, realmID, playerGUID, friendGUID, note) - if err != nil { + friendRealmID := wowguid.PlayerRealmIDOrDefault(realmID, friendGUID) + friendLowGUID := wowguid.PlayerLowGUID(friendGUID) + + if friendRealmID == realmID { + if err := f.charRepo.UpdateFriendNote(ctx, realmID, playerGUID, friendLowGUID, note); err != nil { + return err + } + } + + if err := f.updateRealIDFriendNoteForCharacterGUID(ctx, realmID, playerGUID, friendRealmID, friendLowGUID, note); err != nil { return err } @@ -225,7 +399,7 @@ func (f *friendsServiceImpl) SetFriendNote(ctx context.Context, realmID uint32, ServiceID: charserver.ServiceID, RealmID: realmID, PlayerGUID: playerGUID, - FriendGUID: friendGUID, + FriendGUID: wowguid.PlayerGUIDForRealm(realmID, friendRealmID, friendLowGUID), Note: note, }) @@ -273,25 +447,272 @@ func (f *friendsServiceImpl) RemoveIgnore(ctx context.Context, realmID uint32, p } func (f *friendsServiceImpl) NotifyStatusChange(ctx context.Context, realmID uint32, playerGUID uint64, status uint8, area, level, classID uint32) error { + playerGUID = wowguid.PlayerLowGUID(playerGUID) + // Get players who have this player as friend notifyPlayers, err := f.charRepo.GetPlayersWhoHaveAsFriend(ctx, realmID, playerGUID) if err != nil { return err } - if len(notifyPlayers) == 0 { - return nil + if len(notifyPlayers) > 0 { + if err := f.eventsProducer.StatusChange(&events.FriendEventStatusChangePayload{ + ServiceID: charserver.ServiceID, + RealmID: realmID, + PlayerGUID: playerGUID, + Status: status, + Area: area, + Level: level, + ClassID: classID, + NotifyPlayers: notifyPlayers, + }); err != nil { + return err + } } - // Publish status change event - return f.eventsProducer.StatusChange(&events.FriendEventStatusChangePayload{ - ServiceID: charserver.ServiceID, - RealmID: realmID, - PlayerGUID: playerGUID, - Status: status, - Area: area, - Level: level, - ClassID: classID, - NotifyPlayers: notifyPlayers, + return f.notifyRealIDFriends(ctx, realmID, playerGUID, status, area, level, classID) +} + +func (f *friendsServiceImpl) realIDFriendListFull(ctx context.Context, realmID uint32, playerGUID uint64, accountID uint32) (bool, error) { + entries, err := f.charRepo.GetFriendsForPlayer(ctx, realmID, playerGUID) + if err != nil { + return false, err + } + friendCount := 0 + for _, entry := range entries { + if entry.Flags == repo.SocialFlagFriend { + friendCount++ + } + } + + if f.accountRepo != nil { + realIDFriends, err := f.accountRepo.AcceptedRealIDFriends(ctx, accountID) + if err != nil { + return false, err + } + friendCount += len(realIDFriends) + } + + return friendCount >= MaxFriendsLimit, nil +} + +func (f *friendsServiceImpl) realIDAddResult(ctx context.Context, realmID uint32, player *repo.Character, relation *repo.RealIDFriendRelation) (*AddFriendResult, error) { + if relation == nil { + return &AddFriendResult{Result: FriendResultNotFound}, nil + } + + friend, err := f.realIDFriendInfo(ctx, realmID, player, relation) + if err != nil { + return nil, err + } + if friend == nil { + return &AddFriendResult{Result: FriendResultNotFound}, nil + } + + result := &AddFriendResult{ + FriendGUID: friend.GUID, + Status: friend.Status, + Area: friend.Area, + Level: friend.Level, + ClassID: friend.ClassID, + Pending: relation.Status == repo.RealIDFriendStatusPending, + Accepted: relation.Status == repo.RealIDFriendStatusAccepted, + } + if friend.Status > 0 && result.Accepted { + result.Result = FriendResultAddedOnline + } else { + result.Result = FriendResultAddedOffline + } + if result.Accepted { + _ = f.eventsProducer.FriendAdded(&events.FriendEventAddedPayload{ + ServiceID: charserver.ServiceID, + RealmID: realmID, + PlayerGUID: player.CharGUID, + FriendGUID: friend.GUID, + FriendName: "", + Note: friend.Note, + }) + _ = f.notifyRealIDAccountAboutPlayer(ctx, player, relation.FriendAccountID) + } + return result, nil +} + +func (f *friendsServiceImpl) realIDFriendInfo(ctx context.Context, viewerRealmID uint32, viewer *repo.Character, relation *repo.RealIDFriendRelation) (*FriendInfo, error) { + if onlineInfo, ok := f.representativeOnlineInfoForAccount(viewerRealmID, relation.FriendAccountID); ok { + if !f.realIDCrossFaction && !sameFaction(uint32(viewer.CharRace), onlineInfo.Race) { + return nil, nil + } + friendRealmID := onlineInfo.RealmID + return &FriendInfo{ + RealmID: friendRealmID, + GUID: wowguid.PlayerGUIDForRealm(viewerRealmID, friendRealmID, onlineInfo.GUID), + Note: relation.Note, + Status: 1, + Area: onlineInfo.Area, + Level: onlineInfo.Level, + ClassID: onlineInfo.Class, + }, nil + } + + displayChar, err := f.charRepo.DisplayCharacterByAccount(ctx, relation.FriendAccountID) + if err != nil { + return nil, err + } + if displayChar == nil { + return nil, nil + } + if !f.realIDCrossFaction && !sameFaction(uint32(viewer.CharRace), uint32(displayChar.CharRace)) { + return nil, nil + } + + return &FriendInfo{ + RealmID: displayChar.RealmID, + GUID: wowguid.PlayerGUIDForRealm(viewerRealmID, displayChar.RealmID, displayChar.CharGUID), + Note: relation.Note, + Status: 0, + }, nil +} + +func (f *friendsServiceImpl) representativeOnlineInfoForAccount(viewerRealmID uint32, accountID uint32) (OnlinePlayerInfo, bool) { + infos := f.onlineCache.GetOnlineInfosForAccount(accountID) + if len(infos) == 0 { + return OnlinePlayerInfo{}, false + } + + sort.Slice(infos, func(i, j int) bool { + leftSameRealm := infos[i].RealmID == viewerRealmID + rightSameRealm := infos[j].RealmID == viewerRealmID + if leftSameRealm != rightSameRealm { + return leftSameRealm + } + if infos[i].RealmID != infos[j].RealmID { + return infos[i].RealmID < infos[j].RealmID + } + return infos[i].GUID < infos[j].GUID }) + + return infos[0], true +} + +func (f *friendsServiceImpl) removeRealIDFriendForCharacterGUID(ctx context.Context, playerRealmID uint32, playerGUID uint64, friendRealmID uint32, friendGUID uint64) error { + if f.accountRepo == nil { + return nil + } + + player, err := f.charRepo.CharacterByGUID(ctx, playerRealmID, playerGUID) + if err != nil || player == nil { + return err + } + friend, err := f.charRepo.CharacterByGUID(ctx, friendRealmID, friendGUID) + if err != nil || friend == nil { + return err + } + + return f.accountRepo.RemoveRealIDFriend(ctx, player.AccountID, friend.AccountID) +} + +func (f *friendsServiceImpl) updateRealIDFriendNoteForCharacterGUID(ctx context.Context, playerRealmID uint32, playerGUID uint64, friendRealmID uint32, friendGUID uint64, note string) error { + if f.accountRepo == nil { + return nil + } + + player, err := f.charRepo.CharacterByGUID(ctx, playerRealmID, playerGUID) + if err != nil || player == nil { + return err + } + friend, err := f.charRepo.CharacterByGUID(ctx, friendRealmID, friendGUID) + if err != nil || friend == nil { + return err + } + + return f.accountRepo.UpdateRealIDFriendNote(ctx, player.AccountID, friend.AccountID, note) +} + +func (f *friendsServiceImpl) notifyRealIDFriends(ctx context.Context, realmID uint32, playerGUID uint64, status uint8, area, level, classID uint32) error { + if f.accountRepo == nil { + return nil + } + + player, err := f.charRepo.CharacterByGUID(ctx, realmID, playerGUID) + if err != nil || player == nil { + return err + } + + realIDFriends, err := f.accountRepo.AcceptedRealIDFriends(ctx, player.AccountID) + if err != nil { + return err + } + + for _, relation := range realIDFriends { + listeners := f.onlineCache.GetOnlineInfosForAccount(relation.FriendAccountID) + for _, listener := range listeners { + if !f.realIDCrossFaction && !sameFaction(uint32(player.CharRace), listener.Race) { + continue + } + if err := f.eventsProducer.StatusChange(&events.FriendEventStatusChangePayload{ + ServiceID: charserver.ServiceID, + RealmID: listener.RealmID, + PlayerGUID: wowguid.PlayerGUIDForRealm(listener.RealmID, realmID, playerGUID), + Status: status, + Area: area, + Level: level, + ClassID: classID, + NotifyPlayers: []uint64{listener.GUID}, + }); err != nil { + return err + } + } + } + + return nil +} + +func (f *friendsServiceImpl) notifyRealIDAccountAboutPlayer(ctx context.Context, player *repo.Character, accountID uint32) error { + if player == nil { + return nil + } + + status := uint8(0) + area := player.CharZone + level := uint32(player.CharLevel) + classID := uint32(player.CharClass) + if onlineInfo, ok := f.onlineCache.GetOnlineInfo(player.RealmID, player.CharGUID); ok { + status = onlineInfo.Status + area = onlineInfo.Area + level = onlineInfo.Level + classID = onlineInfo.Class + } + + for _, listener := range f.onlineCache.GetOnlineInfosForAccount(accountID) { + if !f.realIDCrossFaction && !sameFaction(uint32(player.CharRace), listener.Race) { + continue + } + if err := f.eventsProducer.StatusChange(&events.FriendEventStatusChangePayload{ + ServiceID: charserver.ServiceID, + RealmID: listener.RealmID, + PlayerGUID: wowguid.PlayerGUIDForRealm(listener.RealmID, player.RealmID, player.CharGUID), + Status: status, + Area: area, + Level: level, + ClassID: classID, + NotifyPlayers: []uint64{listener.GUID}, + }); err != nil { + return err + } + } + + return nil +} + +func sameFaction(leftRace uint32, rightRace uint32) bool { + return isAllianceRace(leftRace) == isAllianceRace(rightRace) +} + +func isAllianceRace(race uint32) bool { + switch race { + case 1, 3, 4, 7, 11: + return true + default: + return false + } } diff --git a/apps/charserver/service/serversregistry-listener.go b/apps/charserver/service/serversregistry-listener.go index 8d98bb7..2aa4cd1 100644 --- a/apps/charserver/service/serversregistry-listener.go +++ b/apps/charserver/service/serversregistry-listener.go @@ -35,7 +35,7 @@ func (c *ServersRegistryListener) Listen() error { return } - userIDs, err := c.charRepo.RemoveAllWithGatewayID(context.TODO(), payload.RealmID, payload.ID) + userIDs, err := c.charRepo.RemoveAllWithGatewayID(context.TODO(), payload.RealmID, payload.ID, payload.EventTimeUnixNano) if err != nil { log.Error().Err(err).Msg("can't delete characters in ServerRegistryEventGWRemovedUnhealthy event") return @@ -43,9 +43,10 @@ func (c *ServersRegistryListener) Listen() error { if len(userIDs) > 0 { err = c.producer.CharsDisconnectedUnhealthyLB(&events.CharEventCharsDisconnectedUnhealthyGWPayload{ - RealmID: payload.RealmID, - GatewayID: payload.ID, - CharactersGUID: userIDs, + RealmID: payload.RealmID, + GatewayID: payload.ID, + EventTimeUnixNano: payload.EventTimeUnixNano, + CharactersGUID: userIDs, }) if err != nil { @@ -67,7 +68,7 @@ func (c *ServersRegistryListener) Listen() error { return } - _, err = c.charRepo.RemoveAllWithGatewayID(context.TODO(), payload.RealmID, payload.GatewayID) + _, err = c.charRepo.RemoveAllWithGatewayID(context.TODO(), payload.RealmID, payload.GatewayID, payload.EventTimeUnixNano) if err != nil { log.Error().Err(err).Msg("can't delete characters in CharEventCharsDisconnectedUnhealthyGW event") return diff --git a/apps/chatserver/cmd/chatserver/main.go b/apps/chatserver/cmd/chatserver/main.go index c8011f7..1a227f4 100644 --- a/apps/chatserver/cmd/chatserver/main.go +++ b/apps/chatserver/cmd/chatserver/main.go @@ -166,7 +166,7 @@ func main() { grpcServer := grpc.NewServer() pb.RegisterChatServiceServer( grpcServer, - server.NewChatService(charRepo, channelMgr, msgProducer, serviceID), + server.NewChatService(charRepo, channelMgr, msgProducer, serviceID, charClient), ) // graceful shutdown handling diff --git a/apps/chatserver/repo/channels.go b/apps/chatserver/repo/channels.go index 60830f8..029d1f6 100644 --- a/apps/chatserver/repo/channels.go +++ b/apps/chatserver/repo/channels.go @@ -7,13 +7,13 @@ import ( // Channel represents a WoW channel type Channel struct { - ChannelID uint32 - Name string - Team uint32 // 0=Alliance, 1=Horde, 2=Neutral - Announce bool - Ownership bool - Password string - LastUsed time.Time + ChannelID uint32 + Name string + Team uint32 // 0=Alliance, 1=Horde, 2=Neutral + Announce bool + Ownership bool + Password string + LastUsed time.Time } // ChannelRights represents channel moderation settings diff --git a/apps/chatserver/repo/characters.go b/apps/chatserver/repo/characters.go index 365644a..3a57787 100644 --- a/apps/chatserver/repo/characters.go +++ b/apps/chatserver/repo/characters.go @@ -10,18 +10,25 @@ type Character struct { RealmID uint32 GatewayID string GUID uint64 + AccountID uint32 Name string Race uint8 + Class uint8 + Gender uint8 MsgSender sender.MsgSender } type CharactersRepo interface { AddCharacter(ctx context.Context, character *Character) error + AddCharacterFromGatewayEvent(ctx context.Context, character *Character, eventTimeUnixNano uint64) (bool, error) RemoveCharacter(ctx context.Context, realmID uint32, characterGUID uint64) error + RemoveCharacterFromGatewayEvent(ctx context.Context, realmID uint32, characterGUID uint64, eventTimeUnixNano uint64) (bool, error) + RemoveCharactersWithGatewayID(ctx context.Context, realmID uint32, gatewayID string, eventTimeUnixNano uint64) error RemoveCharactersWithRealm(ctx context.Context, realmID uint32) error CharacterByRealmAndGUID(ctx context.Context, realmID uint32, characterGUID uint64) (*Character, error) CharacterByRealmAndName(ctx context.Context, realmID uint32, name string) (*Character, error) + CharactersByName(ctx context.Context, name string) ([]*Character, error) } diff --git a/apps/chatserver/repo/characters_inmem.go b/apps/chatserver/repo/characters_inmem.go index 460d3f9..89ec55f 100644 --- a/apps/chatserver/repo/characters_inmem.go +++ b/apps/chatserver/repo/characters_inmem.go @@ -5,11 +5,13 @@ import ( "fmt" "strings" "sync" + "time" ) type charactersInMemRepo struct { - charsByGUID map[string]*Character - charsByName map[string]*Character + charsByGUID map[string]*Character + charsByName map[string]*Character + lifecycleEventTimes map[string]uint64 guidMu sync.RWMutex nameMu sync.RWMutex @@ -17,34 +19,89 @@ type charactersInMemRepo struct { func NewCharactersInMemRepo() CharactersRepo { return &charactersInMemRepo{ - charsByGUID: map[string]*Character{}, - charsByName: map[string]*Character{}, + charsByGUID: map[string]*Character{}, + charsByName: map[string]*Character{}, + lifecycleEventTimes: map[string]uint64{}, } } func (c *charactersInMemRepo) AddCharacter(ctx context.Context, character *Character) error { - c.guidMu.Lock() - c.charsByGUID[c.mapKeyForRealmAndGuid(character.RealmID, character.GUID)] = character - c.guidMu.Unlock() + _, err := c.addCharacter(character, 0) + return err +} + +func (c *charactersInMemRepo) AddCharacterFromGatewayEvent(ctx context.Context, character *Character, eventTimeUnixNano uint64) (bool, error) { + return c.addCharacter(character, eventTimeUnixNano) +} + +func (c *charactersInMemRepo) RemoveCharacter(ctx context.Context, realmID uint32, characterGUID uint64) error { + _, err := c.removeCharacter(realmID, characterGUID, 0) + return err +} + +func (c *charactersInMemRepo) RemoveCharacterFromGatewayEvent(ctx context.Context, realmID uint32, characterGUID uint64, eventTimeUnixNano uint64) (bool, error) { + return c.removeCharacter(realmID, characterGUID, eventTimeUnixNano) +} +func (c *charactersInMemRepo) addCharacter(character *Character, eventTimeUnixNano uint64) (bool, error) { + if character == nil { + return false, nil + } + + guidKey := c.mapKeyForRealmAndGuid(character.RealmID, character.GUID) + c.guidMu.Lock() c.nameMu.Lock() - c.charsByName[c.mapKeyForRealmAndName(character.RealmID, character.Name)] = character - c.nameMu.Unlock() + defer c.nameMu.Unlock() + defer c.guidMu.Unlock() - return nil + if !c.shouldApplyLifecycleEvent(guidKey, eventTimeUnixNano) { + return false, nil + } + if previous := c.charsByGUID[guidKey]; previous != nil { + delete(c.charsByName, c.mapKeyForRealmAndName(previous.RealmID, previous.Name)) + } + c.charsByGUID[guidKey] = character + c.charsByName[c.mapKeyForRealmAndName(character.RealmID, character.Name)] = character + c.rememberLifecycleEventTime(guidKey, eventTimeUnixNano) + return true, nil } -func (c *charactersInMemRepo) RemoveCharacter(ctx context.Context, realmID uint32, characterGUID uint64) error { +func (c *charactersInMemRepo) removeCharacter(realmID uint32, characterGUID uint64, eventTimeUnixNano uint64) (bool, error) { guidKey := c.mapKeyForRealmAndGuid(realmID, characterGUID) c.guidMu.Lock() + c.nameMu.Lock() + defer c.nameMu.Unlock() + defer c.guidMu.Unlock() + + if !c.shouldApplyLifecycleEvent(guidKey, eventTimeUnixNano) { + return false, nil + } char := c.charsByGUID[guidKey] delete(c.charsByGUID, guidKey) - c.guidMu.Unlock() if char != nil { - c.nameMu.Lock() delete(c.charsByName, c.mapKeyForRealmAndName(realmID, char.Name)) - c.nameMu.Unlock() + } + c.rememberLifecycleEventTime(guidKey, eventTimeUnixNano) + + return true, nil +} + +func (c *charactersInMemRepo) RemoveCharactersWithGatewayID(ctx context.Context, realmID uint32, gatewayID string, eventTimeUnixNano uint64) error { + c.guidMu.Lock() + c.nameMu.Lock() + defer c.nameMu.Unlock() + defer c.guidMu.Unlock() + if eventTimeUnixNano == 0 { + eventTimeUnixNano = uint64(time.Now().UnixNano()) + } + + for guidKey, char := range c.charsByGUID { + if char.RealmID == realmID && char.GatewayID == gatewayID && c.lifecycleEventTimes[guidKey] <= eventTimeUnixNano { + delete(c.charsByGUID, guidKey) + delete(c.charsByName, c.mapKeyForRealmAndName(realmID, char.Name)) + c.rememberLifecycleEventTime(guidKey, eventTimeUnixNano) + } } return nil @@ -62,6 +119,21 @@ func (c *charactersInMemRepo) CharacterByRealmAndName(ctx context.Context, realm return c.charsByName[c.mapKeyForRealmAndName(realmID, name)], nil } +func (c *charactersInMemRepo) CharactersByName(ctx context.Context, name string) ([]*Character, error) { + c.nameMu.RLock() + defer c.nameMu.RUnlock() + + matches := make([]*Character, 0) + nameSuffix := ":" + strings.ToLower(name) + for key, char := range c.charsByName { + if strings.HasSuffix(key, nameSuffix) { + matches = append(matches, char) + } + } + + return matches, nil +} + func (c *charactersInMemRepo) RemoveCharactersWithRealm(ctx context.Context, realmID uint32) error { // TODO: need to completely rewrite this @@ -70,7 +142,10 @@ func (c *charactersInMemRepo) RemoveCharactersWithRealm(ctx context.Context, rea prefix := fmt.Sprintf("%d:", realmID) + c.guidMu.Lock() c.nameMu.Lock() + defer c.nameMu.Unlock() + defer c.guidMu.Unlock() for k, char := range c.charsByName { if strings.HasPrefix(k, prefix) { @@ -87,8 +162,6 @@ func (c *charactersInMemRepo) RemoveCharactersWithRealm(ctx context.Context, rea delete(c.charsByGUID, k) } - c.nameMu.Unlock() - return nil } @@ -99,3 +172,17 @@ func (c *charactersInMemRepo) mapKeyForRealmAndGuid(realm uint32, guid uint64) s func (c *charactersInMemRepo) mapKeyForRealmAndName(realm uint32, name string) string { return fmt.Sprintf("%d:%s", realm, strings.ToLower(name)) } + +func (c *charactersInMemRepo) shouldApplyLifecycleEvent(guidKey string, eventTimeUnixNano uint64) bool { + return eventTimeUnixNano == 0 || c.lifecycleEventTimes[guidKey] <= eventTimeUnixNano +} + +func (c *charactersInMemRepo) rememberLifecycleEventTime(guidKey string, eventTimeUnixNano uint64) { + if eventTimeUnixNano == 0 { + return + } + if c.lifecycleEventTimes == nil { + c.lifecycleEventTimes = map[string]uint64{} + } + c.lifecycleEventTimes[guidKey] = eventTimeUnixNano +} diff --git a/apps/chatserver/sender/sender.go b/apps/chatserver/sender/sender.go index 76b4fa5..9721753 100644 --- a/apps/chatserver/sender/sender.go +++ b/apps/chatserver/sender/sender.go @@ -11,7 +11,10 @@ type Character struct { RealmID uint32 GUID uint64 Race uint8 + Class uint8 + Gender uint8 Name string + ChatTag uint8 } type MsgSender interface { @@ -37,13 +40,18 @@ func NewMsgSenderNatsJSON(conn *nats.Conn, gatewayID string) MsgSender { func (m msgSenderNatsJSON) SendWhisper(sender *Character, receiver *Character, language uint32, msg string) error { return m.producer.IncomingWhisper(&events.ChatEventIncomingWhisperPayload{ - SenderGUID: sender.GUID, - SenderName: sender.Name, - SenderRace: sender.Race, - ReceiverGUID: receiver.GUID, - ReceiverName: receiver.Name, - Language: language, - Msg: msg, + SenderRealmID: sender.RealmID, + SenderGUID: sender.GUID, + SenderName: sender.Name, + SenderRace: sender.Race, + SenderClass: sender.Class, + SenderGender: sender.Gender, + SenderChatTag: sender.ChatTag, + ReceiverRealmID: receiver.RealmID, + ReceiverGUID: receiver.GUID, + ReceiverName: receiver.Name, + Language: language, + Msg: msg, }) } diff --git a/apps/chatserver/server/chat-channels.go b/apps/chatserver/server/chat-channels.go index 8dc24de..0b87d61 100644 --- a/apps/chatserver/server/chat-channels.go +++ b/apps/chatserver/server/chat-channels.go @@ -14,8 +14,13 @@ import ( ) func (s *ChatService) JoinChannel(ctx context.Context, req *pb.JoinChannelRequest) (*pb.JoinChannelResponse, error) { + channelFlags := uint8(req.ChannelFlags) + if req.ChannelID == 0 && channelFlags == 0 { + channelFlags = service.ChannelFlagCustom + } + // Get or create the channel with worldserver's flags if provided - channel, err := s.channelMgr.GetOrCreateChannel(ctx, req.RealmID, req.ChannelName, req.ChannelID, req.TeamID, req.Password, uint8(req.ChannelFlags)) + channel, err := s.channelMgr.GetOrCreateChannel(ctx, req.RealmID, req.ChannelName, req.ChannelID, req.TeamID, req.Password, channelFlags) if err != nil { return nil, fmt.Errorf("failed to get/create channel: %w", err) } @@ -62,7 +67,7 @@ func (s *ChatService) LeaveChannel(ctx context.Context, req *pb.LeaveChannelRequ Str("channelName", req.ChannelName). Msg("LeaveChannel request") - playerName, isCustom, newOwnerGUID, err := s.channelMgr.LeaveChannelByGUID(ctx, req.RealmID, req.ChannelName, req.TeamID, req.PlayerGUID) + playerName, isCustom, newOwnerGUID, oldFlags, newFlags, err := s.channelMgr.LeaveChannelByGUID(ctx, req.RealmID, req.ChannelName, req.TeamID, req.PlayerGUID) if err != nil { if errors.Is(err, service.ErrNotMember) || errors.Is(err, service.ErrChannelNotFound) { return &pb.LeaveChannelResponse{ @@ -77,13 +82,13 @@ func (s *ChatService) LeaveChannel(ctx context.Context, req *pb.LeaveChannelRequ if isCustom { channel := s.channelMgr.GetChannel(req.RealmID, req.ChannelName, req.TeamID) if channel != nil { - if err := s.broadcastChannelLeft(req.RealmID, channel, req.PlayerGUID, playerName); err != nil { + if err := s.broadcastChannelLeft(req.RealmID, channel, req.PlayerGUID, playerName, false); err != nil { log.Error().Err(err).Msg("Failed to broadcast channel leave") } // If ownership was transferred, broadcast the ownership change if newOwnerGUID != 0 { - if err := s.broadcastModeChange(req.RealmID, channel, newOwnerGUID); err != nil { + if err := s.broadcastModeChange(req.RealmID, channel, newOwnerGUID, oldFlags, newFlags); err != nil { log.Error().Err(err).Msg("Failed to broadcast owner mode change after leave") } if err := s.broadcastOwnerChanged(req.RealmID, channel, newOwnerGUID); err != nil { @@ -118,7 +123,7 @@ func (s *ChatService) SendChannelMessage(ctx context.Context, req *pb.SendChanne } // Broadcast the message - if err := s.broadcastChannelMessage(req.RealmID, channel, req.SenderGUID, req.SenderName, req.Language, req.Message); err != nil { + if err := s.broadcastChannelMessage(req.RealmID, channel, req.SenderGUID, req.SenderName, req.Language, req.Message, uint8(req.SenderChatTag)); err != nil { log.Error().Err(err).Msg("Failed to broadcast channel message") return nil, err } @@ -170,7 +175,7 @@ func (s *ChatService) GetChannelList(ctx context.Context, req *pb.GetChannelList } func (s *ChatService) KickFromChannel(ctx context.Context, req *pb.KickFromChannelRequest) (*pb.KickFromChannelResponse, error) { - channel, targetGUID, err := s.channelMgr.KickPlayer(ctx, req.RealmID, req.ChannelName, req.TeamID, req.KickerGUID, req.TargetName) + channel, targetGUID, newOwnerGUID, oldFlags, newFlags, err := s.channelMgr.KickPlayer(ctx, req.RealmID, req.ChannelName, req.TeamID, req.KickerGUID, req.TargetName) if err != nil { if errors.Is(err, service.ErrNotMember) || errors.Is(err, service.ErrChannelNotFound) { return &pb.KickFromChannelResponse{ @@ -193,9 +198,21 @@ func (s *ChatService) KickFromChannel(ctx context.Context, req *pb.KickFromChann return nil, err } - // Broadcast kick notification - if err := s.broadcastChannelLeft(req.RealmID, channel, targetGUID, req.TargetName); err != nil { - log.Error().Err(err).Msg("Failed to broadcast channel kick") + if err := s.broadcastPlayerKicked(req.RealmID, channel, targetGUID, req.KickerGUID); err != nil { + log.Error().Err(err).Msg("Failed to broadcast channel kick notification") + } + + if err := s.broadcastChannelLeft(req.RealmID, channel, targetGUID, req.TargetName, true); err != nil { + log.Error().Err(err).Msg("Failed to broadcast silent channel kick") + } + + if newOwnerGUID != 0 { + if err := s.broadcastModeChange(req.RealmID, channel, newOwnerGUID, oldFlags, newFlags); err != nil { + log.Error().Err(err).Msg("Failed to broadcast owner mode change after kick") + } + if err := s.broadcastOwnerChanged(req.RealmID, channel, newOwnerGUID); err != nil { + log.Error().Err(err).Msg("Failed to broadcast owner changed after kick") + } } return &pb.KickFromChannelResponse{ @@ -205,7 +222,7 @@ func (s *ChatService) KickFromChannel(ctx context.Context, req *pb.KickFromChann } func (s *ChatService) BanFromChannel(ctx context.Context, req *pb.BanFromChannelRequest) (*pb.BanFromChannelResponse, error) { - _, _, err := s.channelMgr.BanPlayerByName(ctx, req.RealmID, req.ChannelName, req.TeamID, req.BannerGUID, req.TargetName) + channel, targetGUID, newOwnerGUID, oldFlags, newFlags, err := s.channelMgr.BanPlayerByName(ctx, req.RealmID, req.ChannelName, req.TeamID, req.BannerGUID, req.TargetName) if err != nil { if errors.Is(err, service.ErrNotMember) || errors.Is(err, service.ErrChannelNotFound) { return &pb.BanFromChannelResponse{ @@ -229,6 +246,23 @@ func (s *ChatService) BanFromChannel(ctx context.Context, req *pb.BanFromChannel return nil, err } + if err := s.broadcastPlayerBanned(req.RealmID, channel, targetGUID, req.BannerGUID); err != nil { + log.Error().Err(err).Msg("Failed to broadcast channel ban notification") + } + + if err := s.broadcastChannelLeft(req.RealmID, channel, targetGUID, req.TargetName, true); err != nil { + log.Error().Err(err).Msg("Failed to broadcast silent channel ban") + } + + if newOwnerGUID != 0 { + if err := s.broadcastModeChange(req.RealmID, channel, newOwnerGUID, oldFlags, newFlags); err != nil { + log.Error().Err(err).Msg("Failed to broadcast owner mode change after ban") + } + if err := s.broadcastOwnerChanged(req.RealmID, channel, newOwnerGUID); err != nil { + log.Error().Err(err).Msg("Failed to broadcast owner changed after ban") + } + } + return &pb.BanFromChannelResponse{ Api: chatserver.Ver, Status: pb.BanFromChannelResponse_Ok, @@ -236,7 +270,7 @@ func (s *ChatService) BanFromChannel(ctx context.Context, req *pb.BanFromChannel } func (s *ChatService) UnbanFromChannel(ctx context.Context, req *pb.UnbanFromChannelRequest) (*pb.UnbanFromChannelResponse, error) { - _, err := s.channelMgr.UnbanPlayerByName(ctx, req.RealmID, req.ChannelName, req.TeamID, req.UnbannerGUID, req.TargetName) + targetGUID, err := s.channelMgr.UnbanPlayerByName(ctx, req.RealmID, req.ChannelName, req.TeamID, req.UnbannerGUID, req.TargetName) if err != nil { if errors.Is(err, service.ErrChannelNotFound) { return &pb.UnbanFromChannelResponse{ @@ -260,6 +294,13 @@ func (s *ChatService) UnbanFromChannel(ctx context.Context, req *pb.UnbanFromCha return nil, err } + channel := s.channelMgr.GetChannel(req.RealmID, req.ChannelName, req.TeamID) + if channel != nil { + if err := s.broadcastPlayerUnbanned(req.RealmID, channel, targetGUID, req.UnbannerGUID); err != nil { + log.Error().Err(err).Msg("Failed to broadcast channel unban notification") + } + } + return &pb.UnbanFromChannelResponse{ Api: chatserver.Ver, Status: pb.UnbanFromChannelResponse_Ok, @@ -267,7 +308,7 @@ func (s *ChatService) UnbanFromChannel(ctx context.Context, req *pb.UnbanFromCha } func (s *ChatService) SetChannelModerator(ctx context.Context, req *pb.SetChannelModeratorRequest) (*pb.SetChannelModeratorResponse, error) { - targetGUID, err := s.channelMgr.SetModeratorByName(ctx, req.RealmID, req.ChannelName, req.TeamID, req.SetterGUID, req.TargetName, true) + targetGUID, oldFlags, newFlags, err := s.channelMgr.SetModeratorByName(ctx, req.RealmID, req.ChannelName, req.TeamID, req.SetterGUID, req.TargetName, true) if err != nil { if errors.Is(err, service.ErrNotMember) || errors.Is(err, service.ErrChannelNotFound) { return &pb.SetChannelModeratorResponse{ @@ -293,7 +334,7 @@ func (s *ChatService) SetChannelModerator(ctx context.Context, req *pb.SetChanne // Broadcast mode change notification to all channel members channel := s.channelMgr.GetChannel(req.RealmID, req.ChannelName, req.TeamID) if channel != nil { - if err := s.broadcastModeChange(req.RealmID, channel, targetGUID); err != nil { + if err := s.broadcastModeChange(req.RealmID, channel, targetGUID, oldFlags, newFlags); err != nil { log.Error().Err(err).Msg("Failed to broadcast moderator change") } } @@ -305,7 +346,7 @@ func (s *ChatService) SetChannelModerator(ctx context.Context, req *pb.SetChanne } func (s *ChatService) UnsetChannelModerator(ctx context.Context, req *pb.UnsetChannelModeratorRequest) (*pb.UnsetChannelModeratorResponse, error) { - targetGUID, err := s.channelMgr.SetModeratorByName(ctx, req.RealmID, req.ChannelName, req.TeamID, req.SetterGUID, req.TargetName, false) + targetGUID, oldFlags, newFlags, err := s.channelMgr.SetModeratorByName(ctx, req.RealmID, req.ChannelName, req.TeamID, req.SetterGUID, req.TargetName, false) if err != nil { if errors.Is(err, service.ErrNotMember) || errors.Is(err, service.ErrChannelNotFound) { return &pb.UnsetChannelModeratorResponse{ @@ -331,7 +372,7 @@ func (s *ChatService) UnsetChannelModerator(ctx context.Context, req *pb.UnsetCh // Broadcast mode change notification to all channel members channel := s.channelMgr.GetChannel(req.RealmID, req.ChannelName, req.TeamID) if channel != nil { - if err := s.broadcastModeChange(req.RealmID, channel, targetGUID); err != nil { + if err := s.broadcastModeChange(req.RealmID, channel, targetGUID, oldFlags, newFlags); err != nil { log.Error().Err(err).Msg("Failed to broadcast moderator change") } } @@ -343,7 +384,7 @@ func (s *ChatService) UnsetChannelModerator(ctx context.Context, req *pb.UnsetCh } func (s *ChatService) SetChannelMute(ctx context.Context, req *pb.SetChannelMuteRequest) (*pb.SetChannelMuteResponse, error) { - targetGUID, err := s.channelMgr.SetMuteByName(ctx, req.RealmID, req.ChannelName, req.TeamID, req.MuterGUID, req.TargetName, true) + targetGUID, oldFlags, newFlags, err := s.channelMgr.SetMuteByName(ctx, req.RealmID, req.ChannelName, req.TeamID, req.MuterGUID, req.TargetName, true) if err != nil { if errors.Is(err, service.ErrNotMember) || errors.Is(err, service.ErrChannelNotFound) { return &pb.SetChannelMuteResponse{ @@ -369,7 +410,7 @@ func (s *ChatService) SetChannelMute(ctx context.Context, req *pb.SetChannelMute // Broadcast mode change notification to all channel members channel := s.channelMgr.GetChannel(req.RealmID, req.ChannelName, req.TeamID) if channel != nil { - if err := s.broadcastModeChange(req.RealmID, channel, targetGUID); err != nil { + if err := s.broadcastModeChange(req.RealmID, channel, targetGUID, oldFlags, newFlags); err != nil { log.Error().Err(err).Msg("Failed to broadcast mute change") } } @@ -381,7 +422,7 @@ func (s *ChatService) SetChannelMute(ctx context.Context, req *pb.SetChannelMute } func (s *ChatService) UnsetChannelMute(ctx context.Context, req *pb.UnsetChannelMuteRequest) (*pb.UnsetChannelMuteResponse, error) { - targetGUID, err := s.channelMgr.SetMuteByName(ctx, req.RealmID, req.ChannelName, req.TeamID, req.UnmuterGUID, req.TargetName, false) + targetGUID, oldFlags, newFlags, err := s.channelMgr.SetMuteByName(ctx, req.RealmID, req.ChannelName, req.TeamID, req.UnmuterGUID, req.TargetName, false) if err != nil { if errors.Is(err, service.ErrNotMember) || errors.Is(err, service.ErrChannelNotFound) { return &pb.UnsetChannelMuteResponse{ @@ -407,7 +448,7 @@ func (s *ChatService) UnsetChannelMute(ctx context.Context, req *pb.UnsetChannel // Broadcast mode change notification to all channel members channel := s.channelMgr.GetChannel(req.RealmID, req.ChannelName, req.TeamID) if channel != nil { - if err := s.broadcastModeChange(req.RealmID, channel, targetGUID); err != nil { + if err := s.broadcastModeChange(req.RealmID, channel, targetGUID, oldFlags, newFlags); err != nil { log.Error().Err(err).Msg("Failed to broadcast unmute change") } } @@ -419,7 +460,7 @@ func (s *ChatService) UnsetChannelMute(ctx context.Context, req *pb.UnsetChannel } func (s *ChatService) SetChannelOwner(ctx context.Context, req *pb.SetChannelOwnerRequest) (*pb.SetChannelOwnerResponse, error) { - targetGUID, err := s.channelMgr.SetOwnerByName(ctx, req.RealmID, req.ChannelName, req.TeamID, req.SetterGUID, req.TargetName) + targetGUID, oldFlags, newFlags, err := s.channelMgr.SetOwnerByName(ctx, req.RealmID, req.ChannelName, req.TeamID, req.SetterGUID, req.TargetName) if err != nil { if errors.Is(err, service.ErrChannelNotFound) { return &pb.SetChannelOwnerResponse{ @@ -447,7 +488,7 @@ func (s *ChatService) SetChannelOwner(ctx context.Context, req *pb.SetChannelOwn channel := s.channelMgr.GetChannel(req.RealmID, req.ChannelName, req.TeamID) if channel != nil { // First send mode change (same as C++ line 893-894) - if err := s.broadcastModeChange(req.RealmID, channel, targetGUID); err != nil { + if err := s.broadcastModeChange(req.RealmID, channel, targetGUID, oldFlags, newFlags); err != nil { log.Error().Err(err).Msg("Failed to broadcast owner mode change") } // Then send owner changed notification with GUID (same as C++ line 899-900) @@ -581,7 +622,8 @@ func (s *ChatService) InviteToChannel(ctx context.Context, req *pb.InviteToChann RealmID: req.RealmID, ChannelName: req.ChannelName, ChannelID: channel.GetChannelID(), - NotifyType: 0x18, // ChatInviteNotice + TeamID: uint32(channel.GetTeamID()), + NotifyType: 0x18, // ChatInviteNotice TargetGUID: req.InviterGUID, // The inviter's GUID (shown in the packet) TargetName: req.TargetName, SecondGUID: req.InviterGUID, @@ -600,15 +642,17 @@ func (s *ChatService) InviteToChannel(ctx context.Context, req *pb.InviteToChann // Broadcast helpers -func (s *ChatService) broadcastChannelMessage(realmID uint32, channel *service.ActiveChannel, senderGUID uint64, senderName string, language uint32, message string) error { +func (s *ChatService) broadcastChannelMessage(realmID uint32, channel *service.ActiveChannel, senderGUID uint64, senderName string, language uint32, message string, senderChatTag uint8) error { payload := &events.ChatEventChannelMessagePayload{ - RealmID: realmID, - ChannelName: channel.GetName(), - ChannelID: channel.GetChannelID(), - SenderGUID: senderGUID, - SenderName: senderName, - Language: language, - Message: message, + RealmID: realmID, + ChannelName: channel.GetName(), + ChannelID: channel.GetChannelID(), + TeamID: uint32(channel.GetTeamID()), + SenderGUID: senderGUID, + SenderName: senderName, + Language: language, + Message: message, + SenderChatTag: senderChatTag, } return s.msgProducer.ProduceChannelMessage(payload) @@ -616,38 +660,50 @@ func (s *ChatService) broadcastChannelMessage(realmID uint32, channel *service.A func (s *ChatService) broadcastChannelJoined(realmID uint32, channel *service.ActiveChannel, playerGUID uint64, playerName string) error { payload := &events.ChatEventChannelJoinedPayload{ - ServiceID: s.serviceID, - RealmID: realmID, - ChannelName: channel.GetName(), - ChannelID: channel.GetChannelID(), - PlayerGUID: playerGUID, - PlayerName: playerName, - PlayerFlags: channel.GetMemberFlags(playerGUID), + ServiceID: s.serviceID, + RealmID: realmID, + ChannelName: channel.GetName(), + ChannelID: channel.GetChannelID(), + ChannelFlags: uint32(channel.GetFlags()), + TeamID: uint32(channel.GetTeamID()), + NumMembers: uint32(channel.GetNumMembers()), + PlayerGUID: playerGUID, + PlayerName: playerName, + PlayerFlags: channel.GetMemberFlags(playerGUID), } return s.msgProducer.ProduceChannelJoined(payload) } -func (s *ChatService) broadcastChannelLeft(realmID uint32, channel *service.ActiveChannel, playerGUID uint64, playerName string) error { +func (s *ChatService) broadcastChannelLeft(realmID uint32, channel *service.ActiveChannel, playerGUID uint64, playerName string, silent bool) error { payload := &events.ChatEventChannelLeftPayload{ - ServiceID: s.serviceID, - RealmID: realmID, - ChannelName: channel.GetName(), - ChannelID: channel.GetChannelID(), - PlayerGUID: playerGUID, - PlayerName: playerName, + ServiceID: s.serviceID, + RealmID: realmID, + ChannelName: channel.GetName(), + ChannelID: channel.GetChannelID(), + ChannelFlags: uint32(channel.GetFlags()), + TeamID: uint32(channel.GetTeamID()), + NumMembers: uint32(channel.GetNumMembers()), + PlayerGUID: playerGUID, + PlayerName: playerName, + Silent: silent, } return s.msgProducer.ProduceChannelLeft(payload) } -func (s *ChatService) broadcastModeChange(realmID uint32, channel *service.ActiveChannel, targetGUID uint64) error { +func (s *ChatService) broadcastModeChange(realmID uint32, channel *service.ActiveChannel, targetGUID uint64, oldFlags uint8, newFlags uint8) error { payload := &events.ChatEventChannelNotificationPayload{ - RealmID: realmID, - ChannelName: channel.GetName(), - ChannelID: channel.GetChannelID(), - NotifyType: 0x0C, // CHAT_MODE_CHANGE_NOTICE - TargetGUID: targetGUID, + RealmID: realmID, + ChannelName: channel.GetName(), + ChannelID: channel.GetChannelID(), + ChannelFlags: uint32(channel.GetFlags()), + TeamID: uint32(channel.GetTeamID()), + NumMembers: uint32(channel.GetNumMembers()), + NotifyType: 0x0C, // CHAT_MODE_CHANGE_NOTICE + TargetGUID: targetGUID, + OldFlags: oldFlags, + NewFlags: newFlags, } return s.msgProducer.ProduceChannelNotification(payload) @@ -655,11 +711,42 @@ func (s *ChatService) broadcastModeChange(realmID uint32, channel *service.Activ func (s *ChatService) broadcastOwnerChanged(realmID uint32, channel *service.ActiveChannel, newOwnerGUID uint64) error { payload := &events.ChatEventChannelNotificationPayload{ - RealmID: realmID, - ChannelName: channel.GetName(), - ChannelID: channel.GetChannelID(), - NotifyType: 0x08, // CHAT_OWNER_CHANGED_NOTICE - TargetGUID: newOwnerGUID, + RealmID: realmID, + ChannelName: channel.GetName(), + ChannelID: channel.GetChannelID(), + ChannelFlags: uint32(channel.GetFlags()), + TeamID: uint32(channel.GetTeamID()), + NumMembers: uint32(channel.GetNumMembers()), + NotifyType: 0x08, // CHAT_OWNER_CHANGED_NOTICE + TargetGUID: newOwnerGUID, + } + + return s.msgProducer.ProduceChannelNotification(payload) +} + +func (s *ChatService) broadcastPlayerKicked(realmID uint32, channel *service.ActiveChannel, targetGUID, actorGUID uint64) error { + return s.broadcastTwoPlayerChannelNotification(realmID, channel, 0x12, targetGUID, actorGUID) +} + +func (s *ChatService) broadcastPlayerBanned(realmID uint32, channel *service.ActiveChannel, targetGUID, actorGUID uint64) error { + return s.broadcastTwoPlayerChannelNotification(realmID, channel, 0x14, targetGUID, actorGUID) +} + +func (s *ChatService) broadcastPlayerUnbanned(realmID uint32, channel *service.ActiveChannel, targetGUID, actorGUID uint64) error { + return s.broadcastTwoPlayerChannelNotification(realmID, channel, 0x15, targetGUID, actorGUID) +} + +func (s *ChatService) broadcastTwoPlayerChannelNotification(realmID uint32, channel *service.ActiveChannel, notifyType uint8, targetGUID, actorGUID uint64) error { + payload := &events.ChatEventChannelNotificationPayload{ + RealmID: realmID, + ChannelName: channel.GetName(), + ChannelID: channel.GetChannelID(), + ChannelFlags: uint32(channel.GetFlags()), + TeamID: uint32(channel.GetTeamID()), + NumMembers: uint32(channel.GetNumMembers()), + NotifyType: notifyType, + TargetGUID: targetGUID, + SecondGUID: actorGUID, } return s.msgProducer.ProduceChannelNotification(payload) diff --git a/apps/chatserver/server/chat.go b/apps/chatserver/server/chat.go index 40304fe..9edb51c 100644 --- a/apps/chatserver/server/chat.go +++ b/apps/chatserver/server/chat.go @@ -2,6 +2,7 @@ package server import ( "context" + "errors" "github.com/rs/zerolog/log" @@ -9,28 +10,40 @@ import ( "github.com/walkline/ToCloud9/apps/chatserver/repo" "github.com/walkline/ToCloud9/apps/chatserver/sender" "github.com/walkline/ToCloud9/apps/chatserver/service" + pbChar "github.com/walkline/ToCloud9/gen/characters/pb" "github.com/walkline/ToCloud9/gen/chat/pb" + wowguid "github.com/walkline/ToCloud9/shared/wow/guid" ) +var errWhisperReceiverAmbiguous = errors.New("whisper receiver ambiguous") + type ChatService struct { pb.UnimplementedChatServiceServer charRepo repo.CharactersRepo channelMgr *service.ChannelManager msgProducer sender.MsgProducer + charClient pbChar.CharactersServiceClient serviceID string } -func NewChatService(charRepo repo.CharactersRepo, channelMgr *service.ChannelManager, msgProducer sender.MsgProducer, serviceID string) *ChatService { +func NewChatService(charRepo repo.CharactersRepo, channelMgr *service.ChannelManager, msgProducer sender.MsgProducer, serviceID string, charClient pbChar.CharactersServiceClient) *ChatService { return &ChatService{ charRepo: charRepo, channelMgr: channelMgr, msgProducer: msgProducer, + charClient: charClient, serviceID: serviceID, } } func (s *ChatService) SendWhisperMessage(ctx context.Context, request *pb.SendWhisperMessageRequest) (*pb.SendWhisperMessageResponse, error) { - char, err := s.charRepo.CharacterByRealmAndName(ctx, request.RealmID, request.ReceiverName) + char, err := s.resolveWhisperReceiver(ctx, request.RealmID, request.ReceiverRealmID, request.ReceiverName) + if errors.Is(err, errWhisperReceiverAmbiguous) { + return &pb.SendWhisperMessageResponse{ + Api: chatserver.Ver, + Status: pb.SendWhisperMessageResponse_CharacterAmbiguous, + }, nil + } if err != nil { return nil, err } @@ -42,7 +55,21 @@ func (s *ChatService) SendWhisperMessage(ctx context.Context, request *pb.SendWh }, nil } - log.Debug().Msgf("New whisper from '%s' to '%s'", request.SenderName, char.Name) + allowed, err := s.crossrealmWhisperAllowed(ctx, request, char) + if err != nil { + return nil, err + } + if !allowed { + return &pb.SendWhisperMessageResponse{ + Api: chatserver.Ver, + Status: pb.SendWhisperMessageResponse_CharacterNotFound, + }, nil + } + + log.Debug(). + Uint32("senderRealmID", request.RealmID). + Uint32("receiverRealmID", char.RealmID). + Msgf("New whisper from '%s' to '%s'", request.SenderName, char.Name) err = char.MsgSender.SendWhisper( &sender.Character{ @@ -50,12 +77,17 @@ func (s *ChatService) SendWhisperMessage(ctx context.Context, request *pb.SendWh GUID: request.SenderGUID, Name: request.SenderName, Race: uint8(request.SenderRace), + Class: uint8(request.SenderClass), + Gender: uint8(request.SenderGender), + ChatTag: uint8(request.SenderChatTag), }, &sender.Character{ - RealmID: request.RealmID, + RealmID: char.RealmID, GUID: char.GUID, Name: char.Name, Race: char.Race, + Class: char.Class, + Gender: char.Gender, }, request.Language, request.Msg, @@ -65,8 +97,86 @@ func (s *ChatService) SendWhisperMessage(ctx context.Context, request *pb.SendWh } return &pb.SendWhisperMessageResponse{ - Api: chatserver.Ver, - Status: pb.SendWhisperMessageResponse_Ok, - ReceiverGUID: char.GUID, + Api: chatserver.Ver, + Status: pb.SendWhisperMessageResponse_Ok, + ReceiverGUID: wowguid.PlayerGUIDForRealm(request.RealmID, char.RealmID, char.GUID), + ReceiverRealmID: char.RealmID, + ReceiverName: char.Name, + ReceiverRace: uint32(char.Race), + ReceiverClass: uint32(char.Class), + ReceiverGender: uint32(char.Gender), }, nil } + +func (s *ChatService) resolveWhisperReceiver(ctx context.Context, senderRealmID uint32, receiverRealmID uint32, receiverName string) (*repo.Character, error) { + if receiverRealmID != 0 { + return s.charRepo.CharacterByRealmAndName(ctx, receiverRealmID, receiverName) + } + + char, err := s.charRepo.CharacterByRealmAndName(ctx, senderRealmID, receiverName) + if err != nil { + return nil, err + } + if char != nil { + return char, nil + } + + matches, err := s.charRepo.CharactersByName(ctx, receiverName) + if err != nil { + return nil, err + } + if len(matches) == 0 { + return nil, nil + } + if len(matches) > 1 { + return nil, errWhisperReceiverAmbiguous + } + + return matches[0], nil +} + +func (s *ChatService) crossrealmWhisperAllowed(ctx context.Context, request *pb.SendWhisperMessageRequest, char *repo.Character) (bool, error) { + if char.RealmID == request.RealmID { + return true, nil + } + if request.GetGatewayValidatedGameplayCrossrealmWhisper() { + log.Debug(). + Uint32("senderRealmID", request.RealmID). + Uint32("receiverRealmID", char.RealmID). + Uint32("senderAccountID", request.SenderAccountID). + Uint32("receiverAccountID", char.AccountID). + Str("receiverName", char.Name). + Msg("Allowed crossrealm whisper through gateway-validated gameplay context") + return true, nil + } + if request.SenderAccountID == 0 || char.AccountID == 0 { + log.Debug(). + Uint32("senderRealmID", request.RealmID). + Uint32("receiverRealmID", char.RealmID). + Uint32("senderAccountID", request.SenderAccountID). + Uint32("receiverAccountID", char.AccountID). + Str("receiverName", char.Name). + Msg("Denied crossrealm whisper without account identity") + return false, nil + } + if s.charClient == nil { + log.Warn(). + Uint32("senderRealmID", request.RealmID). + Uint32("receiverRealmID", char.RealmID). + Uint32("senderAccountID", request.SenderAccountID). + Uint32("receiverAccountID", char.AccountID). + Msg("Denied crossrealm whisper because characters service client is unavailable") + return false, nil + } + + res, err := s.charClient.AreRealIDFriends(ctx, &pbChar.AreRealIDFriendsRequest{ + Api: chatserver.Ver, + AccountID: request.SenderAccountID, + FriendAccountID: char.AccountID, + }) + if err != nil { + return false, err + } + + return res.GetAccepted(), nil +} diff --git a/apps/chatserver/service/channel-manager.go b/apps/chatserver/service/channel-manager.go index b70964e..d99d9ba 100644 --- a/apps/chatserver/service/channel-manager.go +++ b/apps/chatserver/service/channel-manager.go @@ -120,40 +120,53 @@ func (cm *ChannelManager) GetOrCreateChannel(ctx context.Context, realmID uint32 // Try to load from database var dbChannel *repo.Channel var err error - if channelID != 0 && flags&ChannelFlagCustom != 0 { - dbChannel, err = cm.repo.GetChannelByID(ctx, realmID, channelID) - } else { - dbChannel, err = cm.repo.GetChannelByName(ctx, realmID, channelName, uint32(team)) - } - if err != nil { - return nil, fmt.Errorf("failed to get channel from DB: %w", err) + if realmID != 0 { + if channelID != 0 && flags&ChannelFlagCustom != 0 { + dbChannel, err = cm.repo.GetChannelByID(ctx, realmID, channelID) + } else { + dbChannel, err = cm.repo.GetChannelByName(ctx, realmID, channelName, uint32(team)) + } + if err != nil { + return nil, fmt.Errorf("failed to get channel from DB: %w", err) + } } // Create new channel if not found in DB if dbChannel == nil { dbChannel = &repo.Channel{ + ChannelID: channelID, Name: channelName, Team: uint32(team), Announce: true, - Ownership: true, + Ownership: flags&ChannelFlagCustom != 0, Password: password, LastUsed: time.Now(), } - if err := cm.repo.CreateChannel(ctx, realmID, dbChannel); err != nil { - return nil, fmt.Errorf("failed to create channel in DB: %w", err) + if realmID != 0 { + if err := cm.repo.CreateChannel(ctx, realmID, dbChannel); err != nil { + return nil, fmt.Errorf("failed to create channel in DB: %w", err) + } + } else if dbChannel.ChannelID == 0 { + dbChannel.ChannelID = uint32(len(cm.channels) + 1) } } // Load channel rights - rights, err := cm.repo.GetChannelRights(ctx, realmID, channelName) - if err != nil { - return nil, fmt.Errorf("failed to get channel rights: %w", err) + var rights *repo.ChannelRights + if realmID != 0 { + rights, err = cm.repo.GetChannelRights(ctx, realmID, channelName) + if err != nil { + return nil, fmt.Errorf("failed to get channel rights: %w", err) + } } // Load bans - bans, err := cm.repo.GetChannelBans(ctx, realmID, dbChannel.ChannelID) - if err != nil { - return nil, fmt.Errorf("failed to get channel bans: %w", err) + var bans []repo.ChannelBan + if realmID != 0 { + bans, err = cm.repo.GetChannelBans(ctx, realmID, dbChannel.ChannelID) + if err != nil { + return nil, fmt.Errorf("failed to get channel bans: %w", err) + } } bannedUntil := make(map[uint64]time.Time) @@ -164,9 +177,12 @@ func (cm *ChannelManager) GetOrCreateChannel(ctx context.Context, realmID uint32 } // Load members - loadedMembers, err := cm.repo.LoadChannelMembers(ctx, realmID, dbChannel.ChannelID) - if err != nil { - return nil, fmt.Errorf("failed to load channel members: %w", err) + var loadedMembers []repo.ChannelMember + if realmID != 0 { + loadedMembers, err = cm.repo.LoadChannelMembers(ctx, realmID, dbChannel.ChannelID) + if err != nil { + return nil, fmt.Errorf("failed to load channel members: %w", err) + } } members := make(map[uint64]*repo.ChannelMember) @@ -230,7 +246,7 @@ func (ch *ActiveChannel) JoinChannel(ctx context.Context, cm *ChannelManager, re flags := MemberFlagNone if len(ch.members) == 0 && ch.ownership { // First member becomes owner - flags = MemberFlagOwner + flags = MemberFlagOwner | MemberFlagModerator ch.ownerGUID = playerGUID } @@ -253,37 +269,43 @@ func (ch *ActiveChannel) JoinChannel(ctx context.Context, cm *ChannelManager, re ch.members[playerGUID] = member // Write through to DB - if err := cm.repo.SaveChannelMember(ctx, realmID, ch.channelID, member); err != nil { - // Remove from memory on DB failure - delete(ch.members, playerGUID) - return fmt.Errorf("failed to persist channel member: %w", err) + if realmID != 0 { + if err := cm.repo.SaveChannelMember(ctx, realmID, ch.channelID, member); err != nil { + // Remove from memory on DB failure + delete(ch.members, playerGUID) + return fmt.Errorf("failed to persist channel member: %w", err) + } } return nil } -// LeaveChannel removes a player from a channel with write-through persistence -// Returns (newOwnerGUID, error) - newOwnerGUID is non-zero if ownership was transferred -func (ch *ActiveChannel) LeaveChannel(ctx context.Context, cm *ChannelManager, realmID uint32, playerGUID uint64) (uint64, error) { +// LeaveChannel removes a player from a channel with write-through persistence. +// Returns new owner data when ownership was transferred. +func (ch *ActiveChannel) LeaveChannel(ctx context.Context, cm *ChannelManager, realmID uint32, playerGUID uint64) (uint64, uint8, uint8, error) { ch.mu.Lock() defer ch.mu.Unlock() member, exists := ch.members[playerGUID] if !exists { - return 0, ErrNotMember + return 0, 0, 0, ErrNotMember } wasOwner := member.Flags&MemberFlagOwner != 0 delete(ch.members, playerGUID) // Write through to DB - if err := cm.repo.RemoveChannelMember(ctx, realmID, ch.channelID, playerGUID); err != nil { - // Re-add to memory on DB failure - ch.members[playerGUID] = member - return 0, fmt.Errorf("failed to remove channel member from DB: %w", err) + if realmID != 0 { + if err := cm.repo.RemoveChannelMember(ctx, realmID, ch.channelID, playerGUID); err != nil { + // Re-add to memory on DB failure + ch.members[playerGUID] = member + return 0, 0, 0, fmt.Errorf("failed to remove channel member from DB: %w", err) + } } var newOwnerGUID uint64 + var oldFlags uint8 + var newFlags uint8 // Transfer ownership if owner left if wasOwner && len(ch.members) > 0 && ch.ownership { @@ -302,19 +324,24 @@ func (ch *ActiveChannel) LeaveChannel(ctx context.Context, cm *ChannelManager, r } } if newOwnerGUID != 0 { - ch.members[newOwnerGUID].Flags |= MemberFlagOwner + newOwner := ch.members[newOwnerGUID] + oldFlags = newOwner.Flags | MemberFlagModerator + newOwner.Flags = oldFlags | MemberFlagOwner + newFlags = newOwner.Flags ch.ownerGUID = newOwnerGUID // Persist new owner flag - if err := cm.repo.UpdateMemberFlags(ctx, realmID, ch.channelID, newOwnerGUID, ch.members[newOwnerGUID].Flags); err != nil { - // Log but don't fail - ownership transfer is in-memory - log.Error().Err(err). - Uint64("newOwnerGUID", newOwnerGUID). - Msg("Failed to persist owner flag transfer") + if realmID != 0 { + if err := cm.repo.UpdateMemberFlags(ctx, realmID, ch.channelID, newOwnerGUID, ch.members[newOwnerGUID].Flags); err != nil { + // Log but don't fail - ownership transfer is in-memory + log.Error().Err(err). + Uint64("newOwnerGUID", newOwnerGUID). + Msg("Failed to persist owner flag transfer") + } } } } - return newOwnerGUID, nil + return newOwnerGUID, oldFlags, newFlags, nil } // GetMembers returns a copy of all channel members @@ -377,10 +404,12 @@ func (ch *ActiveChannel) SetModerator(ctx context.Context, cm *ChannelManager, r } // Write through to DB - if err := cm.repo.UpdateMemberFlags(ctx, realmID, ch.channelID, playerGUID, member.Flags); err != nil { - // Revert on failure - member.Flags = oldFlags - return fmt.Errorf("failed to persist moderator flag: %w", err) + if realmID != 0 { + if err := cm.repo.UpdateMemberFlags(ctx, realmID, ch.channelID, playerGUID, member.Flags); err != nil { + // Revert on failure + member.Flags = oldFlags + return fmt.Errorf("failed to persist moderator flag: %w", err) + } } return nil @@ -404,10 +433,12 @@ func (ch *ActiveChannel) SetMute(ctx context.Context, cm *ChannelManager, realmI } // Write through to DB - if err := cm.repo.UpdateMemberFlags(ctx, realmID, ch.channelID, playerGUID, member.Flags); err != nil { - // Revert on failure - member.Flags = oldFlags - return fmt.Errorf("failed to persist mute flag: %w", err) + if realmID != 0 { + if err := cm.repo.UpdateMemberFlags(ctx, realmID, ch.channelID, playerGUID, member.Flags); err != nil { + // Revert on failure + member.Flags = oldFlags + return fmt.Errorf("failed to persist mute flag: %w", err) + } } return nil @@ -420,6 +451,13 @@ func (ch *ActiveChannel) GetChannelID() uint32 { return ch.channelID } +// GetTeamID returns the channel team ID. +func (ch *ActiveChannel) GetTeamID() pbChat.TeamID { + ch.mu.RLock() + defer ch.mu.RUnlock() + return ch.team +} + // GetName returns the channel name func (ch *ActiveChannel) GetName() string { ch.mu.RLock() @@ -499,6 +537,9 @@ func (cm *ChannelManager) BanPlayer(ctx context.Context, realmID uint32, channel ch.mu.Unlock() // Write through to DB + if realmID == 0 { + return nil + } ban := &repo.ChannelBan{ ChannelID: ch.channelID, PlayerGUID: playerGUID, @@ -520,6 +561,9 @@ func (cm *ChannelManager) UnbanPlayer(ctx context.Context, realmID uint32, chann ch.mu.Unlock() // Write through to DB + if realmID == 0 { + return nil + } return cm.repo.RemoveChannelBan(ctx, realmID, channelID, playerGUID) } @@ -539,6 +583,9 @@ func (ch *ActiveChannel) SetPassword(ctx context.Context, cm *ChannelManager, re ch.mu.Unlock() // Write through to DB + if realmID == 0 { + return nil + } return cm.repo.UpdateChannel(ctx, realmID, dbChannel) } @@ -561,28 +608,32 @@ func (ch *ActiveChannel) SetOwner(ctx context.Context, cm *ChannelManager, realm oldOwnerOldFlags = oldOwner.Flags oldOwner.Flags &= ^MemberFlagOwner // Persist old owner flag change - if err := cm.repo.UpdateMemberFlags(ctx, realmID, ch.channelID, ch.ownerGUID, oldOwner.Flags); err != nil { - // Revert - oldOwner.Flags = oldOwnerOldFlags - return fmt.Errorf("failed to persist old owner flag removal: %w", err) + if realmID != 0 { + if err := cm.repo.UpdateMemberFlags(ctx, realmID, ch.channelID, ch.ownerGUID, oldOwner.Flags); err != nil { + // Revert + oldOwner.Flags = oldOwnerOldFlags + return fmt.Errorf("failed to persist old owner flag removal: %w", err) + } } } } // Set new owner newOwnerOldFlags := newOwner.Flags - newOwner.Flags |= MemberFlagOwner + newOwner.Flags |= MemberFlagOwner | MemberFlagModerator // Persist new owner flag - if err := cm.repo.UpdateMemberFlags(ctx, realmID, ch.channelID, newOwnerGUID, newOwner.Flags); err != nil { - // Revert both changes - newOwner.Flags = newOwnerOldFlags - if oldOwnerGUID != 0 { - if oldOwner, exists := ch.members[oldOwnerGUID]; exists { - oldOwner.Flags = oldOwnerOldFlags + if realmID != 0 { + if err := cm.repo.UpdateMemberFlags(ctx, realmID, ch.channelID, newOwnerGUID, newOwner.Flags); err != nil { + // Revert both changes + newOwner.Flags = newOwnerOldFlags + if oldOwnerGUID != 0 { + if oldOwner, exists := ch.members[oldOwnerGUID]; exists { + oldOwner.Flags = oldOwnerOldFlags + } } + return fmt.Errorf("failed to persist new owner flag: %w", err) } - return fmt.Errorf("failed to persist new owner flag: %w", err) } ch.ownerGUID = newOwnerGUID @@ -600,7 +651,7 @@ func (cm *ChannelManager) UpdateLastUsed(ctx context.Context, realmID uint32, ch channelID := ch.channelID ch.mu.RUnlock() - if channelID == 0 { + if realmID == 0 || channelID == 0 { return nil } @@ -621,67 +672,83 @@ func (ch *ActiveChannel) PersistToggleAnnouncements(ctx context.Context, cm *Cha } ch.mu.RUnlock() + if realmID == 0 { + return nil + } return cm.repo.UpdateChannel(ctx, realmID, dbChannel) } // High-level service methods that combine permission checks, name resolution, and business logic +func (ch *ActiveChannel) removeMemberAfterModeration(ctx context.Context, cm *ChannelManager, realmID uint32, actorGUID uint64, targetGUID uint64) (uint64, uint8, uint8, error) { + if targetGUID != actorGUID && ch.ownership && ch.GetMemberFlags(targetGUID)&MemberFlagOwner != 0 && ch.IsMember(actorGUID) { + oldFlags := ch.GetMemberFlags(actorGUID) | MemberFlagModerator + if err := ch.SetOwner(ctx, cm, realmID, actorGUID); err != nil { + return 0, 0, 0, err + } + if _, _, _, err := ch.LeaveChannel(ctx, cm, realmID, targetGUID); err != nil { + return 0, 0, 0, err + } + return actorGUID, oldFlags, ch.GetMemberFlags(actorGUID), nil + } + + return ch.LeaveChannel(ctx, cm, realmID, targetGUID) +} + // KickPlayer kicks a player from a channel after checking permissions. -// Returns the channel and target GUID for broadcasting. -func (cm *ChannelManager) KickPlayer(ctx context.Context, realmID uint32, channelName string, team pbChat.TeamID, kickerGUID uint64, targetName string) (*ActiveChannel, uint64, error) { +// Returns the channel, target GUID, and owner-transfer data for broadcasting. +func (cm *ChannelManager) KickPlayer(ctx context.Context, realmID uint32, channelName string, team pbChat.TeamID, kickerGUID uint64, targetName string) (*ActiveChannel, uint64, uint64, uint8, uint8, error) { channel := cm.GetChannel(realmID, channelName, team) if channel == nil { - return nil, 0, ErrChannelNotFound + return nil, 0, 0, 0, 0, ErrChannelNotFound } if !channel.IsMember(kickerGUID) { - return nil, 0, ErrNotMember + return nil, 0, 0, 0, 0, ErrNotMember } kickerFlags := channel.GetMemberFlags(kickerGUID) if kickerFlags&(MemberFlagModerator|MemberFlagOwner) == 0 { - return nil, 0, ErrNotModerator + return nil, 0, 0, 0, 0, ErrNotModerator } targetGUID := channel.FindMemberByName(targetName) if targetGUID == 0 { - return nil, 0, ErrPlayerNotFound + return nil, 0, 0, 0, 0, ErrPlayerNotFound } - newOwnerGUID, err := channel.LeaveChannel(ctx, cm, realmID, targetGUID) + newOwnerGUID, oldFlags, newFlags, err := channel.removeMemberAfterModeration(ctx, cm, realmID, kickerGUID, targetGUID) if err != nil { - return nil, 0, err + return nil, 0, 0, 0, 0, err } - // If ownership was transferred, we need to notify (caller should broadcast) - _ = newOwnerGUID // Caller will handle broadcasting - - return channel, targetGUID, nil + return channel, targetGUID, newOwnerGUID, oldFlags, newFlags, nil } // BanPlayerByName kicks and bans a player, persisting the ban to DB. -func (cm *ChannelManager) BanPlayerByName(ctx context.Context, realmID uint32, channelName string, team pbChat.TeamID, bannerGUID uint64, targetName string) (*ActiveChannel, uint64, error) { +func (cm *ChannelManager) BanPlayerByName(ctx context.Context, realmID uint32, channelName string, team pbChat.TeamID, bannerGUID uint64, targetName string) (*ActiveChannel, uint64, uint64, uint8, uint8, error) { channel := cm.GetChannel(realmID, channelName, team) if channel == nil { - return nil, 0, ErrChannelNotFound + return nil, 0, 0, 0, 0, ErrChannelNotFound } if !channel.IsMember(bannerGUID) { - return nil, 0, ErrNotMember + return nil, 0, 0, 0, 0, ErrNotMember } bannerFlags := channel.GetMemberFlags(bannerGUID) if bannerFlags&(MemberFlagModerator|MemberFlagOwner) == 0 { - return nil, 0, ErrNotModerator + return nil, 0, 0, 0, 0, ErrNotModerator } targetGUID := channel.FindMemberByName(targetName) if targetGUID == 0 { - return nil, 0, ErrPlayerNotFound + return nil, 0, 0, 0, 0, ErrPlayerNotFound } // Kick from channel - if _, err := channel.LeaveChannel(ctx, cm, realmID, targetGUID); err != nil { + newOwnerGUID, oldFlags, newFlags, err := channel.removeMemberAfterModeration(ctx, cm, realmID, bannerGUID, targetGUID) + if err != nil { // Log but don't fail if already left log.Debug().Err(err).Msg("Failed to kick player during ban (already left?)") } @@ -689,10 +756,10 @@ func (cm *ChannelManager) BanPlayerByName(ctx context.Context, realmID uint32, c // Add permanent ban (100 years) banTime := time.Now().Add(100 * 365 * 24 * time.Hour) if err := cm.BanPlayer(ctx, realmID, channelName, team, targetGUID, banTime); err != nil { - return nil, 0, fmt.Errorf("failed to persist ban: %w", err) + return nil, 0, 0, 0, 0, fmt.Errorf("failed to persist ban: %w", err) } - return channel, targetGUID, nil + return channel, targetGUID, newOwnerGUID, oldFlags, newFlags, nil } // UnbanPlayerByName removes a ban after checking permissions. @@ -724,83 +791,86 @@ func (cm *ChannelManager) UnbanPlayerByName(ctx context.Context, realmID uint32, } // SetModeratorByName sets/unsets moderator for a player found by name. Requires owner. -func (cm *ChannelManager) SetModeratorByName(ctx context.Context, realmID uint32, channelName string, team pbChat.TeamID, setterGUID uint64, targetName string, isModerator bool) (uint64, error) { +func (cm *ChannelManager) SetModeratorByName(ctx context.Context, realmID uint32, channelName string, team pbChat.TeamID, setterGUID uint64, targetName string, isModerator bool) (uint64, uint8, uint8, error) { channel := cm.GetChannel(realmID, channelName, team) if channel == nil { - return 0, ErrChannelNotFound + return 0, 0, 0, ErrChannelNotFound } if !channel.IsMember(setterGUID) { - return 0, ErrNotMember + return 0, 0, 0, ErrNotMember } setterFlags := channel.GetMemberFlags(setterGUID) if setterFlags&MemberFlagOwner == 0 { - return 0, ErrNotOwner + return 0, 0, 0, ErrNotOwner } targetGUID := channel.FindMemberByName(targetName) if targetGUID == 0 { - return 0, ErrPlayerNotFound + return 0, 0, 0, ErrPlayerNotFound } + oldFlags := channel.GetMemberFlags(targetGUID) if err := channel.SetModerator(ctx, cm, realmID, targetGUID, isModerator); err != nil { - return 0, err + return 0, 0, 0, err } - return targetGUID, nil + return targetGUID, oldFlags, channel.GetMemberFlags(targetGUID), nil } // SetMuteByName sets/unsets mute for a player found by name. Requires moderator or owner. -func (cm *ChannelManager) SetMuteByName(ctx context.Context, realmID uint32, channelName string, team pbChat.TeamID, muterGUID uint64, targetName string, isMuted bool) (uint64, error) { +func (cm *ChannelManager) SetMuteByName(ctx context.Context, realmID uint32, channelName string, team pbChat.TeamID, muterGUID uint64, targetName string, isMuted bool) (uint64, uint8, uint8, error) { channel := cm.GetChannel(realmID, channelName, team) if channel == nil { - return 0, ErrChannelNotFound + return 0, 0, 0, ErrChannelNotFound } if !channel.IsMember(muterGUID) { - return 0, ErrNotMember + return 0, 0, 0, ErrNotMember } muterFlags := channel.GetMemberFlags(muterGUID) if muterFlags&(MemberFlagModerator|MemberFlagOwner) == 0 { - return 0, ErrNotModerator + return 0, 0, 0, ErrNotModerator } targetGUID := channel.FindMemberByName(targetName) if targetGUID == 0 { - return 0, ErrPlayerNotFound + return 0, 0, 0, ErrPlayerNotFound } + oldFlags := channel.GetMemberFlags(targetGUID) if err := channel.SetMute(ctx, cm, realmID, targetGUID, isMuted); err != nil { - return 0, err + return 0, 0, 0, err } - return targetGUID, nil + return targetGUID, oldFlags, channel.GetMemberFlags(targetGUID), nil } // SetOwnerByName transfers ownership. Requires current owner. -func (cm *ChannelManager) SetOwnerByName(ctx context.Context, realmID uint32, channelName string, team pbChat.TeamID, setterGUID uint64, targetName string) (uint64, error) { +func (cm *ChannelManager) SetOwnerByName(ctx context.Context, realmID uint32, channelName string, team pbChat.TeamID, setterGUID uint64, targetName string) (uint64, uint8, uint8, error) { channel := cm.GetChannel(realmID, channelName, team) if channel == nil { - return 0, ErrChannelNotFound + return 0, 0, 0, ErrChannelNotFound } setterFlags := channel.GetMemberFlags(setterGUID) if setterFlags&MemberFlagOwner == 0 { - return 0, ErrNotOwner + return 0, 0, 0, ErrNotOwner } targetGUID := channel.FindMemberByName(targetName) if targetGUID == 0 { - return 0, ErrPlayerNotFound + return 0, 0, 0, ErrPlayerNotFound } + oldFlags := channel.GetMemberFlags(targetGUID) | MemberFlagModerator if err := channel.SetOwner(ctx, cm, realmID, targetGUID); err != nil { - return 0, err + return 0, 0, 0, err } - return targetGUID, nil + return targetGUID, oldFlags, channel.GetMemberFlags(targetGUID), nil } // SetChannelPassword sets the password. Requires owner. Persists to DB. @@ -828,6 +898,9 @@ func (cm *ChannelManager) SetChannelPassword(ctx context.Context, realmID uint32 } channel.mu.Unlock() + if realmID == 0 { + return nil + } return cm.repo.UpdateChannel(ctx, realmID, dbChannel) } @@ -882,6 +955,9 @@ func (cm *ChannelManager) ToggleChannelAnnouncements(ctx context.Context, realmI } channel.mu.RUnlock() + if realmID == 0 { + return enabled, nil + } if err := cm.repo.UpdateChannel(ctx, realmID, dbChannel); err != nil { return enabled, fmt.Errorf("failed to persist announcement toggle: %w", err) } @@ -889,11 +965,12 @@ func (cm *ChannelManager) ToggleChannelAnnouncements(ctx context.Context, realmI return enabled, nil } -// LeaveChannelByGUID handles a player leaving a channel. Returns player name, whether channel is custom, and new owner GUID if transferred. -func (cm *ChannelManager) LeaveChannelByGUID(ctx context.Context, realmID uint32, channelName string, team pbChat.TeamID, playerGUID uint64) (string, bool, uint64, error) { +// LeaveChannelByGUID handles a player leaving a channel. +// Returns player name, whether channel is custom, and new owner data if transferred. +func (cm *ChannelManager) LeaveChannelByGUID(ctx context.Context, realmID uint32, channelName string, team pbChat.TeamID, playerGUID uint64) (string, bool, uint64, uint8, uint8, error) { channel := cm.GetChannel(realmID, channelName, team) if channel == nil { - return "", false, 0, ErrChannelNotFound + return "", false, 0, 0, 0, ErrChannelNotFound } // Find player name before leaving @@ -906,13 +983,13 @@ func (cm *ChannelManager) LeaveChannelByGUID(ctx context.Context, realmID uint32 } } - newOwnerGUID, err := channel.LeaveChannel(ctx, cm, realmID, playerGUID) + newOwnerGUID, oldFlags, newFlags, err := channel.LeaveChannel(ctx, cm, realmID, playerGUID) if err != nil { - return "", false, 0, err + return "", false, 0, 0, 0, err } isCustom := channel.GetFlags()&ChannelFlagCustom != 0 - return playerName, isCustom, newOwnerGUID, nil + return playerName, isCustom, newOwnerGUID, oldFlags, newFlags, nil } // ValidateSendMessage validates that a player can send a message. Returns channel for broadcasting. @@ -937,8 +1014,12 @@ func (cm *ChannelManager) ValidateSendMessage(realmID uint32, channelName string type OwnershipTransfer struct { ChannelName string ChannelID uint32 + ChannelFlags uint8 TeamID pbChat.TeamID + NumMembers uint32 NewOwnerGUID uint64 + OldFlags uint8 + NewFlags uint8 } // TransferOwnershipOnLogout transfers channel ownership when owner logs out (but keeps them as member) @@ -1012,7 +1093,9 @@ func (cm *ChannelManager) TransferOwnershipOnLogout(realmID uint32, playerGUID u } // Transfer ownership to new owner - channel.members[newOwnerGUID].Flags |= MemberFlagOwner + oldFlags := channel.members[newOwnerGUID].Flags | MemberFlagModerator + channel.members[newOwnerGUID].Flags = oldFlags | MemberFlagOwner + newFlags := channel.members[newOwnerGUID].Flags channel.ownerGUID = newOwnerGUID // Persist new owner flag @@ -1027,8 +1110,12 @@ func (cm *ChannelManager) TransferOwnershipOnLogout(realmID uint32, playerGUID u transfers = append(transfers, OwnershipTransfer{ ChannelName: channel.name, ChannelID: channel.channelID, + ChannelFlags: channel.flags, TeamID: channel.team, + NumMembers: uint32(len(channel.members)), NewOwnerGUID: newOwnerGUID, + OldFlags: oldFlags, + NewFlags: newFlags, }) log.Debug(). @@ -1219,7 +1306,7 @@ func (ch *ActiveChannel) PruneOfflineMembers(ctx context.Context, cm *ChannelMan } } if newOwnerGUID != 0 { - ch.members[newOwnerGUID].Flags |= MemberFlagOwner + ch.members[newOwnerGUID].Flags |= MemberFlagOwner | MemberFlagModerator ch.ownerGUID = newOwnerGUID // Persist new owner flag if err := cm.repo.UpdateMemberFlags(ctx, realmID, ch.channelID, newOwnerGUID, ch.members[newOwnerGUID].Flags); err != nil { diff --git a/apps/chatserver/service/channels-listener.go b/apps/chatserver/service/channels-listener.go index 526ff79..e66ec94 100644 --- a/apps/chatserver/service/channels-listener.go +++ b/apps/chatserver/service/channels-listener.go @@ -6,6 +6,7 @@ import ( "github.com/nats-io/nats.go" "github.com/rs/zerolog/log" + pbChat "github.com/walkline/ToCloud9/gen/chat/pb" "github.com/walkline/ToCloud9/shared/events" ) @@ -36,9 +37,14 @@ func (c *ChannelsListener) Listen() error { return } + channelFlags := uint8(payload.ChannelFlags) + if channelFlags == 0 { + channelFlags = ChannelFlagCustom + } + ch, err := c.channelMgr.GetOrCreateChannel( context.TODO(), payload.RealmID, payload.ChannelName, - payload.ChannelID, 0, "", ChannelFlagCustom, + payload.ChannelID, pbChat.TeamID(payload.TeamID), "", channelFlags, ) if err != nil { log.Error().Err(err).Str("channel", payload.ChannelName).Msg("sync: failed to get/create channel") @@ -65,12 +71,12 @@ func (c *ChannelsListener) Listen() error { return } - ch := c.channelMgr.GetChannel(payload.RealmID, payload.ChannelName, 0) + ch := c.channelMgr.GetChannel(payload.RealmID, payload.ChannelName, pbChat.TeamID(payload.TeamID)) if ch == nil { return } - _, err = ch.LeaveChannel(context.TODO(), c.channelMgr, payload.RealmID, payload.PlayerGUID) + _, _, _, err = ch.LeaveChannel(context.TODO(), c.channelMgr, payload.RealmID, payload.PlayerGUID) if err != nil { log.Debug().Err(err).Str("channel", payload.ChannelName).Uint64("player", payload.PlayerGUID).Msg("sync: leave skipped") } diff --git a/apps/chatserver/service/characters-listener.go b/apps/chatserver/service/characters-listener.go index b7e422b..03b4f6b 100644 --- a/apps/chatserver/service/characters-listener.go +++ b/apps/chatserver/service/characters-listener.go @@ -37,19 +37,31 @@ func (c *CharactersListener) Listen() error { return } - err = c.charRepo.AddCharacter(context.TODO(), &repo.Character{ + applied, err := c.charRepo.AddCharacterFromGatewayEvent(context.TODO(), &repo.Character{ RealmID: loggedInP.RealmID, GatewayID: loggedInP.GatewayID, GUID: loggedInP.CharGUID, + AccountID: loggedInP.AccountID, Name: loggedInP.CharName, Race: loggedInP.CharRace, + Class: loggedInP.CharClass, + Gender: loggedInP.CharGender, MsgSender: sender.NewMsgSenderNatsJSON(c.nc, loggedInP.GatewayID), - }) + }, loggedInP.EventTimeUnixNano) if err != nil { log.Error().Err(err).Msg("can't add character in GWEventCharacterLoggedIn event") return } + if !applied { + log.Debug(). + Uint32("realmID", loggedInP.RealmID). + Uint64("charGUID", loggedInP.CharGUID). + Str("gatewayID", loggedInP.GatewayID). + Uint64("eventTimeUnixNano", loggedInP.EventTimeUnixNano). + Msg("ignored repository-stale GWEventCharacterLoggedIn event") + return + } }) if err != nil { return err @@ -65,18 +77,36 @@ func (c *CharactersListener) Listen() error { return } - // Transfer ownership if player was owner (but keep them as member) + applied, err := c.charRepo.RemoveCharacterFromGatewayEvent(context.TODO(), loggedOutP.RealmID, loggedOutP.CharGUID, loggedOutP.EventTimeUnixNano) + if err != nil { + log.Error().Err(err).Msg("can't remove character in GWEventCharacterLoggedOut event") + return + } + if !applied { + log.Debug(). + Uint32("realmID", loggedOutP.RealmID). + Uint64("charGUID", loggedOutP.CharGUID). + Str("gatewayID", loggedOutP.GatewayID). + Uint64("eventTimeUnixNano", loggedOutP.EventTimeUnixNano). + Msg("ignored repository-stale GWEventCharacterLoggedOut event") + return + } + + // Transfer ownership if player was owner (but keep them as member). transfers := c.channelMgr.TransferOwnershipOnLogout(loggedOutP.RealmID, loggedOutP.CharGUID) - // Broadcast ownership changes for each transfer for _, transfer := range transfers { - // Send mode change notification payload := &events.ChatEventChannelNotificationPayload{ - RealmID: loggedOutP.RealmID, - ChannelName: transfer.ChannelName, - ChannelID: transfer.ChannelID, - NotifyType: 0x0C, // CHAT_MODE_CHANGE_NOTICE - TargetGUID: transfer.NewOwnerGUID, + RealmID: loggedOutP.RealmID, + ChannelName: transfer.ChannelName, + ChannelID: transfer.ChannelID, + ChannelFlags: uint32(transfer.ChannelFlags), + TeamID: uint32(transfer.TeamID), + NumMembers: transfer.NumMembers, + NotifyType: 0x0C, // CHAT_MODE_CHANGE_NOTICE + TargetGUID: transfer.NewOwnerGUID, + OldFlags: transfer.OldFlags, + NewFlags: transfer.NewFlags, } if err := c.producer.ChannelNotification(payload); err != nil { log.Error().Err(err). @@ -85,7 +115,6 @@ func (c *CharactersListener) Listen() error { Msg("Failed to broadcast mode change on logout ownership transfer") } - // Send owner changed notification payload.NotifyType = 0x08 // CHAT_OWNER_CHANGED_NOTICE if err := c.producer.ChannelNotification(payload); err != nil { log.Error().Err(err). @@ -94,12 +123,6 @@ func (c *CharactersListener) Listen() error { Msg("Failed to broadcast owner changed on logout ownership transfer") } } - - err = c.charRepo.RemoveCharacter(context.TODO(), loggedOutP.RealmID, loggedOutP.CharGUID) - if err != nil { - log.Error().Err(err).Msg("can't remove character in GWEventCharacterLoggedOut event") - return - } }) if err != nil { c.unsubscribe() diff --git a/apps/chatserver/service/serversregistry-listener.go b/apps/chatserver/service/serversregistry-listener.go index 523c748..7cf6dfc 100644 --- a/apps/chatserver/service/serversregistry-listener.go +++ b/apps/chatserver/service/serversregistry-listener.go @@ -32,9 +32,9 @@ func (c *ServersRegistryListener) Listen() error { return } - err = c.charRepo.RemoveCharactersWithRealm(context.TODO(), payload.RealmID) + err = c.charRepo.RemoveCharactersWithGatewayID(context.TODO(), payload.RealmID, payload.ID, payload.EventTimeUnixNano) if err != nil { - log.Error().Err(err).Msg("can't add character in GWEventCharacterLoggedIn event") + log.Error().Err(err).Msg("can't remove gateway characters in ServerRegistryEventGWRemovedUnhealthy event") return } }) diff --git a/apps/mailserver/cmd/mailserver/main.go b/apps/mailserver/cmd/mailserver/main.go index 189b030..c76b189 100644 --- a/apps/mailserver/cmd/mailserver/main.go +++ b/apps/mailserver/cmd/mailserver/main.go @@ -7,6 +7,7 @@ import ( "net" "os" "os/signal" + "sort" "sync" "syscall" "time" @@ -68,7 +69,7 @@ func main() { time.Second*time.Duration(cfg.DefaultMailExpirationTimeSecs), ) - ticker := service.NewMailsCleanupTicker([]uint32{1}, time.Second*time.Duration(cfg.ExpiredMailsCleanupSecsDelay), mailService) + ticker := service.NewMailsCleanupTicker(configuredRealmIDs(cfg.CharDBConnection), time.Second*time.Duration(cfg.ExpiredMailsCleanupSecsDelay), mailService) go ticker.Start(context.TODO()) // grpc setup @@ -114,3 +115,14 @@ func configureDBConn(db *sql.DB) { db.SetConnMaxLifetime(time.Minute * 4) db.SetConnMaxIdleTime(time.Minute * 8) } + +func configuredRealmIDs(connections map[uint32]string) []uint32 { + realmIDs := make([]uint32, 0, len(connections)) + for realmID := range connections { + realmIDs = append(realmIDs, realmID) + } + sort.Slice(realmIDs, func(i, j int) bool { + return realmIDs[i] < realmIDs[j] + }) + return realmIDs +} From 957badd2a9f560c6c4ab0df0eae616f980cb62f7 Mon Sep 17 00:00:00 2001 From: VG-Prog Date: Fri, 22 May 2026 21:48:22 +0200 Subject: [PATCH 06/11] feat(Cluster/Groups): Add distributed group state Add clustered group authority for realm-scoped membership, native LFG materialized groups, member-state freshness, latest-state catch-up, receiver-aware fanout, offline cleanup, debug tracing, and persistent group identity support. --- apps/groupserver/cmd/groupserver/main.go | 22 +- apps/groupserver/repo/group.go | 48 +- apps/groupserver/repo/group_mysql.go | 216 +- apps/groupserver/repo/stmt.go | 25 +- apps/groupserver/server/group-debug-extra.go | 51 + apps/groupserver/server/group-logger_debug.go | 24 + apps/groupserver/server/group.go | 273 +- apps/groupserver/service/group-cache_inmem.go | 438 +- apps/groupserver/service/group.go | 3831 ++++++++++++++--- 9 files changed, 4264 insertions(+), 664 deletions(-) create mode 100644 apps/groupserver/server/group-debug-extra.go diff --git a/apps/groupserver/cmd/groupserver/main.go b/apps/groupserver/cmd/groupserver/main.go index 89cec08..4b5fa23 100644 --- a/apps/groupserver/cmd/groupserver/main.go +++ b/apps/groupserver/cmd/groupserver/main.go @@ -106,31 +106,23 @@ func createGroupService(cfg *config.Config, natsCon *nats.Conn) service.GroupsSe groupsRepo := repo.NewMysqlGroupsRepo(charDB) cache := service.NewInMemGroupsCache(groupsRepo) - err := events.NewGatewayConsumer( - natsCon, - events.WithGWConsumerLoggedInHandler(cache), - events.WithGWConsumerLoggedOutHandler(cache), - ).Listen() - if err != nil { - log.Fatal().Err(err).Msg("can't listen to gateway updates") - } - err = cache.Warmup(context.Background(), 1) - if err != nil { - log.Fatal().Err(err).Msg("can't warmup groups cache") + for realmID := range cfg.CharDBConnection { + if err := cache.Warmup(context.Background(), realmID); err != nil { + log.Fatal().Err(err).Uint32("realmID", realmID).Msg("can't warmup groups cache") + } } charClient := charService(cfg) s := service.NewGroupsService(cache, charClient, events.NewGroupServiceProducerNatsJSON(natsCon, groupserver.Ver)) - // TODO: combine this with consumer for cache - err = events.NewGatewayConsumer( + if err := events.NewGatewayConsumer( natsCon, + events.WithGWConsumerGatewayStartedHandler(s), events.WithGWConsumerLoggedInHandler(s), events.WithGWConsumerLoggedOutHandler(s), - ).Listen() - if err != nil { + ).Listen(); err != nil { log.Fatal().Err(err).Msg("can't listen to gateway updates") } diff --git a/apps/groupserver/repo/group.go b/apps/groupserver/repo/group.go index a06620f..d63c47f 100644 --- a/apps/groupserver/repo/group.go +++ b/apps/groupserver/repo/group.go @@ -2,6 +2,8 @@ package repo import ( "context" + + "github.com/walkline/ToCloud9/shared/wow/guid" ) const MaxTargetIcons = 8 @@ -9,9 +11,12 @@ const MaxTargetIcons = 8 type GroupTypeFlags uint8 const ( - GroupTypeFlagsNormal GroupTypeFlags = iota - GroupTypeFlagsBG GroupTypeFlags = 1 << (iota - 1) - GroupTypeFlagsRaid + // Mirrors AzerothCore GroupType flags in src/server/game/Groups/Group.h. + GroupTypeFlagsNormal GroupTypeFlags = 0x00 + GroupTypeFlagsBG GroupTypeFlags = 0x01 + GroupTypeFlagsRaid GroupTypeFlags = 0x02 + GroupTypeFlagsLFGRestricted GroupTypeFlags = 0x04 + GroupTypeFlagsLFG GroupTypeFlags = 0x08 ) const ( @@ -22,7 +27,7 @@ const ( type LootType uint8 const ( - LootTypeFreeForAll LootType = 0 + LootTypeFreeForAll LootType = iota LootTypeRoundRobin LootTypeMasterLoot LootTypeGroupLoot @@ -32,7 +37,7 @@ const ( type ItemQuality uint8 const ( - ItemQualityPoor ItemQuality = 0 + ItemQualityPoor ItemQuality = iota ItemQualityNormal ItemQualityUncommon ItemQualityRare @@ -43,6 +48,7 @@ const ( type Group struct { ID uint + RealmID uint32 LeaderGUID uint64 LootMethod uint8 LooterGUID uint64 @@ -52,13 +58,14 @@ type Group struct { Difficulty uint8 RaidDifficulty uint8 MasterLooterGuid uint64 + LfgDungeonEntry uint32 Members []GroupMember } -func (g *Group) MemberByGUID(guid uint64) *GroupMember { +func (g *Group) MemberByGUID(playerGUID uint64) *GroupMember { for i := range g.Members { - if g.Members[i].MemberGUID == guid { + if guid.SamePlayer(g.RealmID, g.Members[i].MemberGUID, g.RealmID, playerGUID) { return &g.Members[i] } } @@ -77,6 +84,10 @@ func (g *Group) IsRaid() bool { return g.GroupType&GroupTypeFlagsRaid > 0 } +func (g *Group) IsLFG() bool { + return g.GroupType&GroupTypeFlagsLFG > 0 +} + func (g *Group) OnlineMemberGUIDs() []uint64 { onlinePlayers := []uint64{} for _, member := range g.Members { @@ -89,6 +100,12 @@ func (g *Group) OnlineMemberGUIDs() []uint64 { type RoleFlags uint8 +const ( + MemberFlagAssistant uint8 = 1 << iota + MemberFlagMainTank + MemberFlagMainAssistant +) + const ( RoleFlagsAssistant RoleFlags = 1 << iota RoleFlagsMainTank @@ -97,6 +114,7 @@ const ( type GroupMember struct { GroupID uint + RealmID uint32 MemberGUID uint64 MemberFlags uint8 MemberName string @@ -106,17 +124,20 @@ type GroupMember struct { } func (m GroupMember) IsAssistant() bool { - return m.Roles&RoleFlagsAssistant > 0 + return m.MemberFlags&MemberFlagAssistant > 0 } type GroupInvite struct { - Inviter uint64 - InviterName string + Inviter uint64 + InviterRealmID uint32 + InviterName string - Invitee uint64 - InviteeName string + Invitee uint64 + InviteeRealmID uint32 + InviteeName string - GroupID uint + GroupID uint + GroupRealmID uint32 } type GroupsRepo interface { @@ -137,4 +158,5 @@ type GroupsRepo interface { AddInvite(ctx context.Context, realmID uint32, invite GroupInvite) error GetInviteByInvitedPlayer(ctx context.Context, realmID uint32, invitedPlayer uint64) (*GroupInvite, error) + RemoveInvite(ctx context.Context, realmID uint32, invitedPlayer uint64) error } diff --git a/apps/groupserver/repo/group_mysql.go b/apps/groupserver/repo/group_mysql.go index f891de4..1586dbe 100644 --- a/apps/groupserver/repo/group_mysql.go +++ b/apps/groupserver/repo/group_mysql.go @@ -6,6 +6,7 @@ import ( "fmt" shrepo "github.com/walkline/ToCloud9/shared/repo" + "github.com/walkline/ToCloud9/shared/wow/guid" ) type groupsRepoMysql struct { @@ -15,6 +16,7 @@ type groupsRepoMysql struct { func NewMysqlGroupsRepo(db shrepo.CharactersDB) GroupsRepo { db.SetPreparedStatement(StmtReplaceGroupInvite) db.SetPreparedStatement(StmtSelectGroupInviteByInvited) + db.SetPreparedStatement(StmtDeleteGroupInviteByInvited) db.SetPreparedStatement(StmtInsertNewGroup) db.SetPreparedStatement(StmtInsertNewGroupMember) db.SetPreparedStatement(StmtUpdateGroupWithID) @@ -22,6 +24,8 @@ func NewMysqlGroupsRepo(db shrepo.CharactersDB) GroupsRepo { db.SetPreparedStatement(StmtDeleteGroupMembersWithGroupID) db.SetPreparedStatement(StmtDeleteGroupWithID) db.SetPreparedStatement(StmtDeleteGroupMemberWithID) + db.SetPreparedStatement(StmtReplaceLfgData) + db.SetPreparedStatement(StmtDeleteLfgData) return &groupsRepoMysql{ db: db, @@ -29,11 +33,12 @@ func NewMysqlGroupsRepo(db shrepo.CharactersDB) GroupsRepo { } func (g groupsRepoMysql) LoadAllForRealm(ctx context.Context, realmID uint32) (map[uint]*Group, error) { - rows, err := g.db.DBByRealm(realmID).QueryContext(ctx, `SELECT - guid, leaderGuid, lootMethod, looterGuid, lootThreshold, - icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, - groupType, difficulty, raidDifficulty, masterLooterGuid -FROM `+"`groups`") + rows, err := g.db.DBByRealm(realmID).QueryContext(ctx, `SELECT + g.guid, g.leaderGuid, g.lootMethod, g.looterGuid, g.lootThreshold, + g.icon1, g.icon2, g.icon3, g.icon4, g.icon5, g.icon6, g.icon7, g.icon8, + g.groupType, g.difficulty, g.raidDifficulty, g.masterLooterGuid, COALESCE(ld.dungeon, 0) + FROM `+"`groups`"+` g + LEFT JOIN lfg_data ld ON ld.guid = g.guid`) if err != nil { return nil, err } @@ -47,11 +52,15 @@ FROM `+"`groups`") &group.ID, &group.LeaderGUID, &group.LootMethod, &group.LooterGUID, &group.LootThreshold, &group.TargetIcons[0], &group.TargetIcons[1], &group.TargetIcons[2], &group.TargetIcons[3], &group.TargetIcons[4], &group.TargetIcons[5], &group.TargetIcons[6], &group.TargetIcons[7], - &group.GroupType, &group.Difficulty, &group.RaidDifficulty, &group.MasterLooterGuid, + &group.GroupType, &group.Difficulty, &group.RaidDifficulty, &group.MasterLooterGuid, &group.LfgDungeonEntry, ) if err != nil { return nil, err } + group.RealmID = realmID + group.LeaderGUID = guid.NormalizePlayerGUIDForRealm(realmID, group.LeaderGUID) + group.LooterGUID = guid.NormalizePlayerGUIDForRealm(realmID, group.LooterGUID) + group.MasterLooterGuid = guid.NormalizePlayerGUIDForRealm(realmID, group.MasterLooterGuid) result[group.ID] = &group } @@ -63,7 +72,7 @@ FROM `+"`groups`") rows.Close() rows, err = g.db.DBByRealm(realmID).QueryContext(ctx, `SELECT - gm.guid, gm.memberGuid, gm.memberFlags, gm.subgroup, gm.roles, c.name, c.online + gm.guid, gm.memberGuid, gm.memberFlags, gm.subgroup, gm.roles, COALESCE(NULLIF(gm.memberName, ''), c.name, ''), COALESCE(c.online, 0) FROM group_member gm LEFT JOIN characters c ON c.guid = gm.memberGuid`) if err != nil { @@ -81,6 +90,8 @@ LEFT JOIN characters c ON c.guid = gm.memberGuid`) if err != nil { return nil, err } + gm.RealmID = guid.PlayerRealmIDOrDefault(realmID, gm.MemberGUID) + gm.MemberGUID = guid.PlayerGUIDForRealm(realmID, gm.RealmID, gm.MemberGUID) group := result[gm.GroupID] if group != nil { @@ -92,6 +103,7 @@ LEFT JOIN characters c ON c.guid = gm.memberGuid`) } func (g groupsRepoMysql) Create(ctx context.Context, realmID uint32, group *Group) error { + group.RealmID = realmID execRes, err := g.db.PreparedStatement(realmID, StmtInsertNewGroup).ExecContext( ctx, group.LeaderGUID, group.LootMethod, group.LooterGUID, group.LootThreshold, group.TargetIcons[0], group.TargetIcons[1], group.TargetIcons[2], group.TargetIcons[3], @@ -109,14 +121,20 @@ func (g groupsRepoMysql) Create(ctx context.Context, realmID uint32, group *Grou group.ID = uint(id) + if err := g.syncLfgData(ctx, realmID, group); err != nil { + return err + } + if len(group.Members) == 0 { return nil } for i, m := range group.Members { group.Members[i].GroupID = group.ID + group.Members[i].RealmID = guid.PlayerRealmIDOrDefault(realmID, m.MemberGUID) + group.Members[i].MemberGUID = guid.PlayerGUIDForRealm(realmID, group.Members[i].RealmID, m.MemberGUID) _, err = g.db.PreparedStatement(realmID, StmtInsertNewGroupMember).ExecContext( - ctx, group.ID, m.MemberGUID, m.MemberFlags, m.SubGroup, m.Roles, + ctx, group.ID, group.Members[i].MemberGUID, group.Members[i].MemberName, group.Members[i].MemberFlags, group.Members[i].SubGroup, group.Members[i].Roles, ) if err != nil { return fmt.Errorf("can't insert group member, err: %w", err) @@ -127,39 +145,167 @@ func (g groupsRepoMysql) Create(ctx context.Context, realmID uint32, group *Grou } func (g groupsRepoMysql) GroupByID(ctx context.Context, realmID uint32, partyID uint, loadMembers bool) (*Group, error) { - //TODO implement me - panic("implement me") + group := Group{} + row := g.db.DBByRealm(realmID).QueryRowContext(ctx, `SELECT + g.guid, g.leaderGuid, g.lootMethod, g.looterGuid, g.lootThreshold, + g.icon1, g.icon2, g.icon3, g.icon4, g.icon5, g.icon6, g.icon7, g.icon8, + g.groupType, g.difficulty, g.raidDifficulty, g.masterLooterGuid, COALESCE(ld.dungeon, 0) + FROM `+"`groups`"+` g + LEFT JOIN lfg_data ld ON ld.guid = g.guid + WHERE g.guid = ?`, partyID) + + err := row.Scan( + &group.ID, &group.LeaderGUID, &group.LootMethod, &group.LooterGUID, &group.LootThreshold, + &group.TargetIcons[0], &group.TargetIcons[1], &group.TargetIcons[2], &group.TargetIcons[3], + &group.TargetIcons[4], &group.TargetIcons[5], &group.TargetIcons[6], &group.TargetIcons[7], + &group.GroupType, &group.Difficulty, &group.RaidDifficulty, &group.MasterLooterGuid, &group.LfgDungeonEntry, + ) + if err == sql.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, err + } + group.RealmID = realmID + group.LeaderGUID = guid.NormalizePlayerGUIDForRealm(realmID, group.LeaderGUID) + group.LooterGUID = guid.NormalizePlayerGUIDForRealm(realmID, group.LooterGUID) + group.MasterLooterGuid = guid.NormalizePlayerGUIDForRealm(realmID, group.MasterLooterGuid) + + if !loadMembers { + return &group, nil + } + + rows, err := g.db.DBByRealm(realmID).QueryContext(ctx, `SELECT + gm.guid, gm.memberGuid, gm.memberFlags, gm.subgroup, gm.roles, COALESCE(NULLIF(gm.memberName, ''), c.name, ''), COALESCE(c.online, 0) +FROM group_member gm +LEFT JOIN characters c ON c.guid = gm.memberGuid +WHERE gm.guid = ? +ORDER BY gm.memberGuid ASC`, partyID) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + gm := GroupMember{} + err = rows.Scan( + &gm.GroupID, &gm.MemberGUID, &gm.MemberFlags, + &gm.SubGroup, &gm.Roles, &gm.MemberName, &gm.IsOnline, + ) + if err != nil { + return nil, err + } + gm.RealmID = guid.PlayerRealmIDOrDefault(realmID, gm.MemberGUID) + gm.MemberGUID = guid.PlayerGUIDForRealm(realmID, gm.RealmID, gm.MemberGUID) + + group.Members = append(group.Members, gm) + } + + if rows.Err() != nil { + return nil, rows.Err() + } + + return &group, nil } func (g groupsRepoMysql) GroupIDByPlayer(ctx context.Context, realmID uint32, player uint64) (uint, error) { - panic("implement me") + player = guid.PlayerGUIDForRealm(realmID, guid.PlayerRealmIDOrDefault(realmID, player), player) + var groupID uint + err := g.db.DBByRealm(realmID).QueryRowContext(ctx, ` +SELECT guid +FROM group_member +WHERE memberGuid = ? +LIMIT 1`, player).Scan(&groupID) + if err == sql.ErrNoRows { + return 0, nil + } + if err != nil { + return 0, err + } + + return groupID, nil } func (g groupsRepoMysql) AddMember(ctx context.Context, realmID uint32, m *GroupMember) error { + m.RealmID = guid.PlayerRealmIDOrDefault(realmID, m.MemberGUID) + m.MemberGUID = guid.PlayerGUIDForRealm(realmID, m.RealmID, m.MemberGUID) _, err := g.db.PreparedStatement(realmID, StmtInsertNewGroupMember).ExecContext( - ctx, m.GroupID, m.MemberGUID, m.MemberFlags, m.SubGroup, m.Roles, + ctx, m.GroupID, m.MemberGUID, m.MemberName, m.MemberFlags, m.SubGroup, m.Roles, ) return err } func (g groupsRepoMysql) Update(ctx context.Context, realmID uint32, group *Group) error { + group.RealmID = realmID _, err := g.db.PreparedStatement(realmID, StmtUpdateGroupWithID).ExecContext( ctx, group.LeaderGUID, group.LootMethod, group.LooterGUID, group.LootThreshold, group.TargetIcons[0], group.TargetIcons[1], group.TargetIcons[2], group.TargetIcons[3], group.TargetIcons[4], group.TargetIcons[5], group.TargetIcons[6], group.TargetIcons[7], group.GroupType, group.Difficulty, group.RaidDifficulty, group.MasterLooterGuid, group.ID, ) - return err + if err != nil { + return err + } + return g.syncLfgData(ctx, realmID, group) +} + +func (g groupsRepoMysql) RegisterAcceptedLfgGroup(ctx context.Context, realmID uint32, group *Group) error { + if group == nil { + return nil + } + + members := group.Members + if group.ID == 0 { + group.Members = nil + if err := g.Create(ctx, realmID, group); err != nil { + group.Members = members + return err + } + group.Members = members + } else { + if err := g.Update(ctx, realmID, group); err != nil { + return err + } + + if _, err := g.db.PreparedStatement(realmID, StmtDeleteGroupMembersWithGroupID).ExecContext(ctx, group.ID); err != nil { + return err + } + } + + for i, member := range members { + group.Members[i].GroupID = group.ID + group.Members[i].RealmID = guid.PlayerRealmIDOrDefault(realmID, member.MemberGUID) + group.Members[i].MemberGUID = guid.PlayerGUIDForRealm(realmID, group.Members[i].RealmID, member.MemberGUID) + if _, err := g.db.PreparedStatement(realmID, StmtDeleteGroupMemberWithID).ExecContext(ctx, group.Members[i].MemberGUID); err != nil { + return err + } + if _, err := g.db.PreparedStatement(realmID, StmtInsertNewGroupMember).ExecContext( + ctx, + group.ID, + group.Members[i].MemberGUID, + group.Members[i].MemberName, + group.Members[i].MemberFlags, + group.Members[i].SubGroup, + group.Members[i].Roles, + ); err != nil { + return fmt.Errorf("can't insert accepted LFG group member, err: %w", err) + } + } + + return nil } func (g groupsRepoMysql) UpdateMember(ctx context.Context, realmID uint32, m *GroupMember) error { + m.RealmID = guid.PlayerRealmIDOrDefault(realmID, m.MemberGUID) + m.MemberGUID = guid.PlayerGUIDForRealm(realmID, m.RealmID, m.MemberGUID) _, err := g.db.PreparedStatement(realmID, StmtUpdateGroupMemberWithID).ExecContext( - ctx, m.GroupID, m.MemberFlags, m.SubGroup, m.Roles, m.MemberGUID, + ctx, m.GroupID, m.MemberName, m.MemberFlags, m.SubGroup, m.Roles, m.MemberGUID, ) return err } func (g groupsRepoMysql) RemoveMember(ctx context.Context, realmID uint32, memberGUID uint64) error { + memberGUID = guid.PlayerGUIDForRealm(realmID, guid.PlayerRealmIDOrDefault(realmID, memberGUID), memberGUID) _, err := g.db.PreparedStatement(realmID, StmtDeleteGroupMemberWithID).ExecContext( ctx, memberGUID, ) @@ -173,33 +319,69 @@ func (g groupsRepoMysql) Delete(ctx context.Context, realmID uint32, groupID uin if err != nil { return err } + if _, err = g.db.PreparedStatement(realmID, StmtDeleteLfgData).ExecContext(ctx, groupID); err != nil { + return err + } _, err = g.db.PreparedStatement(realmID, StmtDeleteGroupWithID).ExecContext( ctx, groupID, ) return err } +func (g groupsRepoMysql) syncLfgData(ctx context.Context, realmID uint32, group *Group) error { + if group == nil || group.ID == 0 || group.LfgDungeonEntry == 0 || group.GroupType&GroupTypeFlagsLFG == 0 { + return nil + } + + const lfgStateDungeon uint8 = 5 // Mirrors AzerothCore lfg::LFG_STATE_DUNGEON. + _, err := g.db.PreparedStatement(realmID, StmtReplaceLfgData).ExecContext(ctx, group.ID, group.LfgDungeonEntry, lfgStateDungeon) + return err +} + func (g groupsRepoMysql) AddInvite(ctx context.Context, realmID uint32, invite GroupInvite) error { + if invite.InviterRealmID == 0 { + invite.InviterRealmID = guid.PlayerRealmIDOrDefault(realmID, invite.Inviter) + } + invite.Inviter = guid.PlayerGUIDForRealm(realmID, invite.InviterRealmID, invite.Inviter) + if invite.InviteeRealmID == 0 { + invite.InviteeRealmID = guid.PlayerRealmIDOrDefault(realmID, invite.Invitee) + } + invite.Invitee = guid.PlayerGUIDForRealm(realmID, invite.InviteeRealmID, invite.Invitee) + if invite.GroupRealmID == 0 { + invite.GroupRealmID = realmID + } _, err := g.db.PreparedStatement(realmID, StmtReplaceGroupInvite).ExecContext( - ctx, invite.Invitee, invite.Inviter, invite.GroupID, invite.InviteeName, invite.InviterName, + ctx, invite.Invitee, invite.Inviter, invite.GroupID, invite.InviteeName, invite.InviterName, invite.GroupRealmID, ) return err } func (g groupsRepoMysql) GetInviteByInvitedPlayer(ctx context.Context, realmID uint32, invitedPlayer uint64) (*GroupInvite, error) { + invitedPlayer = guid.PlayerGUIDForRealm(realmID, guid.PlayerRealmIDOrDefault(realmID, invitedPlayer), invitedPlayer) row := g.db.PreparedStatement(realmID, StmtSelectGroupInviteByInvited).QueryRowContext(ctx, invitedPlayer) groupInvite := GroupInvite{ - Invitee: invitedPlayer, + Invitee: invitedPlayer, + InviteeRealmID: guid.PlayerRealmIDOrDefault(realmID, invitedPlayer), } - err := row.Scan(&groupInvite.Inviter, &groupInvite.GroupID, &groupInvite.InviteeName, &groupInvite.InviterName) + err := row.Scan(&groupInvite.Inviter, &groupInvite.GroupID, &groupInvite.InviteeName, &groupInvite.InviterName, &groupInvite.GroupRealmID) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } + groupInvite.InviterRealmID = guid.PlayerRealmIDOrDefault(realmID, groupInvite.Inviter) + if groupInvite.GroupRealmID == 0 { + groupInvite.GroupRealmID = realmID + } return &groupInvite, nil } + +func (g groupsRepoMysql) RemoveInvite(ctx context.Context, realmID uint32, invitedPlayer uint64) error { + invitedPlayer = guid.PlayerGUIDForRealm(realmID, guid.PlayerRealmIDOrDefault(realmID, invitedPlayer), invitedPlayer) + _, err := g.db.PreparedStatement(realmID, StmtDeleteGroupInviteByInvited).ExecContext(ctx, invitedPlayer) + return err +} diff --git a/apps/groupserver/repo/stmt.go b/apps/groupserver/repo/stmt.go index bcc0c8e..a150b5a 100644 --- a/apps/groupserver/repo/stmt.go +++ b/apps/groupserver/repo/stmt.go @@ -13,6 +13,9 @@ const ( // StmtSelectGroupInviteByInvited selects group invite by invited GUID. StmtSelectGroupInviteByInvited + // StmtDeleteGroupInviteByInvited deletes group invite by invited GUID. + StmtDeleteGroupInviteByInvited + // StmtInsertNewGroup inserts new group record. StmtInsertNewGroup @@ -33,6 +36,12 @@ const ( // StmtDeleteGroupMembersWithGroupID deletes group members with given guild ID. StmtDeleteGroupMembersWithGroupID + + // StmtReplaceLfgData creates or replaces native LFG data for a group. + StmtReplaceLfgData + + // StmtDeleteLfgData deletes native LFG data for a group. + StmtDeleteLfgData ) // ID returns identifier of prepared statement. @@ -44,9 +53,11 @@ func (s CharsPreparedStatements) ID() uint32 { func (s CharsPreparedStatements) Stmt() string { switch s { case StmtReplaceGroupInvite: - return "REPLACE INTO group_invites (invited, inviter, groupId, invitedName, inviterName) VALUES (?, ?, ?, ?, ?)" + return "REPLACE INTO group_invites (invited, inviter, groupId, invitedName, inviterName, groupRealmId) VALUES (?, ?, ?, ?, ?, ?)" case StmtSelectGroupInviteByInvited: - return "SELECT inviter, groupId, invitedName, inviterName FROM group_invites WHERE invited = ?" + return "SELECT inviter, groupId, invitedName, inviterName, groupRealmId FROM group_invites WHERE invited = ?" + case StmtDeleteGroupInviteByInvited: + return "DELETE FROM group_invites WHERE invited = ?" case StmtInsertNewGroup: return `INSERT INTO ` + "`groups`" + `(leaderGuid, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, groupType, difficulty, raidDifficulty, masterLooterGuid) @@ -60,11 +71,11 @@ func (s CharsPreparedStatements) Stmt() string { groupType = ?, difficulty = ?, raidDifficulty = ?, masterLooterGuid = ? WHERE guid = ?` case StmtInsertNewGroupMember: - return `INSERT INTO group_member(guid, memberGuid, memberFlags, subgroup, roles) - VALUES (?, ?, ?, ?, ?)` + return `INSERT INTO group_member(guid, memberGuid, memberName, memberFlags, subgroup, roles) + VALUES (?, ?, ?, ?, ?, ?)` case StmtUpdateGroupMemberWithID: return `UPDATE group_member - SET guid = ?, memberFlags = ?, subgroup = ?, roles = ? + SET guid = ?, memberName = ?, memberFlags = ?, subgroup = ?, roles = ? WHERE memberGuid = ?` case StmtDeleteGroupMemberWithID: return "DELETE FROM group_member WHERE memberGuid = ?" @@ -72,6 +83,10 @@ func (s CharsPreparedStatements) Stmt() string { return "DELETE FROM `groups` WHERE guid = ?" case StmtDeleteGroupMembersWithGroupID: return "DELETE FROM group_member WHERE guid = ?" + case StmtReplaceLfgData: + return "REPLACE INTO lfg_data (guid, dungeon, state) VALUES (?, ?, ?)" + case StmtDeleteLfgData: + return "DELETE FROM lfg_data WHERE guid = ?" } panic(fmt.Errorf("unk stmt %d", s)) } diff --git a/apps/groupserver/server/group-debug-extra.go b/apps/groupserver/server/group-debug-extra.go new file mode 100644 index 0000000..7fac69d --- /dev/null +++ b/apps/groupserver/server/group-debug-extra.go @@ -0,0 +1,51 @@ +package server + +import ( + "context" + + "github.com/walkline/ToCloud9/gen/group/pb" +) + +func (g groupDebugLoggerMiddleware) StartReadyCheck(ctx context.Context, params *pb.StartReadyCheckRequest) (*pb.StartReadyCheckResponse, error) { + return g.realServer.StartReadyCheck(ctx, params) +} + +func (g groupDebugLoggerMiddleware) SetReadyCheckMemberState(ctx context.Context, params *pb.SetReadyCheckMemberStateRequest) (*pb.SetReadyCheckMemberStateResponse, error) { + return g.realServer.SetReadyCheckMemberState(ctx, params) +} + +func (g groupDebugLoggerMiddleware) FinishReadyCheck(ctx context.Context, params *pb.FinishReadyCheckRequest) (*pb.FinishReadyCheckResponse, error) { + return g.realServer.FinishReadyCheck(ctx, params) +} + +func (g groupDebugLoggerMiddleware) ChangeMemberSubGroup(ctx context.Context, params *pb.ChangeMemberSubGroupRequest) (*pb.ChangeMemberSubGroupResponse, error) { + return g.realServer.ChangeMemberSubGroup(ctx, params) +} + +func (g groupDebugLoggerMiddleware) SetMemberFlags(ctx context.Context, params *pb.SetMemberFlagsRequest) (*pb.SetMemberFlagsResponse, error) { + return g.realServer.SetMemberFlags(ctx, params) +} + +func (g groupDebugLoggerMiddleware) RegisterAcceptedLfgGroup(ctx context.Context, params *pb.RegisterAcceptedLfgGroupRequest) (*pb.RegisterAcceptedLfgGroupResponse, error) { + return g.realServer.RegisterAcceptedLfgGroup(ctx, params) +} + +func (g groupDebugLoggerMiddleware) RegisterMaterializedLfgGroup(ctx context.Context, params *pb.RegisterMaterializedLfgGroupRequest) (*pb.RegisterMaterializedLfgGroupResponse, error) { + return g.realServer.RegisterMaterializedLfgGroup(ctx, params) +} + +func (g groupDebugLoggerMiddleware) UpdateMemberState(ctx context.Context, params *pb.UpdateMemberStateRequest) (*pb.UpdateMemberStateResponse, error) { + return g.realServer.UpdateMemberState(ctx, params) +} + +func (g groupDebugLoggerMiddleware) BulkUpdateMemberStates(ctx context.Context, params *pb.BulkUpdateMemberStatesRequest) (*pb.BulkUpdateMemberStatesResponse, error) { + return g.realServer.BulkUpdateMemberStates(ctx, params) +} + +func (g groupDebugLoggerMiddleware) ResetInstance(ctx context.Context, params *pb.ResetInstanceRequest) (*pb.ResetInstanceResponse, error) { + return g.realServer.ResetInstance(ctx, params) +} + +func (g groupDebugLoggerMiddleware) SetInstanceBindExtension(ctx context.Context, params *pb.SetInstanceBindExtensionRequest) (*pb.SetInstanceBindExtensionResponse, error) { + return g.realServer.SetInstanceBindExtension(ctx, params) +} diff --git a/apps/groupserver/server/group-logger_debug.go b/apps/groupserver/server/group-logger_debug.go index d50ed7b..f8bf41f 100644 --- a/apps/groupserver/server/group-logger_debug.go +++ b/apps/groupserver/server/group-logger_debug.go @@ -73,6 +73,18 @@ func (g groupDebugLoggerMiddleware) AcceptInvite(ctx context.Context, params *pb return } +func (g groupDebugLoggerMiddleware) DeclineInvite(ctx context.Context, params *pb.DeclineInviteParams) (res *pb.DeclineInviteResponse, err error) { + defer func(t time.Time) { + g.logger.Debug(). + Uint64("player", params.Player). + Err(err). + Msgf("Handled DeclineInvite for %v.", time.Since(t)) + }(time.Now()) + + res, err = g.realServer.DeclineInvite(ctx, params) + return +} + func (g groupDebugLoggerMiddleware) Uninvite(ctx context.Context, params *pb.UninviteParams) (res *pb.UninviteResponse, err error) { defer func(t time.Time) { g.logger.Debug(). @@ -147,6 +159,18 @@ func (g groupDebugLoggerMiddleware) GetGroupByMember(ctx context.Context, params return } +func (g groupDebugLoggerMiddleware) GetMemberPlacements(ctx context.Context, params *pb.GetMemberPlacementsRequest) (res *pb.GetMemberPlacementsResponse, err error) { + defer func(t time.Time) { + g.logger.Debug(). + Int("members", len(params.MemberGUIDs)). + Err(err). + Msgf("Handled GetMemberPlacements for %v.", time.Since(t)) + }(time.Now()) + + res, err = g.realServer.GetMemberPlacements(ctx, params) + return +} + func (g groupDebugLoggerMiddleware) SetGroupTargetIcon(ctx context.Context, params *pb.SetGroupTargetIconRequest) (res *pb.SetGroupTargetIconResponse, err error) { defer func(t time.Time) { g.logger.Debug(). diff --git a/apps/groupserver/server/group.go b/apps/groupserver/server/group.go index c0aaae0..b433f17 100644 --- a/apps/groupserver/server/group.go +++ b/apps/groupserver/server/group.go @@ -54,17 +54,46 @@ func (g GroupServer) GetGroupByMember(ctx context.Context, request *pb.GetGroupB } func (g GroupServer) GetGroupIDByPlayer(ctx context.Context, request *pb.GetGroupIDByPlayerRequest) (*pb.GetGroupIDByPlayerResponse, error) { - groupID, err := g.groupService.GroupIDByPlayer(ctx, request.RealmID, request.Player) + groupRealmID, groupID, err := g.groupService.GroupRealmIDByPlayer(ctx, request.RealmID, request.Player) if err != nil { return nil, err } return &pb.GetGroupIDByPlayerResponse{ - Api: groupserver.Ver, - GroupID: uint32(groupID), + Api: groupserver.Ver, + GroupID: uint32(groupID), + GroupRealmID: groupRealmID, }, nil } +func (g GroupServer) GetMemberPlacements(ctx context.Context, request *pb.GetMemberPlacementsRequest) (*pb.GetMemberPlacementsResponse, error) { + placements, err := g.groupService.MemberPlacements(ctx, request.RealmID, request.MemberGUIDs) + if err != nil { + return nil, err + } + + res := &pb.GetMemberPlacementsResponse{ + Api: groupserver.Ver, + Placements: make([]*pb.MemberPlacement, 0, len(placements)), + } + for _, placement := range placements { + res.Placements = append(res.Placements, &pb.MemberPlacement{ + MemberGUID: placement.MemberGUID, + Online: placement.Online, + Fresh: placement.Fresh, + GatewayID: placement.GatewayID, + WorldserverID: placement.WorldserverID, + MapID: placement.MapID, + InstanceID: placement.InstanceID, + InstanceKnown: placement.InstanceKnown, + TimestampMs: placement.TimestampMs, + UpdatedAtMs: placement.UpdatedAtMs, + }) + } + + return res, nil +} + func (g GroupServer) Invite(ctx context.Context, params *pb.InviteParams) (*pb.InviteResponse, error) { err := g.groupService.Invite(ctx, params.RealmID, params.Inviter, params.Invited, params.InviterName, params.InvitedName) if err != nil { @@ -96,6 +125,23 @@ func (g GroupServer) AcceptInvite(ctx context.Context, params *pb.AcceptInvitePa }, nil } +func (g GroupServer) DeclineInvite(ctx context.Context, params *pb.DeclineInviteParams) (*pb.DeclineInviteResponse, error) { + status := pb.DeclineInviteResponse_Ok + err := g.groupService.DeclineInvite(ctx, params.RealmID, params.Player) + if err != nil { + if errors.Is(err, service.ErrInviteNotFound) { + status = pb.DeclineInviteResponse_InviteNotFound + } else { + return nil, err + } + } + + return &pb.DeclineInviteResponse{ + Api: groupserver.Ver, + Status: status, + }, nil +} + func (g GroupServer) Uninvite(ctx context.Context, params *pb.UninviteParams) (*pb.UninviteResponse, error) { err := g.groupService.Uninvite(ctx, params.RealmID, params.Initiator, params.Target, params.Reason) if err != nil { @@ -151,6 +197,7 @@ func (g GroupServer) SendMessage(ctx context.Context, params *pb.SendGroupMessag params.Message, params.Language, service.MessageType(params.MessageType), + uint8(params.SenderChatTag), ) if err != nil { return nil, err @@ -228,7 +275,8 @@ func (g GroupServer) getGroupResponse(group *repo.Group) *pb.GetGroupResponse { Name: member.MemberName, IsOnline: member.IsOnline, SubGroup: uint32(member.SubGroup), - Roles: uint32(member.SubGroup), + Roles: uint32(member.Roles), + RealmID: member.RealmID, } } @@ -246,6 +294,223 @@ func (g GroupServer) getGroupResponse(group *repo.Group) *pb.GetGroupResponse { MasterLooter: group.MasterLooterGuid, Members: members, TargetIconsList: group.TargetIcons[:], + RealmID: group.RealmID, }, } } + +func (g GroupServer) StartReadyCheck(ctx context.Context, params *pb.StartReadyCheckRequest) (*pb.StartReadyCheckResponse, error) { + err := g.groupService.StartReadyCheck(ctx, params.RealmID, params.LeaderGUID, params.DurationMs) + if err != nil { + return nil, err + } + + return &pb.StartReadyCheckResponse{Api: groupserver.Ver}, nil +} + +func (g GroupServer) SetReadyCheckMemberState(ctx context.Context, params *pb.SetReadyCheckMemberStateRequest) (*pb.SetReadyCheckMemberStateResponse, error) { + err := g.groupService.SetReadyCheckMemberState(ctx, params.RealmID, params.MemberGUID, uint8(params.State)) + if err != nil { + return nil, err + } + + return &pb.SetReadyCheckMemberStateResponse{Api: groupserver.Ver}, nil +} + +func (g GroupServer) FinishReadyCheck(ctx context.Context, params *pb.FinishReadyCheckRequest) (*pb.FinishReadyCheckResponse, error) { + err := g.groupService.FinishReadyCheck(ctx, params.RealmID, params.PlayerGUID) + if err != nil { + return nil, err + } + + return &pb.FinishReadyCheckResponse{Api: groupserver.Ver}, nil +} + +func (g GroupServer) ChangeMemberSubGroup(ctx context.Context, params *pb.ChangeMemberSubGroupRequest) (*pb.ChangeMemberSubGroupResponse, error) { + err := g.groupService.ChangeMemberSubGroup(ctx, params.RealmID, params.UpdaterGUID, params.MemberGUID, uint8(params.SubGroup)) + if err != nil { + return nil, err + } + + return &pb.ChangeMemberSubGroupResponse{Api: groupserver.Ver}, nil +} + +func (g GroupServer) SetMemberFlags(ctx context.Context, params *pb.SetMemberFlagsRequest) (*pb.SetMemberFlagsResponse, error) { + err := g.groupService.SetMemberFlags(ctx, params.RealmID, params.UpdaterGUID, params.MemberGUID, uint8(params.Flags), uint8(params.Roles)) + if err != nil { + return nil, err + } + + return &pb.SetMemberFlagsResponse{Api: groupserver.Ver}, nil +} + +func (g GroupServer) RegisterAcceptedLfgGroup(ctx context.Context, params *pb.RegisterAcceptedLfgGroupRequest) (*pb.RegisterAcceptedLfgGroupResponse, error) { + members := make([]service.AcceptedLfgGroupMember, 0, len(params.Members)) + for _, member := range params.Members { + if member == nil { + continue + } + members = append(members, service.AcceptedLfgGroupMember{ + RealmID: member.RealmID, + PlayerGUID: member.PlayerGUID, + SelectedRoles: uint8(member.SelectedRoles), + AssignedRole: uint8(member.AssignedRole), + QueueLeaderRealmID: member.QueueLeaderRealmID, + QueueLeaderGUID: member.QueueLeaderGUID, + }) + } + + groupID, err := g.groupService.RegisterAcceptedLfgGroup( + ctx, + params.RealmID, + params.ProposalID, + params.DungeonEntry, + params.LeaderRealmID, + params.LeaderGUID, + params.CrossRealm, + members, + ) + if err != nil { + return nil, err + } + + return &pb.RegisterAcceptedLfgGroupResponse{Api: groupserver.Ver, GroupID: uint32(groupID)}, nil +} + +func (g GroupServer) RegisterMaterializedLfgGroup(ctx context.Context, params *pb.RegisterMaterializedLfgGroupRequest) (*pb.RegisterMaterializedLfgGroupResponse, error) { + members := make([]service.MaterializedLfgGroupMember, 0, len(params.Members)) + for _, member := range params.Members { + if member == nil { + continue + } + members = append(members, service.MaterializedLfgGroupMember{ + RealmID: member.RealmID, + PlayerGUID: member.PlayerGUID, + Name: member.Name, + Online: member.IsOnline, + Flags: uint8(member.Flags), + Roles: uint8(member.Roles), + SubGroup: uint8(member.SubGroup), + }) + } + + err := g.groupService.RegisterMaterializedLfgGroup( + ctx, + params.RealmID, + uint(params.GroupID), + params.LeaderGUID, + uint8(params.GroupType), + uint8(params.Difficulty), + uint8(params.RaidDifficulty), + members, + ) + if err != nil { + return nil, err + } + + return &pb.RegisterMaterializedLfgGroupResponse{Api: groupserver.Ver}, nil +} + +func (g GroupServer) UpdateMemberState(ctx context.Context, params *pb.UpdateMemberStateRequest) (*pb.UpdateMemberStateResponse, error) { + err := g.groupService.UpdateMemberState( + ctx, + params.RealmID, + params.MemberGUID, + params.Online, + uint8(params.Level), + uint8(params.ClassID), + params.ZoneID, + params.MapID, + params.Health, + params.MaxHealth, + uint8(params.PowerType), + params.Power, + params.MaxPower, + params.InstanceID, + ) + if err != nil { + return nil, err + } + + return &pb.UpdateMemberStateResponse{Api: groupserver.Ver}, nil +} + +func (g GroupServer) BulkUpdateMemberStates(ctx context.Context, params *pb.BulkUpdateMemberStatesRequest) (*pb.BulkUpdateMemberStatesResponse, error) { + snapshots := make([]service.MemberStateSnapshot, 0, len(params.Snapshots)) + for _, snapshot := range params.Snapshots { + if snapshot == nil { + continue + } + + snapshots = append(snapshots, service.MemberStateSnapshot{ + MemberGUID: snapshot.MemberGUID, + Online: snapshot.Online, + Level: uint8(snapshot.Level), + Class: uint8(snapshot.ClassID), + ZoneID: snapshot.ZoneID, + MapID: snapshot.MapID, + Health: snapshot.Health, + MaxHealth: snapshot.MaxHealth, + PowerType: uint8(snapshot.PowerType), + Power: snapshot.Power, + MaxPower: snapshot.MaxPower, + InstanceID: snapshot.InstanceID, + AurasKnown: snapshot.AurasKnown, + Auras: protoMemberAuras(snapshot.Auras), + TimestampMs: snapshot.TimestampMs, + Dead: snapshot.Dead, + Ghost: snapshot.Ghost, + }) + } + + err := g.groupService.BulkUpdateMemberStates( + ctx, + params.RealmID, + params.SourceGatewayID, + params.SourceWorldserverID, + snapshots, + ) + if err != nil { + return nil, err + } + + return &pb.BulkUpdateMemberStatesResponse{Api: groupserver.Ver}, nil +} + +func protoMemberAuras(auras []*pb.PlayerAuraSnapshot) []service.MemberAuraState { + if len(auras) == 0 { + return nil + } + + out := make([]service.MemberAuraState, 0, len(auras)) + for _, aura := range auras { + if aura == nil { + continue + } + out = append(out, service.MemberAuraState{ + Slot: uint8(aura.Slot), + SpellID: aura.SpellID, + Flags: uint8(aura.Flags), + }) + } + + return out +} + +func (g GroupServer) ResetInstance(ctx context.Context, params *pb.ResetInstanceRequest) (*pb.ResetInstanceResponse, error) { + err := g.groupService.ResetInstance(ctx, params.RealmID, params.PlayerGUID, params.MapID, uint8(params.Difficulty)) + if err != nil { + return nil, err + } + + return &pb.ResetInstanceResponse{Api: groupserver.Ver}, nil +} + +func (g GroupServer) SetInstanceBindExtension(ctx context.Context, params *pb.SetInstanceBindExtensionRequest) (*pb.SetInstanceBindExtensionResponse, error) { + err := g.groupService.SetInstanceBindExtension(ctx, params.RealmID, params.PlayerGUID, params.MapID, uint8(params.Difficulty), params.Extended) + if err != nil { + return nil, err + } + + return &pb.SetInstanceBindExtensionResponse{Api: groupserver.Ver}, nil +} diff --git a/apps/groupserver/service/group-cache_inmem.go b/apps/groupserver/service/group-cache_inmem.go index b883121..d5e322e 100644 --- a/apps/groupserver/service/group-cache_inmem.go +++ b/apps/groupserver/service/group-cache_inmem.go @@ -7,6 +7,7 @@ import ( "github.com/walkline/ToCloud9/apps/groupserver/repo" "github.com/walkline/ToCloud9/shared/events" + "github.com/walkline/ToCloud9/shared/wow/guid" ) const MaxRealmID = 256 @@ -24,6 +25,11 @@ type groupsCacheInMem struct { // Holds reference to the member object inside groupsCache, // so modifying object here modifies it in groupsCache as well. groupMembersCache [MaxRealmID]map[uint64]*repo.GroupMember + + // groupMemberHomeRealmCache stores the realm that owns the group row for a + // member identity. The first index is the member's own realm, not the group + // realm. + groupMemberHomeRealmCache [MaxRealmID]map[uint64]uint32 } // NewInMemGroupsCache creates in memory groups cache. @@ -38,111 +44,257 @@ func (g *groupsCacheInMem) LoadAllForRealm(ctx context.Context, realmID uint32) } func (g *groupsCacheInMem) GroupByID(ctx context.Context, realmID uint32, groupID uint, loadMembers bool) (r *repo.Group, err error) { + if err = validateCacheRealmID(realmID); err != nil { + return nil, err + } + g.cacheLock.RLock() - r = g.groupsCache[realmID][groupID] + if g.groupsCache[realmID] != nil { + r = cloneGroup(g.groupsCache[realmID][groupID]) + } g.cacheLock.RUnlock() return } func (g *groupsCacheInMem) GroupIDByPlayer(ctx context.Context, realmID uint32, player uint64) (uint, error) { - member := g.groupMemberByGUID(realmID, player) + memberRealmID, memberGUID, err := cacheMemberKey(realmID, player) + if err != nil { + return 0, err + } + + g.cacheLock.RLock() + defer g.cacheLock.RUnlock() + + if g.groupMembersCache[memberRealmID] == nil { + return 0, nil + } + member := g.groupMembersCache[memberRealmID][memberGUID] if member == nil { return 0, nil } + return member.GroupID, nil } +func (g *groupsCacheInMem) GroupRealmIDByPlayer(ctx context.Context, realmID uint32, player uint64) (uint32, uint, error) { + memberRealmID, memberGUID, err := cacheMemberKey(realmID, player) + if err != nil { + return 0, 0, err + } + + g.cacheLock.RLock() + defer g.cacheLock.RUnlock() + + member := (*repo.GroupMember)(nil) + if g.groupMembersCache[memberRealmID] != nil { + member = g.groupMembersCache[memberRealmID][memberGUID] + } + if member == nil { + return 0, 0, nil + } + + groupRealmID := realmID + if g.groupMemberHomeRealmCache[memberRealmID] != nil { + if cachedRealmID, ok := g.groupMemberHomeRealmCache[memberRealmID][memberGUID]; ok { + groupRealmID = cachedRealmID + } + } + + return groupRealmID, member.GroupID, nil +} + func (g *groupsCacheInMem) Create(ctx context.Context, realmID uint32, group *repo.Group) error { + if err := validateCacheRealmID(realmID); err != nil { + return err + } + err := g.r.Create(ctx, realmID, group) if err != nil { return err } g.cacheLock.Lock() - g.groupsCache[realmID][group.ID] = group - - for i, member := range group.Members { - g.groupMembersCache[realmID][member.MemberGUID] = &group.Members[i] - } - + g.ensureRealmCacheLocked(realmID) + cachedGroup := cloneGroup(group) + normalizeGroupForRealm(cachedGroup, realmID) + g.groupsCache[realmID][cachedGroup.ID] = cachedGroup + g.rebuildGroupMembersCacheLocked(realmID, cachedGroup) g.cacheLock.Unlock() return nil } func (g *groupsCacheInMem) Delete(ctx context.Context, realmID uint32, groupID uint) error { - if err := g.r.Delete(ctx, realmID, groupID); err != nil { + if err := validateCacheRealmID(realmID); err != nil { return err } - group, err := g.GroupByID(ctx, realmID, groupID, true) - if err != nil { + if err := g.r.Delete(ctx, realmID, groupID); err != nil { return err } g.cacheLock.Lock() + defer g.cacheLock.Unlock() - for _, member := range group.Members { - delete(g.groupMembersCache[realmID], member.MemberGUID) + if g.groupsCache[realmID] == nil { + return nil + } + group := g.groupsCache[realmID][groupID] + if group == nil { + return nil } - delete(g.groupsCache[realmID], groupID) + g.ensureRealmCacheLocked(realmID) - g.cacheLock.Unlock() + g.clearGroupMembersCacheLocked(realmID, group) + + delete(g.groupsCache[realmID], groupID) return nil } func (g *groupsCacheInMem) Update(ctx context.Context, realmID uint32, group *repo.Group) error { + if err := validateCacheRealmID(realmID); err != nil { + return err + } + if err := g.r.Update(ctx, realmID, group); err != nil { return err } g.cacheLock.Lock() - g.groupsCache[realmID][group.ID] = group + g.ensureRealmCacheLocked(realmID) + cachedGroup := cloneGroup(group) + normalizeGroupForRealm(cachedGroup, realmID) + g.groupsCache[realmID][cachedGroup.ID] = cachedGroup + g.rebuildGroupMembersCacheLocked(realmID, cachedGroup) g.cacheLock.Unlock() return nil } +func (g *groupsCacheInMem) RegisterMaterializedLfgGroup(_ context.Context, realmID uint32, group *repo.Group) error { + if err := validateCacheRealmID(realmID); err != nil { + return err + } + if group == nil { + return nil + } + + g.cacheLock.Lock() + defer g.cacheLock.Unlock() + + g.ensureRealmCacheLocked(realmID) + cachedGroup := cloneGroup(group) + normalizeGroupForRealm(cachedGroup, realmID) + if existing := g.groupsCache[realmID][cachedGroup.ID]; existing != nil { + g.clearGroupMembersCacheLocked(realmID, existing) + } + g.groupsCache[realmID][cachedGroup.ID] = cachedGroup + g.rebuildGroupMembersCacheLocked(realmID, cachedGroup) + + return nil +} + +func (g *groupsCacheInMem) RegisterAcceptedLfgGroup(ctx context.Context, realmID uint32, group *repo.Group) error { + if err := validateCacheRealmID(realmID); err != nil { + return err + } + if group == nil { + return nil + } + + if registrar, ok := g.r.(acceptedLfgGroupRegistrar); ok { + if err := registrar.RegisterAcceptedLfgGroup(ctx, realmID, group); err != nil { + return err + } + } else if group.ID == 0 { + if err := g.r.Create(ctx, realmID, group); err != nil { + return err + } + } else if err := g.r.Update(ctx, realmID, group); err != nil { + return err + } + + g.cacheLock.Lock() + defer g.cacheLock.Unlock() + + g.ensureRealmCacheLocked(realmID) + cachedGroup := cloneGroup(group) + normalizeGroupForRealm(cachedGroup, realmID) + if existing := g.groupsCache[realmID][cachedGroup.ID]; existing != nil { + g.clearGroupMembersCacheLocked(realmID, existing) + } + g.groupsCache[realmID][cachedGroup.ID] = cachedGroup + g.rebuildGroupMembersCacheLocked(realmID, cachedGroup) + + return nil +} + func (g *groupsCacheInMem) AddMember(ctx context.Context, realmID uint32, groupMember *repo.GroupMember) error { + if err := validateCacheRealmID(realmID); err != nil { + return err + } + + g.cacheLock.RLock() + group := g.groupsCache[realmID][groupMember.GroupID] + g.cacheLock.RUnlock() + if group == nil { + return fmt.Errorf("group %d not found in realm %d cache", groupMember.GroupID, realmID) + } + if err := g.r.AddMember(ctx, realmID, groupMember); err != nil { return err } g.cacheLock.Lock() - g.groupsCache[realmID][groupMember.GroupID].Members = append(g.groupsCache[realmID][groupMember.GroupID].Members, *groupMember) - g.groupMembersCache[realmID][groupMember.MemberGUID] = &g.groupsCache[realmID][groupMember.GroupID].Members[len(g.groupsCache[realmID][groupMember.GroupID].Members)-1] + g.ensureRealmCacheLocked(realmID) + group = g.groupsCache[realmID][groupMember.GroupID] + if group == nil { + g.cacheLock.Unlock() + return fmt.Errorf("group %d not found in realm %d cache", groupMember.GroupID, realmID) + } + normalizeGroupMemberForRealm(groupMember, realmID) + group.Members = append(group.Members, *groupMember) + g.rebuildGroupMembersCacheLocked(realmID, group) g.cacheLock.Unlock() return nil } func (g *groupsCacheInMem) UpdateMember(ctx context.Context, realmID uint32, groupMember *repo.GroupMember) error { - if err := g.r.UpdateMember(ctx, realmID, groupMember); err != nil { + if err := validateCacheRealmID(realmID); err != nil { return err } - group, err := g.GroupByID(ctx, realmID, groupMember.GroupID, true) - if err != nil { + if err := g.r.UpdateMember(ctx, realmID, groupMember); err != nil { return err } g.cacheLock.Lock() + defer g.cacheLock.Unlock() + + g.ensureRealmCacheLocked(realmID) + group := g.groupsCache[realmID][groupMember.GroupID] + if group == nil { + return fmt.Errorf("group %d not found in realm %d cache", groupMember.GroupID, realmID) + } + + normalizeGroupMemberForRealm(groupMember, realmID) for i, member := range group.Members { - if member.MemberGUID == groupMember.MemberGUID { + if guid.SamePlayer(realmID, member.MemberGUID, realmID, groupMember.MemberGUID) { group.Members[i] = *groupMember - g.groupMembersCache[realmID][groupMember.MemberGUID] = &group.Members[i] + g.rebuildGroupMembersCacheLocked(realmID, group) break } } - g.cacheLock.Unlock() return nil } func (g *groupsCacheInMem) RemoveMember(ctx context.Context, realmID uint32, memberGUID uint64) error { - if err := g.r.RemoveMember(ctx, realmID, memberGUID); err != nil { + memberRealmID, memberLowGUID, err := cacheMemberKey(realmID, memberGUID) + if err != nil { return err } @@ -151,20 +303,37 @@ func (g *groupsCacheInMem) RemoveMember(ctx context.Context, realmID uint32, mem return err } - group, err := g.GroupByID(ctx, realmID, groupID, true) - if err != nil { + groupRealmID := realmID + g.cacheLock.RLock() + if g.groupMemberHomeRealmCache[memberRealmID] != nil { + if cachedRealmID, ok := g.groupMemberHomeRealmCache[memberRealmID][memberLowGUID]; ok { + groupRealmID = cachedRealmID + } + } + g.cacheLock.RUnlock() + + if err := g.r.RemoveMember(ctx, groupRealmID, guid.PlayerGUIDForRealm(groupRealmID, memberRealmID, memberLowGUID)); err != nil { return err } g.cacheLock.Lock() + defer g.cacheLock.Unlock() + + group := (*repo.Group)(nil) + if g.groupsCache[groupRealmID] != nil { + group = g.groupsCache[groupRealmID][groupID] + } + if group == nil { + return nil + } + for i, member := range group.Members { - if member.MemberGUID == memberGUID { + if guid.SamePlayer(realmID, memberGUID, groupRealmID, member.MemberGUID) { group.Members = append(group.Members[:i], group.Members[i+1:]...) - delete(g.groupMembersCache[realmID], member.MemberGUID) + g.rebuildGroupMembersCacheLocked(groupRealmID, group) break } } - g.cacheLock.Unlock() return nil } @@ -177,52 +346,215 @@ func (g *groupsCacheInMem) GetInviteByInvitedPlayer(ctx context.Context, realmID return g.r.GetInviteByInvitedPlayer(ctx, realmID, invitedPlayer) } +func (g *groupsCacheInMem) RemoveInvite(ctx context.Context, realmID uint32, invitedPlayer uint64) error { + return g.r.RemoveInvite(ctx, realmID, invitedPlayer) +} + func (g *groupsCacheInMem) HandleCharacterLoggedIn(payload events.GWEventCharacterLoggedInPayload) error { - member := g.groupMemberByGUID(payload.RealmID, payload.CharGUID) - if member == nil { - return nil + _, err := g.SetMemberOnlineStatus(context.Background(), payload.RealmID, payload.CharGUID, true) + return err +} + +func (g *groupsCacheInMem) HandleCharacterLoggedOut(payload events.GWEventCharacterLoggedOutPayload) error { + _, err := g.SetMemberOnlineStatus(context.Background(), payload.RealmID, payload.CharGUID, false) + return err +} + +func (g *groupsCacheInMem) SetMemberOnlineStatus(ctx context.Context, realmID uint32, memberGUID uint64, online bool) (*repo.Group, error) { + memberRealmID, memberLowGUID, err := cacheMemberKey(realmID, memberGUID) + if err != nil { + return nil, err } - member.IsOnline = true + g.cacheLock.Lock() + defer g.cacheLock.Unlock() - return nil -} + if g.groupMembersCache[memberRealmID] == nil { + return nil, nil + } -func (g *groupsCacheInMem) HandleCharacterLoggedOut(payload events.GWEventCharacterLoggedOutPayload) error { - member := g.groupMemberByGUID(payload.RealmID, payload.CharGUID) + member := g.groupMembersCache[memberRealmID][memberLowGUID] if member == nil { - return nil + return nil, nil + } + + groupRealmID := realmID + if g.groupMemberHomeRealmCache[memberRealmID] != nil { + if cachedRealmID, ok := g.groupMemberHomeRealmCache[memberRealmID][memberLowGUID]; ok { + groupRealmID = cachedRealmID + } } - member.IsOnline = false + group := g.groupsCache[groupRealmID][member.GroupID] + if group == nil { + return nil, nil + } - return nil + for i := range group.Members { + if guid.SamePlayer(realmID, memberGUID, groupRealmID, group.Members[i].MemberGUID) { + group.Members[i].IsOnline = online + break + } + } + g.rebuildGroupMembersCacheLocked(groupRealmID, group) + + return cloneGroup(group), nil } func (g *groupsCacheInMem) Warmup(ctx context.Context, realmID uint32) error { - if realmID > MaxRealmID { - panic(fmt.Errorf("realmID overflow, %d > %d", realmID, MaxRealmID)) + if err := validateCacheRealmID(realmID); err != nil { + return err } groups, err := g.r.LoadAllForRealm(ctx, realmID) if err != nil { return err } + if groups == nil { + groups = map[uint]*repo.Group{} + } - g.groupsCache[realmID] = groups + cachedGroups := make(map[uint]*repo.Group, len(groups)) + for groupID, group := range groups { + cachedGroup := cloneGroup(group) + if cachedGroup == nil { + continue + } + normalizeGroupForRealm(cachedGroup, realmID) + // Online state is a session lease, not persistent group state. Gateway + // login/member-state events will re-establish live members after warmup. + for i := range cachedGroup.Members { + cachedGroup.Members[i].IsOnline = false + } + cachedGroups[groupID] = cachedGroup + } + + g.cacheLock.Lock() + g.clearRealmOwnedMembersCacheLocked(realmID) + g.groupsCache[realmID] = cachedGroups g.groupMembersCache[realmID] = map[uint64]*repo.GroupMember{} - for _, group := range groups { - for i := range group.Members { - g.groupMembersCache[realmID][group.Members[i].MemberGUID] = &group.Members[i] - } + g.groupMemberHomeRealmCache[realmID] = map[uint64]uint32{} + for _, group := range cachedGroups { + g.rebuildGroupMembersCacheLocked(realmID, group) } + g.cacheLock.Unlock() return nil } -func (g *groupsCacheInMem) groupMemberByGUID(realmID uint32, player uint64) (r *repo.GroupMember) { - g.cacheLock.RLock() - r = g.groupMembersCache[realmID][player] - g.cacheLock.RUnlock() - return +func validateCacheRealmID(realmID uint32) error { + if realmID >= MaxRealmID { + return fmt.Errorf("realmID overflow, %d >= %d", realmID, MaxRealmID) + } + + return nil +} + +func (g *groupsCacheInMem) rebuildGroupMembersCacheLocked(realmID uint32, group *repo.Group) { + g.ensureRealmCacheLocked(realmID) + if group == nil { + return + } + + normalizeGroupForRealm(group, realmID) + g.clearGroupMembersCacheLocked(realmID, group) + for i := range group.Members { + memberRealmID, memberLowGUID, err := cacheMemberKey(realmID, group.Members[i].MemberGUID) + if err != nil { + continue + } + g.ensureMemberRealmCacheLocked(memberRealmID) + g.groupMembersCache[memberRealmID][memberLowGUID] = &group.Members[i] + g.groupMemberHomeRealmCache[memberRealmID][memberLowGUID] = realmID + } +} + +func (g *groupsCacheInMem) ensureRealmCacheLocked(realmID uint32) { + if g.groupsCache[realmID] == nil { + g.groupsCache[realmID] = map[uint]*repo.Group{} + } + g.ensureMemberRealmCacheLocked(realmID) +} + +func (g *groupsCacheInMem) ensureMemberRealmCacheLocked(realmID uint32) { + if g.groupMembersCache[realmID] == nil { + g.groupMembersCache[realmID] = map[uint64]*repo.GroupMember{} + } + if g.groupMemberHomeRealmCache[realmID] == nil { + g.groupMemberHomeRealmCache[realmID] = map[uint64]uint32{} + } +} + +func (g *groupsCacheInMem) clearGroupMembersCacheLocked(groupRealmID uint32, group *repo.Group) { + if group == nil { + return + } + for memberRealmID := uint32(0); memberRealmID < MaxRealmID; memberRealmID++ { + for memberGUID, member := range g.groupMembersCache[memberRealmID] { + if member == nil || member.GroupID != group.ID { + continue + } + if g.groupMemberHomeRealmCache[memberRealmID] != nil && g.groupMemberHomeRealmCache[memberRealmID][memberGUID] != groupRealmID { + continue + } + delete(g.groupMembersCache[memberRealmID], memberGUID) + delete(g.groupMemberHomeRealmCache[memberRealmID], memberGUID) + } + } +} + +func (g *groupsCacheInMem) clearRealmOwnedMembersCacheLocked(groupRealmID uint32) { + for memberRealmID := uint32(0); memberRealmID < MaxRealmID; memberRealmID++ { + for memberGUID := range g.groupMembersCache[memberRealmID] { + if g.groupMemberHomeRealmCache[memberRealmID] == nil || g.groupMemberHomeRealmCache[memberRealmID][memberGUID] != groupRealmID { + continue + } + delete(g.groupMembersCache[memberRealmID], memberGUID) + delete(g.groupMemberHomeRealmCache[memberRealmID], memberGUID) + } + } +} + +func cloneGroup(group *repo.Group) *repo.Group { + if group == nil { + return nil + } + + clone := *group + if group.Members != nil { + clone.Members = append([]repo.GroupMember(nil), group.Members...) + } + return &clone +} + +func cacheMemberKey(defaultRealmID uint32, playerGUID uint64) (uint32, uint64, error) { + memberRealmID := guid.PlayerRealmIDOrDefault(defaultRealmID, playerGUID) + if err := validateCacheRealmID(memberRealmID); err != nil { + return 0, 0, err + } + return memberRealmID, guid.PlayerLowGUID(playerGUID), nil +} + +func normalizeGroupForRealm(group *repo.Group, realmID uint32) { + if group == nil { + return + } + group.RealmID = realmID + group.LeaderGUID = guid.NormalizePlayerGUIDForRealm(realmID, group.LeaderGUID) + group.LooterGUID = guid.NormalizePlayerGUIDForRealm(realmID, group.LooterGUID) + group.MasterLooterGuid = guid.NormalizePlayerGUIDForRealm(realmID, group.MasterLooterGuid) + for i := range group.TargetIcons { + group.TargetIcons[i] = guid.NormalizePlayerGUIDForRealm(realmID, group.TargetIcons[i]) + } + for i := range group.Members { + normalizeGroupMemberForRealm(&group.Members[i], realmID) + } +} + +func normalizeGroupMemberForRealm(groupMember *repo.GroupMember, groupRealmID uint32) { + if groupMember == nil { + return + } + groupMember.RealmID = guid.PlayerRealmIDOrDefault(groupRealmID, groupMember.MemberGUID) + groupMember.MemberGUID = guid.PlayerGUIDForRealm(groupRealmID, groupMember.RealmID, groupMember.MemberGUID) } diff --git a/apps/groupserver/service/group.go b/apps/groupserver/service/group.go index b5835d1..a8fe4a4 100644 --- a/apps/groupserver/service/group.go +++ b/apps/groupserver/service/group.go @@ -4,23 +4,105 @@ import ( "context" "errors" "fmt" + "sort" + "strconv" + "strings" + "sync" + "time" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/walkline/ToCloud9/apps/groupserver" "github.com/walkline/ToCloud9/apps/groupserver/repo" "github.com/walkline/ToCloud9/gen/characters/pb" "github.com/walkline/ToCloud9/shared/events" + "github.com/walkline/ToCloud9/shared/groupstatetrace" + "github.com/walkline/ToCloud9/shared/wow" + "github.com/walkline/ToCloud9/shared/wow/guid" ) +type readyCheckKey struct { + realmID uint32 + groupID uint +} + +type pendingSubGroupSwapKey struct { + realmID uint32 + groupID uint + updaterGUID uint64 +} + +type pendingSubGroupSwap struct { + memberGUID uint64 + fromSubGroup uint8 + toSubGroup uint8 + expiresUnixNano int64 +} + +type memberStateTimestampKey struct { + realmID uint32 + memberGUID uint64 +} + +type memberPlacement struct { + gatewayID string + worldserverID string + online bool + mapID uint32 + instanceID uint32 + instanceKnown bool + timestampMs uint64 + updatedAt time.Time +} + +type memberLastState struct { + snapshot MemberStateSnapshot + updatedAt time.Time +} + +type memberStateTracker struct { + mu sync.Mutex + timestamps map[memberStateTimestampKey]uint64 + gatewayLifecycleEventTimes map[memberStateTimestampKey]uint64 + placements map[memberStateTimestampKey]memberPlacement + states map[memberStateTimestampKey]memberLastState +} + +func memberStateKey(defaultRealmID uint32, playerGUID uint64) memberStateTimestampKey { + return memberStateTimestampKey{ + realmID: guid.PlayerRealmIDOrDefault(defaultRealmID, playerGUID), + memberGUID: guid.PlayerLowGUID(playerGUID), + } +} + +type offlineLeaderPromotionKey struct { + realmID uint32 + groupID uint + leaderGUID uint64 +} + var ( ErrAlreadyInGroup = errors.New("player already in group") ErrNoPermissions = errors.New("player has not enough permissions") + ErrLFGGroupRestricted = errors.New("lfg group does not allow this operation") + ErrInvalidGroupOperation = errors.New("invalid group operation") ErrGroupFull = errors.New("group is full") ErrGroupNotFound = errors.New("group not found") ErrGroupMemberNotFound = errors.New("group member not found") ErrMemberInDungeonOrRaid = errors.New("group member is in dungeon or raid") ErrInviteNotFound = errors.New("invite not found") + + readyCheckMu sync.Mutex + readyCheckSequences = map[readyCheckKey]uint64{} + + pendingSubGroupSwapMu sync.Mutex + pendingSubGroupSwaps = map[pendingSubGroupSwapKey]pendingSubGroupSwap{} + + offlineLeaderPromotionDelay = 2 * time.Minute + offlineLeaderPromotionMu sync.Mutex + offlineLeaderPromotionSequence uint64 + offlineLeaderPromotionSequences = map[offlineLeaderPromotionKey]uint64{} ) type MessageType uint8 @@ -30,786 +112,3421 @@ const ( MessageTypeGroupLeader MessageType = 0x33 MessageTypeRaid MessageType = 0x3 MessageTypeRaidLeader MessageType = 0x27 -) - -type GroupsService interface { - GroupByID(ctx context.Context, realmID uint32, groupID uint) (*repo.Group, error) - GroupByMemberGUID(ctx context.Context, realmID uint32, memberGUID uint64) (*repo.Group, error) - GroupIDByPlayer(ctx context.Context, realmID uint32, player uint64) (uint, error) - - Invite(ctx context.Context, realmID uint32, inviter, invited uint64, inviterName, invitedName string) error - Uninvite(ctx context.Context, realmID uint32, initiator, target uint64, reason string) error - Leave(ctx context.Context, realmID uint32, player uint64) error + MessageTypeRaidWarning MessageType = 0x28 - ChangeLeader(ctx context.Context, realmID uint32, player, newLeader uint64) error - ConvertToRaid(ctx context.Context, realmID uint32, player uint64) error - - AcceptInvite(ctx context.Context, realmID uint32, player uint64) error + subGroupSwapPendingFlag uint8 = 0x80 - SendMessage(ctx context.Context, realmID uint32, senderGUID uint64, message string, lang uint32, messageType MessageType) error + // Mirrors AzerothCore MAX_DUNGEON_DIFFICULTY and MAX_RAID_DIFFICULTY. + maxDungeonDifficulty uint8 = 3 + maxRaidDifficulty uint8 = 4 - SetTargetIcon(ctx context.Context, realmID uint32, updaterGUID uint64, iconID uint8, targetGUID uint64) error - SetLootMethod(ctx context.Context, realmID uint32, updaterGUID uint64, method uint8, lootMaster uint64, lootThreshold uint8) error + memberPlacementFreshness = 15 * time.Second +) - SetDungeonDifficulty(ctx context.Context, realmID uint32, updaterGUID uint64, difficulty uint8) error - SetRaidDifficulty(ctx context.Context, realmID uint32, updaterGUID uint64, difficulty uint8) error +func startReadyCheckTimeout(realmID uint32, groupID uint) uint64 { + readyCheckMu.Lock() + defer readyCheckMu.Unlock() - // GWCharacterLoggedInHandler updates cache with player logged in. - events.GWCharacterLoggedInHandler - // GWCharacterLoggedOutHandler updates cache with player logged out. - events.GWCharacterLoggedOutHandler + key := readyCheckKey{realmID: realmID, groupID: groupID} + readyCheckSequences[key]++ + return readyCheckSequences[key] } -func NewGroupsService(r repo.GroupsRepo, charClient pb.CharactersServiceClient, ep events.GroupServiceProducer) GroupsService { - return &groupServiceImpl{ - r: r, - ep: ep, - charClient: charClient, +func consumeReadyCheckTimeout(realmID uint32, groupID uint, sequence uint64) bool { + readyCheckMu.Lock() + defer readyCheckMu.Unlock() + + key := readyCheckKey{realmID: realmID, groupID: groupID} + if readyCheckSequences[key] != sequence { + return false } + + delete(readyCheckSequences, key) + return true } -type groupServiceImpl struct { - r repo.GroupsRepo - ep events.GroupServiceProducer +func clearReadyCheckTimeout(realmID uint32, groupID uint) { + readyCheckMu.Lock() + defer readyCheckMu.Unlock() - charClient pb.CharactersServiceClient + delete(readyCheckSequences, readyCheckKey{realmID: realmID, groupID: groupID}) } -func (g groupServiceImpl) GroupIDByPlayer(ctx context.Context, realmID uint32, player uint64) (uint, error) { - return g.r.GroupIDByPlayer(ctx, realmID, player) -} +func consumePendingSubGroupSwap(realmID uint32, groupID uint, updaterGUID uint64, memberGUID uint64, fromSubGroup, toSubGroup uint8) (pendingSubGroupSwap, bool) { + pendingSubGroupSwapMu.Lock() + defer pendingSubGroupSwapMu.Unlock() -func (g groupServiceImpl) GroupByID(ctx context.Context, realmID uint32, groupID uint) (*repo.Group, error) { - return g.r.GroupByID(ctx, realmID, groupID, true) -} + key := pendingSubGroupSwapKey{realmID: realmID, groupID: groupID, updaterGUID: updaterGUID} + pending, ok := pendingSubGroupSwaps[key] + if !ok { + return pendingSubGroupSwap{}, false + } -func (g groupServiceImpl) GroupByMemberGUID(ctx context.Context, realmID uint32, memberGUID uint64) (*repo.Group, error) { - groupID, err := g.GroupIDByPlayer(ctx, realmID, memberGUID) - if err != nil { - return nil, err + if time.Now().UnixNano() > pending.expiresUnixNano { + delete(pendingSubGroupSwaps, key) + return pendingSubGroupSwap{}, false + } + + if pending.memberGUID == memberGUID || pending.fromSubGroup != toSubGroup || pending.toSubGroup != fromSubGroup { + return pendingSubGroupSwap{}, false } - return g.GroupByID(ctx, realmID, groupID) + delete(pendingSubGroupSwaps, key) + return pending, true } -func (g groupServiceImpl) Invite(ctx context.Context, realmID uint32, inviter, invited uint64, inviterName, invitedName string) error { - groupID, err := g.r.GroupIDByPlayer(ctx, realmID, invited) - if err != nil { - return err - } +func queuePendingSubGroupSwap(realmID uint32, groupID uint, updaterGUID uint64, memberGUID uint64, fromSubGroup, toSubGroup uint8) error { + pendingSubGroupSwapMu.Lock() + defer pendingSubGroupSwapMu.Unlock() - if groupID != 0 { - return ErrAlreadyInGroup + now := time.Now() + pruneExpiredPendingSubGroupSwapsLocked(now.UnixNano()) + + key := pendingSubGroupSwapKey{realmID: realmID, groupID: groupID, updaterGUID: updaterGUID} + if pending, ok := pendingSubGroupSwaps[key]; ok && now.UnixNano() <= pending.expiresUnixNano { + return ErrGroupFull } - inviterGroupID, err := g.r.GroupIDByPlayer(ctx, realmID, inviter) - if err != nil { - return err + pendingSubGroupSwaps[key] = pendingSubGroupSwap{ + memberGUID: memberGUID, + fromSubGroup: fromSubGroup, + toSubGroup: toSubGroup, + expiresUnixNano: now.Add(2 * time.Second).UnixNano(), } - if inviterGroupID == 0 { - if err = g.r.AddInvite(ctx, realmID, repo.GroupInvite{ - Inviter: inviter, - InviterName: inviterName, - Invitee: invited, - InviteeName: invitedName, - GroupID: 0, - }); err != nil { - return err + return nil +} + +func pruneExpiredPendingSubGroupSwapsLocked(nowUnixNano int64) { + for key, pending := range pendingSubGroupSwaps { + if nowUnixNano > pending.expiresUnixNano { + delete(pendingSubGroupSwaps, key) } + } +} - err = g.ep.InviteCreated(&events.GroupEventInviteCreatedPayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: 0, - InviterGUID: inviter, - InviterName: inviterName, - InviteeGUID: invited, - InviteeName: invitedName, - }) +func clearPendingSubGroupSwapsForMember(realmID uint32, groupID uint, memberGUID uint64) { + pendingSubGroupSwapMu.Lock() + defer pendingSubGroupSwapMu.Unlock() - if err != nil { - log.Error().Err(err).Msg("can't create invite created event") + for key, pending := range pendingSubGroupSwaps { + if key.realmID == realmID && key.groupID == groupID && (key.updaterGUID == memberGUID || pending.memberGUID == memberGUID) { + delete(pendingSubGroupSwaps, key) } - - return nil } +} - group, err := g.r.GroupByID(ctx, realmID, inviterGroupID, true) - if err != nil { - return err - } +func clearPendingSubGroupSwapsForGroup(realmID uint32, groupID uint) { + pendingSubGroupSwapMu.Lock() + defer pendingSubGroupSwapMu.Unlock() - member := group.MemberByGUID(inviter) - if member == nil { - return fmt.Errorf("can't find player %d in the guild %d", inviter, inviterGroupID) + for key := range pendingSubGroupSwaps { + if key.realmID == realmID && key.groupID == groupID { + delete(pendingSubGroupSwaps, key) + } } +} - if !(group.LeaderGUID == inviter || member.IsAssistant()) { - return ErrNoPermissions +func newMemberStateTracker() *memberStateTracker { + return &memberStateTracker{ + timestamps: map[memberStateTimestampKey]uint64{}, + gatewayLifecycleEventTimes: map[memberStateTimestampKey]uint64{}, + placements: map[memberStateTimestampKey]memberPlacement{}, + states: map[memberStateTimestampKey]memberLastState{}, } +} - if group.IsFull() { - return ErrGroupFull +func (t *memberStateTracker) acceptTimestamp(realmID uint32, memberGUID uint64, timestampMs uint64, allowEqualBump bool) (uint64, bool) { + if timestampMs == 0 { + return timestampMs, true } - if err = g.r.AddInvite(ctx, realmID, repo.GroupInvite{ - Inviter: inviter, - InviterName: inviterName, - Invitee: invited, - InviteeName: invitedName, - GroupID: inviterGroupID, - }); err != nil { - return err + key := memberStateKey(realmID, memberGUID) + t.mu.Lock() + defer t.mu.Unlock() + + if last, ok := t.timestamps[key]; ok { + if timestampMs < last { + return timestampMs, false + } + if timestampMs == last { + if !allowEqualBump || last == ^uint64(0) { + return timestampMs, false + } + timestampMs = last + 1 + } } - err = g.ep.InviteCreated(&events.GroupEventInviteCreatedPayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: inviterGroupID, - InviterGUID: inviter, - InviterName: inviterName, - InviteeGUID: invited, - InviteeName: invitedName, - }) + t.timestamps[key] = timestampMs + return timestampMs, true +} - if err != nil { - log.Error().Err(err).Msg("can't create invite created event") - } +func (t *memberStateTracker) hasTimestamp(realmID uint32, memberGUID uint64) bool { + key := memberStateKey(realmID, memberGUID) + t.mu.Lock() + defer t.mu.Unlock() - return nil + _, ok := t.timestamps[key] + return ok } -func (g groupServiceImpl) AcceptInvite(ctx context.Context, realmID uint32, player uint64) error { - invite, err := g.r.GetInviteByInvitedPlayer(ctx, realmID, player) - if err != nil { - return err - } +func (t *memberStateTracker) clearMember(realmID uint32, memberGUID uint64) { + t.mu.Lock() + defer t.mu.Unlock() - if invite == nil { - return ErrInviteNotFound - } + key := memberStateKey(realmID, memberGUID) + // Placement is session-scoped, not group-scoped. Keep it across a leave or + // disband so immediate regroup flows such as LFG can still route to the + // owning worldserver before the next gateway state flush. + delete(t.states, key) +} - if invite.GroupID == 0 { - return g.createGroup(ctx, realmID, invite) - } +func (t *memberStateTracker) clearGroup(realmID uint32, members []repo.GroupMember) { + t.mu.Lock() + defer t.mu.Unlock() - group, err := g.r.GroupByID(ctx, realmID, invite.GroupID, true) - if err != nil { - return err + for _, member := range members { + key := memberStateKey(realmID, member.MemberGUID) + delete(t.states, key) } - - return g.addMember(ctx, realmID, group, invite) } -func (g groupServiceImpl) Uninvite(ctx context.Context, realmID uint32, initiator, target uint64, reason string) error { - groupID, err := g.r.GroupIDByPlayer(ctx, realmID, initiator) - if err != nil { - return fmt.Errorf("can't get groupID, err: %w", err) - } - if groupID == 0 { - return ErrGroupNotFound +func (t *memberStateTracker) recordMemberState(update memberStateUpdate, snapshot MemberStateSnapshot) { + if snapshot.MemberGUID == 0 { + return + } + + key := memberStateKey(update.realmID, snapshot.MemberGUID) + now := time.Now() + t.mu.Lock() + defer t.mu.Unlock() + + t.recordMemberPlacementLocked(key, update, snapshot, now) + + if snapshot.Online { + if isAuraOnlyMemberStateSnapshot(snapshot) { + if last, ok := t.states[key]; ok && last.snapshot.Online { + last.snapshot.AurasKnown = true + last.snapshot.Auras = append([]MemberAuraState(nil), snapshot.Auras...) + if snapshot.Dead != nil { + last.snapshot.Dead = snapshot.Dead + } + if snapshot.Ghost != nil { + last.snapshot.Ghost = snapshot.Ghost + } + if snapshot.TimestampMs != 0 { + last.snapshot.TimestampMs = snapshot.TimestampMs + } + last.updatedAt = now + t.states[key] = last + } + return + } + t.states[key] = memberLastState{snapshot: snapshot, updatedAt: now} + } else { + delete(t.states, key) } +} - group, err := g.r.GroupByID(ctx, realmID, groupID, true) - if err != nil { - return fmt.Errorf("can't get group, err: %w", err) +func (t *memberStateTracker) recordMemberPlacement(update memberStateUpdate, snapshot MemberStateSnapshot) { + if snapshot.MemberGUID == 0 { + return } - if group == nil { - return ErrGroupNotFound + key := memberStateKey(update.realmID, snapshot.MemberGUID) + now := time.Now() + t.mu.Lock() + defer t.mu.Unlock() + + if snapshot.TimestampMs != 0 { + if last, ok := t.timestamps[key]; ok && snapshot.TimestampMs < last { + return + } } + t.recordMemberPlacementLocked(key, update, snapshot, now) +} - targetMember := group.MemberByGUID(target) - if targetMember == nil { - return ErrGroupNotFound +func (t *memberStateTracker) recordMemberPlacementLocked(key memberStateTimestampKey, update memberStateUpdate, snapshot MemberStateSnapshot, now time.Time) { + hasPlacementEvidence := update.sourceGatewayID != "" || update.sourceWorldserverID != "" + if !hasPlacementEvidence && snapshot.Online { + return } - if group.LeaderGUID != initiator { - return ErrNoPermissions + placement, hadPlacement := t.placements[key] + sourceChanged := hadPlacement && placement.worldserverID != "" && update.sourceWorldserverID != "" && placement.worldserverID != update.sourceWorldserverID + gatewayChanged := hadPlacement && placement.gatewayID != "" && update.sourceGatewayID != "" && placement.gatewayID != update.sourceGatewayID + mapChanged := hadPlacement && placement.mapID != snapshot.MapID + if update.sourceGatewayID != "" { + placement.gatewayID = update.sourceGatewayID + } + if update.sourceWorldserverID != "" { + placement.worldserverID = update.sourceWorldserverID } + placement.online = snapshot.Online + if !snapshot.Online { + placement.worldserverID = "" + } + placement.mapID = snapshot.MapID + if snapshot.InstanceID != nil { + placement.instanceID = *snapshot.InstanceID + placement.instanceKnown = *snapshot.InstanceID != 0 + } else if !snapshot.Online || sourceChanged || gatewayChanged || mapChanged { + placement.instanceID = 0 + placement.instanceKnown = false + } + if snapshot.TimestampMs != 0 { + placement.timestampMs = snapshot.TimestampMs + } + placement.updatedAt = now + t.placements[key] = placement +} - membersCount := len(group.Members) +func (t *memberStateTracker) memberStateUpdatesForGroup(realmID uint32, members []repo.GroupMember, excludeGUID uint64) []events.GroupMemberStateUpdate { + t.mu.Lock() + defer t.mu.Unlock() - if membersCount <= 2 { - if err = g.disband(ctx, realmID, group); err != nil { - return fmt.Errorf("can't disband group, err: %w", err) - } - } else { - eventToSend := events.GroupEventGroupMemberLeftPayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: groupID, - MemberGUID: targetMember.MemberGUID, - MemberName: targetMember.MemberName, - NewLeaderID: group.LeaderGUID, - OnlineMembers: group.OnlineMemberGUIDs(), - } - if err = g.r.RemoveMember(ctx, realmID, target); err != nil { - return fmt.Errorf("can't remove member, err: %w", err) + updates := make([]events.GroupMemberStateUpdate, 0, len(members)) + for _, member := range members { + if member.MemberGUID == 0 || guid.SamePlayer(realmID, member.MemberGUID, realmID, excludeGUID) || !member.IsOnline { + continue } - err = g.ep.GroupMemberLeft(&eventToSend) - if err != nil { - log.Error().Err(err).Msg("can't create GroupMemberLeft event") + last, ok := t.states[memberStateKey(realmID, member.MemberGUID)] + if !ok || !last.snapshot.Online { + continue } + + update := memberStateSnapshotToUpdate(last.snapshot) + update.MemberGUID = member.MemberGUID + updates = append(updates, update) } - return nil + sort.Slice(updates, func(i, j int) bool { + return updates[i].MemberGUID < updates[j].MemberGUID + }) + + return updates } -func (g groupServiceImpl) Leave(ctx context.Context, realmID uint32, player uint64) error { - groupID, err := g.r.GroupIDByPlayer(ctx, realmID, player) - if err != nil { - return fmt.Errorf("can't get groupID, err: %w", err) - } - if groupID == 0 { - return ErrGroupNotFound +func (t *memberStateTracker) hasOnlineState(realmID uint32, memberGUID uint64) bool { + if memberGUID == 0 { + return false } - group, err := g.r.GroupByID(ctx, realmID, groupID, true) - if err != nil { - return fmt.Errorf("can't get group, err: %w", err) - } + key := memberStateKey(realmID, memberGUID) + t.mu.Lock() + defer t.mu.Unlock() - member := group.MemberByGUID(player) - if member == nil { - return ErrGroupNotFound - } + last, ok := t.states[key] + return ok && last.snapshot.Online +} - if len(group.Members) <= 2 { - return g.disband(ctx, realmID, group) +func (t *memberStateTracker) preserveKnownVitals(realmID uint32, snapshot MemberStateSnapshot) MemberStateSnapshot { + if !snapshot.Online || snapshot.MemberGUID == 0 { + return snapshot + } + + key := memberStateKey(realmID, snapshot.MemberGUID) + t.mu.Lock() + last, ok := t.states[key] + t.mu.Unlock() + if !ok || !last.snapshot.Online { + return snapshot + } + if isAuraOnlyMemberStateSnapshot(snapshot) { + snapshot.Health = last.snapshot.Health + snapshot.MaxHealth = last.snapshot.MaxHealth + snapshot.PowerType = last.snapshot.PowerType + snapshot.Power = last.snapshot.Power + snapshot.MaxPower = last.snapshot.MaxPower + if snapshot.Dead == nil && last.snapshot.Dead != nil { + snapshot.Dead = last.snapshot.Dead + } + if snapshot.Ghost == nil && last.snapshot.Ghost != nil { + snapshot.Ghost = last.snapshot.Ghost + } + return snapshot } - if player == group.LeaderGUID { - var newLeader uint64 - for _, groupMember := range group.Members { - if !groupMember.IsOnline || groupMember.MemberGUID == player { - continue - } - - newLeader = groupMember.MemberGUID - break + if snapshot.MaxHealth == 0 && last.snapshot.MaxHealth > 0 { + if snapshot.Health == 0 && snapshot.AurasKnown && !boolPtrValue(snapshot.Dead) { + snapshot.Health = last.snapshot.Health } - - if err = g.changeLeader(ctx, realmID, group, newLeader, false); err != nil { - return fmt.Errorf("can't change group leader, err: %w", err) + snapshot.MaxHealth = last.snapshot.MaxHealth + } + if snapshot.MaxPower == 0 && last.snapshot.MaxPower > 0 { + if snapshot.Power == 0 && snapshot.AurasKnown { + snapshot.Power = last.snapshot.Power } + if snapshot.PowerType == 0 && last.snapshot.PowerType != 0 { + snapshot.PowerType = last.snapshot.PowerType + } + snapshot.MaxPower = last.snapshot.MaxPower } - - eventToSend := events.GroupEventGroupMemberLeftPayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: groupID, - MemberGUID: member.MemberGUID, - MemberName: member.MemberName, - NewLeaderID: group.LeaderGUID, - OnlineMembers: group.OnlineMemberGUIDs(), + if !snapshot.AurasKnown && last.snapshot.AurasKnown { + snapshot.AurasKnown = true + snapshot.Auras = append([]MemberAuraState(nil), last.snapshot.Auras...) + } + if snapshot.Dead == nil && last.snapshot.Dead != nil { + snapshot.Dead = last.snapshot.Dead + } + if snapshot.Ghost == nil && last.snapshot.Ghost != nil { + snapshot.Ghost = last.snapshot.Ghost } - if err = g.r.RemoveMember(ctx, realmID, player); err != nil { - return fmt.Errorf("can't remove group member, err: %w", err) + return snapshot +} + +func isAuraOnlyMemberStateSnapshot(snapshot MemberStateSnapshot) bool { + return snapshot.Online && + snapshot.AurasKnown && + !boolPtrValue(snapshot.Dead) && + !boolPtrValue(snapshot.Ghost) && + snapshot.Health == 0 && + snapshot.MaxHealth == 0 && + snapshot.Power == 0 && + snapshot.MaxPower == 0 +} + +func boolPtrValue(v *bool) bool { + return v != nil && *v +} + +func (t *memberStateTracker) recordGatewayLogin(payload events.GWEventCharacterLoggedInPayload) bool { + if payload.CharGUID == 0 { + return false } - err = g.ep.GroupMemberLeft(&eventToSend) - if err != nil { - log.Error().Err(err).Msg("can't create GroupMemberLeft event") + key := memberStateKey(payload.RealmID, payload.CharGUID) + t.mu.Lock() + defer t.mu.Unlock() + if !t.acceptGatewayLifecycleEventLocked(key, payload.EventTimeUnixNano) { + return false } - return nil + placement := t.placements[key] + if payload.GatewayID != "" { + placement.gatewayID = payload.GatewayID + } + placement.worldserverID = "" + placement.instanceID = 0 + placement.instanceKnown = false + placement.online = true + placement.mapID = payload.CharMap + placement.updatedAt = time.Now() + t.placements[key] = placement + return true } -func (g groupServiceImpl) ChangeLeader(ctx context.Context, realmID uint32, player, newLeader uint64) error { - group, err := g.getGroupWithLeader(ctx, realmID, player) - if err != nil { - return err +func (t *memberStateTracker) recordGatewayLogout(payload events.GWEventCharacterLoggedOutPayload) bool { + if payload.CharGUID == 0 { + return false } - newLeaderMember := group.MemberByGUID(newLeader) - if newLeaderMember == nil { - return ErrGroupNotFound + key := memberStateKey(payload.RealmID, payload.CharGUID) + t.mu.Lock() + defer t.mu.Unlock() + if !t.acceptGatewayLifecycleEventLocked(key, payload.EventTimeUnixNano) { + return false } - return g.changeLeader(ctx, realmID, group, newLeader, true) + placement := t.placements[key] + if payload.GatewayID != "" { + placement.gatewayID = payload.GatewayID + } + placement.worldserverID = "" + placement.instanceID = 0 + placement.instanceKnown = false + placement.online = false + placement.updatedAt = time.Now() + t.placements[key] = placement + return true } -func (g groupServiceImpl) ConvertToRaid(ctx context.Context, realmID uint32, player uint64) error { - group, err := g.getGroupWithLeader(ctx, realmID, player) - if err != nil { - return err +func (t *memberStateTracker) acceptGatewayLifecycleEventLocked(key memberStateTimestampKey, eventTimeUnixNano uint64) bool { + if eventTimeUnixNano == 0 { + return true } - - group.GroupType |= repo.GroupTypeFlagsRaid - if err := g.r.Update(ctx, realmID, group); err != nil { - return fmt.Errorf("can't update group win a new leader, err: %w", err) + if t.gatewayLifecycleEventTimes == nil { + t.gatewayLifecycleEventTimes = map[memberStateTimestampKey]uint64{} } - err = g.ep.ConvertedToRaid(&events.GroupEventGroupConvertedToRaidPayload{ + if last := t.gatewayLifecycleEventTimes[key]; last > eventTimeUnixNano { + return false + } + t.gatewayLifecycleEventTimes[key] = eventTimeUnixNano + return true +} + +func (t *memberStateTracker) onlineMembersForGateway(realmID uint32, gatewayID string, startedAt time.Time) []uint64 { + if gatewayID == "" { + return nil + } + + t.mu.Lock() + defer t.mu.Unlock() + + members := make([]uint64, 0) + for key, placement := range t.placements { + if key.realmID == realmID && + placement.gatewayID == gatewayID && + placement.online && + (startedAt.IsZero() || placement.updatedAt.IsZero() || !placement.updatedAt.After(startedAt)) { + members = append(members, key.memberGUID) + } + } + sort.Slice(members, func(i, j int) bool { + return members[i] < members[j] + }) + + return members +} + +func (t *memberStateTracker) nextTimestampAfterLast(realmID uint32, memberGUID uint64, timestampMs uint64) uint64 { + if timestampMs == 0 { + return 0 + } + + key := memberStateKey(realmID, memberGUID) + t.mu.Lock() + defer t.mu.Unlock() + + if last, ok := t.timestamps[key]; ok && last != ^uint64(0) && timestampMs <= last { + return last + 1 + } + + return timestampMs +} + +func (t *memberStateTracker) placement(realmID uint32, memberGUID uint64) (memberPlacement, bool) { + t.mu.Lock() + defer t.mu.Unlock() + + placement, ok := t.placements[memberStateKey(realmID, memberGUID)] + return placement, ok +} + +func (t *memberStateTracker) freshPlacement(realmID uint32, memberGUID uint64, now time.Time, maxAge time.Duration) (memberPlacement, bool) { + t.mu.Lock() + defer t.mu.Unlock() + + placement, ok := t.placements[memberStateKey(realmID, memberGUID)] + if !ok || placement.updatedAt.IsZero() || now.Sub(placement.updatedAt) > maxAge { + return memberPlacement{}, false + } + + return placement, true +} + +func (t *memberStateTracker) memberHasOnlineEvidence(realmID uint32, memberGUID uint64) bool { + if memberGUID == 0 { + return false + } + + key := memberStateKey(realmID, memberGUID) + t.mu.Lock() + defer t.mu.Unlock() + + if placement, ok := t.placements[key]; ok { + return placement.online + } + + last, ok := t.states[key] + return ok && last.snapshot.Online +} + +func queueOfflineLeaderPromotionTimer(realmID uint32, groupID uint, leaderGUID uint64) (uint64, bool) { + offlineLeaderPromotionMu.Lock() + defer offlineLeaderPromotionMu.Unlock() + + key := offlineLeaderPromotionKey{realmID: realmID, groupID: groupID, leaderGUID: leaderGUID} + if sequence, ok := offlineLeaderPromotionSequences[key]; ok { + return sequence, false + } + + offlineLeaderPromotionSequence++ + offlineLeaderPromotionSequences[key] = offlineLeaderPromotionSequence + return offlineLeaderPromotionSequence, true +} + +func consumeOfflineLeaderPromotionTimer(realmID uint32, groupID uint, leaderGUID uint64, sequence uint64) bool { + offlineLeaderPromotionMu.Lock() + defer offlineLeaderPromotionMu.Unlock() + + key := offlineLeaderPromotionKey{realmID: realmID, groupID: groupID, leaderGUID: leaderGUID} + if offlineLeaderPromotionSequences[key] != sequence { + return false + } + + delete(offlineLeaderPromotionSequences, key) + return true +} + +func clearOfflineLeaderPromotionTimer(realmID uint32, groupID uint, leaderGUID uint64) { + offlineLeaderPromotionMu.Lock() + defer offlineLeaderPromotionMu.Unlock() + + delete(offlineLeaderPromotionSequences, offlineLeaderPromotionKey{realmID: realmID, groupID: groupID, leaderGUID: leaderGUID}) +} + +func clearGroupOfflineLeaderPromotionTimers(realmID uint32, groupID uint) { + offlineLeaderPromotionMu.Lock() + defer offlineLeaderPromotionMu.Unlock() + + for key := range offlineLeaderPromotionSequences { + if key.realmID == realmID && key.groupID == groupID { + delete(offlineLeaderPromotionSequences, key) + } + } +} + +type GroupsService interface { + GroupByID(ctx context.Context, realmID uint32, groupID uint) (*repo.Group, error) + GroupByMemberGUID(ctx context.Context, realmID uint32, memberGUID uint64) (*repo.Group, error) + GroupIDByPlayer(ctx context.Context, realmID uint32, player uint64) (uint, error) + GroupRealmIDByPlayer(ctx context.Context, realmID uint32, player uint64) (uint32, uint, error) + MemberPlacements(ctx context.Context, realmID uint32, memberGUIDs []uint64) ([]MemberPlacementSnapshot, error) + + Invite(ctx context.Context, realmID uint32, inviter, invited uint64, inviterName, invitedName string) error + Uninvite(ctx context.Context, realmID uint32, initiator, target uint64, reason string) error + Leave(ctx context.Context, realmID uint32, player uint64) error + + ChangeLeader(ctx context.Context, realmID uint32, player, newLeader uint64) error + ConvertToRaid(ctx context.Context, realmID uint32, player uint64) error + + AcceptInvite(ctx context.Context, realmID uint32, player uint64) error + DeclineInvite(ctx context.Context, realmID uint32, player uint64) error + + SendMessage(ctx context.Context, realmID uint32, senderGUID uint64, message string, lang uint32, messageType MessageType, senderChatTag uint8) error + + SetTargetIcon(ctx context.Context, realmID uint32, updaterGUID uint64, iconID uint8, targetGUID uint64) error + SetLootMethod(ctx context.Context, realmID uint32, updaterGUID uint64, method uint8, lootMaster uint64, lootThreshold uint8) error + + SetDungeonDifficulty(ctx context.Context, realmID uint32, updaterGUID uint64, difficulty uint8) error + SetRaidDifficulty(ctx context.Context, realmID uint32, updaterGUID uint64, difficulty uint8) error + + StartReadyCheck(ctx context.Context, realmID uint32, leaderGUID uint64, durationMs uint32) error + SetReadyCheckMemberState(ctx context.Context, realmID uint32, memberGUID uint64, state uint8) error + FinishReadyCheck(ctx context.Context, realmID uint32, playerGUID uint64) error + ChangeMemberSubGroup(ctx context.Context, realmID uint32, updaterGUID, memberGUID uint64, subGroup uint8) error + SetMemberFlags(ctx context.Context, realmID uint32, updaterGUID, memberGUID uint64, flags, roles uint8) error + RegisterAcceptedLfgGroup(ctx context.Context, realmID, proposalID, dungeonEntry, leaderRealmID uint32, leaderGUID uint64, crossRealm bool, members []AcceptedLfgGroupMember) (uint, error) + RegisterMaterializedLfgGroup(ctx context.Context, realmID uint32, groupID uint, leaderGUID uint64, groupType, difficulty, raidDifficulty uint8, members []MaterializedLfgGroupMember) error + UpdateMemberState(ctx context.Context, realmID uint32, memberGUID uint64, online bool, level, class uint8, zoneID, mapID uint32, health, maxHealth uint32, powerType uint8, power, maxPower uint32, instanceID *uint32) error + BulkUpdateMemberStates(ctx context.Context, realmID uint32, sourceGatewayID, sourceWorldserverID string, snapshots []MemberStateSnapshot) error + ResetInstance(ctx context.Context, realmID uint32, playerGUID uint64, mapID uint32, difficulty uint8) error + SetInstanceBindExtension(ctx context.Context, realmID uint32, playerGUID uint64, mapID uint32, difficulty uint8, extended bool) error + + // GWCharacterLoggedInHandler updates cache with player logged in. + events.GWCharacterLoggedInHandler + // GWGatewayStartedHandler expires state owned by a restarted gateway. + events.GWGatewayStartedHandler + // GWCharacterLoggedOutHandler updates cache with player logged out. + events.GWCharacterLoggedOutHandler +} + +type MemberStateSnapshot struct { + MemberGUID uint64 + Online bool + Level uint8 + Class uint8 + ZoneID uint32 + MapID uint32 + Health uint32 + MaxHealth uint32 + PowerType uint8 + Power uint32 + MaxPower uint32 + InstanceID *uint32 + AurasKnown bool + Auras []MemberAuraState + TimestampMs uint64 + Dead *bool + Ghost *bool +} + +type MaterializedLfgGroupMember struct { + RealmID uint32 + PlayerGUID uint64 + Name string + Online bool + Flags uint8 + Roles uint8 + SubGroup uint8 +} + +type AcceptedLfgGroupMember struct { + RealmID uint32 + PlayerGUID uint64 + SelectedRoles uint8 + AssignedRole uint8 + QueueLeaderRealmID uint32 + QueueLeaderGUID uint64 +} + +type MemberAuraState struct { + Slot uint8 + SpellID uint32 + Flags uint8 +} + +type MemberPlacementSnapshot struct { + MemberGUID uint64 + Online bool + Fresh bool + GatewayID string + WorldserverID string + MapID uint32 + InstanceID uint32 + InstanceKnown bool + TimestampMs uint64 + UpdatedAtMs uint64 +} + +type memberStateUpdate struct { + realmID uint32 + sourceGatewayID string + sourceWorldserverID string + snapshot MemberStateSnapshot +} + +type memberStateEvent struct { + realmID uint32 + groupID uint + sourceGatewayID string + sourceWorldserverID string + receivers []uint64 + state events.GroupMemberStateUpdate +} + +type memberStateBatchKey struct { + realmID uint32 + groupID uint + sourceGatewayID string + sourceWorldserverID string + receiversKey string +} + +type memberStateBatch struct { + realmID uint32 + groupID uint + sourceGatewayID string + sourceWorldserverID string + receivers []uint64 + states []events.GroupMemberStateUpdate +} + +func subgroupMemberCount(group *repo.Group, subGroup uint8, exceptGUID uint64) int { + count := 0 + groupRealmID := groupHomeRealmID(0, group) + + for _, member := range group.Members { + if guid.SamePlayer(groupRealmID, member.MemberGUID, groupRealmID, exceptGUID) { + continue + } + + if member.SubGroup == subGroup { + count++ + } + } + + return count +} + +func nextLeaderAfterMemberLeaves(group *repo.Group, leavingGUID uint64) uint64 { + var fallback uint64 + groupRealmID := groupHomeRealmID(0, group) + for _, member := range group.Members { + if guid.SamePlayer(groupRealmID, member.MemberGUID, groupRealmID, leavingGUID) { + continue + } + + if fallback == 0 { + fallback = member.MemberGUID + } + + if member.IsOnline { + return member.MemberGUID + } + } + + return fallback +} + +func nextLeaderAfterOfflineLeader(group *repo.Group, leaderGUID uint64) uint64 { + groupRealmID := groupHomeRealmID(0, group) + if group.IsRaid() { + for _, member := range group.Members { + if !guid.SamePlayer(groupRealmID, member.MemberGUID, groupRealmID, leaderGUID) && member.IsOnline && member.IsAssistant() { + return member.MemberGUID + } + } + } + + for _, member := range group.Members { + if !guid.SamePlayer(groupRealmID, member.MemberGUID, groupRealmID, leaderGUID) && member.IsOnline { + return member.MemberGUID + } + } + + return 0 +} + +func NewGroupsService(r repo.GroupsRepo, charClient pb.CharactersServiceClient, ep events.GroupServiceProducer) GroupsService { + return &groupServiceImpl{ + r: r, + ep: ep, + charClient: charClient, + memberStateTracker: newMemberStateTracker(), + } +} + +type groupServiceImpl struct { + r repo.GroupsRepo + ep events.GroupServiceProducer + + charClient pb.CharactersServiceClient + + memberStateTracker *memberStateTracker +} + +type groupMemberOnlineStatusUpdater interface { + SetMemberOnlineStatus(ctx context.Context, realmID uint32, memberGUID uint64, online bool) (*repo.Group, error) +} + +type groupRealmIDByPlayerResolver interface { + GroupRealmIDByPlayer(ctx context.Context, realmID uint32, player uint64) (uint32, uint, error) +} + +type materializedLfgGroupRegistrar interface { + RegisterMaterializedLfgGroup(ctx context.Context, realmID uint32, group *repo.Group) error +} + +type acceptedLfgGroupRegistrar interface { + RegisterAcceptedLfgGroup(ctx context.Context, realmID uint32, group *repo.Group) error +} + +func (g *groupServiceImpl) stateTracker() *memberStateTracker { + if g.memberStateTracker == nil { + g.memberStateTracker = newMemberStateTracker() + } + return g.memberStateTracker +} + +func (g groupServiceImpl) GroupIDByPlayer(ctx context.Context, realmID uint32, player uint64) (uint, error) { + return g.r.GroupIDByPlayer(ctx, realmID, player) +} + +func (g groupServiceImpl) GroupRealmIDByPlayer(ctx context.Context, realmID uint32, player uint64) (uint32, uint, error) { + return g.groupRealmIDByPlayer(ctx, realmID, player) +} + +func (g groupServiceImpl) groupRealmIDByPlayer(ctx context.Context, realmID uint32, player uint64) (uint32, uint, error) { + if resolver, ok := g.r.(groupRealmIDByPlayerResolver); ok { + return resolver.GroupRealmIDByPlayer(ctx, realmID, player) + } + + groupID, err := g.r.GroupIDByPlayer(ctx, realmID, player) + return realmID, groupID, err +} + +func groupHomeRealmID(fallbackRealmID uint32, group *repo.Group) uint32 { + if group != nil && group.RealmID != 0 { + return group.RealmID + } + return fallbackRealmID +} + +func groupPlayerGUID(groupRealmID, requestRealmID uint32, playerGUID uint64) uint64 { + return guid.PlayerGUIDForRealm(groupRealmID, guid.PlayerRealmIDOrDefault(requestRealmID, playerGUID), playerGUID) +} + +func (g groupServiceImpl) shortOnlineCharactersForGroup(ctx context.Context, groupRealmID uint32, memberGUIDs []uint64) ([]*pb.ShortCharactersDataByGUIDsResponse_ShortCharData, error) { + if len(memberGUIDs) == 0 { + return nil, nil + } + + guidsByRealm := make(map[uint32][]uint64) + for _, memberGUID := range memberGUIDs { + if memberGUID == 0 { + continue + } + + memberRealmID := guid.PlayerRealmIDOrDefault(groupRealmID, memberGUID) + guidsByRealm[memberRealmID] = append(guidsByRealm[memberRealmID], guid.PlayerLowGUID(memberGUID)) + } + + characters := make([]*pb.ShortCharactersDataByGUIDsResponse_ShortCharData, 0, len(memberGUIDs)) + for memberRealmID, realmGUIDs := range guidsByRealm { + response, err := g.charClient.ShortOnlineCharactersDataByGUIDs(ctx, &pb.ShortCharactersDataByGUIDsRequest{ + Api: groupserver.Ver, + RealmID: memberRealmID, + GUIDs: realmGUIDs, + }) + if err != nil { + return nil, err + } + if response != nil { + characters = append(characters, response.Characters...) + } + } + + return characters, nil +} + +func (g groupServiceImpl) GroupByID(ctx context.Context, realmID uint32, groupID uint) (*repo.Group, error) { + if groupID == 0 { + return nil, nil + } + return g.r.GroupByID(ctx, realmID, groupID, true) +} + +func (g groupServiceImpl) GroupByMemberGUID(ctx context.Context, realmID uint32, memberGUID uint64) (*repo.Group, error) { + groupRealmID, groupID, err := g.groupRealmIDByPlayer(ctx, realmID, memberGUID) + if err != nil { + return nil, err + } + + return g.GroupByID(ctx, groupRealmID, groupID) +} + +func (g groupServiceImpl) MemberPlacements(_ context.Context, realmID uint32, memberGUIDs []uint64) ([]MemberPlacementSnapshot, error) { + now := time.Now() + tracker := g.stateTracker() + placements := make([]MemberPlacementSnapshot, 0, len(memberGUIDs)) + for _, memberGUID := range memberGUIDs { + snapshot := MemberPlacementSnapshot{ + MemberGUID: memberGUID, + } + placement, fresh := tracker.freshPlacement(realmID, memberGUID, now, memberPlacementFreshness) + if fresh { + snapshot.Online = placement.online + snapshot.Fresh = true + snapshot.GatewayID = placement.gatewayID + snapshot.WorldserverID = placement.worldserverID + snapshot.MapID = placement.mapID + snapshot.InstanceID = placement.instanceID + snapshot.InstanceKnown = placement.instanceKnown + snapshot.TimestampMs = placement.timestampMs + snapshot.UpdatedAtMs = uint64(placement.updatedAt.UnixMilli()) + } + placements = append(placements, snapshot) + } + return placements, nil +} + +func (g groupServiceImpl) Invite(ctx context.Context, realmID uint32, inviter, invited uint64, inviterName, invitedName string) error { + inviter = guid.NormalizePlayerGUIDForRealm(realmID, inviter) + invitedRealmID := guid.PlayerRealmIDOrDefault(realmID, invited) + invited = guid.PlayerGUIDForRealm(realmID, invitedRealmID, invited) + + _, groupID, err := g.groupRealmIDByPlayer(ctx, realmID, invited) + if err != nil { + return err + } + + if groupID != 0 { + return ErrAlreadyInGroup + } + + inviterGroupRealmID, inviterGroupID, err := g.groupRealmIDByPlayer(ctx, realmID, inviter) + if err != nil { + return err + } + + if inviterGroupID == 0 { + if err = g.r.AddInvite(ctx, invitedRealmID, repo.GroupInvite{ + Inviter: inviter, + InviterRealmID: realmID, + InviterName: inviterName, + Invitee: invited, + InviteeRealmID: invitedRealmID, + InviteeName: invitedName, + GroupID: 0, + GroupRealmID: realmID, + }); err != nil { + return err + } + + err = g.ep.InviteCreated(&events.GroupEventInviteCreatedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: realmID, + GroupID: 0, + InviterGUID: inviter, + InviterName: inviterName, + InviteeGUID: invited, + InviteeName: invitedName, + }) + + if err != nil { + log.Error().Err(err).Msg("can't create invite created event") + } + + return nil + } + + group, err := g.r.GroupByID(ctx, inviterGroupRealmID, inviterGroupID, true) + if err != nil { + return err + } + groupRealmID := groupHomeRealmID(inviterGroupRealmID, group) + inviter = groupPlayerGUID(groupRealmID, realmID, inviter) + + member := group.MemberByGUID(inviter) + if member == nil { + return fmt.Errorf("can't find player %d in the guild %d", inviter, inviterGroupID) + } + + if !guid.SamePlayer(groupRealmID, group.LeaderGUID, groupRealmID, inviter) && !member.IsAssistant() { + return ErrNoPermissions + } + + if group.IsFull() { + return ErrGroupFull + } + + invited = groupPlayerGUID(groupRealmID, invitedRealmID, invited) + if err = g.r.AddInvite(ctx, invitedRealmID, repo.GroupInvite{ + Inviter: inviter, + InviterRealmID: realmID, + InviterName: inviterName, + Invitee: invited, + InviteeRealmID: invitedRealmID, + InviteeName: invitedName, + GroupID: inviterGroupID, + GroupRealmID: groupRealmID, + }); err != nil { + return err + } + + err = g.ep.InviteCreated(&events.GroupEventInviteCreatedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: inviterGroupID, + InviterGUID: inviter, + InviterName: inviterName, + InviteeGUID: invited, + InviteeName: invitedName, + }) + + if err != nil { + log.Error().Err(err).Msg("can't create invite created event") + } + + return nil +} + +func (g groupServiceImpl) AcceptInvite(ctx context.Context, realmID uint32, player uint64) error { + invite, err := g.r.GetInviteByInvitedPlayer(ctx, realmID, player) + if err != nil { + return err + } + + if invite == nil { + return ErrInviteNotFound + } + + if invite.GroupID == 0 { + groupRealmID := invite.GroupRealmID + if groupRealmID == 0 { + groupRealmID = realmID + } + if err = g.createGroup(ctx, groupRealmID, invite); err != nil { + return err + } + g.removeInviteAfterAccept(ctx, realmID, player) + return nil + } + + groupRealmID := invite.GroupRealmID + if groupRealmID == 0 { + groupRealmID = realmID + } + group, err := g.r.GroupByID(ctx, groupRealmID, invite.GroupID, true) + if err != nil { + return err + } + if group == nil { + return ErrGroupNotFound + } + + if err = g.addMember(ctx, groupRealmID, group, invite); err != nil { + return err + } + g.removeInviteAfterAccept(ctx, realmID, player) + return nil +} + +func (g groupServiceImpl) DeclineInvite(ctx context.Context, realmID uint32, player uint64) error { + invite, err := g.r.GetInviteByInvitedPlayer(ctx, realmID, player) + if err != nil { + return err + } + + if invite == nil { + return ErrInviteNotFound + } + + if err = g.r.RemoveInvite(ctx, realmID, player); err != nil { + return err + } + + groupRealmID := invite.GroupRealmID + if groupRealmID == 0 { + groupRealmID = realmID + } + err = g.ep.InviteDeclined(&events.GroupEventInviteDeclinedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + InviterGUID: invite.Inviter, + InviteeGUID: invite.Invitee, + InviteeName: invite.InviteeName, + }) + if err != nil { + log.Error().Err(err).Msg("can't create invite declined event") + } + + return nil +} + +func (g groupServiceImpl) removeInviteAfterAccept(ctx context.Context, realmID uint32, player uint64) { + if err := g.r.RemoveInvite(ctx, realmID, player); err != nil { + log.Error(). + Err(err). + Uint32("realmID", realmID). + Uint64("player", player). + Msg("can't remove accepted group invite") + } +} + +func (g *groupServiceImpl) Uninvite(ctx context.Context, realmID uint32, initiator, target uint64, reason string) error { + if guid.SamePlayer(realmID, initiator, realmID, target) { + return ErrNoPermissions + } + + group, err := g.GroupByMemberGUID(ctx, realmID, initiator) + if err != nil { + return fmt.Errorf("can't get group, err: %w", err) + } + if group == nil { + return ErrGroupNotFound + } + groupRealmID := groupHomeRealmID(realmID, group) + initiator = groupPlayerGUID(groupRealmID, realmID, initiator) + target = groupPlayerGUID(groupRealmID, realmID, target) + + targetMember := group.MemberByGUID(target) + if targetMember == nil { + return ErrGroupNotFound + } + + if !guid.SamePlayer(groupRealmID, group.LeaderGUID, groupRealmID, initiator) { + return ErrNoPermissions + } + + membersCount := len(group.Members) + + if membersCount <= 2 { + if err = g.disband(ctx, groupRealmID, group); err != nil { + return fmt.Errorf("can't disband group, err: %w", err) + } + } else { + eventToSend := events.GroupEventGroupMemberLeftPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + MemberGUID: targetMember.MemberGUID, + MemberName: targetMember.MemberName, + NewLeaderID: group.LeaderGUID, + OnlineMembers: group.OnlineMemberGUIDs(), + } + if err = g.r.RemoveMember(ctx, groupRealmID, target); err != nil { + return fmt.Errorf("can't remove member, err: %w", err) + } + clearPendingSubGroupSwapsForMember(groupRealmID, group.ID, target) + g.stateTracker().clearMember(realmID, target) + + err = g.ep.GroupMemberLeft(&eventToSend) + if err != nil { + log.Error().Err(err).Msg("can't create GroupMemberLeft event") + } + } + + return nil +} + +func (g *groupServiceImpl) Leave(ctx context.Context, realmID uint32, player uint64) error { + group, err := g.GroupByMemberGUID(ctx, realmID, player) + if err != nil { + return fmt.Errorf("can't get group, err: %w", err) + } + if group == nil { + return ErrGroupNotFound + } + groupRealmID := groupHomeRealmID(realmID, group) + player = groupPlayerGUID(groupRealmID, realmID, player) + + member := group.MemberByGUID(player) + if member == nil { + return ErrGroupNotFound + } + + if len(group.Members) <= 2 { + return g.disband(ctx, groupRealmID, group) + } + + if guid.SamePlayer(groupRealmID, player, groupRealmID, group.LeaderGUID) { + newLeader := nextLeaderAfterMemberLeaves(group, player) + if err = g.changeLeader(ctx, groupRealmID, group, newLeader, false); err != nil { + return fmt.Errorf("can't change group leader, err: %w", err) + } + } + + eventToSend := events.GroupEventGroupMemberLeftPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + MemberGUID: member.MemberGUID, + MemberName: member.MemberName, + NewLeaderID: group.LeaderGUID, + OnlineMembers: group.OnlineMemberGUIDs(), + } + + if err = g.r.RemoveMember(ctx, groupRealmID, player); err != nil { + return fmt.Errorf("can't remove group member, err: %w", err) + } + clearPendingSubGroupSwapsForMember(groupRealmID, group.ID, player) + g.stateTracker().clearMember(realmID, player) + + err = g.ep.GroupMemberLeft(&eventToSend) + if err != nil { + log.Error().Err(err).Msg("can't create GroupMemberLeft event") + } + + return nil +} + +func (g groupServiceImpl) ChangeLeader(ctx context.Context, realmID uint32, player, newLeader uint64) error { + group, err := g.getGroupWithLeader(ctx, realmID, player) + if err != nil { + return err + } + groupRealmID := groupHomeRealmID(realmID, group) + newLeader = groupPlayerGUID(groupRealmID, realmID, newLeader) + + newLeaderMember := group.MemberByGUID(newLeader) + if newLeaderMember == nil { + return ErrGroupNotFound + } + + return g.changeLeader(ctx, groupRealmID, group, newLeader, true) +} + +func (g groupServiceImpl) ConvertToRaid(ctx context.Context, realmID uint32, player uint64) error { + group, err := g.getGroupWithLeader(ctx, realmID, player) + if err != nil { + return err + } + groupRealmID := groupHomeRealmID(realmID, group) + if group.IsLFG() { + return ErrLFGGroupRestricted + } + if len(group.Members) < 2 { + return ErrInvalidGroupOperation + } + + group.GroupType |= repo.GroupTypeFlagsRaid + if err := g.r.Update(ctx, groupRealmID, group); err != nil { + return fmt.Errorf("can't update group win a new leader, err: %w", err) + } + err = g.ep.ConvertedToRaid(&events.GroupEventGroupConvertedToRaidPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + Leader: group.LeaderGUID, + OnlineMembers: group.OnlineMemberGUIDs(), + }) + if err != nil { + log.Error().Err(err).Msg("can't create ConvertedToRaid event") + } + + return nil +} + +func (g groupServiceImpl) SendMessage(ctx context.Context, realmID uint32, senderGUID uint64, message string, lang uint32, messageType MessageType, senderChatTag uint8) error { + group, err := g.GroupByMemberGUID(ctx, realmID, senderGUID) + if err != nil { + return fmt.Errorf("can't get group, err: %w", err) + } + + if group == nil { + return ErrGroupNotFound + } + groupRealmID := groupHomeRealmID(realmID, group) + senderGUID = groupPlayerGUID(groupRealmID, realmID, senderGUID) + + member := group.MemberByGUID(senderGUID) + if member == nil { + return ErrGroupMemberNotFound + } + + requiresLeader := false + requiresLeaderOrAssistant := false + switch messageType { + case MessageTypeGroup, MessageTypeRaid: + case MessageTypeGroupLeader, MessageTypeRaidLeader: + requiresLeader = true + case MessageTypeRaidWarning: + requiresLeaderOrAssistant = true + default: + return fmt.Errorf("message with type %d unsupported", messageType) + } + + if requiresLeader && !guid.SamePlayer(groupRealmID, group.LeaderGUID, groupRealmID, senderGUID) { + return ErrNoPermissions + } + + if requiresLeaderOrAssistant && !guid.SamePlayer(groupRealmID, group.LeaderGUID, groupRealmID, senderGUID) && !member.IsAssistant() { + return ErrNoPermissions + } + + err = g.ep.SendChatMessage(&events.GroupEventNewMessagePayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + SenderGUID: senderGUID, + SenderName: member.MemberName, + SenderChatTag: senderChatTag, + Language: lang, + Msg: message, + MessageType: uint8(messageType), + Receivers: group.OnlineMemberGUIDs(), + }) + if err != nil { + log.Error().Err(err).Msg("can't create SendChatMessage event") + } + + return nil +} + +func (g groupServiceImpl) SetTargetIcon(ctx context.Context, realmID uint32, updaterGUID uint64, iconID uint8, targetGUID uint64) error { + if repo.MaxTargetIcons <= iconID { + return fmt.Errorf("iconID (%d) is invalid", iconID) + } + + group, err := g.GroupByMemberGUID(ctx, realmID, updaterGUID) + if err != nil { + return fmt.Errorf("can't get group, err: %w", err) + } + + if group == nil { + return ErrGroupNotFound + } + groupRealmID := groupHomeRealmID(realmID, group) + updaterGUID = groupPlayerGUID(groupRealmID, realmID, updaterGUID) + targetGUID = groupPlayerGUID(groupRealmID, realmID, targetGUID) + + groupMember := group.MemberByGUID(updaterGUID) + if groupMember == nil { + return ErrGroupMemberNotFound + } + if group.IsRaid() && !guid.SamePlayer(groupRealmID, group.LeaderGUID, groupRealmID, updaterGUID) && !groupMember.IsAssistant() { + return ErrNoPermissions + } + + if targetGUID != 0 { + for i, target := range group.TargetIcons { + if guid.SamePlayer(groupRealmID, target, groupRealmID, targetGUID) { + group.TargetIcons[i] = 0 + + err = g.ep.TargetIconUpdated(&events.GroupEventNewTargetIconPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + Updater: 0, + Target: 0, + IconID: uint8(i), + Receivers: group.OnlineMemberGUIDs(), + }) + if err != nil { + log.Error().Err(err).Msg("can't create TargetIconUpdated clear event") + } + + break + } + } + } + + group.TargetIcons[iconID] = targetGUID + + if err = g.r.Update(ctx, groupRealmID, group); err != nil { + return fmt.Errorf("can't update icon for the group (%d), err: %w", group.ID, err) + } + + err = g.ep.TargetIconUpdated(&events.GroupEventNewTargetIconPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + Updater: updaterGUID, + Target: targetGUID, + IconID: iconID, + Receivers: group.OnlineMemberGUIDs(), + }) + if err != nil { + log.Error().Err(err).Msg("can't create TargetIconUpdated event") + } + + return nil +} + +func (g groupServiceImpl) SetLootMethod(ctx context.Context, realmID uint32, updaterGUID uint64, method uint8, lootMaster uint64, lootThreshold uint8) error { + group, err := g.getGroupWithLeader(ctx, realmID, updaterGUID) + if err != nil { + return err + } + groupRealmID := groupHomeRealmID(realmID, group) + if group.IsLFG() { + return ErrLFGGroupRestricted + } + lootMaster = groupPlayerGUID(groupRealmID, realmID, lootMaster) + if method > uint8(repo.LootTypeNeedBeforeGreed) || + lootThreshold < uint8(repo.ItemQualityUncommon) || + lootThreshold > uint8(repo.ItemQualityArtifact) { + return ErrInvalidGroupOperation + } + if method == uint8(repo.LootTypeMasterLoot) && group.MemberByGUID(lootMaster) == nil { + return ErrInvalidGroupOperation + } + + group.LootMethod = method + group.LootThreshold = lootThreshold + group.LooterGUID = lootMaster + + if err = g.r.Update(ctx, groupRealmID, group); err != nil { + return err + } + + err = g.ep.LootTypeChanged(&events.GroupEventGroupLootTypeChangedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + NewLootType: group.LootMethod, + NewLooterGUID: group.LooterGUID, + NewLooterThreshold: group.LootThreshold, + OnlineMembers: group.OnlineMemberGUIDs(), + }) + if err != nil { + log.Error().Err(err).Msg("can't send loot changed event") + } + + return nil +} + +func (g groupServiceImpl) SetDungeonDifficulty(ctx context.Context, realmID uint32, updaterGUID uint64, difficulty uint8) error { + if difficulty >= maxDungeonDifficulty { + return ErrInvalidGroupOperation + } + + group, err := g.getGroupWithLeader(ctx, realmID, updaterGUID) + if err != nil { + return err + } + groupRealmID := groupHomeRealmID(realmID, group) + + characters, err := g.shortOnlineCharactersForGroup(ctx, groupRealmID, group.OnlineMemberGUIDs()) + if err != nil { + return fmt.Errorf("failed to get characters, err: %w", err) + } + + for _, char := range characters { + if MapID(int(char.CharMap)).IsDungeon() { + return ErrMemberInDungeonOrRaid + } + } + + group.Difficulty = difficulty + + if err = g.r.Update(ctx, groupRealmID, group); err != nil { + return err + } + + err = g.ep.GroupDifficultyChanged(&events.GroupEventGroupDifficultyChangedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + Updater: updaterGUID, + DungeonDifficulty: &difficulty, + RaidDifficulty: nil, + Receivers: group.OnlineMemberGUIDs(), + }) + if err != nil { + log.Error().Err(err).Msg("can't send difficulty changed event") + } + + return nil +} + +func (g groupServiceImpl) SetRaidDifficulty(ctx context.Context, realmID uint32, updaterGUID uint64, difficulty uint8) error { + if difficulty >= maxRaidDifficulty { + return ErrInvalidGroupOperation + } + + group, err := g.getGroupWithLeader(ctx, realmID, updaterGUID) + if err != nil { + return err + } + groupRealmID := groupHomeRealmID(realmID, group) + + characters, err := g.shortOnlineCharactersForGroup(ctx, groupRealmID, group.OnlineMemberGUIDs()) + if err != nil { + return fmt.Errorf("failed to get characters, err: %w", err) + } + + for _, char := range characters { + if MapID(int(char.CharMap)).IsRaid() { + return ErrMemberInDungeonOrRaid + } + } + + group.RaidDifficulty = difficulty + + if err = g.r.Update(ctx, groupRealmID, group); err != nil { + return err + } + + err = g.ep.GroupDifficultyChanged(&events.GroupEventGroupDifficultyChangedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + Updater: updaterGUID, + DungeonDifficulty: nil, + RaidDifficulty: &difficulty, + Receivers: group.OnlineMemberGUIDs(), + }) + if err != nil { + log.Error().Err(err).Msg("can't send difficulty changed event") + } + + return nil +} + +func (g *groupServiceImpl) HandleCharacterLoggedIn(payload events.GWEventCharacterLoggedInPayload) error { + if !g.stateTracker().recordGatewayLogin(payload) { + return nil + } + + if err := g.setGroupMemberOnlineStatus(payload.RealmID, payload.CharGUID, true); err != nil { + return err + } + + group, err := g.GroupByMemberGUID(context.Background(), payload.RealmID, payload.CharGUID) + if err != nil { + return err + } + + return g.publishMemberStateCatchup(payload.RealmID, group, payload.CharGUID) +} + +func (g *groupServiceImpl) HandleCharacterLoggedOut(payload events.GWEventCharacterLoggedOutPayload) error { + if !g.stateTracker().recordGatewayLogout(payload) { + return nil + } + + if err := g.setGroupMemberOnlineStatus(payload.RealmID, payload.CharGUID, false); err != nil { + return err + } + + return nil +} + +func (g *groupServiceImpl) HandleGatewayStarted(payload events.GWEventGatewayStartedPayload) error { + var startedAt time.Time + if payload.StartedAtMs != 0 { + startedAt = time.UnixMilli(int64(payload.StartedAtMs)) + } + + members := g.stateTracker().onlineMembersForGateway(payload.RealmID, payload.GatewayID, startedAt) + if len(members) == 0 { + return nil + } + + log.Info(). + Uint32("realmID", payload.RealmID). + Str("gatewayID", payload.GatewayID). + Uint64("startedAtMs", payload.StartedAtMs). + Int("memberCount", len(members)). + Msg("expiring gateway-owned group members after gateway start") + + nowMs := uint64(time.Now().UnixMilli()) + if payload.StartedAtMs != 0 { + nowMs = payload.StartedAtMs + } + for _, memberGUID := range members { + if err := g.setGroupMemberOnlineStatus(payload.RealmID, memberGUID, false); err != nil { + return err + } + + event, err := g.updateMemberState(context.Background(), memberStateUpdate{ + realmID: payload.RealmID, + sourceGatewayID: payload.GatewayID, + snapshot: MemberStateSnapshot{ + MemberGUID: memberGUID, + Online: false, + TimestampMs: g.stateTracker().nextTimestampAfterLast(payload.RealmID, memberGUID, nowMs), + }, + }) + if err != nil { + return err + } + if event == nil { + continue + } + if err := g.publishMemberStateBatch(&memberStateBatch{ + realmID: event.realmID, + groupID: event.groupID, + sourceGatewayID: event.sourceGatewayID, + receivers: append([]uint64(nil), event.receivers...), + states: []events.GroupMemberStateUpdate{event.state}, + }); err != nil { + return err + } + } + + return nil +} + +func (g *groupServiceImpl) setGroupMemberOnlineStatus(realmID uint32, player uint64, online bool) error { + ctx := context.Background() + + if updater, ok := g.r.(groupMemberOnlineStatusUpdater); ok { + group, err := updater.SetMemberOnlineStatus(ctx, realmID, player, online) + if err != nil { + return err + } + if group == nil { + return nil + } + + groupRealmID := groupHomeRealmID(realmID, group) + player = groupPlayerGUID(groupRealmID, realmID, player) + g.handleOfflineLeaderPromotion(ctx, groupRealmID, group) + return g.ep.GroupMemberOnlineStatusChanged(&events.GroupEventGroupMemberOnlineStatusChangedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + MemberGUID: player, + IsOnline: online, + OnlineMembers: group.OnlineMemberGUIDs(), + }) + } + + groupRealmID, groupID, err := g.groupRealmIDByPlayer(ctx, realmID, player) + if err != nil { + return err + } + + if groupID == 0 { + return nil + } + + group, err := g.GroupByID(ctx, groupRealmID, groupID) + if err != nil { + return err + } + + player = groupPlayerGUID(groupRealmID, realmID, player) + member := group.MemberByGUID(player) + if member == nil { + return nil + } + + member.IsOnline = online + g.handleOfflineLeaderPromotion(ctx, groupRealmID, group) + + return g.ep.GroupMemberOnlineStatusChanged(&events.GroupEventGroupMemberOnlineStatusChangedPayload{ ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: group.ID, - Leader: group.LeaderGUID, + RealmID: groupRealmID, + GroupID: groupID, + MemberGUID: player, + IsOnline: online, OnlineMembers: group.OnlineMemberGUIDs(), }) +} + +func (g groupServiceImpl) getGroupWithLeader(ctx context.Context, realmID uint32, leaderGUID uint64) (*repo.Group, error) { + group, err := g.GroupByMemberGUID(ctx, realmID, leaderGUID) + if err != nil { + return nil, fmt.Errorf("can't get group, err: %w", err) + } + + if group == nil { + return nil, ErrGroupNotFound + } + groupRealmID := groupHomeRealmID(realmID, group) + leaderGUID = groupPlayerGUID(groupRealmID, realmID, leaderGUID) + + if !guid.SamePlayer(groupRealmID, group.LeaderGUID, groupRealmID, leaderGUID) { + return nil, ErrNoPermissions + } + + return group, nil +} + +func (g groupServiceImpl) createGroup(ctx context.Context, realmID uint32, invite *repo.GroupInvite) error { + invite.Inviter = groupPlayerGUID(realmID, invite.InviterRealmID, invite.Inviter) + invite.Invitee = groupPlayerGUID(realmID, invite.InviteeRealmID, invite.Invitee) + group := repo.Group{ + RealmID: realmID, + LeaderGUID: invite.Inviter, + LootMethod: uint8(repo.LootTypeFreeForAll), + LooterGUID: invite.Inviter, + LootThreshold: uint8(repo.ItemQualityUncommon), + TargetIcons: [8]uint64{}, + GroupType: repo.GroupTypeFlagsNormal, + Difficulty: 0, + RaidDifficulty: 0, + MasterLooterGuid: invite.Inviter, + Members: []repo.GroupMember{ + { + RealmID: guid.PlayerRealmIDOrDefault(realmID, invite.Inviter), + MemberGUID: invite.Inviter, + MemberFlags: 0, + MemberName: invite.InviterName, + IsOnline: true, + SubGroup: 0, + Roles: 0, + }, + { + RealmID: guid.PlayerRealmIDOrDefault(realmID, invite.Invitee), + MemberGUID: invite.Invitee, + MemberFlags: 0, + MemberName: invite.InviteeName, + IsOnline: true, + SubGroup: 0, + Roles: 0, + }, + }, + } + + err := g.r.Create(ctx, realmID, &group) + if err != nil { + return err + } + + members := make([]events.GroupMember, len(group.Members)) + for i, member := range group.Members { + members[i].MemberGUID = member.MemberGUID + members[i].MemberFlags = member.MemberFlags + members[i].MemberName = member.MemberName + members[i].SubGroup = member.SubGroup + members[i].IsOnline = member.IsOnline + members[i].Roles = uint8(member.Roles) + } + + err = g.ep.GroupCreated(&events.GroupEventGroupCreatedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: realmID, + GroupID: group.ID, + LeaderGUID: group.LeaderGUID, + LootMethod: group.LootMethod, + LooterGUID: group.LooterGUID, + LootThreshold: group.LootThreshold, + GroupType: uint8(group.GroupType), + Difficulty: group.Difficulty, + RaidDifficulty: group.RaidDifficulty, + MasterLooterGuid: group.MasterLooterGuid, + LfgDungeonEntry: group.LfgDungeonEntry, + Members: members, + }) + if err != nil { + log.Error().Err(err).Msg("can't send group created event") + } + + if err := g.publishMemberStateCatchupsForOnlineMembers(realmID, &group); err != nil { + log.Error(). + Err(err). + Str("reason", "group create"). + Msg("can't send group member-state catch-up") + } + + return nil +} + +func (g groupServiceImpl) addMember(ctx context.Context, realmID uint32, group *repo.Group, invite *repo.GroupInvite) error { + if group == nil { + return ErrGroupNotFound + } + + if group.IsFull() { + return ErrGroupFull + } + + groupRealmID := groupHomeRealmID(realmID, group) + invite.Invitee = groupPlayerGUID(groupRealmID, invite.InviteeRealmID, invite.Invitee) + onlineMembers := append(group.OnlineMemberGUIDs(), invite.Invitee) + + member := repo.GroupMember{ + GroupID: invite.GroupID, + RealmID: guid.PlayerRealmIDOrDefault(groupRealmID, invite.Invitee), + MemberGUID: invite.Invitee, + MemberFlags: 0, + MemberName: invite.InviteeName, + IsOnline: true, + SubGroup: 0, + Roles: 0, + } + + err := g.r.AddMember(ctx, groupRealmID, &member) + if err != nil { + return err + } + if group.MemberByGUID(invite.Invitee) == nil { + group.Members = append(group.Members, member) + } + + err = g.ep.MemberAdded(&events.GroupEventGroupMemberAddedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + MemberGUID: invite.Invitee, + MemberName: invite.InviteeName, + OnlineMembers: onlineMembers, + }) + if err != nil { + log.Error().Err(err).Msg("can't send group member added event") + } + + if err := g.publishMemberStateCatchupsForOnlineMembers(groupRealmID, group); err != nil { + log.Error(). + Err(err). + Str("reason", "member add"). + Msg("can't send group member-state catch-up") + } + + return nil +} + +func (g *groupServiceImpl) disband(ctx context.Context, realmID uint32, group *repo.Group) error { + players := group.OnlineMemberGUIDs() + clearReadyCheckTimeout(realmID, group.ID) + clearGroupOfflineLeaderPromotionTimers(realmID, group.ID) + clearPendingSubGroupSwapsForGroup(realmID, group.ID) + + err := g.r.Delete(ctx, realmID, group.ID) + if err != nil { + return fmt.Errorf("can't delete group, err: %w", err) + } + g.stateTracker().clearGroup(realmID, group.Members) + + err = g.publishGroupDisband(realmID, group.ID, players) + if err != nil { + log.Error().Err(err).Msg("can't create GroupDisband event") + } + + return nil +} + +func (g groupServiceImpl) changeLeader(ctx context.Context, realmID uint32, group *repo.Group, newLeader uint64, needsEventUpdate bool) error { + prevLeader := group.LeaderGUID + realmID = groupHomeRealmID(realmID, group) + newLeader = groupPlayerGUID(realmID, realmID, newLeader) + clearGroupOfflineLeaderPromotionTimers(realmID, group.ID) + + group.LeaderGUID = newLeader + if err := g.r.Update(ctx, realmID, group); err != nil { + return fmt.Errorf("can't update group win a new leader, err: %w", err) + } + if needsEventUpdate { + err := g.ep.LeaderChanged(&events.GroupEventGroupLeaderChangedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: realmID, + GroupID: group.ID, + PreviousLeader: prevLeader, + NewLeader: newLeader, + OnlineMembers: group.OnlineMemberGUIDs(), + }) + if err != nil { + log.Error().Err(err).Msg("can't create LeaderChanged event") + } + } + + return nil +} + +func (g groupServiceImpl) handleOfflineLeaderPromotion(ctx context.Context, realmID uint32, group *repo.Group) { + if group == nil || group.LeaderGUID == 0 { + return + } + + leader := group.MemberByGUID(group.LeaderGUID) + if leader == nil || leader.IsOnline { + clearOfflineLeaderPromotionTimer(realmID, group.ID, group.LeaderGUID) + return + } + + if nextLeaderAfterOfflineLeader(group, group.LeaderGUID) == 0 { + clearOfflineLeaderPromotionTimer(realmID, group.ID, group.LeaderGUID) + return + } + + sequence, queued := queueOfflineLeaderPromotionTimer(realmID, group.ID, group.LeaderGUID) + if !queued { + return + } + + groupID := group.ID + leaderGUID := group.LeaderGUID + go func() { + timer := time.NewTimer(offlineLeaderPromotionDelay) + defer timer.Stop() + + <-timer.C + if err := g.promoteOfflineLeaderIfStillNeeded(context.Background(), realmID, groupID, leaderGUID, sequence); err != nil { + log.Error().Err(err).Uint32("realm_id", realmID).Uint("group_id", groupID).Uint64("leader_guid", leaderGUID).Msg("can't promote offline group leader") + } + }() +} + +func (g groupServiceImpl) promoteOfflineLeaderIfStillNeeded(ctx context.Context, realmID uint32, groupID uint, leaderGUID uint64, sequence uint64) error { + if !consumeOfflineLeaderPromotionTimer(realmID, groupID, leaderGUID, sequence) { + return nil + } + + group, err := g.GroupByID(ctx, realmID, groupID) + if err != nil { + return err + } + if group == nil || !guid.SamePlayer(realmID, group.LeaderGUID, realmID, leaderGUID) { + return nil + } + + leader := group.MemberByGUID(leaderGUID) + if leader == nil || leader.IsOnline { + return nil + } + + newLeader := nextLeaderAfterOfflineLeader(group, leaderGUID) + if newLeader == 0 { + return nil + } + + return g.changeLeader(ctx, realmID, group, newLeader, true) +} + +func (g groupServiceImpl) StartReadyCheck(ctx context.Context, realmID uint32, leaderGUID uint64, durationMs uint32) error { + if durationMs == 0 { + durationMs = 35000 + } + + group, err := g.GroupByMemberGUID(ctx, realmID, leaderGUID) + if err != nil { + return err + } + if group == nil { + return ErrGroupNotFound + } + groupRealmID := groupHomeRealmID(realmID, group) + leaderGUID = groupPlayerGUID(groupRealmID, realmID, leaderGUID) + + member := group.MemberByGUID(leaderGUID) + if member == nil { + return ErrGroupMemberNotFound + } + + if !guid.SamePlayer(groupRealmID, group.LeaderGUID, groupRealmID, leaderGUID) && !member.IsAssistant() { + return ErrNoPermissions + } + + receivers := group.OnlineMemberGUIDs() + + if err := g.ep.GroupReadyCheckStarted(&events.GroupEventReadyCheckStartedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + LeaderGUID: leaderGUID, + DurationMs: durationMs, + Receivers: receivers, + }); err != nil { + return err + } + + for _, groupMember := range group.Members { + if groupMember.IsOnline { + continue + } + + if err := g.ep.GroupReadyCheckMemberState(&events.GroupEventReadyCheckMemberStatePayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + MemberGUID: groupMember.MemberGUID, + State: 0, + Receivers: receivers, + }); err != nil { + return err + } + } + + sequence := startReadyCheckTimeout(groupRealmID, group.ID) + go func(realmID uint32, groupID uint, durationMs uint32, sequence uint64) { + time.Sleep(time.Duration(durationMs) * time.Millisecond) + if !consumeReadyCheckTimeout(realmID, groupID, sequence) { + return + } + + group, err := g.GroupByID(context.Background(), realmID, groupID) + if err != nil { + log.Error().Err(err).Uint32("realm_id", realmID).Uint("group_id", groupID).Msg("can't load group for ready check timeout") + return + } + if group == nil { + return + } + + _ = g.ep.GroupReadyCheckFinished(&events.GroupEventReadyCheckFinishedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: realmID, + GroupID: groupID, + Receivers: group.OnlineMemberGUIDs(), + }) + }(groupRealmID, group.ID, durationMs, sequence) + + return nil +} + +func (g groupServiceImpl) SetReadyCheckMemberState(ctx context.Context, realmID uint32, memberGUID uint64, state uint8) error { + if state > 2 { + state = 2 + } + + group, err := g.GroupByMemberGUID(ctx, realmID, memberGUID) + if err != nil { + return err + } + if group == nil { + return ErrGroupNotFound + } + groupRealmID := groupHomeRealmID(realmID, group) + memberGUID = groupPlayerGUID(groupRealmID, realmID, memberGUID) + + if group.MemberByGUID(memberGUID) == nil { + return ErrGroupMemberNotFound + } + + return g.ep.GroupReadyCheckMemberState(&events.GroupEventReadyCheckMemberStatePayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + MemberGUID: memberGUID, + State: state, + Receivers: group.OnlineMemberGUIDs(), + }) +} + +func (g groupServiceImpl) FinishReadyCheck(ctx context.Context, realmID uint32, playerGUID uint64) error { + group, err := g.GroupByMemberGUID(ctx, realmID, playerGUID) if err != nil { - log.Error().Err(err).Msg("can't create ConvertedToRaid event") + return err + } + if group == nil { + return ErrGroupNotFound + } + groupRealmID := groupHomeRealmID(realmID, group) + playerGUID = groupPlayerGUID(groupRealmID, realmID, playerGUID) + + member := group.MemberByGUID(playerGUID) + if member == nil { + return ErrGroupMemberNotFound + } + + if !guid.SamePlayer(groupRealmID, group.LeaderGUID, groupRealmID, playerGUID) && !member.IsAssistant() { + return ErrNoPermissions + } + + clearReadyCheckTimeout(groupRealmID, group.ID) + + return g.ep.GroupReadyCheckFinished(&events.GroupEventReadyCheckFinishedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + Receivers: group.OnlineMemberGUIDs(), + }) +} + +func (g groupServiceImpl) ChangeMemberSubGroup(ctx context.Context, realmID uint32, updaterGUID, memberGUID uint64, subGroup uint8) error { + queueSwap := subGroup&subGroupSwapPendingFlag != 0 + subGroup &^= subGroupSwapPendingFlag + + if subGroup >= 8 { + return ErrNoPermissions + } + + group, err := g.GroupByMemberGUID(ctx, realmID, updaterGUID) + if err != nil { + return err + } + if group == nil { + return ErrGroupNotFound + } + groupRealmID := groupHomeRealmID(realmID, group) + updaterGUID = groupPlayerGUID(groupRealmID, realmID, updaterGUID) + memberGUID = groupPlayerGUID(groupRealmID, realmID, memberGUID) + + updater := group.MemberByGUID(updaterGUID) + if updater == nil { + return ErrGroupMemberNotFound + } + + if !guid.SamePlayer(groupRealmID, group.LeaderGUID, groupRealmID, updaterGUID) && !updater.IsAssistant() { + return ErrNoPermissions + } + + member := group.MemberByGUID(memberGUID) + if member == nil { + return ErrGroupMemberNotFound + } + + if member.SubGroup == subGroup { + return nil + } + + if pending, ok := consumePendingSubGroupSwap(groupRealmID, group.ID, updaterGUID, memberGUID, member.SubGroup, subGroup); ok { + firstMember := group.MemberByGUID(pending.memberGUID) + if firstMember == nil { + return ErrGroupMemberNotFound + } + if firstMember.SubGroup != pending.fromSubGroup { + return ErrGroupFull + } + + firstMember.SubGroup = pending.toSubGroup + member.SubGroup = subGroup + + if err := g.r.UpdateMember(ctx, groupRealmID, firstMember); err != nil { + return err + } + + if err := g.r.UpdateMember(ctx, groupRealmID, member); err != nil { + return err + } + + receivers := group.OnlineMemberGUIDs() + if err := g.ep.GroupMemberSubGroupChanged(&events.GroupEventMemberSubGroupChangedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + MemberGUID: firstMember.MemberGUID, + SubGroup: firstMember.SubGroup, + Receivers: receivers, + }); err != nil { + return err + } + + return g.ep.GroupMemberSubGroupChanged(&events.GroupEventMemberSubGroupChangedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + MemberGUID: memberGUID, + SubGroup: subGroup, + Receivers: receivers, + }) + } + + if subgroupMemberCount(group, subGroup, memberGUID) >= repo.MaxGroupSize { + if queueSwap { + return queuePendingSubGroupSwap(groupRealmID, group.ID, updaterGUID, memberGUID, member.SubGroup, subGroup) + } + + return ErrGroupFull + } + + member.SubGroup = subGroup + + if err := g.r.UpdateMember(ctx, groupRealmID, member); err != nil { + return err + } + + return g.ep.GroupMemberSubGroupChanged(&events.GroupEventMemberSubGroupChangedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + MemberGUID: memberGUID, + SubGroup: subGroup, + Receivers: group.OnlineMemberGUIDs(), + }) +} + +func (g groupServiceImpl) SetMemberFlags(ctx context.Context, realmID uint32, updaterGUID, memberGUID uint64, flags, roles uint8) error { + group, err := g.GroupByMemberGUID(ctx, realmID, updaterGUID) + if err != nil { + return err + } + if group == nil { + return ErrGroupNotFound + } + groupRealmID := groupHomeRealmID(realmID, group) + updaterGUID = groupPlayerGUID(groupRealmID, realmID, updaterGUID) + memberGUID = groupPlayerGUID(groupRealmID, realmID, memberGUID) + + updater := group.MemberByGUID(updaterGUID) + if updater == nil { + return ErrGroupMemberNotFound + } + + if !guid.SamePlayer(groupRealmID, group.LeaderGUID, groupRealmID, updaterGUID) && !updater.IsAssistant() { + return ErrNoPermissions + } + + member := group.MemberByGUID(memberGUID) + if member == nil { + return ErrGroupMemberNotFound + } + + if member.MemberFlags&repo.MemberFlagAssistant != flags&repo.MemberFlagAssistant && !guid.SamePlayer(groupRealmID, group.LeaderGUID, groupRealmID, updaterGUID) { + return ErrNoPermissions + } + + receivers := group.OnlineMemberGUIDs() + updatedMembers := make([]repo.GroupMember, 0, 3) + + uniqueFlags := flags & (repo.MemberFlagMainTank | repo.MemberFlagMainAssistant) + if uniqueFlags != 0 { + for i := range group.Members { + if guid.SamePlayer(groupRealmID, group.Members[i].MemberGUID, groupRealmID, memberGUID) || group.Members[i].MemberFlags&uniqueFlags == 0 { + continue + } + + group.Members[i].MemberFlags &^= uniqueFlags + if err := g.r.UpdateMember(ctx, groupRealmID, &group.Members[i]); err != nil { + return err + } + + updatedMembers = append(updatedMembers, group.Members[i]) + } + } + + if member.MemberFlags != flags || uint8(member.Roles) != roles { + member.MemberFlags = flags + member.Roles = repo.RoleFlags(roles) + + if err := g.r.UpdateMember(ctx, groupRealmID, member); err != nil { + return err + } + + updatedMembers = append(updatedMembers, *member) + } + + if len(updatedMembers) == 0 { + return nil + } + + for _, updatedMember := range updatedMembers { + if err := g.ep.GroupMemberFlagsChanged(&events.GroupEventMemberFlagsChangedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: groupRealmID, + GroupID: group.ID, + MemberGUID: updatedMember.MemberGUID, + Flags: updatedMember.MemberFlags, + Roles: uint8(updatedMember.Roles), + Receivers: receivers, + }); err != nil { + return err + } + } + + return nil +} + +func (g *groupServiceImpl) RegisterMaterializedLfgGroup(ctx context.Context, realmID uint32, groupID uint, leaderGUID uint64, groupType, difficulty, raidDifficulty uint8, members []MaterializedLfgGroupMember) error { + if realmID == 0 || groupID == 0 || leaderGUID == 0 || len(members) == 0 { + return nil + } + + registrar, ok := g.r.(materializedLfgGroupRegistrar) + if !ok { + return fmt.Errorf("group repo does not support materialized LFG group registration") + } + + group := &repo.Group{ + ID: groupID, + RealmID: realmID, + LeaderGUID: groupPlayerGUID(realmID, realmID, leaderGUID), + GroupType: repo.GroupTypeFlags(groupType), + Difficulty: difficulty, + RaidDifficulty: raidDifficulty, + Members: make([]repo.GroupMember, 0, len(members)), + } + + seenMembers := make(map[uint64]struct{}, len(members)) + for _, member := range members { + if member.PlayerGUID == 0 || member.RealmID == 0 { + continue + } + + memberGUID := groupPlayerGUID(realmID, member.RealmID, member.PlayerGUID) + if _, ok := seenMembers[memberGUID]; ok { + continue + } + seenMembers[memberGUID] = struct{}{} + + memberOnline := member.Online + if !memberOnline && g.stateTracker().memberHasOnlineEvidence(realmID, memberGUID) { + memberOnline = true + if event := groupstatetrace.Event(nil, "groupserver.materialized_lfg_member_online_recovered", memberGUID); event != nil { + event. + Uint32("realmID", guid.PlayerRealmIDOrDefault(realmID, memberGUID)). + Uint64("memberGUID", memberGUID). + Uint("groupID", groupID). + Msg(groupstatetrace.Message) + } + } + + group.Members = append(group.Members, repo.GroupMember{ + GroupID: groupID, + RealmID: guid.PlayerRealmIDOrDefault(realmID, memberGUID), + MemberGUID: memberGUID, + MemberFlags: member.Flags, + MemberName: member.Name, + IsOnline: memberOnline, + SubGroup: member.SubGroup, + Roles: repo.RoleFlags(member.Roles), + }) + } + + if len(group.Members) == 0 { + return nil + } + + if group.MemberByGUID(group.LeaderGUID) == nil { + group.LeaderGUID = group.Members[0].MemberGUID + } + + oldGroups, err := g.materializedLfgSupersededGroups(ctx, realmID, group) + if err != nil { + return err + } + canonicalGroup := materializedLfgCanonicalAcceptedGroup(realmID, group, oldGroups) + if canonicalGroup != nil { + realmID = canonicalGroup.realmID + group.ID = canonicalGroup.group.ID + group.RealmID = canonicalGroup.realmID + group.LfgDungeonEntry = canonicalGroup.group.LfgDungeonEntry + group.LeaderGUID = groupPlayerGUID(realmID, guid.PlayerRealmIDOrDefault(realmID, group.LeaderGUID), guid.PlayerLowGUID(group.LeaderGUID)) + for i := range group.Members { + memberRealmID := guid.PlayerRealmIDOrDefault(realmID, group.Members[i].MemberGUID) + group.Members[i].GroupID = group.ID + group.Members[i].RealmID = memberRealmID + group.Members[i].MemberGUID = groupPlayerGUID(realmID, memberRealmID, guid.PlayerLowGUID(group.Members[i].MemberGUID)) + } + } + for _, oldGroup := range oldGroups { + if oldGroup.group == nil { + continue + } + if canonicalGroup != nil && oldGroup.realmID == canonicalGroup.realmID && oldGroup.group.ID == canonicalGroup.group.ID { + continue + } + if err := g.removeAcceptedLfgOldGroup(ctx, oldGroup.realmID, oldGroup.group); err != nil { + return err + } + } + + if err := registrar.RegisterMaterializedLfgGroup(ctx, realmID, group); err != nil { + return err + } + + log.Debug(). + Uint32("realmID", realmID). + Uint("groupID", groupID). + Uint("canonicalGroupID", group.ID). + Uint64("leaderGUID", group.LeaderGUID). + Uint8("groupType", groupType). + Int("memberCount", len(group.Members)). + Msg("registered materialized LFG group for clustered member state") + + if err := g.publishMemberStateCatchupsForOnlineMembers(realmID, group); err != nil { + log.Error(). + Err(err). + Str("reason", "materialized LFG group registration"). + Msg("can't send group member-state catch-up") + } + + return nil +} + +func materializedLfgCanonicalAcceptedGroup(realmID uint32, group *repo.Group, oldGroups []acceptedLfgExistingGroup) *acceptedLfgExistingGroup { + if group == nil || len(group.Members) == 0 { + return nil + } + + for i := range oldGroups { + oldGroup := oldGroups[i] + if materializedLfgMatchesAcceptedGroup(realmID, group, oldGroup.realmID, oldGroup.group) { + return &oldGroups[i] + } + } + + return nil +} + +func materializedLfgMatchesAcceptedGroup(materializedRealmID uint32, materializedGroup *repo.Group, acceptedRealmID uint32, acceptedGroup *repo.Group) bool { + if materializedGroup == nil || acceptedGroup == nil || acceptedGroup.ID == 0 { + return false + } + if acceptedGroup.GroupType&repo.GroupTypeFlagsLFG == 0 { + return false + } + if len(materializedGroup.Members) != len(acceptedGroup.Members) { + return false + } + + leaderRealmID := guid.PlayerRealmIDOrDefault(materializedRealmID, materializedGroup.LeaderGUID) + leaderGUID := groupPlayerGUID(acceptedRealmID, leaderRealmID, guid.PlayerLowGUID(materializedGroup.LeaderGUID)) + if acceptedGroup.MemberByGUID(leaderGUID) == nil { + return false + } + + for _, member := range materializedGroup.Members { + memberRealmID := guid.PlayerRealmIDOrDefault(materializedRealmID, member.MemberGUID) + memberGUID := groupPlayerGUID(acceptedRealmID, memberRealmID, guid.PlayerLowGUID(member.MemberGUID)) + if acceptedGroup.MemberByGUID(memberGUID) == nil { + return false + } } - return nil + return true +} + +func (g *groupServiceImpl) materializedLfgSupersededGroups(ctx context.Context, realmID uint32, group *repo.Group) ([]acceptedLfgExistingGroup, error) { + if group == nil || group.ID == 0 { + return nil, nil + } + + seenGroups := map[acceptedLfgGroupKey]struct{}{} + oldGroups := make([]acceptedLfgExistingGroup, 0) + currentGroupKey := acceptedLfgGroupKey{realmID: realmID, groupID: group.ID} + + for _, member := range group.Members { + if member.MemberGUID == 0 { + continue + } + + memberRealmID := guid.PlayerRealmIDOrDefault(realmID, member.MemberGUID) + memberGUID := guid.PlayerLowGUID(member.MemberGUID) + groupRealmID, groupID, err := g.groupRealmIDByPlayer(ctx, memberRealmID, memberGUID) + if err != nil || groupID == 0 { + return nil, err + } + + key := acceptedLfgGroupKey{realmID: groupRealmID, groupID: groupID} + if key == currentGroupKey { + continue + } + if _, ok := seenGroups[key]; ok { + continue + } + seenGroups[key] = struct{}{} + + oldGroup, err := g.GroupByID(ctx, groupRealmID, groupID) + if err != nil { + return nil, err + } + if oldGroup == nil { + continue + } + + oldGroups = append(oldGroups, acceptedLfgExistingGroup{realmID: groupRealmID, group: oldGroup}) + } + + return oldGroups, nil } -func (g groupServiceImpl) SendMessage(ctx context.Context, realmID uint32, senderGUID uint64, message string, lang uint32, messageType MessageType) error { - groupID, err := g.r.GroupIDByPlayer(ctx, realmID, senderGUID) +func (g *groupServiceImpl) RegisterAcceptedLfgGroup(ctx context.Context, realmID, proposalID, dungeonEntry, leaderRealmID uint32, leaderGUID uint64, crossRealm bool, members []AcceptedLfgGroupMember) (uint, error) { + groupRealmID := leaderRealmID + if groupRealmID == 0 { + groupRealmID = realmID + } + if groupRealmID == 0 || leaderGUID == 0 || len(members) == 0 { + return 0, nil + } + + registrar, ok := g.r.(acceptedLfgGroupRegistrar) + if !ok { + return 0, fmt.Errorf("group repo does not support accepted LFG group registration") + } + + baseRealmID, baseGroup, oldGroups, err := g.acceptedLfgBaseGroup(ctx, groupRealmID, leaderRealmID, leaderGUID, members) if err != nil { - return fmt.Errorf("can't get groupID, err: %w", err) + return 0, err } - if groupID == 0 { - return ErrGroupNotFound + if baseGroup != nil { + groupRealmID = baseRealmID + } + for _, oldGroup := range oldGroups { + if oldGroup.group == nil { + continue + } + if err := g.removeAcceptedLfgOldGroup(ctx, oldGroup.realmID, oldGroup.group); err != nil { + return 0, err + } } - group, err := g.r.GroupByID(ctx, realmID, groupID, true) + namesByGUID, onlineByGUID, err := g.acceptedLfgMemberNames(ctx, groupRealmID, members) if err != nil { - return fmt.Errorf("can't get group, err: %w", err) + return 0, err } - if group == nil { - return ErrGroupNotFound + group := acceptedLfgGroupFromMembers(groupRealmID, leaderRealmID, leaderGUID, dungeonEntry, baseGroup, members, namesByGUID, onlineByGUID) + if group == nil || len(group.Members) == 0 { + return 0, nil } - member := group.MemberByGUID(senderGUID) - if member == nil { - return ErrGroupMemberNotFound + if err := registrar.RegisterAcceptedLfgGroup(ctx, groupRealmID, group); err != nil { + return 0, err + } + if group.ID == 0 { + return 0, fmt.Errorf("accepted LFG group registration did not allocate a group id") } - isLeader := false - switch messageType { - case MessageTypeGroup, MessageTypeRaid: - isLeader = false - case MessageTypeGroupLeader, MessageTypeRaidLeader: - isLeader = true - default: - return fmt.Errorf("message with type %d unsupported", messageType) + log.Debug(). + Uint32("realmID", realmID). + Uint32("groupRealmID", groupRealmID). + Uint32("proposalID", proposalID). + Uint32("dungeonEntry", dungeonEntry). + Uint("groupID", group.ID). + Uint64("leaderGUID", group.LeaderGUID). + Bool("crossRealm", crossRealm). + Int("memberCount", len(group.Members)). + Msg("registered accepted LFG group for clustered travel state") + + if err := g.publishGroupCreated(groupRealmID, group); err != nil { + log.Error(). + Err(err). + Uint32("realmID", groupRealmID). + Uint("groupID", group.ID). + Msg("can't send accepted LFG group created event") } - if isLeader && group.LeaderGUID != senderGUID { - return ErrNoPermissions + return group.ID, nil +} + +type acceptedLfgExistingGroup struct { + realmID uint32 + group *repo.Group +} + +type acceptedLfgGroupKey struct { + realmID uint32 + groupID uint +} + +func (g *groupServiceImpl) acceptedLfgBaseGroup(ctx context.Context, fallbackRealmID, leaderRealmID uint32, leaderGUID uint64, members []AcceptedLfgGroupMember) (uint32, *repo.Group, []acceptedLfgExistingGroup, error) { + seenGroups := map[acceptedLfgGroupKey]struct{}{} + groups := make([]acceptedLfgExistingGroup, 0, len(members)) + var leaderGroupKey acceptedLfgGroupKey + leaderHasGroup := false + leaderIdentityRealmID := leaderRealmID + if leaderIdentityRealmID == 0 { + leaderIdentityRealmID = fallbackRealmID } - err = g.ep.SendChatMessage(&events.GroupEventNewMessagePayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: group.ID, - SenderGUID: senderGUID, - SenderName: member.MemberName, - Language: lang, - Msg: message, - MessageType: uint8(messageType), - Receivers: group.OnlineMemberGUIDs(), - }) + tryPlayer := func(playerRealmID uint32, playerGUID uint64) (*acceptedLfgGroupKey, error) { + if playerRealmID == 0 { + playerRealmID = fallbackRealmID + } + if playerRealmID == 0 || playerGUID == 0 { + return nil, nil + } + + groupRealmID, groupID, err := g.groupRealmIDByPlayer(ctx, playerRealmID, playerGUID) + if err != nil || groupID == 0 { + return nil, err + } + key := acceptedLfgGroupKey{realmID: groupRealmID, groupID: groupID} + if _, ok := seenGroups[key]; ok { + return &key, nil + } + seenGroups[key] = struct{}{} + + group, err := g.GroupByID(ctx, groupRealmID, groupID) + if err != nil || group == nil { + return nil, err + } + groups = append(groups, acceptedLfgExistingGroup{realmID: groupRealmID, group: group}) + return &key, nil + } + + if key, err := tryPlayer(leaderRealmID, leaderGUID); err != nil { + return 0, nil, nil, err + } else if key != nil { + leaderGroupKey = *key + leaderHasGroup = true + } + for _, member := range members { + memberRealmID := acceptedLfgMemberRealmID(fallbackRealmID, member) + key, err := tryPlayer(memberRealmID, member.PlayerGUID) + if err != nil { + return 0, nil, nil, err + } + if key != nil && !leaderHasGroup && guid.SamePlayer(leaderIdentityRealmID, leaderGUID, memberRealmID, member.PlayerGUID) { + leaderGroupKey = *key + leaderHasGroup = true + } + } + + if len(groups) == 0 { + return fallbackRealmID, nil, nil, nil + } + if !leaderHasGroup { + return fallbackRealmID, nil, groups, nil + } + + oldGroups := make([]acceptedLfgExistingGroup, 0, len(groups)-1) + var baseGroup *repo.Group + baseRealmID := fallbackRealmID + for _, existing := range groups { + if existing.group == nil { + continue + } + key := acceptedLfgGroupKey{realmID: existing.realmID, groupID: existing.group.ID} + if key == leaderGroupKey { + baseRealmID = existing.realmID + baseGroup = existing.group + continue + } + oldGroups = append(oldGroups, existing) + } + + if baseGroup == nil { + return fallbackRealmID, nil, groups, nil + } + return baseRealmID, baseGroup, oldGroups, nil +} + +func (g *groupServiceImpl) acceptedLfgMemberNames(ctx context.Context, groupRealmID uint32, members []AcceptedLfgGroupMember) (map[uint64]string, map[uint64]bool, error) { + namesByGUID := map[uint64]string{} + onlineByGUID := map[uint64]bool{} + if g.charClient == nil { + return namesByGUID, onlineByGUID, nil + } + + memberGUIDs := make([]uint64, 0, len(members)) + seen := make(map[uint64]struct{}, len(members)) + for _, member := range members { + memberRealmID := acceptedLfgMemberRealmID(groupRealmID, member) + if member.PlayerGUID == 0 || memberRealmID == 0 { + continue + } + memberGUID := groupPlayerGUID(groupRealmID, memberRealmID, member.PlayerGUID) + if _, ok := seen[memberGUID]; ok { + continue + } + seen[memberGUID] = struct{}{} + memberGUIDs = append(memberGUIDs, memberGUID) + } + + characters, err := g.shortOnlineCharactersForGroup(ctx, groupRealmID, memberGUIDs) if err != nil { - log.Error().Err(err).Msg("can't create SendChatMessage event") + return nil, nil, err + } + for _, character := range characters { + if character == nil || character.GetRealmID() == 0 || character.GetCharGUID() == 0 { + continue + } + memberGUID := groupPlayerGUID(groupRealmID, character.GetRealmID(), character.GetCharGUID()) + namesByGUID[memberGUID] = character.GetCharName() + onlineByGUID[memberGUID] = character.GetIsOnline() + } + + return namesByGUID, onlineByGUID, nil +} + +func acceptedLfgGroupFromMembers(groupRealmID, leaderRealmID uint32, leaderGUID uint64, dungeonEntry uint32, baseGroup *repo.Group, members []AcceptedLfgGroupMember, namesByGUID map[uint64]string, onlineByGUID map[uint64]bool) *repo.Group { + leader := groupPlayerGUID(groupRealmID, leaderRealmID, leaderGUID) + group := &repo.Group{ + RealmID: groupRealmID, + LeaderGUID: leader, + LootMethod: uint8(repo.LootTypeNeedBeforeGreed), + LooterGUID: leader, + LootThreshold: uint8(repo.ItemQualityUncommon), + GroupType: repo.GroupTypeFlagsLFG | repo.GroupTypeFlagsLFGRestricted, + MasterLooterGuid: 0, + LfgDungeonEntry: dungeonEntry, + } + existingMembers := map[uint64]repo.GroupMember{} + if baseGroup != nil { + group = cloneGroup(baseGroup) + group.RealmID = groupRealmID + group.LeaderGUID = leader + group.GroupType |= repo.GroupTypeFlagsLFG + group.LfgDungeonEntry = dungeonEntry + for _, member := range baseGroup.Members { + existingMembers[member.MemberGUID] = member + } + } + + seen := make(map[uint64]struct{}, len(members)) + group.Members = make([]repo.GroupMember, 0, len(members)) + for _, member := range members { + memberRealmID := acceptedLfgMemberRealmID(groupRealmID, member) + if member.PlayerGUID == 0 || memberRealmID == 0 { + continue + } + + memberGUID := groupPlayerGUID(groupRealmID, memberRealmID, member.PlayerGUID) + if _, ok := seen[memberGUID]; ok { + continue + } + seen[memberGUID] = struct{}{} + + role := member.AssignedRole + if role == 0 { + role = member.SelectedRoles + } + + memberName := namesByGUID[memberGUID] + if memberName == "" { + if existingMember, ok := existingMembers[memberGUID]; ok { + memberName = existingMember.MemberName + } + } + + online := true + if knownOnline, ok := onlineByGUID[memberGUID]; ok { + online = knownOnline + } + + group.Members = append(group.Members, repo.GroupMember{ + GroupID: group.ID, + RealmID: guid.PlayerRealmIDOrDefault(groupRealmID, memberGUID), + MemberGUID: memberGUID, + MemberName: memberName, + IsOnline: online, + Roles: repo.RoleFlags(role), + }) + } + + if len(group.Members) == 0 { + return nil + } + if group.MemberByGUID(group.LeaderGUID) == nil { + group.LeaderGUID = group.Members[0].MemberGUID + } + if group.LooterGUID == 0 { + group.LooterGUID = group.LeaderGUID + } + + return group +} + +func acceptedLfgMemberRealmID(fallbackRealmID uint32, member AcceptedLfgGroupMember) uint32 { + if member.RealmID != 0 { + return member.RealmID + } + if member.QueueLeaderRealmID != 0 { + return member.QueueLeaderRealmID + } + return fallbackRealmID +} + +func (g *groupServiceImpl) removeAcceptedLfgOldGroup(ctx context.Context, realmID uint32, group *repo.Group) error { + if group == nil { + return nil + } + + players := group.OnlineMemberGUIDs() + clearReadyCheckTimeout(realmID, group.ID) + clearGroupOfflineLeaderPromotionTimers(realmID, group.ID) + clearPendingSubGroupSwapsForGroup(realmID, group.ID) + + if err := g.r.Delete(ctx, realmID, group.ID); err != nil { + return fmt.Errorf("can't delete accepted LFG superseded group, err: %w", err) + } + + if err := g.publishGroupDisband(realmID, group.ID, players); err != nil { + log.Error().Err(err).Msg("can't create accepted LFG superseded GroupDisband event") } return nil } -func (g groupServiceImpl) SetTargetIcon(ctx context.Context, realmID uint32, updaterGUID uint64, iconID uint8, targetGUID uint64) error { - if repo.MaxTargetIcons <= iconID { - return fmt.Errorf("iconID (%d) is invalid", iconID) +func (g *groupServiceImpl) publishGroupDisband(realmID uint32, groupID uint, onlineMembers []uint64) error { + return g.ep.GroupDisband(&events.GroupEventGroupDisbandPayload{ + ServiceID: groupserver.ServiceID, + RealmID: realmID, + GroupID: groupID, + OnlineMembers: onlineMembers, + }) +} + +func (g *groupServiceImpl) publishGroupCreated(realmID uint32, group *repo.Group) error { + members := make([]events.GroupMember, len(group.Members)) + for i, member := range group.Members { + members[i].MemberGUID = member.MemberGUID + members[i].MemberFlags = member.MemberFlags + members[i].MemberName = member.MemberName + members[i].SubGroup = member.SubGroup + members[i].IsOnline = member.IsOnline + members[i].Roles = uint8(member.Roles) + } + + return g.ep.GroupCreated(&events.GroupEventGroupCreatedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: realmID, + GroupID: group.ID, + LeaderGUID: group.LeaderGUID, + LootMethod: group.LootMethod, + LooterGUID: group.LooterGUID, + LootThreshold: group.LootThreshold, + GroupType: uint8(group.GroupType), + Difficulty: group.Difficulty, + RaidDifficulty: group.RaidDifficulty, + MasterLooterGuid: group.MasterLooterGuid, + LfgDungeonEntry: group.LfgDungeonEntry, + Members: members, + }) +} + +func (g *groupServiceImpl) UpdateMemberState(ctx context.Context, realmID uint32, memberGUID uint64, online bool, level, class uint8, zoneID, mapID uint32, health, maxHealth uint32, powerType uint8, power, maxPower uint32, instanceID *uint32) error { + event, err := g.updateMemberState(ctx, memberStateUpdate{ + realmID: realmID, + snapshot: MemberStateSnapshot{ + MemberGUID: memberGUID, + Online: online, + Level: level, + Class: class, + ZoneID: zoneID, + MapID: mapID, + Health: health, + MaxHealth: maxHealth, + PowerType: powerType, + Power: power, + MaxPower: maxPower, + InstanceID: instanceID, + }, + }) + if err != nil || event == nil { + return err } - groupID, err := g.r.GroupIDByPlayer(ctx, realmID, updaterGUID) + return g.publishMemberStateBatch(&memberStateBatch{ + realmID: event.realmID, + groupID: event.groupID, + sourceGatewayID: event.sourceGatewayID, + sourceWorldserverID: event.sourceWorldserverID, + receivers: event.receivers, + states: []events.GroupMemberStateUpdate{event.state}, + }) +} + +func (g *groupServiceImpl) updateMemberState(ctx context.Context, update memberStateUpdate) (*memberStateEvent, error) { + snapshot := update.snapshot + + if snapshot.MaxHealth > 0 && snapshot.Health > snapshot.MaxHealth { + snapshot.Health = snapshot.MaxHealth + } + if snapshot.MaxPower > 0 && snapshot.Power > snapshot.MaxPower { + snapshot.Power = snapshot.MaxPower + } + if event := groupstatetrace.Event(nil, "groupserver.member_state.received", snapshot.MemberGUID); event != nil { + traceMemberStateSnapshot(event, update.realmID, snapshot). + Str("sourceGatewayID", update.sourceGatewayID). + Str("sourceWorldserverID", update.sourceWorldserverID). + Msg(groupstatetrace.Message) + } + snapshot = normalizeFixedClassMemberPower(snapshot) + if isInvalidOnlineTransitionSnapshot(snapshot) { + if event := groupstatetrace.Event(nil, "groupserver.member_state.drop_invalid_transition", snapshot.MemberGUID); event != nil { + traceMemberStateSnapshot(event, update.realmID, snapshot). + Str("sourceGatewayID", update.sourceGatewayID). + Str("sourceWorldserverID", update.sourceWorldserverID). + Msg(groupstatetrace.Message) + } + return nil, nil + } + group, err := g.GroupByMemberGUID(ctx, update.realmID, snapshot.MemberGUID) if err != nil { - return fmt.Errorf("can't get groupID, err: %w", err) + return nil, err } - if groupID == 0 { - return ErrGroupNotFound + if group == nil { + g.recordUngroupedMemberState(update, snapshot) + return nil, nil } + groupRealmID := groupHomeRealmID(update.realmID, group) + snapshot.MemberGUID = groupPlayerGUID(groupRealmID, update.realmID, snapshot.MemberGUID) - group, err := g.r.GroupByID(ctx, realmID, groupID, true) - if err != nil { - return fmt.Errorf("can't get group, err: %w", err) + member := group.MemberByGUID(snapshot.MemberGUID) + if member == nil { + return nil, nil } + snapshot.MemberGUID = member.MemberGUID - if group == nil { - return ErrGroupNotFound + if update.sourceGatewayID == "" && update.sourceWorldserverID == "" && snapshot.TimestampMs == 0 && g.stateTracker().hasTimestamp(update.realmID, snapshot.MemberGUID) { + if event := groupstatetrace.Event(nil, "groupserver.member_state.drop_source_less_untimestamped", snapshot.MemberGUID); event != nil { + traceMemberStateSnapshot(event, update.realmID, snapshot). + Msg(groupstatetrace.Message) + } + return nil, nil + } + if isAuraOnlyMemberStateSnapshot(snapshot) && !g.stateTracker().hasOnlineState(update.realmID, snapshot.MemberGUID) { + g.stateTracker().recordMemberPlacement(update, snapshot) + if event := groupstatetrace.Event(nil, "groupserver.member_state.drop_initial_aura_only", snapshot.MemberGUID); event != nil { + traceMemberStateSnapshot(event, update.realmID, snapshot). + Str("sourceGatewayID", update.sourceGatewayID). + Str("sourceWorldserverID", update.sourceWorldserverID). + Msg(groupstatetrace.Message) + } + return nil, nil } - groupMember := group.MemberByGUID(updaterGUID) - if group.IsRaid() && group.LeaderGUID != updaterGUID && !groupMember.IsAssistant() { - return ErrNoPermissions + var accepted bool + snapshot.TimestampMs, accepted = g.stateTracker().acceptTimestamp(update.realmID, snapshot.MemberGUID, snapshot.TimestampMs, snapshot.AurasKnown) + if !accepted { + if event := groupstatetrace.Event(nil, "groupserver.member_state.drop_stale", snapshot.MemberGUID); event != nil { + traceMemberStateSnapshot(event, update.realmID, snapshot). + Str("sourceGatewayID", update.sourceGatewayID). + Str("sourceWorldserverID", update.sourceWorldserverID). + Msg(groupstatetrace.Message) + } + return nil, nil } - for i, target := range group.TargetIcons { - if target == targetGUID { - group.TargetIcons[i] = 0 - - err = g.ep.TargetIconUpdated(&events.GroupEventNewTargetIconPayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: group.ID, - Updater: 0, - Target: 0, - IconID: uint8(i), - Receivers: group.OnlineMemberGUIDs(), - }) - if err != nil { - log.Error().Err(err).Msg("can't create TargetIconUpdated clear event") - } + snapshot = g.stateTracker().preserveKnownVitals(update.realmID, snapshot) + snapshot = normalizeFixedClassMemberPower(snapshot) + if snapshot.MaxHealth > 0 && snapshot.Health > snapshot.MaxHealth { + snapshot.Health = snapshot.MaxHealth + } + if snapshot.MaxPower > 0 && snapshot.Power > snapshot.MaxPower { + snapshot.Power = snapshot.MaxPower + } + if event := groupstatetrace.Event(nil, "groupserver.member_state.accepted", snapshot.MemberGUID); event != nil { + traceMemberStateSnapshot(event, update.realmID, snapshot). + Uint("groupID", group.ID). + Str("sourceGatewayID", update.sourceGatewayID). + Str("sourceWorldserverID", update.sourceWorldserverID). + Msg(groupstatetrace.Message) + } + + if member.IsOnline != snapshot.Online { + member.IsOnline = snapshot.Online + + if err := g.r.UpdateMember(ctx, groupRealmID, member); err != nil { + return nil, err + } + + g.handleOfflineLeaderPromotion(ctx, groupRealmID, group) + } + + g.stateTracker().recordMemberState(update, snapshot) + + event := &memberStateEvent{ + realmID: groupRealmID, + groupID: group.ID, + sourceGatewayID: update.sourceGatewayID, + sourceWorldserverID: update.sourceWorldserverID, + receivers: group.OnlineMemberGUIDs(), + state: memberStateSnapshotToUpdate(snapshot), + } + + suppressed, suppressionReason := g.localMemberStateFanoutSuppressionDecision(event) + if traceEvent := groupstatetrace.Event(nil, "groupserver.member_state.fanout_decision", event.state.MemberGUID); traceEvent != nil { + traceEvent. + Uint32("realmID", event.realmID). + Uint("groupID", event.groupID). + Uint64("memberGUID", event.state.MemberGUID). + Str("sourceGatewayID", event.sourceGatewayID). + Str("sourceWorldserverID", event.sourceWorldserverID). + Bool("suppressed", suppressed). + Str("reason", suppressionReason). + Int("receiverCount", len(event.receivers)). + Str("receivers", memberStateReceiversKey(event.receivers)). + Msg(groupstatetrace.Message) + } + if suppressed { + return nil, nil + } + + return event, nil +} + +func (g *groupServiceImpl) recordUngroupedMemberState(update memberStateUpdate, snapshot MemberStateSnapshot) { + hasSource := update.sourceGatewayID != "" || update.sourceWorldserverID != "" + if snapshot.Online && !hasSource { + g.stateTracker().recordMemberPlacement(update, snapshot) + return + } + + if update.sourceGatewayID == "" && update.sourceWorldserverID == "" && snapshot.TimestampMs == 0 && g.stateTracker().hasTimestamp(update.realmID, snapshot.MemberGUID) { + return + } + if isAuraOnlyMemberStateSnapshot(snapshot) && !g.stateTracker().hasOnlineState(update.realmID, snapshot.MemberGUID) { + g.stateTracker().recordMemberPlacement(update, snapshot) + return + } + + var accepted bool + snapshot.TimestampMs, accepted = g.stateTracker().acceptTimestamp(update.realmID, snapshot.MemberGUID, snapshot.TimestampMs, snapshot.AurasKnown) + if !accepted { + return + } + + snapshot = g.stateTracker().preserveKnownVitals(update.realmID, snapshot) + snapshot = normalizeFixedClassMemberPower(snapshot) + if snapshot.MaxHealth > 0 && snapshot.Health > snapshot.MaxHealth { + snapshot.Health = snapshot.MaxHealth + } + if snapshot.MaxPower > 0 && snapshot.Power > snapshot.MaxPower { + snapshot.Power = snapshot.MaxPower + } + + g.stateTracker().recordMemberState(update, snapshot) +} + +func memberStateSnapshotToUpdate(snapshot MemberStateSnapshot) events.GroupMemberStateUpdate { + return events.GroupMemberStateUpdate{ + MemberGUID: snapshot.MemberGUID, + Online: snapshot.Online, + Level: snapshot.Level, + Class: snapshot.Class, + ZoneID: snapshot.ZoneID, + MapID: snapshot.MapID, + Health: snapshot.Health, + MaxHealth: snapshot.MaxHealth, + PowerType: snapshot.PowerType, + Power: snapshot.Power, + MaxPower: snapshot.MaxPower, + AurasKnown: snapshot.AurasKnown, + Auras: memberAurasToEvent(normalizeMemberAuras(snapshot.Auras)), + DeadKnown: snapshot.Dead != nil, + Dead: boolPtrValue(snapshot.Dead), + GhostKnown: snapshot.Ghost != nil, + Ghost: boolPtrValue(snapshot.Ghost), + } +} + +func isInvalidOnlineTransitionSnapshot(snapshot MemberStateSnapshot) bool { + return snapshot.Online && snapshot.MapID == ^uint32(0) +} + +func (g *groupServiceImpl) localMemberStateFanoutSuppressionDecision(event *memberStateEvent) (bool, string) { + if event == nil || event.sourceWorldserverID == "" || !event.state.Online || len(event.receivers) == 0 { + return false, "not-eligible" + } + + now := time.Now() + tracker := g.stateTracker() + source, ok := tracker.freshPlacement(event.realmID, event.state.MemberGUID, now, memberPlacementFreshness) + if !ok || !sameFreshLocalPlacement(source, event.sourceGatewayID, event.sourceWorldserverID, event.state.MapID) { + return false, "source-placement-not-fresh-local" + } + + mapID := MapID(int(event.state.MapID)) + requireInstance := mapID.IsDungeon() || mapID.IsRaid() + if requireInstance && (!source.instanceKnown || source.instanceID == 0) { + return false, "source-instance-unknown" + } + + for _, receiverGUID := range event.receivers { + receiver, ok := tracker.freshPlacement(event.realmID, receiverGUID, now, memberPlacementFreshness) + if !ok || !sameFreshLocalPlacement(receiver, "", event.sourceWorldserverID, event.state.MapID) { + return false, "receiver-placement-not-fresh-local" + } + if requireInstance && (!receiver.instanceKnown || receiver.instanceID == 0 || receiver.instanceID != source.instanceID) { + return false, "receiver-instance-mismatch" + } + } + + return true, "same-fresh-local-owner" +} + +func sameFreshLocalPlacement(placement memberPlacement, gatewayID, worldserverID string, mapID uint32) bool { + if !placement.online || placement.worldserverID == "" || placement.worldserverID != worldserverID || placement.mapID != mapID { + return false + } + + return gatewayID == "" || placement.gatewayID == "" || placement.gatewayID == gatewayID +} + +func (g *groupServiceImpl) BulkUpdateMemberStates(ctx context.Context, realmID uint32, sourceGatewayID, sourceWorldserverID string, snapshots []MemberStateSnapshot) error { + batches := map[memberStateBatchKey]*memberStateBatch{} + + for _, snapshot := range snapshots { + if snapshot.MemberGUID == 0 { + continue + } + + event, err := g.updateMemberState(ctx, memberStateUpdate{ + realmID: realmID, + sourceGatewayID: sourceGatewayID, + sourceWorldserverID: sourceWorldserverID, + snapshot: snapshot, + }) + if err != nil { + return err + } + if event == nil { + continue + } - break + key := memberStateBatchKey{ + realmID: event.realmID, + groupID: event.groupID, + sourceGatewayID: event.sourceGatewayID, + sourceWorldserverID: event.sourceWorldserverID, + receiversKey: memberStateReceiversKey(event.receivers), } - } - group.TargetIcons[iconID] = targetGUID + batch := batches[key] + if batch == nil { + batch = &memberStateBatch{ + realmID: event.realmID, + groupID: event.groupID, + sourceGatewayID: event.sourceGatewayID, + sourceWorldserverID: event.sourceWorldserverID, + receivers: append([]uint64(nil), event.receivers...), + } + batches[key] = batch + } - if err = g.r.Update(ctx, realmID, group); err != nil { - return fmt.Errorf("can't update icon for the group (%d), err: %w", groupID, err) + batch.states = append(batch.states, event.state) } - err = g.ep.TargetIconUpdated(&events.GroupEventNewTargetIconPayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: group.ID, - Updater: updaterGUID, - Target: targetGUID, - IconID: iconID, - Receivers: group.OnlineMemberGUIDs(), + keys := make([]memberStateBatchKey, 0, len(batches)) + for key := range batches { + keys = append(keys, key) + } + sort.Slice(keys, func(i, j int) bool { + if keys[i].realmID != keys[j].realmID { + return keys[i].realmID < keys[j].realmID + } + if keys[i].groupID != keys[j].groupID { + return keys[i].groupID < keys[j].groupID + } + if keys[i].sourceGatewayID != keys[j].sourceGatewayID { + return keys[i].sourceGatewayID < keys[j].sourceGatewayID + } + if keys[i].sourceWorldserverID != keys[j].sourceWorldserverID { + return keys[i].sourceWorldserverID < keys[j].sourceWorldserverID + } + return keys[i].receiversKey < keys[j].receiversKey }) - if err != nil { - log.Error().Err(err).Msg("can't create TargetIconUpdated event") + + for _, key := range keys { + if err := g.publishMemberStateBatch(batches[key]); err != nil { + return err + } } return nil } -func (g groupServiceImpl) SetLootMethod(ctx context.Context, realmID uint32, updaterGUID uint64, method uint8, lootMaster uint64, lootThreshold uint8) error { - group, err := g.getGroupWithLeader(ctx, realmID, updaterGUID) - if err != nil { - return err +func (g groupServiceImpl) publishMemberStateBatch(batch *memberStateBatch) error { + if batch == nil || len(batch.states) == 0 { + return nil } - group.LootMethod = method - group.LootThreshold = lootThreshold - group.LooterGUID = lootMaster - - if err = g.r.Update(ctx, realmID, group); err != nil { - return err + if len(batch.states) == 1 { + return g.ep.GroupMemberStateChanged(singleMemberStateChangedPayload(batch, batch.states[0])) } - err = g.ep.LootTypeChanged(&events.GroupEventGroupLootTypeChangedPayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: group.ID, - NewLootType: group.LootMethod, - NewLooterGUID: group.LooterGUID, - NewLooterThreshold: group.LootThreshold, - OnlineMembers: group.OnlineMemberGUIDs(), + return g.ep.GroupMemberStatesChanged(&events.GroupEventMemberStatesChangedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: batch.realmID, + GroupID: batch.groupID, + SourceGatewayID: batch.sourceGatewayID, + SourceWorldserverID: batch.sourceWorldserverID, + States: batch.states, + Receivers: append([]uint64(nil), batch.receivers...), }) - if err != nil { - log.Error().Err(err).Msg("can't send loot changed event") - } - - return nil } -func (g groupServiceImpl) SetDungeonDifficulty(ctx context.Context, realmID uint32, updaterGUID uint64, difficulty uint8) error { - group, err := g.getGroupWithLeader(ctx, realmID, updaterGUID) - if err != nil { - return err +func (g *groupServiceImpl) publishMemberStateCatchup(realmID uint32, group *repo.Group, receiverGUID uint64) error { + if group == nil || receiverGUID == 0 { + return nil } - characters, err := g.charClient.ShortOnlineCharactersDataByGUIDs(ctx, &pb.ShortCharactersDataByGUIDsRequest{ - Api: groupserver.Ver, - RealmID: realmID, - GUIDs: group.OnlineMemberGUIDs(), - }) - if err != nil { - return fmt.Errorf("failed to get characters, err: %w", err) + groupRealmID := groupHomeRealmID(realmID, group) + receiverGUID = groupPlayerGUID(groupRealmID, realmID, receiverGUID) + states := g.stateTracker().memberStateUpdatesForGroup(groupRealmID, group.Members, receiverGUID) + if len(states) == 0 { + return nil } - for _, char := range characters.Characters { - if MapID(int(char.CharMap)).IsDungeon() { - return ErrMemberInDungeonOrRaid + log.Debug(). + Uint32("realmID", groupRealmID). + Uint("groupID", group.ID). + Uint64("receiverGUID", receiverGUID). + Int("stateCount", len(states)). + Msg("publishing group member-state catch-up") + + for _, state := range states { + if event := groupstatetrace.Event(nil, "groupserver.member_state.catchup", receiverGUID, state.MemberGUID); event != nil { + event. + Uint32("realmID", groupRealmID). + Uint("groupID", group.ID). + Uint64("receiverGUID", receiverGUID). + Uint64("memberGUID", state.MemberGUID). + Bool("online", state.Online). + Uint8("level", state.Level). + Uint8("class", state.Class). + Uint32("zoneID", state.ZoneID). + Uint32("mapID", state.MapID). + Uint32("health", state.Health). + Uint32("maxHealth", state.MaxHealth). + Uint8("powerType", state.PowerType). + Uint32("power", state.Power). + Uint32("maxPower", state.MaxPower). + Bool("aurasKnown", state.AurasKnown). + Int("auraCount", len(state.Auras)). + Str("auraSpells", formatEventMemberAuraTrace(state.Auras)). + Msg(groupstatetrace.Message) } } - group.Difficulty = difficulty + return g.publishMemberStateBatch(&memberStateBatch{ + realmID: groupRealmID, + groupID: group.ID, + receivers: []uint64{receiverGUID}, + states: states, + }) +} - if err = g.r.Update(ctx, realmID, group); err != nil { - return err +func (g *groupServiceImpl) publishMemberStateCatchupsForOnlineMembers(realmID uint32, group *repo.Group) error { + if group == nil { + return nil } - err = g.ep.GroupDifficultyChanged(&events.GroupEventGroupDifficultyChangedPayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: group.ID, - Updater: updaterGUID, - DungeonDifficulty: &difficulty, - RaidDifficulty: nil, - Receivers: group.OnlineMemberGUIDs(), - }) - if err != nil { - log.Error().Err(err).Msg("can't send difficulty changed event") + groupRealmID := groupHomeRealmID(realmID, group) + for _, receiverGUID := range group.OnlineMemberGUIDs() { + if err := g.publishMemberStateCatchup(groupRealmID, group, receiverGUID); err != nil { + return err + } } return nil } -func (g groupServiceImpl) SetRaidDifficulty(ctx context.Context, realmID uint32, updaterGUID uint64, difficulty uint8) error { - group, err := g.getGroupWithLeader(ctx, realmID, updaterGUID) - if err != nil { - return err - } - - characters, err := g.charClient.ShortOnlineCharactersDataByGUIDs(ctx, &pb.ShortCharactersDataByGUIDsRequest{ - Api: groupserver.Ver, - RealmID: realmID, - GUIDs: group.OnlineMemberGUIDs(), - }) - if err != nil { - return fmt.Errorf("failed to get characters, err: %w", err) - } - - for _, char := range characters.Characters { - if MapID(int(char.CharMap)).IsRaid() { - return ErrMemberInDungeonOrRaid - } +func singleMemberStateChangedPayload(batch *memberStateBatch, state events.GroupMemberStateUpdate) *events.GroupEventMemberStateChangedPayload { + return &events.GroupEventMemberStateChangedPayload{ + ServiceID: groupserver.ServiceID, + RealmID: batch.realmID, + GroupID: batch.groupID, + SourceGatewayID: batch.sourceGatewayID, + SourceWorldserverID: batch.sourceWorldserverID, + MemberGUID: state.MemberGUID, + Online: state.Online, + Level: state.Level, + Class: state.Class, + ZoneID: state.ZoneID, + MapID: state.MapID, + Health: state.Health, + MaxHealth: state.MaxHealth, + PowerType: state.PowerType, + Power: state.Power, + MaxPower: state.MaxPower, + AurasKnown: state.AurasKnown, + Auras: state.Auras, + DeadKnown: state.DeadKnown, + Dead: state.Dead, + GhostKnown: state.GhostKnown, + Ghost: state.Ghost, + Receivers: append([]uint64(nil), batch.receivers...), } +} - group.RaidDifficulty = difficulty - - if err = g.r.Update(ctx, realmID, group); err != nil { - return err +func memberStateReceiversKey(receivers []uint64) string { + if len(receivers) == 0 { + return "" } - err = g.ep.GroupDifficultyChanged(&events.GroupEventGroupDifficultyChangedPayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: group.ID, - Updater: updaterGUID, - DungeonDifficulty: nil, - RaidDifficulty: &difficulty, - Receivers: group.OnlineMemberGUIDs(), + sortedReceivers := append([]uint64(nil), receivers...) + sort.Slice(sortedReceivers, func(i, j int) bool { + return sortedReceivers[i] < sortedReceivers[j] }) - if err != nil { - log.Error().Err(err).Msg("can't send difficulty changed event") + + parts := make([]string, 0, len(sortedReceivers)) + for _, receiver := range sortedReceivers { + parts = append(parts, strconv.FormatUint(receiver, 10)) } + return strings.Join(parts, ",") +} - return nil +func traceMemberStateSnapshot(event *zerolog.Event, realmID uint32, snapshot MemberStateSnapshot) *zerolog.Event { + event = event. + Uint32("realmID", realmID). + Uint64("memberGUID", snapshot.MemberGUID). + Bool("online", snapshot.Online). + Uint8("level", snapshot.Level). + Uint8("class", snapshot.Class). + Uint32("zoneID", snapshot.ZoneID). + Uint32("mapID", snapshot.MapID). + Bool("hasInstance", snapshot.InstanceID != nil). + Uint32("health", snapshot.Health). + Uint32("maxHealth", snapshot.MaxHealth). + Uint8("powerType", snapshot.PowerType). + Uint32("power", snapshot.Power). + Uint32("maxPower", snapshot.MaxPower). + Bool("hasDead", snapshot.Dead != nil). + Bool("dead", boolPtrValue(snapshot.Dead)). + Bool("hasGhost", snapshot.Ghost != nil). + Bool("ghost", boolPtrValue(snapshot.Ghost)). + Bool("aurasKnown", snapshot.AurasKnown). + Int("auraCount", len(snapshot.Auras)). + Str("auraSpells", formatMemberAuraTrace(snapshot.Auras)). + Uint64("timestampMs", snapshot.TimestampMs) + + if snapshot.InstanceID != nil { + event = event.Uint32("instanceID", *snapshot.InstanceID) + } + + return event } -func (g groupServiceImpl) HandleCharacterLoggedIn(payload events.GWEventCharacterLoggedInPayload) error { - p, err := g.buildGroupMemberOnlineStatusChangedPayload(payload.RealmID, payload.CharGUID) - if err != nil { - return err +func normalizeFixedClassMemberPower(snapshot MemberStateSnapshot) MemberStateSnapshot { + if !wow.IsFixedClassInactivePowerType(snapshot.Class, snapshot.PowerType) { + return snapshot + } + if snapshot.Power == 0 && snapshot.MaxPower == 0 { + return snapshot } - if p == nil { - return nil + snapshot.PowerType, _ = wow.FixedPrimaryPowerTypeForClass(snapshot.Class) + snapshot.Power = 0 + if snapshot.MaxPower == 0 { + snapshot.MaxPower = wow.DefaultMaxPowerForClass(snapshot.Class) } - p.IsOnline = true - return g.ep.GroupMemberOnlineStatusChanged(p) + return snapshot } -func (g groupServiceImpl) HandleCharacterLoggedOut(payload events.GWEventCharacterLoggedOutPayload) error { - p, err := g.buildGroupMemberOnlineStatusChangedPayload(payload.RealmID, payload.CharGUID) - if err != nil { - return err +func formatMemberAuraTrace(auras []MemberAuraState) string { + auras = normalizeMemberAuras(auras) + if len(auras) == 0 { + return "" } - if p == nil { - return nil + parts := make([]string, 0, len(auras)) + for _, aura := range auras { + parts = append(parts, strconv.Itoa(int(aura.Slot))+":"+strconv.FormatUint(uint64(aura.SpellID), 10)+":"+strconv.Itoa(int(aura.Flags))) } - p.IsOnline = false - return g.ep.GroupMemberOnlineStatusChanged(p) + return strings.Join(parts, ",") } -func (g groupServiceImpl) buildGroupMemberOnlineStatusChangedPayload(realmID uint32, player uint64) (*events.GroupEventGroupMemberOnlineStatusChangedPayload, error) { - groupID, err := g.GroupIDByPlayer(context.Background(), realmID, player) - if err != nil { - return nil, err +func formatEventMemberAuraTrace(auras []events.GroupMemberAuraState) string { + if len(auras) == 0 { + return "" } - if groupID == 0 { - return nil, nil + normalized := make([]events.GroupMemberAuraState, 0, len(auras)) + for _, aura := range auras { + if aura.SpellID == 0 { + continue + } + normalized = append(normalized, aura) } + sort.Slice(normalized, func(i, j int) bool { + return normalized[i].Slot < normalized[j].Slot + }) - group, err := g.GroupByID(context.Background(), realmID, groupID) - if err != nil { - return nil, err + parts := make([]string, 0, len(normalized)) + for _, aura := range normalized { + parts = append(parts, strconv.Itoa(int(aura.Slot))+":"+strconv.FormatUint(uint64(aura.SpellID), 10)+":"+strconv.Itoa(int(aura.Flags))) } - return &events.GroupEventGroupMemberOnlineStatusChangedPayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: groupID, - MemberGUID: player, - OnlineMembers: group.OnlineMemberGUIDs(), - }, nil + return strings.Join(parts, ",") } -func (g groupServiceImpl) getGroupWithLeader(ctx context.Context, realmID uint32, leaderGUID uint64) (*repo.Group, error) { - groupID, err := g.r.GroupIDByPlayer(ctx, realmID, leaderGUID) - if err != nil { - return nil, fmt.Errorf("can't get groupID, err: %w", err) - } - if groupID == 0 { - return nil, ErrGroupNotFound - } +func normalizeMemberAuras(auras []MemberAuraState) []MemberAuraState { + const maxGroupAuraSlots = 64 - group, err := g.r.GroupByID(ctx, realmID, groupID, true) - if err != nil { - return nil, fmt.Errorf("can't get group, err: %w", err) + if len(auras) == 0 { + return nil } - if group == nil { - return nil, ErrGroupNotFound + bySlot := make(map[uint8]MemberAuraState, len(auras)) + for _, aura := range auras { + if aura.Slot >= maxGroupAuraSlots || aura.SpellID == 0 { + continue + } + bySlot[aura.Slot] = aura } - if group.LeaderGUID != leaderGUID { - return nil, ErrNoPermissions + out := make([]MemberAuraState, 0, len(bySlot)) + for _, aura := range bySlot { + out = append(out, aura) } + sort.Slice(out, func(i, j int) bool { + return out[i].Slot < out[j].Slot + }) - return group, nil + return out } -func (g groupServiceImpl) createGroup(ctx context.Context, realmID uint32, invite *repo.GroupInvite) error { - group := repo.Group{ - LeaderGUID: invite.Inviter, - LootMethod: uint8(repo.LootTypeFreeForAll), - LooterGUID: invite.Inviter, - LootThreshold: uint8(repo.ItemQualityUncommon), - TargetIcons: [8]uint64{}, - GroupType: repo.GroupTypeFlagsNormal, - Difficulty: 0, - RaidDifficulty: 0, - MasterLooterGuid: invite.Inviter, - Members: []repo.GroupMember{ - { - MemberGUID: invite.Inviter, - MemberFlags: 0, - MemberName: invite.InviterName, - IsOnline: true, - SubGroup: 0, - Roles: 0, - }, - { - MemberGUID: invite.Invitee, - MemberFlags: 0, - MemberName: invite.InviteeName, - IsOnline: true, - SubGroup: 0, - Roles: 0, - }, - }, - } - - err := g.r.Create(ctx, realmID, &group) - if err != nil { - return err - } - - members := make([]events.GroupMember, len(group.Members)) - for i, member := range group.Members { - members[i].MemberGUID = member.MemberGUID - members[i].MemberFlags = member.MemberFlags - members[i].MemberName = member.MemberName - members[i].SubGroup = member.SubGroup - members[i].IsOnline = member.IsOnline - members[i].Roles = uint8(member.Roles) +func memberAurasToEvent(auras []MemberAuraState) []events.GroupMemberAuraState { + if len(auras) == 0 { + return nil } - err = g.ep.GroupCreated(&events.GroupEventGroupCreatedPayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: group.ID, - LeaderGUID: group.LeaderGUID, - LootMethod: group.LootMethod, - LooterGUID: group.LooterGUID, - LootThreshold: group.LootThreshold, - GroupType: uint8(group.GroupType), - Difficulty: group.Difficulty, - RaidDifficulty: group.RaidDifficulty, - MasterLooterGuid: group.MasterLooterGuid, - Members: members, - }) - if err != nil { - log.Error().Err(err).Msg("can't send group created event") + out := make([]events.GroupMemberAuraState, 0, len(auras)) + for _, aura := range auras { + out = append(out, events.GroupMemberAuraState{ + Slot: aura.Slot, + SpellID: aura.SpellID, + Flags: aura.Flags, + }) } - return nil + return out } -func (g groupServiceImpl) addMember(ctx context.Context, realmID uint32, group *repo.Group, invite *repo.GroupInvite) error { - err := g.r.AddMember(ctx, realmID, &repo.GroupMember{ - GroupID: invite.GroupID, - MemberGUID: invite.Invitee, - MemberFlags: 0, - MemberName: invite.InviteeName, - IsOnline: true, - SubGroup: 0, - Roles: 0, - }) +func (g groupServiceImpl) ResetInstance(ctx context.Context, realmID uint32, playerGUID uint64, mapID uint32, difficulty uint8) error { + group, err := g.GroupByMemberGUID(ctx, realmID, playerGUID) if err != nil { return err } - err = g.ep.MemberAdded(&events.GroupEventGroupMemberAddedPayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: group.ID, - MemberGUID: invite.Invitee, - MemberName: invite.InviteeName, - OnlineMembers: append(group.OnlineMemberGUIDs(), invite.Invitee), - }) - if err != nil { - log.Error().Err(err).Msg("can't send group member added event") - } + groupID := uint(0) + receivers := []uint64{playerGUID} - return nil -} + if group != nil { + if group.LeaderGUID != playerGUID { + return ErrNoPermissions + } -func (g groupServiceImpl) disband(ctx context.Context, realmID uint32, group *repo.Group) error { - players := group.OnlineMemberGUIDs() - err := g.r.Delete(ctx, realmID, group.ID) - if err != nil { - return fmt.Errorf("can't delete group, err: %w", err) + groupID = group.ID + receivers = group.OnlineMemberGUIDs() } - err = g.ep.GroupDisband(&events.GroupEventGroupDisbandPayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: group.ID, - OnlineMembers: players, + return g.ep.GroupInstanceResetRequest(&events.GroupEventInstanceResetRequestPayload{ + ServiceID: groupserver.ServiceID, + RealmID: realmID, + GroupID: groupID, + PlayerGUID: playerGUID, + MapID: mapID, + Difficulty: difficulty, + Receivers: receivers, }) - if err != nil { - log.Error().Err(err).Msg("can't create GroupDisband event") - } - - return nil } -func (g groupServiceImpl) changeLeader(ctx context.Context, realmID uint32, group *repo.Group, newLeader uint64, needsEventUpdate bool) error { - prevLeader := group.LeaderGUID - group.LeaderGUID = newLeader - if err := g.r.Update(ctx, realmID, group); err != nil { - return fmt.Errorf("can't update group win a new leader, err: %w", err) +func (g groupServiceImpl) SetInstanceBindExtension(ctx context.Context, realmID uint32, playerGUID uint64, mapID uint32, difficulty uint8, extended bool) error { + group, err := g.GroupByMemberGUID(ctx, realmID, playerGUID) + if err != nil { + return err } - if needsEventUpdate { - err := g.ep.LeaderChanged(&events.GroupEventGroupLeaderChangedPayload{ - ServiceID: groupserver.ServiceID, - RealmID: realmID, - GroupID: group.ID, - PreviousLeader: prevLeader, - NewLeader: newLeader, - OnlineMembers: group.OnlineMemberGUIDs(), - }) - if err != nil { - log.Error().Err(err).Msg("can't create LeaderChanged event") - } + + groupID := uint(0) + if group != nil { + groupID = group.ID } - return nil + return g.ep.GroupInstanceBindExtensionRequest(&events.GroupEventInstanceBindExtensionRequestPayload{ + ServiceID: groupserver.ServiceID, + RealmID: realmID, + GroupID: groupID, + PlayerGUID: playerGUID, + MapID: mapID, + Difficulty: difficulty, + Extended: extended, + Receivers: []uint64{playerGUID}, + }) } From bdd5fd282dbc2cbe05aefe1e320a6da876151bd1 Mon Sep 17 00:00:00 2001 From: VG-Prog Date: Fri, 22 May 2026 21:48:40 +0200 Subject: [PATCH 07/11] feat(Cluster/Guilds): Add distributed guild services Add realm-scoped guild service routing, charter offer/sign/query/signature handling, native petition persistence, guild cache lifecycle, gateway-facing events, and debug logging for same-realm clustered guild workflows. --- apps/guildserver/cmd/guildserver/main.go | 43 +- apps/guildserver/config/config.go | 6 + apps/guildserver/repo/guilds.go | 202 ++++- apps/guildserver/repo/guilds_mysql.go | 371 ++++++++- apps/guildserver/repo/stmts.go | 5 + .../guildserver/server/guilds-logger_debug.go | 42 + apps/guildserver/server/guilds.go | 384 ++++++++- apps/guildserver/service/guilds-cache.go | 3 + .../guildserver/service/guilds-cache_inmem.go | 208 ++++- apps/guildserver/service/guilds.go | 776 +++++++++++++++++- 10 files changed, 1936 insertions(+), 104 deletions(-) diff --git a/apps/guildserver/cmd/guildserver/main.go b/apps/guildserver/cmd/guildserver/main.go index e6c4fa8..578bac3 100644 --- a/apps/guildserver/cmd/guildserver/main.go +++ b/apps/guildserver/cmd/guildserver/main.go @@ -7,6 +7,7 @@ import ( "net" "os" "os/signal" + "sort" "strconv" "sync" "syscall" @@ -23,6 +24,7 @@ import ( "github.com/walkline/ToCloud9/apps/guildserver/repo" "github.com/walkline/ToCloud9/apps/guildserver/server" "github.com/walkline/ToCloud9/apps/guildserver/service" + pbGuid "github.com/walkline/ToCloud9/gen/guid/pb" "github.com/walkline/ToCloud9/gen/guilds/pb" "github.com/walkline/ToCloud9/shared/events" shrepo "github.com/walkline/ToCloud9/shared/repo" @@ -102,28 +104,61 @@ func createGuildService(cfg *config.Config, natsCon *nats.Conn) service.GuildSer charDB.SetDBForRealm(realmID, cdb) } - guildsRepo, err := repo.NewGuildsMySQLRepo(charDB) + worldDB, err := sql.Open("mysql", cfg.WorldDBConnection) + if err != nil { + log.Fatal().Err(err).Msg("can't connect to world db") + } + configureDBConn(worldDB) + + guildsRepo, err := repo.NewGuildsMySQLRepoWithWorldDB(charDB, worldDB) if err != nil { log.Fatal().Err(err).Msg("can't create guilds repo") } + guidConn, err := grpc.Dial(cfg.GuidProviderServiceAddress, grpc.WithInsecure()) + if err != nil { + log.Fatal().Err(err).Str("address", cfg.GuidProviderServiceAddress).Msg("can't connect to guid service") + } + itemGUIDAllocator := service.NewGuidServiceItemGUIDAllocator(pbGuid.NewGuidServiceClient(guidConn)) + cache := service.NewGuildsInMemCache(guildsRepo) + guildService := service.NewGuildServiceWithBankRepoAndItemGUIDAllocator(cache, guildsRepo, itemGUIDAllocator, events.NewGuildServiceProducerNatsJSON(natsCon, guildserver.Ver)) err = events.NewGatewayConsumer( natsCon, events.WithGWConsumerLoggedInHandler(cache), events.WithGWConsumerLoggedOutHandler(cache), events.WithGWConsumerCharsUpdatesHandler(cache), + events.WithGWConsumerGuildCreatedHandler(guildService), ).Listen() if err != nil { log.Fatal().Err(err).Msg("can't listen to gateway updates") } - err = cache.Warmup(context.Background(), 1) - if err != nil { + if err = warmupGuildCache(context.Background(), cache, cfg.CharDBConnection); err != nil { log.Fatal().Err(err).Msg("can't warmup guilds cache") } - return service.NewGuildService(cache, events.NewGuildServiceProducerNatsJSON(natsCon, guildserver.Ver)) + return guildService +} + +type guildCacheWarmer interface { + Warmup(ctx context.Context, realmID uint32) error +} + +func warmupGuildCache(ctx context.Context, cache guildCacheWarmer, realmConnections map[uint32]string) error { + realmIDs := make([]int, 0, len(realmConnections)) + for realmID := range realmConnections { + realmIDs = append(realmIDs, int(realmID)) + } + sort.Ints(realmIDs) + + for _, realmID := range realmIDs { + if err := cache.Warmup(ctx, uint32(realmID)); err != nil { + return fmt.Errorf("warmup guild cache for realm %d: %w", realmID, err) + } + } + + return nil } func configureDBConn(db *sql.DB) { diff --git a/apps/guildserver/config/config.go b/apps/guildserver/config/config.go index c05eaf1..fc8b3d1 100644 --- a/apps/guildserver/config/config.go +++ b/apps/guildserver/config/config.go @@ -16,6 +16,12 @@ type Config struct { // CharDBConnection is connection string to the characters database CharDBConnection map[uint32]string `yaml:"charactersDB" env:"CHAR_DB_CONNECTION" env-separator:";" env-default:"1:trinity:trinity@tcp(127.0.0.1:3306)/characters"` + + // WorldDBConnection is connection string to the world database. + WorldDBConnection string `yaml:"worldDB" env:"WORLD_DB_CONNECTION" env-default:"trinity:trinity@tcp(127.0.0.1:3306)/world"` + + // GuidProviderServiceAddress is address of service that provides item GUIDs. + GuidProviderServiceAddress string `yaml:"guidProviderServiceAddress" env:"GUID_PROVIDER_SERVICE_ADDRESS" env-default:"localhost:8996"` } // LoadConfig loads config from file or/and env variables diff --git a/apps/guildserver/repo/guilds.go b/apps/guildserver/repo/guilds.go index 80e2bb6..1597072 100644 --- a/apps/guildserver/repo/guilds.go +++ b/apps/guildserver/repo/guilds.go @@ -1,18 +1,22 @@ package repo -import "context" +import ( + "context" + "errors" +) // Guild represents in game guild. type Guild struct { - RealmID uint32 - ID uint64 - Name string - CrateTimeUnix int64 - LeaderGUID uint64 - Emblem GuildEmblem - Info string - MessageOfTheDay string - BankMoney uint64 + RealmID uint32 + ID uint64 + Name string + CrateTimeUnix int64 + LeaderGUID uint64 + Emblem GuildEmblem + Info string + MessageOfTheDay string + BankMoney uint64 + PurchasedBankTabs uint8 GuildRanks []GuildRank GuildMembers []*GuildMember @@ -40,20 +44,51 @@ const ( GuildRankMember GuildRankInitiate - GuildRankMax = 10 + GuildRankMinCount = 5 + GuildRankMax = 10 ) // GuildRank represents ranks of the guild. // Guild has limited amount of ranks - 10. // By default guild has ranks from GuildRankDefaultID. type GuildRank struct { - GuildID uint64 - Rank uint8 - Name string - Rights uint32 - MoneyPerDay uint32 + GuildID uint64 + Rank uint8 + Name string + Rights uint32 + MoneyPerDay uint32 + BankTabRights [GuildBankMaxTabs]GuildBankTabRight } +const ( + GuildBankMaxTabs = 6 + GuildBankMaxSlots = 98 + GuildBankWithdrawSlots = GuildBankMaxTabs + 1 + GuildBankMoneyLimit = 0x7FFFFFFFFFFFF +) + +var ( + ErrGuildBankInvalidTab = errors.New("invalid guild bank tab") + ErrGuildBankInvalidSlot = errors.New("invalid guild bank slot") + ErrGuildBankNotEnoughGold = errors.New("not enough guild bank gold") + ErrGuildBankFull = errors.New("guild bank full") + ErrGuildBankWithdrawLimit = errors.New("guild bank withdraw limit") + ErrGuildBankItemNotFound = errors.New("guild bank item not found") +) + +type GuildBankTabRight struct { + TabID uint8 + Flags uint32 + WithdrawItemLimit uint32 +} + +const ( + GuildBankRightViewTab uint32 = 0x01 + GuildBankRightPutItem uint32 = 0x02 + GuildBankRightUpdateText uint32 = 0x04 + GuildBankRightDepositItem = GuildBankRightViewTab | GuildBankRightPutItem +) + func (r *GuildRank) HasRight(right uint32) bool { return (r.Rights & right) != RightEmpty } @@ -90,22 +125,101 @@ const ( // GuildMember represents member of the guild. type GuildMember struct { - GuildID uint64 - PlayerGUID uint64 - Rank uint8 - PublicNote string - OfficerNote string - Name string - Race uint8 - Class uint8 - Lvl uint8 - Gender uint8 - AreaID uint32 - Account uint64 - LogoutTime int64 - Status GuildMemberStatus + GuildID uint64 + PlayerGUID uint64 + Rank uint8 + PublicNote string + OfficerNote string + BankWithdraw [GuildBankWithdrawSlots]uint32 + Name string + Race uint8 + Class uint8 + Lvl uint8 + Gender uint8 + AreaID uint32 + Account uint64 + LogoutTime int64 + Status GuildMemberStatus } +// GuildPetitionSignature represents one charter signer. +type GuildPetitionSignature struct { + PlayerGUID uint64 + PlayerAccount uint32 +} + +// GuildPetition represents a native AzerothCore petition charter. +type GuildPetition struct { + RealmID uint32 + PetitionID uint32 + PetitionGUID uint64 + OwnerGUID uint64 + Name string + Type uint8 + Signatures []GuildPetitionSignature +} + +// GuildBankSocketEnchant is one visible socket enchant in a guild bank item. +type GuildBankSocketEnchant struct { + SocketIndex uint8 + SocketEnchantID uint32 +} + +// GuildBankItem is the DB-backed item state needed to render and mutate guild bank slots. +type GuildBankItem struct { + ItemGUID uint64 + Entry uint32 + Slot uint8 + Count uint32 + Flags uint32 + RandomPropertyID int32 + RandomPropertySeed int32 + Durability uint32 + EnchantmentID uint32 + SocketEnchants []GuildBankSocketEnchant + Charges uint32 + Text string +} + +// GuildBankTab is one purchased guild bank tab. +type GuildBankTab struct { + TabID uint8 + Name string + Icon string + Text string + Items []GuildBankItem +} + +// GuildBank is the full persisted bank state needed by gateway packet renderers. +type GuildBank struct { + GuildID uint64 + Money uint64 + Tabs []GuildBankTab +} + +type GuildBankLogEntry struct { + PlayerGUID uint64 + TimeOffset uint32 + EntryType int8 + Money uint32 + ItemID int32 + Count int32 + OtherTab int8 +} + +type GuildBankEventLogType int8 + +const ( + GuildBankLogDepositItem GuildBankEventLogType = 1 + GuildBankLogWithdrawItem GuildBankEventLogType = 2 + GuildBankLogMoveItem GuildBankEventLogType = 3 + GuildBankLogDepositMoney GuildBankEventLogType = 4 + GuildBankLogWithdrawMoney GuildBankEventLogType = 5 + GuildBankLogRepairMoney GuildBankEventLogType = 6 + GuildBankLogMoveItem2 GuildBankEventLogType = 7 + GuildBankLogBuySlot GuildBankEventLogType = 9 +) + // GuildsRepo represents repository for Guilds. // //go:generate mockery --name=GuildsRepo --filename=guilds-repo.go @@ -120,6 +234,9 @@ type GuildsRepo interface { // GuildIDByRealmAndMemberGUID returns guild id by guild member GUID. GuildIDByRealmAndMemberGUID(ctx context.Context, realmID uint32, memberGUID uint64) (uint64, error) + // IgnoredByGuildMembers returns receivers that ignore the sender. + IgnoredByGuildMembers(ctx context.Context, realmID uint32, senderGUID uint64, receiverGUIDs []uint64) (map[uint64]bool, error) + // AddGuildInvite links user invite to a specific guild. AddGuildInvite(ctx context.Context, realmID uint32, charGUID, guildID uint64) error @@ -151,11 +268,34 @@ type GuildsRepo interface { SetGuildInfo(ctx context.Context, realmID uint32, guildID uint64, info string) error // UpdateGuildRank updates guild rank. - UpdateGuildRank(ctx context.Context, realmID uint32, guildID uint64, rank uint8, name string, rights, moneyPerDay uint32) error + UpdateGuildRank(ctx context.Context, realmID uint32, guildID uint64, rank uint8, name string, rights, moneyPerDay uint32, bankTabRights [GuildBankMaxTabs]GuildBankTabRight) error // AddGuildRank adds guild rank. AddGuildRank(ctx context.Context, realmID uint32, guildID uint64, rank uint8, name string, rights, moneyPerDay uint32) error // DeleteLowestGuildRank deletes lowes guild rank. DeleteLowestGuildRank(ctx context.Context, realmID uint32, guildID uint64, rank uint8) error + + // GuildPetitionByGUID loads a native petition by item GUID. + GuildPetitionByGUID(ctx context.Context, realmID uint32, petitionGUID uint64) (*GuildPetition, error) + + // AddGuildPetitionSignature persists a native guild petition signature. + AddGuildPetitionSignature(ctx context.Context, realmID uint32, petitionID uint32, petitionGUID, ownerGUID, playerGUID uint64, playerAccount uint32) error + + // GuildBank loads the persisted guild bank state for one guild. + GuildBank(ctx context.Context, realmID uint32, guildID uint64, tabID uint8, fullUpdate bool) (*GuildBank, error) + + // GuildBankLog loads the persisted guild bank log for one tab. + GuildBankLog(ctx context.Context, realmID uint32, guildID uint64, tabID uint8) ([]GuildBankLogEntry, error) + + SetGuildBankTabInfo(ctx context.Context, realmID uint32, guildID uint64, tabID uint8, name, icon string) error + SetGuildBankTabText(ctx context.Context, realmID uint32, guildID uint64, tabID uint8, text string) error + BuyGuildBankTab(ctx context.Context, realmID uint32, guildID uint64, tabID uint8) error + DepositGuildBankMoney(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, amount uint32) error + WithdrawGuildBankMoney(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, amount uint32, repair bool) (uint32, error) + RollbackGuildBankMoneyWithdraw(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, amount uint32, repair bool, logGUID uint32) error + DepositGuildBankItem(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, tabID, slotID uint8, item GuildBankItem) error + WithdrawGuildBankItem(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, tabID, slotID uint8, count uint32, splitItemGUID uint64) (*GuildBankItem, uint32, error) + RollbackGuildBankItemWithdraw(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, tabID, slotID uint8, item GuildBankItem, logGUID uint32) ([]uint8, error) + MoveGuildBankItem(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, sourceTabID, sourceSlotID, destinationTabID, destinationSlotID uint8, count uint32, splitItemGUID uint64) ([]uint8, error) } diff --git a/apps/guildserver/repo/guilds_mysql.go b/apps/guildserver/repo/guilds_mysql.go index 44de862..8268eeb 100644 --- a/apps/guildserver/repo/guilds_mysql.go +++ b/apps/guildserver/repo/guilds_mysql.go @@ -2,15 +2,26 @@ package repo import ( "context" + "database/sql" + "fmt" + "strings" shrepo "github.com/walkline/ToCloud9/shared/repo" + wowguid "github.com/walkline/ToCloud9/shared/wow/guid" ) +const characterSocialFlagIgnore = 0x02 + type guildsMySQLRepo struct { - db shrepo.CharactersDB + db shrepo.CharactersDB + worldDB *sql.DB } func NewGuildsMySQLRepo(db shrepo.CharactersDB) (GuildsRepo, error) { + return NewGuildsMySQLRepoWithWorldDB(db, nil) +} + +func NewGuildsMySQLRepoWithWorldDB(db shrepo.CharactersDB, worldDB *sql.DB) (GuildsRepo, error) { db.SetPreparedStatement(StmtGetGuildInvite) db.SetPreparedStatement(StmtAddGuildInvite) db.SetPreparedStatement(StmtRemoveGuildInvite) @@ -25,9 +36,11 @@ func NewGuildsMySQLRepo(db shrepo.CharactersDB) (GuildsRepo, error) { db.SetPreparedStatement(StmtUpdateGuildRank) db.SetPreparedStatement(StmtAddGuildRank) db.SetPreparedStatement(StmtDeleteGuildRank) + db.SetPreparedStatement(StmtAddGuildPetitionSignature) return &guildsMySQLRepo{ - db: db, + db: db, + worldDB: worldDB, }, nil } @@ -59,6 +72,7 @@ ORDER BY g.guildid ASC`) return nil, err } + guild.RealmID = realmID result[guild.ID] = &guild } @@ -92,8 +106,12 @@ ORDER BY g.guildid ASC`) // load guild members rows, err = g.db.DBByRealm(realmID).Query(` SELECT - guildid, gm.guid, ` + "`rank`" + `, pnote, offnote, c.name, c.level, c.class, c.gender, c.zone, c.account, c.online, c.logout_time + guildid, gm.guid, ` + "`rank`" + `, pnote, offnote, + COALESCE(w.tab0, 0), COALESCE(w.tab1, 0), COALESCE(w.tab2, 0), COALESCE(w.tab3, 0), + COALESCE(w.tab4, 0), COALESCE(w.tab5, 0), COALESCE(w.money, 0), + c.name, c.level, c.class, c.gender, c.zone, c.account, c.online, c.logout_time FROM guild_member gm +LEFT JOIN guild_member_withdraw w ON gm.guid = w.guid LEFT JOIN characters c ON c.guid = gm.guid ORDER BY guildid ASC`) if err != nil { return nil, err @@ -102,8 +120,10 @@ LEFT JOIN characters c ON c.guid = gm.guid ORDER BY guildid ASC`) for rows.Next() { member := GuildMember{} err = rows.Scan( - &member.GuildID, &member.PlayerGUID, &member.Rank, &member.PublicNote, &member.OfficerNote, &member.Name, - &member.Lvl, &member.Class, &member.Gender, &member.AreaID, &member.Account, &member.Status, &member.LogoutTime, + &member.GuildID, &member.PlayerGUID, &member.Rank, &member.PublicNote, &member.OfficerNote, + &member.BankWithdraw[0], &member.BankWithdraw[1], &member.BankWithdraw[2], &member.BankWithdraw[3], + &member.BankWithdraw[4], &member.BankWithdraw[5], &member.BankWithdraw[6], + &member.Name, &member.Lvl, &member.Class, &member.Gender, &member.AreaID, &member.Account, &member.Status, &member.LogoutTime, ) if err != nil { return nil, err @@ -119,15 +139,221 @@ LEFT JOIN characters c ON c.guid = gm.guid ORDER BY guildid ASC`) return nil, rows.Err() } + if err = g.loadBankTabsForGuilds(ctx, realmID, result); err != nil { + return nil, err + } + + if err = g.loadBankRightsForGuilds(ctx, realmID, result); err != nil { + return nil, err + } + return result, nil } // GuildByRealmAndID loads guild by realm and id. -// TODO: implement me -// Currently unused since we have cached version for this. func (g *guildsMySQLRepo) GuildByRealmAndID(ctx context.Context, realmID uint32, guildID uint64) (*Guild, error) { - panic("implement me") - return nil, nil + guild := Guild{RealmID: realmID} + err := g.db.DBByRealm(realmID).QueryRowContext(ctx, ` +SELECT + g.guildid, g.name, g.leaderguid, g.EmblemStyle, g.EmblemColor, g.BorderStyle, + g.BorderColor, g.BackgroundColor, g.info, g.motd, g.createdate, g.BankMoney +FROM guild g +WHERE g.guildid = ?`, guildID).Scan( + &guild.ID, &guild.Name, &guild.LeaderGUID, &guild.Emblem.Style, &guild.Emblem.Color, + &guild.Emblem.BorderStyle, &guild.Emblem.BorderColor, &guild.Emblem.BackgroundColor, + &guild.Info, &guild.MessageOfTheDay, &guild.CrateTimeUnix, &guild.BankMoney, + ) + if err == sql.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, err + } + + rows, err := g.db.DBByRealm(realmID).QueryContext(ctx, ` +SELECT guildid, rid, rname, rights, BankMoneyPerDay +FROM guild_rank +WHERE guildid = ? +ORDER BY rid ASC`, guildID) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + rank := GuildRank{} + err = rows.Scan(&rank.GuildID, &rank.Rank, &rank.Name, &rank.Rights, &rank.MoneyPerDay) + if err != nil { + return nil, err + } + + guild.GuildRanks = append(guild.GuildRanks, rank) + } + + if rows.Err() != nil { + return nil, rows.Err() + } + + if err = g.loadBankTabsForGuild(ctx, realmID, &guild); err != nil { + return nil, err + } + + if err = g.loadBankRightsForGuild(ctx, realmID, &guild); err != nil { + return nil, err + } + + rows, err = g.db.DBByRealm(realmID).QueryContext(ctx, ` +SELECT + guildid, gm.guid, `+"`rank`"+`, pnote, offnote, + COALESCE(w.tab0, 0), COALESCE(w.tab1, 0), COALESCE(w.tab2, 0), COALESCE(w.tab3, 0), + COALESCE(w.tab4, 0), COALESCE(w.tab5, 0), COALESCE(w.money, 0), + c.name, c.level, c.class, c.gender, c.zone, c.account, c.online, c.logout_time +FROM guild_member gm +LEFT JOIN guild_member_withdraw w ON gm.guid = w.guid +LEFT JOIN characters c ON c.guid = gm.guid +WHERE gm.guildid = ? +ORDER BY gm.guid ASC`, guildID) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + member := GuildMember{} + err = rows.Scan( + &member.GuildID, &member.PlayerGUID, &member.Rank, &member.PublicNote, &member.OfficerNote, + &member.BankWithdraw[0], &member.BankWithdraw[1], &member.BankWithdraw[2], &member.BankWithdraw[3], + &member.BankWithdraw[4], &member.BankWithdraw[5], &member.BankWithdraw[6], + &member.Name, &member.Lvl, &member.Class, &member.Gender, &member.AreaID, &member.Account, &member.Status, &member.LogoutTime, + ) + if err != nil { + return nil, err + } + + guild.GuildMembers = append(guild.GuildMembers, &member) + } + + if rows.Err() != nil { + return nil, rows.Err() + } + + return &guild, nil +} + +func (g *guildsMySQLRepo) loadBankTabsForGuilds(ctx context.Context, realmID uint32, guilds map[uint64]*Guild) error { + rows, err := g.db.DBByRealm(realmID).QueryContext(ctx, ` +SELECT guildid, COUNT(*) +FROM guild_bank_tab +GROUP BY guildid`) + if err != nil { + return err + } + defer rows.Close() + + for rows.Next() { + var guildID uint64 + var tabs uint32 + if err = rows.Scan(&guildID, &tabs); err != nil { + return err + } + + if guild := guilds[guildID]; guild != nil { + guild.PurchasedBankTabs = clampGuildBankTabCount(tabs) + } + } + + return rows.Err() +} + +func (g *guildsMySQLRepo) loadBankTabsForGuild(ctx context.Context, realmID uint32, guild *Guild) error { + var tabs uint32 + err := g.db.DBByRealm(realmID).QueryRowContext(ctx, ` +SELECT COUNT(*) +FROM guild_bank_tab +WHERE guildid = ?`, guild.ID).Scan(&tabs) + if err != nil { + return err + } + + guild.PurchasedBankTabs = clampGuildBankTabCount(tabs) + return nil +} + +func (g *guildsMySQLRepo) loadBankRightsForGuilds(ctx context.Context, realmID uint32, guilds map[uint64]*Guild) error { + rows, err := g.db.DBByRealm(realmID).QueryContext(ctx, ` +SELECT guildid, TabId, rid, gbright, SlotPerDay +FROM guild_bank_right +ORDER BY guildid ASC, rid ASC, TabId ASC`) + if err != nil { + return err + } + defer rows.Close() + + for rows.Next() { + var guildID uint64 + var tabID uint8 + var rankID uint8 + var flags uint32 + var withdrawItemLimit uint32 + if err = rows.Scan(&guildID, &tabID, &rankID, &flags, &withdrawItemLimit); err != nil { + return err + } + + applyGuildBankRight(guilds[guildID], tabID, rankID, flags, withdrawItemLimit) + } + + return rows.Err() +} + +func (g *guildsMySQLRepo) loadBankRightsForGuild(ctx context.Context, realmID uint32, guild *Guild) error { + rows, err := g.db.DBByRealm(realmID).QueryContext(ctx, ` +SELECT TabId, rid, gbright, SlotPerDay +FROM guild_bank_right +WHERE guildid = ? +ORDER BY rid ASC, TabId ASC`, guild.ID) + if err != nil { + return err + } + defer rows.Close() + + for rows.Next() { + var tabID uint8 + var rankID uint8 + var flags uint32 + var withdrawItemLimit uint32 + if err = rows.Scan(&tabID, &rankID, &flags, &withdrawItemLimit); err != nil { + return err + } + + applyGuildBankRight(guild, tabID, rankID, flags, withdrawItemLimit) + } + + return rows.Err() +} + +func applyGuildBankRight(guild *Guild, tabID, rankID uint8, flags, withdrawItemLimit uint32) { + if guild == nil || tabID >= GuildBankMaxTabs { + return + } + + for i := range guild.GuildRanks { + if guild.GuildRanks[i].Rank == rankID { + guild.GuildRanks[i].BankTabRights[tabID] = GuildBankTabRight{ + TabID: tabID, + Flags: flags, + WithdrawItemLimit: withdrawItemLimit, + } + return + } + } +} + +func clampGuildBankTabCount(tabs uint32) uint8 { + if tabs > GuildBankMaxTabs { + return GuildBankMaxTabs + } + + return uint8(tabs) } // AddGuildInvite links user invite to a specific guild. @@ -145,6 +371,9 @@ func (g *guildsMySQLRepo) GuildIDByCharInvite(ctx context.Context, realmID uint3 var guildID uint64 err := row.Scan(&guildID) + if err == sql.ErrNoRows { + return 0, nil + } if err != nil { return 0, err } @@ -166,12 +395,51 @@ func (g *guildsMySQLRepo) GuildIDByRealmAndMemberGUID(ctx context.Context, realm var guildID uint64 err := row.Scan(&guildID) + if err == sql.ErrNoRows { + return 0, nil + } if err != nil { return 0, err } return guildID, nil } +func (g *guildsMySQLRepo) IgnoredByGuildMembers(ctx context.Context, realmID uint32, senderGUID uint64, receiverGUIDs []uint64) (map[uint64]bool, error) { + ignored := make(map[uint64]bool) + if len(receiverGUIDs) == 0 { + return ignored, nil + } + + placeholders := strings.TrimRight(strings.Repeat("?,", len(receiverGUIDs)), ",") + args := make([]any, 0, len(receiverGUIDs)+2) + for _, receiverGUID := range receiverGUIDs { + args = append(args, receiverGUID) + } + args = append(args, senderGUID, characterSocialFlagIgnore) + + rows, err := g.db.DBByRealm(realmID).QueryContext(ctx, fmt.Sprintf( + "SELECT guid FROM character_social WHERE guid IN (%s) AND friend = ? AND (flags & ?) <> 0", + placeholders, + ), args...) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var receiverGUID uint64 + if err := rows.Scan(&receiverGUID); err != nil { + return nil, err + } + ignored[receiverGUID] = true + } + if rows.Err() != nil { + return nil, rows.Err() + } + + return ignored, nil +} + // AddGuildMember adds guild member to the guild. func (g *guildsMySQLRepo) AddGuildMember(ctx context.Context, realmID uint32, member GuildMember) error { _, err := g.db.PreparedStatement(realmID, StmtAddGuildMember).ExecContext( @@ -217,9 +485,34 @@ func (g *guildsMySQLRepo) SetGuildInfo(ctx context.Context, realmID uint32, guil } // UpdateGuildRank updates guild rank. -func (g *guildsMySQLRepo) UpdateGuildRank(ctx context.Context, realmID uint32, guildID uint64, rank uint8, name string, rights, moneyPerDay uint32) error { - _, err := g.db.PreparedStatement(realmID, StmtUpdateGuildRank).ExecContext(ctx, name, rights, moneyPerDay, rank, guildID) - return err +func (g *guildsMySQLRepo) UpdateGuildRank(ctx context.Context, realmID uint32, guildID uint64, rank uint8, name string, rights, moneyPerDay uint32, bankTabRights [GuildBankMaxTabs]GuildBankTabRight) error { + tx, err := g.db.DBByRealm(realmID).BeginTx(ctx, nil) + if err != nil { + return err + } + + if _, err = tx.ExecContext(ctx, StmtUpdateGuildRank.Stmt(), name, rights, moneyPerDay, rank, guildID); err != nil { + tx.Rollback() + return err + } + + for _, right := range bankTabRights { + if _, err = tx.ExecContext(ctx, ` +INSERT INTO guild_bank_right (guildid, TabId, rid, gbright, SlotPerDay) +VALUES (?, ?, ?, ?, ?) +ON DUPLICATE KEY UPDATE gbright = VALUES(gbright), SlotPerDay = VALUES(SlotPerDay)`, + guildID, right.TabID, rank, right.Flags, right.WithdrawItemLimit); err != nil { + tx.Rollback() + return err + } + } + + if err = tx.Commit(); err != nil { + tx.Rollback() + return err + } + + return nil } // AddGuildRank adds guild rank. @@ -233,3 +526,57 @@ func (g *guildsMySQLRepo) DeleteLowestGuildRank(ctx context.Context, realmID uin _, err := g.db.PreparedStatement(realmID, StmtDeleteGuildRank).ExecContext(ctx, guildID, rank) return err } + +// GuildPetitionByGUID loads a native petition by item GUID. +func (g *guildsMySQLRepo) GuildPetitionByGUID(ctx context.Context, realmID uint32, petitionGUID uint64) (*GuildPetition, error) { + petitionGuidLow := uint64(wowguid.New(petitionGUID).GetCounter()) + petition := GuildPetition{RealmID: realmID} + err := g.db.DBByRealm(realmID).QueryRowContext(ctx, ` +SELECT petition_id, ownerguid, petitionguid, name, type +FROM petition +WHERE petitionguid = ?`, petitionGuidLow).Scan( + &petition.PetitionID, + &petition.OwnerGUID, + &petitionGuidLow, + &petition.Name, + &petition.Type, + ) + if err == sql.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, err + } + + petition.PetitionGUID = wowguid.NewFromCounter(wowguid.Item, wowguid.LowType(petitionGuidLow)).GetRawValue() + + rows, err := g.db.DBByRealm(realmID).QueryContext(ctx, ` +SELECT playerguid, player_account +FROM petition_sign +WHERE petition_id = ? +ORDER BY playerguid ASC`, petition.PetitionID) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var signature GuildPetitionSignature + if err = rows.Scan(&signature.PlayerGUID, &signature.PlayerAccount); err != nil { + return nil, err + } + petition.Signatures = append(petition.Signatures, signature) + } + if rows.Err() != nil { + return nil, rows.Err() + } + + return &petition, nil +} + +// AddGuildPetitionSignature persists a native guild petition signature. +func (g *guildsMySQLRepo) AddGuildPetitionSignature(ctx context.Context, realmID uint32, petitionID uint32, petitionGUID, ownerGUID, playerGUID uint64, playerAccount uint32) error { + petitionGuidLow := uint64(wowguid.New(petitionGUID).GetCounter()) + _, err := g.db.PreparedStatement(realmID, StmtAddGuildPetitionSignature).ExecContext(ctx, ownerGUID, petitionGuidLow, petitionID, playerGUID, playerAccount) + return err +} diff --git a/apps/guildserver/repo/stmts.go b/apps/guildserver/repo/stmts.go index 00b1d05..cf82a70 100644 --- a/apps/guildserver/repo/stmts.go +++ b/apps/guildserver/repo/stmts.go @@ -44,6 +44,9 @@ const ( // StmtDeleteGuildRank deletes guild rank. StmtDeleteGuildRank + + // StmtAddGuildPetitionSignature adds a signer to a native guild petition. + StmtAddGuildPetitionSignature ) // CharsPreparedStatements represents prepared statements for the characters database. @@ -86,6 +89,8 @@ func (s CharsPreparedStatements) Stmt() string { return "INSERT INTO guild_rank (guildid, rid, rname, rights, BankMoneyPerDay) VALUES (?, ?, ?, ?, ?)" case StmtDeleteGuildRank: return "DELETE FROM guild_rank WHERE guildid = ? AND rid >= ?" + case StmtAddGuildPetitionSignature: + return "INSERT INTO petition_sign (ownerguid, petitionguid, petition_id, playerguid, player_account) VALUES (?, ?, ?, ?, ?)" } panic(fmt.Errorf("unk stmt %d", s)) } diff --git a/apps/guildserver/server/guilds-logger_debug.go b/apps/guildserver/server/guilds-logger_debug.go index 4f5c524..70f16e0 100644 --- a/apps/guildserver/server/guilds-logger_debug.go +++ b/apps/guildserver/server/guilds-logger_debug.go @@ -221,3 +221,45 @@ func (g *guildDebugLoggerMiddleware) SendGuildMessage(ctx context.Context, param res, err = g.realService.SendGuildMessage(ctx, params) return } + +func (g *guildDebugLoggerMiddleware) GetGuildPetition(ctx context.Context, params *pb.GetGuildPetitionParams) (res *pb.GetGuildPetitionResponse, err error) { + defer func(t time.Time) { + g.logger.Debug(). + Uint32("realmID", params.RealmID). + Uint64("petitionGUID", params.PetitionGUID). + Err(err). + Msgf("Handled GetGuildPetition for %v.", time.Since(t)) + }(time.Now()) + + res, err = g.realService.GetGuildPetition(ctx, params) + return +} + +func (g *guildDebugLoggerMiddleware) OfferGuildPetition(ctx context.Context, params *pb.OfferGuildPetitionParams) (res *pb.OfferGuildPetitionResponse, err error) { + defer func(t time.Time) { + g.logger.Debug(). + Uint32("realmID", params.RealmID). + Uint64("ownerGUID", params.OwnerGUID). + Uint64("targetGUID", params.TargetGUID). + Uint64("petitionGUID", params.PetitionGUID). + Err(err). + Msgf("Handled OfferGuildPetition for %v.", time.Since(t)) + }(time.Now()) + + res, err = g.realService.OfferGuildPetition(ctx, params) + return +} + +func (g *guildDebugLoggerMiddleware) SignGuildPetition(ctx context.Context, params *pb.SignGuildPetitionParams) (res *pb.SignGuildPetitionResponse, err error) { + defer func(t time.Time) { + g.logger.Debug(). + Uint32("realmID", params.RealmID). + Uint64("signerGUID", params.SignerGUID). + Uint64("petitionGUID", params.PetitionGUID). + Err(err). + Msgf("Handled SignGuildPetition for %v.", time.Since(t)) + }(time.Now()) + + res, err = g.realService.SignGuildPetition(ctx, params) + return +} diff --git a/apps/guildserver/server/guilds.go b/apps/guildserver/server/guilds.go index 1e22abc..6c972b7 100644 --- a/apps/guildserver/server/guilds.go +++ b/apps/guildserver/server/guilds.go @@ -4,6 +4,7 @@ import ( "context" "github.com/walkline/ToCloud9/apps/guildserver" + "github.com/walkline/ToCloud9/apps/guildserver/repo" "github.com/walkline/ToCloud9/apps/guildserver/service" "github.com/walkline/ToCloud9/gen/guilds/pb" ) @@ -67,33 +68,54 @@ func (g *GuildServer) GetRosterInfo(ctx context.Context, params *pb.GetRosterInf LogoutTime: guild.GuildMembers[i].LogoutTime, Note: guild.GuildMembers[i].PublicNote, OfficerNote: guild.GuildMembers[i].OfficerNote, + BankWithdraw: []uint32{ + guild.GuildMembers[i].BankWithdraw[0], + guild.GuildMembers[i].BankWithdraw[1], + guild.GuildMembers[i].BankWithdraw[2], + guild.GuildMembers[i].BankWithdraw[3], + guild.GuildMembers[i].BankWithdraw[4], + guild.GuildMembers[i].BankWithdraw[5], + guild.GuildMembers[i].BankWithdraw[6], + }, } } ranks := make([]*pb.GetRosterInfoResponse_Rank, len(guild.GuildRanks)) for i := range guild.GuildRanks { + bankTabRights := make([]*pb.GetRosterInfoResponse_Rank_BankTabRight, len(guild.GuildRanks[i].BankTabRights)) + for tabID := range guild.GuildRanks[i].BankTabRights { + right := guild.GuildRanks[i].BankTabRights[tabID] + bankTabRights[tabID] = &pb.GetRosterInfoResponse_Rank_BankTabRight{ + TabID: uint32(right.TabID), + Flags: right.Flags, + WithdrawItemLimit: right.WithdrawItemLimit, + } + } + ranks[i] = &pb.GetRosterInfoResponse_Rank{ - Id: uint32(guild.GuildRanks[i].Rank), - Flags: guild.GuildRanks[i].Rights, - GoldLimit: guild.GuildRanks[i].MoneyPerDay, + Id: uint32(guild.GuildRanks[i].Rank), + Flags: guild.GuildRanks[i].Rights, + GoldLimit: guild.GuildRanks[i].MoneyPerDay, + BankTabRights: bankTabRights, } } return &pb.GetRosterInfoResponse{ Api: guildserver.Ver, Guild: &pb.GetRosterInfoResponse_Guild{ - Id: guild.ID, - WelcomeText: guild.MessageOfTheDay, - InfoText: guild.Info, - Members: members, - Ranks: ranks, + Id: guild.ID, + WelcomeText: guild.MessageOfTheDay, + InfoText: guild.Info, + Members: members, + Ranks: ranks, + PurchasedBankTabs: uint32(guild.PurchasedBankTabs), }, }, nil } // InviteMember handles members invite. func (g *GuildServer) InviteMember(ctx context.Context, params *pb.InviteMemberParams) (*pb.InviteMemberResponse, error) { - err := g.guildsService.InviteMember(ctx, params.RealmID, params.Inviter, params.Invitee, params.InviteeName) + err := g.guildsService.InviteMember(ctx, params.RealmID, params.Inviter, params.Invitee, params.InviteeName, uint8(params.InviteeRace), params.AllowCrossFaction) if err != nil { return nil, err } @@ -114,6 +136,8 @@ func (g *GuildServer) InviteAccepted(ctx context.Context, params *pb.InviteAccep CharGender: uint8(params.Character.Gender), CharAreaID: params.Character.AreaID, CharAccount: params.Character.AccountID, + + AllowCrossFaction: params.AllowCrossFaction, }) if err != nil { return nil, err @@ -193,11 +217,23 @@ func (g *GuildServer) SetGuildInfo(ctx context.Context, params *pb.SetGuildInfoP // UpdateRank handles guild rank update. func (g *GuildServer) UpdateRank(ctx context.Context, params *pb.RankUpdateParams) (*pb.RankUpdateResponse, error) { + var bankTabRights [repo.GuildBankMaxTabs]repo.GuildBankTabRight + for _, right := range params.BankTabRights { + if right.TabID < repo.GuildBankMaxTabs { + bankTabRights[right.TabID] = repo.GuildBankTabRight{ + TabID: uint8(right.TabID), + Flags: right.Flags, + WithdrawItemLimit: right.WithdrawItemLimit, + } + } + } + err := g.guildsService.UpdateGuildRank(ctx, params.RealmID, params.ChangerGUID, service.GuildRank{ - RankID: uint8(params.Rank), - Name: params.RankName, - Rights: params.Rights, - MoneyPerDay: params.MoneyPerDay, + RankID: uint8(params.Rank), + Name: params.RankName, + Rights: params.Rights, + MoneyPerDay: params.MoneyPerDay, + BankTabRights: bankTabRights, }) if err != nil { return nil, err @@ -253,7 +289,7 @@ func (g *GuildServer) DemoteMember(ctx context.Context, params *pb.PromoteDemote // SendGuildMessage sends new message to the guild members. func (g *GuildServer) SendGuildMessage(ctx context.Context, params *pb.SendGuildMessageParams) (*pb.SendGuildMessageResponse, error) { - err := g.guildsService.SendGuildMessage(ctx, params.RealmID, params.SenderGUID, params.Message, params.Language, params.IsOfficerMessage) + err := g.guildsService.SendGuildMessage(ctx, params.RealmID, params.SenderGUID, params.Message, params.Language, params.IsOfficerMessage, uint8(params.SenderChatTag)) if err != nil { return nil, err } @@ -262,3 +298,323 @@ func (g *GuildServer) SendGuildMessage(ctx context.Context, params *pb.SendGuild Api: guildserver.Ver, }, nil } + +// GetGuildPetition handles a native guild petition lookup. +func (g *GuildServer) GetGuildPetition(ctx context.Context, params *pb.GetGuildPetitionParams) (*pb.GetGuildPetitionResponse, error) { + petition, err := g.guildsService.GuildPetitionByGUID(ctx, params.RealmID, params.PetitionGUID) + if err != nil { + return nil, err + } + + return &pb.GetGuildPetitionResponse{ + Api: guildserver.Ver, + Petition: repoGuildPetitionToPB(petition), + }, nil +} + +// OfferGuildPetition handles same-realm distributed guild charter offers. +func (g *GuildServer) OfferGuildPetition(ctx context.Context, params *pb.OfferGuildPetitionParams) (*pb.OfferGuildPetitionResponse, error) { + status, err := g.guildsService.OfferGuildPetition(ctx, params.RealmID, params.OwnerGUID, params.TargetGUID, params.TargetName, params.PetitionGUID) + if err != nil { + return nil, err + } + + return &pb.OfferGuildPetitionResponse{ + Api: guildserver.Ver, + Status: pb.OfferGuildPetitionResponse_Status(status), + }, nil +} + +// SignGuildPetition handles same-realm distributed guild charter signatures. +func (g *GuildServer) SignGuildPetition(ctx context.Context, params *pb.SignGuildPetitionParams) (*pb.SignGuildPetitionResponse, error) { + status, err := g.guildsService.SignGuildPetition(ctx, params.RealmID, params.SignerGUID, params.SignerName, params.SignerAccountID, params.SignerGuildID, params.PetitionGUID) + if err != nil { + return nil, err + } + + return &pb.SignGuildPetitionResponse{ + Api: guildserver.Ver, + Status: pb.SignGuildPetitionResponse_Status(status), + }, nil +} + +func (g *GuildServer) GetGuildBank(ctx context.Context, params *pb.GetGuildBankParams) (*pb.GetGuildBankResponse, error) { + bank, remaining, status, err := g.guildsService.GetGuildBank(ctx, params.RealmID, params.GuildID, params.MemberGUID, uint8(params.TabID), params.FullUpdate) + if err != nil { + return nil, err + } + resp := &pb.GetGuildBankResponse{ + Api: guildserver.Ver, + Status: guildBankStatusToPB(status), + TabID: params.TabID, + WithdrawalsRemaining: remaining, + FullUpdate: params.FullUpdate, + } + if bank != nil { + resp.Money = bank.Money + resp.Tabs = repoGuildBankTabsToPB(bank.Tabs) + for _, tab := range bank.Tabs { + if tab.TabID == uint8(params.TabID) { + resp.Items = repoGuildBankItemsToPB(tab.Items) + break + } + } + } + return resp, nil +} + +func (g *GuildServer) GetGuildBankLog(ctx context.Context, params *pb.GetGuildBankLogParams) (*pb.GetGuildBankLogResponse, error) { + entries, status, err := g.guildsService.GetGuildBankLog(ctx, params.RealmID, params.GuildID, params.MemberGUID, uint8(params.TabID)) + if err != nil { + return nil, err + } + return &pb.GetGuildBankLogResponse{ + Api: guildserver.Ver, + Status: guildBankStatusToPB(status), + TabID: params.TabID, + Entries: repoGuildBankLogEntriesToPB(entries), + }, nil +} + +func (g *GuildServer) GetGuildBankTabText(ctx context.Context, params *pb.GetGuildBankTabTextParams) (*pb.GetGuildBankTabTextResponse, error) { + text, status, err := g.guildsService.GetGuildBankTabText(ctx, params.RealmID, params.GuildID, params.MemberGUID, uint8(params.TabID)) + if err != nil { + return nil, err + } + return &pb.GetGuildBankTabTextResponse{ + Api: guildserver.Ver, + Status: guildBankStatusToPB(status), + TabID: params.TabID, + Text: text, + }, nil +} + +func (g *GuildServer) UpdateGuildBankTab(ctx context.Context, params *pb.UpdateGuildBankTabParams) (*pb.GuildBankActionResponse, error) { + status, err := g.guildsService.UpdateGuildBankTab(ctx, params.RealmID, params.GuildID, params.MemberGUID, uint8(params.TabID), params.Name, params.Icon) + if err != nil { + return nil, err + } + return &pb.GuildBankActionResponse{Api: guildserver.Ver, Status: guildBankStatusToPB(status)}, nil +} + +func (g *GuildServer) SetGuildBankTabText(ctx context.Context, params *pb.SetGuildBankTabTextParams) (*pb.GuildBankActionResponse, error) { + status, err := g.guildsService.SetGuildBankTabText(ctx, params.RealmID, params.GuildID, params.MemberGUID, uint8(params.TabID), params.Text) + if err != nil { + return nil, err + } + return &pb.GuildBankActionResponse{Api: guildserver.Ver, Status: guildBankStatusToPB(status)}, nil +} + +func (g *GuildServer) BuyGuildBankTab(ctx context.Context, params *pb.BuyGuildBankTabParams) (*pb.BuyGuildBankTabResponse, error) { + status, err := g.guildsService.BuyGuildBankTab(ctx, params.RealmID, params.GuildID, params.MemberGUID, uint8(params.TabID), 0) + if err != nil { + return nil, err + } + return &pb.BuyGuildBankTabResponse{Api: guildserver.Ver, Status: guildBankStatusToPB(status)}, nil +} + +func (g *GuildServer) DepositGuildBankMoney(ctx context.Context, params *pb.DepositGuildBankMoneyParams) (*pb.GuildBankActionResponse, error) { + status, err := g.guildsService.DepositGuildBankMoney(ctx, params.RealmID, params.GuildID, params.MemberGUID, params.Amount) + if err != nil { + return nil, err + } + return &pb.GuildBankActionResponse{Api: guildserver.Ver, Status: guildBankStatusToPB(status)}, nil +} + +func (g *GuildServer) WithdrawGuildBankMoney(ctx context.Context, params *pb.WithdrawGuildBankMoneyParams) (*pb.GuildBankActionResponse, error) { + logGUID, status, err := g.guildsService.WithdrawGuildBankMoney(ctx, params.RealmID, params.GuildID, params.MemberGUID, params.Amount, params.Repair) + if err != nil { + return nil, err + } + return &pb.GuildBankActionResponse{Api: guildserver.Ver, Status: guildBankStatusToPB(status), LogGUID: logGUID}, nil +} + +func (g *GuildServer) RollbackGuildBankMoneyWithdraw(ctx context.Context, params *pb.RollbackGuildBankMoneyWithdrawParams) (*pb.GuildBankActionResponse, error) { + status, err := g.guildsService.RollbackGuildBankMoneyWithdraw(ctx, params.RealmID, params.GuildID, params.MemberGUID, params.Amount, params.Repair, params.LogGUID) + if err != nil { + return nil, err + } + return &pb.GuildBankActionResponse{Api: guildserver.Ver, Status: guildBankStatusToPB(status)}, nil +} + +func (g *GuildServer) DepositGuildBankItem(ctx context.Context, params *pb.DepositGuildBankItemParams) (*pb.GuildBankItemMutationResponse, error) { + status, err := g.guildsService.DepositGuildBankItem(ctx, params.RealmID, params.GuildID, params.MemberGUID, uint8(params.TabID), uint8(params.SlotID), pbGuildBankItemToRepo(params.Item)) + if err != nil { + return nil, err + } + return &pb.GuildBankItemMutationResponse{Api: guildserver.Ver, Status: guildBankStatusToPB(status), ChangedSlots: []uint32{params.SlotID}}, nil +} + +func (g *GuildServer) WithdrawGuildBankItem(ctx context.Context, params *pb.WithdrawGuildBankItemParams) (*pb.GuildBankItemMutationResponse, error) { + item, logGUID, status, err := g.guildsService.WithdrawGuildBankItem(ctx, params.RealmID, params.GuildID, params.MemberGUID, uint8(params.TabID), uint8(params.SlotID), params.Count) + if err != nil { + return nil, err + } + return &pb.GuildBankItemMutationResponse{Api: guildserver.Ver, Status: guildBankStatusToPB(status), Item: repoGuildBankItemToPB(item), ChangedSlots: []uint32{params.SlotID}, LogGUID: logGUID}, nil +} + +func (g *GuildServer) RollbackGuildBankItemWithdraw(ctx context.Context, params *pb.RollbackGuildBankItemWithdrawParams) (*pb.GuildBankItemMutationResponse, error) { + changed, status, err := g.guildsService.RollbackGuildBankItemWithdraw(ctx, params.RealmID, params.GuildID, params.MemberGUID, uint8(params.TabID), uint8(params.SlotID), pbGuildBankItemToRepo(params.Item), params.LogGUID) + if err != nil { + return nil, err + } + changedSlots := make([]uint32, len(changed)) + for i := range changed { + changedSlots[i] = uint32(changed[i]) + } + return &pb.GuildBankItemMutationResponse{Api: guildserver.Ver, Status: guildBankStatusToPB(status), ChangedSlots: changedSlots}, nil +} + +func (g *GuildServer) MoveGuildBankItem(ctx context.Context, params *pb.MoveGuildBankItemParams) (*pb.GuildBankItemMutationResponse, error) { + changed, status, err := g.guildsService.MoveGuildBankItem(ctx, params.RealmID, params.GuildID, params.MemberGUID, uint8(params.SourceTabID), uint8(params.SourceSlotID), uint8(params.DestinationTabID), uint8(params.DestinationSlotID), params.Count) + if err != nil { + return nil, err + } + changedSlots := make([]uint32, len(changed)) + for i := range changed { + changedSlots[i] = uint32(changed[i]) + } + return &pb.GuildBankItemMutationResponse{Api: guildserver.Ver, Status: guildBankStatusToPB(status), ChangedSlots: changedSlots}, nil +} + +func repoGuildPetitionToPB(petition *repo.GuildPetition) *pb.GuildPetition { + if petition == nil { + return nil + } + + signatures := make([]*pb.GuildPetitionSignature, len(petition.Signatures)) + for i := range petition.Signatures { + signatures[i] = &pb.GuildPetitionSignature{ + PlayerGUID: petition.Signatures[i].PlayerGUID, + PlayerAccount: petition.Signatures[i].PlayerAccount, + } + } + + return &pb.GuildPetition{ + PetitionGUID: petition.PetitionGUID, + PetitionID: petition.PetitionID, + OwnerGUID: petition.OwnerGUID, + Name: petition.Name, + Type: uint32(petition.Type), + Signatures: signatures, + } +} + +func guildBankStatusToPB(status service.GuildBankStatus) pb.GuildBankStatus_Status { + switch status { + case service.GuildBankStatusOK: + return pb.GuildBankStatus_Ok + case service.GuildBankStatusGuildNotFound: + return pb.GuildBankStatus_GuildNotFound + case service.GuildBankStatusNotInGuild: + return pb.GuildBankStatus_NotInGuild + case service.GuildBankStatusNotEnoughRights: + return pb.GuildBankStatus_NotEnoughRights + case service.GuildBankStatusInvalidTab: + return pb.GuildBankStatus_InvalidTab + case service.GuildBankStatusInvalidSlot: + return pb.GuildBankStatus_InvalidSlot + case service.GuildBankStatusNotEnoughMoney: + return pb.GuildBankStatus_NotEnoughMoney + case service.GuildBankStatusBankFull: + return pb.GuildBankStatus_BankFull + case service.GuildBankStatusWithdrawLimit: + return pb.GuildBankStatus_WithdrawLimit + case service.GuildBankStatusItemNotFound: + return pb.GuildBankStatus_ItemNotFound + default: + return pb.GuildBankStatus_Failed + } +} + +func repoGuildBankTabsToPB(tabs []repo.GuildBankTab) []*pb.GuildBankTab { + result := make([]*pb.GuildBankTab, len(tabs)) + for i := range tabs { + result[i] = &pb.GuildBankTab{ + TabID: uint32(tabs[i].TabID), + Name: tabs[i].Name, + Icon: tabs[i].Icon, + Text: tabs[i].Text, + Items: repoGuildBankItemsToPB(tabs[i].Items), + } + } + return result +} + +func repoGuildBankItemsToPB(items []repo.GuildBankItem) []*pb.GuildBankItem { + result := make([]*pb.GuildBankItem, len(items)) + for i := range items { + result[i] = repoGuildBankItemToPB(&items[i]) + } + return result +} + +func repoGuildBankItemToPB(item *repo.GuildBankItem) *pb.GuildBankItem { + if item == nil { + return nil + } + sockets := make([]*pb.GuildBankSocketEnchant, len(item.SocketEnchants)) + for i := range item.SocketEnchants { + sockets[i] = &pb.GuildBankSocketEnchant{ + SocketIndex: uint32(item.SocketEnchants[i].SocketIndex), + SocketEnchantID: item.SocketEnchants[i].SocketEnchantID, + } + } + return &pb.GuildBankItem{ + ItemGUID: item.ItemGUID, + Entry: item.Entry, + Slot: uint32(item.Slot), + Count: item.Count, + Flags: item.Flags, + RandomPropertyID: item.RandomPropertyID, + RandomPropertySeed: item.RandomPropertySeed, + Durability: item.Durability, + EnchantmentID: item.EnchantmentID, + SocketEnchants: sockets, + Charges: item.Charges, + Text: item.Text, + } +} + +func pbGuildBankItemToRepo(item *pb.GuildBankItem) repo.GuildBankItem { + if item == nil { + return repo.GuildBankItem{} + } + sockets := make([]repo.GuildBankSocketEnchant, len(item.SocketEnchants)) + for i := range item.SocketEnchants { + sockets[i] = repo.GuildBankSocketEnchant{ + SocketIndex: uint8(item.SocketEnchants[i].SocketIndex), + SocketEnchantID: item.SocketEnchants[i].SocketEnchantID, + } + } + return repo.GuildBankItem{ + ItemGUID: item.ItemGUID, + Entry: item.Entry, + Slot: uint8(item.Slot), + Count: item.Count, + Flags: item.Flags, + RandomPropertyID: item.RandomPropertyID, + RandomPropertySeed: item.RandomPropertySeed, + Durability: item.Durability, + EnchantmentID: item.EnchantmentID, + SocketEnchants: sockets, + Charges: item.Charges, + Text: item.Text, + } +} + +func repoGuildBankLogEntriesToPB(entries []repo.GuildBankLogEntry) []*pb.GuildBankLogEntry { + result := make([]*pb.GuildBankLogEntry, len(entries)) + for i := range entries { + result[i] = &pb.GuildBankLogEntry{ + PlayerGUID: entries[i].PlayerGUID, + TimeOffset: entries[i].TimeOffset, + EntryType: int32(entries[i].EntryType), + Money: entries[i].Money, + ItemID: entries[i].ItemID, + Count: entries[i].Count, + OtherTab: int32(entries[i].OtherTab), + } + } + return result +} diff --git a/apps/guildserver/service/guilds-cache.go b/apps/guildserver/service/guilds-cache.go index c6b1019..d5551c8 100644 --- a/apps/guildserver/service/guilds-cache.go +++ b/apps/guildserver/service/guilds-cache.go @@ -22,4 +22,7 @@ type GuildsCache interface { // Warmup called on startup to warmup cache if possible. Warmup(ctx context.Context, realmID uint32) error + + // RefreshGuildByMemberGUID reloads one guild from backing storage by member guid. + RefreshGuildByMemberGUID(ctx context.Context, realmID uint32, memberGUID uint64) (*repo.Guild, error) } diff --git a/apps/guildserver/service/guilds-cache_inmem.go b/apps/guildserver/service/guilds-cache_inmem.go index da5ea6d..27f8425 100644 --- a/apps/guildserver/service/guilds-cache_inmem.go +++ b/apps/guildserver/service/guilds-cache_inmem.go @@ -23,14 +23,17 @@ type guildsInMemCache struct { // guildMembersCache usage example: // member := guildMembersCache[realmID][characterID] guildMembersCache map[uint32]map[uint64]*repo.GuildMember + + lifecycleEventTimes map[uint32]map[uint64]uint64 } // NewGuildsInMemCache returns in memory guilds cache. func NewGuildsInMemCache(r repo.GuildsRepo) GuildsCache { return &guildsInMemCache{ - r: r, - cache: map[uint32]map[uint64]*repo.Guild{}, - guildMembersCache: map[uint32]map[uint64]*repo.GuildMember{}, + r: r, + cache: map[uint32]map[uint64]*repo.Guild{}, + guildMembersCache: map[uint32]map[uint64]*repo.GuildMember{}, + lifecycleEventTimes: map[uint32]map[uint64]uint64{}, } } @@ -75,6 +78,10 @@ func (g *guildsInMemCache) GuildIDByRealmAndMemberGUID(_ context.Context, realmI return member.GuildID, nil } +func (g *guildsInMemCache) IgnoredByGuildMembers(ctx context.Context, realmID uint32, senderGUID uint64, receiverGUIDs []uint64) (map[uint64]bool, error) { + return g.r.IgnoredByGuildMembers(ctx, realmID, senderGUID, receiverGUIDs) +} + // AddGuildMember adds guild member to the guild. func (g *guildsInMemCache) AddGuildMember(ctx context.Context, realmID uint32, member repo.GuildMember) error { if err := g.r.AddGuildMember(ctx, realmID, member); err != nil { @@ -82,8 +89,16 @@ func (g *guildsInMemCache) AddGuildMember(ctx context.Context, realmID uint32, m } g.cacheMutex.Lock() + if g.guildMembersCache[realmID] == nil { + g.guildMembersCache[realmID] = map[uint64]*repo.GuildMember{} + } + if g.cache[realmID] == nil { + g.cache[realmID] = map[uint64]*repo.Guild{} + } g.guildMembersCache[realmID][member.PlayerGUID] = &member - g.cache[realmID][member.GuildID].GuildMembers = append(g.cache[realmID][member.GuildID].GuildMembers, &member) + if g.cache[realmID][member.GuildID] != nil { + g.cache[realmID][member.GuildID].GuildMembers = append(g.cache[realmID][member.GuildID].GuildMembers, &member) + } g.cacheMutex.Unlock() return nil @@ -220,9 +235,9 @@ func (g *guildsInMemCache) SetGuildInfo(ctx context.Context, realmID uint32, gui // UpdateGuildRank updates guild rank. func (g *guildsInMemCache) UpdateGuildRank( ctx context.Context, realmID uint32, guildID uint64, - rank uint8, name string, rights, moneyPerDay uint32, + rank uint8, name string, rights, moneyPerDay uint32, bankTabRights [repo.GuildBankMaxTabs]repo.GuildBankTabRight, ) error { - err := g.r.UpdateGuildRank(ctx, realmID, guildID, rank, name, rights, moneyPerDay) + err := g.r.UpdateGuildRank(ctx, realmID, guildID, rank, name, rights, moneyPerDay, bankTabRights) if err != nil { return err } @@ -238,11 +253,12 @@ func (g *guildsInMemCache) UpdateGuildRank( for i, guildRank := range guild.GuildRanks { if guildRank.Rank == rank { guild.GuildRanks[i] = repo.GuildRank{ - GuildID: guildID, - Rank: rank, - Name: name, - Rights: rights, - MoneyPerDay: moneyPerDay, + GuildID: guildID, + Rank: rank, + Name: name, + Rights: rights, + MoneyPerDay: moneyPerDay, + BankTabRights: bankTabRights, } break } @@ -301,6 +317,64 @@ func (g *guildsInMemCache) DeleteLowestGuildRank(ctx context.Context, realmID ui return nil } +// GuildPetitionByGUID loads a native petition by item GUID. Uncached. +func (g *guildsInMemCache) GuildPetitionByGUID(ctx context.Context, realmID uint32, petitionGUID uint64) (*repo.GuildPetition, error) { + return g.r.GuildPetitionByGUID(ctx, realmID, petitionGUID) +} + +// AddGuildPetitionSignature persists a native guild petition signature. Uncached. +func (g *guildsInMemCache) AddGuildPetitionSignature(ctx context.Context, realmID uint32, petitionID uint32, petitionGUID, ownerGUID, playerGUID uint64, playerAccount uint32) error { + return g.r.AddGuildPetitionSignature(ctx, realmID, petitionID, petitionGUID, ownerGUID, playerGUID, playerAccount) +} + +func (g *guildsInMemCache) GuildBank(ctx context.Context, realmID uint32, guildID uint64, tabID uint8, fullUpdate bool) (*repo.GuildBank, error) { + return g.r.GuildBank(ctx, realmID, guildID, tabID, fullUpdate) +} + +func (g *guildsInMemCache) GuildBankLog(ctx context.Context, realmID uint32, guildID uint64, tabID uint8) ([]repo.GuildBankLogEntry, error) { + return g.r.GuildBankLog(ctx, realmID, guildID, tabID) +} + +func (g *guildsInMemCache) SetGuildBankTabInfo(ctx context.Context, realmID uint32, guildID uint64, tabID uint8, name, icon string) error { + return g.r.SetGuildBankTabInfo(ctx, realmID, guildID, tabID, name, icon) +} + +func (g *guildsInMemCache) SetGuildBankTabText(ctx context.Context, realmID uint32, guildID uint64, tabID uint8, text string) error { + return g.r.SetGuildBankTabText(ctx, realmID, guildID, tabID, text) +} + +func (g *guildsInMemCache) BuyGuildBankTab(ctx context.Context, realmID uint32, guildID uint64, tabID uint8) error { + return g.r.BuyGuildBankTab(ctx, realmID, guildID, tabID) +} + +func (g *guildsInMemCache) DepositGuildBankMoney(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, amount uint32) error { + return g.r.DepositGuildBankMoney(ctx, realmID, guildID, memberGUID, amount) +} + +func (g *guildsInMemCache) WithdrawGuildBankMoney(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, amount uint32, repair bool) (uint32, error) { + return g.r.WithdrawGuildBankMoney(ctx, realmID, guildID, memberGUID, amount, repair) +} + +func (g *guildsInMemCache) RollbackGuildBankMoneyWithdraw(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, amount uint32, repair bool, logGUID uint32) error { + return g.r.RollbackGuildBankMoneyWithdraw(ctx, realmID, guildID, memberGUID, amount, repair, logGUID) +} + +func (g *guildsInMemCache) DepositGuildBankItem(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, tabID, slotID uint8, item repo.GuildBankItem) error { + return g.r.DepositGuildBankItem(ctx, realmID, guildID, memberGUID, tabID, slotID, item) +} + +func (g *guildsInMemCache) WithdrawGuildBankItem(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, tabID, slotID uint8, count uint32, splitItemGUID uint64) (*repo.GuildBankItem, uint32, error) { + return g.r.WithdrawGuildBankItem(ctx, realmID, guildID, memberGUID, tabID, slotID, count, splitItemGUID) +} + +func (g *guildsInMemCache) RollbackGuildBankItemWithdraw(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, tabID, slotID uint8, item repo.GuildBankItem, logGUID uint32) ([]uint8, error) { + return g.r.RollbackGuildBankItemWithdraw(ctx, realmID, guildID, memberGUID, tabID, slotID, item, logGUID) +} + +func (g *guildsInMemCache) MoveGuildBankItem(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, sourceTabID, sourceSlotID, destinationTabID, destinationSlotID uint8, count uint32, splitItemGUID uint64) ([]uint8, error) { + return g.r.MoveGuildBankItem(ctx, realmID, guildID, memberGUID, sourceTabID, sourceSlotID, destinationTabID, destinationSlotID, count, splitItemGUID) +} + // Warmup called on startup to warmup cache if possible. func (g *guildsInMemCache) Warmup(ctx context.Context, realmID uint32) error { g.cacheMutex.Lock() @@ -323,13 +397,88 @@ func (g *guildsInMemCache) Warmup(ctx context.Context, realmID uint32) error { return nil } +// RefreshGuildByMemberGUID reloads one guild from backing storage by member guid. +func (g *guildsInMemCache) RefreshGuildByMemberGUID(ctx context.Context, realmID uint32, memberGUID uint64) (*repo.Guild, error) { + guildID, err := g.r.GuildIDByRealmAndMemberGUID(ctx, realmID, memberGUID) + if err != nil { + return nil, err + } + if guildID == 0 { + return nil, nil + } + + guild, err := g.r.GuildByRealmAndID(ctx, realmID, guildID) + if err != nil { + return nil, err + } + if guild == nil { + return nil, nil + } + + g.cacheMutex.Lock() + defer g.cacheMutex.Unlock() + + if g.cache[realmID] == nil { + g.cache[realmID] = map[uint64]*repo.Guild{} + } + if g.guildMembersCache[realmID] == nil { + g.guildMembersCache[realmID] = map[uint64]*repo.GuildMember{} + } + + if old := g.cache[realmID][guild.ID]; old != nil { + for _, member := range old.GuildMembers { + delete(g.guildMembersCache[realmID], member.PlayerGUID) + } + } + + g.cache[realmID][guild.ID] = guild + for _, member := range guild.GuildMembers { + g.guildMembersCache[realmID][member.PlayerGUID] = member + } + + return guild, nil +} + +func (g *guildsInMemCache) refreshGuildByID(ctx context.Context, realmID uint32, guildID uint64) { + guild, err := g.r.GuildByRealmAndID(ctx, realmID, guildID) + if err != nil || guild == nil { + return + } + + g.cacheMutex.Lock() + defer g.cacheMutex.Unlock() + + if g.cache[realmID] == nil { + g.cache[realmID] = map[uint64]*repo.Guild{} + } + if g.guildMembersCache[realmID] == nil { + g.guildMembersCache[realmID] = map[uint64]*repo.GuildMember{} + } + + if old := g.cache[realmID][guildID]; old != nil { + for _, member := range old.GuildMembers { + delete(g.guildMembersCache[realmID], member.PlayerGUID) + } + } + + g.cache[realmID][guildID] = guild + for _, member := range guild.GuildMembers { + g.guildMembersCache[realmID][member.PlayerGUID] = member + } +} + // HandleCharacterLoggedIn updates cache with player logged in. func (g *guildsInMemCache) HandleCharacterLoggedIn(payload events.GWEventCharacterLoggedInPayload) error { g.cacheMutex.Lock() + if !g.shouldApplyLifecycleEventLocked(payload.RealmID, payload.CharGUID, payload.EventTimeUnixNano) { + g.cacheMutex.Unlock() + return nil + } member := g.guildMembersCache[payload.RealmID][payload.CharGUID] if member != nil { member.Status = repo.GuildMemberStatusOnline } + g.rememberLifecycleEventTimeLocked(payload.RealmID, payload.CharGUID, payload.EventTimeUnixNano) g.cacheMutex.Unlock() return nil } @@ -337,11 +486,16 @@ func (g *guildsInMemCache) HandleCharacterLoggedIn(payload events.GWEventCharact // HandleCharacterLoggedOut updates cache with player logged out. func (g *guildsInMemCache) HandleCharacterLoggedOut(payload events.GWEventCharacterLoggedOutPayload) error { g.cacheMutex.Lock() + if !g.shouldApplyLifecycleEventLocked(payload.RealmID, payload.CharGUID, payload.EventTimeUnixNano) { + g.cacheMutex.Unlock() + return nil + } member := g.guildMembersCache[payload.RealmID][payload.CharGUID] if member != nil { member.Status = repo.GuildMemberStatusOffline member.LogoutTime = time.Now().Unix() } + g.rememberLifecycleEventTimeLocked(payload.RealmID, payload.CharGUID, payload.EventTimeUnixNano) g.cacheMutex.Unlock() return nil } @@ -352,6 +506,13 @@ func (g *guildsInMemCache) HandleCharactersUpdates(payload events.GWEventCharact for _, update := range payload.Updates { member := g.guildMembersCache[payload.RealmID][update.ID] if member != nil { + eventTimeUnixNano := update.EventTimeUnixNano + if eventTimeUnixNano == 0 { + eventTimeUnixNano = payload.EventTimeUnixNano + } + if eventTimeUnixNano != 0 && g.lifecycleEventTimeLocked(payload.RealmID, update.ID) > eventTimeUnixNano { + continue + } applyCharUpdate(member, update) } } @@ -359,6 +520,31 @@ func (g *guildsInMemCache) HandleCharactersUpdates(payload events.GWEventCharact return nil } +func (g *guildsInMemCache) shouldApplyLifecycleEventLocked(realmID uint32, charGUID uint64, eventTimeUnixNano uint64) bool { + return eventTimeUnixNano == 0 || g.lifecycleEventTimeLocked(realmID, charGUID) <= eventTimeUnixNano +} + +func (g *guildsInMemCache) lifecycleEventTimeLocked(realmID uint32, charGUID uint64) uint64 { + realmEvents := g.lifecycleEventTimes[realmID] + if realmEvents == nil { + return 0 + } + return realmEvents[charGUID] +} + +func (g *guildsInMemCache) rememberLifecycleEventTimeLocked(realmID uint32, charGUID uint64, eventTimeUnixNano uint64) { + if eventTimeUnixNano == 0 { + return + } + if g.lifecycleEventTimes == nil { + g.lifecycleEventTimes = map[uint32]map[uint64]uint64{} + } + if g.lifecycleEventTimes[realmID] == nil { + g.lifecycleEventTimes[realmID] = map[uint64]uint64{} + } + g.lifecycleEventTimes[realmID][charGUID] = eventTimeUnixNano +} + func applyCharUpdate(member *repo.GuildMember, upd *events.CharacterUpdate) { if upd.Area != nil { member.AreaID = *upd.Area diff --git a/apps/guildserver/service/guilds.go b/apps/guildserver/service/guilds.go index 49299d0..b8916c3 100644 --- a/apps/guildserver/service/guilds.go +++ b/apps/guildserver/service/guilds.go @@ -8,12 +8,58 @@ import ( "github.com/walkline/ToCloud9/apps/guildserver" "github.com/walkline/ToCloud9/apps/guildserver/repo" "github.com/walkline/ToCloud9/shared/events" + "github.com/walkline/ToCloud9/shared/wow" ) var ( ErrNotEnoughRight = errors.New("not enough rights") ErrGuildNotFound = errors.New("guild not found") ErrLeaderCantLeave = errors.New("leader can't leave") + ErrGuildNotAllied = errors.New("guild target is not allied") +) + +const guildPetitionType = 9 + +type GuildPetitionOfferStatus uint8 + +const ( + GuildPetitionOfferOK GuildPetitionOfferStatus = iota + GuildPetitionOfferNotFound + GuildPetitionOfferNotOwner + GuildPetitionOfferTargetNotFound + GuildPetitionOfferTargetAlreadyInGuild + GuildPetitionOfferTargetAlreadyInvited + GuildPetitionOfferFailed +) + +type GuildPetitionSignStatus uint8 + +const ( + GuildPetitionSignOK GuildPetitionSignStatus = iota + GuildPetitionSignAlreadySigned + GuildPetitionSignAlreadyInGuild + GuildPetitionSignCantSignOwn + GuildPetitionSignNotServer + GuildPetitionSignNotFound + GuildPetitionSignFull + GuildPetitionSignFailed + GuildPetitionSignAlreadyInvited +) + +type GuildBankStatus uint8 + +const ( + GuildBankStatusOK GuildBankStatus = iota + GuildBankStatusFailed + GuildBankStatusGuildNotFound + GuildBankStatusNotInGuild + GuildBankStatusNotEnoughRights + GuildBankStatusInvalidTab + GuildBankStatusInvalidSlot + GuildBankStatusNotEnoughMoney + GuildBankStatusBankFull + GuildBankStatusWithdrawLimit + GuildBankStatusItemNotFound ) // InviteAcceptedParams represents parameters for InviteAcceptedParams.InviteAccepted function. @@ -26,14 +72,17 @@ type InviteAcceptedParams struct { CharGender uint8 CharAreaID uint32 CharAccount uint64 + + AllowCrossFaction bool } // GuildRank represents guild rank. type GuildRank struct { - RankID uint8 - Name string - Rights uint32 - MoneyPerDay uint32 + RankID uint8 + Name string + Rights uint32 + MoneyPerDay uint32 + BankTabRights [repo.GuildBankMaxTabs]repo.GuildBankTabRight } // GuildService is service to handle guilds. @@ -41,8 +90,22 @@ type GuildService interface { // GuildByRealmAndID returns guild by realmID and guildID. GuildByRealmAndID(ctx context.Context, realmID uint32, guildID uint64) (*repo.Guild, error) + GetGuildBank(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID uint8, fullUpdate bool) (*repo.GuildBank, int32, GuildBankStatus, error) + GetGuildBankLog(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID uint8) ([]repo.GuildBankLogEntry, GuildBankStatus, error) + GetGuildBankTabText(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID uint8) (string, GuildBankStatus, error) + UpdateGuildBankTab(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID uint8, name, icon string) (GuildBankStatus, error) + SetGuildBankTabText(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID uint8, text string) (GuildBankStatus, error) + BuyGuildBankTab(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID uint8, cost uint32) (GuildBankStatus, error) + DepositGuildBankMoney(ctx context.Context, realmID uint32, guildID, memberGUID uint64, amount uint32) (GuildBankStatus, error) + WithdrawGuildBankMoney(ctx context.Context, realmID uint32, guildID, memberGUID uint64, amount uint32, repair bool) (uint32, GuildBankStatus, error) + RollbackGuildBankMoneyWithdraw(ctx context.Context, realmID uint32, guildID, memberGUID uint64, amount uint32, repair bool, logGUID uint32) (GuildBankStatus, error) + DepositGuildBankItem(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID, slotID uint8, item repo.GuildBankItem) (GuildBankStatus, error) + WithdrawGuildBankItem(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID, slotID uint8, count uint32) (*repo.GuildBankItem, uint32, GuildBankStatus, error) + RollbackGuildBankItemWithdraw(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID, slotID uint8, item repo.GuildBankItem, logGUID uint32) ([]uint8, GuildBankStatus, error) + MoveGuildBankItem(ctx context.Context, realmID uint32, guildID, memberGUID uint64, sourceTabID, sourceSlotID, destinationTabID, destinationSlotID uint8, count uint32) ([]uint8, GuildBankStatus, error) + // InviteMember creates invite to the guild. - InviteMember(ctx context.Context, realmID uint32, inviterGUID uint64, inviteeGUID uint64, inviteeName string) error + InviteMember(ctx context.Context, realmID uint32, inviterGUID uint64, inviteeGUID uint64, inviteeName string, inviteeRace uint8, allowCrossFaction bool) error // InviteAccepted handles guild invite accept users action. Returns guild id of the new member. InviteAccepted(ctx context.Context, realmID uint32, params InviteAcceptedParams) (uint64, error) @@ -81,21 +144,53 @@ type GuildService interface { DemoteMember(ctx context.Context, realmID uint32, updaterGUID, targetGUID uint64) error // SendGuildMessage sends guild message to the online player. - SendGuildMessage(ctx context.Context, realmID uint32, senderGUID uint64, message string, lang uint32, isOfficers bool) error + SendGuildMessage(ctx context.Context, realmID uint32, senderGUID uint64, message string, lang uint32, isOfficers bool, senderChatTag uint8) error + + // GuildPetitionByGUID returns a native AzerothCore guild petition by item GUID. + GuildPetitionByGUID(ctx context.Context, realmID uint32, petitionGUID uint64) (*repo.GuildPetition, error) + + // OfferGuildPetition routes a native guild petition offer to an online same-realm target. + OfferGuildPetition(ctx context.Context, realmID uint32, ownerGUID, targetGUID uint64, targetName string, petitionGUID uint64) (GuildPetitionOfferStatus, error) + + // SignGuildPetition validates and persists a native guild petition signature. + SignGuildPetition(ctx context.Context, realmID uint32, signerGUID uint64, signerName string, signerAccountID uint32, signerGuildID uint32, petitionGUID uint64) (GuildPetitionSignStatus, error) + + events.GWGuildCreatedHandler } // guildServiceImpl is implementation of GuildService. type guildServiceImpl struct { - guildsRepo repo.GuildsRepo - eventsProducer events.GuildServiceProducer + guildsRepo repo.GuildsRepo + guildBankRepo repo.GuildsRepo + itemGUIDAllocator ItemGUIDAllocator + eventsProducer events.GuildServiceProducer } // NewGuildService creates GuildService. func NewGuildService(guildsRepo repo.GuildsRepo, eventsProducer events.GuildServiceProducer) GuildService { + return NewGuildServiceWithBankRepo(guildsRepo, guildsRepo, eventsProducer) +} + +// NewGuildServiceWithBankRepo creates GuildService with a separate uncached repo for guild bank paths. +func NewGuildServiceWithBankRepo(guildsRepo repo.GuildsRepo, guildBankRepo repo.GuildsRepo, eventsProducer events.GuildServiceProducer) GuildService { + return NewGuildServiceWithBankRepoAndItemGUIDAllocator(guildsRepo, guildBankRepo, nil, eventsProducer) +} + +// NewGuildServiceWithBankRepoAndItemGUIDAllocator creates GuildService with a separate uncached repo and item GUID allocator for guild bank paths. +func NewGuildServiceWithBankRepoAndItemGUIDAllocator(guildsRepo repo.GuildsRepo, guildBankRepo repo.GuildsRepo, itemGUIDAllocator ItemGUIDAllocator, eventsProducer events.GuildServiceProducer) GuildService { return &guildServiceImpl{ - guildsRepo: guildsRepo, - eventsProducer: eventsProducer, + guildsRepo: guildsRepo, + guildBankRepo: guildBankRepo, + itemGUIDAllocator: itemGUIDAllocator, + eventsProducer: eventsProducer, + } +} + +func (g *guildServiceImpl) bankRepo() repo.GuildsRepo { + if g.guildBankRepo != nil { + return g.guildBankRepo } + return g.guildsRepo } // GuildByRealmAndID returns guild by realmID and guildID. @@ -103,8 +198,221 @@ func (g *guildServiceImpl) GuildByRealmAndID(ctx context.Context, realmID uint32 return g.guildsRepo.GuildByRealmAndID(ctx, realmID, guildID) } +func (g *guildServiceImpl) GetGuildBank(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID uint8, fullUpdate bool) (*repo.GuildBank, int32, GuildBankStatus, error) { + guild, member, rank, status, err := g.guildBankMemberRank(ctx, realmID, guildID, memberGUID) + if status != GuildBankStatusOK || err != nil { + return nil, 0, status, err + } + if status = g.guildBankCanViewTab(guild, rank, tabID); status != GuildBankStatusOK { + return nil, 0, status, nil + } + + bank, err := g.bankRepo().GuildBank(ctx, realmID, guildID, tabID, fullUpdate) + if err != nil { + return nil, 0, GuildBankStatusFailed, err + } + return bank, g.guildBankRemainingSlots(rank, member, tabID), GuildBankStatusOK, nil +} + +func (g *guildServiceImpl) GetGuildBankLog(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID uint8) ([]repo.GuildBankLogEntry, GuildBankStatus, error) { + guild, _, rank, status, err := g.guildBankMemberRank(ctx, realmID, guildID, memberGUID) + if status != GuildBankStatusOK || err != nil { + return nil, status, err + } + if status = g.guildBankCanViewTab(guild, rank, tabID); status != GuildBankStatusOK { + return nil, status, nil + } + + entries, err := g.bankRepo().GuildBankLog(ctx, realmID, guildID, tabID) + if err != nil { + return nil, GuildBankStatusFailed, err + } + return entries, GuildBankStatusOK, nil +} + +func (g *guildServiceImpl) GetGuildBankTabText(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID uint8) (string, GuildBankStatus, error) { + bank, _, status, err := g.GetGuildBank(ctx, realmID, guildID, memberGUID, tabID, false) + if status != GuildBankStatusOK || err != nil { + return "", status, err + } + for _, tab := range bank.Tabs { + if tab.TabID == tabID { + return tab.Text, GuildBankStatusOK, nil + } + } + return "", GuildBankStatusInvalidTab, nil +} + +func (g *guildServiceImpl) UpdateGuildBankTab(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID uint8, name, icon string) (GuildBankStatus, error) { + guild, _, rank, status, err := g.guildBankMemberRank(ctx, realmID, guildID, memberGUID) + if status != GuildBankStatusOK || err != nil { + return status, err + } + if status = g.guildBankCanUpdateText(guild, rank, tabID); status != GuildBankStatusOK { + return status, nil + } + if err = g.bankRepo().SetGuildBankTabInfo(ctx, realmID, guildID, tabID, name, icon); err != nil { + return repoGuildBankErrorToStatus(err), err + } + return GuildBankStatusOK, nil +} + +func (g *guildServiceImpl) SetGuildBankTabText(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID uint8, text string) (GuildBankStatus, error) { + guild, _, rank, status, err := g.guildBankMemberRank(ctx, realmID, guildID, memberGUID) + if status != GuildBankStatusOK || err != nil { + return status, err + } + if status = g.guildBankCanUpdateText(guild, rank, tabID); status != GuildBankStatusOK { + return status, nil + } + if err = g.bankRepo().SetGuildBankTabText(ctx, realmID, guildID, tabID, text); err != nil { + return repoGuildBankErrorToStatus(err), err + } + return GuildBankStatusOK, nil +} + +func (g *guildServiceImpl) BuyGuildBankTab(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID uint8, cost uint32) (GuildBankStatus, error) { + guild, _, rank, status, err := g.guildBankMemberRank(ctx, realmID, guildID, memberGUID) + if status != GuildBankStatusOK || err != nil { + return status, err + } + if rank.Rank != uint8(repo.GuildRankGuildMaster) { + return GuildBankStatusNotEnoughRights, nil + } + if tabID != guild.PurchasedBankTabs || tabID >= repo.GuildBankMaxTabs { + return GuildBankStatusInvalidTab, nil + } + + if err = g.bankRepo().BuyGuildBankTab(ctx, realmID, guildID, tabID); err != nil { + return repoGuildBankErrorToStatus(err), err + } + return GuildBankStatusOK, nil +} + +func (g *guildServiceImpl) DepositGuildBankMoney(ctx context.Context, realmID uint32, guildID, memberGUID uint64, amount uint32) (GuildBankStatus, error) { + if _, _, _, status, err := g.guildBankMemberRank(ctx, realmID, guildID, memberGUID); status != GuildBankStatusOK || err != nil { + return status, err + } + if err := g.bankRepo().DepositGuildBankMoney(ctx, realmID, guildID, memberGUID, amount); err != nil { + return repoGuildBankErrorToStatus(err), err + } + return GuildBankStatusOK, nil +} + +func (g *guildServiceImpl) WithdrawGuildBankMoney(ctx context.Context, realmID uint32, guildID, memberGUID uint64, amount uint32, repair bool) (uint32, GuildBankStatus, error) { + _, member, rank, status, err := g.guildBankMemberRank(ctx, realmID, guildID, memberGUID) + if status != GuildBankStatusOK || err != nil { + return 0, status, err + } + if repair { + if !rank.HasRight(repo.RightWithdrawRepair) { + return 0, GuildBankStatusNotEnoughRights, nil + } + } else if !rank.HasRight(repo.RightWithdrawGold) { + return 0, GuildBankStatusNotEnoughRights, nil + } + if g.guildBankRemainingMoney(rank, member) >= 0 && uint32(g.guildBankRemainingMoney(rank, member)) < amount { + return 0, GuildBankStatusWithdrawLimit, nil + } + logGUID, err := g.bankRepo().WithdrawGuildBankMoney(ctx, realmID, guildID, memberGUID, amount, repair) + if err != nil { + return 0, repoGuildBankErrorToStatus(err), err + } + return logGUID, GuildBankStatusOK, nil +} + +func (g *guildServiceImpl) RollbackGuildBankMoneyWithdraw(ctx context.Context, realmID uint32, guildID, memberGUID uint64, amount uint32, repair bool, logGUID uint32) (GuildBankStatus, error) { + if _, _, _, status, err := g.guildBankMemberRank(ctx, realmID, guildID, memberGUID); status != GuildBankStatusOK || err != nil { + return status, err + } + if err := g.bankRepo().RollbackGuildBankMoneyWithdraw(ctx, realmID, guildID, memberGUID, amount, repair, logGUID); err != nil { + return repoGuildBankErrorToStatus(err), err + } + return GuildBankStatusOK, nil +} + +func (g *guildServiceImpl) DepositGuildBankItem(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID, slotID uint8, item repo.GuildBankItem) (GuildBankStatus, error) { + guild, _, rank, status, err := g.guildBankMemberRank(ctx, realmID, guildID, memberGUID) + if status != GuildBankStatusOK || err != nil { + return status, err + } + if status = g.guildBankCanDeposit(guild, rank, tabID); status != GuildBankStatusOK { + return status, nil + } + if err = g.bankRepo().DepositGuildBankItem(ctx, realmID, guildID, memberGUID, tabID, slotID, item); err != nil { + return repoGuildBankErrorToStatus(err), err + } + return GuildBankStatusOK, nil +} + +func (g *guildServiceImpl) WithdrawGuildBankItem(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID, slotID uint8, count uint32) (*repo.GuildBankItem, uint32, GuildBankStatus, error) { + guild, member, rank, status, err := g.guildBankMemberRank(ctx, realmID, guildID, memberGUID) + if status != GuildBankStatusOK || err != nil { + return nil, 0, status, err + } + if status = g.guildBankCanWithdraw(guild, member, rank, tabID); status != GuildBankStatusOK { + return nil, 0, status, nil + } + splitItemGUID, err := g.nextGuildBankSplitItemGUID(ctx, realmID, count) + if err != nil { + return nil, 0, GuildBankStatusFailed, err + } + item, logGUID, err := g.bankRepo().WithdrawGuildBankItem(ctx, realmID, guildID, memberGUID, tabID, slotID, count, splitItemGUID) + if err != nil { + return nil, 0, repoGuildBankErrorToStatus(err), err + } + return item, logGUID, GuildBankStatusOK, nil +} + +func (g *guildServiceImpl) RollbackGuildBankItemWithdraw(ctx context.Context, realmID uint32, guildID, memberGUID uint64, tabID, slotID uint8, item repo.GuildBankItem, logGUID uint32) ([]uint8, GuildBankStatus, error) { + if _, _, _, status, err := g.guildBankMemberRank(ctx, realmID, guildID, memberGUID); status != GuildBankStatusOK || err != nil { + return nil, status, err + } + changed, err := g.bankRepo().RollbackGuildBankItemWithdraw(ctx, realmID, guildID, memberGUID, tabID, slotID, item, logGUID) + if err != nil { + return nil, repoGuildBankErrorToStatus(err), err + } + return changed, GuildBankStatusOK, nil +} + +func (g *guildServiceImpl) MoveGuildBankItem(ctx context.Context, realmID uint32, guildID, memberGUID uint64, sourceTabID, sourceSlotID, destinationTabID, destinationSlotID uint8, count uint32) ([]uint8, GuildBankStatus, error) { + guild, member, rank, status, err := g.guildBankMemberRank(ctx, realmID, guildID, memberGUID) + if status != GuildBankStatusOK || err != nil { + return nil, status, err + } + if sourceTabID != destinationTabID { + if status = g.guildBankCanWithdraw(guild, member, rank, sourceTabID); status != GuildBankStatusOK { + return nil, status, nil + } + if status = g.guildBankCanDeposit(guild, rank, destinationTabID); status != GuildBankStatusOK { + return nil, status, nil + } + } else if status = g.guildBankCanViewTab(guild, rank, sourceTabID); status != GuildBankStatusOK { + return nil, status, nil + } + splitItemGUID, err := g.nextGuildBankSplitItemGUID(ctx, realmID, count) + if err != nil { + return nil, GuildBankStatusFailed, err + } + changed, err := g.bankRepo().MoveGuildBankItem(ctx, realmID, guildID, memberGUID, sourceTabID, sourceSlotID, destinationTabID, destinationSlotID, count, splitItemGUID) + if err != nil { + return nil, repoGuildBankErrorToStatus(err), err + } + return changed, GuildBankStatusOK, nil +} + +func (g *guildServiceImpl) nextGuildBankSplitItemGUID(ctx context.Context, realmID uint32, count uint32) (uint64, error) { + if count == 0 { + return 0, nil + } + if g.itemGUIDAllocator == nil { + return 0, errGuildBankItemGUIDAllocatorMissing + } + return g.itemGUIDAllocator.NextItemGUID(ctx, realmID) +} + // InviteMember creates invite to the guild. -func (g *guildServiceImpl) InviteMember(ctx context.Context, realmID uint32, inviterGUID uint64, inviteeGUID uint64, inviteeName string) error { +func (g *guildServiceImpl) InviteMember(ctx context.Context, realmID uint32, inviterGUID uint64, inviteeGUID uint64, inviteeName string, inviteeRace uint8, allowCrossFaction bool) error { guildID, err := g.guildsRepo.GuildIDByRealmAndMemberGUID(ctx, realmID, inviterGUID) if err != nil { return fmt.Errorf("can't fetch guild id for member, err: %w", err) @@ -132,6 +440,15 @@ func (g *guildServiceImpl) InviteMember(ctx context.Context, realmID uint32, inv return ErrGuildNotFound } + member := g.guildMemberForMemberGuid(guild, inviterGUID) + if member == nil { + return fmt.Errorf("can't find guild member %d in guild %d", inviterGUID, guildID) + } + + if !allowCrossFaction && !guildSameFaction(member.Race, inviteeRace) { + return ErrGuildNotAllied + } + rank := g.rankForMember(guild, inviterGUID) if rank == nil { return fmt.Errorf("can't find rank for player %d and guild %d", inviterGUID, guildID) @@ -146,8 +463,6 @@ func (g *guildServiceImpl) InviteMember(ctx context.Context, realmID uint32, inv return fmt.Errorf("can't invite with bind palyer to the guild, err: %w", err) } - member := g.guildMemberForMemberGuid(guild, inviterGUID) - return g.eventsProducer.InviteCreated(&events.GuildEventInviteCreatedPayload{ ServiceID: guildserver.ServiceID, RealmID: realmID, @@ -180,6 +495,10 @@ func (g *guildServiceImpl) InviteAccepted(ctx context.Context, realmID uint32, p return 0, ErrGuildNotFound } + if !params.AllowCrossFaction && !guildLeaderSameFaction(guild, params.CharRace) { + return 0, ErrGuildNotAllied + } + err = g.guildsRepo.RemoveGuildInviteForCharacter(ctx, realmID, params.CharGUID) if err != nil { return 0, err @@ -463,7 +782,7 @@ func (g *guildServiceImpl) SetMemberOfficerNote(ctx context.Context, realmID uin return err } - rank := g.rankForMember(guild, updaterGuildID) + rank := g.rankForMember(guild, updaterGUID) if !rank.HasRight(repo.RightEditOfficersNote) { return ErrNotEnoughRight } @@ -503,11 +822,14 @@ func (g *guildServiceImpl) UpdateGuildRank(ctx context.Context, realmID uint32, } memberRank := g.rankForMember(guild, updaterGUID) + if memberRank == nil { + return ErrGuildNotFound + } if memberRank.Rank != uint8(repo.GuildRankGuildMaster) { return ErrNotEnoughRight } - err = g.guildsRepo.UpdateGuildRank(ctx, realmID, guild.ID, rank.RankID, rank.Name, rank.Rights, rank.MoneyPerDay) + err = g.guildsRepo.UpdateGuildRank(ctx, realmID, guild.ID, rank.RankID, rank.Name, rank.Rights, rank.MoneyPerDay, rank.BankTabRights) if err != nil { return err } @@ -539,6 +861,9 @@ func (g *guildServiceImpl) AddGuildRank(ctx context.Context, realmID uint32, upd } memberRank := g.rankForMember(guild, updaterGUID) + if memberRank == nil { + return ErrGuildNotFound + } if memberRank.Rank != uint8(repo.GuildRankGuildMaster) { return ErrNotEnoughRight } @@ -548,7 +873,8 @@ func (g *guildServiceImpl) AddGuildRank(ctx context.Context, realmID uint32, upd } rankID := g.lowestRankInGuild(guild) + 1 - err = g.guildsRepo.AddGuildRank(ctx, realmID, guild.ID, rankID, rankName, repo.RightEmpty, 0) + ranksCount := uint8(len(guild.GuildRanks) + 1) + err = g.guildsRepo.AddGuildRank(ctx, realmID, guild.ID, rankID, rankName, repo.RightChatListen|repo.RightChatSpeak, 0) if err != nil { return err } @@ -557,7 +883,7 @@ func (g *guildServiceImpl) AddGuildRank(ctx context.Context, realmID uint32, upd GenericGuildEvent: *g.buildGenericEventPayload(guild), RankID: rankID, RankName: rankName, - RanksCount: uint8(len(guild.GuildRanks)), + RanksCount: ranksCount, }) if err != nil { return err @@ -578,18 +904,29 @@ func (g *guildServiceImpl) DeleteLastGuildRank(ctx context.Context, realmID uint } memberRank := g.rankForMember(guild, updaterGUID) + if memberRank == nil { + return ErrGuildNotFound + } if memberRank.Rank != uint8(repo.GuildRankGuildMaster) { return ErrNotEnoughRight } + if len(guild.GuildRanks) <= repo.GuildRankMinCount { + return nil + } lowestRank := g.lowestRankInGuild(guild) - var rankToDelete repo.GuildRank + var rankToDelete *repo.GuildRank for _, rank := range guild.GuildRanks { if rank.Rank == lowestRank { - rankToDelete = rank + rankCopy := rank + rankToDelete = &rankCopy break } } + if rankToDelete == nil { + return ErrGuildNotFound + } + ranksCount := uint8(len(guild.GuildRanks) - 1) err = g.guildsRepo.DeleteLowestGuildRank(ctx, realmID, guild.ID, lowestRank) if err != nil { @@ -598,9 +935,9 @@ func (g *guildServiceImpl) DeleteLastGuildRank(ctx context.Context, realmID uint err = g.eventsProducer.RankDeleted(&events.GuildEventRankDeletedPayload{ GenericGuildEvent: *g.buildGenericEventPayload(guild), - RankID: memberRank.Rank, + RankID: lowestRank, RankName: rankToDelete.Name, - RanksCount: uint8(len(guild.GuildRanks)), + RanksCount: ranksCount, }) if err != nil { return err @@ -620,7 +957,7 @@ func (g *guildServiceImpl) DemoteMember(ctx context.Context, realmID uint32, upd } // SendGuildMessage sends guild message to the online player. -func (g *guildServiceImpl) SendGuildMessage(ctx context.Context, realmID uint32, senderGUID uint64, message string, lang uint32, isOfficers bool) error { +func (g *guildServiceImpl) SendGuildMessage(ctx context.Context, realmID uint32, senderGUID uint64, message string, lang uint32, isOfficers bool, senderChatTag uint8) error { guild, err := g.guildByMemberGUID(ctx, realmID, senderGUID) if err != nil { return err @@ -634,25 +971,201 @@ func (g *guildServiceImpl) SendGuildMessage(ctx context.Context, realmID uint32, if sender == nil { return ErrGuildNotFound } + senderRank := g.rankForMember(guild, senderGUID) + if senderRank == nil { + return ErrGuildNotFound + } + if isOfficers { + if !senderRank.HasRight(repo.RightOfficerChatSpeak) { + return ErrNotEnoughRight + } + } else if !senderRank.HasRight(repo.RightChatSpeak) { + return ErrNotEnoughRight + } var receivers []uint64 for _, member := range guild.GuildMembers { - if member.Status == repo.GuildMemberStatusOnline && member.PlayerGUID != senderGUID { + if member.Status != repo.GuildMemberStatusOnline || member.PlayerGUID == senderGUID { + continue + } + memberRank := g.rankWithID(guild, member.Rank) + if memberRank == nil { + continue + } + if isOfficers { + if memberRank.HasRight(repo.RightOfficerChatListen) { + receivers = append(receivers, member.PlayerGUID) + } + continue + } + if memberRank.HasRight(repo.RightChatListen) { receivers = append(receivers, member.PlayerGUID) } } + ignoredReceivers, err := g.guildsRepo.IgnoredByGuildMembers(ctx, realmID, senderGUID, receivers) + if err != nil { + return err + } + filteredReceivers := receivers[:0] + for _, receiverGUID := range receivers { + if !ignoredReceivers[receiverGUID] { + filteredReceivers = append(filteredReceivers, receiverGUID) + } + } + receivers = filteredReceivers return g.eventsProducer.NewMessage(&events.GuildEventNewMessagePayload{ - ServiceID: guildserver.ServiceID, - RealmID: realmID, - GuildID: guild.ID, - SenderGUID: senderGUID, - SenderName: sender.Name, - Language: lang, - Msg: message, - ForOfficers: isOfficers, - Receivers: receivers, + ServiceID: guildserver.ServiceID, + RealmID: realmID, + GuildID: guild.ID, + SenderGUID: senderGUID, + SenderName: sender.Name, + SenderChatTag: senderChatTag, + Language: lang, + Msg: message, + ForOfficers: isOfficers, + Receivers: receivers, + }) +} + +// GuildPetitionByGUID returns a native AzerothCore guild petition by item GUID. +func (g *guildServiceImpl) GuildPetitionByGUID(ctx context.Context, realmID uint32, petitionGUID uint64) (*repo.GuildPetition, error) { + petition, err := g.guildsRepo.GuildPetitionByGUID(ctx, realmID, petitionGUID) + if err != nil { + return nil, err + } + if petition == nil || petition.Type != guildPetitionType { + return nil, nil + } + + return petition, nil +} + +// OfferGuildPetition routes a native guild petition offer to an online same-realm target. +func (g *guildServiceImpl) OfferGuildPetition(ctx context.Context, realmID uint32, ownerGUID, targetGUID uint64, targetName string, petitionGUID uint64) (GuildPetitionOfferStatus, error) { + petition, err := g.GuildPetitionByGUID(ctx, realmID, petitionGUID) + if err != nil { + return GuildPetitionOfferFailed, err + } + if petition == nil { + return GuildPetitionOfferNotFound, nil + } + if petition.OwnerGUID != ownerGUID { + return GuildPetitionOfferNotOwner, nil + } + + targetGuildID, err := g.guildsRepo.GuildIDByRealmAndMemberGUID(ctx, realmID, targetGUID) + if err != nil { + return GuildPetitionOfferFailed, err + } + if targetGuildID != 0 { + return GuildPetitionOfferTargetAlreadyInGuild, nil + } + + targetInviteID, err := g.guildsRepo.GuildIDByCharInvite(ctx, realmID, targetGUID) + if err != nil { + return GuildPetitionOfferFailed, err + } + if targetInviteID != 0 { + return GuildPetitionOfferTargetAlreadyInvited, nil + } + + err = g.eventsProducer.PetitionOffered(&events.GuildEventPetitionOfferedPayload{ + ServiceID: guildserver.ServiceID, + RealmID: realmID, + PetitionGUID: petition.PetitionGUID, + PetitionID: petition.PetitionID, + OwnerGUID: petition.OwnerGUID, + TargetGUID: targetGUID, + TargetName: targetName, + GuildName: petition.Name, + RequiredSigns: uint32(petition.Type), + Signatures: petitionSignaturesToEvents(petition.Signatures), }) + if err != nil { + return GuildPetitionOfferFailed, err + } + + return GuildPetitionOfferOK, nil +} + +// SignGuildPetition validates and persists a native guild petition signature. +func (g *guildServiceImpl) SignGuildPetition(ctx context.Context, realmID uint32, signerGUID uint64, signerName string, signerAccountID uint32, signerGuildID uint32, petitionGUID uint64) (GuildPetitionSignStatus, error) { + petition, err := g.GuildPetitionByGUID(ctx, realmID, petitionGUID) + if err != nil { + return GuildPetitionSignFailed, err + } + if petition == nil { + return GuildPetitionSignNotFound, nil + } + if petition.OwnerGUID == signerGUID { + return GuildPetitionSignCantSignOwn, nil + } + + if signerGuildID != 0 { + return GuildPetitionSignAlreadyInGuild, nil + } + signerCurrentGuildID, err := g.guildsRepo.GuildIDByRealmAndMemberGUID(ctx, realmID, signerGUID) + if err != nil { + return GuildPetitionSignFailed, err + } + if signerCurrentGuildID != 0 { + return GuildPetitionSignAlreadyInGuild, nil + } + + signerInviteID, err := g.guildsRepo.GuildIDByCharInvite(ctx, realmID, signerGUID) + if err != nil { + return GuildPetitionSignFailed, err + } + if signerInviteID != 0 { + return GuildPetitionSignAlreadyInvited, nil + } + + for _, signature := range petition.Signatures { + if signature.PlayerGUID == signerGUID || signature.PlayerAccount == signerAccountID { + return g.publishGuildPetitionSigned(realmID, petition, signerGUID, signerName, GuildPetitionSignAlreadySigned) + } + } + + if len(petition.Signatures)+1 > int(petition.Type) { + return GuildPetitionSignFull, nil + } + + err = g.guildsRepo.AddGuildPetitionSignature(ctx, realmID, petition.PetitionID, petition.PetitionGUID, petition.OwnerGUID, signerGUID, signerAccountID) + if err != nil { + return GuildPetitionSignFailed, err + } + + return g.publishGuildPetitionSigned(realmID, petition, signerGUID, signerName, GuildPetitionSignOK) +} + +func (g *guildServiceImpl) publishGuildPetitionSigned(realmID uint32, petition *repo.GuildPetition, signerGUID uint64, signerName string, status GuildPetitionSignStatus) (GuildPetitionSignStatus, error) { + err := g.eventsProducer.PetitionSigned(&events.GuildEventPetitionSignedPayload{ + ServiceID: guildserver.ServiceID, + RealmID: realmID, + PetitionGUID: petition.PetitionGUID, + OwnerGUID: petition.OwnerGUID, + SignerGUID: signerGUID, + SignerName: signerName, + NativeStatus: uint32(status), + RequiredSigns: uint32(petition.Type), + }) + if err != nil { + return GuildPetitionSignFailed, err + } + + return status, nil +} + +func petitionSignaturesToEvents(signatures []repo.GuildPetitionSignature) []events.GuildPetitionSignature { + result := make([]events.GuildPetitionSignature, len(signatures)) + for i := range signatures { + result[i] = events.GuildPetitionSignature{ + PlayerGUID: signatures[i].PlayerGUID, + PlayerAccount: signatures[i].PlayerAccount, + } + } + return result } // updateRank handles promote and demote actions. @@ -680,6 +1193,9 @@ func (g *guildServiceImpl) updateRank(ctx context.Context, realmID uint32, updat } rank := g.rankForMember(guild, updaterGUID) + if rank == nil { + return ErrGuildNotFound + } if promote && !rank.HasRight(repo.RightPromote) { return ErrNotEnoughRight } else if !promote && !rank.HasRight(repo.RightDemote) { @@ -687,8 +1203,14 @@ func (g *guildServiceImpl) updateRank(ctx context.Context, realmID uint32, updat } targetRank := g.rankForMember(guild, targetGUID) + if targetRank == nil { + return ErrGuildNotFound + } var newRank uint8 if promote { + if targetRank.Rank == uint8(repo.GuildRankGuildMaster) { + return ErrNotEnoughRight + } newRank = targetRank.Rank - 1 if rank.Rank >= newRank { @@ -708,6 +1230,9 @@ func (g *guildServiceImpl) updateRank(ctx context.Context, realmID uint32, updat newRankObj := g.rankWithID(guild, newRank) updater := g.guildMemberForMemberGuid(guild, updaterGUID) target := g.guildMemberForMemberGuid(guild, targetGUID) + if newRankObj == nil || updater == nil || target == nil { + return ErrGuildNotFound + } err = g.guildsRepo.SetMemberRank(ctx, realmID, targetGUID, newRank) if err != nil { @@ -742,6 +1267,38 @@ func (g *guildServiceImpl) updateRank(ctx context.Context, realmID uint32, updat return nil } +// HandleGuildCreated refreshes newly created guild state after gateway observes worldserver success. +func (g *guildServiceImpl) HandleGuildCreated(payload events.GWEventGuildCreatedPayload) error { + refresher, ok := g.guildsRepo.(interface { + RefreshGuildByMemberGUID(ctx context.Context, realmID uint32, memberGUID uint64) (*repo.Guild, error) + }) + if !ok { + return fmt.Errorf("guild repo does not support refresh") + } + + guild, err := refresher.RefreshGuildByMemberGUID(context.Background(), payload.RealmID, payload.LeaderGUID) + if err != nil { + return err + } + if guild == nil { + return ErrGuildNotFound + } + + genericPayload := *g.buildGenericEventPayload(guild) + for _, member := range guild.GuildMembers { + err = g.eventsProducer.MemberAdded(&events.GuildEventMemberAddedPayload{ + GenericGuildEvent: genericPayload, + MemberGUID: member.PlayerGUID, + MemberName: member.Name, + }) + if err != nil { + return err + } + } + + return nil +} + // rankForMember returns rank in guild with given member guid. func (g *guildServiceImpl) rankForMember(guild *repo.Guild, memberGUID uint64) *repo.GuildRank { member := g.guildMemberForMemberGuid(guild, memberGUID) @@ -777,6 +1334,161 @@ func (g *guildServiceImpl) guildMemberForMemberGuid(guild *repo.Guild, memberGUI return nil } +func guildLeaderSameFaction(guild *repo.Guild, race uint8) bool { + if guild == nil { + return false + } + + for _, member := range guild.GuildMembers { + if member.PlayerGUID == guild.LeaderGUID { + return guildSameFaction(member.Race, race) + } + } + + return false +} + +func guildSameFaction(leftRace, rightRace uint8) bool { + leftTeam, ok := guildRaceTeam(leftRace) + if !ok { + return false + } + rightTeam, ok := guildRaceTeam(rightRace) + if !ok { + return false + } + return leftTeam == rightTeam +} + +func guildRaceTeam(race uint8) (wow.Team, bool) { + if int(race) >= len(wow.DefaultRaces) { + return 0, false + } + raceInfo := wow.DefaultRaces[race] + if raceInfo.ID == 0 { + return 0, false + } + return raceInfo.Team, true +} + +func (g *guildServiceImpl) guildBankMemberRank(ctx context.Context, realmID uint32, guildID, memberGUID uint64) (*repo.Guild, *repo.GuildMember, *repo.GuildRank, GuildBankStatus, error) { + guild, err := g.bankRepo().GuildByRealmAndID(ctx, realmID, guildID) + if err != nil { + return nil, nil, nil, GuildBankStatusFailed, err + } + if guild == nil { + return nil, nil, nil, GuildBankStatusGuildNotFound, nil + } + + member := g.guildMemberForMemberGuid(guild, memberGUID) + if member == nil { + return guild, nil, nil, GuildBankStatusNotInGuild, nil + } + rank := g.rankForMember(guild, memberGUID) + if rank == nil { + return guild, member, nil, GuildBankStatusNotEnoughRights, nil + } + + return guild, member, rank, GuildBankStatusOK, nil +} + +func (g *guildServiceImpl) guildBankCanViewTab(guild *repo.Guild, rank *repo.GuildRank, tabID uint8) GuildBankStatus { + if tabID >= repo.GuildBankMaxTabs || tabID >= guild.PurchasedBankTabs { + return GuildBankStatusInvalidTab + } + right := g.guildBankTabRight(rank, tabID) + if right == nil || right.Flags&repo.GuildBankRightViewTab == 0 { + return GuildBankStatusNotEnoughRights + } + return GuildBankStatusOK +} + +func (g *guildServiceImpl) guildBankCanDeposit(guild *repo.Guild, rank *repo.GuildRank, tabID uint8) GuildBankStatus { + if tabID >= repo.GuildBankMaxTabs || tabID >= guild.PurchasedBankTabs { + return GuildBankStatusInvalidTab + } + right := g.guildBankTabRight(rank, tabID) + if right == nil || right.Flags&repo.GuildBankRightDepositItem != repo.GuildBankRightDepositItem { + return GuildBankStatusNotEnoughRights + } + return GuildBankStatusOK +} + +func (g *guildServiceImpl) guildBankCanWithdraw(guild *repo.Guild, member *repo.GuildMember, rank *repo.GuildRank, tabID uint8) GuildBankStatus { + if status := g.guildBankCanViewTab(guild, rank, tabID); status != GuildBankStatusOK { + return status + } + if g.guildBankRemainingSlots(rank, member, tabID) == 0 { + return GuildBankStatusWithdrawLimit + } + return GuildBankStatusOK +} + +func (g *guildServiceImpl) guildBankCanUpdateText(guild *repo.Guild, rank *repo.GuildRank, tabID uint8) GuildBankStatus { + if tabID >= repo.GuildBankMaxTabs || tabID >= guild.PurchasedBankTabs { + return GuildBankStatusInvalidTab + } + right := g.guildBankTabRight(rank, tabID) + if right == nil || right.Flags&repo.GuildBankRightUpdateText == 0 { + return GuildBankStatusNotEnoughRights + } + return GuildBankStatusOK +} + +func (g *guildServiceImpl) guildBankTabRight(rank *repo.GuildRank, tabID uint8) *repo.GuildBankTabRight { + if rank == nil || tabID >= repo.GuildBankMaxTabs { + return nil + } + right := rank.BankTabRights[tabID] + return &right +} + +func (g *guildServiceImpl) guildBankRemainingSlots(rank *repo.GuildRank, member *repo.GuildMember, tabID uint8) int32 { + if rank == nil || member == nil || tabID >= repo.GuildBankMaxTabs { + return 0 + } + right := rank.BankTabRights[tabID] + return guildBankRemainingLimit(right.WithdrawItemLimit, member.BankWithdraw[tabID]) +} + +func (g *guildServiceImpl) guildBankRemainingMoney(rank *repo.GuildRank, member *repo.GuildMember) int32 { + if rank == nil || member == nil { + return 0 + } + return guildBankRemainingLimit(rank.MoneyPerDay, member.BankWithdraw[repo.GuildBankMaxTabs]) +} + +func guildBankRemainingLimit(limit uint32, used uint32) int32 { + if limit == 0xFFFFFFFF { + return -1 + } + if used >= limit { + return 0 + } + return int32(limit - used) +} + +func repoGuildBankErrorToStatus(err error) GuildBankStatus { + switch { + case err == nil: + return GuildBankStatusOK + case errors.Is(err, repo.ErrGuildBankInvalidTab): + return GuildBankStatusInvalidTab + case errors.Is(err, repo.ErrGuildBankInvalidSlot): + return GuildBankStatusInvalidSlot + case errors.Is(err, repo.ErrGuildBankNotEnoughGold): + return GuildBankStatusNotEnoughMoney + case errors.Is(err, repo.ErrGuildBankFull): + return GuildBankStatusBankFull + case errors.Is(err, repo.ErrGuildBankWithdrawLimit): + return GuildBankStatusWithdrawLimit + case errors.Is(err, repo.ErrGuildBankItemNotFound): + return GuildBankStatusItemNotFound + default: + return GuildBankStatusFailed + } +} + // lowestRankInGuild returns the lowest rank in given guild. Ranks are inverted, so we are interested in a rank with the highest id. func (g *guildServiceImpl) lowestRankInGuild(guild *repo.Guild) uint8 { highestRank := uint8(0) From a24a16bfeba1fdeab95c5546f5fad85901e75113 Mon Sep 17 00:00:00 2001 From: VG-Prog Date: Fri, 22 May 2026 21:48:44 +0200 Subject: [PATCH 08/11] feat(Cluster/GuildBank): Add transactional bank coordination Add guildserver-owned same-realm guild bank item, money, tab, log, text, split, merge, withdraw-limit, rollback, and idempotency helpers backed by direct MySQL transactions and guidserver item allocation. --- apps/guildserver/repo/guild_bank_mysql.go | 1251 +++++++++++++++++++ apps/guildserver/service/guild_bank_guid.go | 41 + 2 files changed, 1292 insertions(+) create mode 100644 apps/guildserver/repo/guild_bank_mysql.go create mode 100644 apps/guildserver/service/guild_bank_guid.go diff --git a/apps/guildserver/repo/guild_bank_mysql.go b/apps/guildserver/repo/guild_bank_mysql.go new file mode 100644 index 0000000..820664a --- /dev/null +++ b/apps/guildserver/repo/guild_bank_mysql.go @@ -0,0 +1,1251 @@ +package repo + +import ( + "context" + "database/sql" + "errors" + "sort" + "strconv" + "strings" + "time" +) + +const guildBankLogLimit = 25 + +var errGuildBankItemCantStack = errors.New("guild bank item can't stack") + +func (g *guildsMySQLRepo) GuildBank(ctx context.Context, realmID uint32, guildID uint64, tabID uint8, fullUpdate bool) (*GuildBank, error) { + bank := &GuildBank{GuildID: guildID} + + rows, err := g.db.DBByRealm(realmID).QueryContext(ctx, ` +SELECT + t.TabId, COALESCE(t.TabName, ''), COALESCE(t.TabIcon, ''), COALESCE(t.TabText, ''), + bi.SlotId, ii.guid, ii.itemEntry, ii.count, ii.flags, ii.randomPropertyId, ii.durability, + ii.charges, ii.enchantments, COALESCE(ii.text, '') +FROM guild_bank_tab t +LEFT JOIN guild_bank_item bi ON bi.guildid = t.guildid AND bi.TabId = t.TabId +LEFT JOIN item_instance ii ON ii.guid = bi.item_guid +WHERE t.guildid = ? AND (? OR t.TabId = ?) +ORDER BY t.TabId ASC, bi.SlotId ASC`, guildID, fullUpdate, tabID) + if err != nil { + return nil, err + } + defer rows.Close() + + tabByID := map[uint8]*GuildBankTab{} + for rows.Next() { + var tab GuildBankTab + var slotID sql.NullInt64 + var itemGUID sql.NullInt64 + var itemEntry sql.NullInt64 + var count sql.NullInt64 + var flags sql.NullInt64 + var randomPropertyID sql.NullInt64 + var durability sql.NullInt64 + var charges sql.NullString + var enchantments sql.NullString + var itemText string + + if err = rows.Scan( + &tab.TabID, &tab.Name, &tab.Icon, &tab.Text, + &slotID, &itemGUID, &itemEntry, &count, &flags, &randomPropertyID, &durability, + &charges, &enchantments, &itemText, + ); err != nil { + return nil, err + } + + bankTab := tabByID[tab.TabID] + if bankTab == nil { + bank.Tabs = append(bank.Tabs, tab) + bankTab = &bank.Tabs[len(bank.Tabs)-1] + tabByID[tab.TabID] = bankTab + } + + if !itemGUID.Valid || !itemEntry.Valid || !slotID.Valid { + continue + } + + item := GuildBankItem{ + ItemGUID: uint64(itemGUID.Int64), + Entry: uint32(itemEntry.Int64), + Slot: uint8(slotID.Int64), + Count: uint32(count.Int64), + Flags: uint32(flags.Int64), + RandomPropertyID: int32(randomPropertyID.Int64), + Durability: uint32(durability.Int64), + Charges: firstItemCharge(charges.String), + Text: itemText, + } + item.EnchantmentID, item.SocketEnchants = guildBankEnchants(enchantments.String) + bankTab.Items = append(bankTab.Items, item) + } + if rows.Err() != nil { + return nil, rows.Err() + } + + if err = g.db.DBByRealm(realmID).QueryRowContext(ctx, ` +SELECT BankMoney +FROM guild +WHERE guildid = ?`, guildID).Scan(&bank.Money); err == sql.ErrNoRows { + return nil, nil + } else if err != nil { + return nil, err + } + + return bank, nil +} + +func (g *guildsMySQLRepo) GuildBankLog(ctx context.Context, realmID uint32, guildID uint64, tabID uint8) ([]GuildBankLogEntry, error) { + rows, err := g.db.DBByRealm(realmID).QueryContext(ctx, ` +SELECT EventType, PlayerGuid, ItemOrMoney, ItemStackCount, DestTabId, TimeStamp +FROM guild_bank_eventlog +WHERE guildid = ? AND TabId = ? +ORDER BY TimeStamp DESC, LogGuid DESC +LIMIT ?`, guildID, tabID, guildBankLogLimit) + if err != nil { + return nil, err + } + defer rows.Close() + + now := uint32(time.Now().Unix()) + var result []GuildBankLogEntry + for rows.Next() { + var eventType int8 + var playerGUID uint64 + var itemOrMoney uint32 + var itemStackCount uint32 + var destTab uint8 + var timestamp uint32 + if err = rows.Scan(&eventType, &playerGUID, &itemOrMoney, &itemStackCount, &destTab, ×tamp); err != nil { + return nil, err + } + + entry := GuildBankLogEntry{ + PlayerGUID: playerGUID, + TimeOffset: func() uint32 { + if now < timestamp { + return 0 + } + return now - timestamp + }(), + EntryType: eventType, + } + switch GuildBankEventLogType(eventType) { + case GuildBankLogDepositItem, GuildBankLogWithdrawItem: + entry.ItemID = int32(itemOrMoney) + entry.Count = int32(itemStackCount) + case GuildBankLogMoveItem, GuildBankLogMoveItem2: + entry.ItemID = int32(itemOrMoney) + entry.Count = int32(itemStackCount) + entry.OtherTab = int8(destTab) + default: + entry.Money = itemOrMoney + } + result = append(result, entry) + } + + return result, rows.Err() +} + +func (g *guildsMySQLRepo) SetGuildBankTabInfo(ctx context.Context, realmID uint32, guildID uint64, tabID uint8, name, icon string) error { + _, err := g.db.DBByRealm(realmID).ExecContext(ctx, ` +UPDATE guild_bank_tab +SET TabName = ?, TabIcon = ? +WHERE guildid = ? AND TabId = ?`, name, icon, guildID, tabID) + return err +} + +func (g *guildsMySQLRepo) SetGuildBankTabText(ctx context.Context, realmID uint32, guildID uint64, tabID uint8, text string) error { + _, err := g.db.DBByRealm(realmID).ExecContext(ctx, ` +UPDATE guild_bank_tab +SET TabText = ? +WHERE guildid = ? AND TabId = ?`, text, guildID, tabID) + return err +} + +func (g *guildsMySQLRepo) BuyGuildBankTab(ctx context.Context, realmID uint32, guildID uint64, tabID uint8) error { + tx, err := g.db.DBByRealm(realmID).BeginTx(ctx, nil) + if err != nil { + return err + } + defer rollbackTx(tx) + + var guildIDFound uint64 + if err = tx.QueryRowContext(ctx, ` +SELECT guildid +FROM guild +WHERE guildid = ? +FOR UPDATE`, guildID).Scan(&guildIDFound); err != nil { + return err + } + + var tabs uint8 + if err = tx.QueryRowContext(ctx, ` +SELECT COUNT(*) +FROM guild_bank_tab +WHERE guildid = ?`, guildID).Scan(&tabs); err != nil { + return err + } + if tabs >= GuildBankMaxTabs || tabID != tabs { + return ErrGuildBankInvalidTab + } + + if _, err = tx.ExecContext(ctx, ` +INSERT INTO guild_bank_tab (guildid, TabId) +VALUES (?, ?)`, guildID, tabID); err != nil { + return err + } + if err = insertGuildBankLogTx(ctx, tx, guildID, GuildBankLogBuySlot, tabID, 0, 0, 0, 0); err != nil { + return err + } + + return tx.Commit() +} + +func (g *guildsMySQLRepo) DepositGuildBankMoney(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, amount uint32) error { + tx, err := g.db.DBByRealm(realmID).BeginTx(ctx, nil) + if err != nil { + return err + } + defer rollbackTx(tx) + + var money uint64 + if err = tx.QueryRowContext(ctx, ` +SELECT BankMoney +FROM guild +WHERE guildid = ? +FOR UPDATE`, guildID).Scan(&money); err != nil { + return err + } + if money > GuildBankMoneyLimit-uint64(amount) { + return ErrGuildBankFull + } + + if _, err = tx.ExecContext(ctx, ` +UPDATE guild +SET BankMoney = BankMoney + ? +WHERE guildid = ?`, amount, guildID); err != nil { + return err + } + if err = insertGuildBankLogTx(ctx, tx, guildID, GuildBankLogDepositMoney, 0, memberGUID, amount, 0, 0); err != nil { + return err + } + + return tx.Commit() +} + +func (g *guildsMySQLRepo) WithdrawGuildBankMoney(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, amount uint32, repair bool) (uint32, error) { + tx, err := g.db.DBByRealm(realmID).BeginTx(ctx, nil) + if err != nil { + return 0, err + } + defer rollbackTx(tx) + + var money uint64 + if err = tx.QueryRowContext(ctx, ` +SELECT BankMoney +FROM guild +WHERE guildid = ? +FOR UPDATE`, guildID).Scan(&money); err != nil { + return 0, err + } + if money < uint64(amount) { + return 0, ErrGuildBankNotEnoughGold + } + + if _, err = tx.ExecContext(ctx, ` +UPDATE guild +SET BankMoney = BankMoney - ? +WHERE guildid = ?`, amount, guildID); err != nil { + return 0, err + } + if err = incrementGuildBankWithdrawTx(ctx, tx, guildID, memberGUID, GuildBankMaxTabs, amount); err != nil { + return 0, err + } + + eventType := GuildBankLogWithdrawMoney + if repair { + eventType = GuildBankLogRepairMoney + } + logGUID, err := insertGuildBankLogReturningGUIDTx(ctx, tx, guildID, eventType, 0, memberGUID, amount, 0, 0) + if err != nil { + return 0, err + } + + return logGUID, tx.Commit() +} + +func (g *guildsMySQLRepo) RollbackGuildBankMoneyWithdraw(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, amount uint32, repair bool, logGUID uint32) error { + tx, err := g.db.DBByRealm(realmID).BeginTx(ctx, nil) + if err != nil { + return err + } + defer rollbackTx(tx) + + eventType := GuildBankLogWithdrawMoney + if repair { + eventType = GuildBankLogRepairMoney + } + deleted, err := deleteGuildBankLogTx(ctx, tx, guildID, logGUID, eventType, 0, memberGUID, amount, 0, 0) + if err != nil { + return err + } + if !deleted { + return tx.Commit() + } + + if _, err = tx.ExecContext(ctx, ` +UPDATE guild +SET BankMoney = BankMoney + ? +WHERE guildid = ?`, amount, guildID); err != nil { + return err + } + if err = decrementGuildBankWithdrawTx(ctx, tx, memberGUID, GuildBankMaxTabs, amount); err != nil { + return err + } + + return tx.Commit() +} + +func (g *guildsMySQLRepo) DepositGuildBankItem(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, tabID, slotID uint8, item GuildBankItem) error { + if tabID >= GuildBankMaxTabs { + return ErrGuildBankInvalidTab + } + if slotID >= GuildBankMaxSlots { + return ErrGuildBankInvalidSlot + } + + tx, err := g.db.DBByRealm(realmID).BeginTx(ctx, nil) + if err != nil { + return err + } + defer rollbackTx(tx) + + if err = lockGuildBankTabTx(ctx, tx, guildID, tabID); err != nil { + return err + } + existingGuildID, existingTabID, existingSlotID, exists, err := guildBankItemLocationForUpdateTx(ctx, tx, item.ItemGUID) + if err != nil { + return err + } + if exists { + if existingGuildID == guildID && existingTabID == tabID && existingSlotID == slotID { + return tx.Commit() + } + return ErrGuildBankFull + } + tabItems, err := guildBankTabItemsForUpdateTx(ctx, tx, guildID, tabID) + if err != nil { + return err + } + maxStack, err := g.guildBankItemMaxStack(ctx, item.Entry) + if err != nil { + return err + } + placements, err := guildBankPlanStore(tabItems, nil, false, slotID, item.Entry, item.Count, maxStack) + if errors.Is(err, errGuildBankItemCantStack) { + return ErrGuildBankFull + } + if err != nil { + return err + } + if err = applyGuildBankIncomingStoreTx(ctx, tx, guildID, tabID, item.ItemGUID, item.Count, placements); err != nil { + return err + } + if err = insertGuildBankLogTx(ctx, tx, guildID, GuildBankLogDepositItem, tabID, memberGUID, item.Entry, uint16(item.Count), 0); err != nil { + return err + } + + return tx.Commit() +} + +func (g *guildsMySQLRepo) WithdrawGuildBankItem(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, tabID, slotID uint8, count uint32, splitItemGUID uint64) (*GuildBankItem, uint32, error) { + if tabID >= GuildBankMaxTabs { + return nil, 0, ErrGuildBankInvalidTab + } + if slotID >= GuildBankMaxSlots { + return nil, 0, ErrGuildBankInvalidSlot + } + + tx, err := g.db.DBByRealm(realmID).BeginTx(ctx, nil) + if err != nil { + return nil, 0, err + } + defer rollbackTx(tx) + + if err = lockGuildBankTabTx(ctx, tx, guildID, tabID); err != nil { + return nil, 0, err + } + item, err := guildBankItemForUpdateTx(ctx, tx, guildID, tabID, slotID) + if errors.Is(err, sql.ErrNoRows) { + return nil, 0, ErrGuildBankItemNotFound + } + if err != nil { + return nil, 0, err + } + moveCount := guildBankMoveCount(item.Count, count) + + if moveCount < item.Count { + if splitItemGUID == 0 { + return nil, 0, ErrGuildBankItemNotFound + } + if err = cloneGuildBankItemInstanceTx(ctx, tx, item.ItemGUID, splitItemGUID, moveCount); err != nil { + return nil, 0, err + } + if err = setItemInstanceCountTx(ctx, tx, item.ItemGUID, item.Count-moveCount); err != nil { + return nil, 0, err + } + item.ItemGUID = splitItemGUID + item.Count = moveCount + } else if _, err = tx.ExecContext(ctx, ` +DELETE FROM guild_bank_item +WHERE guildid = ? AND TabId = ? AND SlotId = ?`, guildID, tabID, slotID); err != nil { + return nil, 0, err + } + if err = incrementGuildBankWithdrawTx(ctx, tx, guildID, memberGUID, tabID, 1); err != nil { + return nil, 0, err + } + logGUID, err := insertGuildBankLogReturningGUIDTx(ctx, tx, guildID, GuildBankLogWithdrawItem, tabID, memberGUID, item.Entry, uint16(moveCount), 0) + if err != nil { + return nil, 0, err + } + + return item, logGUID, tx.Commit() +} + +func (g *guildsMySQLRepo) RollbackGuildBankItemWithdraw(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, tabID, slotID uint8, item GuildBankItem, logGUID uint32) ([]uint8, error) { + if tabID >= GuildBankMaxTabs { + return nil, ErrGuildBankInvalidTab + } + if slotID >= GuildBankMaxSlots { + return nil, ErrGuildBankInvalidSlot + } + if item.ItemGUID == 0 || item.Entry == 0 || item.Count == 0 { + return nil, ErrGuildBankItemNotFound + } + + tx, err := g.db.DBByRealm(realmID).BeginTx(ctx, nil) + if err != nil { + return nil, err + } + defer rollbackTx(tx) + + if err = lockGuildBankTabTx(ctx, tx, guildID, tabID); err != nil { + return nil, err + } + deleted, err := deleteGuildBankLogTx(ctx, tx, guildID, logGUID, GuildBankLogWithdrawItem, tabID, memberGUID, item.Entry, uint16(item.Count), 0) + if err != nil { + return nil, err + } + if !deleted { + return nil, tx.Commit() + } + + existingGuildID, _, existingSlotID, exists, err := guildBankItemLocationForUpdateTx(ctx, tx, item.ItemGUID) + if err != nil { + return nil, err + } + if exists { + if existingGuildID == guildID { + if err = decrementGuildBankWithdrawTx(ctx, tx, memberGUID, tabID, 1); err != nil { + return nil, err + } + return uniqueGuildBankSlots(existingSlotID), tx.Commit() + } + return nil, ErrGuildBankFull + } + + tabItems, err := guildBankTabItemsForUpdateTx(ctx, tx, guildID, tabID) + if err != nil { + return nil, err + } + maxStack, err := g.guildBankItemMaxStack(ctx, item.Entry) + if err != nil { + return nil, err + } + placements, err := guildBankPlanStore(tabItems, nil, false, slotID, item.Entry, item.Count, maxStack) + if errors.Is(err, errGuildBankItemCantStack) { + return nil, ErrGuildBankFull + } + if err != nil { + return nil, err + } + if err = applyGuildBankIncomingStoreTx(ctx, tx, guildID, tabID, item.ItemGUID, item.Count, placements); err != nil { + return nil, err + } + if err = decrementGuildBankWithdrawTx(ctx, tx, memberGUID, tabID, 1); err != nil { + return nil, err + } + + changed := make([]uint8, 0, len(placements)) + for _, placement := range placements { + changed = append(changed, placement.slotID) + } + return uniqueGuildBankSlots(changed...), tx.Commit() +} + +func (g *guildsMySQLRepo) MoveGuildBankItem(ctx context.Context, realmID uint32, guildID uint64, memberGUID uint64, sourceTabID, sourceSlotID, destinationTabID, destinationSlotID uint8, count uint32, splitItemGUID uint64) ([]uint8, error) { + if sourceTabID >= GuildBankMaxTabs || destinationTabID >= GuildBankMaxTabs { + return nil, ErrGuildBankInvalidTab + } + if sourceSlotID >= GuildBankMaxSlots || destinationSlotID >= GuildBankMaxSlots { + return nil, ErrGuildBankInvalidSlot + } + if sourceTabID == destinationTabID && sourceSlotID == destinationSlotID { + return []uint8{sourceSlotID}, nil + } + + tx, err := g.db.DBByRealm(realmID).BeginTx(ctx, nil) + if err != nil { + return nil, err + } + defer rollbackTx(tx) + + if err = lockGuildBankTabsTx(ctx, tx, guildID, sourceTabID, destinationTabID); err != nil { + return nil, err + } + source, err := guildBankItemForUpdateTx(ctx, tx, guildID, sourceTabID, sourceSlotID) + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrGuildBankItemNotFound + } + if err != nil { + return nil, err + } + moveCount := guildBankMoveCount(source.Count, count) + split := moveCount < source.Count + if split && splitItemGUID == 0 { + return nil, ErrGuildBankItemNotFound + } + maxStack, err := g.guildBankItemMaxStack(ctx, source.Entry) + if err != nil { + return nil, err + } + destTabItems, err := guildBankTabItemsForUpdateTx(ctx, tx, guildID, destinationTabID) + if err != nil { + return nil, err + } + sourceSlot := sourceSlotID + sourceSlotAvailable := !split && sourceTabID == destinationTabID + placements, err := guildBankPlanStore(destTabItems, &sourceSlot, sourceSlotAvailable, destinationSlotID, source.Entry, moveCount, maxStack) + if errors.Is(err, errGuildBankItemCantStack) { + if split { + return nil, ErrGuildBankFull + } + if err = swapGuildBankItemsTx(ctx, tx, guildID, sourceTabID, sourceSlotID, destinationTabID, destinationSlotID); err != nil { + return nil, err + } + if sourceTabID != destinationTabID { + if err = incrementGuildBankWithdrawTx(ctx, tx, guildID, memberGUID, sourceTabID, 1); err != nil { + return nil, err + } + } + if err = insertGuildBankLogTx(ctx, tx, guildID, GuildBankLogMoveItem, sourceTabID, memberGUID, source.Entry, uint16(moveCount), destinationTabID); err != nil { + return nil, err + } + return uniqueGuildBankSlots(sourceSlotID, destinationSlotID), tx.Commit() + } + if err != nil { + return nil, err + } + + if split { + if err = applyGuildBankSplitMoveTx(ctx, tx, guildID, source, sourceTabID, sourceSlotID, destinationTabID, splitItemGUID, moveCount, placements); err != nil { + return nil, err + } + } else if err = applyGuildBankFullMoveTx(ctx, tx, guildID, source, sourceTabID, sourceSlotID, destinationTabID, placements); err != nil { + return nil, err + } + + if sourceTabID != destinationTabID { + if err = incrementGuildBankWithdrawTx(ctx, tx, guildID, memberGUID, sourceTabID, 1); err != nil { + return nil, err + } + } + if err = insertGuildBankLogTx(ctx, tx, guildID, GuildBankLogMoveItem, sourceTabID, memberGUID, source.Entry, uint16(moveCount), destinationTabID); err != nil { + return nil, err + } + + changed := []uint8{sourceSlotID} + for _, placement := range placements { + changed = append(changed, placement.slotID) + } + return uniqueGuildBankSlots(changed...), tx.Commit() +} + +type guildBankStorePlacement struct { + slotID uint8 + existingGUID uint64 + count uint32 +} + +func guildBankMoveCount(sourceCount, requestedCount uint32) uint32 { + if requestedCount == 0 || requestedCount >= sourceCount { + return sourceCount + } + return requestedCount +} + +func guildBankPlanStore(tabItems map[uint8]*GuildBankItem, sourceSlot *uint8, sourceSlotAvailable bool, destinationSlot uint8, entry uint32, count uint32, maxStack uint32) ([]guildBankStorePlacement, error) { + if count == 0 { + return nil, ErrGuildBankItemNotFound + } + if maxStack == 0 { + return nil, ErrGuildBankItemNotFound + } + + placements := make([]guildBankStorePlacement, 0, 2) + remaining := count + skipMerge := map[uint8]bool{destinationSlot: true} + skipEmpty := map[uint8]bool{destinationSlot: true} + if sourceSlot != nil { + skipMerge[*sourceSlot] = true + if !sourceSlotAvailable { + skipEmpty[*sourceSlot] = true + } + } + + if destination := tabItems[destinationSlot]; destination != nil { + if destination.Entry != entry || destination.Count >= maxStack { + return nil, errGuildBankItemCantStack + } + free := maxStack - destination.Count + if free > remaining { + free = remaining + } + placements = append(placements, guildBankStorePlacement{ + slotID: destinationSlot, + existingGUID: destination.ItemGUID, + count: free, + }) + remaining -= free + } else { + free := maxStack + if free > remaining { + free = remaining + } + placements = append(placements, guildBankStorePlacement{ + slotID: destinationSlot, + count: free, + }) + remaining -= free + } + if remaining == 0 { + return placements, nil + } + + if maxStack > 1 { + for slotID := uint8(0); slotID < GuildBankMaxSlots && remaining > 0; slotID++ { + if skipMerge[slotID] { + continue + } + item := tabItems[slotID] + if item == nil || item.Entry != entry || item.Count >= maxStack { + continue + } + free := maxStack - item.Count + if free > remaining { + free = remaining + } + placements = append(placements, guildBankStorePlacement{ + slotID: slotID, + existingGUID: item.ItemGUID, + count: free, + }) + remaining -= free + } + } + + for slotID := uint8(0); slotID < GuildBankMaxSlots && remaining > 0; slotID++ { + sourceSlotIsAvailable := sourceSlot != nil && *sourceSlot == slotID && sourceSlotAvailable + if skipEmpty[slotID] || (tabItems[slotID] != nil && !sourceSlotIsAvailable) { + continue + } + free := maxStack + if free > remaining { + free = remaining + } + placements = append(placements, guildBankStorePlacement{ + slotID: slotID, + count: free, + }) + remaining -= free + } + if remaining != 0 { + return nil, ErrGuildBankFull + } + + return placements, nil +} + +func applyGuildBankIncomingStoreTx(ctx context.Context, tx *sql.Tx, guildID uint64, tabID uint8, itemGUID uint64, itemCount uint32, placements []guildBankStorePlacement) error { + remainingForNewSlot := itemCount + var emptyPlacement *guildBankStorePlacement + for i := range placements { + placement := placements[i] + if placement.existingGUID == 0 { + if emptyPlacement != nil { + return ErrGuildBankFull + } + emptyPlacement = &placements[i] + continue + } + if err := addItemInstanceCountTx(ctx, tx, placement.existingGUID, placement.count); err != nil { + return err + } + remainingForNewSlot -= placement.count + } + + if emptyPlacement == nil { + return deleteItemInstanceTx(ctx, tx, itemGUID) + } + if remainingForNewSlot != emptyPlacement.count { + return ErrGuildBankFull + } + if err := setItemInstanceOwnerAndCountTx(ctx, tx, itemGUID, 0, emptyPlacement.count); err != nil { + return err + } + return insertGuildBankItemTx(ctx, tx, guildID, tabID, emptyPlacement.slotID, itemGUID) +} + +func applyGuildBankSplitMoveTx(ctx context.Context, tx *sql.Tx, guildID uint64, source *GuildBankItem, sourceTabID, sourceSlotID, destinationTabID uint8, splitItemGUID uint64, moveCount uint32, placements []guildBankStorePlacement) error { + if err := setItemInstanceCountTx(ctx, tx, source.ItemGUID, source.Count-moveCount); err != nil { + return err + } + + remainingForNewSlot := moveCount + var emptyPlacement *guildBankStorePlacement + for i := range placements { + placement := placements[i] + if placement.existingGUID == 0 { + if emptyPlacement != nil { + return ErrGuildBankFull + } + emptyPlacement = &placements[i] + continue + } + if err := addItemInstanceCountTx(ctx, tx, placement.existingGUID, placement.count); err != nil { + return err + } + remainingForNewSlot -= placement.count + } + if emptyPlacement == nil { + return nil + } + if remainingForNewSlot != emptyPlacement.count { + return ErrGuildBankFull + } + if err := cloneGuildBankItemInstanceTx(ctx, tx, source.ItemGUID, splitItemGUID, emptyPlacement.count); err != nil { + return err + } + return insertGuildBankItemTx(ctx, tx, guildID, destinationTabID, emptyPlacement.slotID, splitItemGUID) +} + +func applyGuildBankFullMoveTx(ctx context.Context, tx *sql.Tx, guildID uint64, source *GuildBankItem, sourceTabID, sourceSlotID, destinationTabID uint8, placements []guildBankStorePlacement) error { + remainingForNewSlot := source.Count + var emptyPlacement *guildBankStorePlacement + for i := range placements { + placement := placements[i] + if placement.existingGUID == 0 { + if emptyPlacement != nil { + return ErrGuildBankFull + } + emptyPlacement = &placements[i] + continue + } + if err := addItemInstanceCountTx(ctx, tx, placement.existingGUID, placement.count); err != nil { + return err + } + remainingForNewSlot -= placement.count + } + + if emptyPlacement == nil { + if _, err := tx.ExecContext(ctx, ` +DELETE FROM guild_bank_item +WHERE guildid = ? AND TabId = ? AND SlotId = ?`, guildID, sourceTabID, sourceSlotID); err != nil { + return err + } + return deleteItemInstanceTx(ctx, tx, source.ItemGUID) + } + if remainingForNewSlot != emptyPlacement.count { + return ErrGuildBankFull + } + if err := setItemInstanceOwnerAndCountTx(ctx, tx, source.ItemGUID, 0, emptyPlacement.count); err != nil { + return err + } + _, err := tx.ExecContext(ctx, ` +UPDATE guild_bank_item +SET TabId = ?, SlotId = ? +WHERE guildid = ? AND TabId = ? AND SlotId = ?`, destinationTabID, emptyPlacement.slotID, guildID, sourceTabID, sourceSlotID) + return err +} + +func swapGuildBankItemsTx(ctx context.Context, tx *sql.Tx, guildID uint64, sourceTabID, sourceSlotID, destinationTabID, destinationSlotID uint8) error { + source, dest, err := guildBankMoveItemsForUpdateTx(ctx, tx, guildID, sourceTabID, sourceSlotID, destinationTabID, destinationSlotID) + if errors.Is(err, sql.ErrNoRows) { + return ErrGuildBankItemNotFound + } + if err != nil { + return err + } + if source == nil || dest == nil { + return ErrGuildBankItemNotFound + } + if _, err = tx.ExecContext(ctx, ` +DELETE FROM guild_bank_item +WHERE guildid = ? AND ((TabId = ? AND SlotId = ?) OR (TabId = ? AND SlotId = ?))`, + guildID, sourceTabID, sourceSlotID, destinationTabID, destinationSlotID); err != nil { + return err + } + _, err = tx.ExecContext(ctx, ` +INSERT INTO guild_bank_item (guildid, TabId, SlotId, item_guid) +VALUES (?, ?, ?, ?), (?, ?, ?, ?)`, + guildID, destinationTabID, destinationSlotID, source.ItemGUID, + guildID, sourceTabID, sourceSlotID, dest.ItemGUID) + return err +} + +func uniqueGuildBankSlots(slots ...uint8) []uint8 { + if len(slots) == 0 { + return nil + } + seen := map[uint8]bool{} + out := make([]uint8, 0, len(slots)) + for _, slot := range slots { + if seen[slot] { + continue + } + seen[slot] = true + out = append(out, slot) + } + sort.Slice(out, func(i, j int) bool { return out[i] < out[j] }) + return out +} + +func firstItemCharge(charges string) uint32 { + fields := strings.Fields(charges) + if len(fields) == 0 { + return 0 + } + v, _ := strconv.ParseInt(fields[0], 10, 32) + if v < 0 { + return uint32(-v) + } + return uint32(v) +} + +func guildBankEnchants(enchantments string) (uint32, []GuildBankSocketEnchant) { + fields := strings.Fields(enchantments) + if len(fields) < 3 { + return 0, nil + } + + enchantID := parseEnchant(fields, 0) + var sockets []GuildBankSocketEnchant + for socketIndex := 0; socketIndex < 3; socketIndex++ { + value := parseEnchant(fields, (2+socketIndex)*3) + if value != 0 { + sockets = append(sockets, GuildBankSocketEnchant{ + SocketIndex: uint8(socketIndex), + SocketEnchantID: value, + }) + } + } + return enchantID, sockets +} + +func parseEnchant(fields []string, index int) uint32 { + if index < 0 || index >= len(fields) { + return 0 + } + v, _ := strconv.ParseUint(fields[index], 10, 32) + return uint32(v) +} + +func rollbackTx(tx *sql.Tx) { + _ = tx.Rollback() +} + +func lockGuildBankTabTx(ctx context.Context, tx *sql.Tx, guildID uint64, tabID uint8) error { + var found uint8 + err := tx.QueryRowContext(ctx, ` +SELECT TabId +FROM guild_bank_tab +WHERE guildid = ? AND TabId = ? +FOR UPDATE`, guildID, tabID).Scan(&found) + if errors.Is(err, sql.ErrNoRows) { + return ErrGuildBankInvalidTab + } + return err +} + +func lockGuildBankTabsTx(ctx context.Context, tx *sql.Tx, guildID uint64, tabs ...uint8) error { + seen := map[uint8]bool{} + uniqueTabs := make([]int, 0, len(tabs)) + for _, tab := range tabs { + if seen[tab] { + continue + } + seen[tab] = true + uniqueTabs = append(uniqueTabs, int(tab)) + } + sort.Ints(uniqueTabs) + for _, tab := range uniqueTabs { + if err := lockGuildBankTabTx(ctx, tx, guildID, uint8(tab)); err != nil { + return err + } + } + return nil +} + +func (g *guildsMySQLRepo) guildBankItemMaxStack(ctx context.Context, entry uint32) (uint32, error) { + if g.worldDB == nil { + return 1, nil + } + + var stackable int32 + err := g.worldDB.QueryRowContext(ctx, ` +SELECT stackable +FROM item_template +WHERE entry = ?`, entry).Scan(&stackable) + if errors.Is(err, sql.ErrNoRows) { + return 0, ErrGuildBankItemNotFound + } + if err != nil { + return 0, err + } + if stackable == 2147483647 || stackable <= 0 { + return 0x7FFFFFFF - 1, nil + } + return uint32(stackable), nil +} + +func guildBankSlotOccupiedTx(ctx context.Context, tx *sql.Tx, guildID uint64, tabID, slotID uint8) (bool, error) { + var itemGUID uint64 + err := tx.QueryRowContext(ctx, ` +SELECT item_guid +FROM guild_bank_item +WHERE guildid = ? AND TabId = ? AND SlotId = ? +FOR UPDATE`, guildID, tabID, slotID).Scan(&itemGUID) + if errors.Is(err, sql.ErrNoRows) { + return false, nil + } + return err == nil, err +} + +func guildBankItemLocationForUpdateTx(ctx context.Context, tx *sql.Tx, itemGUID uint64) (uint64, uint8, uint8, bool, error) { + var guildID uint64 + var tabID uint8 + var slotID uint8 + err := tx.QueryRowContext(ctx, ` +SELECT guildid, TabId, SlotId +FROM guild_bank_item +WHERE item_guid = ? +FOR UPDATE`, itemGUID).Scan(&guildID, &tabID, &slotID) + if errors.Is(err, sql.ErrNoRows) { + return 0, 0, 0, false, nil + } + if err != nil { + return 0, 0, 0, false, err + } + return guildID, tabID, slotID, true, nil +} + +func guildBankTabItemsForUpdateTx(ctx context.Context, tx *sql.Tx, guildID uint64, tabID uint8) (map[uint8]*GuildBankItem, error) { + rows, err := tx.QueryContext(ctx, ` +SELECT + bi.SlotId, + ii.guid, ii.itemEntry, ii.count, ii.flags, ii.randomPropertyId, ii.durability, + ii.charges, ii.enchantments, COALESCE(ii.text, '') +FROM guild_bank_item bi +INNER JOIN item_instance ii ON ii.guid = bi.item_guid +WHERE bi.guildid = ? AND bi.TabId = ? +ORDER BY bi.SlotId ASC +FOR UPDATE`, guildID, tabID) + if err != nil { + return nil, err + } + defer rows.Close() + + items := map[uint8]*GuildBankItem{} + for rows.Next() { + item := GuildBankItem{} + var charges string + var enchantments string + if err = rows.Scan( + &item.Slot, + &item.ItemGUID, &item.Entry, &item.Count, &item.Flags, &item.RandomPropertyID, &item.Durability, + &charges, &enchantments, &item.Text, + ); err != nil { + return nil, err + } + item.Charges = firstItemCharge(charges) + item.EnchantmentID, item.SocketEnchants = guildBankEnchants(enchantments) + items[item.Slot] = &item + } + return items, rows.Err() +} + +func insertGuildBankItemTx(ctx context.Context, tx *sql.Tx, guildID uint64, tabID, slotID uint8, itemGUID uint64) error { + _, err := tx.ExecContext(ctx, ` +INSERT INTO guild_bank_item (guildid, TabId, SlotId, item_guid) +VALUES (?, ?, ?, ?)`, guildID, tabID, slotID, itemGUID) + return err +} + +func cloneGuildBankItemInstanceTx(ctx context.Context, tx *sql.Tx, sourceItemGUID, newItemGUID uint64, count uint32) error { + res, err := tx.ExecContext(ctx, ` +INSERT INTO item_instance (guid, itemEntry, owner_guid, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text) +SELECT ?, itemEntry, 0, creatorGuid, giftCreatorGuid, ?, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text +FROM item_instance +WHERE guid = ?`, newItemGUID, count, sourceItemGUID) + if err != nil { + return err + } + affected, err := res.RowsAffected() + if err != nil { + return err + } + if affected == 0 { + return ErrGuildBankItemNotFound + } + return nil +} + +func setItemInstanceCountTx(ctx context.Context, tx *sql.Tx, itemGUID uint64, count uint32) error { + _, err := tx.ExecContext(ctx, ` +UPDATE item_instance +SET count = ? +WHERE guid = ?`, count, itemGUID) + return err +} + +func addItemInstanceCountTx(ctx context.Context, tx *sql.Tx, itemGUID uint64, count uint32) error { + _, err := tx.ExecContext(ctx, ` +UPDATE item_instance +SET count = count + ? +WHERE guid = ?`, count, itemGUID) + return err +} + +func setItemInstanceOwnerAndCountTx(ctx context.Context, tx *sql.Tx, itemGUID uint64, ownerGUID uint64, count uint32) error { + _, err := tx.ExecContext(ctx, ` +UPDATE item_instance +SET owner_guid = ?, count = ? +WHERE guid = ?`, ownerGUID, count, itemGUID) + return err +} + +func deleteItemInstanceTx(ctx context.Context, tx *sql.Tx, itemGUID uint64) error { + _, err := tx.ExecContext(ctx, ` +DELETE FROM item_instance +WHERE guid = ?`, itemGUID) + return err +} + +func guildBankItemForUpdateTx(ctx context.Context, tx *sql.Tx, guildID uint64, tabID, slotID uint8) (*GuildBankItem, error) { + var item GuildBankItem + var charges string + var enchantments string + err := tx.QueryRowContext(ctx, ` +SELECT + ii.guid, ii.itemEntry, bi.SlotId, ii.count, ii.flags, ii.randomPropertyId, ii.durability, + ii.charges, ii.enchantments, COALESCE(ii.text, '') +FROM guild_bank_item bi +INNER JOIN item_instance ii ON ii.guid = bi.item_guid +WHERE bi.guildid = ? AND bi.TabId = ? AND bi.SlotId = ? +FOR UPDATE`, guildID, tabID, slotID).Scan( + &item.ItemGUID, &item.Entry, &item.Slot, &item.Count, &item.Flags, &item.RandomPropertyID, &item.Durability, + &charges, &enchantments, &item.Text, + ) + if err != nil { + return nil, err + } + item.Charges = firstItemCharge(charges) + item.EnchantmentID, item.SocketEnchants = guildBankEnchants(enchantments) + return &item, nil +} + +func guildBankMoveItemsForUpdateTx(ctx context.Context, tx *sql.Tx, guildID uint64, sourceTabID, sourceSlotID, destinationTabID, destinationSlotID uint8) (*GuildBankItem, *GuildBankItem, error) { + rows, err := tx.QueryContext(ctx, ` +SELECT + bi.TabId, bi.SlotId, + ii.guid, ii.itemEntry, ii.count, ii.flags, ii.randomPropertyId, ii.durability, + ii.charges, ii.enchantments, COALESCE(ii.text, '') +FROM guild_bank_item bi +INNER JOIN item_instance ii ON ii.guid = bi.item_guid +WHERE bi.guildid = ? AND ((bi.TabId = ? AND bi.SlotId = ?) OR (bi.TabId = ? AND bi.SlotId = ?)) +ORDER BY bi.TabId ASC, bi.SlotId ASC +FOR UPDATE`, guildID, sourceTabID, sourceSlotID, destinationTabID, destinationSlotID) + if err != nil { + return nil, nil, err + } + defer rows.Close() + + var source *GuildBankItem + var dest *GuildBankItem + for rows.Next() { + var tabID uint8 + var item GuildBankItem + var charges string + var enchantments string + if err = rows.Scan( + &tabID, &item.Slot, + &item.ItemGUID, &item.Entry, &item.Count, &item.Flags, &item.RandomPropertyID, &item.Durability, + &charges, &enchantments, &item.Text, + ); err != nil { + return nil, nil, err + } + item.Charges = firstItemCharge(charges) + item.EnchantmentID, item.SocketEnchants = guildBankEnchants(enchantments) + if tabID == sourceTabID && item.Slot == sourceSlotID { + source = &item + } else if tabID == destinationTabID && item.Slot == destinationSlotID { + dest = &item + } + } + if err = rows.Err(); err != nil { + return nil, nil, err + } + if source == nil { + return nil, nil, sql.ErrNoRows + } + return source, dest, nil +} + +func incrementGuildBankWithdrawTx(ctx context.Context, tx *sql.Tx, guildID uint64, memberGUID uint64, tabID uint8, amount uint32) error { + if tabID > GuildBankMaxTabs { + return ErrGuildBankInvalidTab + } + + column := "money" + if tabID < GuildBankMaxTabs { + column = "tab" + strconv.Itoa(int(tabID)) + } + + _, err := tx.ExecContext(ctx, ` +INSERT INTO guild_member_withdraw (guid, tab0, tab1, tab2, tab3, tab4, tab5, money) +VALUES (?, 0, 0, 0, 0, 0, 0, 0) +ON DUPLICATE KEY UPDATE guid = VALUES(guid)`, memberGUID) + if err != nil { + return err + } + + limit, used, err := guildBankWithdrawLimitForUpdateTx(ctx, tx, guildID, memberGUID, tabID, column) + if err != nil { + return err + } + if limit != 0xFFFFFFFF && (used >= limit || amount > limit-used) { + return ErrGuildBankWithdrawLimit + } + + _, err = tx.ExecContext(ctx, "UPDATE guild_member_withdraw SET "+column+" = "+column+" + ? WHERE guid = ?", amount, memberGUID) + return err +} + +func decrementGuildBankWithdrawTx(ctx context.Context, tx *sql.Tx, memberGUID uint64, tabID uint8, amount uint32) error { + if tabID > GuildBankMaxTabs { + return ErrGuildBankInvalidTab + } + + column := "money" + if tabID < GuildBankMaxTabs { + column = "tab" + strconv.Itoa(int(tabID)) + } + + _, err := tx.ExecContext(ctx, "UPDATE guild_member_withdraw SET "+column+" = CASE WHEN "+column+" > ? THEN "+column+" - ? ELSE 0 END WHERE guid = ?", amount, amount, memberGUID) + return err +} + +func guildBankWithdrawLimitForUpdateTx(ctx context.Context, tx *sql.Tx, guildID uint64, memberGUID uint64, tabID uint8, column string) (uint32, uint32, error) { + if tabID == GuildBankMaxTabs { + var limit uint32 + var used uint32 + err := tx.QueryRowContext(ctx, ` +SELECT gr.BankMoneyPerDay, COALESCE(w.money, 0) +FROM guild_member gm +INNER JOIN guild_rank gr ON gr.guildid = gm.guildid AND gr.rid = gm.rank +LEFT JOIN guild_member_withdraw w ON w.guid = gm.guid +WHERE gm.guildid = ? AND gm.guid = ? +FOR UPDATE`, guildID, memberGUID).Scan(&limit, &used) + return limit, used, err + } + + var flags uint32 + var limit uint32 + var used uint32 + err := tx.QueryRowContext(ctx, ` +SELECT gbr.gbright, gbr.SlotPerDay, COALESCE(w.`+column+`, 0) +FROM guild_member gm +INNER JOIN guild_bank_right gbr ON gbr.guildid = gm.guildid AND gbr.rid = gm.rank AND gbr.TabId = ? +LEFT JOIN guild_member_withdraw w ON w.guid = gm.guid +WHERE gm.guildid = ? AND gm.guid = ? +FOR UPDATE`, tabID, guildID, memberGUID).Scan(&flags, &limit, &used) + if err != nil { + return 0, 0, err + } + if flags&GuildBankRightViewTab == 0 { + return 0, 0, ErrGuildBankWithdrawLimit + } + return limit, used, nil +} + +func insertGuildBankLogTx(ctx context.Context, tx *sql.Tx, guildID uint64, eventType GuildBankEventLogType, tabID uint8, playerGUID uint64, itemOrMoney uint32, itemStackCount uint16, destTabID uint8) error { + _, err := insertGuildBankLogReturningGUIDTx(ctx, tx, guildID, eventType, tabID, playerGUID, itemOrMoney, itemStackCount, destTabID) + return err +} + +func insertGuildBankLogReturningGUIDTx(ctx context.Context, tx *sql.Tx, guildID uint64, eventType GuildBankEventLogType, tabID uint8, playerGUID uint64, itemOrMoney uint32, itemStackCount uint16, destTabID uint8) (uint32, error) { + if err := lockGuildBankLogTx(ctx, tx, guildID); err != nil { + return 0, err + } + + var logGUID uint32 + err := tx.QueryRowContext(ctx, ` +SELECT COALESCE(MAX(LogGuid), 0) + 1 +FROM guild_bank_eventlog +WHERE guildid = ? AND TabId = ?`, guildID, tabID).Scan(&logGUID) + if err != nil { + return 0, err + } + + _, err = tx.ExecContext(ctx, ` +INSERT INTO guild_bank_eventlog (guildid, LogGuid, TabId, EventType, PlayerGuid, ItemOrMoney, ItemStackCount, DestTabId, TimeStamp) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, + guildID, logGUID, tabID, eventType, playerGUID, itemOrMoney, itemStackCount, destTabID, time.Now().Unix(), + ) + if err != nil { + return 0, err + } + return logGUID, nil +} + +func deleteGuildBankLogTx(ctx context.Context, tx *sql.Tx, guildID uint64, logGUID uint32, eventType GuildBankEventLogType, tabID uint8, playerGUID uint64, itemOrMoney uint32, itemStackCount uint16, destTabID uint8) (bool, error) { + if logGUID == 0 { + return false, nil + } + if err := lockGuildBankLogTx(ctx, tx, guildID); err != nil { + return false, err + } + + res, err := tx.ExecContext(ctx, ` +DELETE FROM guild_bank_eventlog +WHERE guildid = ? AND LogGuid = ? AND TabId = ? AND EventType = ? AND PlayerGuid = ? AND ItemOrMoney = ? AND ItemStackCount = ? AND DestTabId = ?`, + guildID, logGUID, tabID, eventType, playerGUID, itemOrMoney, itemStackCount, destTabID) + if err != nil { + return false, err + } + affected, err := res.RowsAffected() + if err != nil { + return false, err + } + return affected > 0, nil +} + +func lockGuildBankLogTx(ctx context.Context, tx *sql.Tx, guildID uint64) error { + var found uint64 + return tx.QueryRowContext(ctx, ` +SELECT guildid +FROM guild +WHERE guildid = ? +FOR UPDATE`, guildID).Scan(&found) +} diff --git a/apps/guildserver/service/guild_bank_guid.go b/apps/guildserver/service/guild_bank_guid.go new file mode 100644 index 0000000..e967bca --- /dev/null +++ b/apps/guildserver/service/guild_bank_guid.go @@ -0,0 +1,41 @@ +package service + +import ( + "context" + "errors" + "fmt" + + "github.com/walkline/ToCloud9/apps/guildserver" + guidpb "github.com/walkline/ToCloud9/gen/guid/pb" +) + +var errGuildBankItemGUIDAllocatorMissing = errors.New("guild bank item GUID allocator missing") + +// ItemGUIDAllocator allocates native item_instance GUIDs for DB-side stack splits. +type ItemGUIDAllocator interface { + NextItemGUID(ctx context.Context, realmID uint32) (uint64, error) +} + +type guidServiceItemGUIDAllocator struct { + client guidpb.GuidServiceClient +} + +func NewGuidServiceItemGUIDAllocator(client guidpb.GuidServiceClient) ItemGUIDAllocator { + return &guidServiceItemGUIDAllocator{client: client} +} + +func (a *guidServiceItemGUIDAllocator) NextItemGUID(ctx context.Context, realmID uint32) (uint64, error) { + resp, err := a.client.GetGUIDPool(ctx, &guidpb.GetGUIDPoolRequest{ + Api: guildserver.Ver, + RealmID: realmID, + GuidType: guidpb.GuidType_Item, + DesiredPoolSize: 1, + }) + if err != nil { + return 0, fmt.Errorf("get item GUID pool: %w", err) + } + if len(resp.GetReceiverGUID()) == 0 || resp.GetReceiverGUID()[0].GetStart() == 0 || resp.GetReceiverGUID()[0].GetStart() > resp.GetReceiverGUID()[0].GetEnd() { + return 0, errors.New("guid service returned empty item GUID pool") + } + return resp.GetReceiverGUID()[0].GetStart(), nil +} From 03c985b321699703097c4dc8403faf6f04d9c53e Mon Sep 17 00:00:00 2001 From: VG-Prog Date: Fri, 22 May 2026 21:48:56 +0200 Subject: [PATCH 09/11] feat(Cluster/Matchmaking): Add crossrealm LFG and arenas Extend matchmaking and mysqlreverseproxy for crossrealm LFG proposal materialization, persistent instance routes, battleground owner placement, arena-team persistence and rated-result handling, character-state listeners, and route-aware SQL interception. --- .../battleground/battleground.go | 12 +- .../battleground/players-group.go | 8 + .../cmd/matchmakingserver/main.go | 88 +- apps/matchmakingserver/config/config.go | 34 + apps/matchmakingserver/repo/arena-team.go | 239 +++ .../server/matchmaking-logger_debug.go | 89 + apps/matchmakingserver/server/matchmaking.go | 281 ++- .../service/arena_match_result.go | 477 +++++ .../service/battleground-queue.go | 172 +- .../service/battleground_service.go | 301 ++- .../service/characters-listener.go | 140 +- apps/matchmakingserver/service/lfg.go | 1852 +++++++++++++++++ .../service/lfg_materializer.go | 324 +++ .../service/lfg_materializer_listener.go | 95 + apps/mysqlreverseproxy/proxy/proxy.go | 487 ++++- apps/mysqlreverseproxy/sqlparser/parser.go | 112 +- 16 files changed, 4597 insertions(+), 114 deletions(-) create mode 100644 apps/matchmakingserver/repo/arena-team.go create mode 100644 apps/matchmakingserver/service/arena_match_result.go create mode 100644 apps/matchmakingserver/service/lfg.go create mode 100644 apps/matchmakingserver/service/lfg_materializer.go create mode 100644 apps/matchmakingserver/service/lfg_materializer_listener.go diff --git a/apps/matchmakingserver/battleground/battleground.go b/apps/matchmakingserver/battleground/battleground.go index 6a60a57..c42c2f0 100644 --- a/apps/matchmakingserver/battleground/battleground.go +++ b/apps/matchmakingserver/battleground/battleground.go @@ -109,6 +109,7 @@ func (b *Battleground) InviteGroups(eventsProducer events.MatchmakingServiceProd for realm, queuedGroups := range groupsByRealm { players := make([]guid.LowType, 0, b.MaxPlayersPerTeam) slots := map[guid.LowType]uint8{} + arenaType, isRated := pvpOptionsForQueuedGroups(queuedGroups) for _, group := range queuedGroups { players = append(players, group.LeaderGUID.LowGUID) for _, member := range group.Members { @@ -131,8 +132,8 @@ func (b *Battleground) InviteGroups(eventsProducer events.MatchmakingServiceProd RealmID: realm, PlayersGUID: players, QueueSlotByPlayer: slots, - ArenaType: 0, // TODO: implement later - IsRated: false, // TODO: implement later + ArenaType: arenaType, + IsRated: isRated, PVPQueueMinLVL: minLvl, PVPQueueMaxLVL: maxLvl, TypeID: uint8(b.QueueTypeID), @@ -148,6 +149,13 @@ func (b *Battleground) InviteGroups(eventsProducer events.MatchmakingServiceProd return nil } +func pvpOptionsForQueuedGroups(groups []QueuedGroup) (uint8, bool) { + if len(groups) == 0 { + return 0, false + } + return groups[0].ArenaType, groups[0].IsRated +} + func (b *Battleground) TeamForInvitedPlayer(playerGUID uint64, realmID uint32) (found bool, team PVPTeam) { unwrappedGUID := guid.PlayerUnwrapped{ RealmID: uint16(realmID), diff --git a/apps/matchmakingserver/battleground/players-group.go b/apps/matchmakingserver/battleground/players-group.go index 05d46c3..fc89f43 100644 --- a/apps/matchmakingserver/battleground/players-group.go +++ b/apps/matchmakingserver/battleground/players-group.go @@ -16,5 +16,13 @@ type QueuedGroup struct { RealmID uint32 TeamID PVPTeam + ArenaType uint8 + IsRated bool + + ArenaTeamID uint32 + ArenaTeamRating uint32 + ArenaMatchmakerRating uint32 + ArenaPreviousOpponentsTeamID uint32 + EnqueuedTime time.Time } diff --git a/apps/matchmakingserver/cmd/matchmakingserver/main.go b/apps/matchmakingserver/cmd/matchmakingserver/main.go index fb9f827..b45a5cd 100644 --- a/apps/matchmakingserver/cmd/matchmakingserver/main.go +++ b/apps/matchmakingserver/cmd/matchmakingserver/main.go @@ -5,6 +5,7 @@ import ( "database/sql" "fmt" "net" + "net/http" "os" "os/signal" "sync" @@ -22,11 +23,13 @@ import ( "github.com/walkline/ToCloud9/apps/matchmakingserver/repo" "github.com/walkline/ToCloud9/apps/matchmakingserver/server" "github.com/walkline/ToCloud9/apps/matchmakingserver/service" + pbChar "github.com/walkline/ToCloud9/gen/characters/pb" + pbGroup "github.com/walkline/ToCloud9/gen/group/pb" "github.com/walkline/ToCloud9/gen/matchmaking/pb" pbServ "github.com/walkline/ToCloud9/gen/servers-registry/pb" "github.com/walkline/ToCloud9/shared/events" "github.com/walkline/ToCloud9/shared/gameserver/conn" - shrepo "github.com/walkline/ToCloud9/shared/repo" + "github.com/walkline/ToCloud9/shared/healthandmetrics" ) func main() { @@ -39,22 +42,25 @@ func main() { log.Logger = cfg.Logger() + healthCheckServer := healthandmetrics.NewServer(cfg.HealthCheckPort, nil) + go func() { + if err := healthCheckServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatal().Err(err).Msg("can't start health check server") + } + }() + defer func() { + if err := healthCheckServer.Shutdown(context.Background()); err != nil { + log.Error().Err(err).Msg("can't shutdown health check server") + } + }() + wdb, err := sql.Open("mysql", cfg.WorldDBConnection) if err != nil { log.Fatal().Err(err).Msg("can't connect to world db") } - charDB := shrepo.NewCharactersDB() - realmIDs := []uint32{} - for realmID, charDBConn := range cfg.CharDBConnection { - cdb, err := sql.Open("mysql", charDBConn) - if err != nil { - log.Fatal().Err(err).Msg("can't connect to char db") - } - - charDB.SetDBForRealm(realmID, cdb) - + for realmID := range cfg.CharDBConnection { realmIDs = append(realmIDs, realmID) } @@ -71,6 +77,8 @@ func main() { defer nc.Close() serversRegistryClient := servRegistryService(cfg) + registerMatchmakingServer(serversRegistryClient, cfg, healthCheckServer.StartedAtUnixMs()) + battlegroupsRepo, err := repo.NewBattleGroupsInMemWithConfigValue(cfg.BattleGroups) if err != nil { log.Fatal().Err(err).Msg("can't create BattleGroupsInMem repository") @@ -84,19 +92,32 @@ func main() { gameserverConnMgr := conn.NewGameServerGRPCConnMgr() bgService, err := service.NewBattleGroundService( repo.NewMySQLBattlegroundTemplateRepo(wdb), + repo.NewCharserverArenaTeamRepo(charService(cfg)), battlegroupsRepo, repo.NewBattlegroundInMemRepo(), crossRealmTracker, events.NewMatchmakingServiceProducerNatsJSON(nc, matchmaking.Ver), serversRegistryClient, gameserverConnMgr, + cfg.ArenaStartMatchmakerRating, + service.ArenaRatingConfig{ + WinModifier1: float64(cfg.ArenaWinRatingModifier1), + WinModifier2: float64(cfg.ArenaWinRatingModifier2), + LoseModifier: float64(cfg.ArenaLoseRatingModifier), + MatchmakerModifier: float64(cfg.ArenaMatchmakerRatingModifier), + MaxAllowedMMRDrop: cfg.MaxAllowedMMRDrop, + }, realmIDs, ) if err != nil { log.Fatal().Err(err).Msg("can't create BattleGroundService service") } + matchmakingEventsProducer := events.NewMatchmakingServiceProducerNatsJSON(nc, matchmaking.Ver) + groupServiceClient := groupService(cfg) + lfgService := service.NewLFGServiceWithBattleGroupsAndGroupRegistrar(battlegroupsRepo, crossRealmTracker, groupServiceClient, matchmakingEventsProducer) + lfgMaterializer := service.NewLFGMaterializer(serversRegistryClient, gameserverConnMgr, groupServiceClient) - charLis := service.NewCharactersListener(bgService, nc) + charLis := service.NewCharactersListener(bgService, lfgService, nc) if err = charLis.Listen(); err != nil { log.Fatal().Err(err).Msg("can't start char listener") } @@ -112,19 +133,26 @@ func main() { } defer registryList.Stop() + lfgMaterializerListener := service.NewLFGMaterializerListener(nc, lfgMaterializer, lfgService) + if err = lfgMaterializerListener.Listen(); err != nil { + log.Fatal().Err(err).Msg("can't start LFG materializer listener") + } + defer lfgMaterializerListener.Stop() + lis, err := net.Listen("tcp4", ":"+cfg.Port) if err != nil { log.Fatal().Err(err).Msg("can't listen for incoming connections") } grpcServer := grpc.NewServer() - matchmakingServer := server.NewMatchmakingServer(bgService, gameserverConnMgr) + matchmakingServer := server.NewMatchmakingServer(bgService, lfgService, gameserverConnMgr) if cfg.LogLevel == zerolog.DebugLevel { matchmakingServer = server.NewMatchmakingDebugLoggerMiddleware(matchmakingServer, log.Logger) } pb.RegisterMatchmakingServiceServer(grpcServer, matchmakingServer) go bgService.ProcessExpiredBattlegroundInvites(ctx) + go lfgService.ProcessExpiredLfgProposals(ctx) // graceful shutdown handling sigCh := make(chan os.Signal, 1) @@ -159,3 +187,37 @@ func servRegistryService(cnf *config.Config) pbServ.ServersRegistryServiceClient return pbServ.NewServersRegistryServiceClient(conn) } + +func registerMatchmakingServer(client pbServ.ServersRegistryServiceClient, cnf *config.Config, startedAtUnixMs int64) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + _, err := client.RegisterMatchmakingServer(ctx, &pbServ.RegisterMatchmakingServerRequest{ + Api: matchmaking.SupportedServerRegistryVer, + ServicePort: uint32(cnf.PortInt()), + HealthPort: uint32(cnf.HealthCheckPortInt()), + PreferredHostName: cnf.PreferredHostname, + StartedAtUnixMs: startedAtUnixMs, + }) + if err != nil { + log.Fatal().Err(err).Msg("can't register matchmaking server") + } +} + +func groupService(cnf *config.Config) pbGroup.GroupServiceClient { + conn, err := grpc.Dial(cnf.GroupServiceAddress, grpc.WithInsecure()) + if err != nil { + log.Fatal().Err(err).Msg("can't connect to group service") + } + + return pbGroup.NewGroupServiceClient(conn) +} + +func charService(cnf *config.Config) pbChar.CharactersServiceClient { + conn, err := grpc.Dial(cnf.CharServiceAddress, grpc.WithInsecure()) + if err != nil { + log.Fatal().Err(err).Msg("can't connect to character service") + } + + return pbChar.NewCharactersServiceClient(conn) +} diff --git a/apps/matchmakingserver/config/config.go b/apps/matchmakingserver/config/config.go index 2f8761c..882b3d9 100644 --- a/apps/matchmakingserver/config/config.go +++ b/apps/matchmakingserver/config/config.go @@ -1,6 +1,8 @@ package config import ( + "strconv" + "github.com/walkline/ToCloud9/shared/config" ) @@ -11,6 +13,12 @@ type Config struct { // Port is port that would be used for grpc server Port string `yaml:"port" env:"PORT" env-default:"8994"` + // HealthCheckPort is port that would be used to listen for health checks. + HealthCheckPort string `yaml:"healthCheckPort" env:"HEALTH_CHECK_PORT" env-default:"8904"` + + // PreferredHostname is host name that servers registry should use to reach this service. + PreferredHostname string `yaml:"preferredHostname" env:"PREFERRED_HOSTNAME"` + // CharDBConnection is connection string to the characters database CharDBConnection map[uint32]string `yaml:"charactersDB" env:"CHAR_DB_CONNECTION" env-separator:";" env-default:"1:trinity:trinity@tcp(127.0.0.1:3306)/characters"` @@ -23,6 +31,12 @@ type Config struct { // ServersRegistryServiceAddress is address of servers registry service ServersRegistryServiceAddress string `yaml:"serversRegistryServiceAddress" env:"SERVERS_REGISTRY_SERVICE_ADDRESS" env-default:"localhost:8999"` + // GroupServiceAddress is address of group service. + GroupServiceAddress string `yaml:"groupServiceAddress" env:"GROUP_SERVICE_ADDRESS" env-default:"localhost:8998"` + + // CharServiceAddress is address of characters service. + CharServiceAddress string `yaml:"charactersServiceAddress" env:"CHAR_SERVICE_ADDRESS" env-default:"localhost:8991"` + // BattleGroups are unions of realms that can participate in PvP between each other (such as battlegrounds). // If you don't want to have cross-realm - simply leave it empty. // To create a BattleGroup simply set some id for battle group and specify realms ids, example: @@ -33,6 +47,26 @@ type Config struct { // Battle group 1 consist from realms with ID 1 and 2. // Battle group 2 consist from realms with ID 3 and 4. BattleGroups map[uint32]string `yaml:"battleGroups" env:"BATTLE_GROUPS" env-separator:";" env-default:"1:1"` + + // ArenaStartMatchmakerRating mirrors AzerothCore Arena.ArenaStartMatchmakerRating. + ArenaStartMatchmakerRating uint32 `yaml:"arenaStartMatchmakerRating" env:"ARENA_START_MATCHMAKER_RATING" env-default:"1500"` + + // Arena rating calculation modifiers mirror AzerothCore Arena.*RatingModifier values. + ArenaWinRatingModifier1 float32 `yaml:"arenaWinRatingModifier1" env:"ARENA_WIN_RATING_MODIFIER_1" env-default:"48"` + ArenaWinRatingModifier2 float32 `yaml:"arenaWinRatingModifier2" env:"ARENA_WIN_RATING_MODIFIER_2" env-default:"24"` + ArenaLoseRatingModifier float32 `yaml:"arenaLoseRatingModifier" env:"ARENA_LOSE_RATING_MODIFIER" env-default:"24"` + ArenaMatchmakerRatingModifier float32 `yaml:"arenaMatchmakerRatingModifier" env:"ARENA_MATCHMAKER_RATING_MODIFIER" env-default:"24"` + MaxAllowedMMRDrop uint32 `yaml:"maxAllowedMMRDrop" env:"MAX_ALLOWED_MMR_DROP" env-default:"500"` +} + +func (c Config) PortInt() (p int) { + p, _ = strconv.Atoi(c.Port) + return +} + +func (c Config) HealthCheckPortInt() (p int) { + p, _ = strconv.Atoi(c.HealthCheckPort) + return } // LoadConfig loads config from env variables diff --git a/apps/matchmakingserver/repo/arena-team.go b/apps/matchmakingserver/repo/arena-team.go new file mode 100644 index 0000000..735c6da --- /dev/null +++ b/apps/matchmakingserver/repo/arena-team.go @@ -0,0 +1,239 @@ +package repo + +import ( + "context" + "errors" + "fmt" + + charserver "github.com/walkline/ToCloud9/apps/charserver" + pbChar "github.com/walkline/ToCloud9/gen/characters/pb" +) + +var ( + ErrArenaTeamNotFound = errors.New("arena team not found") + ErrArenaTeamMemberMismatch = errors.New("arena team member mismatch") + ErrArenaTeamPartySize = errors.New("invalid arena team party size") + ErrArenaTeamInvalidType = errors.New("invalid arena team type") +) + +type ArenaTeamQueueData struct { + ArenaTeamID uint32 + TeamRating uint32 + MatchmakerRating uint32 + PreviousOpponentsTeamID uint32 +} + +type ArenaTeamSaveStatsRequest struct { + RealmID uint32 + ArenaTeamID uint32 + Rating uint32 + WeekGames uint32 + WeekWins uint32 + SeasonGames uint32 + SeasonWins uint32 + Rank uint32 + Slot uint32 + Members []ArenaTeamSaveStatsMember +} + +type ArenaTeamDetails struct { + RealmID uint32 + ArenaTeamID uint32 + Name string + Type uint8 + Rating uint32 + WeekGames uint32 + WeekWins uint32 + SeasonGames uint32 + SeasonWins uint32 + Rank uint32 + Members []ArenaTeamDetailsMember +} + +type ArenaTeamDetailsMember struct { + PlayerGUID uint64 + PersonalRating uint32 + WeekGames uint32 + WeekWins uint32 + SeasonGames uint32 + SeasonWins uint32 + MatchmakerRating uint32 + MaxMMR uint32 +} + +type ArenaTeamSaveStatsMember struct { + PlayerGUID uint64 + PersonalRating uint32 + WeekGames uint32 + WeekWins uint32 + SeasonGames uint32 + SeasonWins uint32 + MatchmakerRating uint32 + MaxMMR uint32 + SaveArenaStats bool +} + +type ArenaTeamRepository interface { + QueueDataForRatedArena(ctx context.Context, realmID uint32, leaderGUID uint64, playerGUIDs []uint64, arenaType uint8, startMatchmakerRating uint32) (*ArenaTeamQueueData, error) + GetTeam(ctx context.Context, realmID uint32, arenaTeamID uint32) (*ArenaTeamDetails, error) + SaveStats(ctx context.Context, request ArenaTeamSaveStatsRequest) error +} + +type charserverArenaTeamRepo struct { + client pbChar.CharactersServiceClient +} + +func NewCharserverArenaTeamRepo(client pbChar.CharactersServiceClient) ArenaTeamRepository { + return &charserverArenaTeamRepo{client: client} +} + +func (r *charserverArenaTeamRepo) QueueDataForRatedArena(ctx context.Context, realmID uint32, leaderGUID uint64, playerGUIDs []uint64, arenaType uint8, startMatchmakerRating uint32) (*ArenaTeamQueueData, error) { + if r.client == nil { + return nil, errors.New("characters service client is nil") + } + + resp, err := r.client.ArenaTeamQueueDataForRatedArena(ctx, &pbChar.ArenaTeamQueueDataForRatedArenaRequest{ + Api: charserver.Ver, + RealmID: realmID, + LeaderGUID: leaderGUID, + PlayerGUIDs: playerGUIDs, + ArenaType: uint32(arenaType), + StartMatchmakerRating: startMatchmakerRating, + }) + if err != nil { + return nil, err + } + if resp == nil { + return nil, errors.New("characters service returned nil arena team queue data response") + } + + switch resp.GetStatus() { + case pbChar.ArenaTeamQueueDataForRatedArenaResponse_Ok: + return &ArenaTeamQueueData{ + ArenaTeamID: resp.GetArenaTeamID(), + TeamRating: resp.GetTeamRating(), + MatchmakerRating: resp.GetMatchmakerRating(), + PreviousOpponentsTeamID: resp.GetPreviousOpponentsTeamID(), + }, nil + case pbChar.ArenaTeamQueueDataForRatedArenaResponse_NotFound: + return nil, ErrArenaTeamNotFound + case pbChar.ArenaTeamQueueDataForRatedArenaResponse_MemberMismatch: + return nil, ErrArenaTeamMemberMismatch + case pbChar.ArenaTeamQueueDataForRatedArenaResponse_InvalidPartySize: + return nil, ErrArenaTeamPartySize + case pbChar.ArenaTeamQueueDataForRatedArenaResponse_InvalidType: + return nil, ErrArenaTeamInvalidType + default: + return nil, fmt.Errorf("characters service failed arena team queue lookup with status %s", resp.GetStatus().String()) + } +} + +func (r *charserverArenaTeamRepo) GetTeam(ctx context.Context, realmID uint32, arenaTeamID uint32) (*ArenaTeamDetails, error) { + if r.client == nil { + return nil, errors.New("characters service client is nil") + } + + resp, err := r.client.GetArenaTeam(ctx, &pbChar.GetArenaTeamRequest{ + Api: charserver.Ver, + RealmID: realmID, + ArenaTeamID: arenaTeamID, + }) + if err != nil { + return nil, err + } + if resp == nil { + return nil, errors.New("characters service returned nil arena team response") + } + + switch resp.GetStatus() { + case pbChar.GetArenaTeamResponse_Ok: + team := resp.GetTeam() + if team == nil { + return nil, ErrArenaTeamNotFound + } + details := &ArenaTeamDetails{ + RealmID: realmID, + ArenaTeamID: team.GetArenaTeamID(), + Name: team.GetName(), + Type: uint8(team.GetType()), + Rating: team.GetRating(), + WeekGames: team.GetWeekGames(), + WeekWins: team.GetWeekWins(), + SeasonGames: team.GetSeasonGames(), + SeasonWins: team.GetSeasonWins(), + Rank: team.GetRank(), + Members: make([]ArenaTeamDetailsMember, 0, len(team.GetMembers())), + } + for _, member := range team.GetMembers() { + details.Members = append(details.Members, ArenaTeamDetailsMember{ + PlayerGUID: member.GetPlayerGUID(), + PersonalRating: member.GetPersonalRating(), + WeekGames: member.GetWeekGames(), + WeekWins: member.GetWeekWins(), + SeasonGames: member.GetSeasonGames(), + SeasonWins: member.GetSeasonWins(), + MatchmakerRating: member.GetMatchmakerRating(), + MaxMMR: member.GetMaxMMR(), + }) + } + return details, nil + case pbChar.GetArenaTeamResponse_NotFound: + return nil, ErrArenaTeamNotFound + default: + return nil, fmt.Errorf("characters service failed arena team lookup with status %s", resp.GetStatus().String()) + } +} + +func (r *charserverArenaTeamRepo) SaveStats(ctx context.Context, request ArenaTeamSaveStatsRequest) error { + if r.client == nil { + return errors.New("characters service client is nil") + } + + members := make([]*pbChar.ArenaTeamStatsMember, 0, len(request.Members)) + for _, member := range request.Members { + members = append(members, &pbChar.ArenaTeamStatsMember{ + PlayerGUID: member.PlayerGUID, + PersonalRating: member.PersonalRating, + WeekGames: member.WeekGames, + WeekWins: member.WeekWins, + SeasonGames: member.SeasonGames, + SeasonWins: member.SeasonWins, + MatchmakerRating: member.MatchmakerRating, + MaxMMR: member.MaxMMR, + SaveArenaStats: member.SaveArenaStats, + }) + } + + resp, err := r.client.SaveArenaTeamStats(ctx, &pbChar.SaveArenaTeamStatsRequest{ + Api: charserver.Ver, + RealmID: request.RealmID, + ArenaTeamID: request.ArenaTeamID, + Rating: request.Rating, + WeekGames: request.WeekGames, + WeekWins: request.WeekWins, + SeasonGames: request.SeasonGames, + SeasonWins: request.SeasonWins, + Rank: request.Rank, + Slot: request.Slot, + Members: members, + }) + if err != nil { + return err + } + if resp == nil { + return errors.New("characters service returned nil arena team stats response") + } + + switch resp.GetStatus() { + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_OK: + return nil + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_NOT_FOUND: + return ErrArenaTeamNotFound + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_MEMBER_MISMATCH: + return ErrArenaTeamMemberMismatch + case pbChar.ArenaTeamMutationStatus_ARENA_TEAM_MUTATION_INVALID_TYPE: + return ErrArenaTeamInvalidType + default: + return fmt.Errorf("characters service failed arena team stats save with status %s", resp.GetStatus().String()) + } +} diff --git a/apps/matchmakingserver/server/matchmaking-logger_debug.go b/apps/matchmakingserver/server/matchmaking-logger_debug.go index b7774e1..23cab39 100644 --- a/apps/matchmakingserver/server/matchmaking-logger_debug.go +++ b/apps/matchmakingserver/server/matchmaking-logger_debug.go @@ -95,3 +95,92 @@ func (m *matchmakingDebugLoggerMiddleware) BattlegroundStatusChanged(ctx context res, err = m.mmServer.BattlegroundStatusChanged(ctx, params) return } + +func (m *matchmakingDebugLoggerMiddleware) FinishRatedArenaMatch(ctx context.Context, params *pb.FinishRatedArenaMatchRequest) (res *pb.FinishRatedArenaMatchResponse, err error) { + defer func(t time.Time) { + m.logger.Debug(). + Interface("payload", params). + Interface("res", res). + Err(err). + Msgf("Handled FinishRatedArenaMatch for %v.", time.Since(t)) + }(time.Now()) + + res, err = m.mmServer.FinishRatedArenaMatch(ctx, params) + return +} + +func (m *matchmakingDebugLoggerMiddleware) JoinLfg(ctx context.Context, params *pb.JoinLfgRequest) (res *pb.JoinLfgResponse, err error) { + defer func(t time.Time) { + m.logger.Debug(). + Interface("payload", params). + Interface("res", res). + Err(err). + Msgf("Handled JoinLfg for %v.", time.Since(t)) + }(time.Now()) + + res, err = m.mmServer.JoinLfg(ctx, params) + return +} + +func (m *matchmakingDebugLoggerMiddleware) LeaveLfg(ctx context.Context, params *pb.LeaveLfgRequest) (res *pb.LeaveLfgResponse, err error) { + defer func(t time.Time) { + m.logger.Debug(). + Interface("payload", params). + Err(err). + Msgf("Handled LeaveLfg for %v.", time.Since(t)) + }(time.Now()) + + res, err = m.mmServer.LeaveLfg(ctx, params) + return +} + +func (m *matchmakingDebugLoggerMiddleware) SetLfgRoles(ctx context.Context, params *pb.SetLfgRolesRequest) (res *pb.SetLfgRolesResponse, err error) { + defer func(t time.Time) { + m.logger.Debug(). + Interface("payload", params). + Interface("res", res). + Err(err). + Msgf("Handled SetLfgRoles for %v.", time.Since(t)) + }(time.Now()) + + res, err = m.mmServer.SetLfgRoles(ctx, params) + return +} + +func (m *matchmakingDebugLoggerMiddleware) AnswerLfgProposal(ctx context.Context, params *pb.AnswerLfgProposalRequest) (res *pb.AnswerLfgProposalResponse, err error) { + defer func(t time.Time) { + m.logger.Debug(). + Interface("payload", params). + Interface("res", res). + Err(err). + Msgf("Handled AnswerLfgProposal for %v.", time.Since(t)) + }(time.Now()) + + res, err = m.mmServer.AnswerLfgProposal(ctx, params) + return +} + +func (m *matchmakingDebugLoggerMiddleware) LfgStatus(ctx context.Context, params *pb.LfgStatusRequest) (res *pb.LfgStatusResponse, err error) { + defer func(t time.Time) { + m.logger.Debug(). + Interface("payload", params). + Interface("res", res). + Err(err). + Msgf("Handled LfgStatus for %v.", time.Since(t)) + }(time.Now()) + + res, err = m.mmServer.LfgStatus(ctx, params) + return +} + +func (m *matchmakingDebugLoggerMiddleware) CompleteLfgDungeon(ctx context.Context, params *pb.CompleteLfgDungeonRequest) (res *pb.CompleteLfgDungeonResponse, err error) { + defer func(t time.Time) { + m.logger.Debug(). + Interface("payload", params). + Err(err). + Msgf("Handled CompleteLfgDungeon for %v.", time.Since(t)) + }(time.Now()) + + res, err = m.mmServer.CompleteLfgDungeon(ctx, params) + return +} diff --git a/apps/matchmakingserver/server/matchmaking.go b/apps/matchmakingserver/server/matchmaking.go index f3b9e5e..06ac897 100644 --- a/apps/matchmakingserver/server/matchmaking.go +++ b/apps/matchmakingserver/server/matchmaking.go @@ -2,6 +2,7 @@ package server import ( "context" + "errors" matchmaking "github.com/walkline/ToCloud9/apps/matchmakingserver" "github.com/walkline/ToCloud9/apps/matchmakingserver/battleground" @@ -16,18 +17,20 @@ type MatchmakingServer struct { pb.UnimplementedMatchmakingServiceServer bgService service.BattleGroundService + lfgService service.LFGService grpcConnMgr conn.GameServerGRPCConnMgr } -func NewMatchmakingServer(bgService service.BattleGroundService, grpcConnMgr conn.GameServerGRPCConnMgr) pb.MatchmakingServiceServer { +func NewMatchmakingServer(bgService service.BattleGroundService, lfgService service.LFGService, grpcConnMgr conn.GameServerGRPCConnMgr) pb.MatchmakingServiceServer { return &MatchmakingServer{ bgService: bgService, + lfgService: lfgService, grpcConnMgr: grpcConnMgr, } } func (s *MatchmakingServer) EnqueueToBattleground(ctx context.Context, req *pb.EnqueueToBattlegroundRequest) (*pb.EnqueueToBattlegroundResponse, error) { - err := s.bgService.AddGroupToQueue(ctx, req.RealmID, req.LeaderGUID, req.PartyMembers, battleground.QueueTypeID(req.BgTypeID), uint8(req.LeadersLvl), battleground.PVPTeam(req.TeamID)) + err := s.bgService.AddGroupToQueue(ctx, req.RealmID, req.LeaderGUID, req.PartyMembers, battleground.QueueTypeID(req.BgTypeID), uint8(req.LeadersLvl), battleground.PVPTeam(req.TeamID), uint8(req.ArenaType), req.IsRated) if err != nil { return nil, err } @@ -48,7 +51,7 @@ func (s *MatchmakingServer) RemovePlayerFromQueue(ctx context.Context, req *pb.R func (s *MatchmakingServer) BattlegroundQueueDataForPlayer(ctx context.Context, req *pb.BattlegroundQueueDataForPlayerRequest) (*pb.BattlegroundQueueDataForPlayerResponse, error) { links := s.bgService.GetQueueOrBattlegroundLinkForPlayer(service.QueuesByRealmAndPlayerKey{ - guid.PlayerUnwrapped{ + PlayerUnwrapped: guid.PlayerUnwrapped{ RealmID: uint16(req.RealmID), LowGUID: guid.LowType(req.PlayerGUID), }, @@ -64,6 +67,7 @@ func (s *MatchmakingServer) BattlegroundQueueDataForPlayer(ctx context.Context, if err != nil { return nil, err } + _, assignedTeam := bg.TeamForInvitedPlayer(req.PlayerGUID, req.RealmID) slots[i] = &pb.BattlegroundQueueDataForPlayerResponse_QueueSlot{ BgTypeID: uint32(bg.BattlegroundTypeID), Status: pb.PlayerQueueStatus_Invited, @@ -73,6 +77,7 @@ func (s *MatchmakingServer) BattlegroundQueueDataForPlayer(ctx context.Context, BattlegroupID: bg.BattleGroupID, GameserverAddress: bg.GameserverAddress, GameserverGRPCAddress: s.grpcConnMgr.GRPCAddressForGameServer(bg.GameserverAddress), + AssignedTeamID: pb.PVPTeamID(assignedTeam), }, } } else { @@ -121,3 +126,273 @@ func (s *MatchmakingServer) BattlegroundStatusChanged(ctx context.Context, reque Api: matchmaking.Ver, }, nil } + +func (s *MatchmakingServer) FinishRatedArenaMatch(ctx context.Context, request *pb.FinishRatedArenaMatchRequest) (*pb.FinishRatedArenaMatchResponse, error) { + participants := make([]service.RatedArenaParticipant, 0, len(request.GetParticipants())) + for _, participant := range request.GetParticipants() { + participants = append(participants, service.RatedArenaParticipant{ + Team: battlegroundTeamFromProto(participant.GetTeam()), + PlayerGUID: participant.GetPlayerGUID(), + }) + } + + result, err := s.bgService.FinishRatedArenaMatch(ctx, service.RatedArenaMatchResultRequest{ + OwnerRealmID: request.GetOwnerRealmID(), + IsCrossRealm: request.GetIsCrossRealm(), + InstanceID: request.GetInstanceID(), + ArenaType: uint8(request.GetArenaType()), + WinnerTeam: battlegroundTeamFromProto(request.GetWinnerTeam()), + ValidArena: request.GetValidArena(), + AllianceArenaTeamID: request.GetAllianceArenaTeamID(), + HordeArenaTeamID: request.GetHordeArenaTeamID(), + AllianceArenaMatchmakerRating: request.GetAllianceArenaMatchmakerRating(), + HordeArenaMatchmakerRating: request.GetHordeArenaMatchmakerRating(), + Participants: participants, + }) + if err != nil { + return &pb.FinishRatedArenaMatchResponse{ + Api: matchmaking.Ver, + Status: arenaTeamMutationStatusForError(err), + }, nil + } + + memberResults := make([]*pb.RatedArenaMemberResult, 0, len(result.MemberResults)) + for _, member := range result.MemberResults { + memberResults = append(memberResults, &pb.RatedArenaMemberResult{ + Team: protoPVPTeam(member.Team), + PlayerGUID: member.PlayerGUID, + PersonalRating: member.PersonalRating, + WeekGames: member.WeekGames, + SeasonGames: member.SeasonGames, + WeekWins: member.WeekWins, + SeasonWins: member.SeasonWins, + MatchmakerRating: member.MatchmakerRating, + }) + } + + return &pb.FinishRatedArenaMatchResponse{ + Api: matchmaking.Ver, + Status: pb.MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_OK, + AllianceScore: ratedArenaScoreToProto(result.AllianceScore), + HordeScore: ratedArenaScoreToProto(result.HordeScore), + MemberResults: memberResults, + }, nil +} + +func battlegroundTeamFromProto(team pb.PVPTeamID) battleground.PVPTeam { + switch team { + case pb.PVPTeamID_Alliance: + return battleground.TeamAlliance + case pb.PVPTeamID_Horde: + return battleground.TeamHorde + default: + return battleground.TeamAny + } +} + +func protoPVPTeam(team battleground.PVPTeam) pb.PVPTeamID { + switch team { + case battleground.TeamAlliance: + return pb.PVPTeamID_Alliance + case battleground.TeamHorde: + return pb.PVPTeamID_Horde + default: + return pb.PVPTeamID_Any + } +} + +func ratedArenaScoreToProto(score service.RatedArenaTeamScore) *pb.RatedArenaTeamScore { + return &pb.RatedArenaTeamScore{ + RealmID: score.RealmID, + ArenaTeamID: score.ArenaTeamID, + TeamName: score.TeamName, + RatingChange: score.RatingChange, + MatchmakerRating: score.MatchmakerRating, + } +} + +func arenaTeamMutationStatusForError(err error) pb.MatchmakingArenaTeamMutationStatus { + switch { + case errors.Is(err, repo.ErrArenaTeamNotFound): + return pb.MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_NOT_FOUND + case errors.Is(err, repo.ErrArenaTeamMemberMismatch): + return pb.MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_MEMBER_MISMATCH + case errors.Is(err, repo.ErrArenaTeamInvalidType): + return pb.MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_INVALID_TYPE + case errors.Is(err, service.ErrInvalidArenaType): + return pb.MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_INVALID_TYPE + default: + return pb.MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_FAILED + } +} + +func (s *MatchmakingServer) JoinLfg(ctx context.Context, req *pb.JoinLfgRequest) (*pb.JoinLfgResponse, error) { + members := make([]service.LFGMember, 0, len(req.Members)) + for _, member := range req.Members { + realmID := member.RealmID + if realmID == 0 { + realmID = req.RealmID + } + members = append(members, service.LFGMember{ + RealmID: realmID, + PlayerGUID: member.PlayerGUID, + Roles: uint8(member.Roles), + Leader: member.Leader, + WorldserverID: member.WorldserverID, + }) + } + + status, err := s.lfgService.JoinLfg(ctx, service.LFGJoinData{ + RealmID: req.RealmID, + LeaderGUID: req.LeaderGUID, + Members: members, + DungeonEntries: req.DungeonEntries, + Comment: req.Comment, + }) + if err != nil { + return &pb.JoinLfgResponse{ + Api: matchmaking.Ver, + Result: lfgJoinResultForError(err), + Status: lfgStatusToProto(&service.LFGStatus{State: service.LFGStateNone}), + }, nil + } + + return &pb.JoinLfgResponse{ + Api: matchmaking.Ver, + Result: pb.LfgJoinResult_LFG_JOIN_OK, + Status: lfgStatusToProto(status), + }, nil +} + +func lfgJoinResultForError(err error) pb.LfgJoinResult { + switch { + case errors.Is(err, service.ErrLFGInvalidDungeon): + return pb.LfgJoinResult_LFG_JOIN_DUNGEON_INVALID + case errors.Is(err, service.ErrLFGGroupFull): + return pb.LfgJoinResult_LFG_JOIN_TOO_MANY_MEMBERS + case errors.Is(err, service.ErrLFGInvalidMember), + errors.Is(err, service.ErrLFGInvalidRoles), + errors.Is(err, service.ErrLFGAlreadyQueuedOrMatched): + return pb.LfgJoinResult_LFG_JOIN_FAILED + case errors.Is(err, service.ErrLFGMultiRealm): + return pb.LfgJoinResult_LFG_JOIN_MULTI_REALM + default: + return pb.LfgJoinResult_LFG_JOIN_INTERNAL_ERROR + } +} + +func (s *MatchmakingServer) LeaveLfg(ctx context.Context, req *pb.LeaveLfgRequest) (*pb.LeaveLfgResponse, error) { + if err := s.lfgService.LeaveLfg(ctx, req.RealmID, req.PlayerGUID); err != nil { + if errors.Is(err, service.ErrLFGNotFound) || errors.Is(err, service.ErrLFGNotLeader) { + return &pb.LeaveLfgResponse{ + Api: matchmaking.Ver, + }, nil + } + return nil, err + } + + return &pb.LeaveLfgResponse{ + Api: matchmaking.Ver, + }, nil +} + +func (s *MatchmakingServer) SetLfgRoles(ctx context.Context, req *pb.SetLfgRolesRequest) (*pb.SetLfgRolesResponse, error) { + status, err := s.lfgService.SetLfgRoles(ctx, req.RealmID, req.PlayerGUID, uint8(req.Roles)) + if err != nil { + return nil, err + } + + return &pb.SetLfgRolesResponse{ + Api: matchmaking.Ver, + Status: lfgStatusToProto(status), + }, nil +} + +func (s *MatchmakingServer) AnswerLfgProposal(ctx context.Context, req *pb.AnswerLfgProposalRequest) (*pb.AnswerLfgProposalResponse, error) { + status, err := s.lfgService.AnswerLfgProposal(ctx, req.RealmID, req.PlayerGUID, req.ProposalID, req.Accept) + if err != nil { + return nil, err + } + + return &pb.AnswerLfgProposalResponse{ + Api: matchmaking.Ver, + Status: lfgStatusToProto(status), + }, nil +} + +func (s *MatchmakingServer) LfgStatus(ctx context.Context, req *pb.LfgStatusRequest) (*pb.LfgStatusResponse, error) { + status, err := s.lfgService.LfgStatus(ctx, req.RealmID, req.PlayerGUID) + if err != nil { + return nil, err + } + + return &pb.LfgStatusResponse{ + Api: matchmaking.Ver, + Status: lfgStatusToProto(status), + }, nil +} + +func (s *MatchmakingServer) CompleteLfgDungeon(ctx context.Context, req *pb.CompleteLfgDungeonRequest) (*pb.CompleteLfgDungeonResponse, error) { + players := make([]service.LFGPlayerKey, 0, len(req.Players)) + for _, player := range req.Players { + if player.GetRealmID() == 0 || player.GetPlayerGUID() == 0 { + continue + } + players = append(players, service.LFGPlayerKey{ + RealmID: player.GetRealmID(), + PlayerGUID: player.GetPlayerGUID(), + }) + } + if err := s.lfgService.CompleteLfgDungeon(ctx, req.GetCompletedDungeonEntry(), req.GetSelectedDungeonEntry(), players); err != nil { + return nil, err + } + + return &pb.CompleteLfgDungeonResponse{ + Api: matchmaking.Ver, + }, nil +} + +func lfgStatusToProto(status *service.LFGStatus) *pb.LfgStatusData { + if status == nil { + status = &service.LFGStatus{State: service.LFGStateNone} + } + + queuedMembers := make([]*pb.LfgMember, 0, len(status.QueuedMembers)) + for _, member := range status.QueuedMembers { + queuedMembers = append(queuedMembers, &pb.LfgMember{ + RealmID: member.RealmID, + PlayerGUID: member.PlayerGUID, + Roles: uint32(member.Roles), + Leader: member.Leader, + WorldserverID: member.WorldserverID, + QueueLeaderRealmID: member.QueueLeaderRealmID, + QueueLeaderGUID: member.QueueLeaderGUID, + }) + } + + proposalMembers := make([]*pb.LfgProposalMember, 0, len(status.ProposalMembers)) + for _, member := range status.ProposalMembers { + proposalMembers = append(proposalMembers, &pb.LfgProposalMember{ + RealmID: member.RealmID, + PlayerGUID: member.PlayerGUID, + SelectedRoles: uint32(member.SelectedRoles), + AssignedRole: uint32(member.AssignedRole), + Answered: member.Answered, + Accepted: member.Accepted, + }) + } + + return &pb.LfgStatusData{ + State: pb.LfgState(status.State), + ProposalID: status.ProposalID, + ProposalState: pb.LfgProposalState(status.ProposalState), + DungeonEntry: status.DungeonEntry, + SelectedDungeons: status.SelectedDungeons, + QueuedMembers: queuedMembers, + ProposalMembers: proposalMembers, + QueuedTimeMilliseconds: status.QueuedTimeMilliseconds, + TanksNeeded: uint32(status.TanksNeeded), + HealersNeeded: uint32(status.HealersNeeded), + DamageNeeded: uint32(status.DamageNeeded), + } +} diff --git a/apps/matchmakingserver/service/arena_match_result.go b/apps/matchmakingserver/service/arena_match_result.go new file mode 100644 index 0000000..4108d28 --- /dev/null +++ b/apps/matchmakingserver/service/arena_match_result.go @@ -0,0 +1,477 @@ +package service + +import ( + "context" + "math" + + "github.com/walkline/ToCloud9/apps/matchmakingserver/battleground" + "github.com/walkline/ToCloud9/apps/matchmakingserver/repo" + wowarena "github.com/walkline/ToCloud9/shared/wow/arena" + wowguid "github.com/walkline/ToCloud9/shared/wow/guid" +) + +const arenaTimelimitPointsLoss int32 = -16 + +type RatedArenaParticipant struct { + Team battleground.PVPTeam + PlayerGUID uint64 +} + +type RatedArenaMatchResultRequest struct { + OwnerRealmID uint32 + IsCrossRealm bool + InstanceID uint32 + ArenaType uint8 + WinnerTeam battleground.PVPTeam + ValidArena bool + + AllianceArenaTeamID uint32 + HordeArenaTeamID uint32 + AllianceArenaMatchmakerRating uint32 + HordeArenaMatchmakerRating uint32 + Participants []RatedArenaParticipant +} + +type RatedArenaTeamScore struct { + RealmID uint32 + ArenaTeamID uint32 + TeamName string + RatingChange int32 + MatchmakerRating uint32 +} + +type RatedArenaMemberResult struct { + Team battleground.PVPTeam + PlayerGUID uint64 + PersonalRating uint32 + WeekGames uint32 + SeasonGames uint32 + WeekWins uint32 + SeasonWins uint32 + MatchmakerRating uint32 +} + +type RatedArenaMatchResult struct { + AllianceScore RatedArenaTeamScore + HordeScore RatedArenaTeamScore + MemberResults []RatedArenaMemberResult +} + +type ratedArenaTeamRef struct { + realmID uint32 + teamID uint32 +} + +type ratedArenaTeamState struct { + ref ratedArenaTeamRef + ownerRealmID uint32 + team *repo.ArenaTeamDetails + mmr uint32 + score RatedArenaTeamScore + changedMembers map[uint64]struct{} +} + +func (s *battleGroundService) FinishRatedArenaMatch(ctx context.Context, request RatedArenaMatchResultRequest) (*RatedArenaMatchResult, error) { + if s.arenaTeamRepo == nil { + return nil, ErrRatedArenaUnavailable + } + if request.ArenaType != 2 && request.ArenaType != 3 && request.ArenaType != 5 { + return nil, ErrInvalidArenaType + } + + allianceRef := arenaTeamRefFromStartID(request.OwnerRealmID, request.AllianceArenaTeamID) + hordeRef := arenaTeamRefFromStartID(request.OwnerRealmID, request.HordeArenaTeamID) + if allianceRef.realmID == 0 || allianceRef.teamID == 0 || hordeRef.realmID == 0 || hordeRef.teamID == 0 { + return nil, repo.ErrArenaTeamNotFound + } + + allianceTeam, err := s.arenaTeamRepo.GetTeam(ctx, allianceRef.realmID, allianceRef.teamID) + if err != nil { + return nil, err + } + hordeTeam, err := s.arenaTeamRepo.GetTeam(ctx, hordeRef.realmID, hordeRef.teamID) + if err != nil { + return nil, err + } + if allianceTeam.Type != request.ArenaType || hordeTeam.Type != request.ArenaType { + return nil, ErrInvalidArenaType + } + + alliance := ratedArenaTeamState{ + ref: allianceRef, + ownerRealmID: ratedArenaOwnerRealmID(request), + team: allianceTeam, + mmr: arenaMMRFallback(request.AllianceArenaMatchmakerRating, s.arenaStartMMR), + changedMembers: map[uint64]struct{}{}, + score: RatedArenaTeamScore{ + RealmID: allianceRef.realmID, + ArenaTeamID: allianceRef.teamID, + TeamName: allianceTeam.Name, + MatchmakerRating: arenaMMRFallback(request.AllianceArenaMatchmakerRating, s.arenaStartMMR), + }, + } + horde := ratedArenaTeamState{ + ref: hordeRef, + ownerRealmID: ratedArenaOwnerRealmID(request), + team: hordeTeam, + mmr: arenaMMRFallback(request.HordeArenaMatchmakerRating, s.arenaStartMMR), + changedMembers: map[uint64]struct{}{}, + score: RatedArenaTeamScore{ + RealmID: hordeRef.realmID, + ArenaTeamID: hordeRef.teamID, + TeamName: hordeTeam.Name, + MatchmakerRating: arenaMMRFallback(request.HordeArenaMatchmakerRating, s.arenaStartMMR), + }, + } + + result := &RatedArenaMatchResult{ + AllianceScore: alliance.score, + HordeScore: horde.score, + } + + switch request.WinnerTeam { + case battleground.TeamAlliance: + err = s.finishRatedArenaWinLoss(ctx, &alliance, &horde, request.ValidArena, request.Participants, result) + case battleground.TeamHorde: + err = s.finishRatedArenaWinLoss(ctx, &horde, &alliance, request.ValidArena, request.Participants, result) + default: + err = s.finishRatedArenaDraw(ctx, &alliance, &horde, request.ValidArena, request.Participants, result) + } + if err != nil { + return nil, err + } + + result.AllianceScore = alliance.score + result.HordeScore = horde.score + + return result, nil +} + +func (s *battleGroundService) finishRatedArenaWinLoss( + ctx context.Context, + winner *ratedArenaTeamState, + loser *ratedArenaTeamState, + validArena bool, + participants []RatedArenaParticipant, + result *RatedArenaMatchResult, +) error { + var winnerRatingChange int32 + var winnerMMRChange int32 + if validArena { + winnerMMRChange = s.getMatchmakerRatingMod(winner.mmr, loser.mmr, true) + winnerRatingChange = s.getRatingMod(winner.team.Rating, loser.mmr, true) + applyArenaTeamFinish(&winner.team.Rating, &winner.team.WeekGames, &winner.team.SeasonGames, winnerRatingChange) + winner.team.WeekWins++ + winner.team.SeasonWins++ + } + + loserMMRChange := s.getMatchmakerRatingMod(loser.mmr, winner.mmr, false) + loserRatingChange := s.getRatingMod(loser.team.Rating, winner.mmr, false) + applyArenaTeamFinish(&loser.team.Rating, &loser.team.WeekGames, &loser.team.SeasonGames, loserRatingChange) + + winner.score.RatingChange = winnerRatingChange + winner.score.MatchmakerRating = winner.mmr + loser.score.RatingChange = loserRatingChange + loser.score.MatchmakerRating = loser.mmr + + winnerMembers, err := participantSet(participants, teamForState(winner, result), winner.ref.realmID) + if err != nil { + return err + } + loserMembers, err := participantSet(participants, teamForState(loser, result), loser.ref.realmID) + if err != nil { + return err + } + + if validArena { + if err = s.applyWinnerMembers(winner, loser.mmr, winnerMMRChange, winnerMembers, result); err != nil { + return err + } + } + if err = s.applyLoserMembers(loser, winner.mmr, loserMMRChange, loserMembers, result); err != nil { + return err + } + + if validArena { + if err := s.saveArenaTeamState(ctx, winner); err != nil { + return err + } + } + return s.saveArenaTeamState(ctx, loser) +} + +func (s *battleGroundService) finishRatedArenaDraw( + ctx context.Context, + alliance *ratedArenaTeamState, + horde *ratedArenaTeamState, + validArena bool, + participants []RatedArenaParticipant, + result *RatedArenaMatchResult, +) error { + applyArenaTeamFinish(&alliance.team.Rating, &alliance.team.WeekGames, &alliance.team.SeasonGames, arenaTimelimitPointsLoss) + applyArenaTeamFinish(&horde.team.Rating, &horde.team.WeekGames, &horde.team.SeasonGames, arenaTimelimitPointsLoss) + alliance.score.RatingChange = arenaTimelimitPointsLoss + horde.score.RatingChange = arenaTimelimitPointsLoss + + allianceMembers, err := participantSet(participants, battleground.TeamAlliance, alliance.ref.realmID) + if err != nil { + return err + } + hordeMembers, err := participantSet(participants, battleground.TeamHorde, horde.ref.realmID) + if err != nil { + return err + } + + if err = s.applyLoserMembers(alliance, horde.mmr, 0, allianceMembers, result); err != nil { + return err + } + if err = s.applyLoserMembers(horde, alliance.mmr, 0, hordeMembers, result); err != nil { + return err + } + + if validArena { + if err := s.saveArenaTeamState(ctx, horde); err != nil { + return err + } + } + return s.saveArenaTeamState(ctx, alliance) +} + +func (s *battleGroundService) applyWinnerMembers(team *ratedArenaTeamState, opponentMMR uint32, teamMMRChange int32, participants map[uint64]struct{}, result *RatedArenaMatchResult) error { + for i := range team.team.Members { + member := &team.team.Members[i] + if _, ok := participants[member.PlayerGUID]; !ok { + continue + } + mod := s.getRatingMod(member.PersonalRating, opponentMMR, true) + member.PersonalRating = applyRatingChange(member.PersonalRating, mod) + if member.MatchmakerRating == 0 { + member.MatchmakerRating = s.arenaStartMMR + } + if member.MaxMMR == 0 { + member.MaxMMR = member.MatchmakerRating + } + if member.MatchmakerRating < team.team.Rating { + mmrChange := minInt32(teamMMRChange, int32(team.team.Rating-member.MatchmakerRating)) + member.MatchmakerRating, member.MaxMMR = applyMMRChange(member.MatchmakerRating, member.MaxMMR, mmrChange, s.arenaRatingConfig.MaxAllowedMMRDrop) + } + member.WeekGames++ + member.SeasonGames++ + member.WeekWins++ + member.SeasonWins++ + team.changedMembers[member.PlayerGUID] = struct{}{} + result.MemberResults = append(result.MemberResults, arenaMemberResult(teamForState(team, result), team.ownerRealmID, team.ref.realmID, member)) + } + return ensureParticipantsMatched(participants, team.team) +} + +func (s *battleGroundService) applyLoserMembers(team *ratedArenaTeamState, opponentMMR uint32, teamMMRChange int32, participants map[uint64]struct{}, result *RatedArenaMatchResult) error { + for i := range team.team.Members { + member := &team.team.Members[i] + if _, ok := participants[member.PlayerGUID]; !ok { + continue + } + mod := s.getRatingMod(member.PersonalRating, opponentMMR, false) + member.PersonalRating = applyRatingChange(member.PersonalRating, mod) + if member.MatchmakerRating == 0 { + member.MatchmakerRating = s.arenaStartMMR + } + if member.MaxMMR == 0 { + member.MaxMMR = member.MatchmakerRating + } + member.MatchmakerRating, member.MaxMMR = applyMMRChange(member.MatchmakerRating, member.MaxMMR, teamMMRChange, s.arenaRatingConfig.MaxAllowedMMRDrop) + member.WeekGames++ + member.SeasonGames++ + team.changedMembers[member.PlayerGUID] = struct{}{} + result.MemberResults = append(result.MemberResults, arenaMemberResult(teamForState(team, result), team.ownerRealmID, team.ref.realmID, member)) + } + return ensureParticipantsMatched(participants, team.team) +} + +func (s *battleGroundService) saveArenaTeamState(ctx context.Context, team *ratedArenaTeamState) error { + members := make([]repo.ArenaTeamSaveStatsMember, 0, len(team.team.Members)) + slot, ok := arenaSlotByType(team.team.Type) + if !ok { + return ErrInvalidArenaType + } + for _, member := range team.team.Members { + if _, ok := team.changedMembers[member.PlayerGUID]; !ok { + continue + } + members = append(members, repo.ArenaTeamSaveStatsMember{ + PlayerGUID: member.PlayerGUID, + PersonalRating: member.PersonalRating, + WeekGames: member.WeekGames, + WeekWins: member.WeekWins, + SeasonGames: member.SeasonGames, + SeasonWins: member.SeasonWins, + MatchmakerRating: member.MatchmakerRating, + MaxMMR: member.MaxMMR, + SaveArenaStats: true, + }) + } + return s.arenaTeamRepo.SaveStats(ctx, repo.ArenaTeamSaveStatsRequest{ + RealmID: team.ref.realmID, + ArenaTeamID: team.ref.teamID, + Rating: team.team.Rating, + WeekGames: team.team.WeekGames, + WeekWins: team.team.WeekWins, + SeasonGames: team.team.SeasonGames, + SeasonWins: team.team.SeasonWins, + Rank: team.team.Rank, + Slot: uint32(slot), + Members: members, + }) +} + +func (s *battleGroundService) getChanceAgainst(ownRating, opponentRating uint32) float64 { + return 1.0 / (1.0 + math.Exp(math.Log(10.0)*(float64(opponentRating)-float64(ownRating))/650.0)) +} + +func (s *battleGroundService) getMatchmakerRatingMod(ownRating, opponentRating uint32, won bool) int32 { + chance := s.getChanceAgainst(ownRating, opponentRating) + wonMod := 0.0 + if won { + wonMod = 1.0 + } + return int32(math.Ceil((wonMod - chance) * s.arenaRatingConfig.MatchmakerModifier)) +} + +func (s *battleGroundService) getRatingMod(ownRating, opponentRating uint32, won bool) int32 { + chance := s.getChanceAgainst(ownRating, opponentRating) + var mod float64 + if won { + if ownRating < 1300 { + if ownRating < 1000 { + mod = s.arenaRatingConfig.WinModifier1 * (1.0 - chance) + } else { + mod = ((s.arenaRatingConfig.WinModifier1 / 2.0) + ((s.arenaRatingConfig.WinModifier1 / 2.0) * (1300.0 - float64(ownRating)) / 300.0)) * (1.0 - chance) + } + } else { + mod = s.arenaRatingConfig.WinModifier2 * (1.0 - chance) + } + } else { + mod = s.arenaRatingConfig.LoseModifier * (-chance) + } + return int32(math.Ceil(mod)) +} + +func arenaTeamRefFromStartID(ownerRealmID uint32, startTeamID uint32) ratedArenaTeamRef { + realmID := wowarena.TeamIDRealmID(startTeamID) + teamID := wowarena.TeamIDCounter(startTeamID) + if realmID == 0 { + realmID = ownerRealmID + } + return ratedArenaTeamRef{realmID: realmID, teamID: teamID} +} + +func ratedArenaOwnerRealmID(request RatedArenaMatchResultRequest) uint32 { + if request.IsCrossRealm { + return 0 + } + return request.OwnerRealmID +} + +func arenaMMRFallback(value, fallback uint32) uint32 { + if value == 0 { + return fallback + } + return value +} + +func arenaSlotByType(arenaType uint8) (uint8, bool) { + switch arenaType { + case 2: + return 0, true + case 3: + return 1, true + case 5: + return 2, true + default: + return 0, false + } +} + +func applyArenaTeamFinish(rating *uint32, weekGames *uint32, seasonGames *uint32, mod int32) { + *rating = applyRatingChange(*rating, mod) + *weekGames = *weekGames + 1 + *seasonGames = *seasonGames + 1 +} + +func applyRatingChange(rating uint32, mod int32) uint32 { + if int64(rating)+int64(mod) < 0 { + return 0 + } + return uint32(int64(rating) + int64(mod)) +} + +func applyMMRChange(mmr, maxMMR uint32, mod int32, maxAllowedDrop uint32) (uint32, uint32) { + if mod < 0 { + maxDropWindow := int32(mmr) - int32(maxMMR) + int32(maxAllowedDrop) + mod = minInt32(maxInt32(-maxDropWindow, mod), 0) + } + next := applyRatingChange(mmr, mod) + if next > maxMMR { + maxMMR = next + } + return next, maxMMR +} + +func participantSet(participants []RatedArenaParticipant, team battleground.PVPTeam, teamRealmID uint32) (map[uint64]struct{}, error) { + set := make(map[uint64]struct{}) + for _, participant := range participants { + if participant.Team != team { + continue + } + participantRealmID := wowguid.PlayerRealmIDOrDefault(teamRealmID, participant.PlayerGUID) + if participantRealmID != teamRealmID { + return nil, repo.ErrArenaTeamMemberMismatch + } + set[wowguid.PlayerLowGUID(participant.PlayerGUID)] = struct{}{} + } + return set, nil +} + +func ensureParticipantsMatched(participants map[uint64]struct{}, team *repo.ArenaTeamDetails) error { + for _, member := range team.Members { + delete(participants, member.PlayerGUID) + } + if len(participants) > 0 { + return repo.ErrArenaTeamMemberMismatch + } + return nil +} + +func teamForState(team *ratedArenaTeamState, result *RatedArenaMatchResult) battleground.PVPTeam { + if team.score.RealmID == result.AllianceScore.RealmID && team.score.ArenaTeamID == result.AllianceScore.ArenaTeamID { + return battleground.TeamAlliance + } + return battleground.TeamHorde +} + +func arenaMemberResult(team battleground.PVPTeam, ownerRealmID uint32, memberRealmID uint32, member *repo.ArenaTeamDetailsMember) RatedArenaMemberResult { + return RatedArenaMemberResult{ + Team: team, + PlayerGUID: wowguid.PlayerGUIDForRealm(ownerRealmID, memberRealmID, member.PlayerGUID), + PersonalRating: member.PersonalRating, + WeekGames: member.WeekGames, + SeasonGames: member.SeasonGames, + WeekWins: member.WeekWins, + SeasonWins: member.SeasonWins, + MatchmakerRating: member.MatchmakerRating, + } +} + +func minInt32(a, b int32) int32 { + if a < b { + return a + } + return b +} + +func maxInt32(a, b int32) int32 { + if a > b { + return a + } + return b +} diff --git a/apps/matchmakingserver/service/battleground-queue.go b/apps/matchmakingserver/service/battleground-queue.go index 0f8136d..e724dd9 100644 --- a/apps/matchmakingserver/service/battleground-queue.go +++ b/apps/matchmakingserver/service/battleground-queue.go @@ -12,6 +12,7 @@ import ( "github.com/walkline/ToCloud9/apps/matchmakingserver/battleground" "github.com/walkline/ToCloud9/apps/matchmakingserver/repo" + wowarena "github.com/walkline/ToCloud9/shared/wow/arena" "github.com/walkline/ToCloud9/shared/wow/guid" ) @@ -19,6 +20,8 @@ var ( ErrPlayerNotFound = errors.New("player not found") ) +const ratedArenaPreviousOpponentPenalty = 1000000 + type QueuedGroup struct { LeaderGUID guid.PlayerUnwrapped @@ -29,6 +32,14 @@ type QueuedGroup struct { RealmID uint32 TeamID battleground.PVPTeam + ArenaType uint8 + IsRated bool + + ArenaTeamID uint32 + ArenaTeamRating uint32 + ArenaMatchmakerRating uint32 + ArenaPreviousOpponentsTeamID uint32 + EnqueuedTime time.Time IsRandomQueue bool @@ -145,6 +156,11 @@ func (q *GenericBattlegroundQueue) QueuedGroupByPlayer(player guid.PlayerUnwrapp } func (q *GenericBattlegroundQueue) process(ctx context.Context) error { + template := q.getBattlegroundTemplate() + if q.QueueTypeID == battleground.QueueTypeIDAllArenas { + return q.processArena(ctx, template) + } + battlegroundToFillIn, err := q.battleGroundService.BattlegroundsThatNeedPlayers( ctx, q.QueueTypeID, @@ -175,7 +191,6 @@ func (q *GenericBattlegroundQueue) process(ctx context.Context) error { } // Try to create a new battleground - template := q.getBattlegroundTemplate() allianceGroup, hordeGroup := q.balancedGroups(int(template.MinPlayersPerTeam), int(template.MaxPlayersPerTeam)) // If not enough groups - do nothing. @@ -199,6 +214,161 @@ func (q *GenericBattlegroundQueue) process(ctx context.Context) error { return nil } +type arenaQueueKey struct { + arenaType uint8 + isRated bool +} + +func (q *GenericBattlegroundQueue) processArena(ctx context.Context, template repo.BattlegroundTemplate) error { + q.mut.RLock() + groupsByKey := make(map[arenaQueueKey][]QueuedGroup) + for _, group := range q.queuedGroups { + key := arenaQueueKey{ + arenaType: group.ArenaType, + isRated: group.IsRated, + } + groupsByKey[key] = append(groupsByKey[key], group) + } + q.mut.RUnlock() + + for key, groups := range groupsByKey { + teamSize := int(key.arenaType) + if teamSize == 0 { + continue + } + + var allianceGroups []QueuedGroup + var hordeGroups []QueuedGroup + if key.isRated { + allianceGroups, hordeGroups = findRatedArenaGroups(groups, teamSize) + if len(allianceGroups) == 0 || len(hordeGroups) == 0 { + continue + } + } else { + allianceGroups = findArenaGroupsForTeam(groups, teamSize) + if len(allianceGroups) == 0 { + continue + } + + remainingGroups := removeQueuedGroups(groups, allianceGroups) + hordeGroups = findArenaGroupsForTeam(remainingGroups, teamSize) + if len(hordeGroups) == 0 { + continue + } + } + + err := q.battleGroundCreator.CreateBattleground(ctx, template, q.QueueTypeID, BracketID(q.BracketID), q.RealmID, q.BattleGroupID, allianceGroups, hordeGroups) + if err != nil { + return fmt.Errorf("failed to create arena: %w", err) + } + + for _, group := range hordeGroups { + q.removeGroupFromQueue(&group) + } + + for _, group := range allianceGroups { + q.removeGroupFromQueue(&group) + } + + return nil + } + + return nil +} + +func findRatedArenaGroups(groups []QueuedGroup, teamSize int) ([]QueuedGroup, []QueuedGroup) { + var candidates []QueuedGroup + for _, group := range groups { + if queuedGroupPlayerCount(group) != teamSize || group.ArenaTeamID == 0 { + continue + } + candidates = append(candidates, group) + } + + sort.Slice(candidates, func(i, j int) bool { + return candidates[i].EnqueuedTime.Before(candidates[j].EnqueuedTime) + }) + + bestI := -1 + bestJ := -1 + bestDiff := int(^uint(0) >> 1) + for i := 0; i < len(candidates); i++ { + for j := i + 1; j < len(candidates); j++ { + firstTeamID := ratedArenaTeamKey(candidates[i]) + secondTeamID := ratedArenaTeamKey(candidates[j]) + if firstTeamID == secondTeamID { + continue + } + diff := abs(int(candidates[i].ArenaMatchmakerRating) - int(candidates[j].ArenaMatchmakerRating)) + if candidates[i].ArenaPreviousOpponentsTeamID == secondTeamID || + candidates[j].ArenaPreviousOpponentsTeamID == firstTeamID { + diff += ratedArenaPreviousOpponentPenalty + } + if diff < bestDiff { + bestI = i + bestJ = j + bestDiff = diff + } + } + } + + if bestI < 0 || bestJ < 0 { + return nil, nil + } + + return []QueuedGroup{candidates[bestI]}, []QueuedGroup{candidates[bestJ]} +} + +func ratedArenaTeamKey(group QueuedGroup) uint32 { + return wowarena.NewCrossrealmTeamID(uint16(group.RealmID), group.ArenaTeamID) +} + +func findArenaGroupsForTeam(groups []QueuedGroup, teamSize int) []QueuedGroup { + var best []QueuedGroup + var search func(index int, current []QueuedGroup, players int) bool + search = func(index int, current []QueuedGroup, players int) bool { + if players == teamSize { + best = append([]QueuedGroup{}, current...) + return true + } + if players > teamSize || index >= len(groups) { + return false + } + + group := groups[index] + groupPlayers := queuedGroupPlayerCount(group) + if groupPlayers <= teamSize && search(index+1, append(current, group), players+groupPlayers) { + return true + } + + return search(index+1, current, players) + } + + search(0, nil, 0) + return best +} + +func removeQueuedGroups(groups []QueuedGroup, groupsToRemove []QueuedGroup) []QueuedGroup { + remaining := make([]QueuedGroup, 0, len(groups)) + for _, group := range groups { + remove := false + for _, groupToRemove := range groupsToRemove { + if group.LeaderGUID == groupToRemove.LeaderGUID { + remove = true + break + } + } + if !remove { + remaining = append(remaining, group) + } + } + return remaining +} + +func queuedGroupPlayerCount(group QueuedGroup) int { + return len(group.Members) + 1 +} + func (q *GenericBattlegroundQueue) balancedGroups(minPlayers, maxPlayers int) ([]QueuedGroup, []QueuedGroup) { q.mut.RLock() defer q.mut.RUnlock() diff --git a/apps/matchmakingserver/service/battleground_service.go b/apps/matchmakingserver/service/battleground_service.go index 3eb97fd..3d0aba2 100644 --- a/apps/matchmakingserver/service/battleground_service.go +++ b/apps/matchmakingserver/service/battleground_service.go @@ -17,12 +17,15 @@ import ( "github.com/walkline/ToCloud9/gen/worldserver/pb" "github.com/walkline/ToCloud9/shared/events" "github.com/walkline/ToCloud9/shared/gameserver/conn" + wowarena "github.com/walkline/ToCloud9/shared/wow/arena" "github.com/walkline/ToCloud9/shared/wow/guid" ) var ( - ErrAlreadyInQueue = errors.New("already in queue") - ErrNotFound = errors.New("not found") + ErrAlreadyInQueue = errors.New("already in queue") + ErrInvalidArenaType = errors.New("invalid arena type") + ErrRatedArenaUnavailable = errors.New("rated arena team data unavailable") + ErrNotFound = errors.New("not found") ) type BracketID uint8 @@ -60,6 +63,8 @@ type BattleGroundService interface { typeID battleground.QueueTypeID, leaderLvl uint8, teamID battleground.PVPTeam, + arenaType uint8, + isRated bool, ) error InviteGroups(ctx context.Context, groups []QueuedGroup, bg *battleground.Battleground, team battleground.PVPTeam) error @@ -74,6 +79,8 @@ type BattleGroundService interface { BattlegroundStatusChanged(ctx context.Context, status battleground.Status, realmID, instanceID uint32, isCrossrealm bool) error + FinishRatedArenaMatch(ctx context.Context, request RatedArenaMatchResultRequest) (*RatedArenaMatchResult, error) + RemovePlayerFromQueue(ctx context.Context, playerGUID uint64, realmID uint32, typeID battleground.QueueTypeID) error PlayerBecomeOffline(ctx context.Context, playerGUID uint64, realmID uint32) error @@ -108,13 +115,26 @@ type battleGroundService struct { queues map[QueueByRealmOrBattlegroupKey]map[battleground.QueueTypeID]map[BracketID]PVPQueue playersQueueOrBattleground map[QueuesByRealmAndPlayerKey][]QueueOrBattlegroundLink playersQueueOrBattlegroundMutex sync.RWMutex + arenaPreviousOpponents map[uint32]uint32 + arenaPreviousOpponentsMutex sync.RWMutex battleGroupsRepo repo.BattleGroupsRepository + arenaTeamRepo repo.ArenaTeamRepository battlegroundsRepo repo.BattlegroundRepo crossRealmNodesTracker *CrossRealmNodesTracker eventsProducer events.MatchmakingServiceProducer serversRegistryClient pbServRegistry.ServersRegistryServiceClient gameserverGRPCConnMgr conn.GameServerGRPCConnMgr + arenaStartMMR uint32 + arenaRatingConfig ArenaRatingConfig +} + +type ArenaRatingConfig struct { + WinModifier1 float64 + WinModifier2 float64 + LoseModifier float64 + MatchmakerModifier float64 + MaxAllowedMMRDrop uint32 } func (s *battleGroundService) BattlegroundsThatNeedPlayers(ctx context.Context, battlegroundTypeID battleground.QueueTypeID, bracketID uint8, battleGroupID, realmID uint32) ([]battleground.Battleground, error) { @@ -140,12 +160,15 @@ func (s *battleGroundService) BattlegroundsThatNeedPlayers(ctx context.Context, func NewBattleGroundService( templatesRepo repo.BattlegroundTemplesRepo, + arenaTeamRepo repo.ArenaTeamRepository, battleGroupsRepo repo.BattleGroupsRepository, battlegroundsRepo repo.BattlegroundRepo, crossRealmNodesTracker *CrossRealmNodesTracker, eventsProducer events.MatchmakingServiceProducer, serversRegistryClient pbServRegistry.ServersRegistryServiceClient, gameserverGRPCConnMgr conn.GameServerGRPCConnMgr, + arenaStartMMR uint32, + arenaRatingConfig ArenaRatingConfig, realmIDs []uint32, ) (BattleGroundService, error) { templates, err := templatesRepo.GetAll(context.Background()) @@ -155,12 +178,16 @@ func NewBattleGroundService( service := battleGroundService{ playersQueueOrBattleground: map[QueuesByRealmAndPlayerKey][]QueueOrBattlegroundLink{}, + arenaPreviousOpponents: map[uint32]uint32{}, battleGroupsRepo: battleGroupsRepo, + arenaTeamRepo: arenaTeamRepo, battlegroundsRepo: battlegroundsRepo, crossRealmNodesTracker: crossRealmNodesTracker, eventsProducer: eventsProducer, serversRegistryClient: serversRegistryClient, gameserverGRPCConnMgr: gameserverGRPCConnMgr, + arenaStartMMR: arenaStartMMR, + arenaRatingConfig: arenaRatingConfig, } for _, template := range templates { @@ -171,6 +198,24 @@ func NewBattleGroundService( if err != nil { return nil, fmt.Errorf("cannot get all battlegroups: %w", err) } + if service.arenaStartMMR == 0 { + service.arenaStartMMR = 1500 + } + if service.arenaRatingConfig.WinModifier1 == 0 { + service.arenaRatingConfig.WinModifier1 = 48 + } + if service.arenaRatingConfig.WinModifier2 == 0 { + service.arenaRatingConfig.WinModifier2 = 24 + } + if service.arenaRatingConfig.LoseModifier == 0 { + service.arenaRatingConfig.LoseModifier = 24 + } + if service.arenaRatingConfig.MatchmakerModifier == 0 { + service.arenaRatingConfig.MatchmakerModifier = 24 + } + if service.arenaRatingConfig.MaxAllowedMMRDrop == 0 { + service.arenaRatingConfig.MaxAllowedMMRDrop = 500 + } service.queues = generateQueuesForAllBattlegroundTypes(&service, realmIDs, battlegroups) crossRealmNodesTracker.SetObserver(&service) @@ -243,7 +288,37 @@ func (s *battleGroundService) AddGroupToQueue( typeID battleground.QueueTypeID, leaderLvl uint8, teamID battleground.PVPTeam, + arenaType uint8, + isRated bool, ) error { + var arenaQueueData *repo.ArenaTeamQueueData + if typeID == battleground.QueueTypeIDAllArenas { + if arenaType != 2 && arenaType != 3 && arenaType != 5 { + return ErrInvalidArenaType + } + if isRated { + if s.arenaTeamRepo == nil { + return ErrRatedArenaUnavailable + } + + playerGUIDs := make([]uint64, 0, len(partyMembers)+1) + playerGUIDs = append(playerGUIDs, uint64(guid.LowType(leaderGUID))) + for _, memberGUID := range partyMembers { + playerGUIDs = append(playerGUIDs, uint64(guid.LowType(memberGUID))) + } + + var err error + arenaQueueData, err = s.arenaTeamRepo.QueueDataForRatedArena(ctx, realmID, uint64(guid.LowType(leaderGUID)), playerGUIDs, arenaType, s.arenaStartMMR) + if err != nil { + return fmt.Errorf("%w: %v", ErrRatedArenaUnavailable, err) + } + } + teamID = battleground.TeamAny + } else { + arenaType = 0 + isRated = false + } + leaderUnwrappedGUID := guid.PlayerUnwrapped{ RealmID: uint16(realmID), LowGUID: guid.LowType(leaderGUID), @@ -306,8 +381,16 @@ func (s *battleGroundService) AddGroupToQueue( Members: partyMembersGUIDs, RealmID: realmID, TeamID: teamID, + ArenaType: arenaType, + IsRated: isRated, EnqueuedTime: time.Now(), } + if arenaQueueData != nil { + group.ArenaTeamID = arenaQueueData.ArenaTeamID + group.ArenaTeamRating = arenaQueueData.TeamRating + group.ArenaMatchmakerRating = arenaQueueData.MatchmakerRating + group.ArenaPreviousOpponentsTeamID = s.previousRatedArenaOpponentID(realmID, arenaQueueData.ArenaTeamID) + } slots := s.addQueueForGroupMembers(queue, group) @@ -317,6 +400,22 @@ func (s *battleGroundService) AddGroupToQueue( return fmt.Errorf("cannot add queue for bracket id %v and type id: %v: %w", bracketID, typeID, err) } + if typeID == battleground.QueueTypeIDAllArenas { + log.Debug(). + Uint32("realmID", realmID). + Uint64("leaderGUID", uint64(guid.LowType(leaderGUID))). + Interface("partyMembers", partyMembersGUIDs). + Uint8("arenaType", arenaType). + Bool("isRated", isRated). + Uint32("arenaTeamID", group.ArenaTeamID). + Uint32("arenaTeamRating", group.ArenaTeamRating). + Uint32("arenaMatchmakerRating", group.ArenaMatchmakerRating). + Interface("queueKey", queueKey). + Uint8("bracketID", uint8(bracketID)). + Interface("queueSlots", slots). + Msg("TC9 arena group queued") + } + minLvl, maxLvl := battleground.LevelsDiapasonForBracket(uint8(bracketID)) playersLowGuids := make([]guid.LowType, len(partyMembers)+1) @@ -334,8 +433,8 @@ func (s *battleGroundService) AddGroupToQueue( RealmID: realmID, PlayersGUID: playersLowGuids, QueueSlotByPlayer: slotsWithLowGUIDs, - ArenaType: 0, - IsRated: false, + ArenaType: arenaType, + IsRated: isRated, PVPQueueMinLVL: minLvl, PVPQueueMaxLVL: maxLvl, TypeID: uint8(typeID), @@ -524,16 +623,22 @@ func (s *battleGroundService) CreateBattleground( } minLvl, _ := battleground.LevelsDiapasonForBracket(uint8(bracketID)) + arenaType, isRated := queuePVPOptions(allianceGroups, hordeGroups) + arenaStart := arenaStartOptionsForGroups(allianceGroups, hordeGroups, isCrossRealm) startBGResponse, err := gameServerGRPCClient.StartBattleground(ctx, &pb.StartBattlegroundRequest{ - Api: matchmaking.SupportedGameServerVer, - BattlegroundTypeID: pb.BattlegroundType(template.TypeID), - ArenaType: 0, // TODO: implement later - IsRated: false, // TODO: implement later - MapID: template.MapID, - BracketLvl: uint32(minLvl), - PlayersToAddAlliance: alliancePlayers, - PlayersToAddHorde: hordePlayers, + Api: matchmaking.SupportedGameServerVer, + BattlegroundTypeID: pb.BattlegroundType(template.TypeID), + ArenaType: uint32(arenaType), + IsRated: isRated, + MapID: template.MapID, + BracketLvl: uint32(minLvl), + PlayersToAddAlliance: alliancePlayers, + PlayersToAddHorde: hordePlayers, + AllianceArenaTeamID: arenaStart.allianceTeamID, + HordeArenaTeamID: arenaStart.hordeTeamID, + AllianceArenaMatchmakerRating: arenaStart.allianceMMR, + HordeArenaMatchmakerRating: arenaStart.hordeMMR, }) if err != nil { return fmt.Errorf("start battleground failed: %w", err) @@ -556,24 +661,36 @@ func (s *battleGroundService) CreateBattleground( bgHordeGroups := make([]battleground.QueuedGroup, len(hordeGroups)) for i, group := range hordeGroups { bgHordeGroups[i] = battleground.QueuedGroup{ - LeaderGUID: group.LeaderGUID, - Members: group.Members, - SlotsPerMember: group.SlotsPerMember, - RealmID: group.RealmID, - TeamID: group.TeamID, - EnqueuedTime: group.EnqueuedTime, + LeaderGUID: group.LeaderGUID, + Members: group.Members, + SlotsPerMember: group.SlotsPerMember, + RealmID: group.RealmID, + TeamID: group.TeamID, + ArenaType: group.ArenaType, + IsRated: group.IsRated, + ArenaTeamID: group.ArenaTeamID, + ArenaTeamRating: group.ArenaTeamRating, + ArenaMatchmakerRating: group.ArenaMatchmakerRating, + ArenaPreviousOpponentsTeamID: group.ArenaPreviousOpponentsTeamID, + EnqueuedTime: group.EnqueuedTime, } } bgAllianceGroups := make([]battleground.QueuedGroup, len(allianceGroups)) for i, group := range allianceGroups { bgAllianceGroups[i] = battleground.QueuedGroup{ - LeaderGUID: group.LeaderGUID, - Members: group.Members, - SlotsPerMember: group.SlotsPerMember, - RealmID: group.RealmID, - TeamID: group.TeamID, - EnqueuedTime: group.EnqueuedTime, + LeaderGUID: group.LeaderGUID, + Members: group.Members, + SlotsPerMember: group.SlotsPerMember, + RealmID: group.RealmID, + TeamID: group.TeamID, + ArenaType: group.ArenaType, + IsRated: group.IsRated, + ArenaTeamID: group.ArenaTeamID, + ArenaTeamRating: group.ArenaTeamRating, + ArenaMatchmakerRating: group.ArenaMatchmakerRating, + ArenaPreviousOpponentsTeamID: group.ArenaPreviousOpponentsTeamID, + EnqueuedTime: group.EnqueuedTime, } } @@ -607,11 +724,21 @@ func (s *battleGroundService) CreateBattleground( s.removeQueueForGroupMembers(queue, &group) s.addBattlegroundForGroupMembers(bg, &group) } + s.recordRatedArenaOpponents(allianceGroups, hordeGroups) log.Debug(). Interface("RealmKey", realmOrBGGroupQueueKey). Uint8("QType", uint8(queueType)). Uint8("Bracket", bg.BracketID). + Uint8("ArenaType", arenaType). + Bool("IsRated", isRated). + Uint32("InstanceID", bg.InstanceID). + Uint32("MapID", bg.MapID). + Uint32("BattlegroupID", bg.BattleGroupID). + Uint32("AllianceArenaTeamID", arenaStart.allianceTeamID). + Uint32("HordeArenaTeamID", arenaStart.hordeTeamID). + Uint32("AllianceArenaMMR", arenaStart.allianceMMR). + Uint32("HordeArenaMMR", arenaStart.hordeMMR). Interface("AlliancePlayers", alliancePlayers). Interface("HordePlayers", hordePlayers). Msg("Created New Battleground") @@ -630,6 +757,10 @@ func (s *battleGroundService) PlayerLeftBattleground(ctx context.Context, player if isCrossrealm { realmKey.RealmID = 0 } + linkKey := BattlegroundKey{ + RealmID: realmKey.RealmID, + InstanceID: instanceID, + } err := s.battlegroundsRepo.UpdateBattleground(ctx, instanceID, realmKey, func(b *battleground.Battleground) error { b.RemovePlayer(playerGUID, realmID) @@ -637,17 +768,21 @@ func (s *battleGroundService) PlayerLeftBattleground(ctx context.Context, player }) if err != nil { if errors.Is(err, repo.ErrNotFound) { + s.removeBattlegroundLinkForPlayer(linkKey, playerGUID, realmID) return nil } return err } - s.removeBattlegroundLinkForPlayer(BattlegroundKey{ - RealmID: realmKey.RealmID, - InstanceID: instanceID, - BattlegroupID: 0, - }, playerGUID, realmID) + s.removeBattlegroundLinkForPlayer(linkKey, playerGUID, realmID) + + log.Debug(). + Uint64("playerGUID", playerGUID). + Uint32("realmID", realmID). + Uint32("instanceID", instanceID). + Bool("isCrossrealm", isCrossrealm). + Msg("TC9 PVP player left battleground") return nil } @@ -674,6 +809,13 @@ func (s *battleGroundService) PlayerJoinedBattleground(ctx context.Context, play return err } + log.Debug(). + Uint64("playerGUID", playerGUID). + Uint32("realmID", realmID). + Uint32("instanceID", instanceID). + Bool("isCrossrealm", isCrossrealm). + Msg("TC9 PVP player joined battleground") + return nil } @@ -681,12 +823,18 @@ func (s *battleGroundService) InviteGroups(ctx context.Context, groups []QueuedG bgGroups := make([]battleground.QueuedGroup, len(groups)) for i, group := range groups { bgGroups[i] = battleground.QueuedGroup{ - LeaderGUID: group.LeaderGUID, - Members: group.Members, - SlotsPerMember: group.SlotsPerMember, - RealmID: group.RealmID, - TeamID: group.TeamID, - EnqueuedTime: group.EnqueuedTime, + LeaderGUID: group.LeaderGUID, + Members: group.Members, + SlotsPerMember: group.SlotsPerMember, + RealmID: group.RealmID, + TeamID: group.TeamID, + ArenaType: group.ArenaType, + IsRated: group.IsRated, + ArenaTeamID: group.ArenaTeamID, + ArenaTeamRating: group.ArenaTeamRating, + ArenaMatchmakerRating: group.ArenaMatchmakerRating, + ArenaPreviousOpponentsTeamID: group.ArenaPreviousOpponentsTeamID, + EnqueuedTime: group.EnqueuedTime, } } @@ -749,6 +897,13 @@ func (s *battleGroundService) BattlegroundStatusChanged(ctx context.Context, sta return err } + log.Debug(). + Uint8("status", uint8(status)). + Uint32("realmID", realmID). + Uint32("instanceID", instanceID). + Bool("isCrossrealm", isCrossrealm). + Msg("TC9 PVP battleground status changed") + return nil } @@ -986,12 +1141,88 @@ func (s *battleGroundService) GetQueueOrBattlegroundLinkForPlayer(k QueuesByReal return s.playersQueueOrBattleground[k] } +func queuePVPOptions(allianceGroups, hordeGroups []QueuedGroup) (uint8, bool) { + for _, group := range allianceGroups { + return group.ArenaType, group.IsRated + } + for _, group := range hordeGroups { + return group.ArenaType, group.IsRated + } + return 0, false +} + +type arenaStartOptions struct { + allianceTeamID uint32 + hordeTeamID uint32 + allianceMMR uint32 + hordeMMR uint32 +} + +func arenaStartOptionsForGroups(allianceGroups, hordeGroups []QueuedGroup, isCrossRealm bool) arenaStartOptions { + var opts arenaStartOptions + for _, group := range allianceGroups { + if group.IsRated { + opts.allianceTeamID = arenaTeamIDForStart(group, isCrossRealm) + opts.allianceMMR = group.ArenaMatchmakerRating + break + } + } + for _, group := range hordeGroups { + if group.IsRated { + opts.hordeTeamID = arenaTeamIDForStart(group, isCrossRealm) + opts.hordeMMR = group.ArenaMatchmakerRating + break + } + } + return opts +} + +func arenaTeamIDForStart(group QueuedGroup, isCrossRealm bool) uint32 { + if !isCrossRealm { + return group.ArenaTeamID + } + return wowarena.NewCrossrealmTeamID(uint16(group.RealmID), group.ArenaTeamID) +} + +func (s *battleGroundService) previousRatedArenaOpponentID(realmID, teamID uint32) uint32 { + if teamID == 0 { + return 0 + } + key := wowarena.NewCrossrealmTeamID(uint16(realmID), teamID) + s.arenaPreviousOpponentsMutex.RLock() + defer s.arenaPreviousOpponentsMutex.RUnlock() + return s.arenaPreviousOpponents[key] +} + +func (s *battleGroundService) recordRatedArenaOpponents(allianceGroups, hordeGroups []QueuedGroup) { + allianceTeamID := ratedArenaTeamIDForOpponentTracking(allianceGroups) + hordeTeamID := ratedArenaTeamIDForOpponentTracking(hordeGroups) + if allianceTeamID == 0 || hordeTeamID == 0 { + return + } + + s.arenaPreviousOpponentsMutex.Lock() + defer s.arenaPreviousOpponentsMutex.Unlock() + s.arenaPreviousOpponents[allianceTeamID] = hordeTeamID + s.arenaPreviousOpponents[hordeTeamID] = allianceTeamID +} + +func ratedArenaTeamIDForOpponentTracking(groups []QueuedGroup) uint32 { + for _, group := range groups { + if group.IsRated && group.ArenaTeamID != 0 { + return wowarena.NewCrossrealmTeamID(uint16(group.RealmID), group.ArenaTeamID) + } + } + return 0 +} + func generateQueuesForAllBattlegroundTypes(service BattleGroundService, realmIDs []uint32, battlegroups []uint32) map[QueueByRealmOrBattlegroupKey]map[battleground.QueueTypeID]map[BracketID]PVPQueue { res := map[QueueByRealmOrBattlegroupKey]map[battleground.QueueTypeID]map[BracketID]PVPQueue{} types := []battleground.QueueTypeID{ battleground.QueueTypeIDAlteracValley, battleground.QueueTypeIDWarsongGulch, battleground.QueueTypeIDArathiBasin, + battleground.QueueTypeIDAllArenas, battleground.QueueTypeIDEyeOfTheStorm, battleground.QueueTypeIDIsleOfConquest, battleground.QueueTypeIDStrandOfTheAncients, diff --git a/apps/matchmakingserver/service/characters-listener.go b/apps/matchmakingserver/service/characters-listener.go index 6f5969f..0ae5ccc 100644 --- a/apps/matchmakingserver/service/characters-listener.go +++ b/apps/matchmakingserver/service/characters-listener.go @@ -2,6 +2,8 @@ package service import ( "context" + "errors" + "sync" "github.com/nats-io/nats.go" "github.com/rs/zerolog/log" @@ -9,21 +11,50 @@ import ( "github.com/walkline/ToCloud9/shared/events" ) +type characterOfflineBattlegroundService interface { + PlayerBecomeOffline(ctx context.Context, playerGUID uint64, realmID uint32) error +} + +type characterOfflineLFGService interface { + RemoveOfflinePlayer(ctx context.Context, realmID uint32, playerGUID uint64) error +} + type CharactersListener struct { - bg BattleGroundService + bg characterOfflineBattlegroundService + lfg characterOfflineLFGService nc *nats.Conn subs []*nats.Subscription + life *characterLifecycleTracker } -func NewCharactersListener(bgService BattleGroundService, nc *nats.Conn) *CharactersListener { +func NewCharactersListener(bgService characterOfflineBattlegroundService, lfgService characterOfflineLFGService, nc *nats.Conn) *CharactersListener { return &CharactersListener{ - bg: bgService, - nc: nc, + bg: bgService, + lfg: lfgService, + nc: nc, + life: newCharacterLifecycleTracker(), } } func (c *CharactersListener) Listen() error { - sb, err := c.nc.Subscribe(events.GWEventCharacterLoggedOut.SubjectName(), func(msg *nats.Msg) { + sb, err := c.nc.Subscribe(events.GWEventCharacterLoggedIn.SubjectName(), func(msg *nats.Msg) { + loggedInP := events.GWEventCharacterLoggedInPayload{} + _, err := events.Unmarshal(msg.Data, &loggedInP) + if err != nil { + log.Error().Err(err).Msg("can't read GWEventCharacterLoggedIn (payload part) event") + return + } + + c.lifecycle().record(loggedInP.RealmID, loggedInP.CharGUID, loggedInP.EventTimeUnixNano) + }) + if err != nil { + c.unsubscribe() + return err + } + + c.subs = append(c.subs, sb) + + sb, err = c.nc.Subscribe(events.GWEventCharacterLoggedOut.SubjectName(), func(msg *nats.Msg) { loggedOutP := events.GWEventCharacterLoggedOutPayload{} _, err := events.Unmarshal(msg.Data, &loggedOutP) if err != nil { @@ -31,7 +62,16 @@ func (c *CharactersListener) Listen() error { return } - err = c.bg.PlayerBecomeOffline(context.Background(), loggedOutP.CharGUID, loggedOutP.RealmID) + if !c.lifecycle().accept(loggedOutP.RealmID, loggedOutP.CharGUID, loggedOutP.EventTimeUnixNano) { + log.Debug(). + Uint32("realmID", loggedOutP.RealmID). + Uint64("playerGUID", loggedOutP.CharGUID). + Uint64("eventTimeUnixNano", loggedOutP.EventTimeUnixNano). + Msg("ignored stale matchmaking offline event") + return + } + + err = c.playerBecomeOffline(context.Background(), loggedOutP.RealmID, loggedOutP.CharGUID) if err != nil { log.Error().Err(err).Msg("can't remove character in GWEventCharacterLoggedOut event") return @@ -53,7 +93,17 @@ func (c *CharactersListener) Listen() error { } for _, char := range payload.CharactersGUID { - err = c.bg.PlayerBecomeOffline(context.Background(), char, payload.RealmID) + if !c.lifecycle().accept(payload.RealmID, char, payload.EventTimeUnixNano) { + log.Debug(). + Uint32("realmID", payload.RealmID). + Uint64("playerGUID", char). + Str("gatewayID", payload.GatewayID). + Uint64("eventTimeUnixNano", payload.EventTimeUnixNano). + Msg("ignored stale matchmaking unhealthy-gateway offline event") + continue + } + + err = c.playerBecomeOffline(context.Background(), payload.RealmID, char) if err != nil { log.Error().Err(err).Msg("can't remove character in GWEventCharacterLoggedOut event") } @@ -69,6 +119,38 @@ func (c *CharactersListener) Listen() error { return nil } +func (c *CharactersListener) playerBecomeOffline(ctx context.Context, realmID uint32, playerGUID uint64) error { + var offlineErr error + + if c.bg != nil { + if err := c.bg.PlayerBecomeOffline(ctx, playerGUID, realmID); err != nil { + offlineErr = errors.Join(offlineErr, err) + } + } + + if c.lfg != nil { + if err := c.lfg.RemoveOfflinePlayer(ctx, realmID, playerGUID); err != nil { + if !errors.Is(err, ErrLFGNotFound) { + offlineErr = errors.Join(offlineErr, err) + } + } else { + log.Debug(). + Uint32("realmID", realmID). + Uint64("playerGUID", playerGUID). + Msg("removed offline character from LFG state") + } + } + + return offlineErr +} + +func (c *CharactersListener) lifecycle() *characterLifecycleTracker { + if c.life == nil { + c.life = newCharacterLifecycleTracker() + } + return c.life +} + func (c *CharactersListener) Stop() error { return c.unsubscribe() } @@ -82,3 +164,47 @@ func (c *CharactersListener) unsubscribe() error { return nil } + +type characterLifecycleKey struct { + realmID uint32 + playerGUID uint64 +} + +type characterLifecycleTracker struct { + mu sync.Mutex + eventTimes map[characterLifecycleKey]uint64 +} + +func newCharacterLifecycleTracker() *characterLifecycleTracker { + return &characterLifecycleTracker{ + eventTimes: map[characterLifecycleKey]uint64{}, + } +} + +func (t *characterLifecycleTracker) record(realmID uint32, playerGUID uint64, eventTimeUnixNano uint64) { + if eventTimeUnixNano == 0 { + return + } + key := characterLifecycleKey{realmID: realmID, playerGUID: playerGUID} + t.mu.Lock() + if t.eventTimes[key] < eventTimeUnixNano { + t.eventTimes[key] = eventTimeUnixNano + } + t.mu.Unlock() +} + +func (t *characterLifecycleTracker) accept(realmID uint32, playerGUID uint64, eventTimeUnixNano uint64) bool { + if eventTimeUnixNano == 0 { + return true + } + + key := characterLifecycleKey{realmID: realmID, playerGUID: playerGUID} + t.mu.Lock() + defer t.mu.Unlock() + + if last := t.eventTimes[key]; last > eventTimeUnixNano { + return false + } + t.eventTimes[key] = eventTimeUnixNano + return true +} diff --git a/apps/matchmakingserver/service/lfg.go b/apps/matchmakingserver/service/lfg.go new file mode 100644 index 0000000..f63cb39 --- /dev/null +++ b/apps/matchmakingserver/service/lfg.go @@ -0,0 +1,1852 @@ +package service + +import ( + "context" + "errors" + "fmt" + "sort" + "sync" + "time" + + "github.com/rs/zerolog/log" + "github.com/walkline/ToCloud9/apps/matchmakingserver/repo" + pbGroup "github.com/walkline/ToCloud9/gen/group/pb" + "github.com/walkline/ToCloud9/shared/events" + "github.com/walkline/ToCloud9/shared/wow/guid" + "google.golang.org/grpc" +) + +const ( + LFGRoleNone uint8 = 0x00 + LFGRoleLeader uint8 = 0x01 + LFGRoleTank uint8 = 0x02 + LFGRoleHealer uint8 = 0x04 + LFGRoleDamage uint8 = 0x08 + + LFGTanksNeeded uint8 = 1 + LFGHealersNeeded uint8 = 1 + LFGDamageNeeded uint8 = 3 + LFGMaxGroupSize uint8 = 5 + + LFGRoleCheckTimeout = 45 * time.Second + LFGProposalTimeout = 40 * time.Second + + lfgDungeonIDMask uint32 = 0x00FFFFFF +) + +var ( + ErrLFGAlreadyQueuedOrMatched = errors.New("lfg player already queued or matched") + ErrLFGInvalidDungeon = errors.New("lfg invalid dungeon") + ErrLFGInvalidMember = errors.New("lfg invalid member") + ErrLFGInvalidRoles = errors.New("lfg invalid roles") + ErrLFGNotFound = errors.New("lfg not found") + ErrLFGGroupFull = errors.New("lfg group full") + ErrLFGNotLeader = errors.New("lfg player is not group leader") + ErrLFGProposalMismatch = errors.New("lfg proposal mismatch") + ErrLFGMultiRealm = errors.New("lfg multi realm party is not allowed") +) + +type LFGState uint8 + +const ( + LFGStateNone LFGState = iota + LFGStateRolecheck + LFGStateQueued + LFGStateProposal + LFGStateBoot + LFGStateDungeon + LFGStateFinishedDungeon + LFGStateRaidBrowser +) + +type LFGProposalState uint8 + +const ( + LFGProposalInitiating LFGProposalState = iota + LFGProposalFailed + LFGProposalSuccess +) + +type LFGProposalFailure uint8 + +const ( + LFGProposalFailureNone LFGProposalFailure = iota + LFGProposalFailureFailed + LFGProposalFailureDeclined +) + +type LFGPlayerKey struct { + RealmID uint32 + PlayerGUID uint64 +} + +type LFGMember struct { + RealmID uint32 + PlayerGUID uint64 + Roles uint8 + Leader bool + WorldserverID string + QueueLeaderRealmID uint32 + QueueLeaderGUID uint64 +} + +type LFGProposalMember struct { + RealmID uint32 + PlayerGUID uint64 + SelectedRoles uint8 + AssignedRole uint8 + QueueLeaderRealmID uint32 + QueueLeaderGUID uint64 + WorldserverID string + Answered bool + Accepted bool +} + +type LFGStatus struct { + State LFGState + ProposalID uint32 + ProposalState LFGProposalState + ProposalFailure LFGProposalFailure + DungeonEntry uint32 + SelectedDungeons []uint32 + QueuedMembers []LFGMember + ProposalMembers []LFGProposalMember + QueuedTimeMilliseconds uint32 + TanksNeeded uint8 + HealersNeeded uint8 + DamageNeeded uint8 +} + +type LFGJoinData struct { + RealmID uint32 + LeaderGUID uint64 + Members []LFGMember + DungeonEntries []uint32 + Comment string +} + +//go:generate mockery --name=LFGService +type LFGService interface { + JoinLfg(ctx context.Context, data LFGJoinData) (*LFGStatus, error) + LeaveLfg(ctx context.Context, realmID uint32, playerGUID uint64) error + RemoveOfflinePlayer(ctx context.Context, realmID uint32, playerGUID uint64) error + SetLfgRoles(ctx context.Context, realmID uint32, playerGUID uint64, roles uint8) (*LFGStatus, error) + AnswerLfgProposal(ctx context.Context, realmID uint32, playerGUID uint64, proposalID uint32, accept bool) (*LFGStatus, error) + FailLfgProposal(ctx context.Context, realmID uint32, proposalID uint32) error + LfgStatus(ctx context.Context, realmID uint32, playerGUID uint64) (*LFGStatus, error) + CompleteLfgDungeon(ctx context.Context, completedDungeonEntry uint32, selectedDungeonEntry uint32, players []LFGPlayerKey) error + ProcessExpiredLfgProposals(ctx context.Context) +} + +type lfgCrossRealmNodes interface { + IsCrossRealmNodeAvailable() bool +} + +type lfgAcceptedGroupRegistrar interface { + RegisterAcceptedLfgGroup(ctx context.Context, in *pbGroup.RegisterAcceptedLfgGroupRequest, opts ...grpc.CallOption) (*pbGroup.RegisterAcceptedLfgGroupResponse, error) +} + +type lfgQueuedGroup struct { + realmID uint32 + battlegroupID uint32 + leader LFGPlayerKey + members []LFGMember + memberRoles map[LFGPlayerKey]uint8 + dungeonEntries []uint32 + selectedDungeonEntries []uint32 + comment string + queuedAt time.Time +} + +type lfgRoleCheck struct { + realmID uint32 + battlegroupID uint32 + leader LFGPlayerKey + members []LFGMember + memberRoles map[LFGPlayerKey]uint8 + dungeonEntries []uint32 + selectedDungeonEntries []uint32 + comment string + startedAt time.Time +} + +type lfgProposal struct { + id uint32 + realmID uint32 + leaderRealmID uint32 + battlegroupID uint32 + crossRealm bool + dungeonID uint32 + selectedDungeonEntries []uint32 + leader LFGPlayerKey + worldserverID string + state LFGProposalState + members []LFGProposalMember + memberIndex map[LFGPlayerKey]int + queuedGroups []*lfgQueuedGroup + createdAt time.Time +} + +type lfgService struct { + mut sync.RWMutex + now func() time.Time + eventsProducer events.MatchmakingServiceProducer + battleGroupsRepo repo.BattleGroupsRepository + crossRealmNodes lfgCrossRealmNodes + groupRegistrar lfgAcceptedGroupRegistrar + nextProposalID uint32 + queuedGroups map[LFGPlayerKey]*lfgQueuedGroup + playerQueueOwner map[LFGPlayerKey]LFGPlayerKey + roleChecks map[LFGPlayerKey]*lfgRoleCheck + playerRoleCheck map[LFGPlayerKey]LFGPlayerKey + proposals map[uint32]*lfgProposal + playerProposal map[LFGPlayerKey]uint32 + playerDungeon map[LFGPlayerKey]*LFGStatus +} + +func NewLFGService(producer ...events.MatchmakingServiceProducer) LFGService { + return NewLFGServiceWithClock(time.Now, producer...) +} + +func NewLFGServiceWithClock(now func() time.Time, producer ...events.MatchmakingServiceProducer) LFGService { + return NewLFGServiceWithClockAndBattleGroupsAndGroupRegistrar(now, nil, nil, nil, producer...) +} + +func NewLFGServiceWithBattleGroups(battleGroupsRepo repo.BattleGroupsRepository, crossRealmNodes lfgCrossRealmNodes, producer ...events.MatchmakingServiceProducer) LFGService { + return NewLFGServiceWithClockAndBattleGroupsAndGroupRegistrar(time.Now, battleGroupsRepo, crossRealmNodes, nil, producer...) +} + +func NewLFGServiceWithClockAndBattleGroups(now func() time.Time, battleGroupsRepo repo.BattleGroupsRepository, crossRealmNodes lfgCrossRealmNodes, producer ...events.MatchmakingServiceProducer) LFGService { + return NewLFGServiceWithClockAndBattleGroupsAndGroupRegistrar(now, battleGroupsRepo, crossRealmNodes, nil, producer...) +} + +func NewLFGServiceWithBattleGroupsAndGroupRegistrar(battleGroupsRepo repo.BattleGroupsRepository, crossRealmNodes lfgCrossRealmNodes, groupRegistrar lfgAcceptedGroupRegistrar, producer ...events.MatchmakingServiceProducer) LFGService { + return NewLFGServiceWithClockAndBattleGroupsAndGroupRegistrar(time.Now, battleGroupsRepo, crossRealmNodes, groupRegistrar, producer...) +} + +func NewLFGServiceWithClockAndBattleGroupsAndGroupRegistrar(now func() time.Time, battleGroupsRepo repo.BattleGroupsRepository, crossRealmNodes lfgCrossRealmNodes, groupRegistrar lfgAcceptedGroupRegistrar, producer ...events.MatchmakingServiceProducer) LFGService { + var eventsProducer events.MatchmakingServiceProducer + if len(producer) > 0 { + eventsProducer = producer[0] + } + + return &lfgService{ + now: now, + eventsProducer: eventsProducer, + battleGroupsRepo: battleGroupsRepo, + crossRealmNodes: crossRealmNodes, + groupRegistrar: groupRegistrar, + nextProposalID: 1, + queuedGroups: map[LFGPlayerKey]*lfgQueuedGroup{}, + playerQueueOwner: map[LFGPlayerKey]LFGPlayerKey{}, + roleChecks: map[LFGPlayerKey]*lfgRoleCheck{}, + playerRoleCheck: map[LFGPlayerKey]LFGPlayerKey{}, + proposals: map[uint32]*lfgProposal{}, + playerProposal: map[LFGPlayerKey]uint32{}, + playerDungeon: map[LFGPlayerKey]*LFGStatus{}, + } +} + +func (s *lfgService) JoinLfg(ctx context.Context, data LFGJoinData) (*LFGStatus, error) { + group, err := s.buildQueuedGroup(ctx, data) + if err != nil { + return nil, err + } + + s.mut.Lock() + + if existingGroup, ok := s.currentQueuedGroupForJoinLocked(group); ok { + status := s.statusForQueuedGroupLocked(existingGroup) + notifyPlayers := s.queuedStatusPlayerGUIDsLocked(existingGroup) + s.mut.Unlock() + s.publishStatus(notifyPlayers, status) + return status, nil + } + + for member := range group.memberRoles { + if _, ok := s.playerQueueOwner[member]; ok { + s.mut.Unlock() + return nil, ErrLFGAlreadyQueuedOrMatched + } + if _, ok := s.playerRoleCheck[member]; ok { + s.mut.Unlock() + return nil, ErrLFGAlreadyQueuedOrMatched + } + if _, ok := s.playerProposal[member]; ok { + s.mut.Unlock() + return nil, ErrLFGAlreadyQueuedOrMatched + } + if dungeonStatus, ok := s.playerDungeon[member]; ok { + if dungeonStatus != nil && dungeonStatus.State == LFGStateFinishedDungeon { + delete(s.playerDungeon, member) + } else { + s.mut.Unlock() + return nil, ErrLFGAlreadyQueuedOrMatched + } + } + } + + var status *LFGStatus + notifyPlayers := lfgMemberKeys(group.members) + if group.needsRoleCheck() { + roleCheck := newLfgRoleCheckFromGroup(group) + s.roleChecks[roleCheck.leader] = roleCheck + for member := range roleCheck.memberRoles { + s.playerRoleCheck[member] = roleCheck.leader + } + status = statusForRoleCheck(roleCheck) + s.mut.Unlock() + s.publishStatus(notifyPlayers, status) + return status, nil + } + + s.queuedGroups[group.leader] = group + for member := range group.memberRoles { + s.playerQueueOwner[member] = group.leader + } + + status = s.statusForQueuedGroupLocked(group) + notifyPlayers = s.queuedStatusPlayerGUIDsLocked(group) + proposal := s.tryCreateProposalLocked(group) + if proposalID, ok := s.playerProposal[group.leader]; ok { + status = s.statusForProposalLocked(proposalID, group.leader) + } + if proposal != nil { + notifyPlayers = proposalPlayerKeys(proposal) + } + s.mut.Unlock() + s.publishStatus(notifyPlayers, status) + return status, nil +} + +func (s *lfgService) currentQueuedGroupForJoinLocked(group *lfgQueuedGroup) (*lfgQueuedGroup, bool) { + if group == nil || len(group.memberRoles) == 0 { + return nil, false + } + + for member := range group.memberRoles { + leader, ok := s.playerQueueOwner[member] + if !ok || leader != group.leader { + return nil, false + } + } + + existingGroup := s.queuedGroups[group.leader] + if !lfgQueuedGroupsHaveSameMembers(existingGroup, group) { + return nil, false + } + + return existingGroup, true +} + +func (s *lfgService) LeaveLfg(ctx context.Context, realmID uint32, playerGUID uint64) error { + return s.removePlayerFromLfg(ctx, realmID, playerGUID, true) +} + +func (s *lfgService) RemoveOfflinePlayer(ctx context.Context, realmID uint32, playerGUID uint64) error { + return s.removePlayerFromLfg(ctx, realmID, playerGUID, false) +} + +func (s *lfgService) removePlayerFromLfg(_ context.Context, realmID uint32, playerGUID uint64, requireLeader bool) error { + key := LFGPlayerKey{RealmID: realmID, PlayerGUID: playerGUID} + + s.mut.Lock() + + if leader, ok := s.playerQueueOwner[key]; ok { + if requireLeader && leader != key { + s.mut.Unlock() + return ErrLFGNotLeader + } + group := s.queuedGroups[leader] + notifyPlayers := []LFGPlayerKey{key} + var remainingPlayers []LFGPlayerKey + var remainingStatus *LFGStatus + if group != nil { + notifyPlayers = lfgMemberKeys(group.members) + } + s.removeQueuedGroupLocked(leader) + if group != nil { + remainingGroups := s.compatibleGroupsForQueuedGroupLocked(group) + if len(remainingGroups) > 0 { + remainingPlayers = lfgMemberKeysForQueuedGroups(remainingGroups) + remainingStatus = s.statusForQueuedGroupLocked(remainingGroups[0]) + } + } + s.mut.Unlock() + s.publishStatus(notifyPlayers, &LFGStatus{State: LFGStateNone}) + if len(remainingPlayers) > 0 { + s.publishStatus(remainingPlayers, remainingStatus) + } + return nil + } + + if leader, ok := s.playerRoleCheck[key]; ok { + if requireLeader && leader != key { + s.mut.Unlock() + return ErrLFGNotLeader + } + roleCheck := s.roleChecks[leader] + notifyPlayers := []LFGPlayerKey{key} + if roleCheck != nil { + notifyPlayers = lfgMemberKeys(roleCheck.members) + } + s.removeRoleCheckLocked(leader) + s.mut.Unlock() + s.publishStatus(notifyPlayers, &LFGStatus{State: LFGStateNone}) + return nil + } + + if proposalID, ok := s.playerProposal[key]; ok { + proposal := s.proposals[proposalID] + if proposal == nil { + s.mut.Unlock() + return ErrLFGNotFound + } + queueLeader := proposalQueueLeaderForMember(proposal, key) + if requireLeader && queueLeader != key { + s.mut.Unlock() + return ErrLFGNotLeader + } + if idx, ok := proposal.memberIndex[key]; ok { + proposal.members[idx].Answered = true + proposal.members[idx].Accepted = false + } + notifyPlayers := proposalPlayerKeys(proposal) + status := statusForFailedProposal(proposal, LFGProposalFailureDeclined) + s.failProposalLocked(proposalID, LFGProposalFailureDeclined) + s.mut.Unlock() + s.publishStatus(notifyPlayers, status) + return nil + } + + if _, ok := s.playerDungeon[key]; ok { + delete(s.playerDungeon, key) + s.mut.Unlock() + s.publishStatus([]LFGPlayerKey{key}, &LFGStatus{State: LFGStateNone}) + return nil + } + + s.mut.Unlock() + return ErrLFGNotFound +} + +func (s *lfgService) SetLfgRoles(_ context.Context, realmID uint32, playerGUID uint64, roles uint8) (*LFGStatus, error) { + normalizedRoles := normalizeLfgRoles(roles) + if normalizedRoles == LFGRoleNone { + return nil, ErrLFGInvalidRoles + } + + key := LFGPlayerKey{RealmID: realmID, PlayerGUID: playerGUID} + + s.mut.Lock() + + if leader, ok := s.playerRoleCheck[key]; ok { + roleCheck := s.roleChecks[leader] + if roleCheck == nil { + s.mut.Unlock() + return nil, ErrLFGNotFound + } + updateLfgMemberRoles(roleCheck.members, key, normalizedRoles) + roleCheck.memberRoles[key] = normalizedRoles + notifyPlayers := lfgMemberKeys(roleCheck.members) + + if roleCheck.ready() { + group := newQueuedGroupFromRoleCheck(roleCheck, s.now()) + s.removeRoleCheckLocked(leader) + s.queuedGroups[group.leader] = group + for member := range group.memberRoles { + s.playerQueueOwner[member] = group.leader + } + status := s.statusForQueuedGroupLocked(group) + notifyPlayers = s.queuedStatusPlayerGUIDsLocked(group) + proposal := s.tryCreateProposalLocked(group) + if proposalID, ok := s.playerProposal[key]; ok { + status = s.statusForProposalLocked(proposalID, key) + } + if proposal != nil { + notifyPlayers = proposalPlayerKeys(proposal) + } + s.mut.Unlock() + s.publishStatus(notifyPlayers, status) + return status, nil + } + + status := statusForRoleCheck(roleCheck) + s.mut.Unlock() + s.publishStatus(notifyPlayers, status) + return status, nil + } + + leader, ok := s.playerQueueOwner[key] + if !ok { + s.mut.Unlock() + return nil, ErrLFGNotFound + } + + group := s.queuedGroups[leader] + if group == nil { + s.mut.Unlock() + return nil, ErrLFGNotFound + } + + updateLfgMemberRoles(group.members, key, normalizedRoles) + group.memberRoles[key] = normalizedRoles + + status := s.statusForQueuedGroupLocked(group) + notifyPlayers := s.queuedStatusPlayerGUIDsLocked(group) + proposal := s.tryCreateProposalLocked(group) + if proposalID, ok := s.playerProposal[key]; ok { + status = s.statusForProposalLocked(proposalID, key) + } + if proposal != nil { + notifyPlayers = proposalPlayerKeys(proposal) + } + s.mut.Unlock() + s.publishStatus(notifyPlayers, status) + return status, nil +} + +func (s *lfgService) AnswerLfgProposal(ctx context.Context, realmID uint32, playerGUID uint64, proposalID uint32, accept bool) (*LFGStatus, error) { + key := LFGPlayerKey{RealmID: realmID, PlayerGUID: playerGUID} + + s.mut.Lock() + + activeProposalID, ok := s.playerProposal[key] + if !ok { + s.mut.Unlock() + return nil, ErrLFGNotFound + } + if activeProposalID != proposalID { + s.mut.Unlock() + return nil, ErrLFGProposalMismatch + } + + proposal := s.proposals[proposalID] + if proposal == nil { + s.mut.Unlock() + return nil, ErrLFGNotFound + } + + idx, ok := proposal.memberIndex[key] + if !ok { + s.mut.Unlock() + return nil, ErrLFGNotFound + } + + proposal.members[idx].Answered = true + proposal.members[idx].Accepted = accept + notifyPlayers := proposalPlayerKeys(proposal) + + if !accept { + status := statusForFailedProposal(proposal, LFGProposalFailureDeclined) + s.failProposalLocked(proposalID, LFGProposalFailureDeclined) + s.mut.Unlock() + s.publishStatus(notifyPlayers, status) + return status, nil + } + + allAccepted := true + for _, member := range proposal.members { + if !member.Answered || !member.Accepted { + allAccepted = false + break + } + } + var acceptedPayload *events.MatchmakingEventLfgProposalAcceptedPayload + var completedStatus map[LFGPlayerKey]*LFGStatus + if allAccepted && proposal.state != LFGProposalSuccess { + proposal.state = LFGProposalSuccess + acceptedPayload = lfgProposalAcceptedEventPayload(proposal) + completedStatus = playerDungeonStatusesForProposal(proposal) + } + + status := s.statusForProposalLocked(proposalID, key) + if acceptedPayload != nil { + if err := s.registerAcceptedLfgGroup(ctx, acceptedPayload); err != nil { + failedStatus := statusForFailedProposal(s.proposals[proposalID], LFGProposalFailureFailed) + s.failProposalLocked(proposalID, LFGProposalFailureFailed) + s.mut.Unlock() + s.publishStatus(notifyPlayers, failedStatus) + return failedStatus, nil + } + + for member, dungeonStatus := range completedStatus { + s.playerDungeon[member] = dungeonStatus + delete(s.playerProposal, member) + } + delete(s.proposals, proposalID) + } + s.mut.Unlock() + + s.publishStatus(notifyPlayers, status) + s.publishProposalAccepted(acceptedPayload) + return status, nil +} + +func (s *lfgService) LfgStatus(_ context.Context, realmID uint32, playerGUID uint64) (*LFGStatus, error) { + key := LFGPlayerKey{RealmID: realmID, PlayerGUID: playerGUID} + + s.mut.RLock() + defer s.mut.RUnlock() + + return s.statusForPlayerLocked(key), nil +} + +func (s *lfgService) CompleteLfgDungeon(_ context.Context, completedDungeonEntry uint32, selectedDungeonEntry uint32, players []LFGPlayerKey) error { + if len(players) == 0 { + return nil + } + + changedStatuses := map[LFGPlayerKey]*LFGStatus{} + s.mut.Lock() + for _, player := range players { + if player.RealmID == 0 || player.PlayerGUID == 0 { + continue + } + status, ok := s.playerDungeon[player] + if !ok || status == nil || status.State != LFGStateDungeon { + continue + } + if !lfgStatusMatchesSelectedDungeon(status, selectedDungeonEntry) { + continue + } + + finished := cloneLFGStatus(status) + finished.State = LFGStateFinishedDungeon + if completedDungeonEntry != 0 { + finished.DungeonEntry = completedDungeonEntry + } + s.playerDungeon[player] = finished + changedStatuses[player] = finished + } + s.mut.Unlock() + + if len(changedStatuses) > 0 { + log.Info(). + Uint32("completedDungeonEntry", completedDungeonEntry). + Uint32("selectedDungeonEntry", selectedDungeonEntry). + Int("players", len(changedStatuses)). + Msg("completed LFG dungeon") + } + for player, status := range changedStatuses { + s.publishStatus([]LFGPlayerKey{player}, status) + } + return nil +} + +func (s *lfgService) FailLfgProposal(_ context.Context, realmID uint32, proposalID uint32) error { + s.mut.Lock() + + proposal := s.proposals[proposalID] + if proposal == nil || proposal.realmID != realmID { + s.mut.Unlock() + return ErrLFGNotFound + } + + notifyPlayers := proposalPlayerKeys(proposal) + status := statusForFailedProposal(proposal, LFGProposalFailureFailed) + s.failProposalLocked(proposalID, LFGProposalFailureFailed) + s.mut.Unlock() + + s.publishStatus(notifyPlayers, status) + return nil +} + +func (s *lfgService) ProcessExpiredLfgProposals(ctx context.Context) { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case now := <-ticker.C: + s.expireLfgRoleChecks(now) + s.expireLfgProposals(now) + } + } +} + +func (s *lfgService) expireLfgRoleChecks(now time.Time) { + type expiredEvent struct { + players []LFGPlayerKey + } + + expiredEvents := []expiredEvent{} + s.mut.Lock() + for leader, roleCheck := range s.roleChecks { + if now.Before(roleCheck.startedAt.Add(LFGRoleCheckTimeout)) { + continue + } + + expiredEvents = append(expiredEvents, expiredEvent{ + players: lfgMemberKeys(roleCheck.members), + }) + s.removeRoleCheckLocked(leader) + } + s.mut.Unlock() + + for _, event := range expiredEvents { + s.publishStatus(event.players, &LFGStatus{State: LFGStateNone}) + } +} + +func (s *lfgService) expireLfgProposals(now time.Time) { + type expiredEvent struct { + players []LFGPlayerKey + status *LFGStatus + } + + expiredEvents := []expiredEvent{} + s.mut.Lock() + for proposalID, proposal := range s.proposals { + if proposal.state != LFGProposalInitiating || now.Before(proposal.createdAt.Add(LFGProposalTimeout)) { + continue + } + + expiredEvents = append(expiredEvents, expiredEvent{ + players: proposalPlayerKeys(proposal), + status: statusForFailedProposal(proposal, LFGProposalFailureFailed), + }) + s.failProposalLocked(proposalID, LFGProposalFailureFailed) + } + s.mut.Unlock() + + for _, event := range expiredEvents { + s.publishStatus(event.players, event.status) + } +} + +func (s *lfgService) buildQueuedGroup(ctx context.Context, data LFGJoinData) (*lfgQueuedGroup, error) { + if data.RealmID == 0 || data.LeaderGUID == 0 { + return nil, ErrLFGInvalidMember + } + if len(data.DungeonEntries) == 0 { + return nil, ErrLFGInvalidDungeon + } + + dungeons, err := normalizeDungeonEntries(data.DungeonEntries) + if err != nil { + return nil, err + } + selectedDungeons, err := normalizeSelectedDungeonEntries(data.DungeonEntries) + if err != nil { + return nil, err + } + + leaderKey := LFGPlayerKey{RealmID: data.RealmID, PlayerGUID: data.LeaderGUID} + members := normalizeLfgMembers(leaderKey, data.Members) + if len(members) == 0 { + return nil, ErrLFGInvalidMember + } + if len(members) > int(LFGMaxGroupSize) { + return nil, ErrLFGGroupFull + } + + memberRoles := make(map[LFGPlayerKey]uint8, len(members)) + leaderPresent := false + for i := range members { + if members[i].RealmID == 0 { + members[i].RealmID = data.RealmID + } + if members[i].PlayerGUID == 0 { + return nil, ErrLFGInvalidMember + } + members[i].Roles = normalizeLfgRoles(members[i].Roles) + memberKey := lfgMemberKey(members[i]) + if memberKey == leaderKey { + members[i].Leader = true + leaderPresent = true + } + + if _, exists := memberRoles[memberKey]; exists { + return nil, ErrLFGInvalidMember + } + memberRoles[memberKey] = members[i].Roles + } + if !leaderPresent { + return nil, ErrLFGInvalidMember + } + if len(members) == 1 && members[0].Roles == LFGRoleNone { + return nil, ErrLFGInvalidRoles + } + + battlegroupID, err := s.battlegroupForLfgMembers(ctx, members) + if err != nil { + return nil, err + } + + return &lfgQueuedGroup{ + realmID: data.RealmID, + battlegroupID: battlegroupID, + leader: leaderKey, + members: members, + memberRoles: memberRoles, + dungeonEntries: dungeons, + selectedDungeonEntries: selectedDungeons, + comment: data.Comment, + queuedAt: s.now(), + }, nil +} + +func normalizeDungeonEntries(entries []uint32) ([]uint32, error) { + seen := map[uint32]struct{}{} + res := make([]uint32, 0, len(entries)) + for _, entry := range entries { + dungeonID := entry & lfgDungeonIDMask + if dungeonID == 0 { + return nil, ErrLFGInvalidDungeon + } + if _, ok := seen[dungeonID]; ok { + continue + } + seen[dungeonID] = struct{}{} + res = append(res, dungeonID) + } + sort.Slice(res, func(i, j int) bool { return res[i] < res[j] }) + return res, nil +} + +func normalizeSelectedDungeonEntries(entries []uint32) ([]uint32, error) { + seen := map[uint32]struct{}{} + res := make([]uint32, 0, len(entries)) + for _, entry := range entries { + if entry&lfgDungeonIDMask == 0 { + return nil, ErrLFGInvalidDungeon + } + if _, ok := seen[entry]; ok { + continue + } + seen[entry] = struct{}{} + res = append(res, entry) + } + sort.Slice(res, func(i, j int) bool { + leftID := res[i] & lfgDungeonIDMask + rightID := res[j] & lfgDungeonIDMask + if leftID != rightID { + return leftID < rightID + } + return res[i] < res[j] + }) + return res, nil +} + +func normalizeLfgMembers(leader LFGPlayerKey, members []LFGMember) []LFGMember { + res := make([]LFGMember, 0, len(members)+1) + leaderSeen := false + for _, member := range members { + if member.RealmID == 0 { + member.RealmID = leader.RealmID + } + if lfgMemberKey(member) == leader { + member.Leader = true + leaderSeen = true + } + res = append(res, member) + } + if !leaderSeen { + res = append(res, LFGMember{ + RealmID: leader.RealmID, + PlayerGUID: leader.PlayerGUID, + Leader: true, + }) + } + sort.SliceStable(res, func(i, j int) bool { + if res[i].Leader != res[j].Leader { + return res[i].Leader + } + if res[i].RealmID != res[j].RealmID { + return res[i].RealmID < res[j].RealmID + } + return res[i].PlayerGUID < res[j].PlayerGUID + }) + return res +} + +func normalizeLfgRoles(roles uint8) uint8 { + return roles & (LFGRoleTank | LFGRoleHealer | LFGRoleDamage) +} + +func lfgMemberKey(member LFGMember) LFGPlayerKey { + return LFGPlayerKey{ + RealmID: member.RealmID, + PlayerGUID: member.PlayerGUID, + } +} + +func (s *lfgService) battlegroupForLfgMembers(ctx context.Context, members []LFGMember) (uint32, error) { + if len(members) == 0 { + return 0, ErrLFGInvalidMember + } + firstRealmID := members[0].RealmID + multiRealm := false + for _, member := range members { + if member.RealmID == 0 { + return 0, ErrLFGInvalidMember + } + if member.RealmID != firstRealmID { + multiRealm = true + } + } + + if s.battleGroupsRepo == nil || s.crossRealmNodes == nil || !s.crossRealmNodes.IsCrossRealmNodeAvailable() { + if multiRealm { + return 0, ErrLFGMultiRealm + } + return 0, nil + } + + var battlegroupID uint32 + for _, member := range members { + memberBattlegroupID, err := s.battleGroupsRepo.BattleGroupIDByRealmID(ctx, member.RealmID) + if err != nil { + return 0, err + } + if memberBattlegroupID == 0 { + if multiRealm { + return 0, ErrLFGMultiRealm + } + return 0, nil + } + if battlegroupID == 0 { + battlegroupID = memberBattlegroupID + continue + } + if memberBattlegroupID != battlegroupID { + return 0, ErrLFGMultiRealm + } + } + return battlegroupID, nil +} + +func (g *lfgQueuedGroup) needsRoleCheck() bool { + return len(g.members) > 1 +} + +func (r *lfgRoleCheck) ready() bool { + for _, roles := range r.memberRoles { + if roles == LFGRoleNone { + return false + } + } + return true +} + +func newLfgRoleCheckFromGroup(group *lfgQueuedGroup) *lfgRoleCheck { + members := append([]LFGMember(nil), group.members...) + memberRoles := make(map[LFGPlayerKey]uint8, len(group.memberRoles)) + for i := range members { + key := lfgMemberKey(members[i]) + if key == group.leader { + memberRoles[key] = members[i].Roles + continue + } + members[i].Roles = LFGRoleNone + memberRoles[key] = LFGRoleNone + } + + return &lfgRoleCheck{ + realmID: group.realmID, + battlegroupID: group.battlegroupID, + leader: group.leader, + members: members, + memberRoles: memberRoles, + dungeonEntries: append([]uint32(nil), group.dungeonEntries...), + selectedDungeonEntries: append([]uint32(nil), group.selectedDungeonEntries...), + comment: group.comment, + startedAt: group.queuedAt, + } +} + +func newQueuedGroupFromRoleCheck(roleCheck *lfgRoleCheck, queuedAt time.Time) *lfgQueuedGroup { + return &lfgQueuedGroup{ + realmID: roleCheck.realmID, + battlegroupID: roleCheck.battlegroupID, + leader: roleCheck.leader, + members: append([]LFGMember(nil), roleCheck.members...), + memberRoles: cloneLfgMemberRoles(roleCheck.memberRoles), + dungeonEntries: append([]uint32(nil), roleCheck.dungeonEntries...), + selectedDungeonEntries: append([]uint32(nil), roleCheck.selectedDungeonEntries...), + comment: roleCheck.comment, + queuedAt: queuedAt, + } +} + +func cloneLfgQueuedGroup(group *lfgQueuedGroup) *lfgQueuedGroup { + if group == nil { + return nil + } + + return &lfgQueuedGroup{ + realmID: group.realmID, + battlegroupID: group.battlegroupID, + leader: group.leader, + members: append([]LFGMember(nil), group.members...), + memberRoles: cloneLfgMemberRoles(group.memberRoles), + dungeonEntries: append([]uint32(nil), group.dungeonEntries...), + selectedDungeonEntries: append([]uint32(nil), group.selectedDungeonEntries...), + comment: group.comment, + queuedAt: group.queuedAt, + } +} + +func cloneLfgQueuedGroups(groups []*lfgQueuedGroup) []*lfgQueuedGroup { + res := make([]*lfgQueuedGroup, 0, len(groups)) + for _, group := range groups { + res = append(res, cloneLfgQueuedGroup(group)) + } + return res +} + +func cloneLfgMemberRoles(src map[LFGPlayerKey]uint8) map[LFGPlayerKey]uint8 { + dst := make(map[LFGPlayerKey]uint8, len(src)) + for key, roles := range src { + dst[key] = roles + } + return dst +} + +func updateLfgMemberRoles(members []LFGMember, player LFGPlayerKey, roles uint8) { + for i := range members { + if lfgMemberKey(members[i]) == player { + members[i].Roles = roles + return + } + } +} + +func (s *lfgService) tryCreateProposalLocked(newGroup *lfgQueuedGroup) *lfgProposal { + if newGroup == nil { + return nil + } + + for _, dungeonID := range newGroup.dungeonEntries { + groups := s.compatibleGroupsForDungeonLocked(newGroup, dungeonID) + selectedGroups, selectedMembers := chooseLfgGroups(groups) + if len(selectedGroups) == 0 { + continue + } + + assignments, ok := assignLfgRoles(selectedMembers) + if !ok { + continue + } + + crossRealm := lfgGroupsCrossRealm(selectedGroups) + proposalRealmID := selectedGroups[0].realmID + if crossRealm { + proposalRealmID = 0 + } + + proposal := &lfgProposal{ + id: s.nextProposalID, + realmID: proposalRealmID, + leaderRealmID: selectedGroups[0].leader.RealmID, + battlegroupID: selectedGroups[0].battlegroupID, + crossRealm: crossRealm, + dungeonID: dungeonID, + selectedDungeonEntries: selectedDungeonEntriesForProposal(selectedGroups, dungeonID), + leader: selectedGroups[0].leader, + worldserverID: selectedGroups[0].leaderWorldserverID(), + state: LFGProposalInitiating, + members: make([]LFGProposalMember, 0, len(selectedMembers)), + memberIndex: map[LFGPlayerKey]int{}, + queuedGroups: cloneLfgQueuedGroups(selectedGroups), + createdAt: s.now(), + } + s.nextProposalID++ + + queueLeaderByMember := queueLeaderByMemberKey(selectedGroups) + for _, member := range selectedMembers { + key := lfgMemberKey(member) + queueLeader := queueLeaderByMember[key] + proposal.memberIndex[key] = len(proposal.members) + proposal.members = append(proposal.members, LFGProposalMember{ + RealmID: member.RealmID, + PlayerGUID: member.PlayerGUID, + SelectedRoles: member.Roles, + AssignedRole: assignments[key], + QueueLeaderRealmID: queueLeader.RealmID, + QueueLeaderGUID: queueLeader.PlayerGUID, + WorldserverID: member.WorldserverID, + }) + s.playerProposal[key] = proposal.id + } + + s.proposals[proposal.id] = proposal + for _, group := range selectedGroups { + s.removeQueuedGroupLocked(group.leader) + } + return proposal + } + return nil +} + +func (s *lfgService) compatibleGroupsForDungeonLocked(base *lfgQueuedGroup, dungeonID uint32) []*lfgQueuedGroup { + groups := make([]*lfgQueuedGroup, 0, len(s.queuedGroups)) + for _, group := range s.queuedGroups { + if !lfgGroupsShareQueueScope(base, group) || !containsDungeon(group.dungeonEntries, dungeonID) { + continue + } + groups = append(groups, group) + } + sort.Slice(groups, func(i, j int) bool { + if groups[i].queuedAt.Equal(groups[j].queuedAt) { + if groups[i].leader.RealmID != groups[j].leader.RealmID { + return groups[i].leader.RealmID < groups[j].leader.RealmID + } + return groups[i].leader.PlayerGUID < groups[j].leader.PlayerGUID + } + return groups[i].queuedAt.Before(groups[j].queuedAt) + }) + return groups +} + +func (s *lfgService) compatibleGroupsForQueuedGroupLocked(base *lfgQueuedGroup) []*lfgQueuedGroup { + if base == nil { + return nil + } + + groups := make([]*lfgQueuedGroup, 0, len(s.queuedGroups)) + for _, group := range s.queuedGroups { + if !lfgGroupsShareQueueScope(base, group) || !hasSharedDungeon(group.dungeonEntries, base.dungeonEntries) { + continue + } + groups = append(groups, group) + } + sort.Slice(groups, func(i, j int) bool { + if groups[i].queuedAt.Equal(groups[j].queuedAt) { + if groups[i].leader.RealmID != groups[j].leader.RealmID { + return groups[i].leader.RealmID < groups[j].leader.RealmID + } + return groups[i].leader.PlayerGUID < groups[j].leader.PlayerGUID + } + return groups[i].queuedAt.Before(groups[j].queuedAt) + }) + return groups +} + +func lfgGroupsShareQueueScope(left, right *lfgQueuedGroup) bool { + if left == nil || right == nil { + return false + } + if left.realmID == right.realmID { + return true + } + if left.battlegroupID != 0 || right.battlegroupID != 0 { + return left.battlegroupID != 0 && left.battlegroupID == right.battlegroupID + } + return false +} + +func lfgGroupsCrossRealm(groups []*lfgQueuedGroup) bool { + var realmID uint32 + for _, group := range groups { + if group == nil { + continue + } + for _, member := range group.members { + if member.RealmID == 0 { + continue + } + if realmID == 0 { + realmID = member.RealmID + continue + } + if member.RealmID != realmID { + return true + } + } + } + return false +} + +func containsDungeon(entries []uint32, dungeonID uint32) bool { + for _, entry := range entries { + if entry == dungeonID { + return true + } + } + return false +} + +func hasSharedDungeon(left, right []uint32) bool { + for _, entry := range left { + if containsDungeon(right, entry) { + return true + } + } + return false +} + +func chooseLfgGroups(groups []*lfgQueuedGroup) ([]*lfgQueuedGroup, []LFGMember) { + selectedGroups := make([]*lfgQueuedGroup, 0, len(groups)) + selectedMembers := make([]LFGMember, 0, LFGMaxGroupSize) + if chooseLfgGroupsRecursive(groups, 0, selectedGroups, selectedMembers, 0, &selectedGroups, &selectedMembers) { + return selectedGroups, selectedMembers + } + return nil, nil +} + +func chooseLfgGroupsRecursive(groups []*lfgQueuedGroup, idx int, currentGroups []*lfgQueuedGroup, currentMembers []LFGMember, currentSize int, selectedGroups *[]*lfgQueuedGroup, selectedMembers *[]LFGMember) bool { + if currentSize == int(LFGMaxGroupSize) { + if _, ok := assignLfgRoles(currentMembers); ok { + *selectedGroups = append([]*lfgQueuedGroup(nil), currentGroups...) + *selectedMembers = append([]LFGMember(nil), currentMembers...) + return true + } + return false + } + if currentSize > int(LFGMaxGroupSize) || idx >= len(groups) { + return false + } + + group := groups[idx] + if currentSize+len(group.members) <= int(LFGMaxGroupSize) { + if chooseLfgGroupsRecursive(groups, idx+1, append(currentGroups, group), append(currentMembers, group.members...), currentSize+len(group.members), selectedGroups, selectedMembers) { + return true + } + } + return chooseLfgGroupsRecursive(groups, idx+1, currentGroups, currentMembers, currentSize, selectedGroups, selectedMembers) +} + +func assignLfgRoles(members []LFGMember) (map[LFGPlayerKey]uint8, bool) { + if len(members) != int(LFGMaxGroupSize) { + return nil, false + } + + assignments := map[LFGPlayerKey]uint8{} + if assignLfgRolesRecursive(members, 0, int(LFGTanksNeeded), int(LFGHealersNeeded), int(LFGDamageNeeded), assignments) { + return assignments, true + } + return nil, false +} + +func assignLfgRolesRecursive(members []LFGMember, idx, tanks, healers, damage int, assignments map[LFGPlayerKey]uint8) bool { + if idx == len(members) { + return tanks == 0 && healers == 0 && damage == 0 + } + + member := members[idx] + key := lfgMemberKey(member) + for _, role := range []uint8{LFGRoleTank, LFGRoleHealer, LFGRoleDamage} { + if member.Roles&role == 0 { + continue + } + switch role { + case LFGRoleTank: + if tanks == 0 { + continue + } + assignments[key] = role + if assignLfgRolesRecursive(members, idx+1, tanks-1, healers, damage, assignments) { + return true + } + case LFGRoleHealer: + if healers == 0 { + continue + } + assignments[key] = role + if assignLfgRolesRecursive(members, idx+1, tanks, healers-1, damage, assignments) { + return true + } + case LFGRoleDamage: + if damage == 0 { + continue + } + assignments[key] = role + if assignLfgRolesRecursive(members, idx+1, tanks, healers, damage-1, assignments) { + return true + } + } + delete(assignments, key) + } + return false +} + +func (s *lfgService) removeQueuedGroupLocked(leader LFGPlayerKey) { + group := s.queuedGroups[leader] + if group == nil { + return + } + for member := range group.memberRoles { + delete(s.playerQueueOwner, member) + } + delete(s.queuedGroups, leader) +} + +func (s *lfgService) removeRoleCheckLocked(leader LFGPlayerKey) { + roleCheck := s.roleChecks[leader] + if roleCheck == nil { + return + } + for member := range roleCheck.memberRoles { + delete(s.playerRoleCheck, member) + } + delete(s.roleChecks, leader) +} + +func (s *lfgService) failProposalLocked(proposalID uint32, failure LFGProposalFailure) { + proposal := s.proposals[proposalID] + if proposal == nil { + return + } + proposal.state = LFGProposalFailed + removedQueueLeaders := lfgProposalRemovedQueueLeaders(proposal, failure) + for _, group := range proposal.queuedGroups { + if group == nil { + continue + } + if _, removed := removedQueueLeaders[group.leader]; removed { + continue + } + queuedGroup := cloneLfgQueuedGroup(group) + s.queuedGroups[queuedGroup.leader] = queuedGroup + for member := range queuedGroup.memberRoles { + s.playerQueueOwner[member] = queuedGroup.leader + } + } + for _, member := range proposal.members { + delete(s.playerProposal, LFGPlayerKey{RealmID: member.RealmID, PlayerGUID: member.PlayerGUID}) + } + delete(s.proposals, proposalID) +} + +func (s *lfgService) statusForPlayerLocked(key LFGPlayerKey) *LFGStatus { + if proposalID, ok := s.playerProposal[key]; ok { + return s.statusForProposalLocked(proposalID, key) + } + if status, ok := s.playerDungeon[key]; ok { + return cloneLFGStatus(status) + } + if leader, ok := s.playerRoleCheck[key]; ok { + roleCheck := s.roleChecks[leader] + if roleCheck == nil { + return &LFGStatus{State: LFGStateNone} + } + return statusForRoleCheck(roleCheck) + } + if leader, ok := s.playerQueueOwner[key]; ok { + group := s.queuedGroups[leader] + if group == nil { + return &LFGStatus{State: LFGStateNone} + } + return s.statusForQueuedGroupLocked(group) + } + return &LFGStatus{State: LFGStateNone} +} + +func statusForRoleCheck(roleCheck *lfgRoleCheck) *LFGStatus { + return &LFGStatus{ + State: LFGStateRolecheck, + SelectedDungeons: append([]uint32(nil), roleCheck.selectedDungeonEntries...), + QueuedMembers: lfgMembersWithQueueLeader(roleCheck.members, roleCheck.leader), + TanksNeeded: LFGTanksNeeded, + HealersNeeded: LFGHealersNeeded, + DamageNeeded: LFGDamageNeeded, + } +} + +func (s *lfgService) statusForQueuedGroupLocked(group *lfgQueuedGroup) *LFGStatus { + if group == nil { + return &LFGStatus{State: LFGStateNone} + } + + groups := s.compatibleGroupsForQueuedGroupLocked(group) + members := lfgMembersForQueuedGroups(groups) + queuedAt := group.queuedAt + for _, candidate := range groups { + if candidate.queuedAt.Before(queuedAt) { + queuedAt = candidate.queuedAt + } + } + + return statusForQueuedMembers(group.selectedDungeonEntries, members, queuedAt, s.now()) +} + +func statusForQueuedMembers(dungeonEntries []uint32, members []LFGMember, queuedAt, now time.Time) *LFGStatus { + status := &LFGStatus{ + State: LFGStateQueued, + SelectedDungeons: append([]uint32(nil), dungeonEntries...), + QueuedMembers: append([]LFGMember(nil), members...), + TanksNeeded: LFGTanksNeeded, + HealersNeeded: LFGHealersNeeded, + DamageNeeded: LFGDamageNeeded, + } + if now.After(queuedAt) { + status.QueuedTimeMilliseconds = uint32(now.Sub(queuedAt).Seconds()) + } + for _, member := range members { + if member.Roles&LFGRoleTank != 0 && status.TanksNeeded > 0 { + status.TanksNeeded-- + } + if member.Roles&LFGRoleHealer != 0 && status.HealersNeeded > 0 { + status.HealersNeeded-- + } + if member.Roles&LFGRoleDamage != 0 && status.DamageNeeded > 0 { + status.DamageNeeded-- + } + } + return status +} + +func (s *lfgService) queuedStatusPlayerGUIDsLocked(group *lfgQueuedGroup) []LFGPlayerKey { + return lfgMemberKeysForQueuedGroups(s.compatibleGroupsForQueuedGroupLocked(group)) +} + +func (s *lfgService) statusForProposalLocked(proposalID uint32, _ LFGPlayerKey) *LFGStatus { + proposal := s.proposals[proposalID] + if proposal == nil { + return &LFGStatus{State: LFGStateNone} + } + state := LFGStateProposal + if proposal.state == LFGProposalSuccess { + state = LFGStateDungeon + } + status := &LFGStatus{ + State: state, + ProposalID: proposal.id, + ProposalState: proposal.state, + DungeonEntry: proposal.dungeonID, + SelectedDungeons: append([]uint32(nil), + proposal.selectedDungeonEntries...), + QueuedMembers: queuedMembersForProposal(proposal), + ProposalMembers: append([]LFGProposalMember(nil), proposal.members...), + } + sort.Slice(status.ProposalMembers, func(i, j int) bool { + if status.ProposalMembers[i].RealmID != status.ProposalMembers[j].RealmID { + return status.ProposalMembers[i].RealmID < status.ProposalMembers[j].RealmID + } + return status.ProposalMembers[i].PlayerGUID < status.ProposalMembers[j].PlayerGUID + }) + return status +} + +func statusForFailedProposal(proposal *lfgProposal, failure LFGProposalFailure) *LFGStatus { + if proposal == nil { + return &LFGStatus{State: LFGStateNone} + } + + proposalMembers := append([]LFGProposalMember(nil), proposal.members...) + if failure == LFGProposalFailureFailed { + for i := range proposalMembers { + if !proposalMembers[i].Answered { + proposalMembers[i].Answered = true + proposalMembers[i].Accepted = false + } + } + } + + status := &LFGStatus{ + State: LFGStateProposal, + ProposalID: proposal.id, + ProposalState: LFGProposalFailed, + ProposalFailure: failure, + DungeonEntry: proposal.dungeonID, + SelectedDungeons: append([]uint32(nil), + proposal.selectedDungeonEntries...), + QueuedMembers: queuedMembersForProposal(proposal), + ProposalMembers: proposalMembers, + } + sort.Slice(status.ProposalMembers, func(i, j int) bool { + if status.ProposalMembers[i].RealmID != status.ProposalMembers[j].RealmID { + return status.ProposalMembers[i].RealmID < status.ProposalMembers[j].RealmID + } + return status.ProposalMembers[i].PlayerGUID < status.ProposalMembers[j].PlayerGUID + }) + return status +} + +func selectedDungeonEntriesForProposal(groups []*lfgQueuedGroup, dungeonID uint32) []uint32 { + seen := map[uint32]struct{}{} + res := make([]uint32, 0, len(groups)) + for _, group := range groups { + for _, entry := range group.selectedDungeonEntries { + if entry&lfgDungeonIDMask != dungeonID&lfgDungeonIDMask { + continue + } + if _, ok := seen[entry]; ok { + continue + } + seen[entry] = struct{}{} + res = append(res, entry) + } + } + sort.Slice(res, func(i, j int) bool { return res[i] < res[j] }) + if len(res) == 0 && dungeonID != 0 { + res = append(res, dungeonID) + } + return res +} + +func playerDungeonStatusesForProposal(proposal *lfgProposal) map[LFGPlayerKey]*LFGStatus { + if proposal == nil { + return nil + } + res := make(map[LFGPlayerKey]*LFGStatus, len(proposal.members)) + queuedMembers := queuedMembersForProposal(proposal) + for _, member := range proposal.members { + res[LFGPlayerKey{RealmID: member.RealmID, PlayerGUID: member.PlayerGUID}] = &LFGStatus{ + State: LFGStateDungeon, + ProposalState: LFGProposalSuccess, + DungeonEntry: proposal.dungeonID, + SelectedDungeons: append([]uint32(nil), proposal.selectedDungeonEntries...), + QueuedMembers: append([]LFGMember(nil), queuedMembers...), + } + } + return res +} + +func queuedMembersForProposal(proposal *lfgProposal) []LFGMember { + if proposal == nil { + return nil + } + + members := make([]LFGMember, 0, len(proposal.members)) + for _, member := range proposal.members { + members = append(members, LFGMember{ + RealmID: member.RealmID, + PlayerGUID: member.PlayerGUID, + Roles: member.SelectedRoles, + Leader: member.QueueLeaderRealmID == member.RealmID && member.QueueLeaderGUID == member.PlayerGUID, + WorldserverID: member.WorldserverID, + QueueLeaderRealmID: member.QueueLeaderRealmID, + QueueLeaderGUID: member.QueueLeaderGUID, + }) + } + return members +} + +func proposalQueueLeaderForMember(proposal *lfgProposal, key LFGPlayerKey) LFGPlayerKey { + if proposal == nil { + return key + } + idx, ok := proposal.memberIndex[key] + if !ok || idx < 0 || idx >= len(proposal.members) { + return key + } + member := proposal.members[idx] + if member.QueueLeaderRealmID == 0 || member.QueueLeaderGUID == 0 { + return key + } + return LFGPlayerKey{RealmID: member.QueueLeaderRealmID, PlayerGUID: member.QueueLeaderGUID} +} + +func lfgProposalRemovedQueueLeaders(proposal *lfgProposal, failure LFGProposalFailure) map[LFGPlayerKey]struct{} { + res := map[LFGPlayerKey]struct{}{} + if proposal == nil { + return res + } + + for _, member := range proposal.members { + key := LFGPlayerKey{RealmID: member.RealmID, PlayerGUID: member.PlayerGUID} + queueLeader := proposalQueueLeaderForMember(proposal, key) + switch failure { + case LFGProposalFailureDeclined: + if member.Answered && !member.Accepted { + res[queueLeader] = struct{}{} + } + case LFGProposalFailureFailed: + if !member.Accepted { + res[queueLeader] = struct{}{} + } + } + } + + if failure == LFGProposalFailureFailed && len(res) == 0 { + for _, group := range proposal.queuedGroups { + if group != nil { + res[group.leader] = struct{}{} + } + } + } + return res +} + +func cloneLFGStatus(status *LFGStatus) *LFGStatus { + if status == nil { + return &LFGStatus{State: LFGStateNone} + } + clone := *status + clone.SelectedDungeons = append([]uint32(nil), status.SelectedDungeons...) + clone.QueuedMembers = append([]LFGMember(nil), status.QueuedMembers...) + clone.ProposalMembers = append([]LFGProposalMember(nil), status.ProposalMembers...) + return &clone +} + +func lfgStatusMatchesSelectedDungeon(status *LFGStatus, dungeonEntry uint32) bool { + if dungeonEntry == 0 { + return true + } + entry := dungeonEntry & lfgDungeonIDMask + if status.DungeonEntry&lfgDungeonIDMask == entry { + return true + } + for _, selectedDungeon := range status.SelectedDungeons { + if selectedDungeon&lfgDungeonIDMask == entry { + return true + } + } + return false +} + +func lfgMemberKeys(members []LFGMember) []LFGPlayerKey { + res := make([]LFGPlayerKey, 0, len(members)) + for _, member := range members { + res = append(res, lfgMemberKey(member)) + } + return res +} + +func lfgMembersForQueuedGroups(groups []*lfgQueuedGroup) []LFGMember { + var total int + for _, group := range groups { + total += len(group.members) + } + + res := make([]LFGMember, 0, total) + for _, group := range groups { + res = append(res, lfgMembersWithQueueLeader(group.members, group.leader)...) + } + return res +} + +func lfgMembersWithQueueLeader(members []LFGMember, leader LFGPlayerKey) []LFGMember { + res := make([]LFGMember, 0, len(members)) + for _, member := range members { + member.QueueLeaderRealmID = leader.RealmID + member.QueueLeaderGUID = leader.PlayerGUID + res = append(res, member) + } + return res +} + +func lfgQueuedGroupsHaveSameMembers(left, right *lfgQueuedGroup) bool { + if left == nil || right == nil || len(left.memberRoles) != len(right.memberRoles) { + return false + } + for member := range right.memberRoles { + if _, ok := left.memberRoles[member]; !ok { + return false + } + } + return true +} + +func (g *lfgQueuedGroup) leaderWorldserverID() string { + if g == nil { + return "" + } + + for _, member := range g.members { + if lfgMemberKey(member) == g.leader { + return member.WorldserverID + } + } + return "" +} + +func lfgMemberKeysForQueuedGroups(groups []*lfgQueuedGroup) []LFGPlayerKey { + var total int + for _, group := range groups { + total += len(group.members) + } + + res := make([]LFGPlayerKey, 0, total) + for _, group := range groups { + res = append(res, lfgMemberKeys(group.members)...) + } + return res +} + +func queueLeaderByMemberKey(groups []*lfgQueuedGroup) map[LFGPlayerKey]LFGPlayerKey { + res := map[LFGPlayerKey]LFGPlayerKey{} + for _, group := range groups { + if group == nil { + continue + } + for _, member := range group.members { + res[lfgMemberKey(member)] = group.leader + } + } + return res +} + +func proposalPlayerKeys(proposal *lfgProposal) []LFGPlayerKey { + if proposal == nil { + return nil + } + res := make([]LFGPlayerKey, 0, len(proposal.members)) + for _, member := range proposal.members { + res = append(res, LFGPlayerKey{RealmID: member.RealmID, PlayerGUID: member.PlayerGUID}) + } + return res +} + +func (s *lfgService) publishStatus(players []LFGPlayerKey, status *LFGStatus) { + if s.eventsProducer == nil || len(players) == 0 { + return + } + + playersByRealm := map[uint32][]guid.LowType{} + for _, player := range players { + if player.RealmID == 0 || player.PlayerGUID == 0 { + continue + } + playersByRealm[player.RealmID] = append(playersByRealm[player.RealmID], guid.LowType(player.PlayerGUID)) + } + statusPayload := lfgStatusToEventPayload(status) + for realmID, playerGUIDs := range playersByRealm { + payload := &events.MatchmakingEventLfgStatusChangedPayload{ + RealmID: realmID, + PlayersGUID: playerGUIDs, + Status: statusPayload, + } + if err := s.eventsProducer.LfgStatusChanged(payload); err != nil { + log.Error(). + Err(err). + Uint32("realmID", realmID). + Int("players", len(playerGUIDs)). + Msg("failed to publish LFG status changed event") + } + } +} + +func (s *lfgService) publishProposalAccepted(payload *events.MatchmakingEventLfgProposalAcceptedPayload) { + if s.eventsProducer == nil || payload == nil || len(payload.PlayersGUID) == 0 { + return + } + + if err := s.eventsProducer.LfgProposalAccepted(payload); err != nil { + log.Error(). + Err(err). + Uint32("realmID", payload.RealmID). + Uint32("proposalID", payload.ProposalID). + Int("players", len(payload.PlayersGUID)). + Msg("failed to publish LFG proposal accepted event") + } +} + +func (s *lfgService) registerAcceptedLfgGroup(ctx context.Context, payload *events.MatchmakingEventLfgProposalAcceptedPayload) error { + if s.groupRegistrar == nil || payload == nil || len(payload.Members) == 0 { + return nil + } + + members := make([]*pbGroup.AcceptedLfgGroupMember, 0, len(payload.Members)) + for _, member := range payload.Members { + members = append(members, &pbGroup.AcceptedLfgGroupMember{ + RealmID: member.RealmID, + PlayerGUID: uint64(member.PlayerGUID), + SelectedRoles: uint32(member.SelectedRoles), + AssignedRole: uint32(member.AssignedRole), + QueueLeaderRealmID: member.QueueLeaderRealmID, + QueueLeaderGUID: uint64(member.QueueLeaderGUID), + }) + } + + res, err := s.groupRegistrar.RegisterAcceptedLfgGroup(ctx, &pbGroup.RegisterAcceptedLfgGroupRequest{ + RealmID: payload.RealmID, + ProposalID: payload.ProposalID, + DungeonEntry: payload.DungeonEntry, + LeaderRealmID: payload.LeaderRealmID, + LeaderGUID: uint64(payload.LeaderGUID), + CrossRealm: payload.CrossRealm, + Members: members, + }) + if err != nil { + log.Error(). + Err(err). + Uint32("realmID", payload.RealmID). + Uint32("leaderRealmID", payload.LeaderRealmID). + Uint32("proposalID", payload.ProposalID). + Int("members", len(payload.Members)). + Msg("failed to register accepted LFG group") + return err + } + if res == nil { + return fmt.Errorf("accepted LFG group registration returned nil response") + } + if res.GetGroupID() == 0 { + return fmt.Errorf("accepted LFG group registration returned empty group id") + } + payload.GroupID = res.GetGroupID() + return nil +} + +func lfgProposalAcceptedEventPayload(proposal *lfgProposal) *events.MatchmakingEventLfgProposalAcceptedPayload { + if proposal == nil { + return nil + } + + payload := &events.MatchmakingEventLfgProposalAcceptedPayload{ + RealmID: proposal.realmID, + LeaderRealmID: proposal.leaderRealmID, + BattlegroupID: proposal.battlegroupID, + CrossRealm: proposal.crossRealm, + ProposalID: proposal.id, + DungeonEntry: proposal.dungeonID, + LeaderGUID: guid.LowType(proposal.leader.PlayerGUID), + LeaderWorldserverID: proposal.worldserverID, + PlayersGUID: make([]guid.LowType, 0, len(proposal.members)), + Members: make([]events.MatchmakingLfgProposalAcceptedMember, 0, len(proposal.members)), + } + for _, member := range proposal.members { + playerGUID := guid.LowType(member.PlayerGUID) + payload.PlayersGUID = append(payload.PlayersGUID, playerGUID) + payload.Members = append(payload.Members, events.MatchmakingLfgProposalAcceptedMember{ + RealmID: member.RealmID, + PlayerGUID: playerGUID, + SelectedRoles: member.SelectedRoles, + AssignedRole: member.AssignedRole, + QueueLeaderRealmID: member.QueueLeaderRealmID, + QueueLeaderGUID: guid.LowType(member.QueueLeaderGUID), + WorldserverID: member.WorldserverID, + }) + } + return payload +} + +func lfgStatusToEventPayload(status *LFGStatus) events.MatchmakingLfgStatusPayload { + if status == nil { + status = &LFGStatus{State: LFGStateNone} + } + + queuedMembers := make([]events.MatchmakingLfgMember, 0, len(status.QueuedMembers)) + for _, member := range status.QueuedMembers { + queuedMembers = append(queuedMembers, events.MatchmakingLfgMember{ + RealmID: member.RealmID, + PlayerGUID: guid.LowType(member.PlayerGUID), + Roles: member.Roles, + Leader: member.Leader, + QueueLeaderRealmID: member.QueueLeaderRealmID, + QueueLeaderGUID: guid.LowType(member.QueueLeaderGUID), + }) + } + + proposalMembers := make([]events.MatchmakingLfgProposalMember, 0, len(status.ProposalMembers)) + for _, member := range status.ProposalMembers { + proposalMembers = append(proposalMembers, events.MatchmakingLfgProposalMember{ + RealmID: member.RealmID, + PlayerGUID: guid.LowType(member.PlayerGUID), + SelectedRoles: member.SelectedRoles, + AssignedRole: member.AssignedRole, + Answered: member.Answered, + Accepted: member.Accepted, + }) + } + + return events.MatchmakingLfgStatusPayload{ + State: events.MatchmakingLfgState(status.State), + ProposalID: status.ProposalID, + ProposalState: events.MatchmakingLfgProposalState(status.ProposalState), + ProposalFailure: events.MatchmakingLfgProposalFailure(status.ProposalFailure), + DungeonEntry: status.DungeonEntry, + SelectedDungeons: append([]uint32(nil), status.SelectedDungeons...), + QueuedMembers: queuedMembers, + ProposalMembers: proposalMembers, + QueuedTimeMilliseconds: status.QueuedTimeMilliseconds, + TanksNeeded: status.TanksNeeded, + HealersNeeded: status.HealersNeeded, + DamageNeeded: status.DamageNeeded, + } +} diff --git a/apps/matchmakingserver/service/lfg_materializer.go b/apps/matchmakingserver/service/lfg_materializer.go new file mode 100644 index 0000000..412b035 --- /dev/null +++ b/apps/matchmakingserver/service/lfg_materializer.go @@ -0,0 +1,324 @@ +package service + +import ( + "context" + "errors" + "fmt" + "sort" + "time" + + "github.com/rs/zerolog/log" + + matchmaking "github.com/walkline/ToCloud9/apps/matchmakingserver" + pbGroup "github.com/walkline/ToCloud9/gen/group/pb" + pbServRegistry "github.com/walkline/ToCloud9/gen/servers-registry/pb" + pbWorld "github.com/walkline/ToCloud9/gen/worldserver/pb" + "github.com/walkline/ToCloud9/shared/events" + "github.com/walkline/ToCloud9/shared/gameserver/conn" + wowguid "github.com/walkline/ToCloud9/shared/wow/guid" +) + +var ( + ErrLFGMaterializerMissingPayload = errors.New("lfg materializer payload is nil") + ErrLFGMaterializerMissingWorldserver = errors.New("lfg materializer leader worldserver is empty") + ErrLFGMaterializerMissingMembers = errors.New("lfg materializer members are empty") + ErrLFGMaterializerMissingRegistry = errors.New("lfg materializer servers registry client is nil") + ErrLFGMaterializerMissingGRPCConnMgr = errors.New("lfg materializer game server grpc connection manager is nil") + ErrLFGMaterializerMissingWorldService = errors.New("lfg materializer worldserver grpc client is nil") + ErrLFGMaterializerMissingRegistryData = errors.New("lfg materializer servers registry response is nil") + ErrLFGMaterializerMissingMemberWorld = errors.New("lfg materializer member worldserver is empty") + ErrLFGMaterializerNoDungeonOwner = errors.New("lfg materializer no worldserver available for dungeon map") + ErrLFGMaterializerWorldserverNotFound = errors.New("lfg materializer worldserver not found") +) + +const lfgMaterializerNoLocalPlayerRetryDelay = 250 * time.Millisecond + +type LFGMaterializer struct { + serversRegistryClient pbServRegistry.ServersRegistryServiceClient + gameserverGRPCConnMgr conn.GameServerGRPCConnMgr +} + +func NewLFGMaterializer(serversRegistryClient pbServRegistry.ServersRegistryServiceClient, gameserverGRPCConnMgr conn.GameServerGRPCConnMgr, groupServiceClient pbGroup.GroupServiceClient) *LFGMaterializer { + return &LFGMaterializer{ + serversRegistryClient: serversRegistryClient, + gameserverGRPCConnMgr: gameserverGRPCConnMgr, + } +} + +func (m *LFGMaterializer) MaterializeAcceptedProposal(ctx context.Context, payload *events.MatchmakingEventLfgProposalAcceptedPayload) error { + if payload == nil { + return ErrLFGMaterializerMissingPayload + } + if payload.LeaderWorldserverID == "" { + return ErrLFGMaterializerMissingWorldserver + } + if len(payload.Members) == 0 { + return ErrLFGMaterializerMissingMembers + } + if err := ensureLFGMaterializerMemberWorldservers(payload); err != nil { + return err + } + if m.serversRegistryClient == nil { + return ErrLFGMaterializerMissingRegistry + } + if m.gameserverGRPCConnMgr == nil { + return ErrLFGMaterializerMissingGRPCConnMgr + } + + leaderGameServerGRPCClient, err := m.gameServerClientForLeaderWorldserverID(ctx, payload) + if err != nil { + return err + } + if leaderGameServerGRPCClient == nil { + return ErrLFGMaterializerMissingWorldService + } + + targetServer, err := m.lfgDungeonOwnerServer(ctx, payload, leaderGameServerGRPCClient) + if err != nil { + return err + } + targetWorldserverID := targetServer.GetId() + if targetWorldserverID == "" { + return ErrLFGMaterializerNoDungeonOwner + } + + splitWorldservers, err := validateLFGMaterializerMemberWorldservers(payload, targetWorldserverID) + if err != nil { + return err + } + + gameServerGRPCClient, err := m.gameServerClientForRegistryServer(targetServer) + if err != nil { + return err + } + if gameServerGRPCClient == nil { + return ErrLFGMaterializerMissingWorldService + } + + members := make([]*pbWorld.MaterializeLfgProposalRequest_Member, 0, len(payload.Members)) + for _, member := range payload.Members { + members = append(members, &pbWorld.MaterializeLfgProposalRequest_Member{ + PlayerGUID: lfgMaterializerPlayerGUID(payload, member.RealmID, uint64(member.PlayerGUID)), + SelectedRoles: uint32(member.SelectedRoles), + AssignedRole: uint32(member.AssignedRole), + QueueLeaderGUID: lfgMaterializerPlayerGUID(payload, member.QueueLeaderRealmID, uint64(member.QueueLeaderGUID)), + }) + } + + req := &pbWorld.MaterializeLfgProposalRequest{ + Api: matchmaking.SupportedGameServerVer, + RealmID: payload.RealmID, + ProposalID: payload.ProposalID, + DungeonEntry: payload.DungeonEntry, + LeaderGUID: lfgMaterializerPlayerGUID(payload, payload.LeaderRealmID, uint64(payload.LeaderGUID)), + Members: members, + } + + // The accepted-proposal event is what drives gateway worldport routing. + // AzerothCore then remains the readiness authority for materialization by + // returning NoLocalPlayer until the destination worldserver owns a member. + for attempt := 1; ; attempt++ { + res, err := gameServerGRPCClient.MaterializeLfgProposal(ctx, req) + if err != nil { + return fmt.Errorf("materialize lfg proposal %d on worldserver %q failed: %w", payload.ProposalID, targetWorldserverID, err) + } + if res == nil { + return fmt.Errorf("materialize lfg proposal %d on worldserver %q returned nil response", payload.ProposalID, targetWorldserverID) + } + if res.GetStatus() == pbWorld.MaterializeLfgProposalResponse_Success { + log.Info(). + Uint32("realmID", payload.RealmID). + Uint32("proposalID", payload.ProposalID). + Uint32("dungeonEntry", payload.DungeonEntry). + Str("leaderWorldserverID", payload.LeaderWorldserverID). + Str("targetWorldserverID", targetWorldserverID). + Int("members", len(payload.Members)). + Int("attempt", attempt). + Bool("splitWorldservers", splitWorldservers). + Msg("materialized LFG proposal") + return nil + } + if !splitWorldservers || res.GetStatus() != pbWorld.MaterializeLfgProposalResponse_NoLocalPlayer { + return fmt.Errorf("materialize lfg proposal %d on worldserver %q returned status %s", payload.ProposalID, targetWorldserverID, res.GetStatus()) + } + if !waitForLFGMaterializerRetry(ctx) { + return fmt.Errorf("materialize lfg proposal %d on worldserver %q still missing local players after %d attempts: %w", payload.ProposalID, targetWorldserverID, attempt, ctx.Err()) + } + } +} + +func ensureLFGMaterializerMemberWorldservers(payload *events.MatchmakingEventLfgProposalAcceptedPayload) error { + for _, member := range payload.Members { + if member.WorldserverID == "" { + return ErrLFGMaterializerMissingMemberWorld + } + } + return nil +} + +func validateLFGMaterializerMemberWorldservers(payload *events.MatchmakingEventLfgProposalAcceptedPayload, targetWorldserverID string) (bool, error) { + splitWorldservers := false + for _, member := range payload.Members { + if member.WorldserverID == "" { + return false, ErrLFGMaterializerMissingMemberWorld + } + if member.WorldserverID != targetWorldserverID { + splitWorldservers = true + } + } + return splitWorldservers, nil +} + +func (m *LFGMaterializer) lfgDungeonOwnerServer(ctx context.Context, payload *events.MatchmakingEventLfgProposalAcceptedPayload, metadataClient pbWorld.WorldServerServiceClient) (*pbServRegistry.Server, error) { + dungeonInfo, err := metadataClient.GetLfgDungeonInfo(ctx, &pbWorld.GetLfgDungeonInfoRequest{ + Api: matchmaking.SupportedGameServerVer, + DungeonEntry: payload.DungeonEntry, + }) + if err != nil { + return nil, fmt.Errorf("get lfg dungeon info for proposal %d failed: %w", payload.ProposalID, err) + } + if dungeonInfo == nil { + return nil, fmt.Errorf("get lfg dungeon info for proposal %d returned nil response", payload.ProposalID) + } + if dungeonInfo.GetStatus() != pbWorld.GetLfgDungeonInfoResponse_Success { + return nil, fmt.Errorf("get lfg dungeon info for proposal %d returned status %s", payload.ProposalID, dungeonInfo.GetStatus()) + } + + servers, err := m.serversRegistryClient.AvailableGameServersForMapAndRealm(ctx, &pbServRegistry.AvailableGameServersForMapAndRealmRequest{ + Api: matchmaking.SupportedServerRegistryVer, + RealmID: lfgMaterializationRealmID(payload), + MapID: dungeonInfo.GetMapID(), + IsCrossRealm: payload.CrossRealm, + }) + if err != nil { + return nil, fmt.Errorf("list game servers for LFG dungeon map %d in realm %d crossrealm=%t failed: %w", dungeonInfo.GetMapID(), lfgMaterializationRealmID(payload), payload.CrossRealm, err) + } + if servers == nil { + return nil, ErrLFGMaterializerMissingRegistryData + } + + target := chooseLFGMaterializationServer(servers.GetGameServers(), payload.LeaderWorldserverID) + if target == nil { + return nil, ErrLFGMaterializerNoDungeonOwner + } + return target, nil +} + +func chooseLFGMaterializationServer(servers []*pbServRegistry.Server, preferredWorldserverID string) *pbServRegistry.Server { + for _, server := range servers { + if server.GetId() == preferredWorldserverID { + return server + } + } + + candidates := make([]*pbServRegistry.Server, 0, len(servers)) + for _, server := range servers { + if server != nil && server.GetId() != "" { + candidates = append(candidates, server) + } + } + sort.Slice(candidates, func(i, j int) bool { + return candidates[i].GetId() < candidates[j].GetId() + }) + if len(candidates) == 0 { + return nil + } + return candidates[0] +} + +func lfgLeaderRealmID(payload *events.MatchmakingEventLfgProposalAcceptedPayload) uint32 { + if payload == nil { + return 0 + } + if payload.LeaderRealmID != 0 { + return payload.LeaderRealmID + } + return payload.RealmID +} + +func lfgMaterializationRealmID(payload *events.MatchmakingEventLfgProposalAcceptedPayload) uint32 { + if payload != nil && payload.CrossRealm { + return 0 + } + if payload == nil { + return 0 + } + return payload.RealmID +} + +func lfgMaterializerPlayerGUID(payload *events.MatchmakingEventLfgProposalAcceptedPayload, playerRealmID uint32, playerGUID uint64) uint64 { + if playerRealmID == 0 { + playerRealmID = lfgMaterializationRealmID(payload) + } + + // Materialization sends AzerothCore-facing player ObjectGuid values to the + // target dungeon owner. For crossrealm dungeon owners realmID=0, every + // member with a concrete source realm must stay realm-scoped. + return wowguid.PlayerGUIDForRealm(lfgMaterializationRealmID(payload), playerRealmID, playerGUID) +} + +func waitForLFGMaterializerRetry(ctx context.Context) bool { + timer := time.NewTimer(lfgMaterializerNoLocalPlayerRetryDelay) + defer timer.Stop() + + select { + case <-ctx.Done(): + return false + case <-timer.C: + return ctx.Err() == nil + } +} + +func (m *LFGMaterializer) gameServerClientForWorldserverID(ctx context.Context, realmID uint32, worldserverID string) (pbWorld.WorldServerServiceClient, error) { + servers, err := m.serversRegistryClient.ListGameServersForRealm(ctx, &pbServRegistry.ListGameServersForRealmRequest{ + Api: matchmaking.SupportedServerRegistryVer, + RealmID: realmID, + }) + if err != nil { + return nil, fmt.Errorf("list game servers for realm %d failed: %w", realmID, err) + } + if servers == nil { + return nil, ErrLFGMaterializerMissingRegistryData + } + + for _, server := range servers.GetGameServers() { + if server.GetID() != worldserverID { + continue + } + m.gameserverGRPCConnMgr.AddAddressMapping(server.GetAddress(), server.GetGrpcAddress()) + client, err := m.gameserverGRPCConnMgr.GRPCConnByGameServerAddress(server.GetAddress()) + if err != nil { + return nil, fmt.Errorf("get game server grpc client for worldserver %q failed: %w", worldserverID, err) + } + return client, nil + } + + return nil, fmt.Errorf("%w: leader worldserver %q not found in realm %d", ErrLFGMaterializerWorldserverNotFound, worldserverID, realmID) +} + +func (m *LFGMaterializer) gameServerClientForLeaderWorldserverID(ctx context.Context, payload *events.MatchmakingEventLfgProposalAcceptedPayload) (pbWorld.WorldServerServiceClient, error) { + leaderRealmID := lfgLeaderRealmID(payload) + client, err := m.gameServerClientForWorldserverID(ctx, leaderRealmID, payload.LeaderWorldserverID) + if err == nil || !payload.CrossRealm || !errors.Is(err, ErrLFGMaterializerWorldserverNotFound) { + return client, err + } + + materializationRealmID := lfgMaterializationRealmID(payload) + if materializationRealmID == leaderRealmID { + return nil, err + } + + return m.gameServerClientForWorldserverID(ctx, materializationRealmID, payload.LeaderWorldserverID) +} + +func (m *LFGMaterializer) gameServerClientForRegistryServer(server *pbServRegistry.Server) (pbWorld.WorldServerServiceClient, error) { + if server == nil { + return nil, ErrLFGMaterializerNoDungeonOwner + } + m.gameserverGRPCConnMgr.AddAddressMapping(server.GetAddress(), server.GetGrpcAddress()) + client, err := m.gameserverGRPCConnMgr.GRPCConnByGameServerAddress(server.GetAddress()) + if err != nil { + return nil, fmt.Errorf("get game server grpc client for worldserver %q failed: %w", server.GetId(), err) + } + return client, nil +} diff --git a/apps/matchmakingserver/service/lfg_materializer_listener.go b/apps/matchmakingserver/service/lfg_materializer_listener.go new file mode 100644 index 0000000..6de3028 --- /dev/null +++ b/apps/matchmakingserver/service/lfg_materializer_listener.go @@ -0,0 +1,95 @@ +package service + +import ( + "context" + "errors" + "time" + + "github.com/nats-io/nats.go" + "github.com/rs/zerolog/log" + + "github.com/walkline/ToCloud9/shared/events" +) + +const ( + lfgMaterializerQueueGroup = "matchmaking_lfg_materializer" + // Accepted proposal materialization runs after the client answer window and + // includes crossrealm redirects plus target-world loading. + lfgMaterializerAcceptedProposalTimeout = 90 * time.Second +) + +type LFGMaterializerListener struct { + nc *nats.Conn + materializer *LFGMaterializer + lfgService LFGService + subs []*nats.Subscription +} + +func NewLFGMaterializerListener(nc *nats.Conn, materializer *LFGMaterializer, lfgService LFGService) *LFGMaterializerListener { + return &LFGMaterializerListener{ + nc: nc, + materializer: materializer, + lfgService: lfgService, + } +} + +func (l *LFGMaterializerListener) Listen() error { + if l.nc == nil { + return errors.New("lfg materializer listener nats connection is nil") + } + if l.materializer == nil { + return errors.New("lfg materializer listener materializer is nil") + } + if l.lfgService == nil { + return errors.New("lfg materializer listener lfg service is nil") + } + + sub, err := l.nc.QueueSubscribe(events.MatchmakingEventLfgProposalAccepted.SubjectName(), lfgMaterializerQueueGroup, func(msg *nats.Msg) { + payload := events.MatchmakingEventLfgProposalAcceptedPayload{} + if _, err := events.Unmarshal(msg.Data, &payload); err != nil { + log.Error().Err(err).Msg("can't read MatchmakingEventLfgProposalAccepted event") + return + } + + go func() { + ctx, cancel := context.WithTimeout(context.Background(), lfgMaterializerAcceptedProposalTimeout) + defer cancel() + + if err := l.materializer.MaterializeAcceptedProposal(ctx, &payload); err != nil { + log.Error(). + Err(err). + Uint32("realmID", payload.RealmID). + Uint32("proposalID", payload.ProposalID). + Str("leaderWorldserverID", payload.LeaderWorldserverID). + Msg("failed to materialize LFG proposal") + if failErr := l.lfgService.FailLfgProposal(ctx, payload.RealmID, payload.ProposalID); failErr != nil { + log.Error(). + Err(failErr). + Uint32("realmID", payload.RealmID). + Uint32("proposalID", payload.ProposalID). + Msg("failed to mark LFG proposal as failed after materialization error") + } + } + }() + }) + if err != nil { + l.unsubscribe() + return err + } + + l.subs = append(l.subs, sub) + return nil +} + +func (l *LFGMaterializerListener) Stop() error { + return l.unsubscribe() +} + +func (l *LFGMaterializerListener) unsubscribe() error { + for _, sub := range l.subs { + if err := sub.Unsubscribe(); err != nil { + return err + } + } + return nil +} diff --git a/apps/mysqlreverseproxy/proxy/proxy.go b/apps/mysqlreverseproxy/proxy/proxy.go index 6818bd8..e35a286 100644 --- a/apps/mysqlreverseproxy/proxy/proxy.go +++ b/apps/mysqlreverseproxy/proxy/proxy.go @@ -2,6 +2,7 @@ package proxy import ( "fmt" + "sort" "strings" "github.com/go-mysql-org/go-mysql/mysql" @@ -10,14 +11,16 @@ import ( "github.com/rs/zerolog/log" "github.com/walkline/ToCloud9/apps/mysqlreverseproxy/sqlparser" + wowarena "github.com/walkline/ToCloud9/shared/wow/arena" "github.com/walkline/ToCloud9/shared/wow/guid" ) // StmtWithParsedDataContext holds data related to prepared statements and GUIDs for each statement context. type StmtWithParsedDataContext struct { - Stmts map[uint32]DBStatement // Mapped by realm ID - GuidFinder *sqlparser.CharGUIDFinder // Helper to extract GUIDs from queries - IsDummy bool // Flag indicating if it's a dummy statement + Stmts map[uint32]DBStatement // Mapped by realm ID + GuidFinder *sqlparser.CharGUIDFinder // Helper to extract GUIDs from queries + IsDummy bool // Flag indicating if it's a dummy statement + RunOnEveryRealm bool // Replicated instance-state writes must stay present in every home realm DB. } // TransactionState represents the state of a transaction. @@ -38,11 +41,12 @@ const ( // TC9Proxy represents the proxy for handling database connections and transaction state. type TC9Proxy struct { - connectionByRealm map[uint32]DBConnection // Connection mapping by realm ID - clientID int // Client ID - parser *parser.Parser // SQL parser - transactionState TransactionState // Current transaction state - transactionRealmID uint32 // Realm ID for the current transaction + connectionByRealm map[uint32]DBConnection // Connection mapping by realm ID + clientID int // Client ID + parser *parser.Parser // SQL parser + transactionState TransactionState // Current transaction state + transactionRealmID uint32 // Realm ID for the current transaction + transactionAllRealms bool // Transaction was opened on every realm connection. } // NewTC9Proxy creates a new instance of TC9Proxy. @@ -58,9 +62,17 @@ func NewTC9Proxy(clientID int, connections map[uint32]DBConnection, parser *pars // ConnByRealm retrieves the connection for a given realm ID, or a fallback connection. func (p *TC9Proxy) ConnByRealm(realmID uint32) DBConnection { if p.connectionByRealm[realmID] == nil { - // If no connection is found for the realm, return the first available connection. - for _, conn := range p.connectionByRealm { - return conn + // If no connection is found for the realm, return the first available + // connection deterministically. Map iteration order is random in Go. + realms := make([]int, 0, len(p.connectionByRealm)) + for realm := range p.connectionByRealm { + realms = append(realms, int(realm)) + } + sort.Ints(realms) + for _, realm := range realms { + if conn := p.connectionByRealm[uint32(realm)]; conn != nil { + return conn + } } } return p.connectionByRealm[realmID] @@ -80,20 +92,42 @@ func (p *TC9Proxy) UseDB(string) error { // HandleQuery processes a query, handles transactions, and executes the query. func (p *TC9Proxy) HandleQuery(query string) (*mysql.Result, error) { - query = strings.TrimSpace(strings.ToUpper(query)) + query = strings.TrimSpace(query) + normalizedQuery := strings.ToUpper(query) // Default connection (realm 0) conn := p.ConnByRealm(0) - switch query { + switch normalizedQuery { case StartTransaction: p.transactionState = TransactionRequested // Start transaction flag + p.transactionAllRealms = false return nil, nil case CommitTransaction, RollbackTransaction: - p.transactionState = TransactionNone // Reset transaction state + defer func() { + p.transactionState = TransactionNone + p.transactionAllRealms = false + p.transactionRealmID = 0 + }() + if p.transactionAllRealms { + return p.executeQueryOnEveryConn(normalizedQuery) + } conn = p.ConnByRealm(p.transactionRealmID) // Use realm connection for commit/rollback } - return conn.Execute(query) + if querySelectsCharacterInstance(normalizedQuery) { + return p.selectCharacterInstancesFromEveryRealm(normalizedQuery) + } + if querySelectsArenaTeamMembers(normalizedQuery) { + return p.selectArenaTeamMembersFromEveryRealm(normalizedQuery) + } + if querySelectsArenaTeams(normalizedQuery) { + return p.selectArenaTeamsFromEveryRealm(normalizedQuery) + } + if queryShouldRunOnEveryRealm(normalizedQuery) { + return p.executeQueryOnEveryConn(normalizedQuery) + } + + return conn.Execute(normalizedQuery) } // HandleFieldList retrieves a list of fields for a given table. @@ -115,8 +149,9 @@ func (p *TC9Proxy) HandleStmtPrepare(query string) (int, int, interface{}, error // Prepare statements for each realm stmtsWithParsedData := &StmtWithParsedDataContext{ - GuidFinder: &v, - Stmts: map[uint32]DBStatement{}, + GuidFinder: &v, + Stmts: map[uint32]DBStatement{}, + RunOnEveryRealm: statementShouldRunOnEveryRealm(&v), } var stmt DBStatement @@ -156,6 +191,9 @@ func (p *TC9Proxy) HandleStmtExecute(ctx interface{}, query string, args []inter if stmtWithParsedData.IsDummy { return nil, nil } + if stmtWithParsedData.RunOnEveryRealm { + return p.executeStmtOnEveryRealm(stmtWithParsedData, args) + } // Get the correct statement for the realm stmt := stmtWithParsedData.Stmts[realmID] @@ -177,6 +215,7 @@ func (p *TC9Proxy) HandleStmtExecute(ctx interface{}, query string, args []inter } updateOutputGUIDs(res, stmtWithParsedData, rawGUID) + updateOutputArenaTeamIDs(res, stmtWithParsedData, realmID) return res, nil } @@ -229,6 +268,20 @@ func extractGUIDAndRealmID(stmtWithParsedData *StmtWithParsedDataContext, args [ } } + if len(stmtWithParsedData.GuidFinder.InputArenaTeamIDIndexes) > 0 { + var teamID uint32 + for _, i := range stmtWithParsedData.GuidFinder.InputArenaTeamIDIndexes { + teamID = numericArgToUint32(args[i]) + teamRealmID := wowarena.TeamIDRealmID(teamID) + if teamRealmID != 0 { + if realmID == 0 { + realmID = teamRealmID + } + args[i] = wowarena.TeamIDCounter(teamID) + } + } + } + // Default realm ID logic if realmID == 0 { if !stmtWithParsedData.GuidFinder.IsSelectStmt && p.transactionState == TransactionInProgress { @@ -241,34 +294,61 @@ func extractGUIDAndRealmID(stmtWithParsedData *StmtWithParsedDataContext, args [ return realmID, rawGUID, realGUIDCounter } +func numericArgToUint32(arg interface{}) uint32 { + switch v := arg.(type) { + case uint64: + return uint32(v) + case uint32: + return v + case uint: + return uint32(v) + case uint16: + return uint32(v) + case uint8: + return uint32(v) + case int64: + return uint32(v) + case int32: + return uint32(v) + case int: + return uint32(v) + case int16: + return uint32(v) + case int8: + return uint32(v) + default: + return 0 + } +} + // startTransactionIfNeeded starts a transaction if needed based on the query and state. func (p *TC9Proxy) startTransactionIfNeeded(args []interface{}, stmtWithParsedData *StmtWithParsedDataContext, realmID uint32) error { // Logic to start a transaction if required (for dummy queries or transaction control) if stmtWithParsedData.IsDummy && p.transactionState == TransactionRequested { - if _, err := p.ConnByRealm(0).Execute(StartTransaction); err != nil { + contextRealmID, err := explicitRealmContext(args) + if err != nil { + return err + } + if _, err := p.ConnByRealm(contextRealmID).Execute(StartTransaction); err != nil { return fmt.Errorf("failed to start transaction, err: %v", err) } p.transactionState = TransactionInProgress - switch v := args[0].(type) { - case uint64: - p.transactionRealmID = uint32(v) - case uint32: - p.transactionRealmID = v - case int: - p.transactionRealmID = uint32(v) - case uint16: - p.transactionRealmID = uint32(v) - case uint8: - p.transactionRealmID = uint32(v) - default: - return fmt.Errorf("invalid realmID context provider argument type: %T", v) - } + p.transactionRealmID = contextRealmID return nil } if p.transactionState == TransactionRequested { - if _, err := p.ConnByRealm(0).Execute(StartTransaction); err != nil { + if stmtWithParsedData.RunOnEveryRealm { + if _, err := p.executeQueryOnEveryConn(StartTransaction); err != nil { + return fmt.Errorf("failed to start replicated transaction, err: %v", err) + } + p.transactionState = TransactionInProgress + p.transactionAllRealms = true + p.transactionRealmID = 0 + return nil + } + if _, err := p.ConnByRealm(realmID).Execute(StartTransaction); err != nil { return fmt.Errorf("failed to start transaction, err: %v", err) } p.transactionState = TransactionInProgress @@ -278,6 +358,327 @@ func (p *TC9Proxy) startTransactionIfNeeded(args []interface{}, stmtWithParsedDa return nil } +func explicitRealmContext(args []interface{}) (uint32, error) { + if len(args) == 0 { + return 0, fmt.Errorf("missing realmID context provider argument") + } + + switch v := args[0].(type) { + case uint64: + return uint32(v), nil + case uint32: + return v, nil + case uint: + return uint32(v), nil + case uint16: + return uint32(v), nil + case uint8: + return uint32(v), nil + case int64: + if v < 0 { + return 0, fmt.Errorf("invalid negative realmID context provider argument: %d", v) + } + return uint32(v), nil + case int32: + if v < 0 { + return 0, fmt.Errorf("invalid negative realmID context provider argument: %d", v) + } + return uint32(v), nil + case int: + if v < 0 { + return 0, fmt.Errorf("invalid negative realmID context provider argument: %d", v) + } + return uint32(v), nil + case int16: + if v < 0 { + return 0, fmt.Errorf("invalid negative realmID context provider argument: %d", v) + } + return uint32(v), nil + case int8: + if v < 0 { + return 0, fmt.Errorf("invalid negative realmID context provider argument: %d", v) + } + return uint32(v), nil + default: + return 0, fmt.Errorf("invalid realmID context provider argument type: %T", v) + } +} + +func (p *TC9Proxy) executeQueryOnEveryConn(query string) (*mysql.Result, error) { + var first *mysql.Result + for _, realm := range p.sortedRealms() { + res, err := p.connectionByRealm[realm].Execute(query) + if err != nil { + return nil, err + } + if first == nil { + first = res + } + } + return first, nil +} + +func (p *TC9Proxy) executeStmtOnEveryRealm(stmtWithParsedData *StmtWithParsedDataContext, args []interface{}) (*mysql.Result, error) { + var first *mysql.Result + for _, realm := range p.sortedRealms() { + stmt := stmtWithParsedData.Stmts[realm] + if stmt == nil { + return nil, fmt.Errorf("statement not found for realm %d", realm) + } + + res, err := stmt.Execute(args...) + if err != nil { + return nil, err + } + if first == nil { + first = res + } + } + return first, nil +} + +func (p *TC9Proxy) sortedRealms() []uint32 { + realms := make([]int, 0, len(p.connectionByRealm)) + for realm := range p.connectionByRealm { + realms = append(realms, int(realm)) + } + sort.Ints(realms) + + result := make([]uint32, 0, len(realms)) + for _, realm := range realms { + result = append(result, uint32(realm)) + } + return result +} + +func (p *TC9Proxy) selectCharacterInstancesFromEveryRealm(query string) (*mysql.Result, error) { + var merged *mysql.Result + for _, realm := range p.sortedRealms() { + res, err := p.connectionByRealm[realm].Execute(query) + if err != nil { + return nil, err + } + if res == nil || res.Resultset == nil { + continue + } + rewriteCharacterInstanceGUIDs(res, realm) + if merged == nil { + merged = res + continue + } + merged.Values = append(merged.Values, res.Values...) + } + if merged == nil { + return &mysql.Result{}, nil + } + return merged, nil +} + +func (p *TC9Proxy) selectArenaTeamsFromEveryRealm(query string) (*mysql.Result, error) { + return p.selectArenaRowsFromEveryRealm(query, rewriteArenaTeamRows) +} + +func (p *TC9Proxy) selectArenaTeamMembersFromEveryRealm(query string) (*mysql.Result, error) { + return p.selectArenaRowsFromEveryRealm(query, rewriteArenaTeamMemberRows) +} + +func (p *TC9Proxy) selectArenaRowsFromEveryRealm(query string, rewrite func(*mysql.Result, uint32)) (*mysql.Result, error) { + var merged *mysql.Result + for _, realm := range p.sortedRealms() { + res, err := p.connectionByRealm[realm].Execute(query) + if err != nil { + return nil, err + } + if res == nil || res.Resultset == nil { + continue + } + rewrite(res, realm) + if merged == nil { + merged = res + continue + } + merged.Values = append(merged.Values, res.Values...) + } + if merged == nil { + return &mysql.Result{}, nil + } + return merged, nil +} + +func rewriteCharacterInstanceGUIDs(res *mysql.Result, realm uint32) { + if res == nil || res.Resultset == nil || realm == 0 { + return + } + + guidColumn, ok := res.FieldNames["guid"] + if !ok { + guidColumn, ok = res.FieldNames["GUID"] + if !ok { + return + } + } + + for row := range res.Values { + if guidColumn >= len(res.Values[row]) { + continue + } + low := res.Values[row][guidColumn].AsUint64() + if low == 0 || low>>32 != 0 { + continue + } + raw := guid.NewCrossrealmPlayerGUID(uint16(realm), guid.LowType(low)).GetRawValue() + res.Values[row][guidColumn] = mysql.NewFieldValue(mysql.FieldValueTypeUnsigned, raw, []byte(fmt.Sprintf("%d", raw))) + } +} + +func rewriteArenaTeamRows(res *mysql.Result, realm uint32) { + if res == nil || res.Resultset == nil || realm == 0 { + return + } + + rewriteArenaTeamIDColumn(res, realm, "arenaTeamId") + rewritePlayerGUIDColumn(res, realm, "captainGuid") +} + +func rewriteArenaTeamMemberRows(res *mysql.Result, realm uint32) { + if res == nil || res.Resultset == nil || realm == 0 { + return + } + + rewriteArenaTeamIDColumn(res, realm, "arenaTeamId") + rewritePlayerGUIDColumn(res, realm, "guid") +} + +func rewriteArenaTeamIDColumn(res *mysql.Result, realm uint32, column string) { + columnIndex, ok := resultColumnIndex(res, column) + if !ok { + return + } + + for row := range res.Values { + if columnIndex >= len(res.Values[row]) { + continue + } + low := uint32(res.Values[row][columnIndex].AsUint64()) + if low == 0 || wowarena.TeamIDRealmID(low) != 0 { + continue + } + raw := wowarena.NewCrossrealmTeamID(uint16(realm), low) + res.Values[row][columnIndex] = mysql.NewFieldValue(mysql.FieldValueTypeUnsigned, uint64(raw), []byte(fmt.Sprintf("%d", raw))) + } +} + +func rewritePlayerGUIDColumn(res *mysql.Result, realm uint32, column string) { + columnIndex, ok := resultColumnIndex(res, column) + if !ok { + return + } + + for row := range res.Values { + if columnIndex >= len(res.Values[row]) { + continue + } + low := res.Values[row][columnIndex].AsUint64() + if low == 0 || low>>32 != 0 { + continue + } + raw := guid.NewCrossrealmPlayerGUID(uint16(realm), guid.LowType(low)).GetRawValue() + res.Values[row][columnIndex] = mysql.NewFieldValue(mysql.FieldValueTypeUnsigned, raw, []byte(fmt.Sprintf("%d", raw))) + } +} + +func resultColumnIndex(res *mysql.Result, column string) (int, bool) { + if res == nil || res.Resultset == nil { + return 0, false + } + for fieldName, index := range res.FieldNames { + if strings.EqualFold(fieldName, column) { + return index, true + } + } + return 0, false +} + +func querySelectsCharacterInstance(query string) bool { + return isSelectQuery(query) && strings.Contains(query, "CHARACTER_INSTANCE") +} + +func querySelectsArenaTeams(query string) bool { + return isSelectQuery(query) && strings.Contains(query, "FROM ARENA_TEAM") && !strings.Contains(query, "FROM ARENA_TEAM_MEMBER") +} + +func querySelectsArenaTeamMembers(query string) bool { + return isSelectQuery(query) && strings.Contains(query, "FROM ARENA_TEAM_MEMBER") +} + +func queryShouldRunOnEveryRealm(query string) bool { + if isSelectQuery(query) { + return false + } + if queryTouchesArenaGlobalTable(query) { + return true + } + if queryTouchesReplicatedInstanceStateTable(query) { + return true + } + + // Direct character_instance writes in AzerothCore are startup/global cleanup + // queries. Player-specific writes use prepared statements so the proxy can + // route by realm-scoped GUID instead. + return strings.Contains(query, "CHARACTER_INSTANCE") +} + +func statementShouldRunOnEveryRealm(v *sqlparser.CharGUIDFinder) bool { + if v == nil || v.IsSelectStmt { + return false + } + if v.TouchesReplicatedInstanceStateTable() { + return true + } + return v.TouchesTable("character_instance") && len(v.InputGUIDIndexes) == 0 +} + +func isSelectQuery(query string) bool { + return strings.HasPrefix(strings.TrimSpace(query), "SELECT") +} + +func queryTouchesReplicatedInstanceStateTable(query string) bool { + for _, pattern := range []string{ + "CREATURE_RESPAWN", + "GAMEOBJECT_RESPAWN", + "INSTANCE_RESET", + "INSTANCE_SAVED_GO_STATE_DATA", + " FROM INSTANCE", + " INTO INSTANCE", + "INSERT INTO INSTANCE", + "UPDATE INSTANCE", + "DELETE FROM INSTANCE", + " JOIN INSTANCE", + } { + if strings.Contains(query, pattern) { + return true + } + } + return false +} + +func queryTouchesArenaGlobalTable(query string) bool { + for _, pattern := range []string{ + "DELETE FROM ARENA_TEAM_MEMBER WHERE ARENATEAMID NOT IN", + "DELETE FROM ARENA_TEAM_MEMBER", + "DELETE FROM ARENA_TEAM", + "DELETE FROM CHARACTER_ARENA_STATS", + "TRUNCATE TABLE ARENA_TEAM_MEMBER", + "TRUNCATE TABLE ARENA_TEAM", + "TRUNCATE TABLE CHARACTER_ARENA_STATS", + } { + if strings.Contains(query, pattern) { + return true + } + } + return false +} + // updateOutputGUIDs updates the output GUIDs in the query result with crossrealm guid. func updateOutputGUIDs(res *mysql.Result, stmtWithParsedData *StmtWithParsedDataContext, rawGUID uint64) { if rawGUID > 0 && len(stmtWithParsedData.GuidFinder.OutputGUIDIndexes) > 0 { @@ -289,6 +690,26 @@ func updateOutputGUIDs(res *mysql.Result, stmtWithParsedData *StmtWithParsedData } } +func updateOutputArenaTeamIDs(res *mysql.Result, stmtWithParsedData *StmtWithParsedDataContext, realmID uint32) { + if res == nil || stmtWithParsedData == nil || stmtWithParsedData.GuidFinder == nil || realmID == 0 || len(stmtWithParsedData.GuidFinder.OutputArenaTeamIDIndexes) == 0 { + return + } + + for _, column := range stmtWithParsedData.GuidFinder.OutputArenaTeamIDIndexes { + for row := range res.Values { + if column >= len(res.Values[row]) { + continue + } + low := uint32(res.Values[row][column].AsUint64()) + if low == 0 || wowarena.TeamIDRealmID(low) != 0 { + continue + } + raw := wowarena.NewCrossrealmTeamID(uint16(realmID), low) + res.Values[row][column] = mysql.NewFieldValue(mysql.FieldValueTypeUnsigned, uint64(raw), []byte(fmt.Sprintf("%d", raw))) + } + } +} + // EmptyReplicationHandler is a no-op handler for replication commands. type EmptyReplicationHandler struct{ TC9Proxy } diff --git a/apps/mysqlreverseproxy/sqlparser/parser.go b/apps/mysqlreverseproxy/sqlparser/parser.go index 43e4d89..8fc08ea 100644 --- a/apps/mysqlreverseproxy/sqlparser/parser.go +++ b/apps/mysqlreverseproxy/sqlparser/parser.go @@ -9,6 +9,7 @@ import ( var charGuidColumnToTableMap = map[string]string{ "characters": "guid", + "character_instance": "guid", "character_inventory": "guid", "character_queststatus": "guid", "mail_items": "receiver", @@ -41,6 +42,22 @@ var charGuidColumnToTableMap = map[string]string{ "character_aura": "guid", "item_instance": "owner_guid", "battleground_deserters": "guid", + "arena_team": "captainGuid", + "arena_team_member": "guid", + "character_arena_stats": "guid", +} + +var replicatedInstanceStateTables = map[string]struct{}{ + "creature_respawn": {}, + "gameobject_respawn": {}, + "instance": {}, + "instance_reset": {}, + "instance_saved_go_state_data": {}, +} + +var arenaTeamIDTables = map[string]struct{}{ + "arena_team": {}, + "arena_team_member": {}, } type pairOperation struct { @@ -49,20 +66,22 @@ type pairOperation struct { } type CharGUIDFinder struct { - InputGUIDIndexes []int - OutputGUIDIndexes []int - IsSelectStmt bool - prefix string - tableExpSource string - tableNames []string - tableNameShortcuts map[string]string - binOperation *pairOperation - assignOperation *pairOperation - isInFieldList bool - isInsert bool - insertColumns []string - inputParams []string - outputParams []string + InputGUIDIndexes []int + OutputGUIDIndexes []int + InputArenaTeamIDIndexes []int + OutputArenaTeamIDIndexes []int + IsSelectStmt bool + prefix string + tableExpSource string + tableNames []string + tableNameShortcuts map[string]string + binOperation *pairOperation + assignOperation *pairOperation + isInFieldList bool + isInsert bool + insertColumns []string + inputParams []string + outputParams []string } func NewCharGUIDFinder() CharGUIDFinder { @@ -79,9 +98,9 @@ func (v *CharGUIDFinder) Enter(in ast.Node) (ast.Node, bool) { case *ast.SelectStmt: v.IsSelectStmt = true case *ast.TableSource: - v.tableExpSource = node.AsName.String() + v.tableExpSource = strings.ToLower(node.AsName.String()) case *ast.TableName: - tableName := node.Name.String() + tableName := strings.ToLower(node.Name.String()) if v.tableExpSource != "" { v.tableNameShortcuts[v.tableExpSource] = tableName v.tableExpSource = "" @@ -158,20 +177,41 @@ func (v *CharGUIDFinder) handleParamMarkerExpr() { func (v *CharGUIDFinder) FillInGUIDIndexes() { searchColumnNames := make(map[string]struct{}) + touchesArenaTeamIDs := false for _, name := range v.tableNames { columnName := charGuidColumnToTableMap[name] if columnName != "" { searchColumnNames[columnName] = struct{}{} } - } - - if len(searchColumnNames) == 0 { - return + if _, ok := arenaTeamIDTables[name]; ok { + touchesArenaTeamIDs = true + } } for column := range searchColumnNames { v.findGUIDIndexes(column) } + if touchesArenaTeamIDs { + v.findArenaTeamIDIndexes() + } +} + +func (v *CharGUIDFinder) TouchesReplicatedInstanceStateTable() bool { + for _, name := range v.tableNames { + if _, ok := replicatedInstanceStateTables[name]; ok { + return true + } + } + return false +} + +func (v *CharGUIDFinder) TouchesTable(tableName string) bool { + for _, name := range v.tableNames { + if name == tableName { + return true + } + } + return false } func (v *CharGUIDFinder) findGUIDIndexes(column string) { @@ -188,7 +228,23 @@ func (v *CharGUIDFinder) findGUIDIndexes(column string) { } } +func (v *CharGUIDFinder) findArenaTeamIDIndexes() { + for i, param := range v.inputParams { + if v.isArenaTeamIDColumn(param) { + v.InputArenaTeamIDIndexes = append(v.InputArenaTeamIDIndexes, i) + } + } + + for i, param := range v.outputParams { + if v.isArenaTeamIDColumn(param) { + v.OutputArenaTeamIDIndexes = append(v.OutputArenaTeamIDIndexes, i) + } + } +} + func (v *CharGUIDFinder) isGUIDColumn(param, column string) bool { + param = strings.ToLower(param) + column = strings.ToLower(column) if strings.ContainsRune(param, '.') { strs := strings.Split(param, ".") tableName := v.tableNameShortcuts[strs[0]] @@ -200,3 +256,19 @@ func (v *CharGUIDFinder) isGUIDColumn(param, column string) bool { } return param == column } + +func (v *CharGUIDFinder) isArenaTeamIDColumn(param string) bool { + param = strings.ToLower(param) + if strings.ContainsRune(param, '.') { + strs := strings.Split(param, ".") + tableName := v.tableNameShortcuts[strs[0]] + if tableName == "" { + tableName = strs[0] + } + if _, ok := arenaTeamIDTables[tableName]; !ok { + return false + } + return strings.EqualFold(strs[1], "arenateamid") + } + return strings.EqualFold(param, "arenateamid") +} From 7a29c7bef79f2eb31a031f3d425c0341c9fff252 Mon Sep 17 00:00:00 2001 From: VG-Prog Date: Fri, 22 May 2026 21:49:05 +0200 Subject: [PATCH 10/11] feat(Cluster/Sidecar): Add game-server bridge APIs Extend the libsidecar C ABI and Go bridge with grouped member-state events, guild and LFG RPCs, arena-team and item APIs, monitoring/readiness behavior, config loading, and consumer handlers required by the clustered AzerothCore integration. --- game-server/libsidecar/arena-team-api.go | 224 ++++++ game-server/libsidecar/arena-team-api.h | 61 ++ game-server/libsidecar/battleground-api.go | 88 +-- game-server/libsidecar/battleground-api.h | 4 + game-server/libsidecar/character-service.go | 22 + game-server/libsidecar/config/config.go | 153 ++++ game-server/libsidecar/consumer/consumer.go | 260 ++++++- .../libsidecar/consumer/handlers-fabric.go | 9 + game-server/libsidecar/events-group.c | 119 ++- game-server/libsidecar/events-group.go | 691 +++++++++++++++++- game-server/libsidecar/events-group.h | 116 +++ game-server/libsidecar/events.go | 15 +- game-server/libsidecar/group-service.go | 22 + game-server/libsidecar/grpc-api.go | 8 + .../libsidecar/grpcapi/cpp-bindings.go | 139 +++- game-server/libsidecar/grpcapi/lfg-errors.go | 186 +++++ .../libsidecar/grpcapi/server-battleground.go | 18 +- .../libsidecar/grpcapi/server-guild.go | 72 ++ .../libsidecar/grpcapi/server-items.go | 92 ++- game-server/libsidecar/grpcapi/server-lfg.go | 408 +++++++++++ game-server/libsidecar/guids/mgr.go | 2 +- game-server/libsidecar/guild-api.c | 17 + game-server/libsidecar/guild-api.go | 36 + game-server/libsidecar/guild-api.h | 32 + game-server/libsidecar/lfg-api.c | 100 +++ game-server/libsidecar/lfg-api.go | 237 ++++++ game-server/libsidecar/lfg-api.h | 163 +++++ game-server/libsidecar/lib.go | 105 ++- game-server/libsidecar/libsidecar.h | 347 +++++++++ game-server/libsidecar/matchmaking.go | 66 +- game-server/libsidecar/monitoring.go | 109 ++- game-server/libsidecar/player-items-api.c | 17 +- game-server/libsidecar/player-items-api.go | 61 +- game-server/libsidecar/player-items-api.h | 25 +- libsidecar.h | 341 +++++++++ 35 files changed, 4220 insertions(+), 145 deletions(-) create mode 100644 game-server/libsidecar/arena-team-api.go create mode 100644 game-server/libsidecar/arena-team-api.h create mode 100644 game-server/libsidecar/character-service.go create mode 100644 game-server/libsidecar/group-service.go create mode 100644 game-server/libsidecar/grpcapi/lfg-errors.go create mode 100644 game-server/libsidecar/grpcapi/server-guild.go create mode 100644 game-server/libsidecar/grpcapi/server-lfg.go create mode 100644 game-server/libsidecar/guild-api.c create mode 100644 game-server/libsidecar/guild-api.go create mode 100644 game-server/libsidecar/guild-api.h create mode 100644 game-server/libsidecar/lfg-api.c create mode 100644 game-server/libsidecar/lfg-api.go create mode 100644 game-server/libsidecar/lfg-api.h create mode 100644 game-server/libsidecar/libsidecar.h create mode 100644 libsidecar.h diff --git a/game-server/libsidecar/arena-team-api.go b/game-server/libsidecar/arena-team-api.go new file mode 100644 index 0000000..ae52ccc --- /dev/null +++ b/game-server/libsidecar/arena-team-api.go @@ -0,0 +1,224 @@ +package main + +/* +#include "arena-team-api.h" +#include +*/ +import "C" + +import ( + "context" + "errors" + "time" + "unsafe" + + "github.com/rs/zerolog/log" + + mmPb "github.com/walkline/ToCloud9/gen/matchmaking/pb" +) + +// TC9FinishRatedArenaMatch sends a rated arena outcome to matchmaking for rating/stat calculation and persistence. +// +//export TC9FinishRatedArenaMatch +func TC9FinishRatedArenaMatch( + ownerRealmID C.uint32_t, + isCrossRealm C.uint8_t, + instanceID C.uint32_t, + arenaType C.uint32_t, + winnerTeam C.uint8_t, + validArena C.uint8_t, + allianceArenaTeamID C.uint32_t, + hordeArenaTeamID C.uint32_t, + allianceArenaMatchmakerRating C.uint32_t, + hordeArenaMatchmakerRating C.uint32_t, + participants *C.TC9RatedArenaParticipant, + participantsSize C.uint32_t, +) C.TC9FinishRatedArenaMatchResponse { + if matchmakingServiceClient == nil { + return finishRatedArenaMatchResponse(C.ArenaTeamMutationStatusFailed, nil) + } + + log.Debug(). + Uint32("ownerRealmID", uint32(ownerRealmID)). + Bool("isCrossrealm", isCrossRealm != 0). + Uint32("instanceID", uint32(instanceID)). + Uint32("arenaType", uint32(arenaType)). + Uint8("winnerTeam", uint8(winnerTeam)). + Bool("validArena", validArena != 0). + Uint32("allianceArenaTeamID", uint32(allianceArenaTeamID)). + Uint32("hordeArenaTeamID", uint32(hordeArenaTeamID)). + Uint32("allianceArenaMMR", uint32(allianceArenaMatchmakerRating)). + Uint32("hordeArenaMMR", uint32(hordeArenaMatchmakerRating)). + Uint32("participants", uint32(participantsSize)). + Msg("TC9 finish rated arena match request") + + protoParticipants := make([]*mmPb.RatedArenaParticipant, 0, uint32(participantsSize)) + if participantsSize > 0 { + participantSlice := unsafe.Slice((*C.TC9RatedArenaParticipant)(unsafe.Pointer(participants)), int(participantsSize)) + for _, participant := range participantSlice { + protoParticipants = append(protoParticipants, &mmPb.RatedArenaParticipant{ + Team: pvpTeamToProto(uint8(participant.team)), + PlayerGUID: uint64(participant.playerGuid), + }) + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + resp, err := matchmakingServiceClient.FinishRatedArenaMatch(ctx, &mmPb.FinishRatedArenaMatchRequest{ + Api: matchmakingSupportedVer, + OwnerRealmID: uint32(ownerRealmID), + IsCrossRealm: isCrossRealm != 0, + InstanceID: uint32(instanceID), + ArenaType: uint32(arenaType), + WinnerTeam: pvpTeamToProto(uint8(winnerTeam)), + ValidArena: validArena != 0, + AllianceArenaTeamID: uint32(allianceArenaTeamID), + HordeArenaTeamID: uint32(hordeArenaTeamID), + AllianceArenaMatchmakerRating: uint32(allianceArenaMatchmakerRating), + HordeArenaMatchmakerRating: uint32(hordeArenaMatchmakerRating), + Participants: protoParticipants, + }) + if err != nil { + logArenaTeamMutationError(err, "finish rated arena match", ownerRealmID, 0) + return finishRatedArenaMatchResponse(C.ArenaTeamMutationStatusFailed, nil) + } + if resp == nil { + logArenaTeamMutationError(errors.New("matchmaking service returned nil rated arena result response"), "finish rated arena match", ownerRealmID, 0) + return finishRatedArenaMatchResponse(C.ArenaTeamMutationStatusFailed, nil) + } + + log.Debug(). + Uint32("ownerRealmID", uint32(ownerRealmID)). + Uint32("instanceID", uint32(instanceID)). + Str("status", resp.GetStatus().String()). + Uint32("memberResults", uint32(len(resp.GetMemberResults()))). + Msg("TC9 finish rated arena match response") + + return finishRatedArenaMatchResponse(matchmakingArenaTeamMutationStatusToC(resp.GetStatus()), resp) +} + +//export TC9FreeFinishRatedArenaMatchResponse +func TC9FreeFinishRatedArenaMatchResponse(response *C.TC9FinishRatedArenaMatchResponse) { + if response == nil { + return + } + if response.allianceScore.teamName != nil { + C.free(unsafe.Pointer(response.allianceScore.teamName)) + response.allianceScore.teamName = nil + } + if response.hordeScore.teamName != nil { + C.free(unsafe.Pointer(response.hordeScore.teamName)) + response.hordeScore.teamName = nil + } + if response.members != nil { + C.free(unsafe.Pointer(response.members)) + response.members = nil + } + response.membersSize = 0 +} + +func matchmakingArenaTeamMutationStatusToC(status mmPb.MatchmakingArenaTeamMutationStatus) C.ArenaTeamMutationStatus { + switch status { + case mmPb.MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_OK: + return C.ArenaTeamMutationStatusOk + case mmPb.MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_NOT_FOUND: + return C.ArenaTeamMutationStatusNotFound + case mmPb.MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_MEMBER_MISMATCH: + return C.ArenaTeamMutationStatusMemberMismatch + case mmPb.MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_INVALID_TYPE: + return C.ArenaTeamMutationStatusInvalidType + case mmPb.MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_NAME_EXISTS: + return C.ArenaTeamMutationStatusNameExists + case mmPb.MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_ALREADY_IN_TEAM: + return C.ArenaTeamMutationStatusAlreadyInTeam + case mmPb.MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_ROSTER_FULL: + return C.ArenaTeamMutationStatusRosterFull + case mmPb.MatchmakingArenaTeamMutationStatus_MATCHMAKING_ARENA_TEAM_MUTATION_INVALID_NAME: + return C.ArenaTeamMutationStatusInvalidName + default: + return C.ArenaTeamMutationStatusFailed + } +} + +func finishRatedArenaMatchResponse(status C.ArenaTeamMutationStatus, resp *mmPb.FinishRatedArenaMatchResponse) C.TC9FinishRatedArenaMatchResponse { + result := C.TC9FinishRatedArenaMatchResponse{ + status: C.uint32_t(status), + } + if resp == nil { + return result + } + + result.allianceScore = ratedArenaTeamScoreToC(resp.GetAllianceScore()) + result.hordeScore = ratedArenaTeamScoreToC(resp.GetHordeScore()) + + members := resp.GetMemberResults() + if len(members) > 0 { + result.members = (*C.TC9RatedArenaMemberResult)(C.malloc(C.size_t(len(members)) * C.size_t(unsafe.Sizeof(C.TC9RatedArenaMemberResult{})))) + if result.members == nil { + result.status = C.uint32_t(C.ArenaTeamMutationStatusFailed) + return result + } + memberSlice := unsafe.Slice((*C.TC9RatedArenaMemberResult)(unsafe.Pointer(result.members)), len(members)) + for i, member := range members { + memberSlice[i] = C.TC9RatedArenaMemberResult{ + team: C.uint8_t(protoPVPTeamToC(member.GetTeam())), + playerGuid: C.uint64_t(member.GetPlayerGUID()), + personalRating: C.uint32_t(member.GetPersonalRating()), + weekGames: C.uint32_t(member.GetWeekGames()), + seasonGames: C.uint32_t(member.GetSeasonGames()), + weekWins: C.uint32_t(member.GetWeekWins()), + seasonWins: C.uint32_t(member.GetSeasonWins()), + matchmakerRating: C.uint32_t(member.GetMatchmakerRating()), + } + } + result.membersSize = C.uint32_t(len(members)) + } + + return result +} + +func ratedArenaTeamScoreToC(score *mmPb.RatedArenaTeamScore) C.TC9RatedArenaTeamScore { + if score == nil { + return C.TC9RatedArenaTeamScore{} + } + return C.TC9RatedArenaTeamScore{ + realmID: C.uint32_t(score.GetRealmID()), + arenaTeamID: C.uint32_t(score.GetArenaTeamID()), + teamName: C.CString(score.GetTeamName()), + ratingChange: C.int32_t(score.GetRatingChange()), + matchmakerRating: C.uint32_t(score.GetMatchmakerRating()), + } +} + +func pvpTeamToProto(team uint8) mmPb.PVPTeamID { + switch team { + case 1: + return mmPb.PVPTeamID_Alliance + case 2: + return mmPb.PVPTeamID_Horde + default: + return mmPb.PVPTeamID_Any + } +} + +func protoPVPTeamToC(team mmPb.PVPTeamID) uint8 { + switch team { + case mmPb.PVPTeamID_Alliance: + return 1 + case mmPb.PVPTeamID_Horde: + return 2 + default: + return 0 + } +} + +func logArenaTeamMutationError(err error, action string, realmID C.uint32_t, arenaTeamID C.uint32_t) { + log.Error(). + Err(err). + Str("action", action). + Uint32("realmID", uint32(realmID)). + Uint32("arenaTeamID", uint32(arenaTeamID)). + Msg("arena team mutation failed") +} diff --git a/game-server/libsidecar/arena-team-api.h b/game-server/libsidecar/arena-team-api.h new file mode 100644 index 0000000..f685d90 --- /dev/null +++ b/game-server/libsidecar/arena-team-api.h @@ -0,0 +1,61 @@ +#ifndef __ARENA_TEAM_API__ +#define __ARENA_TEAM_API__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum ArenaTeamMutationStatus { + ArenaTeamMutationStatusOk = 0, + ArenaTeamMutationStatusNotFound = 1, + ArenaTeamMutationStatusMemberMismatch = 2, + ArenaTeamMutationStatusInvalidType = 3, + ArenaTeamMutationStatusNameExists = 4, + ArenaTeamMutationStatusAlreadyInTeam = 5, + ArenaTeamMutationStatusRosterFull = 6, + ArenaTeamMutationStatusFailed = 7, + ArenaTeamMutationStatusInvalidName = 8, +} ArenaTeamMutationStatus; + +typedef struct { + uint8_t team; + uint64_t playerGuid; +} TC9RatedArenaParticipant; + +typedef struct { + uint8_t team; + uint64_t playerGuid; + uint32_t personalRating; + uint32_t weekGames; + uint32_t seasonGames; + uint32_t weekWins; + uint32_t seasonWins; + uint32_t matchmakerRating; +} TC9RatedArenaMemberResult; + +typedef struct { + uint32_t realmID; + uint32_t arenaTeamID; + char* teamName; + int32_t ratingChange; + uint32_t matchmakerRating; +} TC9RatedArenaTeamScore; + +typedef struct { + uint32_t status; + TC9RatedArenaTeamScore allianceScore; + TC9RatedArenaTeamScore hordeScore; + TC9RatedArenaMemberResult* members; + uint32_t membersSize; +} TC9FinishRatedArenaMatchResponse; + +extern TC9FinishRatedArenaMatchResponse TC9FinishRatedArenaMatch(uint32_t ownerRealmID, uint8_t isCrossRealm, uint32_t instanceID, uint32_t arenaType, uint8_t winnerTeam, uint8_t validArena, uint32_t allianceArenaTeamID, uint32_t hordeArenaTeamID, uint32_t allianceArenaMatchmakerRating, uint32_t hordeArenaMatchmakerRating, TC9RatedArenaParticipant* participants, uint32_t participantsSize); +extern void TC9FreeFinishRatedArenaMatchResponse(TC9FinishRatedArenaMatchResponse* response); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/game-server/libsidecar/battleground-api.go b/game-server/libsidecar/battleground-api.go index 06ff119..5b7579c 100644 --- a/game-server/libsidecar/battleground-api.go +++ b/game-server/libsidecar/battleground-api.go @@ -11,6 +11,24 @@ import ( "github.com/walkline/ToCloud9/game-server/libsidecar/grpcapi" ) +func tc9BattlegroundGUIDArray(values []uint64) (*C.uint64_t, error) { + if len(values) == 0 { + return nil, nil + } + + ptr := (*C.uint64_t)(C.malloc(C.size_t(len(values)) * C.size_t(unsafe.Sizeof(C.uint64_t(0))))) + if ptr == nil { + return nil, grpcapi.BattlegroundError(C.BattlegroundErrorCodeNoHandler) + } + + slice := unsafe.Slice(ptr, len(values)) + for i, guid := range values { + slice[i] = C.uint64_t(guid) + } + + return ptr, nil +} + // TC9SetBattlegroundStartHandler sets handler for starting battleground. // //export TC9SetBattlegroundStartHandler @@ -26,37 +44,26 @@ func BattlegroundStartHandler(request grpcapi.BattlegroundStartRequest) (*grpcap crequest.isRated = C.bool(request.IsRated) crequest.mapID = C.uint32_t(request.MapID) crequest.bracketLvl = C.uint8_t(request.BracketLvl) + crequest.allianceArenaTeamID = C.uint32_t(request.AllianceArenaTeamID) + crequest.hordeArenaTeamID = C.uint32_t(request.HordeArenaTeamID) + crequest.allianceArenaMatchmakerRating = C.uint32_t(request.AllianceArenaMatchmakerRating) + crequest.hordeArenaMatchmakerRating = C.uint32_t(request.HordeArenaMatchmakerRating) crequest.alliancePlayersToAddSize = C.int(len(request.AlliancePlayerGUIDsToAdd)) crequest.hordePlayersToAddSize = C.int(len(request.HordePlayerGUIDsToAdd)) // TODO: add later crequest.randomBGPlayersSize = 0 - if len(request.AlliancePlayerGUIDsToAdd) > 0 { - crequest.alliancePlayersToAdd = (*C.uint64_t)(C.malloc(C.size_t(len(request.AlliancePlayerGUIDsToAdd)) * C.size_t(unsafe.Sizeof(C.uint64_t(0))))) - if crequest.alliancePlayersToAdd == nil { - return nil, grpcapi.BattlegroundError(C.BattlegroundErrorCodeNoHandler) // or an appropriate error - } - - for i, guid := range request.AlliancePlayerGUIDsToAdd { - cguid := (*C.uint64_t)(unsafe.Pointer(uintptr(unsafe.Pointer(crequest.alliancePlayersToAdd)) + uintptr(i)*unsafe.Sizeof(*crequest.alliancePlayersToAdd))) - *cguid = C.uint64_t(guid) - } - } else { - crequest.alliancePlayersToAdd = nil + var err error + crequest.alliancePlayersToAdd, err = tc9BattlegroundGUIDArray(request.AlliancePlayerGUIDsToAdd) + if err != nil { + return nil, err } - - if len(request.HordePlayerGUIDsToAdd) > 0 { - crequest.hordePlayersToAdd = (*C.uint64_t)(C.malloc(C.size_t(len(request.HordePlayerGUIDsToAdd)) * C.size_t(unsafe.Sizeof(C.uint64_t(0))))) - if crequest.hordePlayersToAdd == nil { - return nil, grpcapi.BattlegroundError(C.BattlegroundErrorCodeNoHandler) // or an appropriate error + crequest.hordePlayersToAdd, err = tc9BattlegroundGUIDArray(request.HordePlayerGUIDsToAdd) + if err != nil { + if crequest.alliancePlayersToAdd != nil { + C.free(unsafe.Pointer(crequest.alliancePlayersToAdd)) } - - for i, guid := range request.HordePlayerGUIDsToAdd { - cguid := (*C.uint64_t)(unsafe.Pointer(uintptr(unsafe.Pointer(crequest.hordePlayersToAdd)) + uintptr(i)*unsafe.Sizeof(*crequest.alliancePlayersToAdd))) - *cguid = C.uint64_t(guid) - } - } else { - crequest.hordePlayersToAdd = nil + return nil, err } res := C.CallBattlegroundStartHandler((*C.BattlegroundStartRequest)(unsafe.Pointer(&crequest))) @@ -96,32 +103,17 @@ func BattlegroundAddPlayersHandler(request grpcapi.BattlegroundAddPlayersRequest // TODO: add later crequest.randomBGPlayersSize = 0 - if len(request.AlliancePlayerGUIDsToAdd) > 0 { - crequest.alliancePlayersToAdd = (*C.uint64_t)(C.malloc(C.size_t(len(request.AlliancePlayerGUIDsToAdd)) * C.size_t(unsafe.Sizeof(C.uint64_t(0))))) - if crequest.alliancePlayersToAdd == nil { - return grpcapi.BattlegroundError(C.BattlegroundErrorCodeNoHandler) // or an appropriate error - } - - for i, guid := range request.AlliancePlayerGUIDsToAdd { - cguid := (*C.uint64_t)(unsafe.Pointer(uintptr(unsafe.Pointer(crequest.alliancePlayersToAdd)) + uintptr(i)*unsafe.Sizeof(*crequest.alliancePlayersToAdd))) - *cguid = C.uint64_t(guid) - } - } else { - crequest.alliancePlayersToAdd = nil + var err error + crequest.alliancePlayersToAdd, err = tc9BattlegroundGUIDArray(request.AlliancePlayerGUIDsToAdd) + if err != nil { + return err } - - if len(request.HordePlayerGUIDsToAdd) > 0 { - crequest.hordePlayersToAdd = (*C.uint64_t)(C.malloc(C.size_t(len(request.HordePlayerGUIDsToAdd)) * C.size_t(unsafe.Sizeof(C.uint64_t(0))))) - if crequest.hordePlayersToAdd == nil { - return grpcapi.BattlegroundError(C.BattlegroundErrorCodeNoHandler) // or an appropriate error - } - - for i, guid := range request.HordePlayerGUIDsToAdd { - cguid := (*C.uint64_t)(unsafe.Pointer(uintptr(unsafe.Pointer(crequest.hordePlayersToAdd)) + uintptr(i)*unsafe.Sizeof(*crequest.alliancePlayersToAdd))) - *cguid = C.uint64_t(guid) + crequest.hordePlayersToAdd, err = tc9BattlegroundGUIDArray(request.HordePlayerGUIDsToAdd) + if err != nil { + if crequest.alliancePlayersToAdd != nil { + C.free(unsafe.Pointer(crequest.alliancePlayersToAdd)) } - } else { - crequest.hordePlayersToAdd = nil + return err } res := C.CallBattlegroundAddPlayersHandler((*C.BattlegroundAddPlayersRequest)(unsafe.Pointer(&crequest))) diff --git a/game-server/libsidecar/battleground-api.h b/game-server/libsidecar/battleground-api.h index 6134db9..53c0346 100644 --- a/game-server/libsidecar/battleground-api.h +++ b/game-server/libsidecar/battleground-api.h @@ -18,6 +18,10 @@ typedef struct { bool isRated; uint32_t mapID; uint8_t bracketLvl; + uint32_t allianceArenaTeamID; + uint32_t hordeArenaTeamID; + uint32_t allianceArenaMatchmakerRating; + uint32_t hordeArenaMatchmakerRating; uint64_t *hordePlayersToAdd; int hordePlayersToAddSize; uint64_t *alliancePlayersToAdd; diff --git a/game-server/libsidecar/character-service.go b/game-server/libsidecar/character-service.go new file mode 100644 index 0000000..9720bc1 --- /dev/null +++ b/game-server/libsidecar/character-service.go @@ -0,0 +1,22 @@ +package main + +import ( + "github.com/rs/zerolog/log" + "github.com/walkline/ToCloud9/game-server/libsidecar/config" + charPb "github.com/walkline/ToCloud9/gen/characters/pb" + "google.golang.org/grpc" +) + +var characterServiceClient charPb.CharactersServiceClient + +func SetupCharacterServiceConnection(cfg *config.Config) *grpc.ClientConn { + log.Info().Str("address", cfg.CharacterServiceAddress).Msg("connecting to character service") + + conn, err := grpc.Dial(cfg.CharacterServiceAddress, grpc.WithInsecure()) + if err != nil { + log.Fatal().Err(err).Msg("can't connect to the character server") + } + + characterServiceClient = charPb.NewCharactersServiceClient(conn) + return conn +} diff --git a/game-server/libsidecar/config/config.go b/game-server/libsidecar/config/config.go index 8fb1987..79ae9ee 100644 --- a/game-server/libsidecar/config/config.go +++ b/game-server/libsidecar/config/config.go @@ -1,6 +1,12 @@ package config import ( + "net" + "net/url" + "os" + "strconv" + "strings" + "github.com/walkline/ToCloud9/shared/config" ) @@ -17,6 +23,9 @@ type Config struct { // PreferredHostname is referred host name that will be used to connect from gateway and for health checks PreferredHostname string `yaml:"preferredHostname" env:"PREFERRED_HOSTNAME"` + // LoopbackHostOverride rewrites loopback service targets for split WSL/Windows local deployments. + LoopbackHostOverride string `yaml:"loopbackHostOverride" env:"LOOPBACK_HOST_OVERRIDE" env-default:"auto"` + // ServersRegistryServiceAddress is address of servers registry service ServersRegistryServiceAddress string `yaml:"serversRegistryServiceAddress" env:"SERVERS_REGISTRY_SERVICE_ADDRESS" env-default:"localhost:8999"` @@ -26,6 +35,11 @@ type Config struct { // GuidProviderServiceAddress is address of service that provides guids to use GuidProviderServiceAddress string `yaml:"guidProviderServiceAddress" env:"GUID_PROVIDER_SERVICE_ADDRESS" env-default:"localhost:8996"` + GroupServiceAddress string `yaml:"groupServiceAddress" env:"GROUP_SERVICE_ADDRESS" env-default:"localhost:8998"` + + // CharacterServiceAddress is address of characters service. + CharacterServiceAddress string `yaml:"charactersServiceAddress" env:"CHAR_SERVICE_ADDRESS" env-default:"localhost:8991"` + // CharacterGuidsBufferSize is the size of the buffer for characters guids CharacterGuidsBufferSize int `yaml:"characterGuidsBufferSize" env:"CHARACTER_GUIDS_BUFFER_SIZE" env-default:"50"` @@ -53,5 +67,144 @@ func LoadConfig() (*Config, error) { return nil, err } + c.Root.applyRuntimeOverrides() + return &c.Root, nil } + +func (c *Config) applyRuntimeOverrides() { + loopbackOverride := resolveLoopbackOverride(c.LoopbackHostOverride) + if loopbackOverride == "" { + return + } + + c.ServersRegistryServiceAddress = rewriteLoopbackHostPort(c.ServersRegistryServiceAddress, loopbackOverride) + c.MatchmakingServiceAddress = rewriteLoopbackHostPort(c.MatchmakingServiceAddress, loopbackOverride) + c.GuidProviderServiceAddress = rewriteLoopbackHostPort(c.GuidProviderServiceAddress, loopbackOverride) + c.GroupServiceAddress = rewriteLoopbackHostPort(c.GroupServiceAddress, loopbackOverride) + c.CharacterServiceAddress = rewriteLoopbackHostPort(c.CharacterServiceAddress, loopbackOverride) + c.NatsURL = rewriteLoopbackURL(c.NatsURL, loopbackOverride) +} + +func resolveLoopbackOverride(value string) string { + normalized := strings.TrimSpace(strings.ToLower(value)) + switch normalized { + case "", "off", "false", "disabled": + return "" + case "auto": + return detectWSLHostIP() + default: + return strings.TrimSpace(value) + } +} + +func rewriteLoopbackHostPort(address string, override string) string { + host, port, err := net.SplitHostPort(address) + if err != nil || !isLoopbackHost(host) { + return address + } + + return net.JoinHostPort(override, port) +} + +func rewriteLoopbackURL(rawURL string, override string) string { + parsedURL, err := url.Parse(rawURL) + if err != nil || parsedURL.Host == "" { + return rawURL + } + + host, port, err := net.SplitHostPort(parsedURL.Host) + if err != nil { + host = parsedURL.Host + } + + if !isLoopbackHost(host) { + return rawURL + } + + if port == "" { + parsedURL.Host = override + } else { + parsedURL.Host = net.JoinHostPort(override, port) + } + + return parsedURL.String() +} + +func isLoopbackHost(host string) bool { + normalized := strings.Trim(strings.ToLower(host), "[]") + return normalized == "localhost" || normalized == "::1" || normalized == "0:0:0:0:0:0:0:1" || strings.HasPrefix(normalized, "127.") +} + +func detectWSLHostIP() string { + osRelease, err := os.ReadFile("/proc/sys/kernel/osrelease") + if err != nil { + return "" + } + + normalizedRelease := strings.ToLower(string(osRelease)) + if !strings.Contains(normalizedRelease, "microsoft") && !strings.Contains(normalizedRelease, "wsl") { + return "" + } + + if ip := detectWSLDefaultGatewayIP(); ip != "" { + return ip + } + + return detectWSLNameserverIP() +} + +func detectWSLDefaultGatewayIP() string { + route, err := os.ReadFile("/proc/net/route") + if err != nil { + return "" + } + + return parseWSLDefaultGatewayIP(string(route)) +} + +func parseWSLDefaultGatewayIP(route string) string { + for _, line := range strings.Split(route, "\n") { + fields := strings.Fields(line) + if len(fields) < 3 || fields[0] == "lo" || fields[1] != "00000000" { + continue + } + + gateway, err := strconv.ParseUint(fields[2], 16, 32) + if err != nil || gateway == 0 { + continue + } + + ip := net.IPv4(byte(gateway), byte(gateway>>8), byte(gateway>>16), byte(gateway>>24)) + if ip == nil || ip.IsLoopback() { + continue + } + + return ip.String() + } + + return "" +} + +func detectWSLNameserverIP() string { + resolvConf, err := os.ReadFile("/etc/resolv.conf") + if err != nil { + return "" + } + + for _, line := range strings.Split(string(resolvConf), "\n") { + fields := strings.Fields(line) + if len(fields) < 2 || fields[0] != "nameserver" { + continue + } + + ip := net.ParseIP(fields[1]) + if ip == nil || ip.IsLoopback() { + continue + } + + return ip.String() + } + + return "" +} diff --git a/game-server/libsidecar/consumer/consumer.go b/game-server/libsidecar/consumer/consumer.go index 8ebf064..61bdce6 100644 --- a/game-server/libsidecar/consumer/consumer.go +++ b/game-server/libsidecar/consumer/consumer.go @@ -117,7 +117,7 @@ func (c *natsConsumer) Start() error { return } - if p.RealmID != c.realmID { + if !c.shouldConsumeGroupEvent(p.RealmID, groupMemberGUIDs(p.Members)) { return } @@ -137,7 +137,7 @@ func (c *natsConsumer) Start() error { return } - if p.RealmID != c.realmID { + if !c.shouldConsumeGroupEvent(p.RealmID, appendGroupGUID(p.OnlineMembers, p.MemberGUID)) { return } @@ -157,7 +157,7 @@ func (c *natsConsumer) Start() error { return } - if p.RealmID != c.realmID { + if !c.shouldConsumeGroupEvent(p.RealmID, appendGroupGUID(p.OnlineMembers, p.MemberGUID)) { return } @@ -169,6 +169,26 @@ func (c *natsConsumer) Start() error { c.subs = append(c.subs, sub) + sub, err = c.nc.Subscribe(events.GroupEventGroupLeaderChanged.SubjectName(), func(msg *nats.Msg) { + p := events.GroupEventGroupLeaderChangedPayload{} + _, err := events.Unmarshal(msg.Data, &p) + if err != nil { + log.Error().Err(err).Msg("can't read GroupEventGroupLeaderChanged (payload part) event") + return + } + + if !c.shouldConsumeGroupEvent(p.RealmID, appendGroupGUIDs(p.OnlineMembers, p.PreviousLeader, p.NewLeader)) { + return + } + + c.queue.Push(c.groupHandlersFabric.GroupLeaderChanged(&p)) + }) + if err != nil { + return err + } + + c.subs = append(c.subs, sub) + sub, err = c.nc.Subscribe(events.GroupEventGroupDisband.SubjectName(), func(msg *nats.Msg) { p := events.GroupEventGroupDisbandPayload{} _, err := events.Unmarshal(msg.Data, &p) @@ -177,7 +197,7 @@ func (c *natsConsumer) Start() error { return } - if p.RealmID != c.realmID { + if !c.shouldConsumeGroupEvent(p.RealmID, p.OnlineMembers) { return } @@ -197,7 +217,7 @@ func (c *natsConsumer) Start() error { return } - if p.RealmID != c.realmID { + if !c.shouldConsumeGroupEvent(p.RealmID, appendGroupGUID(p.OnlineMembers, p.NewLooterGUID)) { return } @@ -217,7 +237,7 @@ func (c *natsConsumer) Start() error { return } - if p.RealmID != c.realmID { + if !c.shouldConsumeGroupEvent(p.RealmID, appendGroupGUID(p.Receivers, p.Updater)) { return } @@ -237,7 +257,7 @@ func (c *natsConsumer) Start() error { return } - if p.RealmID != c.realmID { + if !c.shouldConsumeGroupEvent(p.RealmID, appendGroupGUID(p.OnlineMembers, p.Leader)) { return } @@ -265,6 +285,162 @@ func (c *natsConsumer) Start() error { c.subs = append(c.subs, sub) + sub, err = c.nc.Subscribe(events.GroupEventGroupReadyCheckStarted.SubjectName(), func(msg *nats.Msg) { + p := events.GroupEventReadyCheckStartedPayload{} + _, err := events.Unmarshal(msg.Data, &p) + if err != nil { + log.Error().Err(err).Msg("can't read GroupEventReadyCheckStarted event") + return + } + if !c.shouldConsumeGroupEvent(p.RealmID, appendGroupGUID(p.Receivers, p.LeaderGUID)) { + return + } + c.queue.Push(c.groupHandlersFabric.GroupReadyCheckStarted(&p)) + }) + if err != nil { + return err + } + c.subs = append(c.subs, sub) + + sub, err = c.nc.Subscribe(events.GroupEventGroupReadyCheckMemberState.SubjectName(), func(msg *nats.Msg) { + p := events.GroupEventReadyCheckMemberStatePayload{} + _, err := events.Unmarshal(msg.Data, &p) + if err != nil { + log.Error().Err(err).Msg("can't read GroupEventReadyCheckMemberState event") + return + } + if !c.shouldConsumeGroupEvent(p.RealmID, p.Receivers) { + return + } + c.queue.Push(c.groupHandlersFabric.GroupReadyCheckMemberState(&p)) + }) + if err != nil { + return err + } + c.subs = append(c.subs, sub) + + sub, err = c.nc.Subscribe(events.GroupEventGroupReadyCheckFinished.SubjectName(), func(msg *nats.Msg) { + p := events.GroupEventReadyCheckFinishedPayload{} + _, err := events.Unmarshal(msg.Data, &p) + if err != nil { + log.Error().Err(err).Msg("can't read GroupEventReadyCheckFinished event") + return + } + if !c.shouldConsumeGroupEvent(p.RealmID, p.Receivers) { + return + } + c.queue.Push(c.groupHandlersFabric.GroupReadyCheckFinished(&p)) + }) + if err != nil { + return err + } + c.subs = append(c.subs, sub) + + sub, err = c.nc.Subscribe(events.GroupEventGroupMemberSubGroupChanged.SubjectName(), func(msg *nats.Msg) { + p := events.GroupEventMemberSubGroupChangedPayload{} + _, err := events.Unmarshal(msg.Data, &p) + if err != nil { + log.Error().Err(err).Msg("can't read GroupEventMemberSubGroupChanged event") + return + } + if !c.shouldConsumeGroupEvent(p.RealmID, appendGroupGUID(p.Receivers, p.MemberGUID)) { + return + } + c.queue.Push(c.groupHandlersFabric.GroupMemberSubGroupChanged(&p)) + }) + if err != nil { + return err + } + c.subs = append(c.subs, sub) + + sub, err = c.nc.Subscribe(events.GroupEventGroupMemberFlagsChanged.SubjectName(), func(msg *nats.Msg) { + p := events.GroupEventMemberFlagsChangedPayload{} + _, err := events.Unmarshal(msg.Data, &p) + if err != nil { + log.Error().Err(err).Msg("can't read GroupEventMemberFlagsChanged event") + return + } + if !c.shouldConsumeGroupEvent(p.RealmID, appendGroupGUID(p.Receivers, p.MemberGUID)) { + return + } + c.queue.Push(c.groupHandlersFabric.GroupMemberFlagsChanged(&p)) + }) + if err != nil { + return err + } + c.subs = append(c.subs, sub) + + sub, err = c.nc.Subscribe(events.GroupEventGroupMemberStateChanged.SubjectName(), func(msg *nats.Msg) { + p := events.GroupEventMemberStateChangedPayload{} + _, err := events.Unmarshal(msg.Data, &p) + if err != nil { + log.Error().Err(err).Msg("can't read GroupEventMemberStateChanged event") + return + } + if !c.shouldConsumeGroupEvent(p.RealmID, p.Receivers) { + return + } + c.queue.Push(c.groupHandlersFabric.GroupMemberStateChanged(&p)) + }) + if err != nil { + return err + } + c.subs = append(c.subs, sub) + + sub, err = c.nc.Subscribe(events.GroupEventGroupMemberStatesChanged.SubjectName(), func(msg *nats.Msg) { + p := events.GroupEventMemberStatesChangedPayload{} + _, err := events.Unmarshal(msg.Data, &p) + if err != nil { + log.Error().Err(err).Msg("can't read GroupEventMemberStatesChanged event") + return + } + if !c.shouldConsumeGroupEvent(p.RealmID, p.Receivers) { + return + } + for _, state := range p.States { + payload := groupMemberStateChangedPayloadFromBatch(&p, state) + c.queue.Push(c.groupHandlersFabric.GroupMemberStateChanged(&payload)) + } + }) + if err != nil { + return err + } + c.subs = append(c.subs, sub) + + sub, err = c.nc.Subscribe(events.GroupEventGroupInstanceResetRequest.SubjectName(), func(msg *nats.Msg) { + p := events.GroupEventInstanceResetRequestPayload{} + _, err := events.Unmarshal(msg.Data, &p) + if err != nil { + log.Error().Err(err).Msg("can't read GroupEventInstanceResetRequest event") + return + } + if !c.shouldConsumeGroupEvent(p.RealmID, appendGroupGUID(p.Receivers, p.PlayerGUID)) { + return + } + c.queue.Push(c.groupHandlersFabric.GroupInstanceResetRequest(&p)) + }) + if err != nil { + return err + } + c.subs = append(c.subs, sub) + + sub, err = c.nc.Subscribe(events.GroupEventGroupInstanceBindExtensionRequest.SubjectName(), func(msg *nats.Msg) { + p := events.GroupEventInstanceBindExtensionRequestPayload{} + _, err := events.Unmarshal(msg.Data, &p) + if err != nil { + log.Error().Err(err).Msg("can't read GroupEventInstanceBindExtensionRequest event") + return + } + if !c.shouldConsumeGroupEvent(p.RealmID, appendGroupGUID(p.Receivers, p.PlayerGUID)) { + return + } + c.queue.Push(c.groupHandlersFabric.GroupInstanceBindExtensionRequest(&p)) + }) + if err != nil { + return err + } + c.subs = append(c.subs, sub) + return nil } @@ -272,6 +448,76 @@ func (c *natsConsumer) Stop() error { return c.unsubscribe() } +func groupMemberStateChangedPayloadFromBatch(batch *events.GroupEventMemberStatesChangedPayload, state events.GroupMemberStateUpdate) events.GroupEventMemberStateChangedPayload { + return events.GroupEventMemberStateChangedPayload{ + ServiceID: batch.ServiceID, + RealmID: batch.RealmID, + GroupID: batch.GroupID, + SourceGatewayID: batch.SourceGatewayID, + SourceWorldserverID: batch.SourceWorldserverID, + MemberGUID: state.MemberGUID, + Online: state.Online, + Level: state.Level, + Class: state.Class, + ZoneID: state.ZoneID, + MapID: state.MapID, + Health: state.Health, + MaxHealth: state.MaxHealth, + PowerType: state.PowerType, + Power: state.Power, + MaxPower: state.MaxPower, + AurasKnown: state.AurasKnown, + Auras: state.Auras, + Receivers: batch.Receivers, + } +} + +func (c *natsConsumer) shouldConsumeGroupEvent(groupRealmID uint32, playerGUIDs []uint64) bool { + for _, playerGUID := range playerGUIDs { + if c.isLocalGroupPlayer(groupRealmID, playerGUID) { + return true + } + } + return false +} + +func (c *natsConsumer) isLocalGroupPlayer(groupRealmID uint32, playerGUID uint64) bool { + if playerGUID == 0 { + return false + } + if playerGUID>>48 == 0 { + if playerRealmID := uint32((playerGUID >> 32) & 0xffff); playerRealmID != 0 { + return playerRealmID == c.realmID + } + } + return groupRealmID == c.realmID +} + +func groupMemberGUIDs(members []events.GroupMember) []uint64 { + playerGUIDs := make([]uint64, 0, len(members)) + for _, member := range members { + playerGUIDs = append(playerGUIDs, member.MemberGUID) + } + return playerGUIDs +} + +func appendGroupGUID(playerGUIDs []uint64, playerGUID uint64) []uint64 { + if playerGUID == 0 { + return playerGUIDs + } + return appendGroupGUIDs(playerGUIDs, playerGUID) +} + +func appendGroupGUIDs(playerGUIDs []uint64, extraGUIDs ...uint64) []uint64 { + out := append([]uint64(nil), playerGUIDs...) + for _, playerGUID := range extraGUIDs { + if playerGUID != 0 { + out = append(out, playerGUID) + } + } + return out +} + func (c *natsConsumer) unsubscribe() error { for _, sub := range c.subs { if err := sub.Unsubscribe(); err != nil { diff --git a/game-server/libsidecar/consumer/handlers-fabric.go b/game-server/libsidecar/consumer/handlers-fabric.go index b433571..6932ef8 100644 --- a/game-server/libsidecar/consumer/handlers-fabric.go +++ b/game-server/libsidecar/consumer/handlers-fabric.go @@ -15,10 +15,19 @@ type GroupHandlersFabric interface { GroupCreated(payload *events.GroupEventGroupCreatedPayload) queue.Handler GroupMemberAdded(payload *events.GroupEventGroupMemberAddedPayload) queue.Handler GroupMemberRemoved(payload *events.GroupEventGroupMemberLeftPayload) queue.Handler + GroupLeaderChanged(payload *events.GroupEventGroupLeaderChangedPayload) queue.Handler GroupDisbanded(payload *events.GroupEventGroupDisbandPayload) queue.Handler GroupLootTypeChanged(payload *events.GroupEventGroupLootTypeChangedPayload) queue.Handler GroupDifficultyChanged(payload *events.GroupEventGroupDifficultyChangedPayload) queue.Handler GroupConvertedToRaid(payload *events.GroupEventGroupConvertedToRaidPayload) queue.Handler + GroupReadyCheckStarted(payload *events.GroupEventReadyCheckStartedPayload) queue.Handler + GroupReadyCheckMemberState(payload *events.GroupEventReadyCheckMemberStatePayload) queue.Handler + GroupReadyCheckFinished(payload *events.GroupEventReadyCheckFinishedPayload) queue.Handler + GroupMemberSubGroupChanged(payload *events.GroupEventMemberSubGroupChangedPayload) queue.Handler + GroupMemberFlagsChanged(payload *events.GroupEventMemberFlagsChangedPayload) queue.Handler + GroupMemberStateChanged(payload *events.GroupEventMemberStateChangedPayload) queue.Handler + GroupInstanceResetRequest(payload *events.GroupEventInstanceResetRequestPayload) queue.Handler + GroupInstanceBindExtensionRequest(payload *events.GroupEventInstanceBindExtensionRequestPayload) queue.Handler } type ServerRegistryHandlerFabric interface { diff --git a/game-server/libsidecar/events-group.c b/game-server/libsidecar/events-group.c index 3e43188..fd8aaa9 100644 --- a/game-server/libsidecar/events-group.c +++ b/game-server/libsidecar/events-group.c @@ -21,7 +21,7 @@ void SetOnGroupMemberAddedHook(OnGroupMemberAddedHook h) { } int CallOnGroupMemberAddedHook(uint32_t guid, uint64_t newMemberGuid) { - if (groupCreatedHook == 0) { + if (groupMemberAddedHook == 0) { return GroupHookStatusNoHook; } groupMemberAddedHook(guid, newMemberGuid); @@ -42,6 +42,18 @@ int CallOnGroupMemberRemovedHook(uint32_t guid, uint64_t removedMemberGuid, uint return GroupHookStatusOK; } +static OnGroupLeaderChangedHook groupLeaderChangedHook; +void SetOnGroupLeaderChangedHook(OnGroupLeaderChangedHook h) { + groupLeaderChangedHook = h; +} + +int CallOnGroupLeaderChangedHook(uint32_t guid, uint64_t previousLeaderGuid, uint64_t newLeaderGuid) { + if (groupLeaderChangedHook == 0) { + return GroupHookStatusNoHook; + } + groupLeaderChangedHook(guid, previousLeaderGuid, newLeaderGuid); + return GroupHookStatusOK; +} typedef void (*OnGroupDisbandedHook) (uint32_t guid); void SetOnGroupDisbandedHook(OnGroupDisbandedHook h); @@ -115,3 +127,108 @@ int CallOnGroupConvertedToRaidHook(uint32_t guid) { groupConvertedToRaid(guid); return GroupHookStatusOK; } + + +static OnGroupReadyCheckStartedHook groupReadyCheckStartedHook; +void SetOnGroupReadyCheckStartedHook(OnGroupReadyCheckStartedHook h) { + groupReadyCheckStartedHook = h; +} + +int CallOnGroupReadyCheckStartedHook(GroupReadyCheckStarted* request) { + if (groupReadyCheckStartedHook == 0) + return GroupHookStatusNoHook; + + groupReadyCheckStartedHook(request); + return GroupHookStatusOK; +} + +static OnGroupReadyCheckMemberStateHook groupReadyCheckMemberStateHook; +void SetOnGroupReadyCheckMemberStateHook(OnGroupReadyCheckMemberStateHook h) { + groupReadyCheckMemberStateHook = h; +} + +int CallOnGroupReadyCheckMemberStateHook(GroupReadyCheckMemberState* request) { + if (groupReadyCheckMemberStateHook == 0) + return GroupHookStatusNoHook; + + groupReadyCheckMemberStateHook(request); + return GroupHookStatusOK; +} + +static OnGroupReadyCheckFinishedHook groupReadyCheckFinishedHook; +void SetOnGroupReadyCheckFinishedHook(OnGroupReadyCheckFinishedHook h) { + groupReadyCheckFinishedHook = h; +} + +int CallOnGroupReadyCheckFinishedHook(GroupReadyCheckFinished* request) { + if (groupReadyCheckFinishedHook == 0) + return GroupHookStatusNoHook; + + groupReadyCheckFinishedHook(request); + return GroupHookStatusOK; +} + +static OnGroupMemberSubGroupChangedHook groupMemberSubGroupChangedHook; +void SetOnGroupMemberSubGroupChangedHook(OnGroupMemberSubGroupChangedHook h) { + groupMemberSubGroupChangedHook = h; +} + +int CallOnGroupMemberSubGroupChangedHook(GroupMemberSubGroupChanged* request) { + if (groupMemberSubGroupChangedHook == 0) + return GroupHookStatusNoHook; + + groupMemberSubGroupChangedHook(request); + return GroupHookStatusOK; +} + +static OnGroupMemberFlagsChangedHook groupMemberFlagsChangedHook; +void SetOnGroupMemberFlagsChangedHook(OnGroupMemberFlagsChangedHook h) { + groupMemberFlagsChangedHook = h; +} + +int CallOnGroupMemberFlagsChangedHook(GroupMemberFlagsChanged* request) { + if (groupMemberFlagsChangedHook == 0) + return GroupHookStatusNoHook; + + groupMemberFlagsChangedHook(request); + return GroupHookStatusOK; +} + +static OnGroupMemberStateChangedHook groupMemberStateChangedHook; +void SetOnGroupMemberStateChangedHook(OnGroupMemberStateChangedHook h) { + groupMemberStateChangedHook = h; +} + +int CallOnGroupMemberStateChangedHook(GroupMemberStateChanged* request) { + if (groupMemberStateChangedHook == 0) + return GroupHookStatusNoHook; + + groupMemberStateChangedHook(request); + return GroupHookStatusOK; +} + +static OnGroupInstanceResetRequestHook groupInstanceResetRequestHook; +void SetOnGroupInstanceResetRequestHook(OnGroupInstanceResetRequestHook h) { + groupInstanceResetRequestHook = h; +} + +int CallOnGroupInstanceResetRequestHook(GroupInstanceResetRequest* request) { + if (groupInstanceResetRequestHook == 0) + return GroupHookStatusNoHook; + + groupInstanceResetRequestHook(request); + return GroupHookStatusOK; +} + +static OnGroupInstanceBindExtensionRequestHook groupInstanceBindExtensionRequestHook; +void SetOnGroupInstanceBindExtensionRequestHook(OnGroupInstanceBindExtensionRequestHook h) { + groupInstanceBindExtensionRequestHook = h; +} + +int CallOnGroupInstanceBindExtensionRequestHook(GroupInstanceBindExtensionRequest* request) { + if (groupInstanceBindExtensionRequestHook == 0) + return GroupHookStatusNoHook; + + groupInstanceBindExtensionRequestHook(request); + return GroupHookStatusOK; +} diff --git a/game-server/libsidecar/events-group.go b/game-server/libsidecar/events-group.go index 16f9730..628cb25 100644 --- a/game-server/libsidecar/events-group.go +++ b/game-server/libsidecar/events-group.go @@ -6,14 +6,24 @@ package main import "C" import ( + "context" "fmt" + zlog "github.com/rs/zerolog/log" + "time" "unsafe" + charserver "github.com/walkline/ToCloud9/apps/charserver" + groupserver "github.com/walkline/ToCloud9/apps/groupserver" + charPb "github.com/walkline/ToCloud9/gen/characters/pb" + groupPb "github.com/walkline/ToCloud9/gen/group/pb" + "github.com/rs/zerolog" "github.com/walkline/ToCloud9/game-server/libsidecar/consumer" "github.com/walkline/ToCloud9/game-server/libsidecar/queue" "github.com/walkline/ToCloud9/shared/events" + "github.com/walkline/ToCloud9/shared/groupstatetrace" + wowguid "github.com/walkline/ToCloud9/shared/wow/guid" ) // TC9SetOnGroupCreatedHook sets hook for group created event. @@ -37,6 +47,13 @@ func TC9SetOnGroupMemberRemovedHook(h C.OnGroupMemberRemovedHook) { C.SetOnGroupMemberRemovedHook(h) } +// TC9SetOnGroupLeaderChangedHook sets hook for leader changed event. +// +//export TC9SetOnGroupLeaderChangedHook +func TC9SetOnGroupLeaderChangedHook(h C.OnGroupLeaderChangedHook) { + C.SetOnGroupLeaderChangedHook(h) +} + // TC9SetOnGroupDisbandedHook sets hook for group disbanded event. // //export TC9SetOnGroupDisbandedHook @@ -72,10 +89,162 @@ func TC9SetOnGroupConvertedToRaidHook(h C.OnGroupConvertedToRaidHook) { C.SetOnGroupConvertedToRaidHook(h) } +//export TC9SetOnGroupReadyCheckStartedHook +func TC9SetOnGroupReadyCheckStartedHook(h C.OnGroupReadyCheckStartedHook) { + C.SetOnGroupReadyCheckStartedHook(h) +} + +//export TC9SetOnGroupReadyCheckMemberStateHook +func TC9SetOnGroupReadyCheckMemberStateHook(h C.OnGroupReadyCheckMemberStateHook) { + C.SetOnGroupReadyCheckMemberStateHook(h) +} + +//export TC9SetOnGroupReadyCheckFinishedHook +func TC9SetOnGroupReadyCheckFinishedHook(h C.OnGroupReadyCheckFinishedHook) { + C.SetOnGroupReadyCheckFinishedHook(h) +} + +//export TC9SetOnGroupMemberSubGroupChangedHook +func TC9SetOnGroupMemberSubGroupChangedHook(h C.OnGroupMemberSubGroupChangedHook) { + C.SetOnGroupMemberSubGroupChangedHook(h) +} + +//export TC9SetOnGroupMemberFlagsChangedHook +func TC9SetOnGroupMemberFlagsChangedHook(h C.OnGroupMemberFlagsChangedHook) { + C.SetOnGroupMemberFlagsChangedHook(h) +} + +//export TC9SetOnGroupMemberStateChangedHook +func TC9SetOnGroupMemberStateChangedHook(h C.OnGroupMemberStateChangedHook) { + C.SetOnGroupMemberStateChangedHook(h) +} + +//export TC9SetOnGroupInstanceResetRequestHook +func TC9SetOnGroupInstanceResetRequestHook(h C.OnGroupInstanceResetRequestHook) { + C.SetOnGroupInstanceResetRequestHook(h) +} + +//export TC9SetOnGroupInstanceBindExtensionRequestHook +func TC9SetOnGroupInstanceBindExtensionRequestHook(h C.OnGroupInstanceBindExtensionRequestHook) { + C.SetOnGroupInstanceBindExtensionRequestHook(h) +} + type groupHandlerFabric struct { logger zerolog.Logger } +func playerObjectGUIDForRealm(realmID uint32, guid uint64) C.uint64_t { + // Libsidecar sends AzerothCore-facing player ObjectGuid values. Local + // members are low DB GUIDs; foreign members are realm-scoped player GUIDs. + playerRealmID := wowguid.PlayerRealmIDOrDefault(realmID, guid) + return C.uint64_t(wowguid.PlayerGUIDForRealm(RealmID, playerRealmID, guid)) +} + +func playerDBGUIDFromObjectGUID(guid C.uint64_t) uint64 { + raw := uint64(guid) + if raw == 0 || raw>>32 == 0 || raw>>48 != 0 { + return raw + } + + if uint32((raw>>32)&0xffff) != RealmID { + return raw + } + + return raw & 0xffffffff +} + +func playerServiceKeyFromObjectGUID(defaultRealmID uint32, guid uint64) (uint32, uint64) { + raw := guid + return wowguid.PlayerRealmIDOrDefault(defaultRealmID, raw), wowguid.PlayerLowGUID(raw) +} + +//export TC9RegisterMaterializedLfgGroup +func TC9RegisterMaterializedLfgGroup( + groupGuid C.uint32_t, + leaderGuid C.uint64_t, + groupType C.uint8_t, + difficulty C.uint8_t, + raidDifficulty C.uint8_t, + members *C.GroupMaterializedLfgMember, + membersSize C.uint8_t, +) { + if groupServiceClient == nil || groupGuid == 0 || leaderGuid == 0 || members == nil || membersSize == 0 { + return + } + + groupRealmID, leaderLowGUID := playerServiceKeyFromObjectGUID(RealmID, uint64(leaderGuid)) + if groupRealmID == 0 { + zlog.Error(). + Uint32("realmID", groupRealmID). + Uint64("leaderRaw", uint64(leaderGuid)). + Uint32("groupID", uint32(groupGuid)). + Msg("failed to register materialized LFG group with unknown group realm") + return + } + + cMembers := unsafe.Slice(members, int(membersSize)) + requestMembers := make([]*groupPb.MaterializedLfgGroupMember, 0, len(cMembers)) + for _, member := range cMembers { + if member.memberGuid == 0 { + continue + } + + memberRealmID, memberLowGUID := playerServiceKeyFromObjectGUID(groupRealmID, uint64(member.memberGuid)) + memberName := "" + if member.memberName != nil { + memberName = C.GoString(member.memberName) + } + + requestMembers = append(requestMembers, &groupPb.MaterializedLfgGroupMember{ + RealmID: memberRealmID, + PlayerGUID: memberLowGUID, + Name: memberName, + IsOnline: member.online != 0, + Flags: uint32(member.flags), + Roles: uint32(member.roles), + SubGroup: uint32(member.subGroup), + }) + } + + if len(requestMembers) == 0 { + return + } + + if event := groupstatetrace.Event(nil, "libsidecar.register_materialized_lfg_group.rpc", leaderLowGUID); event != nil { + event. + Uint32("realmID", groupRealmID). + Uint32("groupID", uint32(groupGuid)). + Uint64("leaderGUID", leaderLowGUID). + Uint64("leaderRaw", uint64(leaderGuid)). + Uint8("groupType", uint8(groupType)). + Int("memberCount", len(requestMembers)). + Msg(groupstatetrace.Message) + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + _, err := groupServiceClient.RegisterMaterializedLfgGroup(ctx, &groupPb.RegisterMaterializedLfgGroupRequest{ + Api: groupserver.Ver, + RealmID: groupRealmID, + GroupID: uint32(groupGuid), + LeaderGUID: leaderLowGUID, + GroupType: uint32(groupType), + Difficulty: uint32(difficulty), + RaidDifficulty: uint32(raidDifficulty), + Members: requestMembers, + }) + if err != nil { + zlog.Error(). + Err(err). + Uint32("realmID", groupRealmID). + Uint32("groupID", uint32(groupGuid)). + Uint64("leaderGUID", leaderLowGUID). + Int("memberCount", len(requestMembers)). + Msg("failed to register materialized LFG group") + } +} + func NewGroupHandlerFabric(logger zerolog.Logger) consumer.GroupHandlersFabric { return &groupHandlerFabric{ logger: logger, @@ -84,47 +253,95 @@ func NewGroupHandlerFabric(logger zerolog.Logger) consumer.GroupHandlersFabric { func (g groupHandlerFabric) GroupCreated(payload *events.GroupEventGroupCreatedPayload) queue.Handler { return eventsHandlerFunc(func() { - cMemberGUIDs := C.malloc(C.size_t(len(payload.Members)) * C.size_t(unsafe.Sizeof(C.uint64_t(0)))) - defer C.free(cMemberGUIDs) // Make sure to free the memory when done - - cMemberGUIDsPtr := (*C.uint64_t)(cMemberGUIDs) - for i, guid := range payload.Members { - cMemberGUIDsPtr = (*C.uint64_t)(unsafe.Pointer(uintptr(unsafe.Pointer(cMemberGUIDsPtr)) + uintptr(i)*unsafe.Sizeof(C.uint64_t(0)))) - *cMemberGUIDsPtr = C.uint64_t(guid.MemberGUID) - } + cMemberGUIDs, cMemberNames, membersSize, releaseMembers := groupMembersToC(payload.RealmID, payload.Members) + defer releaseMembers() var group C.EventObjectGroup group.guid = C.uint32_t(payload.GroupID) - group.leader = C.uint64_t(payload.LeaderGUID) + group.leader = playerObjectGUIDForRealm(payload.RealmID, payload.LeaderGUID) group.lootMethod = C.uint8_t(payload.LootMethod) - group.looterGuid = C.uint64_t(payload.LooterGUID) + group.looterGuid = playerObjectGUIDForRealm(payload.RealmID, payload.LooterGUID) group.lootThreshold = C.uint8_t(payload.LootThreshold) group.groupType = C.uint8_t(payload.GroupType) group.difficulty = C.uint8_t(payload.Difficulty) group.raidDifficulty = C.uint8_t(payload.RaidDifficulty) - group.masterLooterGuid = C.uint64_t(payload.MasterLooterGuid) - group.members = (*C.uint64_t)(cMemberGUIDs) - group.membersSize = C.uint8_t(len(payload.Members)) + group.masterLooterGuid = playerObjectGUIDForRealm(payload.RealmID, payload.MasterLooterGuid) + group.members = cMemberGUIDs + group.memberNames = cMemberNames + group.membersSize = membersSize + group.lfgDungeonEntry = C.uint32_t(payload.LfgDungeonEntry) r := C.CallOnGroupCreatedHook(&group) g.handleResponse(int(r), "GroupCreated") }) } +func groupMembersToC(realmID uint32, members []events.GroupMember) (*C.uint64_t, **C.char, C.uint8_t, func()) { + release := func() {} + if len(members) == 0 { + return nil, nil, 0, release + } + + guidPtr := C.malloc(C.size_t(len(members)) * C.size_t(unsafe.Sizeof(C.uint64_t(0)))) + if guidPtr == nil { + return nil, nil, 0, release + } + + namePtr := C.malloc(C.size_t(len(members)) * C.size_t(unsafe.Sizeof(uintptr(0)))) + if namePtr == nil { + C.free(guidPtr) + return nil, nil, 0, release + } + + cMemberGUIDs := unsafe.Slice((*C.uint64_t)(guidPtr), len(members)) + cMemberNames := unsafe.Slice((**C.char)(namePtr), len(members)) + for i, member := range members { + cMemberGUIDs[i] = playerObjectGUIDForRealm(realmID, member.MemberGUID) + cMemberNames[i] = C.CString(member.MemberName) + } + + release = func() { + for _, name := range cMemberNames { + if name != nil { + C.free(unsafe.Pointer(name)) + } + } + C.free(namePtr) + C.free(guidPtr) + } + + return (*C.uint64_t)(guidPtr), (**C.char)(namePtr), C.uint8_t(len(members)), release +} + func (g groupHandlerFabric) GroupMemberAdded(payload *events.GroupEventGroupMemberAddedPayload) queue.Handler { return eventsHandlerFunc(func() { - r := C.CallOnGroupMemberAddedHook(C.uint32_t(payload.GroupID), C.uint64_t(payload.MemberGUID)) + r := C.CallOnGroupMemberAddedHook(C.uint32_t(payload.GroupID), playerObjectGUIDForRealm(payload.RealmID, payload.MemberGUID)) g.handleResponse(int(r), "GroupMemberAdded") }) } func (g groupHandlerFabric) GroupMemberRemoved(payload *events.GroupEventGroupMemberLeftPayload) queue.Handler { return eventsHandlerFunc(func() { - r := C.CallOnGroupMemberRemovedHook(C.uint32_t(payload.GroupID), C.uint64_t(payload.MemberGUID), C.uint64_t(payload.NewLeaderID)) + r := C.CallOnGroupMemberRemovedHook( + C.uint32_t(payload.GroupID), + playerObjectGUIDForRealm(payload.RealmID, payload.MemberGUID), + playerObjectGUIDForRealm(payload.RealmID, payload.NewLeaderID), + ) g.handleResponse(int(r), "GroupMemberRemoved") }) } +func (g groupHandlerFabric) GroupLeaderChanged(payload *events.GroupEventGroupLeaderChangedPayload) queue.Handler { + return eventsHandlerFunc(func() { + r := C.CallOnGroupLeaderChangedHook( + C.uint32_t(payload.GroupID), + playerObjectGUIDForRealm(payload.RealmID, payload.PreviousLeader), + playerObjectGUIDForRealm(payload.RealmID, payload.NewLeader), + ) + g.handleResponse(int(r), "GroupLeaderChanged") + }) +} + func (g groupHandlerFabric) GroupDisbanded(payload *events.GroupEventGroupDisbandPayload) queue.Handler { return eventsHandlerFunc(func() { r := C.CallOnGroupDisbandedHook(C.uint32_t(payload.GroupID)) @@ -132,12 +349,269 @@ func (g groupHandlerFabric) GroupDisbanded(payload *events.GroupEventGroupDisban }) } +//export TC9UpdateGroupMemberState +func TC9UpdateGroupMemberState( + memberGuid C.uint64_t, + online C.uint8_t, + level C.uint8_t, + playerClass C.uint8_t, + zoneId C.uint32_t, + mapId C.uint32_t, + health C.uint32_t, + maxHealth C.uint32_t, + powerType C.uint8_t, + power C.uint32_t, + maxPower C.uint32_t, + instanceID C.uint32_t, +) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + memberRealmID, memberDBGuid := playerServiceKeyFromObjectGUID(RealmID, uint64(memberGuid)) + instanceIDValue := uint32(instanceID) + if event := groupstatetrace.Event(nil, "libsidecar.update_member_state.rpc", memberDBGuid); event != nil { + event. + Uint32("realmID", memberRealmID). + Uint64("memberGUID", memberDBGuid). + Uint64("memberRaw", uint64(memberGuid)). + Str("sourceWorldserverID", AssignedGameServerID). + Bool("online", online != 0). + Uint8("level", uint8(level)). + Uint8("class", uint8(playerClass)). + Uint32("zoneID", uint32(zoneId)). + Uint32("mapID", uint32(mapId)). + Uint32("health", uint32(health)). + Uint32("maxHealth", uint32(maxHealth)). + Uint8("powerType", uint8(powerType)). + Uint32("power", uint32(power)). + Uint32("maxPower", uint32(maxPower)). + Uint32("instanceID", uint32(instanceID)). + Msg(groupstatetrace.Message) + } + + var instanceIDPtr *uint32 + if instanceIDValue != 0 { + instanceIDPtr = &instanceIDValue + } + + _, err := groupServiceClient.BulkUpdateMemberStates(ctx, &groupPb.BulkUpdateMemberStatesRequest{ + Api: groupserver.Ver, + RealmID: memberRealmID, + SourceWorldserverID: AssignedGameServerID, + Snapshots: []*groupPb.PlayerStateSnapshot{ + { + MemberGUID: memberDBGuid, + Online: online != 0, + Level: uint32(level), + ClassID: uint32(playerClass), + ZoneID: uint32(zoneId), + MapID: uint32(mapId), + Health: uint32(health), + MaxHealth: uint32(maxHealth), + PowerType: uint32(powerType), + Power: uint32(power), + MaxPower: uint32(maxPower), + InstanceID: instanceIDPtr, + TimestampMs: uint64(time.Now().UnixMilli()), + }, + }, + }) + if err != nil { + zlog.Error(). + Err(err). + Uint32("realmID", memberRealmID). + Uint64("member", memberDBGuid). + Uint64("memberRaw", uint64(memberGuid)). + Bool("online", online != 0). + Uint8("level", uint8(level)). + Uint8("class", uint8(playerClass)). + Uint32("zone", uint32(zoneId)). + Uint32("health", uint32(health)). + Uint32("maxHealth", uint32(maxHealth)). + Uint8("powerType", uint8(powerType)). + Uint32("power", uint32(power)). + Uint32("maxPower", uint32(maxPower)). + Uint32("instanceID", uint32(instanceID)). + Msg("failed to update group member state") + } +} + +//export TC9StartGroupReadyCheck +func TC9StartGroupReadyCheck(leaderGuid C.uint64_t, durationMs C.uint32_t) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + leaderDBGuid := playerDBGUIDFromObjectGUID(leaderGuid) + + _, err := groupServiceClient.StartReadyCheck(ctx, &groupPb.StartReadyCheckRequest{ + Api: groupserver.Ver, + RealmID: RealmID, + LeaderGUID: leaderDBGuid, + DurationMs: uint32(durationMs), + }) + if err != nil { + zlog.Error(). + Err(err). + Uint64("leader", leaderDBGuid). + Uint64("leaderRaw", uint64(leaderGuid)). + Uint32("durationMs", uint32(durationMs)). + Msg("failed to start group ready check") + } +} + +//export TC9ConfirmLfgDungeonRouteEntered +func TC9ConfirmLfgDungeonRouteEntered( + memberGuid C.uint64_t, + mapId C.uint32_t, + difficulty C.uint8_t, + instanceID C.uint32_t, +) { + if characterServiceClient == nil || mapId == 0 || instanceID == 0 { + return + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + memberRealmID, memberDBGuid := playerServiceKeyFromObjectGUID(RealmID, uint64(memberGuid)) + if event := groupstatetrace.Event(nil, "libsidecar.confirm_lfg_dungeon_route.rpc", memberDBGuid); event != nil { + event. + Uint32("realmID", memberRealmID). + Uint64("memberGUID", memberDBGuid). + Uint64("memberRaw", uint64(memberGuid)). + Uint32("mapID", uint32(mapId)). + Uint8("difficulty", uint8(difficulty)). + Uint32("instanceID", uint32(instanceID)). + Msg(groupstatetrace.Message) + } + + _, err := characterServiceClient.ConfirmLfgDungeonRouteEntered(ctx, &charPb.ConfirmLfgDungeonRouteEnteredRequest{ + Api: charserver.Ver, + RealmID: memberRealmID, + PlayerGUID: memberDBGuid, + MapID: uint32(mapId), + Difficulty: uint32(difficulty), + InstanceID: uint32(instanceID), + }) + if err != nil { + zlog.Error(). + Err(err). + Uint32("realmID", memberRealmID). + Uint64("member", memberDBGuid). + Uint64("memberRaw", uint64(memberGuid)). + Uint32("map", uint32(mapId)). + Uint8("difficulty", uint8(difficulty)). + Uint32("instanceID", uint32(instanceID)). + Msg("can't confirm LFG dungeon route") + } +} + +//export TC9SetGroupReadyCheckMemberState +func TC9SetGroupReadyCheckMemberState(memberGuid C.uint64_t, state C.uint8_t) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + memberDBGuid := playerDBGUIDFromObjectGUID(memberGuid) + + _, err := groupServiceClient.SetReadyCheckMemberState(ctx, &groupPb.SetReadyCheckMemberStateRequest{ + Api: groupserver.Ver, + RealmID: RealmID, + MemberGUID: memberDBGuid, + State: uint32(state), + }) + if err != nil { + zlog.Error(). + Err(err). + Uint64("member", memberDBGuid). + Uint64("memberRaw", uint64(memberGuid)). + Uint8("state", uint8(state)). + Msg("failed to set group ready check member state") + } +} + +//export TC9FinishGroupReadyCheck +func TC9FinishGroupReadyCheck(playerGuid C.uint64_t) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + playerDBGuid := playerDBGUIDFromObjectGUID(playerGuid) + + _, err := groupServiceClient.FinishReadyCheck(ctx, &groupPb.FinishReadyCheckRequest{ + Api: groupserver.Ver, + RealmID: RealmID, + PlayerGUID: playerDBGuid, + }) + if err != nil { + zlog.Error(). + Err(err). + Uint64("player", playerDBGuid). + Uint64("playerRaw", uint64(playerGuid)). + Msg("failed to finish group ready check") + } +} + +//export TC9ChangeGroupMemberSubGroup +func TC9ChangeGroupMemberSubGroup(updaterGuid C.uint64_t, memberGuid C.uint64_t, subGroup C.uint8_t) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + updaterDBGuid := playerDBGUIDFromObjectGUID(updaterGuid) + memberDBGuid := playerDBGUIDFromObjectGUID(memberGuid) + + _, err := groupServiceClient.ChangeMemberSubGroup(ctx, &groupPb.ChangeMemberSubGroupRequest{ + Api: groupserver.Ver, + RealmID: RealmID, + UpdaterGUID: updaterDBGuid, + MemberGUID: memberDBGuid, + SubGroup: uint32(subGroup), + }) + if err != nil { + zlog.Error(). + Err(err). + Uint64("updater", updaterDBGuid). + Uint64("updaterRaw", uint64(updaterGuid)). + Uint64("member", memberDBGuid). + Uint64("memberRaw", uint64(memberGuid)). + Uint8("subGroup", uint8(subGroup)). + Msg("failed to change group member subgroup") + } +} + +//export TC9SetGroupMemberFlags +func TC9SetGroupMemberFlags(updaterGuid C.uint64_t, memberGuid C.uint64_t, flags C.uint8_t, roles C.uint8_t) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + updaterDBGuid := playerDBGUIDFromObjectGUID(updaterGuid) + memberDBGuid := playerDBGUIDFromObjectGUID(memberGuid) + + _, err := groupServiceClient.SetMemberFlags(ctx, &groupPb.SetMemberFlagsRequest{ + Api: groupserver.Ver, + RealmID: RealmID, + UpdaterGUID: updaterDBGuid, + MemberGUID: memberDBGuid, + Flags: uint32(flags), + Roles: uint32(roles), + }) + if err != nil { + zlog.Error(). + Err(err). + Uint64("updater", updaterDBGuid). + Uint64("updaterRaw", uint64(updaterGuid)). + Uint64("member", memberDBGuid). + Uint64("memberRaw", uint64(memberGuid)). + Uint8("flags", uint8(flags)). + Uint8("roles", uint8(roles)). + Msg("failed to set group member flags") + } +} + func (g groupHandlerFabric) GroupLootTypeChanged(payload *events.GroupEventGroupLootTypeChangedPayload) queue.Handler { return eventsHandlerFunc(func() { r := C.CallOnGroupLootTypeChangedHook( C.uint32_t(payload.GroupID), C.uint8_t(payload.NewLootType), - C.uint64_t(payload.NewLooterGUID), + playerObjectGUIDForRealm(payload.RealmID, payload.NewLooterGUID), C.uint8_t(payload.NewLooterThreshold), ) g.handleResponse(int(r), "LootTypeChanged") @@ -187,3 +661,188 @@ func (g groupHandlerFabric) handleResponse(resp int, hookName string) { g.logger.Error().Str("hook", hookName).Msg("unk status") } } + +func (g groupHandlerFabric) GroupReadyCheckStarted(payload *events.GroupEventReadyCheckStartedPayload) queue.Handler { + return eventsHandlerFunc(func() { + req := C.GroupReadyCheckStarted{ + groupGuid: C.uint32_t(payload.GroupID), + leaderGuid: playerObjectGUIDForRealm(payload.RealmID, payload.LeaderGUID), + durationMs: C.uint32_t(payload.DurationMs), + } + + r := C.CallOnGroupReadyCheckStartedHook(&req) + g.handleResponse(int(r), "GroupReadyCheckStarted") + }) +} + +func (g groupHandlerFabric) GroupReadyCheckMemberState(payload *events.GroupEventReadyCheckMemberStatePayload) queue.Handler { + return eventsHandlerFunc(func() { + req := C.GroupReadyCheckMemberState{ + groupGuid: C.uint32_t(payload.GroupID), + memberGuid: playerObjectGUIDForRealm(payload.RealmID, payload.MemberGUID), + state: C.uint8_t(payload.State), + } + + r := C.CallOnGroupReadyCheckMemberStateHook(&req) + g.handleResponse(int(r), "GroupReadyCheckMemberState") + }) +} + +func (g groupHandlerFabric) GroupReadyCheckFinished(payload *events.GroupEventReadyCheckFinishedPayload) queue.Handler { + return eventsHandlerFunc(func() { + req := C.GroupReadyCheckFinished{ + groupGuid: C.uint32_t(payload.GroupID), + } + + r := C.CallOnGroupReadyCheckFinishedHook(&req) + g.handleResponse(int(r), "GroupReadyCheckFinished") + }) +} + +func (g groupHandlerFabric) GroupMemberSubGroupChanged(payload *events.GroupEventMemberSubGroupChangedPayload) queue.Handler { + return eventsHandlerFunc(func() { + req := C.GroupMemberSubGroupChanged{ + groupGuid: C.uint32_t(payload.GroupID), + memberGuid: playerObjectGUIDForRealm(payload.RealmID, payload.MemberGUID), + subGroup: C.uint8_t(payload.SubGroup), + } + + r := C.CallOnGroupMemberSubGroupChangedHook(&req) + g.handleResponse(int(r), "GroupMemberSubGroupChanged") + }) +} + +func (g groupHandlerFabric) GroupMemberFlagsChanged(payload *events.GroupEventMemberFlagsChangedPayload) queue.Handler { + return eventsHandlerFunc(func() { + req := C.GroupMemberFlagsChanged{ + groupGuid: C.uint32_t(payload.GroupID), + memberGuid: playerObjectGUIDForRealm(payload.RealmID, payload.MemberGUID), + flags: C.uint8_t(payload.Flags), + roles: C.uint8_t(payload.Roles), + } + + r := C.CallOnGroupMemberFlagsChangedHook(&req) + g.handleResponse(int(r), "GroupMemberFlagsChanged") + }) +} + +func (g groupHandlerFabric) GroupMemberStateChanged(payload *events.GroupEventMemberStateChangedPayload) queue.Handler { + return eventsHandlerFunc(func() { + if event := groupstatetrace.Event(nil, "libsidecar.group_member_state.hook", payload.MemberGUID); event != nil { + event. + Uint32("realmID", payload.RealmID). + Uint("groupID", payload.GroupID). + Uint64("memberGUID", payload.MemberGUID). + Str("sourceGatewayID", payload.SourceGatewayID). + Str("sourceWorldserverID", payload.SourceWorldserverID). + Bool("online", payload.Online). + Uint8("level", payload.Level). + Uint8("class", payload.Class). + Uint32("zoneID", payload.ZoneID). + Uint32("mapID", payload.MapID). + Uint32("health", payload.Health). + Uint32("maxHealth", payload.MaxHealth). + Uint8("powerType", payload.PowerType). + Uint32("power", payload.Power). + Uint32("maxPower", payload.MaxPower). + Bool("aurasKnown", payload.AurasKnown). + Int("auraCount", len(payload.Auras)). + Msg(groupstatetrace.Message) + } + + auras, auraCount, releaseAuras := groupMemberAurasToC(payload.AurasKnown, payload.Auras) + defer releaseAuras() + + req := C.GroupMemberStateChanged{ + groupGuid: C.uint32_t(payload.GroupID), + memberGuid: playerObjectGUIDForRealm(payload.RealmID, payload.MemberGUID), + online: C.uint8_t(boolToUint8(payload.Online)), + level: C.uint8_t(payload.Level), + playerClass: C.uint8_t(payload.Class), + zoneId: C.uint32_t(payload.ZoneID), + mapId: C.uint32_t(payload.MapID), + health: C.uint32_t(payload.Health), + maxHealth: C.uint32_t(payload.MaxHealth), + powerType: C.uint8_t(payload.PowerType), + power: C.uint32_t(payload.Power), + maxPower: C.uint32_t(payload.MaxPower), + aurasKnown: C.uint8_t(boolToUint8(payload.AurasKnown)), + auraCount: auraCount, + auras: auras, + } + + r := C.CallOnGroupMemberStateChangedHook(&req) + g.handleResponse(int(r), "GroupMemberStateChanged") + }) +} + +func groupMemberAurasToC(known bool, payloadAuras []events.GroupMemberAuraState) (*C.GroupMemberAuraState, C.uint32_t, func()) { + if !known || len(payloadAuras) == 0 { + return nil, 0, func() {} + } + + ptr := C.malloc(C.size_t(len(payloadAuras)) * C.size_t(unsafe.Sizeof(C.GroupMemberAuraState{}))) + if ptr == nil { + return nil, 0, func() {} + } + + auras := unsafe.Slice((*C.GroupMemberAuraState)(ptr), len(payloadAuras)) + count := 0 + for _, aura := range payloadAuras { + if aura.Slot >= 64 || aura.SpellID == 0 { + continue + } + auras[count] = C.GroupMemberAuraState{ + slot: C.uint8_t(aura.Slot), + spellId: C.uint32_t(aura.SpellID), + flags: C.uint8_t(aura.Flags), + } + count++ + } + + if count == 0 { + C.free(ptr) + return nil, 0, func() {} + } + + return (*C.GroupMemberAuraState)(ptr), C.uint32_t(count), func() { + C.free(ptr) + } +} + +func (g groupHandlerFabric) GroupInstanceResetRequest(payload *events.GroupEventInstanceResetRequestPayload) queue.Handler { + return eventsHandlerFunc(func() { + req := C.GroupInstanceResetRequest{ + groupGuid: C.uint32_t(payload.GroupID), + playerGuid: playerObjectGUIDForRealm(payload.RealmID, payload.PlayerGUID), + mapId: C.uint32_t(payload.MapID), + difficulty: C.uint8_t(payload.Difficulty), + } + + r := C.CallOnGroupInstanceResetRequestHook(&req) + g.handleResponse(int(r), "GroupInstanceResetRequest") + }) +} + +func (g groupHandlerFabric) GroupInstanceBindExtensionRequest(payload *events.GroupEventInstanceBindExtensionRequestPayload) queue.Handler { + return eventsHandlerFunc(func() { + req := C.GroupInstanceBindExtensionRequest{ + groupGuid: C.uint32_t(payload.GroupID), + playerGuid: playerObjectGUIDForRealm(payload.RealmID, payload.PlayerGUID), + mapId: C.uint32_t(payload.MapID), + difficulty: C.uint8_t(payload.Difficulty), + extended: C.uint8_t(boolToUint8(payload.Extended)), + } + + r := C.CallOnGroupInstanceBindExtensionRequestHook(&req) + g.handleResponse(int(r), "GroupInstanceBindExtensionRequest") + }) +} + +func boolToUint8(v bool) uint8 { + if v { + return 1 + } + + return 0 +} diff --git a/game-server/libsidecar/events-group.h b/game-server/libsidecar/events-group.h index 05b13b3..f09019c 100644 --- a/game-server/libsidecar/events-group.h +++ b/game-server/libsidecar/events-group.h @@ -20,7 +20,9 @@ typedef struct { uint8_t raidDifficulty; uint64_t masterLooterGuid; uint64_t *members; + char **memberNames; uint8_t membersSize; + uint32_t lfgDungeonEntry; } EventObjectGroup; typedef void (*OnGroupCreatedHook) (EventObjectGroup *group); @@ -35,6 +37,10 @@ typedef void (*OnGroupMemberRemovedHook) (uint32_t guid, uint64_t removedMemberG void SetOnGroupMemberRemovedHook(OnGroupMemberRemovedHook h); int CallOnGroupMemberRemovedHook(uint32_t guid, uint64_t removedMemberGuid, uint64_t newLeaderGuid); +typedef void (*OnGroupLeaderChangedHook) (uint32_t guid, uint64_t previousLeaderGuid, uint64_t newLeaderGuid); +void SetOnGroupLeaderChangedHook(OnGroupLeaderChangedHook h); +int CallOnGroupLeaderChangedHook(uint32_t guid, uint64_t previousLeaderGuid, uint64_t newLeaderGuid); + typedef void (*OnGroupDisbandedHook) (uint32_t guid); void SetOnGroupDisbandedHook(OnGroupDisbandedHook h); int CallOnGroupDisbandedHook(uint32_t guid); @@ -55,4 +61,114 @@ typedef void (*OnGroupConvertedToRaidHook) (uint32_t guid); void SetOnGroupConvertedToRaidHook(OnGroupConvertedToRaidHook h); int CallOnGroupConvertedToRaidHook(uint32_t guid); + +typedef struct { + uint32_t groupGuid; + uint64_t leaderGuid; + uint32_t durationMs; +} GroupReadyCheckStarted; + +typedef struct { + uint32_t groupGuid; + uint64_t memberGuid; + uint8_t state; // 0 = waiting, 1 = ready, 2 = not ready +} GroupReadyCheckMemberState; + +typedef struct { + uint32_t groupGuid; +} GroupReadyCheckFinished; + +typedef struct { + uint32_t groupGuid; + uint64_t memberGuid; + uint8_t subGroup; +} GroupMemberSubGroupChanged; + +typedef struct { + uint32_t groupGuid; + uint64_t memberGuid; + uint8_t flags; + uint8_t roles; +} GroupMemberFlagsChanged; + +typedef struct { + uint8_t slot; + uint32_t spellId; + uint8_t flags; +} GroupMemberAuraState; + +typedef struct { + uint32_t groupGuid; + uint64_t memberGuid; + uint8_t online; + uint8_t level; + uint8_t playerClass; + uint32_t zoneId; + uint32_t mapId; + uint32_t health; + uint32_t maxHealth; + uint8_t powerType; + uint32_t power; + uint32_t maxPower; + uint8_t aurasKnown; + uint32_t auraCount; + GroupMemberAuraState* auras; +} GroupMemberStateChanged; + +typedef struct { + uint32_t groupGuid; + uint64_t playerGuid; + uint32_t mapId; + uint8_t difficulty; +} GroupInstanceResetRequest; + +typedef struct { + uint32_t groupGuid; + uint64_t playerGuid; + uint32_t mapId; + uint8_t difficulty; + uint8_t extended; +} GroupInstanceBindExtensionRequest; + +typedef struct { + uint64_t memberGuid; + const char* memberName; + uint8_t online; + uint8_t flags; + uint8_t roles; + uint8_t subGroup; +} GroupMaterializedLfgMember; + +typedef void (*OnGroupReadyCheckStartedHook)(GroupReadyCheckStarted* request); +void SetOnGroupReadyCheckStartedHook(OnGroupReadyCheckStartedHook h); +int CallOnGroupReadyCheckStartedHook(GroupReadyCheckStarted* request); + +typedef void (*OnGroupReadyCheckMemberStateHook)(GroupReadyCheckMemberState* request); +void SetOnGroupReadyCheckMemberStateHook(OnGroupReadyCheckMemberStateHook h); +int CallOnGroupReadyCheckMemberStateHook(GroupReadyCheckMemberState* request); + +typedef void (*OnGroupReadyCheckFinishedHook)(GroupReadyCheckFinished* request); +void SetOnGroupReadyCheckFinishedHook(OnGroupReadyCheckFinishedHook h); +int CallOnGroupReadyCheckFinishedHook(GroupReadyCheckFinished* request); + +typedef void (*OnGroupMemberSubGroupChangedHook)(GroupMemberSubGroupChanged* request); +void SetOnGroupMemberSubGroupChangedHook(OnGroupMemberSubGroupChangedHook h); +int CallOnGroupMemberSubGroupChangedHook(GroupMemberSubGroupChanged* request); + +typedef void (*OnGroupMemberFlagsChangedHook)(GroupMemberFlagsChanged* request); +void SetOnGroupMemberFlagsChangedHook(OnGroupMemberFlagsChangedHook h); +int CallOnGroupMemberFlagsChangedHook(GroupMemberFlagsChanged* request); + +typedef void (*OnGroupMemberStateChangedHook)(GroupMemberStateChanged* request); +void SetOnGroupMemberStateChangedHook(OnGroupMemberStateChangedHook h); +int CallOnGroupMemberStateChangedHook(GroupMemberStateChanged* request); + +typedef void (*OnGroupInstanceResetRequestHook)(GroupInstanceResetRequest* request); +void SetOnGroupInstanceResetRequestHook(OnGroupInstanceResetRequestHook h); +int CallOnGroupInstanceResetRequestHook(GroupInstanceResetRequest* request); + +typedef void (*OnGroupInstanceBindExtensionRequestHook)(GroupInstanceBindExtensionRequest* request); +void SetOnGroupInstanceBindExtensionRequestHook(OnGroupInstanceBindExtensionRequestHook h); +int CallOnGroupInstanceBindExtensionRequestHook(GroupInstanceBindExtensionRequest* request); + #endif diff --git a/game-server/libsidecar/events.go b/game-server/libsidecar/events.go index 236ac46..9795230 100644 --- a/game-server/libsidecar/events.go +++ b/game-server/libsidecar/events.go @@ -3,6 +3,9 @@ package main import "C" import ( + stdlog "log" + "runtime/debug" + nats "github.com/nats-io/nats.go" "github.com/rs/zerolog" @@ -18,11 +21,21 @@ var eventsHandlersQueue queue.HandlersQueue func TC9ProcessEventsHooks() { handler := eventsHandlersQueue.Pop() for handler != nil { - handler.Handle() + handleEventHook(handler) handler = eventsHandlersQueue.Pop() } } +func handleEventHook(handler queue.Handler) { + defer func() { + if r := recover(); r != nil { + stdlog.Printf("TC9 event hook panic recovered: %v\n%s", r, debug.Stack()) + } + }() + + handler.Handle() +} + func SetupEventsListener(nc *nats.Conn, realmID uint32, log zerolog.Logger) consumer.Consumer { eventsHandlersQueue = queue.NewHandlersFIFOQueue() natsConsumer := consumer.NewNatsEventsConsumer( diff --git a/game-server/libsidecar/group-service.go b/game-server/libsidecar/group-service.go new file mode 100644 index 0000000..68db26a --- /dev/null +++ b/game-server/libsidecar/group-service.go @@ -0,0 +1,22 @@ +package main + +import ( + "github.com/rs/zerolog/log" + "github.com/walkline/ToCloud9/game-server/libsidecar/config" + groupPb "github.com/walkline/ToCloud9/gen/group/pb" + "google.golang.org/grpc" +) + +var groupServiceClient groupPb.GroupServiceClient + +func SetupGroupServiceConnection(cfg *config.Config) *grpc.ClientConn { + log.Info().Str("address", cfg.GroupServiceAddress).Msg("connecting to group service") + + conn, err := grpc.Dial(cfg.GroupServiceAddress, grpc.WithInsecure()) + if err != nil { + log.Fatal().Err(err).Msg("can't connect to the group server") + } + + groupServiceClient = groupPb.NewGroupServiceClient(conn) + return conn +} diff --git a/game-server/libsidecar/grpc-api.go b/game-server/libsidecar/grpc-api.go index e35bc60..0a374ad 100644 --- a/game-server/libsidecar/grpc-api.go +++ b/game-server/libsidecar/grpc-api.go @@ -33,6 +33,7 @@ func SetupGRPCService(conf *config.Config) (net.Listener, *grpc.Server) { grpcapi.NewWorldServerGRPCAPI( grpcapi.CppBindings{ GetPlayerItemsByGuids: GetPlayerItemsByGuidHandler, + TakePlayerItemByPos: TakePlayerItemByPosHandler, RemoveItemsWithGuidsFromPlayer: RemoveItemsWithGuidsFromPlayerHandler, AddExistingItemToPlayer: AddExistingItemToPlayerHandler, GetMoneyForPlayer: GetMoneyForPlayerHandler, @@ -43,6 +44,13 @@ func SetupGRPCService(conf *config.Config) (net.Listener, *grpc.Server) { AddPlayersToBattleground: BattlegroundAddPlayersHandler, CanPlayerJoinBattlegroundQueue: CanPlayerJoinBattlegroundQueueHandler, CanPlayerTeleportToBattleground: CanPlayerTeleportToBattlegroundHandler, + GetLFGPlayerLockInfo: LfgPlayerLockInfoHandler, + GetLFGPlayerInfo: LfgPlayerInfoHandler, + GetLFGDungeonInfo: LfgDungeonInfoHandler, + TeleportLFGPlayer: LfgTeleportPlayerHandler, + SetLFGBootVote: LfgBootVoteHandler, + MaterializeLFGProposal: LfgMaterializeProposalHandler, + CreateGuild: CreateGuildHandler, }, time.Second*5, readRequestsQueue, diff --git a/game-server/libsidecar/grpcapi/cpp-bindings.go b/game-server/libsidecar/grpcapi/cpp-bindings.go index a30c86d..d27510c 100644 --- a/game-server/libsidecar/grpcapi/cpp-bindings.go +++ b/game-server/libsidecar/grpcapi/cpp-bindings.go @@ -8,20 +8,38 @@ type PlayerItem struct { Slot uint8 IsTradable bool Count uint32 - Flags uint16 + Flags uint32 Durability uint32 - RandomPropertyID uint32 + RandomPropertyID int32 Text string } +type TakePlayerItemByPosResponse struct { + Status PlayerItemTakeStatus + Item PlayerItem +} + +type PlayerItemTakeStatus uint8 + +const ( + PlayerItemTakeSuccess PlayerItemTakeStatus = iota + PlayerItemTakePlayerNotFound + PlayerItemTakeItemNotFound + PlayerItemTakeItemNotTradable + PlayerItemTakeFailed +) + type ItemToAdd struct { Guid uint64 Entry uint32 Count uint32 - Flags uint16 + Flags uint32 Durability uint32 - RandomPropertyID uint32 + RandomPropertyID int32 Text string + StoreAtPos bool + BagSlot uint8 + Slot uint8 } type BattlegroundStartRequest struct { @@ -31,6 +49,11 @@ type BattlegroundStartRequest struct { MapID uint32 BracketLvl uint8 + AllianceArenaTeamID uint32 + HordeArenaTeamID uint32 + AllianceArenaMatchmakerRating uint32 + HordeArenaMatchmakerRating uint32 + HordePlayerGUIDsToAdd []uint64 AlliancePlayerGUIDsToAdd []uint64 } @@ -48,8 +71,96 @@ type BattlegroundAddPlayersRequest struct { AlliancePlayerGUIDsToAdd []uint64 } +type LFGDungeonLock struct { + DungeonEntry uint32 + LockStatus uint32 +} + +type LFGPlayerLockInfoRequest struct { + PlayerGUID uint64 + DungeonEntries []uint32 +} + +type LFGPlayerLockInfoResponse struct { + Locks []LFGDungeonLock + JoinResult uint32 + ValidDungeonEntries []uint32 +} + +type LFGPlayerInfoRequest struct { + PlayerGUID uint64 +} + +type LFGRewardItem struct { + ItemID uint32 + DisplayID uint32 + Count uint32 +} + +type LFGRandomDungeonInfo struct { + DungeonEntry uint32 + Done bool + RewardMoney uint32 + RewardXP uint32 + RewardUnknown1 uint32 + RewardUnknown2 uint32 + RewardItems []LFGRewardItem +} + +type LFGPlayerInfoResponse struct { + RandomDungeons []LFGRandomDungeonInfo + Locks []LFGDungeonLock +} + +type LFGDungeonInfoRequest struct { + DungeonEntry uint32 +} + +type LFGDungeonInfoResponse struct { + DungeonEntry uint32 + DungeonID uint32 + MapID uint32 + TypeID uint32 + Difficulty uint32 +} + +type LFGTeleportPlayerRequest struct { + PlayerGUID uint64 + Out bool + DungeonEntry uint32 +} + +type LFGSetBootVoteRequest struct { + PlayerGUID uint64 + Agree bool +} + +type LFGMaterializeProposalMember struct { + PlayerGUID uint64 + SelectedRoles uint8 + AssignedRole uint8 + QueueLeaderGUID uint64 +} + +type LFGMaterializeProposalRequest struct { + RealmID uint32 + ProposalID uint32 + DungeonEntry uint32 + LeaderGUID uint64 + Members []LFGMaterializeProposalMember +} + +type GuildCreateResponse struct { + ErrorCode uint32 + GuildID uint64 +} + +type CreateGuildHandler func(leaderGuid uint64, guildName string) (*GuildCreateResponse, error) + type GetPlayerItemsByGuidsHandler func(player uint64, items []uint64) ([]PlayerItem, error) +type TakePlayerItemByPosHandler func(player uint64, bagSlot, slot uint8, count uint32, assignToPlayer uint64) (*TakePlayerItemByPosResponse, error) + type RemoveItemsWithGuidsFromPlayerHandler func(player uint64, items []uint64, assignToPlayer uint64) ([]uint64, error) type AddExistingItemToPlayerHandler func(player uint64, item *ItemToAdd) error @@ -70,8 +181,21 @@ type CanPlayerJoinBattlegroundQueueHandler func(player uint64) error type CanPlayerTeleportToBattlegroundHandler func(player uint64) error +type GetLFGPlayerLockInfoHandler func(request LFGPlayerLockInfoRequest) (*LFGPlayerLockInfoResponse, error) + +type GetLFGPlayerInfoHandler func(request LFGPlayerInfoRequest) (*LFGPlayerInfoResponse, error) + +type GetLFGDungeonInfoHandler func(request LFGDungeonInfoRequest) (*LFGDungeonInfoResponse, error) + +type TeleportLFGPlayerHandler func(request LFGTeleportPlayerRequest) error + +type SetLFGBootVoteHandler func(request LFGSetBootVoteRequest) error + +type MaterializeLFGProposalHandler func(request LFGMaterializeProposalRequest) error + type CppBindings struct { GetPlayerItemsByGuids GetPlayerItemsByGuidsHandler + TakePlayerItemByPos TakePlayerItemByPosHandler RemoveItemsWithGuidsFromPlayer RemoveItemsWithGuidsFromPlayerHandler AddExistingItemToPlayer AddExistingItemToPlayerHandler GetMoneyForPlayer GetMoneyForPlayerHandler @@ -82,4 +206,11 @@ type CppBindings struct { AddPlayersToBattleground AddPlayersToBattlegroundHandler CanPlayerJoinBattlegroundQueue CanPlayerJoinBattlegroundQueueHandler CanPlayerTeleportToBattleground CanPlayerTeleportToBattlegroundHandler + GetLFGPlayerLockInfo GetLFGPlayerLockInfoHandler + GetLFGPlayerInfo GetLFGPlayerInfoHandler + GetLFGDungeonInfo GetLFGDungeonInfoHandler + TeleportLFGPlayer TeleportLFGPlayerHandler + SetLFGBootVote SetLFGBootVoteHandler + MaterializeLFGProposal MaterializeLFGProposalHandler + CreateGuild CreateGuildHandler } diff --git a/game-server/libsidecar/grpcapi/lfg-errors.go b/game-server/libsidecar/grpcapi/lfg-errors.go new file mode 100644 index 0000000..b8a4efa --- /dev/null +++ b/game-server/libsidecar/grpcapi/lfg-errors.go @@ -0,0 +1,186 @@ +package grpcapi + +import "errors" + +type LFGPlayerLockInfoErrorCode int + +const ( + LFGPlayerLockInfoErrorCodeSuccess LFGPlayerLockInfoErrorCode = iota + LFGPlayerLockInfoErrorCodeNoHandler + LFGPlayerLockInfoErrorCodePlayerNotFound + LFGPlayerLockInfoErrorCodeInternalError +) + +type LFGPlayerLockInfoError LFGPlayerLockInfoErrorCode + +func (e LFGPlayerLockInfoError) Error() string { + switch LFGPlayerLockInfoErrorCode(e) { + case LFGPlayerLockInfoErrorCodeNoHandler: + return "lfg player lock info handler is not registered" + case LFGPlayerLockInfoErrorCodePlayerNotFound: + return "lfg player not found" + default: + return "lfg player lock info handler failed" + } +} + +func LFGPlayerLockInfoErrorCodeForError(err error) LFGPlayerLockInfoErrorCode { + var lockInfoErr LFGPlayerLockInfoError + if errors.As(err, &lockInfoErr) { + return LFGPlayerLockInfoErrorCode(lockInfoErr) + } + return LFGPlayerLockInfoErrorCodeInternalError +} + +type LFGPlayerInfoErrorCode int + +const ( + LFGPlayerInfoErrorCodeSuccess LFGPlayerInfoErrorCode = iota + LFGPlayerInfoErrorCodeNoHandler + LFGPlayerInfoErrorCodePlayerNotFound + LFGPlayerInfoErrorCodeInternalError +) + +type LFGPlayerInfoError LFGPlayerInfoErrorCode + +func (e LFGPlayerInfoError) Error() string { + switch LFGPlayerInfoErrorCode(e) { + case LFGPlayerInfoErrorCodeNoHandler: + return "lfg player info handler is not registered" + case LFGPlayerInfoErrorCodePlayerNotFound: + return "lfg player not found" + default: + return "lfg player info handler failed" + } +} + +func LFGPlayerInfoErrorCodeForError(err error) LFGPlayerInfoErrorCode { + var playerInfoErr LFGPlayerInfoError + if errors.As(err, &playerInfoErr) { + return LFGPlayerInfoErrorCode(playerInfoErr) + } + return LFGPlayerInfoErrorCodeInternalError +} + +type LFGDungeonInfoErrorCode int + +const ( + LFGDungeonInfoErrorCodeSuccess LFGDungeonInfoErrorCode = iota + LFGDungeonInfoErrorCodeNoHandler + LFGDungeonInfoErrorCodeDungeonNotFound + LFGDungeonInfoErrorCodeInternalError +) + +type LFGDungeonInfoError LFGDungeonInfoErrorCode + +func (e LFGDungeonInfoError) Error() string { + switch LFGDungeonInfoErrorCode(e) { + case LFGDungeonInfoErrorCodeNoHandler: + return "lfg dungeon info handler is not registered" + case LFGDungeonInfoErrorCodeDungeonNotFound: + return "lfg dungeon info dungeon not found" + default: + return "lfg dungeon info handler failed" + } +} + +func LFGDungeonInfoErrorCodeForError(err error) LFGDungeonInfoErrorCode { + var dungeonInfoErr LFGDungeonInfoError + if errors.As(err, &dungeonInfoErr) { + return LFGDungeonInfoErrorCode(dungeonInfoErr) + } + return LFGDungeonInfoErrorCodeInternalError +} + +type LFGTeleportPlayerErrorCode int + +const ( + LFGTeleportPlayerErrorCodeSuccess LFGTeleportPlayerErrorCode = iota + LFGTeleportPlayerErrorCodeNoHandler + LFGTeleportPlayerErrorCodePlayerNotFound + LFGTeleportPlayerErrorCodeInternalError +) + +type LFGTeleportPlayerError LFGTeleportPlayerErrorCode + +func (e LFGTeleportPlayerError) Error() string { + switch LFGTeleportPlayerErrorCode(e) { + case LFGTeleportPlayerErrorCodeNoHandler: + return "lfg teleport player handler is not registered" + case LFGTeleportPlayerErrorCodePlayerNotFound: + return "lfg teleport player not found" + default: + return "lfg teleport player handler failed" + } +} + +func LFGTeleportPlayerErrorCodeForError(err error) LFGTeleportPlayerErrorCode { + var teleportErr LFGTeleportPlayerError + if errors.As(err, &teleportErr) { + return LFGTeleportPlayerErrorCode(teleportErr) + } + return LFGTeleportPlayerErrorCodeInternalError +} + +type LFGSetBootVoteErrorCode int + +const ( + LFGSetBootVoteErrorCodeSuccess LFGSetBootVoteErrorCode = iota + LFGSetBootVoteErrorCodeNoHandler + LFGSetBootVoteErrorCodePlayerNotFound + LFGSetBootVoteErrorCodeInternalError +) + +type LFGSetBootVoteError LFGSetBootVoteErrorCode + +func (e LFGSetBootVoteError) Error() string { + switch LFGSetBootVoteErrorCode(e) { + case LFGSetBootVoteErrorCodeNoHandler: + return "lfg boot vote handler is not registered" + case LFGSetBootVoteErrorCodePlayerNotFound: + return "lfg boot vote player not found" + default: + return "lfg boot vote handler failed" + } +} + +func LFGSetBootVoteErrorCodeForError(err error) LFGSetBootVoteErrorCode { + var bootVoteErr LFGSetBootVoteError + if errors.As(err, &bootVoteErr) { + return LFGSetBootVoteErrorCode(bootVoteErr) + } + return LFGSetBootVoteErrorCodeInternalError +} + +type LFGMaterializeProposalErrorCode int + +const ( + LFGMaterializeProposalErrorCodeSuccess LFGMaterializeProposalErrorCode = iota + LFGMaterializeProposalErrorCodeNoHandler + LFGMaterializeProposalErrorCodeDungeonNotFound + LFGMaterializeProposalErrorCodeNoLocalPlayer + LFGMaterializeProposalErrorCodeInternalError +) + +type LFGMaterializeProposalError LFGMaterializeProposalErrorCode + +func (e LFGMaterializeProposalError) Error() string { + switch LFGMaterializeProposalErrorCode(e) { + case LFGMaterializeProposalErrorCodeNoHandler: + return "lfg materialize proposal handler is not registered" + case LFGMaterializeProposalErrorCodeDungeonNotFound: + return "lfg materialize proposal dungeon not found" + case LFGMaterializeProposalErrorCodeNoLocalPlayer: + return "lfg materialize proposal has no local player" + default: + return "lfg materialize proposal handler failed" + } +} + +func LFGMaterializeProposalErrorCodeForError(err error) LFGMaterializeProposalErrorCode { + var materializeErr LFGMaterializeProposalError + if errors.As(err, &materializeErr) { + return LFGMaterializeProposalErrorCode(materializeErr) + } + return LFGMaterializeProposalErrorCodeInternalError +} diff --git a/game-server/libsidecar/grpcapi/server-battleground.go b/game-server/libsidecar/grpcapi/server-battleground.go index fb1d19c..8c376ac 100644 --- a/game-server/libsidecar/grpcapi/server-battleground.go +++ b/game-server/libsidecar/grpcapi/server-battleground.go @@ -21,13 +21,17 @@ func (w *WorldServerGRPCAPI) StartBattleground(ctx context.Context, request *pb. w.writeQueue.Push(queue.HandlerFunc(func() { r, err := w.bindings.StartBattleground(BattlegroundStartRequest{ - BattlegroundTypeID: uint8(request.BattlegroundTypeID), - ArenaType: uint8(request.ArenaType), - IsRated: request.IsRated, - MapID: request.MapID, - BracketLvl: uint8(request.BracketLvl), - HordePlayerGUIDsToAdd: request.PlayersToAddHorde, - AlliancePlayerGUIDsToAdd: request.PlayersToAddAlliance, + BattlegroundTypeID: uint8(request.BattlegroundTypeID), + ArenaType: uint8(request.ArenaType), + IsRated: request.IsRated, + MapID: request.MapID, + BracketLvl: uint8(request.BracketLvl), + AllianceArenaTeamID: request.AllianceArenaTeamID, + HordeArenaTeamID: request.HordeArenaTeamID, + AllianceArenaMatchmakerRating: request.AllianceArenaMatchmakerRating, + HordeArenaMatchmakerRating: request.HordeArenaMatchmakerRating, + HordePlayerGUIDsToAdd: request.PlayersToAddHorde, + AlliancePlayerGUIDsToAdd: request.PlayersToAddAlliance, }) respChan <- respType{ resp: r, diff --git a/game-server/libsidecar/grpcapi/server-guild.go b/game-server/libsidecar/grpcapi/server-guild.go new file mode 100644 index 0000000..8d2b011 --- /dev/null +++ b/game-server/libsidecar/grpcapi/server-guild.go @@ -0,0 +1,72 @@ +package grpcapi + +import ( + "context" + + "github.com/walkline/ToCloud9/game-server/libsidecar/queue" + "github.com/walkline/ToCloud9/gen/worldserver/pb" +) + +func (w *WorldServerGRPCAPI) CreateGuild(ctx context.Context, request *pb.CreateGuildRequest) (*pb.CreateGuildResponse, error) { + ctx, cancel := context.WithTimeout(ctx, w.timeout) + defer cancel() + + type respType struct { + resp *GuildCreateResponse + err error + } + + respChan := make(chan respType, 1) + + w.writeQueue.Push(queue.HandlerFunc(func() { + resp, err := w.bindings.CreateGuild(request.LeaderGuid, request.GuildName) + respChan <- respType{resp: resp, err: err} + close(respChan) + })) + + var resp respType + + select { + case <-ctx.Done(): + return nil, ErrTimeout + case resp = <-respChan: + } + + if resp.err != nil { + return nil, resp.err + } + + status := pb.CreateGuildResponse_Success + + if resp.resp == nil { + status = pb.CreateGuildResponse_InternalError + } else { + switch resp.resp.ErrorCode { + case 0: + status = pb.CreateGuildResponse_Success + case 1: + status = pb.CreateGuildResponse_InternalError + case 2: + status = pb.CreateGuildResponse_NameExists + case 3: + status = pb.CreateGuildResponse_InvalidName + case 4: + status = pb.CreateGuildResponse_LeaderNotFound + case 5: + status = pb.CreateGuildResponse_InternalError + default: + status = pb.CreateGuildResponse_InternalError + } + } + + guildID := uint64(0) + if resp.resp != nil { + guildID = resp.resp.GuildID + } + + return &pb.CreateGuildResponse{ + Api: LibVer, + Status: status, + GuildId: guildID, + }, nil +} diff --git a/game-server/libsidecar/grpcapi/server-items.go b/game-server/libsidecar/grpcapi/server-items.go index 0e0250d..04b817d 100644 --- a/game-server/libsidecar/grpcapi/server-items.go +++ b/game-server/libsidecar/grpcapi/server-items.go @@ -67,6 +67,76 @@ func (w *WorldServerGRPCAPI) GetPlayerItemsByGuids(ctx context.Context, request }, nil } +func (w *WorldServerGRPCAPI) TakePlayerItemByPos(ctx context.Context, request *pb.TakePlayerItemByPosRequest) (*pb.TakePlayerItemByPosResponse, error) { + if request.PlayerGuid == 0 { + return &pb.TakePlayerItemByPosResponse{ + Api: LibVer, + Status: pb.TakePlayerItemByPosResponse_PlayerNotFound, + }, nil + } + + ctx, cancel := context.WithTimeout(ctx, w.timeout) + defer cancel() + + type respType struct { + resp *TakePlayerItemByPosResponse + err error + } + var resp respType + + respChan := make(chan respType, 1) + + w.writeQueue.Push(queue.HandlerFunc(func() { + takeResp, err := w.bindings.TakePlayerItemByPos( + request.PlayerGuid, + uint8(request.BagSlot), + uint8(request.Slot), + request.Count, + request.AssignToPlayerGuid, + ) + respChan <- respType{ + resp: takeResp, + err: err, + } + close(respChan) + })) + + select { + case <-ctx.Done(): + return nil, ErrTimeout + case resp = <-respChan: + } + + if resp.err != nil { + return nil, resp.err + } + if resp.resp == nil { + return &pb.TakePlayerItemByPosResponse{ + Api: LibVer, + Status: pb.TakePlayerItemByPosResponse_Failed, + }, nil + } + + item := resp.resp.Item + return &pb.TakePlayerItemByPosResponse{ + Api: LibVer, + Status: takePlayerItemByPosStatusToPB(resp.resp.Status), + Item: &pb.TakePlayerItemByPosResponse_Item{ + Guid: item.Guid, + Entry: item.Entry, + Owner: item.Owner, + BagSlot: uint32(item.BagSlot), + Slot: uint32(item.Slot), + IsTradable: item.IsTradable, + Count: item.Count, + Flags: item.Flags, + Durability: item.Durability, + RandomPropertyID: item.RandomPropertyID, + Text: item.Text, + }, + }, nil +} + func (w *WorldServerGRPCAPI) RemoveItemsWithGuidsFromPlayer(ctx context.Context, request *pb.RemoveItemsWithGuidsFromPlayerRequest) (*pb.RemoveItemsWithGuidsFromPlayerResponse, error) { if request.PlayerGuid == 0 || len(request.Guids) == 0 { return &pb.RemoveItemsWithGuidsFromPlayerResponse{ @@ -129,10 +199,13 @@ func (w *WorldServerGRPCAPI) AddExistingItemToPlayer(ctx context.Context, reques Guid: request.Item.Guid, Entry: request.Item.Entry, Count: request.Item.Count, - Flags: uint16(request.Item.Flags), + Flags: uint32(request.Item.Flags), Durability: request.Item.Durability, - RandomPropertyID: request.Item.RandomPropertyID, + RandomPropertyID: int32(request.Item.RandomPropertyID), Text: request.Item.Text, + StoreAtPos: request.StoreAtPos, + BagSlot: uint8(request.BagSlot), + Slot: uint8(request.Slot), }) close(respChan) })) @@ -157,3 +230,18 @@ func (w *WorldServerGRPCAPI) AddExistingItemToPlayer(ctx context.Context, reques Status: pb.AddExistingItemToPlayerResponse_Success, }, nil } + +func takePlayerItemByPosStatusToPB(status PlayerItemTakeStatus) pb.TakePlayerItemByPosResponse_Status { + switch status { + case PlayerItemTakeSuccess: + return pb.TakePlayerItemByPosResponse_Success + case PlayerItemTakePlayerNotFound: + return pb.TakePlayerItemByPosResponse_PlayerNotFound + case PlayerItemTakeItemNotFound: + return pb.TakePlayerItemByPosResponse_ItemNotFound + case PlayerItemTakeItemNotTradable: + return pb.TakePlayerItemByPosResponse_ItemNotTradable + default: + return pb.TakePlayerItemByPosResponse_Failed + } +} diff --git a/game-server/libsidecar/grpcapi/server-lfg.go b/game-server/libsidecar/grpcapi/server-lfg.go new file mode 100644 index 0000000..72a23e8 --- /dev/null +++ b/game-server/libsidecar/grpcapi/server-lfg.go @@ -0,0 +1,408 @@ +package grpcapi + +import ( + "context" + "time" + + "github.com/walkline/ToCloud9/game-server/libsidecar/queue" + "github.com/walkline/ToCloud9/gen/worldserver/pb" +) + +// Proposal materialization runs after every member has already accepted the +// client proposal. Keep this as an infrastructure redirect/load budget instead +// of AzerothCore's 40s answer window. +const lfgMaterializeProposalTimeout = 90 * time.Second + +func (w *WorldServerGRPCAPI) GetLfgPlayerLockInfo(ctx context.Context, request *pb.GetLfgPlayerLockInfoRequest) (*pb.GetLfgPlayerLockInfoResponse, error) { + ctx, cancel := context.WithTimeout(ctx, w.timeout) + defer cancel() + + type respType struct { + resp *LFGPlayerLockInfoResponse + err error + } + var resp respType + + respChan := make(chan respType, 1) + + w.writeQueue.Push(queue.HandlerFunc(func() { + if w.bindings.GetLFGPlayerLockInfo == nil { + respChan <- respType{err: LFGPlayerLockInfoError(LFGPlayerLockInfoErrorCodeNoHandler)} + close(respChan) + return + } + r, err := w.bindings.GetLFGPlayerLockInfo(LFGPlayerLockInfoRequest{ + PlayerGUID: request.PlayerGUID, + DungeonEntries: request.DungeonEntries, + }) + respChan <- respType{ + resp: r, + err: err, + } + close(respChan) + })) + select { + case <-ctx.Done(): + return nil, ErrTimeout + case resp = <-respChan: + } + + if resp.err != nil { + switch LFGPlayerLockInfoErrorCodeForError(resp.err) { + case LFGPlayerLockInfoErrorCodePlayerNotFound: + return &pb.GetLfgPlayerLockInfoResponse{ + Api: LibVer, + Status: pb.GetLfgPlayerLockInfoResponse_PlayerNotFound, + }, nil + default: + return &pb.GetLfgPlayerLockInfoResponse{ + Api: LibVer, + Status: pb.GetLfgPlayerLockInfoResponse_InternalError, + }, nil + } + } + if resp.resp == nil { + return &pb.GetLfgPlayerLockInfoResponse{ + Api: LibVer, + Status: pb.GetLfgPlayerLockInfoResponse_InternalError, + }, nil + } + + locks := make([]*pb.LfgDungeonLock, 0, len(resp.resp.Locks)) + for _, lock := range resp.resp.Locks { + locks = append(locks, &pb.LfgDungeonLock{ + DungeonEntry: lock.DungeonEntry, + LockStatus: lock.LockStatus, + }) + } + + return &pb.GetLfgPlayerLockInfoResponse{ + Api: LibVer, + Status: pb.GetLfgPlayerLockInfoResponse_Success, + Locks: locks, + JoinResult: resp.resp.JoinResult, + ValidDungeonEntries: resp.resp.ValidDungeonEntries, + }, nil +} + +func (w *WorldServerGRPCAPI) GetLfgPlayerInfo(ctx context.Context, request *pb.GetLfgPlayerInfoRequest) (*pb.GetLfgPlayerInfoResponse, error) { + ctx, cancel := context.WithTimeout(ctx, w.timeout) + defer cancel() + + type respType struct { + resp *LFGPlayerInfoResponse + err error + } + var resp respType + respChan := make(chan respType, 1) + + w.writeQueue.Push(queue.HandlerFunc(func() { + if w.bindings.GetLFGPlayerInfo == nil { + respChan <- respType{err: LFGPlayerInfoError(LFGPlayerInfoErrorCodeNoHandler)} + close(respChan) + return + } + r, err := w.bindings.GetLFGPlayerInfo(LFGPlayerInfoRequest{ + PlayerGUID: request.GetPlayerGUID(), + }) + respChan <- respType{ + resp: r, + err: err, + } + close(respChan) + })) + + select { + case <-ctx.Done(): + return nil, ErrTimeout + case resp = <-respChan: + } + + if resp.err != nil { + return &pb.GetLfgPlayerInfoResponse{ + Api: LibVer, + Status: lfgPlayerInfoStatusForError(resp.err), + }, nil + } + if resp.resp == nil { + return &pb.GetLfgPlayerInfoResponse{ + Api: LibVer, + Status: pb.GetLfgPlayerInfoResponse_InternalError, + }, nil + } + + randomDungeons := make([]*pb.LfgRandomDungeonInfo, 0, len(resp.resp.RandomDungeons)) + for _, dungeon := range resp.resp.RandomDungeons { + rewardItems := make([]*pb.LfgRewardItem, 0, len(dungeon.RewardItems)) + for _, item := range dungeon.RewardItems { + rewardItems = append(rewardItems, &pb.LfgRewardItem{ + ItemID: item.ItemID, + DisplayID: item.DisplayID, + Count: item.Count, + }) + } + randomDungeons = append(randomDungeons, &pb.LfgRandomDungeonInfo{ + DungeonEntry: dungeon.DungeonEntry, + Done: dungeon.Done, + RewardMoney: dungeon.RewardMoney, + RewardXP: dungeon.RewardXP, + RewardUnknown1: dungeon.RewardUnknown1, + RewardUnknown2: dungeon.RewardUnknown2, + RewardItems: rewardItems, + }) + } + + locks := make([]*pb.LfgDungeonLock, 0, len(resp.resp.Locks)) + for _, lock := range resp.resp.Locks { + locks = append(locks, &pb.LfgDungeonLock{ + DungeonEntry: lock.DungeonEntry, + LockStatus: lock.LockStatus, + }) + } + + return &pb.GetLfgPlayerInfoResponse{ + Api: LibVer, + Status: pb.GetLfgPlayerInfoResponse_Success, + RandomDungeons: randomDungeons, + Locks: locks, + }, nil +} + +func lfgPlayerInfoStatusForError(err error) pb.GetLfgPlayerInfoResponse_Status { + switch LFGPlayerInfoErrorCodeForError(err) { + case LFGPlayerInfoErrorCodeNoHandler: + return pb.GetLfgPlayerInfoResponse_NoHandler + case LFGPlayerInfoErrorCodePlayerNotFound: + return pb.GetLfgPlayerInfoResponse_PlayerNotFound + default: + return pb.GetLfgPlayerInfoResponse_InternalError + } +} + +func (w *WorldServerGRPCAPI) GetLfgDungeonInfo(ctx context.Context, request *pb.GetLfgDungeonInfoRequest) (*pb.GetLfgDungeonInfoResponse, error) { + ctx, cancel := context.WithTimeout(ctx, w.timeout) + defer cancel() + + type respType struct { + resp *LFGDungeonInfoResponse + err error + } + respChan := make(chan respType, 1) + + w.writeQueue.Push(queue.HandlerFunc(func() { + if w.bindings.GetLFGDungeonInfo == nil { + respChan <- respType{err: LFGDungeonInfoError(LFGDungeonInfoErrorCodeNoHandler)} + close(respChan) + return + } + r, err := w.bindings.GetLFGDungeonInfo(LFGDungeonInfoRequest{ + DungeonEntry: request.GetDungeonEntry(), + }) + respChan <- respType{ + resp: r, + err: err, + } + close(respChan) + })) + + var resp respType + select { + case <-ctx.Done(): + return nil, ErrTimeout + case resp = <-respChan: + } + + if resp.err != nil { + return &pb.GetLfgDungeonInfoResponse{ + Api: LibVer, + Status: lfgDungeonInfoStatusForError(resp.err), + }, nil + } + if resp.resp == nil { + return &pb.GetLfgDungeonInfoResponse{ + Api: LibVer, + Status: pb.GetLfgDungeonInfoResponse_InternalError, + }, nil + } + + return &pb.GetLfgDungeonInfoResponse{ + Api: LibVer, + Status: pb.GetLfgDungeonInfoResponse_Success, + DungeonEntry: resp.resp.DungeonEntry, + DungeonID: resp.resp.DungeonID, + MapID: resp.resp.MapID, + TypeID: resp.resp.TypeID, + Difficulty: resp.resp.Difficulty, + }, nil +} + +func lfgDungeonInfoStatusForError(err error) pb.GetLfgDungeonInfoResponse_Status { + switch LFGDungeonInfoErrorCodeForError(err) { + case LFGDungeonInfoErrorCodeNoHandler: + return pb.GetLfgDungeonInfoResponse_NoHandler + case LFGDungeonInfoErrorCodeDungeonNotFound: + return pb.GetLfgDungeonInfoResponse_DungeonNotFound + default: + return pb.GetLfgDungeonInfoResponse_InternalError + } +} + +func (w *WorldServerGRPCAPI) TeleportLfgPlayer(ctx context.Context, request *pb.TeleportLfgPlayerRequest) (*pb.TeleportLfgPlayerResponse, error) { + ctx, cancel := context.WithTimeout(ctx, w.timeout) + defer cancel() + + respChan := make(chan error, 1) + + w.writeQueue.Push(queue.HandlerFunc(func() { + if w.bindings.TeleportLFGPlayer == nil { + respChan <- LFGTeleportPlayerError(LFGTeleportPlayerErrorCodeNoHandler) + close(respChan) + return + } + respChan <- w.bindings.TeleportLFGPlayer(LFGTeleportPlayerRequest{ + PlayerGUID: request.GetPlayerGUID(), + Out: request.GetOut(), + DungeonEntry: request.GetDungeonEntry(), + }) + close(respChan) + })) + + var err error + select { + case <-ctx.Done(): + return nil, ErrTimeout + case err = <-respChan: + } + + return &pb.TeleportLfgPlayerResponse{ + Api: LibVer, + Status: teleportLfgPlayerStatusForError(err), + }, nil +} + +func teleportLfgPlayerStatusForError(err error) pb.TeleportLfgPlayerResponse_Status { + if err == nil { + return pb.TeleportLfgPlayerResponse_Success + } + switch LFGTeleportPlayerErrorCodeForError(err) { + case LFGTeleportPlayerErrorCodeNoHandler: + return pb.TeleportLfgPlayerResponse_NoHandler + case LFGTeleportPlayerErrorCodePlayerNotFound: + return pb.TeleportLfgPlayerResponse_PlayerNotFound + default: + return pb.TeleportLfgPlayerResponse_InternalError + } +} + +func (w *WorldServerGRPCAPI) SetLfgBootVote(ctx context.Context, request *pb.SetLfgBootVoteRequest) (*pb.SetLfgBootVoteResponse, error) { + ctx, cancel := context.WithTimeout(ctx, w.timeout) + defer cancel() + + respChan := make(chan error, 1) + + w.writeQueue.Push(queue.HandlerFunc(func() { + if w.bindings.SetLFGBootVote == nil { + respChan <- LFGSetBootVoteError(LFGSetBootVoteErrorCodeNoHandler) + close(respChan) + return + } + respChan <- w.bindings.SetLFGBootVote(LFGSetBootVoteRequest{ + PlayerGUID: request.GetPlayerGUID(), + Agree: request.GetAgree(), + }) + close(respChan) + })) + + var err error + select { + case <-ctx.Done(): + return nil, ErrTimeout + case err = <-respChan: + } + + return &pb.SetLfgBootVoteResponse{ + Api: LibVer, + Status: setLfgBootVoteStatusForError(err), + }, nil +} + +func setLfgBootVoteStatusForError(err error) pb.SetLfgBootVoteResponse_Status { + if err == nil { + return pb.SetLfgBootVoteResponse_Success + } + switch LFGSetBootVoteErrorCodeForError(err) { + case LFGSetBootVoteErrorCodeNoHandler: + return pb.SetLfgBootVoteResponse_NoHandler + case LFGSetBootVoteErrorCodePlayerNotFound: + return pb.SetLfgBootVoteResponse_PlayerNotFound + default: + return pb.SetLfgBootVoteResponse_InternalError + } +} + +func (w *WorldServerGRPCAPI) MaterializeLfgProposal(ctx context.Context, request *pb.MaterializeLfgProposalRequest) (*pb.MaterializeLfgProposalResponse, error) { + timeout := w.timeout + if timeout < lfgMaterializeProposalTimeout { + timeout = lfgMaterializeProposalTimeout + } + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + respChan := make(chan error, 1) + + members := make([]LFGMaterializeProposalMember, 0, len(request.GetMembers())) + for _, member := range request.GetMembers() { + members = append(members, LFGMaterializeProposalMember{ + PlayerGUID: member.GetPlayerGUID(), + SelectedRoles: uint8(member.GetSelectedRoles()), + AssignedRole: uint8(member.GetAssignedRole()), + QueueLeaderGUID: member.GetQueueLeaderGUID(), + }) + } + + w.writeQueue.Push(queue.HandlerFunc(func() { + if w.bindings.MaterializeLFGProposal == nil { + respChan <- LFGMaterializeProposalError(LFGMaterializeProposalErrorCodeNoHandler) + close(respChan) + return + } + + respChan <- w.bindings.MaterializeLFGProposal(LFGMaterializeProposalRequest{ + RealmID: request.GetRealmID(), + ProposalID: request.GetProposalID(), + DungeonEntry: request.GetDungeonEntry(), + LeaderGUID: request.GetLeaderGUID(), + Members: members, + }) + close(respChan) + })) + + var err error + select { + case <-ctx.Done(): + return nil, ErrTimeout + case err = <-respChan: + } + + return &pb.MaterializeLfgProposalResponse{ + Api: LibVer, + Status: materializeLfgProposalStatusForError(err), + }, nil +} + +func materializeLfgProposalStatusForError(err error) pb.MaterializeLfgProposalResponse_Status { + if err == nil { + return pb.MaterializeLfgProposalResponse_Success + } + + switch LFGMaterializeProposalErrorCodeForError(err) { + case LFGMaterializeProposalErrorCodeNoHandler: + return pb.MaterializeLfgProposalResponse_NoHandler + case LFGMaterializeProposalErrorCodeDungeonNotFound: + return pb.MaterializeLfgProposalResponse_DungeonNotFound + case LFGMaterializeProposalErrorCodeNoLocalPlayer: + return pb.MaterializeLfgProposalResponse_NoLocalPlayer + default: + return pb.MaterializeLfgProposalResponse_InternalError + } +} diff --git a/game-server/libsidecar/guids/mgr.go b/game-server/libsidecar/guids/mgr.go index 2192e8a..1921fdd 100644 --- a/game-server/libsidecar/guids/mgr.go +++ b/game-server/libsidecar/guids/mgr.go @@ -26,7 +26,7 @@ func NewCrossRealmMgr(client pb.GuidServiceClient, guidType pb.GuidType, desired } mgr.InitForRealm(defaultRealmID) - + return mgr } diff --git a/game-server/libsidecar/guild-api.c b/game-server/libsidecar/guild-api.c new file mode 100644 index 0000000..a978515 --- /dev/null +++ b/game-server/libsidecar/guild-api.c @@ -0,0 +1,17 @@ +#include "guild-api.h" + +static GuildCreateHandler guildCreateHandler; + +void SetGuildCreateHandler(GuildCreateHandler h) { + guildCreateHandler = h; +} + +GuildCreateResponse CallGuildCreateHandler(GuildCreateRequest* request) { + if (guildCreateHandler == 0) { + GuildCreateResponse resp = {0}; + resp.errorCode = GuildCreateErrorCodeNoHandler; + return resp; + } + + return guildCreateHandler(request); +} \ No newline at end of file diff --git a/game-server/libsidecar/guild-api.go b/game-server/libsidecar/guild-api.go new file mode 100644 index 0000000..16f05d5 --- /dev/null +++ b/game-server/libsidecar/guild-api.go @@ -0,0 +1,36 @@ +package main + +/* +#include "guild-api.h" +*/ +import "C" + +import ( + "unsafe" + + "github.com/walkline/ToCloud9/game-server/libsidecar/grpcapi" +) + +// TC9SetGuildCreateHandler sets handler for creating guilds. +// +//export TC9SetGuildCreateHandler +func TC9SetGuildCreateHandler(h C.GuildCreateHandler) { + C.SetGuildCreateHandler(h) +} + +func CreateGuildHandler(leaderGuid uint64, guildName string) (*grpcapi.GuildCreateResponse, error) { + cName := C.CString(guildName) + defer C.free(unsafe.Pointer(cName)) + + req := C.GuildCreateRequest{ + leaderGuid: C.uint64_t(leaderGuid), + guildName: cName, + } + + res := C.CallGuildCreateHandler(&req) + + return &grpcapi.GuildCreateResponse{ + ErrorCode: uint32(res.errorCode), + GuildID: uint64(res.guildId), + }, nil +} diff --git a/game-server/libsidecar/guild-api.h b/game-server/libsidecar/guild-api.h new file mode 100644 index 0000000..42f35d3 --- /dev/null +++ b/game-server/libsidecar/guild-api.h @@ -0,0 +1,32 @@ +#ifndef __GUILD_API__ +#define __GUILD_API__ + +#include +#include +#include + +typedef enum GuildCreateErrorCode { + GuildCreateErrorCodeNoError = 0, + GuildCreateErrorCodeNoHandler = 1, + GuildCreateErrorCodeNameExists = 2, + GuildCreateErrorCodeInvalidName = 3, + GuildCreateErrorCodeLeaderNotFound = 4, + GuildCreateErrorCodeInternalError = 5, +} GuildCreateErrorCode; + +typedef struct { + uint64_t leaderGuid; + const char* guildName; +} GuildCreateRequest; + +typedef struct { + int errorCode; + uint64_t guildId; +} GuildCreateResponse; + +typedef GuildCreateResponse (*GuildCreateHandler)(GuildCreateRequest* request); + +void SetGuildCreateHandler(GuildCreateHandler h); +GuildCreateResponse CallGuildCreateHandler(GuildCreateRequest* request); + +#endif \ No newline at end of file diff --git a/game-server/libsidecar/lfg-api.c b/game-server/libsidecar/lfg-api.c new file mode 100644 index 0000000..cb1f9f1 --- /dev/null +++ b/game-server/libsidecar/lfg-api.c @@ -0,0 +1,100 @@ +#include "lfg-api.h" + +static LfgPlayerLockInfoHandler lfgPlayerLockInfoHandler = 0; +static LfgPlayerInfoHandler lfgPlayerInfoHandler = 0; +static LfgDungeonInfoHandler lfgDungeonInfoHandler = 0; +static LfgTeleportPlayerHandler lfgTeleportPlayerHandler = 0; +static LfgBootVoteHandler lfgBootVoteHandler = 0; +static LfgMaterializeProposalHandler lfgMaterializeProposalHandler = 0; + +void SetLfgPlayerLockInfoHandler(LfgPlayerLockInfoHandler h) { + lfgPlayerLockInfoHandler = h; +} + +LfgPlayerLockInfoResponse CallLfgPlayerLockInfoHandler(LfgPlayerLockInfoRequest* request) { + if (lfgPlayerLockInfoHandler == 0) { + LfgPlayerLockInfoResponse resp; + resp.errorCode = LfgPlayerLockInfoErrorCodeNoHandler; + resp.locks = 0; + resp.locksSize = 0; + resp.joinResult = 0; + resp.validDungeonEntries = 0; + resp.validDungeonEntriesSize = 0; + return resp; + } + + return lfgPlayerLockInfoHandler(request); +} + +void SetLfgPlayerInfoHandler(LfgPlayerInfoHandler h) { + lfgPlayerInfoHandler = h; +} + +LfgPlayerInfoResponse CallLfgPlayerInfoHandler(LfgPlayerInfoRequest* request) { + if (lfgPlayerInfoHandler == 0) { + LfgPlayerInfoResponse resp; + resp.errorCode = LfgPlayerInfoErrorCodeNoHandler; + resp.randomDungeons = 0; + resp.randomDungeonsSize = 0; + resp.locks = 0; + resp.locksSize = 0; + return resp; + } + + return lfgPlayerInfoHandler(request); +} + +void SetLfgDungeonInfoHandler(LfgDungeonInfoHandler h) { + lfgDungeonInfoHandler = h; +} + +LfgDungeonInfoResponse CallLfgDungeonInfoHandler(LfgDungeonInfoRequest* request) { + if (lfgDungeonInfoHandler == 0) { + LfgDungeonInfoResponse resp; + resp.errorCode = LfgDungeonInfoErrorCodeNoHandler; + resp.dungeonEntry = 0; + resp.dungeonId = 0; + resp.mapId = 0; + resp.typeId = 0; + resp.difficulty = 0; + return resp; + } + + return lfgDungeonInfoHandler(request); +} + +void SetLfgTeleportPlayerHandler(LfgTeleportPlayerHandler h) { + lfgTeleportPlayerHandler = h; +} + +LfgTeleportPlayerErrorCode CallLfgTeleportPlayerHandler(LfgTeleportPlayerRequest* request) { + if (lfgTeleportPlayerHandler == 0) { + return LfgTeleportPlayerErrorCodeNoHandler; + } + + return lfgTeleportPlayerHandler(request); +} + +void SetLfgBootVoteHandler(LfgBootVoteHandler h) { + lfgBootVoteHandler = h; +} + +LfgBootVoteErrorCode CallLfgBootVoteHandler(LfgBootVoteRequest* request) { + if (lfgBootVoteHandler == 0) { + return LfgBootVoteErrorCodeNoHandler; + } + + return lfgBootVoteHandler(request); +} + +void SetLfgMaterializeProposalHandler(LfgMaterializeProposalHandler h) { + lfgMaterializeProposalHandler = h; +} + +LfgMaterializeProposalErrorCode CallLfgMaterializeProposalHandler(LfgMaterializeProposalRequest* request) { + if (lfgMaterializeProposalHandler == 0) { + return LfgMaterializeProposalErrorCodeNoHandler; + } + + return lfgMaterializeProposalHandler(request); +} diff --git a/game-server/libsidecar/lfg-api.go b/game-server/libsidecar/lfg-api.go new file mode 100644 index 0000000..0294292 --- /dev/null +++ b/game-server/libsidecar/lfg-api.go @@ -0,0 +1,237 @@ +package main + +/* +#include "lfg-api.h" +*/ +import "C" + +import ( + "unsafe" + + "github.com/walkline/ToCloud9/game-server/libsidecar/grpcapi" +) + +// TC9SetLfgPlayerLockInfoHandler sets handler for AzerothCore LFG lock info. +// +//export TC9SetLfgPlayerLockInfoHandler +func TC9SetLfgPlayerLockInfoHandler(h C.LfgPlayerLockInfoHandler) { + C.SetLfgPlayerLockInfoHandler(h) +} + +func LfgPlayerLockInfoHandler(request grpcapi.LFGPlayerLockInfoRequest) (*grpcapi.LFGPlayerLockInfoResponse, error) { + var crequest C.LfgPlayerLockInfoRequest + crequest.playerGuid = C.uint64_t(request.PlayerGUID) + crequest.dungeonEntriesSize = C.int(len(request.DungeonEntries)) + + if len(request.DungeonEntries) > 0 { + crequest.dungeonEntries = (*C.uint32_t)(C.malloc(C.size_t(len(request.DungeonEntries)) * C.size_t(unsafe.Sizeof(C.uint32_t(0))))) + if crequest.dungeonEntries == nil { + return nil, grpcapi.LFGPlayerLockInfoError(grpcapi.LFGPlayerLockInfoErrorCodeInternalError) + } + defer C.free(unsafe.Pointer(crequest.dungeonEntries)) + + for i, entry := range request.DungeonEntries { + centry := (*C.uint32_t)(unsafe.Pointer(uintptr(unsafe.Pointer(crequest.dungeonEntries)) + uintptr(i)*unsafe.Sizeof(*crequest.dungeonEntries))) + *centry = C.uint32_t(entry) + } + } + + res := C.CallLfgPlayerLockInfoHandler((*C.LfgPlayerLockInfoRequest)(unsafe.Pointer(&crequest))) + if res.errorCode != C.LfgPlayerLockInfoErrorCodeSuccess { + return nil, grpcapi.LFGPlayerLockInfoError(res.errorCode) + } + if res.locks != nil { + defer C.free(unsafe.Pointer(res.locks)) + } + if res.validDungeonEntries != nil { + defer C.free(unsafe.Pointer(res.validDungeonEntries)) + } + + locks := make([]grpcapi.LFGDungeonLock, 0, int(res.locksSize)) + for i := 0; i < int(res.locksSize); i++ { + clock := (*C.LfgDungeonLock)(unsafe.Pointer(uintptr(unsafe.Pointer(res.locks)) + uintptr(i)*unsafe.Sizeof(*res.locks))) + locks = append(locks, grpcapi.LFGDungeonLock{ + DungeonEntry: uint32(clock.dungeonEntry), + LockStatus: uint32(clock.lockStatus), + }) + } + + validDungeonEntries := make([]uint32, 0, int(res.validDungeonEntriesSize)) + for i := 0; i < int(res.validDungeonEntriesSize); i++ { + entry := (*C.uint32_t)(unsafe.Pointer(uintptr(unsafe.Pointer(res.validDungeonEntries)) + uintptr(i)*unsafe.Sizeof(*res.validDungeonEntries))) + validDungeonEntries = append(validDungeonEntries, uint32(*entry)) + } + + return &grpcapi.LFGPlayerLockInfoResponse{ + Locks: locks, + JoinResult: uint32(res.joinResult), + ValidDungeonEntries: validDungeonEntries, + }, nil +} + +// TC9SetLfgPlayerInfoHandler sets handler for AzerothCore LFG player catalog and lock info. +// +//export TC9SetLfgPlayerInfoHandler +func TC9SetLfgPlayerInfoHandler(h C.LfgPlayerInfoHandler) { + C.SetLfgPlayerInfoHandler(h) +} + +func LfgPlayerInfoHandler(request grpcapi.LFGPlayerInfoRequest) (*grpcapi.LFGPlayerInfoResponse, error) { + var crequest C.LfgPlayerInfoRequest + crequest.playerGuid = C.uint64_t(request.PlayerGUID) + + res := C.CallLfgPlayerInfoHandler((*C.LfgPlayerInfoRequest)(unsafe.Pointer(&crequest))) + if res.errorCode != C.LfgPlayerInfoErrorCodeSuccess { + return nil, grpcapi.LFGPlayerInfoError(res.errorCode) + } + if res.randomDungeons != nil { + defer C.free(unsafe.Pointer(res.randomDungeons)) + } + if res.locks != nil { + defer C.free(unsafe.Pointer(res.locks)) + } + + randomDungeons := make([]grpcapi.LFGRandomDungeonInfo, 0, int(res.randomDungeonsSize)) + for i := 0; i < int(res.randomDungeonsSize); i++ { + cdungeon := (*C.LfgRandomDungeonInfo)(unsafe.Pointer(uintptr(unsafe.Pointer(res.randomDungeons)) + uintptr(i)*unsafe.Sizeof(*res.randomDungeons))) + if cdungeon.rewardItems != nil { + defer C.free(unsafe.Pointer(cdungeon.rewardItems)) + } + + rewardItems := make([]grpcapi.LFGRewardItem, 0, int(cdungeon.rewardItemsSize)) + for j := 0; j < int(cdungeon.rewardItemsSize); j++ { + citem := (*C.LfgRewardItem)(unsafe.Pointer(uintptr(unsafe.Pointer(cdungeon.rewardItems)) + uintptr(j)*unsafe.Sizeof(*cdungeon.rewardItems))) + rewardItems = append(rewardItems, grpcapi.LFGRewardItem{ + ItemID: uint32(citem.itemId), + DisplayID: uint32(citem.displayId), + Count: uint32(citem.count), + }) + } + + randomDungeons = append(randomDungeons, grpcapi.LFGRandomDungeonInfo{ + DungeonEntry: uint32(cdungeon.dungeonEntry), + Done: cdungeon.done != 0, + RewardMoney: uint32(cdungeon.rewardMoney), + RewardXP: uint32(cdungeon.rewardXP), + RewardUnknown1: uint32(cdungeon.rewardUnknown1), + RewardUnknown2: uint32(cdungeon.rewardUnknown2), + RewardItems: rewardItems, + }) + } + + locks := make([]grpcapi.LFGDungeonLock, 0, int(res.locksSize)) + for i := 0; i < int(res.locksSize); i++ { + clock := (*C.LfgDungeonLock)(unsafe.Pointer(uintptr(unsafe.Pointer(res.locks)) + uintptr(i)*unsafe.Sizeof(*res.locks))) + locks = append(locks, grpcapi.LFGDungeonLock{ + DungeonEntry: uint32(clock.dungeonEntry), + LockStatus: uint32(clock.lockStatus), + }) + } + + return &grpcapi.LFGPlayerInfoResponse{ + RandomDungeons: randomDungeons, + Locks: locks, + }, nil +} + +// TC9SetLfgDungeonInfoHandler sets handler for AzerothCore LFG dungeon metadata. +// +//export TC9SetLfgDungeonInfoHandler +func TC9SetLfgDungeonInfoHandler(h C.LfgDungeonInfoHandler) { + C.SetLfgDungeonInfoHandler(h) +} + +func LfgDungeonInfoHandler(request grpcapi.LFGDungeonInfoRequest) (*grpcapi.LFGDungeonInfoResponse, error) { + var crequest C.LfgDungeonInfoRequest + crequest.dungeonEntry = C.uint32_t(request.DungeonEntry) + + res := C.CallLfgDungeonInfoHandler((*C.LfgDungeonInfoRequest)(unsafe.Pointer(&crequest))) + if res.errorCode != C.LfgDungeonInfoErrorCodeSuccess { + return nil, grpcapi.LFGDungeonInfoError(res.errorCode) + } + + return &grpcapi.LFGDungeonInfoResponse{ + DungeonEntry: uint32(res.dungeonEntry), + DungeonID: uint32(res.dungeonId), + MapID: uint32(res.mapId), + TypeID: uint32(res.typeId), + Difficulty: uint32(res.difficulty), + }, nil +} + +// TC9SetLfgTeleportPlayerHandler sets handler for AzerothCore LFG teleport in/out. +// +//export TC9SetLfgTeleportPlayerHandler +func TC9SetLfgTeleportPlayerHandler(h C.LfgTeleportPlayerHandler) { + C.SetLfgTeleportPlayerHandler(h) +} + +func LfgTeleportPlayerHandler(request grpcapi.LFGTeleportPlayerRequest) error { + var crequest C.LfgTeleportPlayerRequest + crequest.playerGuid = C.uint64_t(request.PlayerGUID) + crequest.out = C.uint8_t(boolToUint8(request.Out)) + crequest.dungeonEntry = C.uint32_t(request.DungeonEntry) + + res := C.CallLfgTeleportPlayerHandler((*C.LfgTeleportPlayerRequest)(unsafe.Pointer(&crequest))) + if res != C.LfgTeleportPlayerErrorCodeSuccess { + return grpcapi.LFGTeleportPlayerError(int(res)) + } + return nil +} + +// TC9SetLfgBootVoteHandler sets handler for AzerothCore LFG boot vote replies. +// +//export TC9SetLfgBootVoteHandler +func TC9SetLfgBootVoteHandler(h C.LfgBootVoteHandler) { + C.SetLfgBootVoteHandler(h) +} + +func LfgBootVoteHandler(request grpcapi.LFGSetBootVoteRequest) error { + var crequest C.LfgBootVoteRequest + crequest.playerGuid = C.uint64_t(request.PlayerGUID) + crequest.agree = C.uint8_t(boolToUint8(request.Agree)) + + res := C.CallLfgBootVoteHandler((*C.LfgBootVoteRequest)(unsafe.Pointer(&crequest))) + if res != C.LfgBootVoteErrorCodeSuccess { + return grpcapi.LFGSetBootVoteError(int(res)) + } + return nil +} + +// TC9SetLfgMaterializeProposalHandler sets handler for AzerothCore LFG proposal materialization. +// +//export TC9SetLfgMaterializeProposalHandler +func TC9SetLfgMaterializeProposalHandler(h C.LfgMaterializeProposalHandler) { + C.SetLfgMaterializeProposalHandler(h) +} + +func LfgMaterializeProposalHandler(request grpcapi.LFGMaterializeProposalRequest) error { + var crequest C.LfgMaterializeProposalRequest + crequest.realmId = C.uint32_t(request.RealmID) + crequest.proposalId = C.uint32_t(request.ProposalID) + crequest.dungeonEntry = C.uint32_t(request.DungeonEntry) + crequest.leaderGuid = C.uint64_t(request.LeaderGUID) + crequest.membersSize = C.int(len(request.Members)) + + if len(request.Members) > 0 { + crequest.members = (*C.LfgMaterializeProposalMember)(C.malloc(C.size_t(len(request.Members)) * C.size_t(unsafe.Sizeof(C.LfgMaterializeProposalMember{})))) + if crequest.members == nil { + return grpcapi.LFGMaterializeProposalError(grpcapi.LFGMaterializeProposalErrorCodeInternalError) + } + defer C.free(unsafe.Pointer(crequest.members)) + + for i, member := range request.Members { + cmember := (*C.LfgMaterializeProposalMember)(unsafe.Pointer(uintptr(unsafe.Pointer(crequest.members)) + uintptr(i)*unsafe.Sizeof(*crequest.members))) + cmember.playerGuid = C.uint64_t(member.PlayerGUID) + cmember.selectedRoles = C.uint8_t(member.SelectedRoles) + cmember.assignedRole = C.uint8_t(member.AssignedRole) + cmember.queueLeaderGuid = C.uint64_t(member.QueueLeaderGUID) + } + } + + res := C.CallLfgMaterializeProposalHandler((*C.LfgMaterializeProposalRequest)(unsafe.Pointer(&crequest))) + if res != C.LfgMaterializeProposalErrorCodeSuccess { + return grpcapi.LFGMaterializeProposalError(int(res)) + } + return nil +} diff --git a/game-server/libsidecar/lfg-api.h b/game-server/libsidecar/lfg-api.h new file mode 100644 index 0000000..d132442 --- /dev/null +++ b/game-server/libsidecar/lfg-api.h @@ -0,0 +1,163 @@ +#ifndef __LFG_API__ +#define __LFG_API__ + +#include +#include + +typedef enum LfgPlayerLockInfoErrorCode { + LfgPlayerLockInfoErrorCodeSuccess = 0, + LfgPlayerLockInfoErrorCodeNoHandler = 1, + LfgPlayerLockInfoErrorCodePlayerNotFound = 2, + LfgPlayerLockInfoErrorCodeInternalError = 3, +} LfgPlayerLockInfoErrorCode; + +typedef struct { + uint32_t dungeonEntry; + uint32_t lockStatus; +} LfgDungeonLock; + +typedef struct { + uint64_t playerGuid; + uint32_t* dungeonEntries; + int dungeonEntriesSize; +} LfgPlayerLockInfoRequest; + +typedef struct { + int errorCode; + LfgDungeonLock* locks; + int locksSize; + uint32_t joinResult; + uint32_t* validDungeonEntries; + int validDungeonEntriesSize; +} LfgPlayerLockInfoResponse; + +typedef enum LfgPlayerInfoErrorCode { + LfgPlayerInfoErrorCodeSuccess = 0, + LfgPlayerInfoErrorCodeNoHandler = 1, + LfgPlayerInfoErrorCodePlayerNotFound = 2, + LfgPlayerInfoErrorCodeInternalError = 3, +} LfgPlayerInfoErrorCode; + +typedef struct { + uint32_t itemId; + uint32_t displayId; + uint32_t count; +} LfgRewardItem; + +typedef struct { + uint32_t dungeonEntry; + uint8_t done; + uint32_t rewardMoney; + uint32_t rewardXP; + uint32_t rewardUnknown1; + uint32_t rewardUnknown2; + LfgRewardItem* rewardItems; + int rewardItemsSize; +} LfgRandomDungeonInfo; + +typedef struct { + uint64_t playerGuid; +} LfgPlayerInfoRequest; + +typedef struct { + int errorCode; + LfgRandomDungeonInfo* randomDungeons; + int randomDungeonsSize; + LfgDungeonLock* locks; + int locksSize; +} LfgPlayerInfoResponse; + +typedef enum LfgDungeonInfoErrorCode { + LfgDungeonInfoErrorCodeSuccess = 0, + LfgDungeonInfoErrorCodeNoHandler = 1, + LfgDungeonInfoErrorCodeDungeonNotFound = 2, + LfgDungeonInfoErrorCodeInternalError = 3, +} LfgDungeonInfoErrorCode; + +typedef struct { + uint32_t dungeonEntry; +} LfgDungeonInfoRequest; + +typedef struct { + int errorCode; + uint32_t dungeonEntry; + uint32_t dungeonId; + uint32_t mapId; + uint32_t typeId; + uint32_t difficulty; +} LfgDungeonInfoResponse; + +typedef enum LfgTeleportPlayerErrorCode { + LfgTeleportPlayerErrorCodeSuccess = 0, + LfgTeleportPlayerErrorCodeNoHandler = 1, + LfgTeleportPlayerErrorCodePlayerNotFound = 2, + LfgTeleportPlayerErrorCodeInternalError = 3, +} LfgTeleportPlayerErrorCode; + +typedef struct { + uint64_t playerGuid; + uint8_t out; + uint32_t dungeonEntry; +} LfgTeleportPlayerRequest; + +typedef enum LfgBootVoteErrorCode { + LfgBootVoteErrorCodeSuccess = 0, + LfgBootVoteErrorCodeNoHandler = 1, + LfgBootVoteErrorCodePlayerNotFound = 2, + LfgBootVoteErrorCodeInternalError = 3, +} LfgBootVoteErrorCode; + +typedef struct { + uint64_t playerGuid; + uint8_t agree; +} LfgBootVoteRequest; + +typedef enum LfgMaterializeProposalErrorCode { + LfgMaterializeProposalErrorCodeSuccess = 0, + LfgMaterializeProposalErrorCodeNoHandler = 1, + LfgMaterializeProposalErrorCodeDungeonNotFound = 2, + LfgMaterializeProposalErrorCodeNoLocalPlayer = 3, + LfgMaterializeProposalErrorCodeInternalError = 4, +} LfgMaterializeProposalErrorCode; + +typedef struct { + uint64_t playerGuid; + uint8_t selectedRoles; + uint8_t assignedRole; + uint64_t queueLeaderGuid; +} LfgMaterializeProposalMember; + +typedef struct { + uint32_t realmId; + uint32_t proposalId; + uint32_t dungeonEntry; + uint64_t leaderGuid; + LfgMaterializeProposalMember* members; + int membersSize; +} LfgMaterializeProposalRequest; + +typedef LfgPlayerLockInfoResponse (*LfgPlayerLockInfoHandler)(LfgPlayerLockInfoRequest* request); +void SetLfgPlayerLockInfoHandler(LfgPlayerLockInfoHandler h); +LfgPlayerLockInfoResponse CallLfgPlayerLockInfoHandler(LfgPlayerLockInfoRequest* request); + +typedef LfgPlayerInfoResponse (*LfgPlayerInfoHandler)(LfgPlayerInfoRequest* request); +void SetLfgPlayerInfoHandler(LfgPlayerInfoHandler h); +LfgPlayerInfoResponse CallLfgPlayerInfoHandler(LfgPlayerInfoRequest* request); + +typedef LfgDungeonInfoResponse (*LfgDungeonInfoHandler)(LfgDungeonInfoRequest* request); +void SetLfgDungeonInfoHandler(LfgDungeonInfoHandler h); +LfgDungeonInfoResponse CallLfgDungeonInfoHandler(LfgDungeonInfoRequest* request); + +typedef LfgTeleportPlayerErrorCode (*LfgTeleportPlayerHandler)(LfgTeleportPlayerRequest* request); +void SetLfgTeleportPlayerHandler(LfgTeleportPlayerHandler h); +LfgTeleportPlayerErrorCode CallLfgTeleportPlayerHandler(LfgTeleportPlayerRequest* request); + +typedef LfgBootVoteErrorCode (*LfgBootVoteHandler)(LfgBootVoteRequest* request); +void SetLfgBootVoteHandler(LfgBootVoteHandler h); +LfgBootVoteErrorCode CallLfgBootVoteHandler(LfgBootVoteRequest* request); + +typedef LfgMaterializeProposalErrorCode (*LfgMaterializeProposalHandler)(LfgMaterializeProposalRequest* request); +void SetLfgMaterializeProposalHandler(LfgMaterializeProposalHandler h); +LfgMaterializeProposalErrorCode CallLfgMaterializeProposalHandler(LfgMaterializeProposalRequest* request); + +#endif diff --git a/game-server/libsidecar/lib.go b/game-server/libsidecar/lib.go index bee7f9e..568c740 100644 --- a/game-server/libsidecar/lib.go +++ b/game-server/libsidecar/lib.go @@ -21,6 +21,13 @@ import ( const ( libVer = "0.0.1" matchmakingSupportedVer + + mapsLoadedAckMaxAttempts = 5 +) + +var ( + mapsLoadedAckRetryDelay = time.Millisecond * 500 + mapsLoadedAckAttemptTimeout = time.Second * 10 ) var ( @@ -54,7 +61,7 @@ func initLib(realmID uint32) (*config.Config, healthandmetrics.Server, ShutdownF healthandmetrics.EnableActiveConnectionsMetrics() healthandmetrics.EnableDelayMetrics() - healthCheckServer := healthandmetrics.NewServer(cfg.HealthCheckPort, monitoringHttpHandler()) + healthCheckServer := healthandmetrics.NewServerWithHealthProbe(cfg.HealthCheckPort, monitoringHttpHandler(), worldLoopHealthProbe) go healthCheckServer.ListenAndServe() natsConsumer := SetupEventsListener(nc, realmID, log) @@ -63,6 +70,10 @@ func initLib(realmID uint32) (*config.Config, healthandmetrics.Server, ShutdownF guidConn := SetupGuidServiceConnection(cfg) + groupConn := SetupGroupServiceConnection(cfg) + + characterConn := SetupCharacterServiceConnection(cfg) + SetupMatchmakingConnection(ctx, cfg) grpcListener, grpcServer := SetupGRPCService(cfg) @@ -103,6 +114,14 @@ func initLib(realmID uint32) (*config.Config, healthandmetrics.Server, ShutdownF log.Fatal().Err(err).Msg("failed to close guid service connection") } + if err = groupConn.Close(); err != nil { + log.Fatal().Err(err).Msg("failed to close group service connection") + } + + if err = characterConn.Close(); err != nil { + log.Fatal().Err(err).Msg("failed to close character service connection") + } + log.Info().Msg("👍 Sidecar successfully stopped.") } } @@ -244,13 +263,85 @@ func TC9ReadyToAcceptPlayersFromMaps(maps *C.uint32_t, mapsLen C.int) { pItr = (*C.uint32_t)(unsafe.Pointer(uintptr(unsafe.Pointer(pItr)) + uintptr(unsafe.Sizeof(C.uint32_t(0))))) } go func() { - _, err := registryClient.GameServerMapsLoaded(context.Background(), &pb.GameServerMapsLoadedRequest{ - Api: libVer, - GameServerID: AssignedGameServerID, - MapsLoaded: mapsSlice, - }) + err := sendGameServerMapsLoadedWithRetry( + context.Background(), + AssignedGameServerID, + mapsSlice, + mapsLoadedAckMaxAttempts, + mapsLoadedAckRetryDelay, + mapsLoadedAckAttemptTimeout, + func(ctx context.Context, req *pb.GameServerMapsLoadedRequest) error { + _, err := registryClient.GameServerMapsLoaded(ctx, req) + return err + }, + ) if err != nil { - log.Err(err).Msg("can't mark maps as loaded failed") + log.Err(err).Int("attempts", mapsLoadedAckMaxAttempts).Msg("can't mark maps as loaded after retries") } }() } + +func sendGameServerMapsLoadedWithRetry( + ctx context.Context, + serverID string, + maps []uint32, + maxAttempts int, + retryDelay time.Duration, + attemptTimeout time.Duration, + send func(context.Context, *pb.GameServerMapsLoadedRequest) error, +) error { + if maxAttempts < 1 { + maxAttempts = 1 + } + + req := &pb.GameServerMapsLoadedRequest{ + Api: libVer, + GameServerID: serverID, + MapsLoaded: append([]uint32(nil), maps...), + } + + var err error + delay := retryDelay + for attempt := 1; attempt <= maxAttempts; attempt++ { + attemptCtx := ctx + cancel := func() {} + if attemptTimeout > 0 { + attemptCtx, cancel = context.WithTimeout(ctx, attemptTimeout) + } + + err = send(attemptCtx, req) + cancel() + if err == nil { + if attempt > 1 { + log.Info().Int("attempt", attempt).Str("serverID", serverID).Int("maps", len(maps)).Msg("marked maps as loaded after retry") + } + return nil + } + + if attempt == maxAttempts { + return err + } + + log.Warn().Err(err).Int("attempt", attempt).Str("serverID", serverID).Int("maps", len(maps)).Msg("failed to mark maps as loaded; retrying") + if delay <= 0 { + continue + } + + timer := time.NewTimer(delay) + select { + case <-ctx.Done(): + timer.Stop() + return ctx.Err() + case <-timer.C: + } + + if delay < time.Second*5 { + delay *= 2 + if delay > time.Second*5 { + delay = time.Second * 5 + } + } + } + + return err +} diff --git a/game-server/libsidecar/libsidecar.h b/game-server/libsidecar/libsidecar.h new file mode 100644 index 0000000..24860ef --- /dev/null +++ b/game-server/libsidecar/libsidecar.h @@ -0,0 +1,347 @@ +/* Code generated by cmd/cgo; DO NOT EDIT. */ + +/* package github.com/walkline/ToCloud9/game-server/libsidecar */ + + +#line 1 "cgo-builtin-export-prolog" + +#include + +#ifndef GO_CGO_EXPORT_PROLOGUE_H +#define GO_CGO_EXPORT_PROLOGUE_H + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef struct { const char *p; ptrdiff_t n; } _GoString_; +#endif + +#endif + +/* Start of preamble from import "C" comments. */ + + +#line 3 "battleground-api.go" + +#include "battleground-api.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "events-group.go" + +#include "events-group.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "events-guilds.go" + +#include "events-guild.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "events-servers-registry.go" + +#include "events-servers-registry.h" + +#line 1 "cgo-generated-wrapper" + + + +#line 3 "guild-api.go" + +#include "guild-api.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "lfg-api.go" + +#include "lfg-api.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "lib.go" + +#include + +#line 1 "cgo-generated-wrapper" + +#line 3 "matchmaking.go" + +#include + +#line 1 "cgo-generated-wrapper" + +#line 3 "monitoring.go" + +#include "monitoring.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "player-interactions-api.go" + +#include "player-interactions-api.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "player-items-api.go" + +#include "player-items-api.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "player-money-api.go" + +#include "player-money-api.h" + +#line 1 "cgo-generated-wrapper" + + +/* End of preamble from import "C" comments. */ + + +/* Start of boilerplate cgo prologue. */ +#line 1 "cgo-gcc-export-header-prolog" + +#ifndef GO_CGO_PROLOGUE_H +#define GO_CGO_PROLOGUE_H + +typedef signed char GoInt8; +typedef unsigned char GoUint8; +typedef short GoInt16; +typedef unsigned short GoUint16; +typedef int GoInt32; +typedef unsigned int GoUint32; +typedef long long GoInt64; +typedef unsigned long long GoUint64; +typedef GoInt64 GoInt; +typedef GoUint64 GoUint; +typedef size_t GoUintptr; +typedef float GoFloat32; +typedef double GoFloat64; +#ifdef _MSC_VER +#include +typedef _Fcomplex GoComplex64; +typedef _Dcomplex GoComplex128; +#else +typedef float _Complex GoComplex64; +typedef double _Complex GoComplex128; +#endif + +/* + static assertion to make sure the file is being used on architecture + at least with matching size of GoInt. +*/ +typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef _GoString_ GoString; +#endif +typedef void *GoMap; +typedef void *GoChan; +typedef struct { void *t; void *v; } GoInterface; +typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; + +#endif + +/* End of boilerplate cgo prologue. */ + +#ifdef __cplusplus +extern "C" { +#endif + + +// TC9SetBattlegroundStartHandler sets handler for starting battleground. +// +extern void TC9SetBattlegroundStartHandler(BattlegroundStartHandler h); + +// TC9SetBattlegroundAddPlayersHandler sets handler for adding players to battleground. +// +extern void TC9SetBattlegroundAddPlayersHandler(BattlegroundAddPlayersHandler h); + +// TC9SetCanPlayerJoinBattlegroundQueueHandler sets handler for checking if player can join to battleground queue. +// +extern void TC9SetCanPlayerJoinBattlegroundQueueHandler(CanPlayerJoinBattlegroundQueueHandler h); + +// TC9SetCanPlayerTeleportToBattlegroundHandler sets handler for checking if player can teleport to battleground. +// +extern void TC9SetCanPlayerTeleportToBattlegroundHandler(CanPlayerTeleportToBattlegroundHandler h); + +// TC9SetOnGroupCreatedHook sets hook for group created event. +// +extern void TC9SetOnGroupCreatedHook(OnGroupCreatedHook h); + +// TC9SetOnGroupMemberAddedHook sets hook for member added event. +// +extern void TC9SetOnGroupMemberAddedHook(OnGroupMemberAddedHook h); + +// TC9SetOnGroupMemberRemovedHook sets hook for member left/kicked event. +// +extern void TC9SetOnGroupMemberRemovedHook(OnGroupMemberRemovedHook h); + +// TC9SetOnGroupLeaderChangedHook sets hook for leader changed event. +// +extern void TC9SetOnGroupLeaderChangedHook(OnGroupLeaderChangedHook h); + +// TC9SetOnGroupDisbandedHook sets hook for group disbanded event. +// +extern void TC9SetOnGroupDisbandedHook(OnGroupDisbandedHook h); + +// TC9SetOnGroupLootTypeChangedHook sets hook for group loot type changed event. +// +extern void TC9SetOnGroupLootTypeChangedHook(OnGroupLootTypeChangedHook h); + +// TC9SetOnGroupDungeonDifficultyChangedHook sets hook for group dungeon difficulty changed event. +// +extern void TC9SetOnGroupDungeonDifficultyChangedHook(OnGroupDungeonDifficultyChangedHook h); + +// TC9SetOnGroupRaidDifficultyChangedHook sets hook for group raid difficulty changed event. +// +extern void TC9SetOnGroupRaidDifficultyChangedHook(OnGroupRaidDifficultyChangedHook h); + +// TC9SetOnGroupConvertedToRaidHook sets hook for group converted to raid event. +// +extern void TC9SetOnGroupConvertedToRaidHook(OnGroupConvertedToRaidHook h); +extern void TC9SetOnGroupReadyCheckStartedHook(OnGroupReadyCheckStartedHook h); +extern void TC9SetOnGroupReadyCheckMemberStateHook(OnGroupReadyCheckMemberStateHook h); +extern void TC9SetOnGroupReadyCheckFinishedHook(OnGroupReadyCheckFinishedHook h); +extern void TC9SetOnGroupMemberSubGroupChangedHook(OnGroupMemberSubGroupChangedHook h); +extern void TC9SetOnGroupMemberFlagsChangedHook(OnGroupMemberFlagsChangedHook h); +extern void TC9SetOnGroupMemberStateChangedHook(OnGroupMemberStateChangedHook h); +extern void TC9SetOnGroupInstanceResetRequestHook(OnGroupInstanceResetRequestHook h); +extern void TC9SetOnGroupInstanceBindExtensionRequestHook(OnGroupInstanceBindExtensionRequestHook h); +extern void TC9UpdateGroupMemberState(uint64_t memberGuid, uint8_t online, uint8_t level, uint8_t playerClass, uint32_t zoneId, uint32_t mapId, uint32_t health, uint32_t maxHealth, uint8_t powerType, uint32_t power, uint32_t maxPower, uint32_t instanceID); +extern void TC9ConfirmLfgDungeonRouteEntered(uint64_t memberGuid, uint32_t mapId, uint8_t difficulty, uint32_t instanceID); +extern void TC9StartGroupReadyCheck(uint64_t leaderGuid, uint32_t durationMs); +extern void TC9SetGroupReadyCheckMemberState(uint64_t memberGuid, uint8_t state); +extern void TC9FinishGroupReadyCheck(uint64_t playerGuid); +extern void TC9ChangeGroupMemberSubGroup(uint64_t updaterGuid, uint64_t memberGuid, uint8_t subGroup); +extern void TC9SetGroupMemberFlags(uint64_t updaterGuid, uint64_t memberGuid, uint8_t flags, uint8_t roles); +extern void TC9RegisterMaterializedLfgGroup(uint32_t groupGuid, uint64_t leaderGuid, uint8_t groupType, uint8_t difficulty, uint8_t raidDifficulty, GroupMaterializedLfgMember* members, uint8_t membersSize); + +// TC9SetOnGuildMemberAddedHook sets hook for guild member added event. +// +extern void TC9SetOnGuildMemberAddedHook(OnGuildMemberAddedHook h); + +// TC9SetOnGuildMemberRemovedHook sets hook for guild member removed (kicked) event. +// +extern void TC9SetOnGuildMemberRemovedHook(OnGuildMemberRemovedHook h); + +// TC9SetOnGuildMemberLeftHook sets hook for guild member left event. +// +extern void TC9SetOnGuildMemberLeftHook(OnGuildMemberLeftHook h); + +// TC9SetOnMapsReassignedHook sets hook for maps reassigning by servers registry event. +// +extern void TC9SetOnMapsReassignedHook(OnMapsReassignedHook h); + +// TC9ProcessEventsHooks calls all events hooks. +// +extern void TC9ProcessEventsHooks(); + +// TC9GetNextAvailableCharacterGuid returns next available characters GUID. Thread unsafe. +// +extern GoUint64 TC9GetNextAvailableCharacterGuid(GoInt realmID); + +// TC9GetNextAvailableItemGuid returns next available item GUID. Thread unsafe. +// +extern GoUint64 TC9GetNextAvailableItemGuid(GoInt realmID); + +// TC9GetNextAvailableInstanceGuid returns next available dungeon/raid instance GUID. Thread unsafe. +// +extern GoUint64 TC9GetNextAvailableInstanceGuid(GoInt realmID); + +// TC9SetGuildCreateHandler sets handler for creating guilds. +// +extern void TC9SetGuildCreateHandler(GuildCreateHandler h); + +// TC9SetLfgPlayerLockInfoHandler sets handler for AzerothCore LFG lock info. +// +extern void TC9SetLfgPlayerLockInfoHandler(LfgPlayerLockInfoHandler h); + +// TC9SetLfgPlayerInfoHandler sets handler for AzerothCore LFG player catalog and lock info. +// +extern void TC9SetLfgPlayerInfoHandler(LfgPlayerInfoHandler h); + +// TC9SetLfgDungeonInfoHandler sets handler for AzerothCore LFG dungeon metadata. +// +extern void TC9SetLfgDungeonInfoHandler(LfgDungeonInfoHandler h); + +// TC9SetLfgTeleportPlayerHandler sets handler for AzerothCore LFG teleport in/out. +// +extern void TC9SetLfgTeleportPlayerHandler(LfgTeleportPlayerHandler h); + +// TC9SetLfgBootVoteHandler sets handler for AzerothCore LFG boot vote replies. +// +extern void TC9SetLfgBootVoteHandler(LfgBootVoteHandler h); + +// TC9SetLfgMaterializeProposalHandler sets handler for AzerothCore LFG proposal materialization. +// +extern void TC9SetLfgMaterializeProposalHandler(LfgMaterializeProposalHandler h); + +// TC9InitLib inits lib by starting services like grpc and healthcheck. +// Adds game server to the servers registry that will make this server visible for gateway. +// +extern void TC9InitLib(GoUint16 port, GoUint32 realmID, GoUint8 isCrossRealm, char* availableMaps, uint32_t** assignedMaps, int* assignedMapsSize); + +// TC9GracefulShutdown gracefully stops all running services. +// +extern void TC9GracefulShutdown(); + +// TC9ProcessGRPCOrHTTPRequests calls all grpc or http handlers in queue. +// +extern void TC9ProcessGRPCOrHTTPRequests(); + +// TC9ReadyToAcceptPlayersFromMaps notifies servers registry that this server +// loaded maps related data and ready to accept players from those maps. +// +extern void TC9ReadyToAcceptPlayersFromMaps(uint32_t* maps, int mapsLen); + +// TC9PlayerLeftBattleground notifies matchmaking server that player left battleground +// +extern void TC9PlayerLeftBattleground(uint64_t playerGUID, uint32_t realmID, uint32_t instanceID); + +// TC9BattlegroundStatusChanged notifies matchmaking server that battleground status changed +// +extern void TC9BattlegroundStatusChanged(uint32_t instanceID, uint8_t status); + +// TC9CompleteLfgDungeon notifies matchmaking server that AzerothCore completed an LFG dungeon. +// +extern void TC9CompleteLfgDungeon(uint32_t completedDungeonEntry, uint32_t selectedDungeonEntry, uint64_t* players, int playersSize); + +// TC9SetMonitoringDataCollectorHandler sets handler for getting data to handle monitoring request. +// +extern void TC9SetMonitoringDataCollectorHandler(MonitoringDataCollectorHandler h); + +// TC9SetCanPlayerInteractWithNPCAndFlagsHandler sets handler for can player interact with NPC and with given NPC flags request. +// +extern void TC9SetCanPlayerInteractWithNPCAndFlagsHandler(CanPlayerInteractWithNPCAndFlagsHandler h); + +// TC9SetCanPlayerInteractWithGOAndTypeHandler sets handler for can player interact with GameObject and with given object type request. +// +extern void TC9SetCanPlayerInteractWithGOAndTypeHandler(CanPlayerInteractWithGOAndTypeHandler h); + +// TC9SetGetPlayerItemsByGuidsHandler sets handler for getting players item by guids request. +// +extern void TC9SetGetPlayerItemsByGuidsHandler(GetPlayerItemsByGuidsHandler h); + +// TC9SetTakePlayerItemByPosHandler sets handler for removing one player item by bag/slot. +// +extern void TC9SetTakePlayerItemByPosHandler(TakePlayerItemByPosHandler h); + +// TC9SetRemoveItemsWithGuidsFromPlayerHandler sets handler for removing items by guids from player request. +// +extern void TC9SetRemoveItemsWithGuidsFromPlayerHandler(RemoveItemsWithGuidsFromPlayerHandler h); + +// TC9SetAddExistingItemToPlayerHandler sets handler for adding item to player request. +// +extern void TC9SetAddExistingItemToPlayerHandler(AddExistingItemToPlayerHandler h); + +// TC9SetGetMoneyForPlayerHandler sets handler for getting money for player request. +// +extern void TC9SetGetMoneyForPlayerHandler(GetMoneyForPlayerHandler h); + +// TC9SetModifyMoneyForPlayerHandler sets handler for modify money for given player request. +// +extern void TC9SetModifyMoneyForPlayerHandler(ModifyMoneyForPlayerHandler h); + +#ifdef __cplusplus +} +#endif diff --git a/game-server/libsidecar/matchmaking.go b/game-server/libsidecar/matchmaking.go index 9621415..2fa6450 100644 --- a/game-server/libsidecar/matchmaking.go +++ b/game-server/libsidecar/matchmaking.go @@ -6,16 +6,20 @@ package main import "C" import ( "context" + "unsafe" "github.com/rs/zerolog/log" "google.golang.org/grpc" "github.com/walkline/ToCloud9/game-server/libsidecar/config" "github.com/walkline/ToCloud9/gen/matchmaking/pb" + "github.com/walkline/ToCloud9/shared/wow/guid" ) var playerLeftBattlegroundRequestsChan chan matchmakingPlayerLeftBattlegroundRequest var battlegroundStatusChangedRequestsChan chan matchmakingBattlegroundStatusChangedRequest +var lfgDungeonCompletedRequestsChan chan matchmakingLfgDungeonCompletedRequest +var matchmakingServiceClient pb.MatchmakingServiceClient type matchmakingPlayerLeftBattlegroundRequest struct { player uint64 @@ -28,16 +32,23 @@ type matchmakingBattlegroundStatusChangedRequest struct { status uint8 } +type matchmakingLfgDungeonCompletedRequest struct { + completedDungeonEntry uint32 + selectedDungeonEntry uint32 + players []uint64 +} + func SetupMatchmakingConnection(ctx context.Context, cfg *config.Config) { conn, err := grpc.Dial(cfg.MatchmakingServiceAddress, grpc.WithInsecure()) if err != nil { log.Fatal().Err(err).Msg("can't connect to the matchmaking server") } - matchmakingClient := pb.NewMatchmakingServiceClient(conn) + matchmakingServiceClient = pb.NewMatchmakingServiceClient(conn) playerLeftBattlegroundRequestsChan = make(chan matchmakingPlayerLeftBattlegroundRequest, 100) battlegroundStatusChangedRequestsChan = make(chan matchmakingBattlegroundStatusChangedRequest, 50) + lfgDungeonCompletedRequestsChan = make(chan matchmakingLfgDungeonCompletedRequest, 50) const processorsCount = 4 for i := 0; i < processorsCount; i++ { @@ -45,7 +56,7 @@ func SetupMatchmakingConnection(ctx context.Context, cfg *config.Config) { for { select { case r := <-playerLeftBattlegroundRequestsChan: - _, err := matchmakingClient.PlayerLeftBattleground(ctx, &pb.PlayerLeftBattlegroundRequest{ + _, err := matchmakingServiceClient.PlayerLeftBattleground(ctx, &pb.PlayerLeftBattlegroundRequest{ Api: matchmakingSupportedVer, RealmID: r.realmID, PlayerGUID: r.player, @@ -56,7 +67,7 @@ func SetupMatchmakingConnection(ctx context.Context, cfg *config.Config) { log.Err(err).Msg("PlayerLeftBattleground failed") } case r := <-battlegroundStatusChangedRequestsChan: - _, err := matchmakingClient.BattlegroundStatusChanged(ctx, &pb.BattlegroundStatusChangedRequest{ + _, err := matchmakingServiceClient.BattlegroundStatusChanged(ctx, &pb.BattlegroundStatusChangedRequest{ Api: matchmakingSupportedVer, RealmID: RealmID, Status: pb.BattlegroundStatusChangedRequest_Status(r.status), @@ -66,6 +77,16 @@ func SetupMatchmakingConnection(ctx context.Context, cfg *config.Config) { if err != nil { log.Err(err).Msg("BattlegroundStatusChanged failed") } + case r := <-lfgDungeonCompletedRequestsChan: + _, err := matchmakingServiceClient.CompleteLfgDungeon(ctx, &pb.CompleteLfgDungeonRequest{ + Api: matchmakingSupportedVer, + CompletedDungeonEntry: r.completedDungeonEntry, + SelectedDungeonEntry: r.selectedDungeonEntry, + Players: lfgCompletedPlayersForRequest(r.players), + }) + if err != nil { + log.Err(err).Msg("CompleteLfgDungeon failed") + } case <-ctx.Done(): return } @@ -78,6 +99,7 @@ func SetupMatchmakingConnection(ctx context.Context, cfg *config.Config) { close(playerLeftBattlegroundRequestsChan) close(battlegroundStatusChangedRequestsChan) + close(lfgDungeonCompletedRequestsChan) if err := conn.Close(); err != nil { log.Fatal().Err(err).Msg("can't close matchmaking connection") @@ -105,3 +127,41 @@ func TC9BattlegroundStatusChanged(instanceID C.uint32_t, status C.uint8_t) { status: uint8(status), } } + +// TC9CompleteLfgDungeon notifies matchmaking server that AzerothCore completed an LFG dungeon. +// +//export TC9CompleteLfgDungeon +func TC9CompleteLfgDungeon(completedDungeonEntry C.uint32_t, selectedDungeonEntry C.uint32_t, players *C.uint64_t, playersSize C.int) { + lfgDungeonCompletedRequestsChan <- matchmakingLfgDungeonCompletedRequest{ + completedDungeonEntry: uint32(completedDungeonEntry), + selectedDungeonEntry: uint32(selectedDungeonEntry), + players: uint64SliceFromC(players, playersSize), + } +} + +func uint64SliceFromC(values *C.uint64_t, size C.int) []uint64 { + if values == nil || size <= 0 { + return nil + } + result := make([]uint64, 0, int(size)) + stride := unsafe.Sizeof(*values) + for i := 0; i < int(size); i++ { + value := *(*C.uint64_t)(unsafe.Pointer(uintptr(unsafe.Pointer(values)) + uintptr(i)*stride)) + if value == 0 { + continue + } + result = append(result, uint64(value)) + } + return result +} + +func lfgCompletedPlayersForRequest(players []uint64) []*pb.CompleteLfgDungeonPlayer { + result := make([]*pb.CompleteLfgDungeonPlayer, 0, len(players)) + for _, player := range players { + result = append(result, &pb.CompleteLfgDungeonPlayer{ + RealmID: guid.PlayerRealmIDOrDefault(RealmID, player), + PlayerGUID: guid.PlayerLowGUID(player), + }) + } + return result +} diff --git a/game-server/libsidecar/monitoring.go b/game-server/libsidecar/monitoring.go index e339aad..e12ad1a 100644 --- a/game-server/libsidecar/monitoring.go +++ b/game-server/libsidecar/monitoring.go @@ -8,6 +8,7 @@ import "C" import ( "context" "errors" + "fmt" "net/http" "time" @@ -17,6 +18,17 @@ import ( "github.com/walkline/ToCloud9/shared/healthandmetrics" ) +const worldLoopHealthCheckTimeout = time.Second * 5 + +type monitoringResponse struct { + diffMean int + diffMedian int + diff95Percentile int + diff99Percentile int + diffMax int + activeConnections int +} + // TC9SetMonitoringDataCollectorHandler sets handler for getting data to handle monitoring request. // //export TC9SetMonitoringDataCollectorHandler @@ -24,59 +36,80 @@ func TC9SetMonitoringDataCollectorHandler(h C.MonitoringDataCollectorHandler) { C.SetMonitoringDataCollectorHandler(h) } -func monitoringHttpHandler() http.Handler { - promHandler := promhttp.Handler() - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx, cancel := context.WithTimeout(r.Context(), time.Second*5) - defer cancel() - - type respType struct { - diffMean int - diffMedian int - diff95Percentile int - diff99Percentile int - diffMax int - activeConnections int - - err error - } - var resp respType - - respChan := make(chan respType, 1) - - readRequestsQueue.Push(queue.HandlerFunc(func() { - res := C.CallMonitoringDataCollectorHandler() - if res.errorCode != C.MonitoringErrorCodeNoError { - respChan <- respType{ - err: errors.New("failed to process monitoring data collection"), - } - close(respChan) - return +func worldLoopHealthProbe(ctx context.Context) error { + ctx, cancel := context.WithTimeout(ctx, worldLoopHealthCheckTimeout) + defer cancel() + + done := make(chan struct{}, 1) + readRequestsQueue.Push(queue.HandlerFunc(func() { + done <- struct{}{} + })) + + select { + case <-ctx.Done(): + return fmt.Errorf("world loop did not process health probe: %w", ctx.Err()) + case <-done: + return nil + } +} + +func collectMonitoringData(ctx context.Context) (monitoringResponse, error) { + ctx, cancel := context.WithTimeout(ctx, worldLoopHealthCheckTimeout) + defer cancel() + + respChan := make(chan struct { + resp monitoringResponse + err error + }, 1) + + readRequestsQueue.Push(queue.HandlerFunc(func() { + res := C.CallMonitoringDataCollectorHandler() + if res.errorCode != C.MonitoringErrorCodeNoError { + respChan <- struct { + resp monitoringResponse + err error + }{ + err: errors.New("failed to process monitoring data collection"), } + return + } - respChan <- respType{ + respChan <- struct { + resp monitoringResponse + err error + }{ + resp: monitoringResponse{ diffMean: int(res.diffMean), diffMedian: int(res.diffMedian), diff95Percentile: int(res.diff95Percentile), diff99Percentile: int(res.diff99Percentile), diffMax: int(res.diffMaxPercentile), activeConnections: int(res.connectedPlayers), - err: nil, - } - close(respChan) - })) + }, + } + })) - select { - case <-ctx.Done(): + select { + case <-ctx.Done(): + return monitoringResponse{}, ctx.Err() + case res := <-respChan: + return res.resp, res.err + } +} + +func monitoringHttpHandler() http.Handler { + promHandler := promhttp.Handler() + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + resp, err := collectMonitoringData(r.Context()) + if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) { w.WriteHeader(http.StatusGatewayTimeout) _, _ = w.Write([]byte("timeout")) return - case resp = <-respChan: } - if resp.err != nil { + if err != nil { w.WriteHeader(http.StatusInternalServerError) - _, _ = w.Write([]byte(resp.err.Error())) + _, _ = w.Write([]byte(err.Error())) return } diff --git a/game-server/libsidecar/player-items-api.c b/game-server/libsidecar/player-items-api.c index a97427d..df3c9ad 100644 --- a/game-server/libsidecar/player-items-api.c +++ b/game-server/libsidecar/player-items-api.c @@ -21,7 +21,7 @@ void SetRemoveItemsWithGuidsFromPlayerHandler(RemoveItemsWithGuidsFromPlayerHand } RemoveItemsWithGuidsFromPlayerResponse CallRemoveItemsWithGuidsFromPlayerHandler(uint64_t player_guid, uint64_t* items_guids, int items_guids_size, uint64_t assign_player_guid) { - if (getPlayerItemsByGuidsHandler == 0) { + if (removeItemsWithGuidsFromPlayerHandler == 0) { RemoveItemsWithGuidsFromPlayerResponse resp; resp.errorCode = PlayerItemErrorCodeNoHandler; return resp; @@ -30,6 +30,21 @@ RemoveItemsWithGuidsFromPlayerResponse CallRemoveItemsWithGuidsFromPlayerHandler return removeItemsWithGuidsFromPlayerHandler(player_guid, items_guids, items_guids_size, assign_player_guid); } +static TakePlayerItemByPosHandler takePlayerItemByPosHandler; +void SetTakePlayerItemByPosHandler(TakePlayerItemByPosHandler h) { + takePlayerItemByPosHandler = h; +} + +TakePlayerItemByPosResponse CallTakePlayerItemByPosHandler(uint64_t player_guid, uint8_t bag_slot, uint8_t slot, uint32_t count, uint64_t assign_player_guid) { + if (takePlayerItemByPosHandler == 0) { + TakePlayerItemByPosResponse resp; + resp.errorCode = PlayerItemErrorCodeNoHandler; + return resp; + } + + return takePlayerItemByPosHandler(player_guid, bag_slot, slot, count, assign_player_guid); +} + static AddExistingItemToPlayerHandler addExistingItemToPlayerHandler; void SetAddExistingItemToPlayerHandler(AddExistingItemToPlayerHandler h) { addExistingItemToPlayerHandler = h; diff --git a/game-server/libsidecar/player-items-api.go b/game-server/libsidecar/player-items-api.go index fe5e917..75c3365 100644 --- a/game-server/libsidecar/player-items-api.go +++ b/game-server/libsidecar/player-items-api.go @@ -18,6 +18,13 @@ func TC9SetGetPlayerItemsByGuidsHandler(h C.GetPlayerItemsByGuidsHandler) { C.SetGetPlayerItemsByGuidsHandler(h) } +// TC9SetTakePlayerItemByPosHandler sets handler for removing one player item by bag/slot. +// +//export TC9SetTakePlayerItemByPosHandler +func TC9SetTakePlayerItemByPosHandler(h C.TakePlayerItemByPosHandler) { + C.SetTakePlayerItemByPosHandler(h) +} + // TC9SetRemoveItemsWithGuidsFromPlayerHandler sets handler for removing items by guids from player request. // //export TC9SetRemoveItemsWithGuidsFromPlayerHandler @@ -49,9 +56,10 @@ func GetPlayerItemsByGuidHandler(player uint64, items []uint64) ([]grpcapi.Playe itemsResult[i].Slot = uint8(returnedItems[i].slot) itemsResult[i].IsTradable = bool(returnedItems[i].isTradable) itemsResult[i].Count = uint32(returnedItems[i].count) - itemsResult[i].Flags = uint16(returnedItems[i].flags) + itemsResult[i].Flags = uint32(returnedItems[i].flags) itemsResult[i].Durability = uint32(returnedItems[i].durability) - itemsResult[i].RandomPropertyID = uint32(returnedItems[i].randomPropertyID) + itemsResult[i].RandomPropertyID = int32(returnedItems[i].randomPropertyID) + itemsResult[i].Text = C.GoString(returnedItems[i].text) C.free((unsafe.Pointer)(returnedItems[i].text)) } @@ -61,6 +69,48 @@ func GetPlayerItemsByGuidHandler(player uint64, items []uint64) ([]grpcapi.Playe return itemsResult, nil } +// TakePlayerItemByPosHandler calls C++ TakePlayerItemByPosHandler implementation and makes Go<->C conversions of in/out params. +func TakePlayerItemByPosHandler(player uint64, bagSlot, slot uint8, count uint32, assignToPlayer uint64) (*grpcapi.TakePlayerItemByPosResponse, error) { + res := C.CallTakePlayerItemByPosHandler( + C.uint64_t(player), + C.uint8_t(bagSlot), + C.uint8_t(slot), + C.uint32_t(count), + C.uint64_t(assignToPlayer), + ) + + switch res.errorCode { + case C.PlayerItemErrorCodeNoError: + item := grpcapi.PlayerItem{ + Guid: uint64(res.item.guid), + Entry: uint32(res.item.entry), + Owner: uint64(res.item.owner), + BagSlot: uint8(res.item.bagSlot), + Slot: uint8(res.item.slot), + IsTradable: bool(res.item.isTradable), + Count: uint32(res.item.count), + Flags: uint32(res.item.flags), + Durability: uint32(res.item.durability), + RandomPropertyID: int32(res.item.randomPropertyID), + Text: C.GoString(res.item.text), + } + C.free((unsafe.Pointer)(res.item.text)) + + return &grpcapi.TakePlayerItemByPosResponse{ + Status: grpcapi.PlayerItemTakeSuccess, + Item: item, + }, nil + case C.PlayerItemErrorCodePlayerNotFound: + return &grpcapi.TakePlayerItemByPosResponse{Status: grpcapi.PlayerItemTakePlayerNotFound}, nil + case C.PlayerItemErrorItemNotFound: + return &grpcapi.TakePlayerItemByPosResponse{Status: grpcapi.PlayerItemTakeItemNotFound}, nil + case C.PlayerItemErrorItemNotTradable: + return &grpcapi.TakePlayerItemByPosResponse{Status: grpcapi.PlayerItemTakeItemNotTradable}, nil + default: + return &grpcapi.TakePlayerItemByPosResponse{Status: grpcapi.PlayerItemTakeFailed}, nil + } +} + // RemoveItemsWithGuidsFromPlayerHandler calls C++ RemoveItemsWithGuidsFromPlayerHandler implementation and makes Go<->C conversions of in/out params. func RemoveItemsWithGuidsFromPlayerHandler(player uint64, items []uint64, assignToPlayer uint64) ([]uint64, error) { res := C.CallRemoveItemsWithGuidsFromPlayerHandler( @@ -91,9 +141,12 @@ func AddExistingItemToPlayerHandler(player uint64, item *grpcapi.ItemToAdd) erro request.itemGuid = C.uint64_t(item.Guid) request.itemEntry = C.uint32_t(item.Entry) request.itemCount = C.uint32_t(item.Count) - request.itemFlags = C.uint16_t(item.Flags) + request.itemFlags = C.uint32_t(item.Flags) request.itemDurability = C.uint8_t(item.Durability) - request.itemRandomPropertyID = C.int8_t(item.RandomPropertyID) + request.itemRandomPropertyID = C.int32_t(item.RandomPropertyID) + request.storeAtPos = C.bool(item.StoreAtPos) + request.bagSlot = C.uint8_t(item.BagSlot) + request.slot = C.uint8_t(item.Slot) res := C.CallAddExistingItemToPlayerHandler(&request) if res != C.PlayerItemErrorCodeNoError { diff --git a/game-server/libsidecar/player-items-api.h b/game-server/libsidecar/player-items-api.h index 8f5dd8d..d5ff298 100644 --- a/game-server/libsidecar/player-items-api.h +++ b/game-server/libsidecar/player-items-api.h @@ -11,7 +11,9 @@ typedef enum PlayerItemErrorCode { PlayerItemErrorCodePlayerNotFound = 2, PlayerItemErrorNoInventorySpace = 3, PlayerItemErrorUnknownTemplate = 4, - PlayerItemErrorFailedToCreateItem = 5 + PlayerItemErrorFailedToCreateItem = 5, + PlayerItemErrorItemNotFound = 6, + PlayerItemErrorItemNotTradable = 7 } PlayerItemErrorCode; // GetPlayerItemsByGuids request. @@ -23,9 +25,9 @@ typedef struct { uint8_t slot; bool isTradable; uint32_t count; - uint16_t flags; + uint32_t flags; uint8_t durability; - int8_t randomPropertyID; + int32_t randomPropertyID; const char* text; } PlayerItem; @@ -39,6 +41,16 @@ typedef GetPlayerItemsByGuidsResponse (*GetPlayerItemsByGuidsHandler) (uint64_t void SetGetPlayerItemsByGuidsHandler(GetPlayerItemsByGuidsHandler h); GetPlayerItemsByGuidsResponse CallGetPlayerItemsByGuidsHandler(uint64_t player_guid, uint64_t* items_guids, int items_guids_size); +// TakePlayerItemByPos request. +typedef struct { + int errorCode; + PlayerItem item; +} TakePlayerItemByPosResponse; + +typedef TakePlayerItemByPosResponse (*TakePlayerItemByPosHandler) (uint64_t /*player_guid*/, uint8_t /*bag_slot*/, uint8_t /*slot*/, uint32_t /*count*/, uint64_t /*assign_player_guid*/); +void SetTakePlayerItemByPosHandler(TakePlayerItemByPosHandler h); +TakePlayerItemByPosResponse CallTakePlayerItemByPosHandler(uint64_t player_guid, uint8_t bag_slot, uint8_t slot, uint32_t count, uint64_t assign_player_guid); + // RemoveItemsWithGuidsFromPlayer request. typedef struct { int errorCode; @@ -56,9 +68,12 @@ typedef struct { uint64_t itemGuid; uint32_t itemEntry; uint32_t itemCount; - uint16_t itemFlags; + uint32_t itemFlags; uint8_t itemDurability; - int8_t itemRandomPropertyID; + int32_t itemRandomPropertyID; + bool storeAtPos; + uint8_t bagSlot; + uint8_t slot; } AddExistingItemToPlayerRequest; typedef PlayerItemErrorCode (*AddExistingItemToPlayerHandler) (AddExistingItemToPlayerRequest*); diff --git a/libsidecar.h b/libsidecar.h new file mode 100644 index 0000000..b9cca8e --- /dev/null +++ b/libsidecar.h @@ -0,0 +1,341 @@ +/* Code generated by cmd/cgo; DO NOT EDIT. */ + +/* package github.com/walkline/ToCloud9/game-server/libsidecar */ + + +#line 1 "cgo-builtin-export-prolog" + +#include + +#ifndef GO_CGO_EXPORT_PROLOGUE_H +#define GO_CGO_EXPORT_PROLOGUE_H + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef struct { const char *p; ptrdiff_t n; } _GoString_; +#endif + +#endif + +/* Start of preamble from import "C" comments. */ + + +#line 3 "battleground-api.go" + +#include "battleground-api.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "events-group.go" + +#include "events-group.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "events-guilds.go" + +#include "events-guild.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "events-servers-registry.go" + +#include "events-servers-registry.h" + +#line 1 "cgo-generated-wrapper" + + + +#line 3 "guild-api.go" + +#include "guild-api.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "lfg-api.go" + +#include "lfg-api.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "lib.go" + +#include + +#line 1 "cgo-generated-wrapper" + +#line 3 "matchmaking.go" + +#include + +#line 1 "cgo-generated-wrapper" + +#line 3 "monitoring.go" + +#include "monitoring.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "player-interactions-api.go" + +#include "player-interactions-api.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "player-items-api.go" + +#include "player-items-api.h" + +#line 1 "cgo-generated-wrapper" + +#line 3 "player-money-api.go" + +#include "player-money-api.h" + +#line 1 "cgo-generated-wrapper" + + +/* End of preamble from import "C" comments. */ + + +/* Start of boilerplate cgo prologue. */ +#line 1 "cgo-gcc-export-header-prolog" + +#ifndef GO_CGO_PROLOGUE_H +#define GO_CGO_PROLOGUE_H + +typedef signed char GoInt8; +typedef unsigned char GoUint8; +typedef short GoInt16; +typedef unsigned short GoUint16; +typedef int GoInt32; +typedef unsigned int GoUint32; +typedef long long GoInt64; +typedef unsigned long long GoUint64; +typedef GoInt64 GoInt; +typedef GoUint64 GoUint; +typedef size_t GoUintptr; +typedef float GoFloat32; +typedef double GoFloat64; +#ifdef _MSC_VER +#include +typedef _Fcomplex GoComplex64; +typedef _Dcomplex GoComplex128; +#else +typedef float _Complex GoComplex64; +typedef double _Complex GoComplex128; +#endif + +/* + static assertion to make sure the file is being used on architecture + at least with matching size of GoInt. +*/ +typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef _GoString_ GoString; +#endif +typedef void *GoMap; +typedef void *GoChan; +typedef struct { void *t; void *v; } GoInterface; +typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; + +#endif + +/* End of boilerplate cgo prologue. */ + +#ifdef __cplusplus +extern "C" { +#endif + + +// TC9SetBattlegroundStartHandler sets handler for starting battleground. +// +extern void TC9SetBattlegroundStartHandler(BattlegroundStartHandler h); + +// TC9SetBattlegroundAddPlayersHandler sets handler for adding players to battleground. +// +extern void TC9SetBattlegroundAddPlayersHandler(BattlegroundAddPlayersHandler h); + +// TC9SetCanPlayerJoinBattlegroundQueueHandler sets handler for checking if player can join to battleground queue. +// +extern void TC9SetCanPlayerJoinBattlegroundQueueHandler(CanPlayerJoinBattlegroundQueueHandler h); + +// TC9SetCanPlayerTeleportToBattlegroundHandler sets handler for checking if player can teleport to battleground. +// +extern void TC9SetCanPlayerTeleportToBattlegroundHandler(CanPlayerTeleportToBattlegroundHandler h); + +// TC9SetOnGroupCreatedHook sets hook for group created event. +// +extern void TC9SetOnGroupCreatedHook(OnGroupCreatedHook h); + +// TC9SetOnGroupMemberAddedHook sets hook for member added event. +// +extern void TC9SetOnGroupMemberAddedHook(OnGroupMemberAddedHook h); + +// TC9SetOnGroupMemberRemovedHook sets hook for member left/kicked event. +// +extern void TC9SetOnGroupMemberRemovedHook(OnGroupMemberRemovedHook h); + +// TC9SetOnGroupLeaderChangedHook sets hook for leader changed event. +// +extern void TC9SetOnGroupLeaderChangedHook(OnGroupLeaderChangedHook h); + +// TC9SetOnGroupDisbandedHook sets hook for group disbanded event. +// +extern void TC9SetOnGroupDisbandedHook(OnGroupDisbandedHook h); + +// TC9SetOnGroupLootTypeChangedHook sets hook for group loot type changed event. +// +extern void TC9SetOnGroupLootTypeChangedHook(OnGroupLootTypeChangedHook h); + +// TC9SetOnGroupDungeonDifficultyChangedHook sets hook for group dungeon difficulty changed event. +// +extern void TC9SetOnGroupDungeonDifficultyChangedHook(OnGroupDungeonDifficultyChangedHook h); + +// TC9SetOnGroupRaidDifficultyChangedHook sets hook for group raid difficulty changed event. +// +extern void TC9SetOnGroupRaidDifficultyChangedHook(OnGroupRaidDifficultyChangedHook h); + +// TC9SetOnGroupConvertedToRaidHook sets hook for group converted to raid event. +// +extern void TC9SetOnGroupConvertedToRaidHook(OnGroupConvertedToRaidHook h); +extern void TC9SetOnGroupReadyCheckStartedHook(OnGroupReadyCheckStartedHook h); +extern void TC9SetOnGroupReadyCheckMemberStateHook(OnGroupReadyCheckMemberStateHook h); +extern void TC9SetOnGroupReadyCheckFinishedHook(OnGroupReadyCheckFinishedHook h); +extern void TC9SetOnGroupMemberSubGroupChangedHook(OnGroupMemberSubGroupChangedHook h); +extern void TC9SetOnGroupMemberFlagsChangedHook(OnGroupMemberFlagsChangedHook h); +extern void TC9SetOnGroupMemberStateChangedHook(OnGroupMemberStateChangedHook h); +extern void TC9SetOnGroupInstanceResetRequestHook(OnGroupInstanceResetRequestHook h); +extern void TC9SetOnGroupInstanceBindExtensionRequestHook(OnGroupInstanceBindExtensionRequestHook h); +extern void TC9UpdateGroupMemberState(uint64_t memberGuid, uint8_t online, uint8_t level, uint8_t playerClass, uint32_t zoneId, uint32_t mapId, uint32_t health, uint32_t maxHealth, uint8_t powerType, uint32_t power, uint32_t maxPower, uint32_t instanceID); +extern void TC9StartGroupReadyCheck(uint64_t leaderGuid, uint32_t durationMs); +extern void TC9SetGroupReadyCheckMemberState(uint64_t memberGuid, uint8_t state); +extern void TC9FinishGroupReadyCheck(uint64_t playerGuid); +extern void TC9ChangeGroupMemberSubGroup(uint64_t updaterGuid, uint64_t memberGuid, uint8_t subGroup); +extern void TC9SetGroupMemberFlags(uint64_t updaterGuid, uint64_t memberGuid, uint8_t flags, uint8_t roles); + +// TC9SetOnGuildMemberAddedHook sets hook for guild member added event. +// +extern void TC9SetOnGuildMemberAddedHook(OnGuildMemberAddedHook h); + +// TC9SetOnGuildMemberRemovedHook sets hook for guild member removed (kicked) event. +// +extern void TC9SetOnGuildMemberRemovedHook(OnGuildMemberRemovedHook h); + +// TC9SetOnGuildMemberLeftHook sets hook for guild member left event. +// +extern void TC9SetOnGuildMemberLeftHook(OnGuildMemberLeftHook h); + +// TC9SetOnMapsReassignedHook sets hook for maps reassigning by servers registry event. +// +extern void TC9SetOnMapsReassignedHook(OnMapsReassignedHook h); + +// TC9ProcessEventsHooks calls all events hooks. +// +extern void TC9ProcessEventsHooks(); + +// TC9GetNextAvailableCharacterGuid returns next available characters GUID. Thread unsafe. +// +extern GoUint64 TC9GetNextAvailableCharacterGuid(GoInt realmID); + +// TC9GetNextAvailableItemGuid returns next available item GUID. Thread unsafe. +// +extern GoUint64 TC9GetNextAvailableItemGuid(GoInt realmID); + +// TC9GetNextAvailableInstanceGuid returns next available dungeon/raid instance GUID. Thread unsafe. +// +extern GoUint64 TC9GetNextAvailableInstanceGuid(GoInt realmID); + +// TC9SetGuildCreateHandler sets handler for creating guilds. +// +extern void TC9SetGuildCreateHandler(GuildCreateHandler h); + +// TC9SetLfgPlayerLockInfoHandler sets handler for AzerothCore LFG lock info. +// +extern void TC9SetLfgPlayerLockInfoHandler(LfgPlayerLockInfoHandler h); + +// TC9SetLfgPlayerInfoHandler sets handler for AzerothCore LFG player catalog and lock info. +// +extern void TC9SetLfgPlayerInfoHandler(LfgPlayerInfoHandler h); + +// TC9SetLfgDungeonInfoHandler sets handler for AzerothCore LFG dungeon metadata. +// +extern void TC9SetLfgDungeonInfoHandler(LfgDungeonInfoHandler h); + +// TC9SetLfgTeleportPlayerHandler sets handler for AzerothCore LFG teleport in/out. +// +extern void TC9SetLfgTeleportPlayerHandler(LfgTeleportPlayerHandler h); + +// TC9SetLfgBootVoteHandler sets handler for AzerothCore LFG boot vote replies. +// +extern void TC9SetLfgBootVoteHandler(LfgBootVoteHandler h); + +// TC9SetLfgMaterializeProposalHandler sets handler for AzerothCore LFG proposal materialization. +// +extern void TC9SetLfgMaterializeProposalHandler(LfgMaterializeProposalHandler h); + +// TC9InitLib inits lib by starting services like grpc and healthcheck. +// Adds game server to the servers registry that will make this server visible for gateway. +// +extern void TC9InitLib(GoUint16 port, GoUint32 realmID, GoUint8 isCrossRealm, char* availableMaps, uint32_t** assignedMaps, int* assignedMapsSize); + +// TC9GracefulShutdown gracefully stops all running services. +// +extern void TC9GracefulShutdown(); + +// TC9ProcessGRPCOrHTTPRequests calls all grpc or http handlers in queue. +// +extern void TC9ProcessGRPCOrHTTPRequests(); + +// TC9ReadyToAcceptPlayersFromMaps notifies servers registry that this server +// loaded maps related data and ready to accept players from those maps. +// +extern void TC9ReadyToAcceptPlayersFromMaps(uint32_t* maps, int mapsLen); + +// TC9PlayerLeftBattleground notifies matchmaking server that player left battleground +// +extern void TC9PlayerLeftBattleground(uint64_t playerGUID, uint32_t realmID, uint32_t instanceID); + +// TC9BattlegroundStatusChanged notifies matchmaking server that battleground status changed +// +extern void TC9BattlegroundStatusChanged(uint32_t instanceID, uint8_t status); + +// TC9CompleteLfgDungeon notifies matchmaking server that AzerothCore completed an LFG dungeon. +// +extern void TC9CompleteLfgDungeon(uint32_t completedDungeonEntry, uint32_t selectedDungeonEntry, uint64_t* players, int playersSize); + +// TC9SetMonitoringDataCollectorHandler sets handler for getting data to handle monitoring request. +// +extern void TC9SetMonitoringDataCollectorHandler(MonitoringDataCollectorHandler h); + +// TC9SetCanPlayerInteractWithNPCAndFlagsHandler sets handler for can player interact with NPC and with given NPC flags request. +// +extern void TC9SetCanPlayerInteractWithNPCAndFlagsHandler(CanPlayerInteractWithNPCAndFlagsHandler h); + +// TC9SetCanPlayerInteractWithGOAndTypeHandler sets handler for can player interact with GameObject and with given object type request. +// +extern void TC9SetCanPlayerInteractWithGOAndTypeHandler(CanPlayerInteractWithGOAndTypeHandler h); + +// TC9SetGetPlayerItemsByGuidsHandler sets handler for getting players item by guids request. +// +extern void TC9SetGetPlayerItemsByGuidsHandler(GetPlayerItemsByGuidsHandler h); + +// TC9SetRemoveItemsWithGuidsFromPlayerHandler sets handler for removing items by guids from player request. +// +extern void TC9SetRemoveItemsWithGuidsFromPlayerHandler(RemoveItemsWithGuidsFromPlayerHandler h); + +// TC9SetAddExistingItemToPlayerHandler sets handler for adding item to player request. +// +extern void TC9SetAddExistingItemToPlayerHandler(AddExistingItemToPlayerHandler h); + +// TC9SetGetMoneyForPlayerHandler sets handler for getting money for player request. +// +extern void TC9SetGetMoneyForPlayerHandler(GetMoneyForPlayerHandler h); + +// TC9SetModifyMoneyForPlayerHandler sets handler for modify money for given player request. +// +extern void TC9SetModifyMoneyForPlayerHandler(ModifyMoneyForPlayerHandler h); + +#ifdef __cplusplus +} +#endif From e2a6835b3142b635c36bd08fd3ed8904981b267c Mon Sep 17 00:00:00 2001 From: VG-Prog Date: Fri, 22 May 2026 21:49:15 +0200 Subject: [PATCH 11/11] fix(DB/Cluster): Add cluster persistence migrations Add auth and character database migrations for Real ID friends, crossrealm group identity, LFG route persistence, stable petition IDs, and guild bank idempotency/locking changes required by clustered services. --- sql/auth/mysql/000002_realid_friends.sql | 14 ++++ .../000005_group_crossrealm_identity.down.sql | 13 ++++ .../000005_group_crossrealm_identity.up.sql | 13 ++++ ...06_lfg_crossrealm_instance_routes.down.sql | 1 + ...0006_lfg_crossrealm_instance_routes.up.sql | 16 +++++ .../mysql/000008_petition_stable_ids.down.sql | 55 ++++++++++++++++ .../mysql/000008_petition_stable_ids.up.sql | 66 +++++++++++++++++++ ...0009_drop_guild_bank_cluster_lock.down.sql | 7 ++ ...000009_drop_guild_bank_cluster_lock.up.sql | 1 + .../000010_guild_bank_idempotency.down.sql | 2 + .../000010_guild_bank_idempotency.up.sql | 2 + 11 files changed, 190 insertions(+) create mode 100644 sql/auth/mysql/000002_realid_friends.sql create mode 100644 sql/characters/mysql/000005_group_crossrealm_identity.down.sql create mode 100644 sql/characters/mysql/000005_group_crossrealm_identity.up.sql create mode 100644 sql/characters/mysql/000006_lfg_crossrealm_instance_routes.down.sql create mode 100644 sql/characters/mysql/000006_lfg_crossrealm_instance_routes.up.sql create mode 100644 sql/characters/mysql/000008_petition_stable_ids.down.sql create mode 100644 sql/characters/mysql/000008_petition_stable_ids.up.sql create mode 100644 sql/characters/mysql/000009_drop_guild_bank_cluster_lock.down.sql create mode 100644 sql/characters/mysql/000009_drop_guild_bank_cluster_lock.up.sql create mode 100644 sql/characters/mysql/000010_guild_bank_idempotency.down.sql create mode 100644 sql/characters/mysql/000010_guild_bank_idempotency.up.sql diff --git a/sql/auth/mysql/000002_realid_friends.sql b/sql/auth/mysql/000002_realid_friends.sql new file mode 100644 index 0000000..208ed28 --- /dev/null +++ b/sql/auth/mysql/000002_realid_friends.sql @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS tc9_realid_friends ( + account_id_low INT UNSIGNED NOT NULL, + account_id_high INT UNSIGNED NOT NULL, + requester_account_id INT UNSIGNED NOT NULL, + status TINYINT UNSIGNED NOT NULL DEFAULT 1, + note_low VARCHAR(48) NOT NULL DEFAULT '', + note_high VARCHAR(48) NOT NULL DEFAULT '', + created_at INT UNSIGNED NOT NULL, + updated_at INT UNSIGNED NOT NULL, + PRIMARY KEY (account_id_low, account_id_high), + KEY idx_tc9_realid_friends_low_status (account_id_low, status), + KEY idx_tc9_realid_friends_high_status (account_id_high, status), + KEY idx_tc9_realid_friends_requester (requester_account_id, status) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/sql/characters/mysql/000005_group_crossrealm_identity.down.sql b/sql/characters/mysql/000005_group_crossrealm_identity.down.sql new file mode 100644 index 0000000..d8441a7 --- /dev/null +++ b/sql/characters/mysql/000005_group_crossrealm_identity.down.sql @@ -0,0 +1,13 @@ +ALTER TABLE group_invites + DROP COLUMN groupRealmId, + MODIFY invited INT UNSIGNED NOT NULL, + MODIFY inviter INT UNSIGNED NOT NULL; + +ALTER TABLE group_member + DROP COLUMN memberName, + MODIFY memberGuid INT UNSIGNED NOT NULL; + +ALTER TABLE `groups` + MODIFY leaderGuid INT UNSIGNED NOT NULL, + MODIFY looterGuid INT UNSIGNED NOT NULL, + MODIFY masterLooterGuid INT UNSIGNED NOT NULL; diff --git a/sql/characters/mysql/000005_group_crossrealm_identity.up.sql b/sql/characters/mysql/000005_group_crossrealm_identity.up.sql new file mode 100644 index 0000000..1a41009 --- /dev/null +++ b/sql/characters/mysql/000005_group_crossrealm_identity.up.sql @@ -0,0 +1,13 @@ +ALTER TABLE `groups` + MODIFY leaderGuid BIGINT UNSIGNED NOT NULL, + MODIFY looterGuid BIGINT UNSIGNED NOT NULL, + MODIFY masterLooterGuid BIGINT UNSIGNED NOT NULL; + +ALTER TABLE group_member + MODIFY memberGuid BIGINT UNSIGNED NOT NULL, + ADD COLUMN memberName VARCHAR(12) NOT NULL DEFAULT '' AFTER memberGuid; + +ALTER TABLE group_invites + MODIFY invited BIGINT UNSIGNED NOT NULL, + MODIFY inviter BIGINT UNSIGNED NOT NULL, + ADD COLUMN groupRealmId INT UNSIGNED NOT NULL DEFAULT 0 AFTER groupId; diff --git a/sql/characters/mysql/000006_lfg_crossrealm_instance_routes.down.sql b/sql/characters/mysql/000006_lfg_crossrealm_instance_routes.down.sql new file mode 100644 index 0000000..97c1ac5 --- /dev/null +++ b/sql/characters/mysql/000006_lfg_crossrealm_instance_routes.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS `tc9_lfg_dungeon_routes`; diff --git a/sql/characters/mysql/000006_lfg_crossrealm_instance_routes.up.sql b/sql/characters/mysql/000006_lfg_crossrealm_instance_routes.up.sql new file mode 100644 index 0000000..0300916 --- /dev/null +++ b/sql/characters/mysql/000006_lfg_crossrealm_instance_routes.up.sql @@ -0,0 +1,16 @@ +CREATE TABLE IF NOT EXISTS `tc9_lfg_dungeon_routes` ( + `realmId` INT UNSIGNED NOT NULL, + `playerGuid` BIGINT UNSIGNED NOT NULL, + `dungeonEntry` INT UNSIGNED NOT NULL, + `mapId` INT UNSIGNED NOT NULL, + `difficulty` TINYINT UNSIGNED NOT NULL DEFAULT 0, + `ownerRealmId` INT UNSIGNED NOT NULL DEFAULT 0, + `isCrossRealm` TINYINT(1) NOT NULL DEFAULT 0, + `requiresBoundInstance` TINYINT(1) NOT NULL DEFAULT 0, + `instanceId` INT UNSIGNED NOT NULL DEFAULT 0, + `createdAt` INT UNSIGNED NOT NULL, + `updatedAt` INT UNSIGNED NOT NULL, + PRIMARY KEY (`realmId`, `playerGuid`, `mapId`, `difficulty`), + INDEX `idx_tc9_lfg_route_player` (`realmId`, `playerGuid`), + INDEX `idx_tc9_lfg_route_instance` (`instanceId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/sql/characters/mysql/000008_petition_stable_ids.down.sql b/sql/characters/mysql/000008_petition_stable_ids.down.sql new file mode 100644 index 0000000..a5d60b5 --- /dev/null +++ b/sql/characters/mysql/000008_petition_stable_ids.down.sql @@ -0,0 +1,55 @@ +SET @tc9_has_petition_sign_id_idx := ( + SELECT COUNT(*) + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'petition_sign' + AND INDEX_NAME = 'idx_petition_id_player' +); +SET @tc9_sql := IF(@tc9_has_petition_sign_id_idx = 1, + 'ALTER TABLE `petition_sign` DROP KEY `idx_petition_id_player`', + 'SELECT 1'); +PREPARE tc9_stmt FROM @tc9_sql; +EXECUTE tc9_stmt; +DEALLOCATE PREPARE tc9_stmt; + +SET @tc9_has_petition_sign_id := ( + SELECT COUNT(*) + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'petition_sign' + AND COLUMN_NAME = 'petition_id' +); +SET @tc9_sql := IF(@tc9_has_petition_sign_id = 1, + 'ALTER TABLE `petition_sign` DROP COLUMN `petition_id`', + 'SELECT 1'); +PREPARE tc9_stmt FROM @tc9_sql; +EXECUTE tc9_stmt; +DEALLOCATE PREPARE tc9_stmt; + +SET @tc9_has_petition_id_idx := ( + SELECT COUNT(*) + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'petition' + AND INDEX_NAME = 'idx_petition_id' +); +SET @tc9_sql := IF(@tc9_has_petition_id_idx = 1, + 'ALTER TABLE `petition` DROP KEY `idx_petition_id`', + 'SELECT 1'); +PREPARE tc9_stmt FROM @tc9_sql; +EXECUTE tc9_stmt; +DEALLOCATE PREPARE tc9_stmt; + +SET @tc9_has_petition_id := ( + SELECT COUNT(*) + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'petition' + AND COLUMN_NAME = 'petition_id' +); +SET @tc9_sql := IF(@tc9_has_petition_id = 1, + 'ALTER TABLE `petition` DROP COLUMN `petition_id`', + 'SELECT 1'); +PREPARE tc9_stmt FROM @tc9_sql; +EXECUTE tc9_stmt; +DEALLOCATE PREPARE tc9_stmt; diff --git a/sql/characters/mysql/000008_petition_stable_ids.up.sql b/sql/characters/mysql/000008_petition_stable_ids.up.sql new file mode 100644 index 0000000..ea2124e --- /dev/null +++ b/sql/characters/mysql/000008_petition_stable_ids.up.sql @@ -0,0 +1,66 @@ +SET @tc9_has_petition_id := ( + SELECT COUNT(*) + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'petition' + AND COLUMN_NAME = 'petition_id' +); +SET @tc9_sql := IF(@tc9_has_petition_id = 0, + 'ALTER TABLE `petition` ADD COLUMN `petition_id` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `petitionguid`', + 'SELECT 1'); +PREPARE tc9_stmt FROM @tc9_sql; +EXECUTE tc9_stmt; +DEALLOCATE PREPARE tc9_stmt; + +SET @tc9_petition_id := (SELECT COALESCE(MAX(`petition_id`), 0) FROM `petition`); +UPDATE `petition` +SET `petition_id` = (@tc9_petition_id := @tc9_petition_id + 1) +WHERE `petition_id` = 0 +ORDER BY `ownerguid`, `type`, `petitionguid`; + +SET @tc9_has_petition_id_idx := ( + SELECT COUNT(*) + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'petition' + AND INDEX_NAME = 'idx_petition_id' +); +SET @tc9_sql := IF(@tc9_has_petition_id_idx = 0, + 'ALTER TABLE `petition` ADD KEY `idx_petition_id` (`petition_id`)', + 'SELECT 1'); +PREPARE tc9_stmt FROM @tc9_sql; +EXECUTE tc9_stmt; +DEALLOCATE PREPARE tc9_stmt; + +SET @tc9_has_petition_sign_id := ( + SELECT COUNT(*) + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'petition_sign' + AND COLUMN_NAME = 'petition_id' +); +SET @tc9_sql := IF(@tc9_has_petition_sign_id = 0, + 'ALTER TABLE `petition_sign` ADD COLUMN `petition_id` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `petitionguid`', + 'SELECT 1'); +PREPARE tc9_stmt FROM @tc9_sql; +EXECUTE tc9_stmt; +DEALLOCATE PREPARE tc9_stmt; + +UPDATE `petition_sign` ps +INNER JOIN `petition` p ON p.`petitionguid` = ps.`petitionguid` +SET ps.`petition_id` = p.`petition_id` +WHERE ps.`petition_id` = 0; + +SET @tc9_has_petition_sign_id_idx := ( + SELECT COUNT(*) + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'petition_sign' + AND INDEX_NAME = 'idx_petition_id_player' +); +SET @tc9_sql := IF(@tc9_has_petition_sign_id_idx = 0, + 'ALTER TABLE `petition_sign` ADD KEY `idx_petition_id_player` (`petition_id`, `playerguid`)', + 'SELECT 1'); +PREPARE tc9_stmt FROM @tc9_sql; +EXECUTE tc9_stmt; +DEALLOCATE PREPARE tc9_stmt; diff --git a/sql/characters/mysql/000009_drop_guild_bank_cluster_lock.down.sql b/sql/characters/mysql/000009_drop_guild_bank_cluster_lock.down.sql new file mode 100644 index 0000000..87e3040 --- /dev/null +++ b/sql/characters/mysql/000009_drop_guild_bank_cluster_lock.down.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS `tc9_guild_bank_lock` ( + `guildid` INT UNSIGNED NOT NULL, + `owner` VARCHAR(128) NOT NULL, + `expires_at` BIGINT UNSIGNED NOT NULL, + PRIMARY KEY (`guildid`), + KEY `idx_tc9_guild_bank_lock_expires` (`expires_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/sql/characters/mysql/000009_drop_guild_bank_cluster_lock.up.sql b/sql/characters/mysql/000009_drop_guild_bank_cluster_lock.up.sql new file mode 100644 index 0000000..8b106e6 --- /dev/null +++ b/sql/characters/mysql/000009_drop_guild_bank_cluster_lock.up.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS `tc9_guild_bank_lock`; diff --git a/sql/characters/mysql/000010_guild_bank_idempotency.down.sql b/sql/characters/mysql/000010_guild_bank_idempotency.down.sql new file mode 100644 index 0000000..3a9f4f8 --- /dev/null +++ b/sql/characters/mysql/000010_guild_bank_idempotency.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE `guild_bank_item` + DROP KEY `idx_tc9_guild_bank_item_guid`; diff --git a/sql/characters/mysql/000010_guild_bank_idempotency.up.sql b/sql/characters/mysql/000010_guild_bank_idempotency.up.sql new file mode 100644 index 0000000..09fd24a --- /dev/null +++ b/sql/characters/mysql/000010_guild_bank_idempotency.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE `guild_bank_item` + ADD UNIQUE KEY `idx_tc9_guild_bank_item_guid` (`item_guid`);