Skip to content

Events and their scoreboards

The state keeps every running event, as the server's sei and tei packets describe it, and client.events reads an event's scoreboard the way its dialog does: it picks the board, the command and your league for you.

The running events

Events are named by the game data's Event enum, which uses the client's own names: Berimond is Event.FACTION.

from empire_core.gamedata import Event

for event_id, event in client.state.get_events().items():
    print(event_id, type(event).__name__, round(event.remaining_seconds()))

point = client.state.get_event(Event.POINT_EVENT)       # None when it is not running
if point is not None:
    print(point.league_id, point.own_rank, point.own_points)

client.state.is_event_active(Event.SAMURAI_INVASION)

Each event is a model of its kind, with the fields its game dialog reads, and raw holding what the server sent:

Model Events What it adds
ScoredEvent the score events below league_id, own_rank (-1 unranked), own_points, max_points, difficulty_id, parts
PointEvent POINT_EVENT point_event_type
BeggingKnightsEvent BEGGING_KNIGHTS reward_set_id, total_hours
LongTermPointEvent LONG_TERM_POINT_EVENT reward_set_id, upcoming_event_ids
GachaEvent the gacha events reward_set_id, free_chest_reset_time
InvasionEvent the alien, red alien, nomad and samurai invasions, the Berimond invasion your and your alliance's scores in parts (SP and A; FB, FR and A for the Berimond invasion; the nomads' khan camp in AC)
BerimondEvent FACTION league_id, unlocked, own_rank, own_points, your faction
RaidBossEvent ALLIANCE_RAIDBOSS_EVENT your score, the alliance's league_id, alliance_points and alliance_rank, boss_level_points
TempServerEvent TEMP_SERVER daily_reset_time, castle_bought
DonationEvent DONATION_EVENT setting_id
KingdomsLeagueEvent SEASON_LEAGUE remaining_days, original_days, has_alliance_ranking
GlobalEffectEvent, GlobalEffectBuffEvent GLOBAL_EFFECT, GLOBAL_EFFECT_BUFF the effects and their ends, the boosts
AllianceTournamentEvent ALLI_TOURNAMENT your alliance's score in parts["A"]
AllianceMobilizationEvent ALLIANCE_MOBILIZATION_EVENT your and your alliance's scores in parts (SP and A), subdivision_id, division_round_id
LuckyWheelEvent LUCKY_WHEEL, SALE_DAYS_LUCKY_WHEEL the point event score, has_free_spin, pro_mode, win_class, the jackpot sets
SeasonEvent THORNKING, SEAQUEEN, UNDERWORLD unlocked, finished, map_id, reward_id
ArtifactEvent the artifact events parts_found, artifact_league_id, skin_id
TournamentEvent TOURNAMENT own_rank, own_fame_points, booby_prize_min_fame, the ranking (each a TournamentRank)
CampaignEvent, CampaignQuestEvent TIME_LIMITED_CAMPAIGN_EVENT, TIME_LIMITED_CAMPAIGN_QUEST_EVENT the campaign's quests (each a Quest), its reward ids; the quests (QuestId) the quest event times
FameBoosterEvent, AllianceBonusEvent FAMEBOOSTER; PRIME_ALLI_BONUS, ALLI_PAYMENT_BONUS bonus_percent
DiscountSaleEvent RELIC_ENCHANTER_PRIME_SALE, SEASON_PASS_PRIME_SALE discount
SkipForFreeEvent, GiftEvent, FortuneTellerEvent SKIP_FOR_FREE, GGS_GIFT, FORTUNE_TELLER free_skip_seconds; collected; tries, daily_reset_time
SpecialEvent any other event_id, event, end_time, raw

Your points arrive in the server's pep pushes as you score, and client.events.get_own_points(event) asks for them (see Your own rank and points). A later entry for a running event is read over it the way the game reads it: a field it leaves out mostly keeps its value, but the samurai and Berimond invasions build their scores anew from every entry, and Berimond's own rank and points start over with each one. A cqs push reads the running campaign's quests again. The models never change: each packet replaces them.

client.events.refresh() asks the server for the events again and returns them once its answer is applied. client.events.get_active_events() lists the running events with their in-game titles (from the game's language CDN, in the language you ask for; a CDN outage only costs the titles):

for event in client.events.get_active_events(lang="de"):
    print(event.display_name, event.details.remaining_seconds())

client.state.on_event_added, on_event_removed and on_events_updated call you back when an event starts, ends, or a packet updates the events.

An event's scores

scores = client.events.get_scores(Event.FACTION)

print(scores.league_id, scores.total)  # the league shown, and how many are ranked
for row in scores.scores:
    print(row.rank, row.name, row.points, row.alliance_name)

By default you get the page that holds your own score, where the game's dialogs open. Other pages:

client.events.get_scores(Event.FACTION, rank=1)            # the top of your league
client.events.get_scores(Event.FACTION, name="Someone")    # around a name
client.events.get_scores(Event.FACTION, league_id=3)       # the top of another league
client.events.get_scores(Event.ALLIANCE_NOMAD_INVASION, alliance=True)  # the alliance board

Berimond's leagues are its lists: 1 blue, 2 red, 3 and 4 the legendary blue and red. While Berimond is locked to you, its board opens on the top of league 1, as in the game.

Each row is an EventScore: rank, points and name always, and what the board gives besides. Player rows on most boards carry player_id, level, alliance_id and alliance_name; alliance rows carry the alliance's alliance_id and member_count, with its name in both name and alliance_name; the leaderboards (long-term points and donations) carry alliance_name and the game server in instance_id, but no ids.

Your own rank and points

client.events.get_own_points(event) asks the server for your rank and points in a running event, as the game does when an event's dialog opens. Only some events keep them, POINT_EVENTS (in empire_core.events), the ones whose game model has a rank and points:

In the game Event
Battle for Berimond FACTION
Nobility Contest, Marauders' contest, Grand Nobility Prize POINT_EVENT, BEGGING_KNIGHTS, LONG_TERM_POINT_EVENT
the alliance tournament ALLI_TOURNAMENT
the alien, red alien, nomad, samurai and Berimond invasions ALLIANCE_ALIEN_INVASION, RED_ALLIANCE_ALIEN_INVASION, ALLIANCE_NOMAD_INVASION, SAMURAI_INVASION, FACTION_INVASION
the alliance mobilisation and raid boss ALLIANCE_MOBILIZATION_EVENT, ALLIANCE_RAIDBOSS_EVENT
the lucky wheels LUCKY_WHEEL, SALE_DAYS_LUCKY_WHEEL
the gacha events GACHA_DECO2X2, CHRISTMAS_GACHA, EASTER_GACHA, SUMMER_GACHA, ANNIVERSARY_GACHA, HALLOWEEN_GACHA, BLACK_FRIDAY_GACHA, CARNIVAL_GACHA

Any other event (the shops, sales and skins, the donation event) raises EventHasNoPointsError without asking, as the server leaves such a request unanswered (seen live); one that is not running raises EventNotRunningError.

points = client.events.get_own_points(Event.SAMURAI_INVASION)
print(points.own_ranks, points.own_points)   # [yours, your alliance's]

event = client.state.get_event(Event.SAMURAI_INVASION)  # updated by the same reply
if event is not None:
    print(event.parts["SP"].own_points, event.parts["A"].own_points)

The reply's own_ranks, own_points and max_points are lists with one value per score the event keeps (-1 is unranked):

Events The values
the score events (nobility contest, marauders, long-term points, gacha) yours
Berimond, the lucky wheel yours, in the first value only
the alliance tournament your alliance's, in the first value only
the alien, red alien, nomad and samurai invasions, the alliance mobilisation yours, your alliance's
the Berimond invasion the blue players', the red players', your alliance's
the alliance raid boss yours, your alliance's, and boss_level_points

The state applies the reply before the call returns, read the event's way: own_rank and own_points on a score event, Berimond or a lucky wheel, one per part in parts on an invasion, the alliance mobilisation or the alliance tournament, score and alliance_points on the raid boss.

Which events have a board

EVENT_SCOREBOARDS (in empire_core.events) maps each event with a board to its Scoreboard, the boards its dialogs open; client.events.scoreboard(event) reads one, and raises ValueError, naming the events that have one, for an event without.

In the game Event Player board Alliance board
Battle for Berimond FACTION list 30
Nobility Contest POINT_EVENT list 40
Marauders' contest BEGGING_KNIGHTS list 41
War of the Realms (alien invasion) ALLIANCE_ALIEN_INVASION list 44 list 45
Nomad Invasion ALLIANCE_NOMAD_INVASION list 47
Samurai Invasion SAMURAI_INVASION list 52
Grand Nobility Prize (long-term points) LONG_TERM_POINT_EVENT leaderboard 53
Berimond Invasion FACTION_INVASION lists 54 (blue) and 55 (red) list 56
Bloodcrow invasion RED_ALLIANCE_ALIEN_INVASION list 58 list 59
Imperial Patronage (donations) DONATION_EVENT leaderboard 79

Berimond Invasion has a player board per faction; name the one you want:

from empire_core.enums import RankingType

client.events.get_scores(Event.FACTION_INVASION, list_type=RankingType.FACTION_INVASION_PLAYER_RED)

The top of every board of every running event:

for event in client.events.get_running_score_events():
    for board in client.events.scoreboard(event).lists:
        top = client.events.get_scores(event, list_type=board, rank=1)
        print(event.name, board.name, [row.points for row in top.scores[:3]])

The nomad and samurai invasions have only an alliance board in the game. Events the game shows no board for are not listed: the lucky wheel and the gacha events show only your own rank (get_own_points reads it), and the colossus uses a command of its own. The kingdoms league, the alliance mobilisation and raid boss events, the tournaments and the temporary-server and battle-ground boards are not covered yet; the client.ranking calls reach their lists directly.

Leagues and pages

Your league is the one the event's model holds (for the invasions, the one of that board's part), and league 1 when the server named none, as in the game; client.events.get_league_id(event_id, part) reads it, and raises EventNotRunningError for an event that is not running. The donation board has no league.

Most boards are hgh lists, and the server decides how long their pages are (8 rows, seen live); rank asks for the page around that rank. The two leaderboards page with llsp, from rank, page_size rows at a time (10 by default), and a name search there pages to the first hit, or comes back empty when there is none.

hgh is shared with other lists, the alliance search among them. If a request times out, its late reply can still arrive and answer your next hgh call: a reply for a list that is not the event's raises ReplyMismatchError, but one for the same board cannot be told apart, so the call after a timeout may return the late page. Seen live with no event running: asking for your own page of a player board got no answer at all, where rank=1 was answered.

When an event is not running

An event runs from the sei packet that names it until a see push ends it or its time (the entry's RS) runs out; one never given a time has ended, as in the game. The kingdoms league and the global effect events come in tei packets instead and end with a tee (or a see); they carry no RS: the kingdoms league runs while it has more than a day left, and on its last day ends with the season event, and a global effect event ends with the last of its effects.

from empire_core import EventNotRunningError

try:
    client.events.get_scores(Event.SAMURAI_INVASION, alliance=True)
except EventNotRunningError:
    print("not running")

Asking for a board the event does not have (the player board of the nomad invasion, say) raises ValueError.