Five functions of asterobot:gamedata cover most of what a script looks up: a text by its id, a record by its key, a whole table, the rows matching some values, and records by their name.
import { text, record, table, find, search } from "asterobot:gamedata";
What a row looks like
record(), table() and find() return rows as plain objects:
- The keys are the table's column names, as the Game data tab shows them, such as
name_id. - A column with no value is left out of the object, so reading it gives
undefined. - Numbers are regular numbers, not BigInt, large ids included.
- A column holding nested data comes as an array or an object, whose fields keep the game's own names.
- In a derived table, a table whose name contains two underscores, each row points to its parent row with
_parent_id.
Most tables key their rows with an _id column, and the others with their first column. That key is what record() looks up.
The key and the game's own id
_id is the key Asterobot gives each row. Some tables also have an id column that comes from the game, and an id you read in a message can refer to either one. The example queries of the SQL tab, for instance, find an item type with WHERE id = ... before using its _id. Check in the Game data tab which column your value matches, then use record() for the key, and find() for any other column:
const byKey = record("item_types", 48);
const [byGameId] = (await find("item_types", { id: 48 })) ?? [];
search() and the _names table give you keys directly, ready for record().
text()
text(key) returns one game text, or undefined.
| Argument | What it looks up |
|---|---|
| A whole number | The text with that numeric id, such as the value of a name_id column |
| A string | The text with that text key, such as ui.common.classic |
It returns undefined when no text has that key, and for every key when the game's text file for the script's language is missing. Any other argument, such as a decimal number or a BigInt, throws TypeError: gamedata.text key must be a string or integer.
Texts come in the language the script was started with, which is fr for every start made from Asteroboard today.
record()
record(tableName, key) returns one row, or undefined when the table has no row with that key.
const monster = record("monsters", 147);
if (monster) {
botInfo(text(monster.name_id));
}
| Problem | What it throws, right away |
|---|---|
| The table name is empty or isn't a string | TypeError: gamedata.record table must be a non-empty string |
| The key isn't a whole number, a BigInt included | TypeError: gamedata.record id must be an integer |
| This game version has no table with that name | An error with the message datacenter: no such table: "monstres" |
Ids in message payloads are often BigInt values. Convert one with Number() before looking it up, as in record("monsters", Number(id)).
table()
table(tableName, options) resolves to an array of rows, sorted by key.
| Option | Default | What it does |
|---|---|---|
limit |
200 | How many rows to return, 5,000 at most |
offset |
0 | How many rows to skip first |
A limit or an offset that isn't a positive whole number is ignored, and its default applies. Read a large table a page at a time:
for (let offset = 0; ; offset += 1000) {
const rows = await table("items", { limit: 1000, offset });
if (!rows || rows.length === 0) break;
botInfo("Read", rows.length, "items starting at", offset);
}
table() resolves to undefined when the bot's game version has no game data. It rejects with gamedata.table name must be a non-empty string for an empty name, and with datacenter: no such table: "itmes" for a table that doesn't exist.
find()
find(tableName, filter, options) resolves to the rows whose columns hold every value in filter.
const spawns = await find("sub_areas__monsters", { value: 147 });
Each row found here links a sub-area, its _parent_id, to the monster whose key is 147. Derived tables like this one exist to answer such reverse questions.
| Option | Default | What it does |
|---|---|---|
limit |
200 | How many rows to return, 5,000 at most |
find() has no offset. Pass numbers for columns that hold numbers, and strings for columns that hold text.
| Problem | The promise rejects with |
|---|---|
| The table name is empty or isn't a string | gamedata.find table must be a non-empty string |
filter is empty or isn't an object |
gamedata.find filter must be a non-empty object |
| The table doesn't exist | datacenter: no such table: "sub_area_monsters" |
| The table has no column with that name | datacenter: no such column: table "sub_areas__monsters" has no column "monster" |
Like table(), find() resolves to undefined when there's no game data.
search()
search(query, options) resolves to the records whose name matches the query, best matches first:
[
{ table: "monsters", id: 147, nameId: 7217, name: "Bouftou Royal" },
// ...
]
| Field | What it holds |
|---|---|
table |
The table of the record |
id |
The record's key, ready for record(hit.table, hit.id) |
nameId |
The id of the game text the name comes from |
name |
The name, in the script's language |
| Option | Default | What it does |
|---|---|---|
table |
Every table | Only searches the records of this table |
limit |
200 | How many results to return, 5,000 at most |
Each word of the query matches as the start of a word, so bouftou roy finds Bouftou Royal, and shorter names come first. Without table, the results take turns between the tables that have a match, so the first results show the different kinds of things with that name. A very short query matches so many names that only the first 2,000 are ranked, and the one you want can be missed: add a letter, or a table.
Names are in the script's language, fr for a script started from Asteroboard today, so search for French names.
| Problem | The promise rejects with |
|---|---|
| The query is empty or isn't a string | gamedata.search query must be a non-empty string |
options.table isn't a string |
gamedata.search options.table must be a string |
| The table doesn't exist | datacenter: no such table: "monstres" |
| The game data has no text in the script's language | datacenter: no text for this language: "fr" |
search() resolves to undefined when there's no game data.
A complete example
This package adds a button that looks monsters up by name and writes what it finds to the bot's console:
import { session, botInfo } from "asterobot:bot";
import { record, search } from "asterobot:gamedata";
import { send } from "asterobot:protocol";
export const actions = {
findMonster: {
label: "Find a monster",
args: {
name: { type: "string", label: "Name" },
},
async run({ name }) {
const hits = await search(name, { table: "monsters", limit: 5 });
// undefined rather than an empty array: this game version has no game data.
if (hits === undefined) throw new Error("No game data for this bot's game version");
for (const hit of hits) {
const monster = record(hit.table, hit.id);
botInfo(`${hit.name}: key ${hit.id}, race ${monster?.race}`);
}
},
},
};
export default async function behavior(launch) {
// A new game connection starts with this message. MITM bots never launch
// with "initial": the Dofus client has already identified their session.
if (launch.reason === "initial") {
await send("IdentificationRequest", {
ticketKey: session.gameToken,
languageCode: session.language,
});
}
}
Run the package on a bot, open the bot's Settings tab, and click Find a monster under Package settings. Type a name, in French for now, and click Run in the dialog. An error in run, such as a misspelled table name, shows as a toast and doesn't stop the script. Declare actions covers buttons in detail.
For questions these five functions can't answer, such as a join or a count, see SQL queries.
Aucun avis à afficher.