REPORT
CEPATO Technical Note2026.08.07

How maimai's Boot-time Network Self-check Works Under the Hood, and How to Troubleshoot a Dead Connection

A deep dive, via reverse engineering, into the network self-check a maimai cabinet runs at boot, plus directions for troubleshooting.

Chongxi
Author:Chongxi
steam: —·spotify: —
Tags:#maimai#Networking
ANALYSIS

The previous article roughly covered maimai’s boot initialisation flow; this one digs into the cabinet networking part that has troubled quite a few people

1. Overview#

1.1 Where the self-check sits in the boot chain#

In the previous initialisation article we covered that a maimai cabinet runs two parallel systems: amdaemon (the native background service, responsible for all communication with the operations server) and the Unity game program (responsible for the game)

Boot proceeds in 6 steps, and the network self-check is step 3 (PowerOnProcess.WaitNetWork). The boot initialisation screen shows the status of each check line by line: Check 初始化中 / Good 好 / NA 跳过 / Bad 坏

The goal of the network self-check is to confirm whether the cabinet can communicate with the server normally; the first 5 checks cover the cabinet itself and the LAN, and the last 4 cover connectivity to and liveness of the server

1.2 Chain overview#

PRTCL // PLAINTEXT
[Boot] amdaemon starts
└─ ALPB boot self-check driver
└─ am::network::Tester runs the 9 network self-checks in sequence (am::networktest)
0 IpAddressChecker ← validity of the local IP
1 GatewayChecker ← gateway connectivity
2 DnsLanChecker ← validity of the DNS configuration
3 HopsChecker ← domain resolution + ICMP route hop count
4 LineTypeChecker ← line type (LAN/WAN)
5 AllnetAuthChecker ← ALL.Net auth + download instruction ready (120s)
6 AimeChecker ← CHIME WeChat AIME service liveness probe
7 EmoneyChecker ← e-money (dead item on the mainland server, NA)
8 AimePayChecker ← AimePay (dead item on the mainland server, NA)
│ each result reported via callback
▼
amdaemon app-side NetworkEventListener
→ availability flags (IP / Hops / AllnetAuth)
▼ IPC (NamedPipe)
[Unity] AMDaemon.Network.TestInfo / PowerOnTestInfo
▼ polled every frame
PowerOnProcess.WaitNetWork
(item0 timeout 15s / item5 Net auth 120s)

1.3 The 9 checks at a glance#

itemchecker class namewhat it checksmain dependency
0IpAddressCheckerWhether the local IP is valid (not APIPA)Local / DHCP
1GatewayCheckerWhether the gateway is pingableLocal LAN
2DnsLanCheckerWhether the count of system DNS configs is validLocal LAN
3HopsCheckerWhether the target domain resolves + ICMP hop-by-hop countDNS + target domain
4LineTypeCheckerWhether the line type is LANLocal network interface
5AllnetAuthCheckerALL.Net auth + download instruction readyALL.Net server (120s)
6AimeCheckerMember server livenessCHIME server
7EmoneyCheckerEmoney server liveness (dead item on the mainland server)None
8AimePayCheckerAimePay server liveness (dead item on the mainland server)None

2. Mechanism basics#

2.1 Three-layer structure#

LayerRoleDescription
Native engine layer (am::network)Actually performs network operationsEmbeds the various engine checkers (IpAddr / Gateway / DnsLan / LocationRouter etc.)
Checker layer (am::networktest)Wrapping and judgingThe master Tester strings together the 9 item checkers, each holding a pointer to its corresponding engine
Game program layer (Unity C#)Reading and displayingPolls the status word every frame, translating it into Good / NA / Bad / Check

The actual network operations are performed in the am::network::* engine classes; am::networktest::* is the wrapper layer, responsible for driving the engines and reporting the results to the game program

2.2 Execution state machine#

Each engine checker maintains an internal asynchronous stepper (Waiter), whose lifecycle is:

PRTCL // PLAINTEXT
clear → enqueue (attach the check target) → start
→ step each round → judge whether complete → take the OK/NG flag → write out the result

Result codes: 4 = OK (passed), 5 = NG (failed), 1 = waiting / skipped

2.3 Core fields of an item checker#

Each item checker object contains the following key fields:

  • item number (0–8)
  • state slot: 2 = OK, 3 = running
  • engine result snapshot: copied in once the engine finishes, for the judgement logic to read
  • display status word: the game program reads this byte every frame to decide what is shown on screen

2.4 Drive loop#

The master Tester walks through the 9 items in order each round:

  1. Read the current item’s state slot; skip if already OK
  2. First round: copy in the engine result snapshot, write the state as “running”, trigger the callback
  3. Afterwards: keep stepping the engine until the result is ready
  4. Once all nine are complete, the Tester’s overall state is set to complete

2.5 shouldRun predicate#

Each item has a shouldRun function that decides whether the item needs to run. If it returns false (e.g. there’s no corresponding configured target), the item is skipped outright and the game side shows NA. On the mainland server, item7 (Emoney) and item8 (AimePay) both return false for this reason, and the check item simply isn’t displayed at all

3. The 9 checks one by one#

3.0 IP address (IpAddressChecker)#

Engine class: am::network::IpAddressChecker

Judgement logic:

Read the current IP address of the local network card and judge by address range:

  • 169.254.x.x (APIPA) → NG: this is the temporary address the operating system assigns automatically when DHCP fails or the network cable is disconnected, meaning the cabinet hasn’t obtained a valid IP
  • The specified private address ranges → OK
  • Network card unconfigured (ignore_unconnected switch triggered) → NA, skipped

Timeout: the game program side sets a 15-second timeout for this item; on timeout it’s judged Bad outright

3.1 Gateway (GatewayChecker)#

Engine class: am::network::GatewayChecker (inherits from GatewayPreChecker)

Judgement logic:

Send an ICMP ping to the gateway address in the configuration and wait for a response:

  • ping succeeds → OK
  • ping fails → back off and retry, with the interval starting at 60 seconds and doubling each time, capped at about 900 seconds (about 4 retries); after all fail it’s judged NG

The underlying layer uses ICMP (altr_icmp). In other words, if the gateway router is pingable it passes; a wrong gateway IP, a broken switch or a physically disconnected cable ends in Bad

3.2 Local DNS (DnsLanChecker)#

Engine class: am::network::DnsLanChecker

Judgement logic:

Read the number of DNS servers in the configuration and compare it with the number of DNS servers actually discoverable on the system:

  • The actual number meets the configuration requirement → OK
  • It doesn’t (e.g. DNS wasn’t delivered or the config is wrong) → waiting / NG

The underlying layer calls the system DNS resolution interface (amDnsInit / amDnsResolveNameA/W). However many DNS servers are written in the config, that many have to be discoverable on the system; if DNS isn’t configured or DHCP didn’t deliver it, it fails

3.3 Hops (HopsChecker)#

Engine class: am::network::HopsCounter (the actual resolution is driven by NameResolver)

Judgement logic:

  1. DNS-resolve the target hostname
  2. After a successful resolution, count hops via ICMP
  3. On the C# side, item3 displays the actual hop count
  • DNS resolution fails → NG
  • Resolution succeeds → hop count displayed and OK

It depends on item2 (DnsLan) being normal: if the domain resolves it passes, and the route hop count is displayed at the same time; if it can’t resolve, it’s Bad.

3.4 Line type (LineTypeChecker)#

Engine class: am::network::LocationRouterChecker

Judgement logic:

Detect the current line type:

  • LAN → OK
  • WAN → NG, error message Network type error(WAN)

Internally the engine judges the line’s attribution by parsing the network address and subnet mask, and the underlying layer depends on NameResolver to resolve the target address

3.5 Net auth (AllnetAuthChecker)#

Engine class: alw_auth (the ALL.Net authentication library)

Judgement logic (two conditions, both must be satisfied):

  • Auth passed: complete the authentication handshake with the ALL.Net server
  • Download instruction ready: obtain the netdeliver download instruction URI from the server

If either condition isn’t met → it stays in the Check state and the engine keeps stepping; the game side sets a hard 120-second timeout, and on timeout it’s judged Bad outright

Auth chain stages: DNS resolution → auth handshake → download instruction retrieval. If any link has a communication problem with the server, it hangs in Check until it times out

An item5 Bad blocks the subsequent title server config download flow and the cabinet can’t reach the game screen, so the entire ALL.Net auth flow must complete for it to pass

3.6 CHIME (AimeChecker)#

Engine: Aime network client (AimeNetworkClient)

Judgement logic:

  • Check whether it’s in progress (the engine state meets the running condition)
  • Read the first character of the CHIME client’s liveness result area:
    • equal to 0 → OK
    • non-zero (error code) → running / NG

On the game side this corresponds to Chime.Instance.IsDBAlive, i.e. the database liveness probe for the WeChat AIME service

The later WaitAime stage (Chime.StartDBAliveCheck) probes once more; the two share the same liveness interface

3.7 Emoney (EmoneyChecker) — dead item on the mainland server#

Engine: EmoneyNetworkClient

Judgement logic: first character of the result area equal to 0 → OK, non-zero → NG (same structure as AimeChecker).

Mainland server: no configured target, shouldRun returns false, permanently NA, skipped. There’s no corresponding display slot in the game UI (that position is reused for the TitleServer liveness probe, using OperationManager.IsAliveServer)

3.8 AimePay (AimePayChecker) — dead item on the mainland server#

Engine: AimePayCheckInSequencer

Judgement logic: first character of the result area equal to 0 → OK, non-zero → NG

Mainland server: same as Emoney — no configured target, permanently NA, skipped, no display slot in the game screen

4. Result reporting and on-screen display#

4.1 Transmission chain#

PRTCL // PLAINTEXT
Engine result (OK / NG)
→ item result area snapshot (copied in once the engine finishes)
→ item display status word (read by the game program every frame)
→ network availability flags (IP / Hops / AllnetAuth reported separately)
→ IPC (NamedPipe)
→ C# WaitNetWork translates each item into a screen status

The three availability flags (IP, Hops, AllnetAuth) are maintained separately on the amdaemon app side, for later flows to judge the network’s readiness state

4.2 Game translation rules#

ConditionDisplay
Not busy and not completeNA
Busy and the native side has already judged NGBad 坏
Busy and item0 (IP) exceeds 15 secondsBad 坏
Busy and item5 (Net auth) exceeds 120 secondsBad 坏
Busy and anything elseCheck 初始化中 (blinking)
Complete and passedGood 好 (item3 additionally shows the hop count)
Complete and skippedNA
Complete and failedBad 坏

The timeout fallback is in the game layer (Stopwatch); the error semantics are in the native layer

4.3 Notes on the game display side#

TestModePageNetwork.ItemEnum has 8 positions in total, with the following mapping:

  • item0–5: read Network.TestInfo directly, corresponding to IpAddress / Gateway / LocalDns / HopNum / LineType / AllnetAuth
  • item6 (Aime): read Chime.Instance.IsDBAlive, i.e. the CHIME WeChat AIME liveness result
  • item7 (TitleServer): read OperationManager.IsAliveServer, unrelated to native item7 Emoney (the mainland server reuses that slot)

There is no corresponding position in EnumEnum for Emoney / AimePay

4.4 Dependencies of later flows#

PRTCL // PLAINTEXT
WaitNetWork any Bad or complete
→ NetWorkReady
→ WaitAime (Chime.StartDBAliveCheck probes once more) → AimeReady
→ OperationManager.StartTest() (download config)
→ WaitTitleServer (download succeeded and title server alive)
→ Released: enter normal game scheduling

An item5 (Net auth) Bad blocks the download in the WaitTitleServer stage; a Bad on the other items doesn’t directly block the flow but affects later functionality

4.5 Title server (TitleServer) check notes#

title is not, strictly speaking, a native network self-check. All of the previous 9 items (item0-8) are executed by amdaemon’s native am::network / am::networktest system and belong to WaitNetWork; the native side has no title check item: the title server isn’t in amdaemon’s boot network self-check enumeration, and is unrelated to item7 Emoney either

But the boot flow does check it. The title check happens after the network self-check and before entering normal game scheduling, driven by the Unity game side’s OperationManager:

PRTCL // PLAINTEXT
WaitNetWork complete → NetWorkReady
→ WaitAime (CHIME probes once more) → AimeReady
→ OperationManager.StartTest() (download config from the title server)
→ WaitTitleServer (download succeeded once + title server alive) → Released
  • Judgement condition: WasDownloadSuccessOnce && IsAliveServer — the title config has been downloaded successfully at least once, and the title server is currently alive
  • Who checks it: the Unity game side (OperationManager), not the native self-check framework
  • Stuck symptom: all 9 network self-checks are Good but it still sits on the title screen → most likely stuck at WaitTitleServer
  • Test menu: the 8th position of TestModePageNetwork shows “TitleServer”, reading OperationManager.IsAliveServer: it reuses native item7’s display slot and has no direct relationship with the native self-check

So title’s position can be understood like this: strictly speaking it’s “config download + liveness probe” and doesn’t belong to native boot initialisation, but because the cabinet reaches this step every time it starts and gets checked in the same way, in a broad sense it can count as part of the boot self-check. It’s not within this article’s judgement system for amdaemon’s native 9 network self-checks; it’s the part that comes after the network self-check passes

5. Architecture class names#

5.1 networktest checker layer#

itemclass namekey vftable slots
0am::networktest::IpAddressCheckershouldRun / update
1am::networktest::GatewayCheckershouldRun / update
2am::networktest::DnsLanCheckershouldRun / update
3am::networktest::HopsCheckershouldRun / update
4am::networktest::LineTypeCheckershouldRun / update
5am::networktest::AllnetAuthCheckershouldRun / update
6am::networktest::AimeCheckershouldRun / update
7am::networktest::EmoneyCheckershouldRun / update
8am::networktest::AimePayCheckershouldRun / update

Each item’s vftable has 6 valid slots: dtor / shouldRun / generic ×2 / helper / update. Among them shouldRun (slot1) decides whether to run, and update (slot5) drives the engine’s stepping and writes the result

5.2 am::network engine layer#

engine classcorresponding function
am::network::IpAddressCheckerIP address validity check
am::network::GatewayChecker(: GatewayPreChecker)ICMP ping the gateway
am::network::DnsLanCheckerDNS config count validation
am::network::HopsCounter / NameResolverdomain resolution + route hop count
am::network::LocationRouterCheckerline type (LAN/WAN) determination
alw_auththe whole ALL.Net auth chain
AimeNetworkClientCHIME client liveness probe
EmoneyNetworkClientEmoney liveness probe (dead item on the mainland server)
AimePayCheckInSequencerAimePay liveness probe (dead item on the mainland server)

5.3 Underlying library responsibilities#

libraryfunction
alw_authALL.Net auth, download instruction retrieval
alw_networknetwork stage initialisation
alw_dnsDNS resolution (alwDnsExecServer)
altr_icmpICMP / ping (underlying layer for the gateway and hop count)
alpb_stream_sslSSL connection
alib_pv2_lite(ALPB)boot self-check state machine driver

6. Dependency classification and troubleshooting#

6.1 Dependency classification#

itemFails if the cabinet is offlineFails if the server doesn’t respondHard timeout
0 IP address✅—15s
1 Gateway✅——
2 Local DNS✅——
3 Hops✅✅ (domain unreachable)—
4 Line type✅——
5 Net auth✅✅120s
6 CHIME✅✅—
7 Emoney——dead item on the mainland server, NA
8 AimePay——dead item on the mainland server, NA

6.2 What the on-screen statuses mean#

  • Check ( 初始化中, blinking): the engine is still waiting for a result. A long-lasting Check is mostly seen on item5 / item6; you need to confirm server connectivity
  • Bad ( 坏 ): this item’s judgement failed. An item5 Bad blocks subsequent flows; a Bad on the other items doesn’t directly block the flow
  • NA: this item doesn’t take part in the check. item7 / item8 are always NA on the mainland server, and item0 is also NA in ignore_unconnected mode

6.3 Troubleshooting directions#

item 0–4 failing on the LAN: check the cabinet’s own network configuration: whether the network cable is connected, whether DHCP is delivering IP and DNS normally, whether the gateway address is configured correctly, whether the line is plugged into the LAN

item 5 (Net auth) failing or stuck in 初始化中 for a long time: a connectivity problem between the cabinet and the ALL.Net authentication server. Check whether the cabinet is connected to the internet, and whether the server is online; you can use isMaiDown to check the server’s online status

item 6 (CHIME) failing: a connectivity problem with the CHIME WeChat AIME service. Check the CHIME server’s status and the network path from the cabinet to the server; the site just mentioned is recommended

item 7 / 8: dead items on the mainland server

6.4 Master table of the 9 judgement criteria#

itemCheckerJudgement conditionPass/Fail
0IpAddressCheckerLocal IP isn’t APIPA (169.254.x.x) and is in a valid subnetOK / NG
1GatewayCheckerICMP ping to the gateway succeedsOK / NG
2DnsLanCheckerThe number of discoverable DNS servers meets the configuration requirementOK / NG
3HopsCheckerThe target domain resolves via DNS and the ICMP hop-by-hop completesOK / NG
4LineTypeCheckerThe line type is LANOK / NG
5AllnetAuthCheckerAuth passed and the download instruction is readyOK / timeout Bad
6AimeCheckerThe CHIME liveness result is normalOK / NG
7EmoneyCheckerDead item on the mainland server, shouldRun is falseNA
8AimePayCheckerDead item on the mainland server, shouldRun is falseNA

Appendix#

Wahlap server status monitor: Is the Wahlap server dead? - isMaiDown?

Chongxi
CHONGXIAuthor
Steam—
Spotify—
Published at:2026.08.07
maimai
Series_Associated

PROJECT: MAIMAI

This article is included in a featured series. Visit the project page to explore the full collection.

Classified
Distribution Notice

How maimai's Boot-time Network Self-check Works Under the Hood, and How to Troubleshoot a Dead Connection

Author CHONGXI/2026.08.07/CEPATO-2026-0807-MAIMAI

This work is licensed under CC BY-NC-SA 4.0. Redistribution, remixing and adaptation are allowed for non-commercial use, provided the same license is used and the original author is credited.

SubscribeDonate
Discussion
Archive_Ref
Copyright © 2011–2026 Chongxi & CEPATO
Powered by Astro & CEPATO-NEXT
StatusNon-Collaborative_Entity // CEPATO_NEXT_v27.0

Type a keyword to search