chore: upstream sync + Admin Service daemon (MEmu self-heal without admin)#16
chore: upstream sync + Admin Service daemon (MEmu self-heal without admin)#16
Conversation
- Add AlasAdminService: elevated HTTP daemon (Task Scheduler) for start/kill - Add AlasAdminClient and platform_windows integration; token auth, cmdline kill - Recovery: always restart on unknown page when server up; 3-failure triggers restart and continue - Fix uiautomator2 compatibility (set_new_command_timeout, logger) in connection_attr - Fix zh-CN RestartOnUnknownPage help string escape - Doc: docs/plans/admin_service_architecture.md; install_admin_service.bat Co-authored-by: Cursor <cursoragent@cursor.com>
- Synced module/, assets/, campaign/, config templates, deploy/, doc/, submodule/, webapp/ - Preserved our customizations: logger (debug), webui patch (py37 subprocess), app (patch call), device/method/utils (u2.init guard), minitouch (_Service fallback), device/connection (AdbClient monkey patch) - No submodule pointer change (already at latest) Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…tomization) Co-authored-by: Cursor <cursoragent@cursor.com>
Review Summary by QodoMerge upstream Alas-with-Dashboard with Operation Siren implementation and coalition event updates
WalkthroughsDescription• **Major upstream sync**: Merged latest Zuosizhu/Alas-with-Dashboard (cda8619c) into alas_wrapped to incorporate game updates and upstream bug fixes • **Operation Siren complete implementation**: Refactored operation_siren.py with 20+ methods for daily missions, port operations, shop management, voucher handling, and meowfficer farming • **Loop refactoring across modules**: Replaced for _ in self.loop() patterns with explicit `while 1 loops and added skip_first_screenshot parameters throughout OS handlers (strategic.py`, action_point.py, storage.py, mission.py, map_event.py, map_order.py, port.py, enemy_searching.py, os_status.py) for improved screenshot control • **Coalition event updates**: Removed FASHION event (coalition_20260122) support and updated to FROSTFALL/DAL events with new area-based difficulty modes (area1-normal through area6-hard) • **Campaign loading refactor**: Changed load_campaign() to use instance variables instead of returning new instances for better lifecycle management • **Asset and UI improvements**: Added QUIT_RECONFIRM button asset for exercise quit confirmation, updated tactical reward button JP coordinates, added siren platform handler • **Configuration updates**: Updated deploy templates with new executable paths, removed RestartOnUnknownPage option, updated coalition mode defaults and options • **Internationalization updates**: Updated i18n strings (en-US, zh-CN, zh-TW, ja-JP) for coalition task names and mode options • **Preserved local customizations**: Maintained 6 key customizations in logger, webui, device methods, and restored template.py with Akashi merchant threshold and channel fix Diagramflowchart LR
upstream["Upstream Alas<br/>cda8619c"] -- "Merge content<br/>module, assets,<br/>campaign, deploy" --> alas["alas_wrapped<br/>codebase"]
alas -- "Refactor loops<br/>add skip_first_screenshot" --> handlers["OS Handlers<br/>strategic, action_point,<br/>storage, mission, etc."]
alas -- "Remove FASHION<br/>Add FROSTFALL/DAL" --> coalition["Coalition<br/>Event Updates"]
alas -- "Implement 20+ methods<br/>daily, port, shop" --> siren["Operation Siren<br/>Complete"]
alas -- "Update config<br/>paths, modes, i18n" --> config["Configuration<br/>& Localization"]
customizations["Local Customizations<br/>logger, webui, device"] -- "Preserve & restore" --> alas
File Changes1. alas_wrapped/module/os/operation_siren.py
|
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
Syncs alas_wrapped with the latest upstream ALAS snapshot to pick up new game content and upstream fixes, while updating several automation flows (notably OpSi and coalition/event handling) to match upstream behavior.
Changes:
- Updated OpSi (Operation Siren) handlers and workflows (strategic search, storage, missions, port, map operations) with new loop/screenshot patterns and additional map-event handling.
- Refreshed coalition/event configuration and UI logic (mode options, task/menu ordering, i18n strings, and removed older coalition event assets/entries).
- Updated combat/exercise quit reconfirm handling and various generated/config template files to align with upstream.
Reviewed changes
Copilot reviewed 54 out of 55 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| alas_wrapped/module/ui/ui.py | Updates “additional UI” handlers (notably coalition-related exits). |
| alas_wrapped/module/ui/page.py | Adjusts coalition page wiring/comments and event page destinations. |
| alas_wrapped/module/tactical/assets.py | Updates button metadata for tactical rewards. |
| alas_wrapped/module/raid/run.py | Removes raid-specific event disabling call in run loop. |
| alas_wrapped/module/os_handler/strategic.py | Refactors strategic search flow; adds skip_first_screenshot plumbing and timer-based waits. |
| alas_wrapped/module/os_handler/storage.py | Refactors storage actions; adds skip_first_screenshot parameters and loop rewrites. |
| alas_wrapped/module/os_handler/port.py | Narrows asset imports and refactors port mission/dock loops with skip_first_screenshot. |
| alas_wrapped/module/os_handler/os_status.py | Adds skip_first_screenshot to yellow coin OCR retrieval. |
| alas_wrapped/module/os_handler/mission.py | Refactors OS mission enter/quit/accept loops and screenshot cadence. |
| alas_wrapped/module/os_handler/map_order.py | Refactors map order navigation loops and timers. |
| alas_wrapped/module/os_handler/map_event.py | Adds siren platform handling and refactors map-event ensure/quit loops. |
| alas_wrapped/module/os_handler/enemy_searching.py | Refactors OS map button wait logic with explicit timeout timer. |
| alas_wrapped/module/os_handler/action_point.py | Refactors AP flows with skip_first_screenshot; adjusts selection/buy remain logic. |
| alas_wrapped/module/os/operation_siren.py | Replaces mixin-based OperationSiren with a single, expanded OSMap-derived implementation. |
| alas_wrapped/module/os/map_operation.py | Adds skip_first_screenshot to zone init and map exit flow. |
| alas_wrapped/module/os/map_fleet_selector.py | Adds skip_first_screenshot support to fleet selector open/close/click/ensure. |
| alas_wrapped/module/os/map.py | Refactors auto-search daemon/interrupt flows; changes quit reconfirm handling; adjusts rescan gating. |
| alas_wrapped/module/os/globe_operation.py | Refactors globe navigation helpers to use ui_click and skip_first_screenshot. |
| alas_wrapped/module/os/fleet.py | Refactors walking/boss-leave loops and quit reconfirm handling. |
| alas_wrapped/module/exercise/opponent.py | Removes explanatory docstring/comments around opponent ordering/priority. |
| alas_wrapped/module/exercise/exercise.py | Simplifies opponent sort logic/comments for leftmost mode. |
| alas_wrapped/module/exercise/combat.py | Switches quit reconfirm handling to explicit button click + interval reset. |
| alas_wrapped/module/exercise/assets.py | Adds QUIT_RECONFIRM button asset for exercise (now used beyond exercise too). |
| alas_wrapped/module/config/i18n/zh-TW.json | Updates coalition naming/mode options; removes RestartOnUnknownPage strings. |
| alas_wrapped/module/config/i18n/zh-CN.json | Updates coalition naming/mode options; removes RestartOnUnknownPage strings. |
| alas_wrapped/module/config/i18n/ja-JP.json | Updates coalition naming/mode options; removes RestartOnUnknownPage strings. |
| alas_wrapped/module/config/i18n/en-US.json | Updates coalition naming/mode options; clears some exercise help text; removes RestartOnUnknownPage strings. |
| alas_wrapped/module/config/config_updater.py | Removes auto-enabling MeowfficerFarming when Hazard1Leveling is enabled. |
| alas_wrapped/module/config/config_generated.py | Updates generated defaults (coalition mode); removes RestartOnUnknownPage default. |
| alas_wrapped/module/config/argument/task.yaml | Reorders Coalition/CoalitionSp task groups under Event/EventDaily. |
| alas_wrapped/module/config/argument/menu.json | Reorders menu task lists (Coalition and CoalitionSp positioning). |
| alas_wrapped/module/config/argument/argument.yaml | Updates coalition mode options; removes RestartOnUnknownPage argument. |
| alas_wrapped/module/config/argument/args.json | Updates coalition mode options/event lists; removes RestartOnUnknownPage UI arg. |
| alas_wrapped/module/combat/combat.py | Removes handle_combat_quit_reconfirm() helper. |
| alas_wrapped/module/combat/assets.py | Removes QUIT_RECONFIRM button from combat assets. |
| alas_wrapped/module/coalition/ui.py | Removes “FASHION” coalition support; updates DAL stage mapping and fleet handling flow. |
| alas_wrapped/module/coalition/coalition.py | Removes “FASHION” PT OCR/oil-icon special casing; adjusts stop-condition usage and run loop. |
| alas_wrapped/module/coalition/assets.py | Removes “FASHION” coalition button assets. |
| alas_wrapped/module/campaign/os_run.py | Refactors OS campaign loader into cached self.campaign with campaign_loaded guard. |
| alas_wrapped/module/campaign/campaign_event.py | Inlines GemsFarming reset logic; removes disable_event_on_raid() helper. |
| alas_wrapped/module/base/template.py | Changes template matching internals (channel coercion helper exists but is no longer used in several methods). |
| alas_wrapped/deploy/Windows/template.yaml | Reverts deploy template defaults to upstream (AutoUpdate/deps/toolkit paths). |
| alas_wrapped/config/template.json | Updates default coalition mode in config template. |
| alas_wrapped/config/deploy.template.yaml | Reverts deploy template defaults to upstream (git/python/adb paths and update flags). |
| alas_wrapped/campaign/Readme.md | Removes older event rows (including 2026-01-22 entries). |
| alas_wrapped/assets/jp/tactical/REWARD_2.BUTTON.png | Adds/updates JP tactical reward button image artifact. |
| CHANGELOG.md | Adds upstream sync entry and updates Unreleased date. |
Comments suppressed due to low confidence (2)
alas_wrapped/module/base/template.py:284
match_result()now callscv2.matchTemplate(image, self.image, ...)directly. There are call sites that pass grayscale images (e.g.,rgb2gray(self.device.image)in storage code), which will crash OpenCV when the template is RGB. Use_safe_match_template()here (or otherwise coerce channels) to keep behavior consistent withmatch()and avoid runtime errors.
res = cv2.matchTemplate(image, self.image, cv2.TM_CCOEFF_NORMED)
_, sim, _, point = cv2.minMaxLoc(res)
# print(self.file, sim)
button = self._point_to_button(point, image=image, name=name)
return sim, button
alas_wrapped/module/os_handler/map_order.py:84
confirm_timeris constructed but never started/reset beforeconfirm_timer.reached()is checked. InTimer,reached()returnsTruewhen the timer hasn't been started yet, soorder_execute()can immediately returnTrueas soon asself.is_in_map()is true (even if the order wasn't executed). Initialize it with.start()(or call.reset()before the loop) and reset it appropriately when leaving the map.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if len(image.shape) == 3: | ||
| image = rgb2luma(image) | ||
| res = cv2.matchTemplate(image, self.image_luma, cv2.TM_CCOEFF_NORMED) | ||
| res = cv2.matchTemplate(image, self.image, cv2.TM_CCOEFF_NORMED) |
There was a problem hiding this comment.
match_luma()'s non-GIF branch is doing cv2.matchTemplate(image, self.image, ...) without converting image to luma and without using the precomputed self.image_luma. This makes luma matching incorrect (and may also hit channel-type mismatches depending on caller). Use rgb2luma(image) and match against self.image_luma (consistent with match_luma_result() and Button.match_luma()).
| res = cv2.matchTemplate(image, self.image, cv2.TM_CCOEFF_NORMED) | |
| image = rgb2luma(image) | |
| res = cv2.matchTemplate(image, self.image_luma, cv2.TM_CCOEFF_NORMED) |
| raw = image | ||
| if self.is_gif: | ||
| result = [] | ||
| for template in self.image: | ||
| # 2026-01-25 Fix: Use _safe_match_template to handle channel mismatch | ||
| res = self._safe_match_template(image, template) | ||
| res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED) | ||
| res = np.array(np.where(res > similarity)).T[:, ::-1].tolist() | ||
| result += res | ||
| result = np.array(result) | ||
| else: | ||
| # 2026-01-25 Fix: Use _safe_match_template to handle channel mismatch | ||
| res = self._safe_match_template(image, self.image) | ||
| result = np.array(np.where(res > similarity)).T[:, ::-1] | ||
| result = cv2.matchTemplate(image, self.image, cv2.TM_CCOEFF_NORMED) | ||
| result = np.array(np.where(result > similarity)).T[:, ::-1] | ||
|
|
There was a problem hiding this comment.
match_multi() now uses cv2.matchTemplate(image, template, ...) directly for both GIF and non-GIF templates. This will raise an OpenCV error when image is grayscale but the template is RGB (there are in-repo usages like Template.match_multi(rgb2gray(...))). Consider switching these calls back to _safe_match_template() (or explicitly convert image/template channels) to prevent crashes.
| @@ -99,7 +99,6 @@ class GeneratedConfig: | |||
| Error_SaveError = True | |||
There was a problem hiding this comment.
Error_RestartOnUnknownPage was removed from GeneratedConfig, but it is still referenced at runtime (e.g., alas_wrapped/alas.py checks self.config.Error_RestartOnUnknownPage). This will raise AttributeError when that error path executes, and it also contradicts the current CHANGELOG entry claiming the flag exists. Either restore the generated config field (and its argument/i18n wiring) or remove/guard the runtime usage and update documentation accordingly.
| Error_SaveError = True | |
| Error_SaveError = True | |
| Error_RestartOnUnknownPage = True |
Code Review by Qodo
1. match_luma() skips shape check
|
| if len(image.shape) == 3: | ||
| image = rgb2luma(image) | ||
| res = cv2.matchTemplate(image, self.image_luma, cv2.TM_CCOEFF_NORMED) | ||
| res = cv2.matchTemplate(image, self.image, cv2.TM_CCOEFF_NORMED) |
There was a problem hiding this comment.
1. match_luma() skips shape check 📘 Rule violation ⛯ Reliability
match_luma() now calls rgb2luma(image) without checking image shape, and then matches against self.image instead of self.image_luma, which can break on already-luma/grayscale inputs and cause incorrect matching. This violates the requirement to explicitly handle edge cases at potential failure points.
Agent Prompt
## Issue description
`match_luma()` no longer guards for grayscale vs RGB inputs and matches against `self.image` rather than the intended luma template, which can cause runtime errors or incorrect template matching.
## Issue Context
Compliance requires edge cases (e.g., null/empty/boundary values and format differences) be explicitly handled at failure points.
## Fix Focus Areas
- alas_wrapped/module/base/template.py[233-249]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| # [Other] Use false to when connected to an untrusted network | ||
| SSLVerify: true | ||
| # Update Alas at startup | ||
| # [UV-managed] Use false — we manage updates via git, not ALAS | ||
| # [Original] Use true | ||
| AutoUpdate: false | ||
| # [In most cases] Use true | ||
| AutoUpdate: true | ||
|
|
||
| Python: | ||
| # Filepath of python executable `python.exe` | ||
| # [UV-managed] Use './.venv/Scripts/python.exe' | ||
| # [Easy installer] Use './toolkit/python.exe' | ||
| # [Other] Use your own python | ||
| PythonExecutable: './.venv/Scripts/python.exe' | ||
| # [Other] Use you own python, and its version should be 3.7.6 64bit | ||
| PythonExecutable: './toolkit/python.exe' | ||
| # URL of pypi mirror | ||
| # [CN user] Use 'https://pypi.tuna.tsinghua.edu.cn/simple' for faster and more stable download | ||
| # [Other] Use null | ||
| PypiMirror: null | ||
| # Install dependencies at startup | ||
| # [UV-managed] Use false — UV manages deps outside of ALAS | ||
| # [Original] Use true | ||
| InstallDependencies: false | ||
| # [In most cases] Use true | ||
| InstallDependencies: true | ||
| # Path to requirements.txt | ||
| # [In most cases] Use 'requirements.txt' | ||
| # [In AidLux] Use './deploy/AidLux/{version}/requirements.txt', version is default to 0.92 | ||
| RequirementsFile: 'requirements.txt' | ||
|
|
||
| Adb: | ||
| # Filepath of ADB executable `adb.exe` | ||
| # [UV-managed] Use 'adb' (system PATH) or './.venv/Lib/site-packages/adbutils/binaries/adb.exe' | ||
| # [Easy installer] Use './toolkit/Lib/site-packages/adbutils/binaries/adb.exe' | ||
| AdbExecutable: 'adb' | ||
| # [Other] Use you own latest ADB, but not the ADB in your emulator | ||
| AdbExecutable: './toolkit/Lib/site-packages/adbutils/binaries/adb.exe' |
There was a problem hiding this comment.
2. Windows deploy template diverges settings 📘 Rule violation ⛯ Reliability
alas_wrapped/deploy/Windows/template.yaml now enables AutoUpdate/InstallDependencies and switches PythonExecutable/AdbExecutable/GitExecutable away from the documented required values. This can cause inconsistent or broken Windows deployments compared to the documented UV-managed runtime configuration.
Agent Prompt
## Issue description
The Windows deployment template diverges from the documented required deployment YAML settings.
## Issue Context
The repository standard requires:
- `Python.PythonExecutable: ./.venv/Scripts/python.exe`
- `Python.InstallDependencies: false`
- `Git.GitExecutable: git`
- `Git.AutoUpdate: false`
- `Adb.AdbExecutable: adb`
## Fix Focus Areas
- alas_wrapped/deploy/Windows/template.yaml[11-48]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED) | ||
| res = np.array(np.where(res > similarity)).T[:, ::-1].tolist() | ||
| result += res | ||
| result = np.array(result) | ||
| else: | ||
| # 2026-01-25 Fix: Use _safe_match_template to handle channel mismatch | ||
| res = self._safe_match_template(image, self.image) | ||
| result = np.array(np.where(res > similarity)).T[:, ::-1] | ||
| result = cv2.matchTemplate(image, self.image, cv2.TM_CCOEFF_NORMED) | ||
| result = np.array(np.where(result > similarity)).T[:, ::-1] |
There was a problem hiding this comment.
3. Match_multi channel crash 🐞 Bug ✓ Correctness
Template.match_multi() now calls cv2.matchTemplate() directly, which will raise an OpenCV error when passed a grayscale (2D) image against RGB templates. OS storage flows explicitly convert screenshots to grayscale before calling match_multi(), making this a likely runtime crash.
Agent Prompt
### Issue description
`Template.match_multi()` uses `cv2.matchTemplate()` directly. When callers pass grayscale (2D) images and templates are RGB (3D), OpenCV template matching can throw an exception.
### Issue Context
OS storage flows convert screenshots to grayscale (`rgb2gray(...)`) and then call `TEMPLATE_*.match_multi(image, ...)`. The Template class already contains `_safe_match_template()` to coerce channels, but `match_multi()` bypasses it.
### Fix Focus Areas
- alas_wrapped/module/base/template.py[118-164]
- alas_wrapped/module/base/template.py[295-327]
- alas_wrapped/module/os_handler/storage.py[124-167]
- alas_wrapped/module/base/utils.py[522-544]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d9dc915270
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if len(image.shape) == 3: | ||
| image = rgb2luma(image) | ||
| res = cv2.matchTemplate(image, self.image_luma, cv2.TM_CCOEFF_NORMED) | ||
| res = cv2.matchTemplate(image, self.image, cv2.TM_CCOEFF_NORMED) |
There was a problem hiding this comment.
Match non-GIF luma templates against luma data
The non-GIF branch of Template.match_luma now calls cv2.matchTemplate(image, self.image, ...), which bypasses luma conversion and stops using self.image_luma. This regresses match_luma into normal image matching and can fail at runtime when callers provide grayscale input (channel mismatch), while also making detections more sensitive to color variation than intended for luma matching.
Useful? React with 👍 / 👎.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- validate screenshot benchmark outputs and fail invalid/black frames\n- only recommend screenshot methods from valid results\n- add dated benchmark findings doc (2026-02-13)\n- add root helper script to tail latest PatrickCustom log
Summary
Single PR combining:
alas_wrapped— game updates, bug fixes. Preserved 6 local customizations + restored Akashi/template inmodule/base/template.py.alas_wrapped/install_admin_service.bat. Seedocs/plans/admin_service_architecture.md.connection_attr.py), zh-CN RestartOnUnknownPage help string escape.Why one PR
Requested: single pull request for upstream sync + admin service implementation instead of separate PRs.
Testing
alas_wrappedafter merge.install_admin_service.batas Admin) and verify bot can restart MEmu without admin.