Every script starts in one function: the default export of the package's index.js. Asterobot calls it each time a bot starts the package, and the way you write it decides whether that start goes through.
The default export
export default async function behavior(launch) {
// ...
}
Asterobot checks three things before and after calling it:
| Rule | Error when it's broken |
|---|---|
index.js has a default export |
behavior module "library/my-first-script/1.0.0/index.js" has no unambiguous default export |
| The default export is a function | behavior module "library/my-first-script/1.0.0/index.js" default export is not callable |
| The function returns a promise | behavior module "library/my-first-script/1.0.0/index.js" default export must be async and return a Promise |
Declaring the function async takes care of the last rule. Its name doesn't matter, and an arrow function works as well: export default async (launch) => { ... }.
Asterobot calls the function once per start. Only the one in the package the bot plays is called: the default exports of its dependencies never are.
The launch object
The function receives one argument, launch:
| Property | Type | Value |
|---|---|---|
launch.reason |
string | Why the script is starting: "initial", "resume" or "reload". |
launch.generation |
BigInt | 1n for the first script start on the bot's current game connection, then one more for every start on that connection. |
The same two values can be read anywhere in your code, not only in the entry function: session.launchReason and session.behaviorGeneration, from asterobot:bot.
Both start over with a new game connection: when a Full socket bot connects again, or when a Dofus client connects through a MITM bot (man-in-the-middle: the bot relays the game session of a Dofus client someone plays). launch.generation is a BigInt, so compare it with another BigInt, as in launch.generation === 1n.
What to do for each launch reason
launch.reason |
When a script starts with it |
|---|---|
"initial" |
On a Full socket bot's new game connection, as long as no script has started successfully on that connection yet. |
"resume" |
Every later start on the same connection, such as Play after Stop, or Run again. On a MITM bot, every start, the first one included. |
"reload" |
When a running script is replaced without being stopped first. No button in Asteroboard does that. |
Here is what each one asks of your script.
"initial"
Nothing has been sent on this game connection yet. On a Full socket bot the script identifies first, with the lines from Identify and run. Choosing a character and entering the game are up to scripts too.
"resume"
The connection is already identified, either by a script that started on it before or, on a MITM bot, by the Dofus client. The character may already be in the game, in a fight or talking to a merchant. Your script didn't receive the messages that arrived while no script was running, so it can't know what happened before it started: work from what arrives from now on. On a MITM bot, someone also plays that character, and session.shared is true.
"reload"
For the game, this is the same as "resume". The difference is that the previous script was replaced without being stopped, so the messages that arrived during the switch are delivered to the new script instead of being lost. No button in Asteroboard starts a script this way. Handle it like "resume".
Most scripts only need to single out "initial":
import { session, botInfo } from "asterobot:bot";
import { send } from "asterobot:protocol";
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,
});
}
botInfo(`Start ${launch.generation} on this connection (${launch.reason})`);
}
When a start counts
Asterobot waits for the promise the function returns, and the start is complete once the function has returned without an error. Until that happens:
- The Play button keeps loading, and Run in the editor doesn't show its Running on toast.
- The script already runs. The handlers you registered receive messages, Stop works, and the package's action buttons can be used.
- If the function fails before returning, the start doesn't count. On a Full socket connection where no start has succeeded yet, the next start is
"initial"again.
So set things up and return. Returning doesn't stop the script: the handlers you registered keep working until something stops it. Work that goes on for the whole session belongs in handlers, or in a loop you start without awaiting it, as Async and timing shows.
Top-level code
The code at the top level of your modules, outside any function, runs before the entry function is called, at every start, in every module index.js reaches. A dependency's top-level code runs before the code of the module that imports it. Top-level await works.
Asterobot also runs that code when it reads the package's settings, for the Package settings section of the bot's Settings tab and when a bot starts the package. That reading happens without a bot or a game session, so the built-in modules behave differently while it lasts:
| During the reading | What happens |
|---|---|
session |
Holds empty values: gameToken and language are "", and launchReason is "initial". |
values, from asterobot:parameters |
Is empty. |
on(), onTraffic(), intercept(), onChange() |
Return as usual, but the handler is never called. |
botInfo() and the other logging functions |
Do nothing. |
packages.info() |
Works as usual. |
send(), request(), wait(), sleep(), disconnect() and every function of asterobot:gamedata |
Throw an error ending with not available while reading a package's declarations. |
A top-level await on something that can't settle during the reading fails with package awaits at import time, which cannot be answered without a Game session.
When the reading fails, Package settings shows Couldn't read this package's settings with the reason, and the script starts without its settings: every read from values gives undefined. Keep the top level to imports, constants, declarations and function definitions, and put everything else in the entry function.
When the start fails
| What went wrong | What you see |
|---|---|
| The code can't be loaded: a syntax error, an import that doesn't resolve, a module limit exceeded | Only a toast: Couldn't start the behavior after Play, or Couldn't run the package after Run. |
| A rule of the default export is broken | The toast, and The behavior stopped with an error at the top of the bot's page, with one of the texts above. |
| The top-level code throws | The toast, and the alert with a text starting with evaluate behavior module. |
export const parameters or export const actions isn't an object of objects, or an action has no run function |
The toast, and the alert with a text starting with read declarations of. |
The entry function throws, or an await in it rejects without a catch |
The toast, and the alert with a text starting with initialize behavior module. |
| The top-level code or the function runs for 250 ms without awaiting | The toast, and the alert with a text containing JavaScript execution deadline exceeded. |
When the code can't be loaded, open the bot's Settings tab: reading the settings loads the same code, so Package settings shows Couldn't read this package's settings followed by the reason. That section describes the package the bot is set to play, which is the one Run gave it. It doesn't cover an inline script.
Errors covers what can go wrong once the script runs, and Play, stop and reload what each start keeps from the previous one.
Aucun avis à afficher.