Community guide format

Guide author23 min readUpdated

In short

The contract between a community guide and Polestar: pack.json, the addon the compiler writes, the rules, import strings, versioning.

On this page
  1. pack.json
  2. The addon
  3. TOC fields
  4. Rules Polestar enforces
  5. Format 3
  6. Format 4
  7. RegisterPack(meta)
  8. RegisterGuide(def)
  9. RegisterMapFrames(frames, packId)
  10. Import strings
  11. Versioning

The contract between a community addon and Polestar, for anyone building tools of their own. Authors who use Polestar's tools never write any of this by hand: they write pack.json and .guide files (GUIDE_FORMAT.md) and polestar compile writes the rest. The handbook is PACKS.md.

On this page: pack.json · The addon · TOC fields · Rules Polestar enforces · Format 3 · Format 4 · Import strings · Versioning

A community addon is an addon for World of Warcraft: Forever that depends on Polestar and registers guides with it. It is built from a pack: a folder with a pack.json and .guide files. In the format, every such addon is a pack; Polestar's own guides are a pack too (Polestar_Guides, pack id guides).

pack.json#

The pack's manifest, in the root of the pack's folder. Every tool works on the pack folder given as its one argument (polestar lint ../joe-routes) or with --pack <dir>, else on the nearest pack.json in the current folder or a parent.

json
{
	"id": "joe-routes",
	"title": "Joe's Routes",
	"author": "Joe",
	"version": "1.2.0",
	"license": "CC-BY-4.0",
	"website": "https://github.com/joe/joe-routes",
	"source": "Written from my own play",
	"folder": "PolestarPack_JoesRoutes"
}
KeyRequiredValue
idYesthe pack id: ^[a-z0-9][a-z0-9-]{1,31}$ (2 to 32 lower-case letters, digits and hyphens, starting with a letter or digit), not reserved (below)
titleYesshown to players; at most 64 characters
authorYesshown to players; at most 64 characters
versionYesone word of letters, digits, ., + and -, starting with a letter or digit, at most 32 characters (1.2.0)
licenseYesthe license of the pack's guides, as an SPDX id where one exists (CC-BY-4.0, CC0-1.0, MIT, GPL-3.0-only); at most 64 characters
websiteNohttps:// or http:// address; at most 200 characters
sourceNowhere the guides come from, in the author's words; at most 200 characters
notesNoone line for the AddOns list, shown when the player points at the pack there; at most 200 characters (default "Community addon for Polestar by <author>")
folderNothe addon's folder name: 1 to 64 letters, digits, _ and -, not Polestar and not starting with Polestar_ (in any case); default PolestarPack_ and the title in CamelCase (Joe's Routes → PolestarPack_JoesRoutes), or the id when the title has no letters or digits

Every value is a string on one line. A missing required field, or a value that breaks these rules, is a lint error; an unknown key is a warning; a | in a displayed value is a warning (it prints literally in game).

The pack folder holds pack.json, the .guide files in any layout, drafts/ (Recorder drafts, compiled only with --include-drafts), dist/ (build output) and the picture files its image lines name (.tga or .blp, at any path inside the folder, such as media/). The compiler reads every .guide file under the folder except in dist/, drafts/, node_modules/ and folders whose name starts with a dot.

The addon#

polestar compile writes dist/<folder>/ (ours: addon/Polestar_Guides/):

text
PolestarPack_JoesRoutes/
  PolestarPack_JoesRoutes.toc
  Pack.lua           Polestar.RegisterPack({ ... })
  MapFrames.lua      Polestar.RegisterMapFrames({ ... }, "joe-routes")
  Guides.lua         one Polestar.RegisterGuide({ ... }) per guide
  LICENSE-DATA.txt   the reference data sources the guides draw on
  LICENSE.txt        the pack folder's LICENSE, copied as is (only when it has one)
  media/cave.tga     each picture an `image` line names, at its path in the pack folder
PolestarPack_JoesRoutes_deDE/   one texts addon per language the pack translates
  PolestarPack_JoesRoutes_deDE.toc
  Texts.lua          Polestar.RegisterGuideTexts("deDE", "joe-routes", { ... })
  LICENSE.txt        as above

Texts addons. A pack whose locales/<locale>/ folder holds translations (docs/TRANSLATING.md, "Guide texts") gets, beside its folder, one load-on-demand addon per language, <folder>_<locale>. Polestar loads the client's own once the pack's files are in, and no other: a German client never reads the French texts. Its TOC carries ## LoadOnDemand: 1, ## Dependencies: Polestar, ## X-Polestar-Texts: <pack id> and ## X-Polestar-Locale: <locale> with the generated-folder marker, and Texts.lua makes one call:

lua
Polestar.RegisterGuideTexts("deDE", "joe-routes", {
	pack = { ["Joe's Routes"] = "Joes Routen" },   -- the pack's title and notes, its guides' folders
	guides = { ["joe-routes/elwynn"] = { ["{npc:240} stands outside the inn."] = "{npc:240} steht vor dem Gasthaus." } },
})

Each table maps a text's English, name tokens and all, to its translation; a text without one shows in English. compile, install and dev write and replace a texts addon folder by the same rule as the pack folder (its marker and X-Polestar-Texts), and remove the one of a language the pack no longer translates; pack puts each into the zip as a folder of its own.

The file set is fixed: a new guide changes Guides.lua, never the file list, so /reload shows every change and only a pack's first install needs a restart of the game (LICENSE.txt is a text file the game never loads); so does the first texts addon of a language, below. Each guide's table is built inside its own function in Guides.lua, so Lua 5.1's constant limit applies per guide.

TOC fields#

text
## Interface: 16001
## Title: Polestar Community: Joe's Routes
## Notes: Community addon for Polestar by Joe
## Author: Joe
## Version: 1.2.0
## Category: Quests
## Group: Polestar
## Dependencies: Polestar
## X-Polestar-Pack: joe-routes
## X-Polestar-Format: 3
## X-Polestar-Needs: 0.2.5
## X-License: CC-BY-4.0
## X-Website: https://github.com/joe/joe-routes
# Generated by `node tools/polestar.js compile` from pack.json and the .guide files. Do not edit: rebuild with that command.

Pack.lua
MapFrames.lua
Guides.lua
FieldValue
Interfacecopied from addon/Polestar/Polestar.toc at build time
Title"Polestar Community: " and pack.json title (the title alone when it already starts so), so the AddOns list reads the addon as a community addon. Community addons built before this prefix carry "Polestar Pack: " until rebuilt; Polestar recognises both. Polestar's own menus show the title without it. Polestar's own pack (guides) gets no prefix
Author, Versionfrom pack.json
Notespack.json notes, else "Community addon for Polestar by <author>"
CategoryQuests, Polestar's category in the AddOns list
GroupPolestar: the AddOns list shows the pack under Polestar's own entry, beside Polestar's own addons. Not written for Polestar's own pack
DependenciesPolestar: the client loads Polestar first
X-Polestar-Packthe pack id. Polestar lists every addon carrying it on the Community guides page, loaded or not
X-Polestar-Formatthe pack format (below)
X-Polestar-Needsthe first Polestar version that reads that format, so an older Polestar can say which version the pack needs
X-Licensepack.json license
X-Websitepack.json website, only when given

The comment line is the generated-folder marker, and with X-Polestar-Pack it makes the folder rule: compile (its output folder), install (dist/<folder>/ and the folder in AddOns, where a symlink is replaced without the check) and dev (the folder in AddOns) write a pack folder only when it is absent, empty, or carrying the generated marker and no other pack's id. Anything else is refused: a folder without the marker as "not a generated community addon", one generated for another pack as holding that pack, with the hint to set folder in pack.json. pack builds a community addon's zip in memory, and play writes into a new temporary folder, so neither touches an existing pack folder.

Rules Polestar enforces#

Core/Guides.lua applies these at registration; lint reports the same ones before a pack is built (tests/packs-parity.test.js holds the two together).

  1. Ids. Pack ids match ^[a-z0-9][a-z0-9-]{1,31}$; guides, forever, lookup, draft, polestar and imported are reserved. Every guide id starts with <pack id>/, except in Polestar's own packs: guides (the addon Polestar_Guides) owns forever/ and draft/, Polestar's internal lookup pack owns lookup/, and imported (the guides pasted as import strings, below) owns every <author>/ that is a valid pack id nobody reserves.
  2. No overrides. A guide id outside its pack's prefix is refused. A pack may register one of its own ids again, which replaces that guide. An id a guide of another pack already holds is refused too ("Guide <id> was refused: <pack> already holds that id."): only an imported guide can share a prefix with a pack.
  3. Clashes. A pack whose id is already registered is refused whole, and the problem names both addons; a pack with a reserved id is refused whole with "Pack id <id> is reserved for Polestar.". Either way the guides and map frames its files register for that id while it loads are dropped, never given to the holder, and the dropped guides are counted in the problem. A pack with a malformed id, no format or a required field missing is refused too ("Pack id <id> is not valid: …", "The pack is missing <field>."), and its guides are dropped without a problem each. A guide registered before its pack is refused (listed once the pack registers); one for a pack that never registers is ignored, since there is no row to list it on.
  4. Map frames. A pack may add Era map frames for maps Polestar's lack; for a map both cover, Polestar's win whatever the load order (lint warns when the bounds differ). Frames for a pack that is not registered, or was refused, are dropped.
  5. Format. A pack declaring a format Polestar does not read is refused whole, with the status too-new and "Needs Polestar <x-polestar-needs> or newer." ("Needs a newer Polestar." when its TOC has no X-Polestar-Needs). An unknown verb or a malformed field inside a known format refuses only that guide.
  6. Visible problems. Every refusal is recorded against its pack with a kind: pack-refused (the pack), guide-refused (one guide, with its id), no-guides (the addon loaded but registered no guides, usually a Lua error in its files). The Community guides page shows them.
  7. Text prints literally. Every string a pack registers is escaped once, at registration (| doubled), so colour and link codes do not work. A guide's ids (id, pack, next, nextChoices) are left as written.
  8. Missing guides keep their place. Saved tabs of a guide whose pack is missing or switched off reopen in their old place when it comes back.
  9. Packs on disk. At login Polestar lists every addon whose TOC carries X-Polestar-Pack, loaded or not, with a status. A registered pack is on, off (switched off on the Community guides page), refused, too-new (a newer format) or broken (registered no guides). One that never registered is, in this order of precedence, too-new, disabled (turned off in the AddOns list), not-loaded (the client did not load it: out of date, a dependency missing) or broken.

Only guides of packs that are on are shown, searched and suggested. Suggestions come from Polestar's guides unless the player picks a pack under Suggest guides from on the Community guides page; only packs with Leveling guides are offered there, and Polestar's guides fill in where the chosen pack has nothing for the character.

Format 3#

The format the compiler writes for a pack whose guides have no when (on a step or a next option), along or label, and whose level goals have no frac (format 4, below). Format 2 is format 1 with the two group keys (a guide's and a goal's) and a guide's reward and why, marked "format 2" below; Polestar 0.2.4 and later read it. Format 3 is format 2 with name tokens in the texts and a guide's names, marked "format 3" below; Polestar 0.2.5 and later read it. A format 1 or 2 pack is the same without them and keeps loading.

Name tokens (format 3). The game's names inside the texts a player reads (a guide's title, why, pitch, pros, cons and reward text; a step's title and note; a goal's note and text) are written {kind:id}: {npc:240}, {object:68}, {item:1528}, {quest:176}, {zone:1429} (a uiMapID), {area:87} (an AreaTable ID), {spell:5487}, {faction:72}, {skill:182} (a profession's skill line). Polestar shows each as the game names it in the client's language (Polestar.Names, the client's map and area names, the profession's translation), else as the guide's names give it, so an English client reads the text as its author wrote it (a text that spells a name otherwise, "Dendrite" for Dendrite Starblaze, is in the guide's written with the author's words). The compiler writes the tokens (GUIDE_FORMAT.md, "Names in texts"); an older Polestar would show them as written, hence the format.

Format 4#

Format 3 with a step's when: the conditions checked during play (GUIDE_FORMAT.md, "Conditions checked during play"), a step's along and label (GUIDE_FORMAT.md, "Along the way"), a level goal's frac: a point inside the level, and a guide's nextWhen: conditional hand-offs (GUIDE_FORMAT.md, "Conditional hand-offs"). Polestar 0.2.6 and later read it. The compiler writes a pack in format 4 only when its guides use one of them; any other pack stays in format 3, so the Polestar it needs does not change. An older Polestar would show every conditional step to every character, stop the route at every along step, count level 12.5 done at level 12 and offer every next option (it ignores keys it does not know), hence the format.

when is a list of predicates, each a table with its kind k, neg = true to turn it around, and the kind's keys; a step applies while all of them hold:

Read the rest of this section535 words
kKeysHolds when
"level"op ("<", "<=", ">", ">="), n; optional frac (0 < frac < 1)the character's level compares so with n; with frac, the level plus the share of it done (XP / XPMax) compares so with n + frac
"onquest"q: a list of quest idsany of them is in the log
"complete"qthe quest is in the log, ready to turn in
"turnedin"qthe quest is handed in
"available"q; optional minLevel, pre, preAll, closedBy (as on an accept)the quest is not in the log or done, the level is reached, one of pre and every one of preAll is done, and none of closedBy is in the log or done
"has"item; optional n (default 1)the bags hold n or more
"zone", "subzone"name; optional areathe zone (subzone) text reads as the area's name in the client's language, or as name
"bound"name; optional areathe bind location reads so
lua
{ title = "Back to Gornek", when = { { k = "onquest", q = { 788 } }, { k = "level", op = "<", n = 6 } }, goals = { ... } }

A when that is not a non-empty list, or a predicate of another kind or without its keys, refuses the guide. Once the route has moved past a step, Polestar stores whether its when held (per character, guide and step) and reads that from then on.

label is a string, the step's name, unique in the guide. along is true, or the label of a later step: the route passes over the step, and its open goals are listed under the steps after it until they are done or the route reaches that labelled step.

lua
{ title = "Boars on the way", along = "camp", goals = { ... } },
{ title = "Goldshire", goals = { ... } },
{ title = "Back at the camp", label = "camp", goals = { ... } },

A label that is not a string or names a second step, or an along that is neither true nor the label of a later step, refuses the guide ("step N: bad label", "step N: duplicate label <name>", "step N: bad along").

frac on a level goal (format 4) is the share of the level's XP the goal asks for, a number with 0 < frac < 1: { t = "level", level = 12, frac = 0.5 } is done at level 12 with XP / XPMax >= 0.5, or at any later level. A goal's frac outside that refuses the guide ("bad frac"); a level predicate's, "bad when".

nextWhen maps a next option to its predicates, the same tables as a step's when: { ["joe-routes/barrens-early"] = { { k = "level", op = "<", n = 12 } } }. Every key must be one of nextChoices, which the compiler writes whenever nextWhen is there, even for one option. Polestar offers an option only while its predicates hold, checked on the character's live state each time it lists the options; with one option offered it goes on to it without asking, with none the guide ends there. Nothing is stored. A nextWhen that is not a table, a key that is not in nextChoices or a malformed predicate list refuses the guide.

RegisterPack(meta)#

lua
local ADDON = ...
Polestar.RegisterPack({
	addon = ADDON, id = "joe-routes", format = 3,
	title = "Joe's Routes", author = "Joe", version = "1.2.0", license = "CC-BY-4.0",
	website = "https://github.com/joe/joe-routes", source = "Written from my own play",
})
FieldRequiredValue
addonYesthe addon's folder name (the file's first vararg)
idYesthe pack id (rule 1)
formatYes3 (1 or 2 for a pack of that format)
title, author, version, licenseYesnon-empty strings
website, source, notesNostrings

Returns ok, err. Call it once, in the first file the TOC loads, before any guide.

RegisterGuide(def)#

One table per guide; returns ok, err.

lua
Polestar.RegisterGuide({
	id = "joe-routes/elwynn", pack = "joe-routes",
	title = "Elwynn Forest", faction = "Alliance", minLevel = 5, maxLevel = 10,
	category = "Leveling", zone = 1429, zoneName = "Elwynn Forest",
	next = "forever/alliance/02-westfall", author = "Joe", version = 1,
	steps = {
		{ title = "Goldshire", goals = {
			{ t = "accept", quest = 62, title = "The Fargodeep Mine", npc = 240, npcName = "Marshal Dughan", minLevel = 4, qlvl = 7, map = 1429, x = 42.11, y = 65.93, f = "e" },
		} },
	},
})
Read the rest of this section1658 words

Guide keys (the compiler writes them in this order):

KeyRequiredValue
idYesthe guide id, under the pack's prefix
packYesthe registered pack id
titleYesstring
factionNo"Alliance" or "Horde"; absent: either
racesNolist of race file names ("Human", "Scourge", "NightElf", …); absent: any
classesNolist of class file names ("WARRIOR", …); absent: any
minLevel, maxLevelNonumbers
categoryNo"Leveling" (default), "Class", "Professions", "Reputation", "Legacy", "Exploration", "Collectibles", "Miscellaneous", "Dungeons"; the compiler passes another name through with a warning
folderNothe guide menu folder
keywordsNolist of extra search words
collectsNolist of item ids the guide obtains
rewardNoformat 2: what the guide's goal gives, shown first in the guide menu's details, in order: { item = 20536, itemName = "Soul Harvester", quality = 3, choice = true } (an item; choice when one of several to choose from; the name shows in the client's quality colour when the item is cached, else the compiled quality's), { spell = 23161, spellName = "Summon Dreadsteed" } or { text = "Shen'dralar reputation" }; an entry with none of item, spell or text refuses the guide
whyNoformat 2: one line on why the goal is worth doing, shown under the reward
zone, zoneNameNothe default zone's uiMapID and name
nextNothe id of the guide that follows: one of the pack's or one of Polestar's
nextChoicesNolist of guide ids offered as a route choice (the first is the default); with nextWhen, every option, even one
nextWhenNoformat 4: { [<guide id>] = { <predicate>, ... } }, an option of nextChoices offered only while its predicates hold (Format 4, above)
pitchNoone line for the route choice card
pros, consNolists of short lines under the pitch
authorNostring; the compiler writes the pack's author when the guide names none
versionNonumber
groupNotrue (format 2): a goal of the guide's route that is not optional needs a group; the guide menu shows a "Group" chip
namesNoformat 3: { ["npc:240"] = "Marshal Dughan", ... }, the English of every name token the guide's texts hold (a token it lacks is shown as written)
writtenNoformat 3: { ["Back to {npc:3649}"] = "Back to Dendrite", ... }, a text whose names its tokens' English in names would spell otherwise, with the author's words an English client reads
stepsYeslist of steps, at least one

Step keys: title (optional), note (optional, player-facing), image (optional: { file = "media\\cave", w = 512, h = 256 }, the step's picture: file its path in the pack's addon folder without the extension, \ between folders, w and h its size in pixels; Polestar shows it under the step's tip, and an imported guide, which has no addon folder, shows none), only (optional: { classes = { ... }, races = { ... }, factions = { ... } }, AND across keys, OR within; Polestar also reads an entry "!X" as "not X", but the compiler writes positive lists only), when (optional, format 4: the step's conditions checked during play, below) and goals (at least one).

Goal keys common to every verb: t (the verb), text (display text), note, optional (true: never blocks the step), only (as on a step), map, x, y (0 to 100, together), z (optional, with them: the height of the NPC or object the point stands for, below), f ("e": the Era map frame; absent: the live frame), r (arrival radius in yards), spots (every place the goal's target spawns, below; with map), group (format 2: the goal needs a group, below).

Verb tRequiredOptional
acceptquest, titlenpc/npcName, object/objName, item/itemName, minLevel, qlvl, pre, preAll, closedBy, preTitles
turninquest, titlenpc/npcName, object/objName, rewards = { choices = { { item, itemName, quality } }, items = { ... }, xp, money }, direct
completequest, title—
killtargettitle, npc, count, quest, oi, spread
collectitem, itemNametitle, count, quest, oi, spread, from, fromAlso, fromId, fromAlsoId, fromKind, fromRank, fromFaction, fromNpcs, npc, npcName, candidates
gotomap, x, ytext, r, area
talknpc, npcName—
interactobject, objNametitle, quest, oi, spread
useitem, itemNametitle, quest, oi
sethomelocation, or candidatesnpc, npcName, area
hearthlocationarea
getfplocationnpc, npcName, node, area
flylocationarea
train—npc, npcName, class, skill, skillLine, rank, rankMax, candidates
buyitem, itemNamecount, npc, npcName, candidates
vendor—npc, npcName, candidates
levellevelfrac (format 4)
notetext—
skillskill, levelskillLine
learnspell, spellNameskillLine, npc/npcName, item/itemName, candidates
craftspell, spellNameitem/itemName, count, untilSkill, skillLine, reagents = { { item, itemName, count } }
repfaction, factionName, standing (1 to 8)—
exploremap, x, ytext
achieveachievement, achievementNamecriteria

The fields:

FieldValue
quest, npc, object, item, spell, faction, achievementnumeric ids
title, npcName, objName, itemName, spellName, factionName, achievementName, targetnames
countnumber to collect, kill or buy; on craft, the number of items to make
oi1-based quest objective index (C_QuestLog.GetQuestObjectives)
minLevelthe level an accept needs; qlvl the quest's level (number or string)
preprerequisite quest ids, any one suffices; preAll every one needed
closedByquest ids that close this one once taken or done
preTitles{ [questId] = title } for the quests those name
profLine, profName, profRankon any goal of a quest only characters with a profession can take: its skill line id, name and the rank it needs; the goal is hidden from characters without it
locationan inn, flight point or zone name as the game shows it
classtrue on train: the nearest trainer of the player's class
skilla profession name; skillLine its skill line id
rank, rankMaxon train: the profession rank (1 Apprentice to 4 Artisan; Polestar also reads a rank name or a skill cap) and its skill cap (written by the compiler, not read by Polestar)
levelon level: the level to reach; on skill: the skill to reach
fracon level (format 4): the share of the level's XP to reach, 0 < frac < 1 (above)
untilSkillon craft: craft until the profession reaches this skill
reagents{ { item, itemName, count } }
standing1 Hated to 8 Exalted
criteriaan achievement's criterion index
zthe height (world z, yards) of the NPC or object the goal's x,y stands for, so the walking route ends on its floor (an Undercity quarter rather than the street above it). Polestar 0.2.4 and later pass it to the walking engine while the arrow leads to that point, and ask again without it when the route found with it fails or ends more than 30 yards above or below it. A spot (spots) and a smart target's candidate (candidates) may carry their own z, which Polestar 0.2.5 and later pass the same way when the arrow leads there (#180). The compiler fills it in (GUIDE_FORMAT.md, "Heights"); a hand-written pack may leave it out. Older Polestar ignores it, so it does not change the format
areathe AreaTable area ID (C_Map.GetAreaInfo) of the place a sethome, hearth, fly or getfp location or a goto label names. Polestar 0.2.5 and later complete a hearth or fly when the zone or subzone text is that area's name in the client's language, accept a sethome bind location that reads so, count a goto arrived when the player goes indoors in that subzone (a cave or mine past its mouth; never for a zone), and show that name in place of the English one. The compiler fills it in (GUIDE_FORMAT.md, "Places by ID"); without it the goal matches by its English name. Older Polestar ignores it, so it does not change the format
nodeon getfp: the taxi node ID (C_TaxiMap nodeID) of its flight point. Polestar 0.2.5 and later count the goal done once the flight map lists that node as known, instead of comparing names. Filled in like area; older Polestar ignores it
spotsspawn places { { x, y, z, r, n, alt } }: centre in the goal's map and frame, its height (optional, see z), radius in yards, spawn count; alt = true for another place the target spawns, which the arrow takes only when the player moves on to it (the world map marks it)
spreadtrue with spots: an area objective, searched spot by spot
from, fromAlso, fromKind, fromRank, fromFactiona collect's main source: name, a second source of the same kind dropping it at 50% or more, "npc" or "object", rank (1 elite, 2 rare elite, 3 boss, 4 rare), faction "A", "H" or "AH"
fromId, fromAlsoIdthe ids of from and fromAlso (of kind fromKind): Polestar names them in the client's language (Polestar.Names), keeping from/fromAlso where it has no name; older Polestar ignores them
npc, npcNamea collect whose guide names the NPC who hands the item over (npc <id>), for an item nothing drops: the target button targets them first. The farm spot a collect … nearest goal settles on carries the same pair
fromNpcsa collect's source creatures where the goal stands: { { npc, npcName } }, the one its point belongs to first, then the other likely sources there, at most 5 (GUIDE_FORMAT.md, "What the compiler fills in"). Newer Polestar gives them to the target button, named in the client's language by their ids, before the item's own droppers from the quest database, whose drop chances rank them over the whole world. Older Polestar ignores them, so it does not change the format
candidatessmart targets: { { npc, npcName, map, x, y, z, f, faction, races, classes, lvl, rate, spawns } } (z optional, see z); Polestar picks the nearest usable one at runtime
rewards{ choices = { { item, itemName, quality } }, items = { ... }, xp, money } (money in copper)
directtrue on a turnin: the quest is handed in straight from the giver's window and never enters the quest log, so the goal needs no accept before it and carries no objectives. Polestar counts it done on the hand-in itself and stores that, so a repeatable stays done after a reload
escorttrue on the accept, complete and objective goals of an escort quest: the guide window says under the goal that the NPC may have to respawn (GUIDE_FORMAT.md, "Escort quests"). Older Polestar ignores it
groupformat 2: the goal needs a group, shown under it in the guide window: { kind = "elite", rank = 1 } (rank 1 elite, 2 rare elite, 3 boss: "Elite: group advised"), { kind = "dungeon", name = "Dire Maul" } ("Dungeon: Dire Maul"), { kind = "raid", name = "Molten Core" } ("Raid: Molten Core"), { kind = "group", size = 3 } ("Group of 3 advised"; without size, "Group advised"). The compiler derives it or takes the author's group [N] (GUIDE_FORMAT.md, "Group content"); Polestar shows nothing for a kind it does not know

Polestar 0.1.0 reads every verb above; 0.2.4 and later read the group keys; 0.2.4 and later read z; 0.2.5 and later read area, node and direct. ARCHITECTURE.md ("Guide data") describes when the Engine counts each goal done.

RegisterMapFrames(frames, packId)#

lua
Polestar.RegisterMapFrames({
	era = {
		[1429] = { c = 0, left = 1535.417, right = -1935.417, top = -7939.583, bottom = -10254.166 },
	},
}, "joe-routes")

The Era world bounds of every map the pack's guides place a goal or a candidate on, where the reference data has them: c the continent, left/right world Y, top/bottom world X. For a map Polestar already covers, Polestar's bounds win (rule 4). Call it after RegisterPack: the frames of a pack that is not registered are dropped.

Import strings#

One compiled guide as one line of text, for a player to paste on Polestar's Import page instead of installing an addon (GUIDE_PACKS.md, "Import strings"). polestar export <file.guide> writes it (tools/lib/pack/importstring.js); addon/Polestar/Core/Import.lua reads it.

text
!PS1!<checksum>!<body>
Read the rest of this section491 words
PartValue
!PS1!the string format, version 1. A later format gets another number; Polestar refuses one it does not know with "This import string needs a newer Polestar."
<checksum>the Adler-32 of the payload (the JSON below, as UTF-8 bytes), 8 hex digits
<body>the payload, compressed with zlib (RFC 1950) and written in base64 (A–Z, a–z, 0–9, +, /, = padding)

Whitespace anywhere in a pasted string is dropped first (a chat window may wrap it). The payload is JSON:

json
{ "format": 3, "license": "CC-BY-4.0", "guide": { "id": "yendi/cozy-sleeping-bag", "title": "Cozy Sleeping Bag", "...": "...", "steps": [ ... ] }, "frames": { "1429": { "c": 0, "left": 1535.417, "right": -1935.417, "top": -7939.583, "bottom": -10254.166 } } }
KeyRequiredValue
formatYesthe guide data's format (above): 4 when a step has when, else 3; Polestar refuses a format it does not read
licenseYesthe license of the guide, from the pack's pack.json
guideYesone guide exactly as RegisterGuide(def) takes it, without pack: the keys above, numbers rounded as Guides.lua writes them, preTitles with the quest ids as the object's keys
framesNoRegisterMapFrames's era table for the maps its goals use, by uiMapID. Polestar takes a frame only for a map the guide's goals or their smart targets stand on, and only where no one (Polestar, a community addon, an earlier import) has registered one

Polestar reads it as data only. The client's own decoders (C_EncodingUtil.DecodeBase64 and DecompressString, the ones the walking maps use) unpack it; Polestar parses the JSON itself (objects, arrays, strings, numbers, true, false; no null, nested 12 deep at most) and builds the guide field by field from the keys this page lists, each with its type. A key it does not know refuses the string, naming the key ("This import string holds a field Polestar does not read (guide.steps[3].goals[1].onLoad)."). Nothing in a string is ever run: the reader never hands its text to loadstring or anything like it.

Then the guide meets the rules above, as a community addon's would, plus:

  • at most 50,000 characters in the string, 256 KB of payload, 60 steps, and 4,096 bytes in any one text or key (a longer one is refused, naming it, before any text is filled in; export refuses it too);
  • its id is <author>/<name>, under no reserved id and no id of a community addon the player has installed (registered or only listed);
  • next and every nextChoices entry name one of Polestar's guides (forever/, draft/) or a guide already imported;
  • Guides.Validate passes (the message is the Community guides page's: "Guide <id> was refused: <reason>.").

A refused string saves nothing. An accepted one is kept account-wide in PolestarDB.imports[<guide id>] as its payload, and registered at every login through the same checks under the reserved pack imported. A guide's version (a number) against the stored one's decides Import, Update (higher) or nothing (equal or lower). Imported guides are listed in the guide menu as Imported under Community guides, credited "By <author> · Imported · <license>", never on the Community guides page, and never suggested.

Versioning#

  • The format number changes when a verb, a field or a field's meaning is added or changed. Polestar ignores keys it does not know; an unknown verb refuses the guide.
  • Polestar keeps reading every format it has shipped, so a published pack keeps working when Polestar updates.
  • The compiler writes the first Polestar version that reads the pack's format into the TOC as X-Polestar-Needs. Format 1 is read by Polestar 0.1.0 and later, format 2 (the group, reward and why keys) by 0.2.4 and later, format 3 (name tokens) by 0.2.5 and later, format 4 (a step's when, along and label, a level goal's frac, a guide's nextWhen) by 0.2.6 and later.
  • A pack in a format this Polestar does not read registers nothing; the Community guides page shows it as too-new with "Needs Polestar <version> or newer." (rule 5).
Was this page helpful?

Start with, or act

  • PlayersInstall and first run
  • PlayersCommunity guides
  • PlayersFAQ
  • Guide authorsWriting a community guide
  • Guide authorsGuide format cheatsheet
  • ContributorsContributing
  • ActionCopy the AddOns folder (Windows)C:\Program Files (x86)\World of Warcraft\_classic_beta_\Interface\AddOns
  • ActionCopy the AddOns folder (macOS)/Applications/World of Warcraft/_classic_beta_/Interface/AddOns
  • ActionFind an optionEvery setting with its default; type its name here to jump to it.
  • ActionEvery slash command