Skip to main content
The Alliance Duel (“VS”) is a weekly alliance-versus-alliance event with one theme per server day. The client learns today’s theme, scoring list, and chest thresholds from one detail read of activity 55000. This page covers that read as the wire shows it, the tables that give the scoring list its meaning, and how goclient’s features use it. Facts confirmed against production on 2026-10-06 are marked confirmed live; everything else is static-analysis-only, from the 1.0.364 Lua and tables.

Finding today’s entry

The init activity entry

The init push’s activity map carries the duel as activity 55000 (type 14, EnumActivity.AllianceCompete). Confirmed live, the entry is:
It’s the only entry in the live activity map, among the types goclient knows, that carries an activityid different from its id (confirmed live, 2026-10-06).

The detail read must send activityid

ActivityInfoData:ParseActivityData sets activityId from the entry’s id, then overrides it with activityid when one is present (ActivityInfoData.lua:126-128, 160-161). AddOneActivity sends that value for the duel, as a UtfString (ActivityListDataManager.lua:158-163):
Confirmed live: asked for "55000", the server answers with a bare {success=true} and no eventList, and no second message follows. The client would ignore that reply anyway: ActivityEventInfoGetMessage processes a reply only when it has no success key (ActivityEventInfoGetMessage.lua:26). Asked for "70000", the reply carries the data directly.

The reply

Top level, confirmed live: activityId ("70000"), type (15, the duel event type), ranking, isFix, and eventList. The client routes it by type == 15 (ActivityListDataManager.lua:1180-1186). Each eventList entry, field names as confirmed live: Pick the entry with t == 15 whose window contains now. Don’t fall back to another entry: a stale entry’s scoring list belongs to another day.

The week

TB:heroactivity#70000, the type-15 row, maps the server weekday to a theme (static-analysis-only): The server day starts at 02:00 UTC, see Identity, login & session. Confirmed live on Tuesday 2026-10-06: theme 110001, with the window ending at 02:00 UTC.

The scoring list

Each id in score is a row of the score table: {id, type, value, points}, where type is a ScoreType, value narrows it, and points is the base award per unit. The client gates its duel prompts by score type, not by theme (AllianceCompeteDataManager.lua:631-654). Its predicted award is units × points × (1 + Σ effects) (GetAllyDuelScoreInfo.lua:39-43). Score types goclient uses (ScoreType, EnumType.lua:15728-15744, plus table types): Type 51 is keyed by the queue an item is used on, not by the item’s own kind. The 2026-10-06 (Base Expansion) entry listed eight ids, confirmed live: goclient embeds the whole table (1,475 rows, table version 39432) in internal/game/duel_scores_gen.go. The generator, internal/game/genduelscores, reads a decoded score.json, checks six known rows before writing, and refuses another table or shifted columns:

The score push

push.act.score.obtain reports earned points for score events: Arms Race always, and the Alliance Duel when type == 15 (PushActScoreObtainMessage.lua:6-16; static-analysis-only). The duel branch feeds GetDuelScoreManager:PushScoreChangeByType, which reads the added points and the alliance total. Nothing in the client waits for it. goclient logs every one at Info as activity score obtained, with afterCmd set to the last request sent on the connection. A push that arrives after a run’s last read isn’t seen.

Chest claims

A chest is claimed with the day’s actId and the chest’s threshold (static-analysis-only):
A chest is claimable when userScore.score has reached its target, its flag isn’t in newRewardFlagList, and its index is within the number of chests the account’s tech unlocks (3, 6, 9, or 12). Confirmed live: newRewardFlagList is a comma-joined list of claimed chest indexes, for example 1,2,3.

How goclient uses it

Every optional feature in goclient can declare the duel score types it earns (Feature.Duel, a list of {Type, Value}; an empty Value matches any). A feature with Hold set runs only on a day whose entry lists one of those types. With no current entry (Sunday, not in an alliance, the read failed) a held feature stays held. -list-features shows each feature’s policy as duel:hold, duel:always, or duel:-.
  • duelPolicy in the session config overrides a feature’s default: {"duelPolicy": {"radar-claims": "always"}}. Valid values are hold and always.
  • -run <feature> of a held feature is refused with the reason; -run-anyway runs it regardless. A spend feature still checks today’s scoring list inside its run, so -run-anyway on a non-scoring day sends nothing.
  • Status line: with any feature enabled, every run logs one alliance duel line: theme, score, next chest, and day end.
The duel-aware features are all off by default; see the README for the full list: duel-speedups-plan, secret-tasks-start-plan, and radar-inventory are read-only previews.

Speed-ups

The speed panel sends items in the items-only form (UISpeedView.lua:1540-1586):
  • The diamond forms are a useGold key (BuildCcdMNewMessage.lua:12-16, QueueCcdMNewMessage.lua:10-12, BuildingCampAccelMessage.lua:9-11) and, for queue.ccd.m.new, an empty itemIDs, which is the client’s own instant finish (ArmyManager.lua:199-203). golloesSpeedTime spends a different free-time pool.
  • No isGold on the wire. The UI passes isGold to queue.ccd.m.new, but the message class never puts it on the wire (QueueCcdMNewMessage.lua:3-17).
  • goclient’s guard. duel-speedups refuses any key outside the three items-only sets above, an empty or malformed item string, and a count above the owned count from init items. It also stops for the rest of the process if a reply shows the diamond balance falling or lacks itemCostArr for the items sent.
Speed-up goods (goods table, static-analysis-only): type2 is the menu, para3 the seconds.

Radar tasks

radar-execute runs the client’s Quick Execute flows for the march-free task types {6, 18, 26, 44}, only while init dataConfig turns on radar_quick_operation_switch (RadarFakeUIMarchManager.lua:105-111):
  • Never sent: reset.detect.event (a diamond re-roll), any battle or march type, and any client-computed result.
  • Help stamina: a Help Teammates task costs 10 stamina unless its cost is 1.
  • Day gating: the talk flow runs only on days whose entry lists type 82, because it claims as it finishes.
The client’s bank model for reference: each refresh adds N tasks into free slots and then into stock, up to detect_max_num, so a refresh with a full bank loses eventNum + N − max(show − slots, 0) − maxNum tasks (detect_level; UIDetectEventCtrl.lua:371-394). goclient claims radar tasks only on days whose entry lists type 82 (radar-claims), and radar-inventory logs the bank figures.

Open questions

  • When each duel action books its points. Start, finish, or claim, per action type: radar tasks, UR secret tasks, power gains, and training. The client’s own Claim All dialog points at the claim for radar tasks (en detect_quick_reward_confirm_text), and the push.act.score.obtain log is in place to settle each one live.
  • Are unclaimed chests mailed at the day’s end? A mail template exists; it isn’t confirmed.
  • Does a finished radar task survive its endTime? The live task list shows finished (state 1) tasks carrying an endTime like unstarted ones.
  • Is an empty or over-count itemIDs charged in diamonds? goclient never sends either, so this stays a static reading.
See also Live validation against production for the reply shapes these features have round-tripped.