Skip to content

Ranking

Highscore lists and leaderboards behind client.ranking.

ranking.service

Highscore lists and event leaderboards.

Classes:

Name Description
RankingService

Highscore lists and event leaderboards.

Attributes:

Name Type Description
logger

logger module-attribute

logger = logging.getLogger(__name__)

RankingService

RankingService(client: 'EmpireClient')

Bases: BaseService

Highscore lists and event leaderboards.

Methods:

Name Description
get_highscore

Search for a highscore entry (e.g. player rank).

get_ranking_list

Get a page of an event leaderboard, from a rank.

get_own_ranking_page

Get the page of an event leaderboard that holds your own score.

get_ranking_window

Get the page of an event leaderboard around a score, e.g. a search hit.

search_leaderboard

Search an event leaderboard for a name.

get_highscore

get_highscore(list_type: RankingType, search_value: str, league_type_id: int = -1, timeout: float = 5.0) -> list[HighscoreRow]

Search for a highscore entry (e.g. player rank).

Parameters:

Name Type Description Default
list_type RankingType

The highscore list (LT)

required
search_value str

Name to search for, a rank as text, or "-1" for your own rank

required
league_type_id int

League type id (LID); -1 for none, the client default

-1
timeout float

Timeout in seconds

5.0

Returns:

Type Description
list[HighscoreRow]

The page's rows, each in its list's layout (see HIGHSCORE_ROW_LAYOUTS)

Note: 'hgh' is shared with the alliance-search command. Requests for it run one at a time, but after one times out its late reply can be taken by the next, a search_alliances() call included: the reply's LT and LID need not be the ones asked for, so no reply is refused on them (see GetHighscoreRequest).

get_ranking_list

get_ranking_list(list_type: RankingType, rank: int, max_results: int, league_type_id: int, sub_division_id: int | None = None, event_id: int | None = None, timeout: float = 5.0) -> list[LeaderboardScore]

Get a page of an event leaderboard, from a rank.

Only works while the list's event runs: the client sends llsp only from event leaderboards, and a live server answered NO_EVENT (145) for every list with no event running, the regular highscore lists included (use get_highscore for those).

Parameters:

Name Type Description Default
list_type RankingType

The event's highscore list (LT), e.g. RankingType.LONG_TERM_POINT_EVENT

required
rank int

The first rank on the page

required
max_results int

Entries per page (M)

required
league_type_id int

The league (LID): the league of the event whose leaderboard it is, as its sei entry names it (client.events.get_league_id(event_id)), or another of its leagues 1 to its league count; -1 only for the donation ranking. The client pairs RankingType.LONG_TERM_POINT_EVENT (53) with event 83 (LongtermPointEventGlobalLeaderBoardDialog, bundle line 100411); the other dialogs are given their event

required
sub_division_id int | None

The alliance event subdivision (SDI), for an alliance event's subdivision ranking

None
event_id int | None

The alliance mobilisation or raid event (EID), for those events' rankings

None
timeout float

Timeout in seconds

5.0

Returns:

Type Description
list[LeaderboardScore]

List of entries from that rank

Raises:

Type Description
CommandError

NO_EVENT (145) when the list's event is not running

Client: LeaderBoardDataProvider (bundle line 75933), built only by GlobalLeaderBoardComponent.init (bundle line 34989) for event leaderboard dialogs

get_own_ranking_page

get_own_ranking_page(list_type: RankingType, max_results: int, league_type_id: int, sub_division_id: int | None = None, event_id: int | None = None, timeout: float = 5.0) -> list[LeaderboardScore]

Get the page of an event leaderboard that holds your own score.

Parameters:

Name Type Description Default
list_type RankingType

The event's highscore list (LT)

required
max_results int

Entries per page (M)

required
league_type_id int

The league (LID), as for get_ranking_list: the event's league, client.events.get_league_id(event_id)

required
sub_division_id int | None

The alliance event subdivision (SDI), as for get_ranking_list

None
event_id int | None

The alliance mobilisation or raid event (EID), as for get_ranking_list

None
timeout float

Timeout in seconds

5.0

Returns:

Type Description
list[LeaderboardScore]

The entries on your page

Raises:

Type Description
CommandError

a live server refused a long-term point list's own page with league -1 (GENERAL_ERROR, 1) and answered it with the event's league

Client: LeaderBoardDataProvider.getCurrentPlayerPage (bundle line 75941)

get_ranking_window

get_ranking_window(list_type: RankingType, score_id: str, max_results: int, league_type_id: int | None, sub_division_id: int | None = None, event_id: int | None = None, timeout: float = 5.0) -> list[LeaderboardScore]

Get the page of an event leaderboard around a score, e.g. a search hit.

Parameters:

Name Type Description Default
list_type RankingType

The event's highscore list (LT)

required
score_id str

A score id from search_leaderboard (SI); empty for your own page

required
max_results int

Entries per page (M)

required
league_type_id int | None

The league the score is in (LID): the search result's league_type_id, or for your own page (empty score_id) the event's league, as for get_ranking_list

required
sub_division_id int | None

The alliance event subdivision (SDI), as for get_ranking_list

None
event_id int | None

The alliance mobilisation or raid event (EID), as for get_ranking_list

None
timeout float

Timeout in seconds

5.0

Returns:

Type Description
list[LeaderboardScore]

The entries on that page

Client: LeaderBoardDataProvider.getCurrentSearchPage (bundle line 75943)

search_leaderboard

search_leaderboard(list_type: RankingType, search_value: str, sub_division_id: int | None = None, event_id: int | None = None, timeout: float = 5.0) -> list[LeaderboardSearchResult]

Search an event leaderboard for a name.

Page to a hit with get_ranking_window, passing its league_type_id and one of its score_ids; the client pages to the first hit, or to your own page when there is none. The search itself takes no league: C2SSearchLeaderboardScoresEventVO sends only LT and SV, and the answer groups the hits by league.

Parameters:

Name Type Description Default
list_type RankingType

The event's highscore list (LT)

required
search_value str

The name to search for (SV); must not be empty

required
sub_division_id int | None

The alliance event subdivision (SDI), as for get_ranking_list

None
event_id int | None

The alliance mobilisation or raid event (EID), as for get_ranking_list

None
timeout float

Timeout in seconds

5.0

Returns:

Type Description
list[LeaderboardSearchResult]

The matching score ids, grouped by league

Raises:

Type Description
ValueError

For an empty search, which the client never sends

Client: LeaderBoardDataProvider.searchLeaderBoard (bundle line 75942), onSearchDataReceived (bundle line 75952)

ranking.models

Highscore and leaderboard models.

Commands:

  • hgh: A page of a highscore list, around a rank or a searched name
  • llsp: A page of an event leaderboard, from a rank
  • llsw: A page of an event leaderboard, around a score or your own rank
  • slse: Search an event leaderboard for a name

Classes:

Name Description
HighscoreAlliance

An alliance on a highscore list: [alliance_id, name, member_count, fame].

HighscorePlayerRow

A player's row: [rank, score, owner_record, display_name?, shown_rank?], or [rank, owner_record]

HighscoreAllianceRow

An alliance's row: [rank, score, alliance].

HighscoreIslandRow

A Storm Islands alliance row: [is_stormlord, rank, score, alliance].

HighscoreTournamentRow

A tournament row: [rank, score, player_id, player_name], with no owner record.

GetHighscoreRequest

A page of a highscore list, around a rank or a searched name.

GetHighscoreResponse

A page of a highscore list, the reply to hgh.

GetRankingListRequest

A page of an event leaderboard, starting at a rank.

GetRankingWindowRequest

A page of an event leaderboard around a score, or around your own rank.

SearchRankingListRequest

Search an event leaderboard for a name.

LeaderboardScore

One entry of an event leaderboard page: an entry of llsp's L.

GetRankingListResponse

A page of an event leaderboard, the reply to llsp.

GetRankingWindowResponse

A page of an event leaderboard around a score, the reply to llsw.

LeaderboardSearchResult

The scores that matched a search in one league: an entry of slse's L.

SearchRankingListResponse

The scores that matched a leaderboard search, the reply to slse.

Attributes:

Name Type Description
logger
HighscoreRow

One row of a hgh reply; HIGHSCORE_ROW_LAYOUTS and the row's shape pick which.

HIGHSCORE_ROW_LAYOUTS dict[int, _RowModel]

The lists whose rows have a layout of their own.

logger module-attribute

logger = logging.getLogger(__name__)

HighscoreRow module-attribute

One row of a hgh reply; HIGHSCORE_ROW_LAYOUTS and the row's shape pick which.

HIGHSCORE_ROW_LAYOUTS module-attribute

HIGHSCORE_ROW_LAYOUTS: dict[int, _RowModel] = {RankingType.ALLIANCE_AQUA_POINTS: HighscoreIslandRow, RankingType.TOURNAMENT_FAME: HighscoreTournamentRow}

The lists whose rows have a layout of their own.

Every other list's row is an alliance's when its third entry is an array, else a player's, as SeasonLeagueMainDialogRanksItem tells them apart (bundle lines 91889, 91908); the other dialogs each read one of those two layouts.

HighscoreAlliance pydantic-model

Bases: BasePayload

An alliance on a highscore list: [alliance_id, name, member_count, fame].

Client: AllianceHighscoreInfoVO.fillFromParamObject (bundle line 27680), which reads nothing from a value that is not an array

Fields:

Validators:

  • _from_row
  • _text → name

alliance_id pydantic-field

alliance_id: ClientInt = 0

Alliance id

name pydantic-field

name: str = ''

Alliance name

member_count pydantic-field

member_count: ClientInt = 0

Number of members

fame pydantic-field

fame: ClientInt = 0

The alliance's current fame points

HighscorePlayerRow pydantic-model

Bases: BasePayload

A player's row: [rank, score, owner_record, display_name?, shown_rank?], or [rank, owner_record] without a score.

Client: CastleHighscoreDialog.onGetHighscoreData (bundle lines 27559-27568), CastleGenericHighscoreDialog.onGetHighscoreData (bundle lines 30827-30836), CastleSingleplayerRankingItem.update (bundle line 91695) for the row without a score, TempServerEventDialogRankingItem.updateWithNewData (bundle lines 118343-118344) for the display name and the shown rank, which 0 leaves at rank

Fields:

Validators:

  • _from_row
  • _owner → owner

rank pydantic-field

rank: ClientInt = 0

Rank on the list

score pydantic-field

score: ClientInt = 0

The listed value; 0 on a row without one

owner pydantic-field

owner: MapObject | None = None

The player's owner record; None when unreadable

display_name pydantic-field

display_name: str | None = None

The name the temporary server lists show; None when the row has none

shown_rank pydantic-field

shown_rank: int | None = None

The rank the previous-run and temporary server lists show; None to show rank

HighscoreAllianceRow pydantic-model

Bases: BasePayload

An alliance's row: [rank, score, alliance].

Client: CastleHighscoreDialog.onGetHighscoreData (bundle lines 27541-27558), CastleAllianceRankingItem.update (bundle line 91645), the generic alliance highscore dialog (bundle lines 38131-38134)

Fields:

Validators:

rank pydantic-field

rank: ClientInt = 0

Rank on the list

score pydantic-field

score: ClientInt = 0

The listed value

alliance pydantic-field

The alliance

HighscoreIslandRow pydantic-model

Bases: HighscoreAllianceRow

A Storm Islands alliance row: [is_stormlord, rank, score, alliance].

Client: CastleEilandAllianceRankingItem.update (bundle lines 98076-98079), shown for HighscoreConst.ALLIANCE_AQUA_POINTS (bundle line 98052)

Fields:

Validators:

is_stormlord pydantic-field

is_stormlord: bool = False

The alliance holds the Storm Islands

HighscoreTournamentRow pydantic-model

Bases: BasePayload

A tournament row: [rank, score, player_id, player_name], with no owner record.

Client: ACastleTournamentRankListItem.parseItemData (bundle line 59120), CastleTournamentRankListItem.parseItemData (bundle line 118660)

Fields:

Validators:

rank pydantic-field

rank: ClientInt = 0

Rank on the list

score pydantic-field

score: ClientInt = 0

Tournament points

player_id pydantic-field

player_id: ClientInt = 0

The player's id

player_name pydantic-field

player_name: str = ''

The player's name

GetHighscoreRequest pydantic-model

Bases: BaseRequest

A page of a highscore list, around a rank or a searched name.

Payload: {"LT": list_type, "LID": league_type_id, "SV": search_value}

The keys follow the client's order: the constructor initialises LT and LID before it sets SV, which it encodes as it encodes any text it sends.

The reply carries LT and LID, but not always the ones asked for, so no reply is refused on them: the highscore dialog switches to the list and league the reply names (CastleHighscoreDialog.onGetHighscoreData, bundle lines 27533-27535), and offers the legend list (7) only in the level-cap league, falling back to honor (5) elsewhere (setLeague, bundle line 27482; switchOverallCategory, bundle lines 27506-27519). A live server answered LT 7 around your own rank ("-1", LID -1) for a league-1 player with LT 5 and LID 1.

Client: C2SGetHighscoreVO (bundle line 14674)

Fields:

list_type pydantic-field

list_type: RankingType

Wire key LT

The highscore list

league_type_id pydantic-field

league_type_id: int = -1

Wire key LID

The league, a level band (see GameData.league_type); -1 for none

search_value pydantic-field

search_value: str

Wire key SV

A name, or a rank as text; "-1" asks for the page around your own rank

GetHighscoreResponse pydantic-model

Bases: BaseResponse

A page of a highscore list, the reply to hgh.

The client has no row parser of its own: each dialog reads the rows in its list's layout, so the rows are read by HIGHSCORE_ROW_LAYOUTS and their shape.

Client: HGHCommand.executeCommand (bundle line 124377)

Fields:

Validators:

list_type pydantic-field

list_type: int | None = None

Wire key LT

league_type_id pydantic-field

league_type_id: int = -1

Wire key LID

League type id; -1 for none

last_rank pydantic-field

last_rank: int | None = None

Wire key LR

search_value pydantic-field

search_value: str | None = None

Wire key SV

rows pydantic-field

rows: tuple[HighscoreRow, ...] = ()

Wire key L

The page's rows, in the list's layout

GetRankingListRequest pydantic-model

Bases: BaseRequest

A page of an event leaderboard, starting at a rank.

Payload: {"LT": list_type, "LID": league_type_id, "M": max_results, "R": rank}

The client pages only event leaderboards this way (score, long-term point, alliance mobilisation/raid and donation events), and always with a league: LeaderBoardDataProvider sends the league it was built with on every page. The score and long-term point dialogs build it with the event's league, the LID of the event's sei entry, and page through leagues 1 to the event's league count (GlobalLeaderBoardLeagueComponent, bundle line 100470; AScoreEventVO.parseBasicsFromParamObject, bundle line 14967); the alliance mobilisation dialog passes the event's league (bundle line 47284); only the donation ranking passes none, so -1 (bundle line 115740). A live server answered NO_EVENT (145) for every list with no event running, the regular highscore lists included; read those with hgh.

Client: C2SListLeaderboardScoresPageVO (bundle line 76002), sent by LeaderBoardDataProvider (bundle line 75933), which only GlobalLeaderBoardComponent.init (bundle line 34989) builds, for ScoreEventGlobalLeaderBoardDialog and LongtermPointEventGlobalLeaderBoardDialog (AGlobalLeaderBoardDialog.showLoaded, bundle line 100442), AllianceMobilizationEventDialogLeaderboard.show (bundle line 47278) and DonationEventDialogRanking (bundle line 115737)

The alliance mobilisation and raid leaderboards also send SDI and EID: LeaderBoardDataProvider.sendCommand (bundle line 75949) copies the {SDI, EID} (subdivision tab) or {EID} (division tab) that AllianceMobilizationEventDialogRanking.switchTab (bundle line 75910) and AllianceRaidEventRankingDialog.switchTab (bundle line 75633) show the leaderboard with onto every request, after the VO's own keys.

Fields:

list_type pydantic-field

list_type: RankingType

Wire key LT

The highscore list

league_type_id pydantic-field

league_type_id: int

Wire key LID

The event's league, a level band (see GameData.league_type); -1 for the donation ranking

max_results pydantic-field

max_results: int

Wire key M

Entries per page

rank pydantic-field

rank: int = 1

Wire key R

The first rank on the page

sub_division_id pydantic-field

sub_division_id: int | None = None

Wire key SDI

The alliance event subdivision, for its subdivision ranking

event_id pydantic-field

event_id: int | None = None

Wire key EID

The alliance mobilisation or raid event

accepts_reply

accepts_reply(payload: Any) -> bool

Whether a reply is for this request, as the client tells.

A reply is refused only when its LT is another list and its league (LID, or -1 without one) is the one asked for; a reply for another list in another league is taken, as the client takes it.

Client: LeaderBoardDataProvider.onScoreDataReceived (bundle line 75957), which then adopts the reply's LID

GetRankingWindowRequest pydantic-model

Bases: BaseRequest

A page of an event leaderboard around a score, or around your own rank.

Payload: {"LT": list_type, "LID": league_type_id, "M": max_results, "SI": score_id}

SI is a score id from a slse search result, or empty for the page holding your own score; the client encodes it as it encodes any text it sends. The reply is shaped like llsp's. LID, SDI and EID are sent as for llsp: your own page goes with the event's league, a search hit's page with the hit's league. A live server refused a long-term point list's own page with league -1 (GENERAL_ERROR, 1) and answered it with the event's league.

Client: C2SListLeaderboardScoresWindowVO (bundle line 76012), sent by LeaderBoardDataProvider.getCurrentPlayerPage (bundle line 75941) with no score id and getCurrentSearchPage (bundle line 75943) with a search result's league and score id

Fields:

list_type pydantic-field

list_type: RankingType

Wire key LT

The highscore list

league_type_id pydantic-field

league_type_id: int | None

Wire key LID

The event's league, or a search hit's, a level band (see GameData.league_type); -1 for the donation ranking, None for a hit without one

max_results pydantic-field

max_results: int

Wire key M

Entries per page

score_id pydantic-field

score_id: str = ''

Wire key SI

The score to page around, from a search result; empty for your own

sub_division_id pydantic-field

sub_division_id: int | None = None

Wire key SDI

The alliance event subdivision, for its subdivision ranking

event_id pydantic-field

event_id: int | None = None

Wire key EID

The alliance mobilisation or raid event

accepts_reply

accepts_reply(payload: Any) -> bool

Whether a reply is for this request, as the client tells.

A reply is refused only when its LT is another list and its league (LID, or -1 without one) is the one asked for; a reply for another list in another league is taken, as the client takes it.

Client: LeaderBoardDataProvider.onScoreDataReceived (bundle line 75957), which then adopts the reply's LID

SearchRankingListRequest pydantic-model

Bases: BaseRequest

Search an event leaderboard for a name.

Payload: {"LT": list_type, "SV": search_value}

The client encodes SV as it encodes any text it sends, and sends nothing for an empty search. It then pages to the first hit with llsw, or to your own page when there is none. SDI and EID are sent as for llsp.

Client: C2SSearchLeaderboardScoresEventVO (bundle line 76022), sent by LeaderBoardDataProvider.searchLeaderBoard (bundle line 75942)

Fields:

list_type pydantic-field

list_type: RankingType

Wire key LT

The highscore list

search_value pydantic-field

search_value: str

Wire key SV

The name to search for

sub_division_id pydantic-field

sub_division_id: int | None = None

Wire key SDI

The alliance event subdivision, for its subdivision ranking

event_id pydantic-field

event_id: int | None = None

Wire key EID

The alliance mobilisation or raid event

accepts_reply

accepts_reply(payload: Any) -> bool

Whether a reply is for this list: its LT, when sent, is the one asked for.

Client: LeaderBoardDataProvider.onSearchDataReceived (bundle line 75953) skips a reply only when its LT differs and the dialog's league is the reply's LID or -1. The search sends no league, so the library cannot tell the dialog's and refuses any other LT.

LeaderboardScore pydantic-model

Bases: BasePayload

One entry of an event leaderboard page: an entry of llsp's L.

Client: AGlobalLeaderBoardItem getters (bundle lines 47303-47316) and LeaderBoardDataProvider.onScoreDataReceived (bundle line 75957).

Fields:

Validators:

  • _getters_guard_every_key

rank pydantic-field

rank: ClientInt = -1

Wire key R

Rank on the list

score pydantic-field

score: int | float = -1

Wire key S

Points

player_name pydantic-field

player_name: str = ''

Wire key P

Player name

alliance_name pydantic-field

alliance_name: str = ''

Wire key A

Alliance name, empty without one

instance_id pydantic-field

instance_id: ClientInt | None = None

Wire key I

Game server (instance) the player is on

score_id pydantic-field

score_id: int | str | None = None

Wire key SI

Paging key that search results are matched against; not an owner id

GetRankingListResponse pydantic-model

Bases: BaseResponse

A page of an event leaderboard, the reply to llsp.

Client: LLSPCommand.executeCommand (bundle line 124397), LeaderBoardDataProvider.onScoreDataReceived (bundle line 75957).

Fields:

Validators:

list_type pydantic-field

list_type: int | None = None

Wire key LT

league_type_id pydantic-field

league_type_id: int | None = None

Wire key LID

League type id; None for none

scores pydantic-field

Wire key L

The page's entries

total pydantic-field

total: int = 0

Wire key T

Number of scores on the whole list

GetRankingWindowResponse pydantic-model

Bases: GetRankingListResponse

A page of an event leaderboard around a score, the reply to llsw.

Client: LLSWCommand.executeCommand (bundle line 124411), which hands the reply to LeaderBoardDataProvider.onScoreDataReceived (bundle line 75957) as LLSPCommand does

Fields:

Validators:

LeaderboardSearchResult pydantic-model

Bases: BasePayload

The scores that matched a search in one league: an entry of slse's L.

Client: LeaderBoardDataProvider.onSearchDataReceived (bundle line 75952)

Fields:

Validators:

league_type_id pydantic-field

league_type_id: int | None = None

Wire key LID

The league the scores are in

score_ids pydantic-field

score_ids: list[str]

Wire key L

The matching scores' ids

SearchRankingListResponse pydantic-model

Bases: BaseResponse

The scores that matched a leaderboard search, the reply to slse.

The client throws on a missing L or a result that is not an object; the library reads those as no results, and a score id that is a number as text.

Client: SLSECommand.executeCommand (bundle line 124440), LeaderBoardDataProvider.onSearchDataReceived (bundle line 75952)

Fields:

Validators:

list_type pydantic-field

list_type: int | None = None

Wire key LT

league_type_id pydantic-field

league_type_id: int = -1

Wire key LID

League type id; -1 for none

results pydantic-field

Wire key L

The matches, grouped by league, in the client's order