From 36f6dad7a939f0a7ebf5d81e6c50947b4437cabd Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sat, 29 Mar 2025 00:22:40 +0800 Subject: [PATCH 01/12] Fix: Reset interval after itering clue --- module/event_hospital/clue.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/module/event_hospital/clue.py b/module/event_hospital/clue.py index 583ccfaf9..19b924d57 100644 --- a/module/event_hospital/clue.py +++ b/module/event_hospital/clue.py @@ -137,6 +137,7 @@ class HospitalClue(HospitalUI): return button if TEMPLATE_REMAIN_TIMES.match(image): return button + return None def clue_enter(self, skip_first_screenshot=True): """ @@ -205,6 +206,7 @@ class HospitalClue(HospitalUI): return False logger.info(f'is_in_clue -> {invest}') self.device.click(invest) + self.interval_reset(HOSIPITAL_CLUE_CHECK, interval=2) continue if self.appear_then_click(HOSPITAL_BATTLE_PREPARE, offset=(20, 20), interval=2): continue @@ -302,6 +304,7 @@ class HospitalClue(HospitalUI): return False logger.info(f'is_in_clue -> {aside}') self.device.click(aside) + self.interval_reset(HOSIPITAL_CLUE_CHECK, interval=2) continue if self.handle_clue_exit(): continue From eb350ffb87cbb2d1bd1c217a9113e28e5b4f63e8 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sat, 29 Mar 2025 00:32:45 +0800 Subject: [PATCH 02/12] Upd: [EN] use the "Start" word as TEMPLATE_INVEST because bugged game client get "Investigation" overflowed --- assets/en/event_hospital/TEMPLATE_INVEST.png | Bin 1513 -> 1633 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/en/event_hospital/TEMPLATE_INVEST.png b/assets/en/event_hospital/TEMPLATE_INVEST.png index fe9f7ccf9b964e378a337d48447fb0837e99f62a..eff961e782b5bb3d2d0b587a5776258a8f5d0875 100644 GIT binary patch delta 775 zcmV+i1Ni*u3*ihQiBL{Q4GJ0x0000DNk~Le0000r0000G2mk;80F&%!DX}4G1AoFv zL_t(|+GNpBOdNF-!13?=ey_tUEG&fX?to>rDU~RxLeUbMMyb(Mj}3|TWEvWK&=?yD z@#dAQ@kDH5h%v^4rs+YlH7ce^I5gUzv{hB`&V~ocRKT14=g7y@AAnvFE7t} z$~pYfGjUV+ZQ_FfNwDb0O`eg0g1aT-8^ z|2N>gHCbPp0&y?Ijkj%LUCsg9R|yPA00CkvYJ^(C<{o8yyB7=~$6dT1(~U!8=2KGf zL#XH=rFDsRcJxFl_qXx{IuWO^;~s2Zt0q$r9i(Jke1p==f^R#W8Ry;CVt*>M>b1}3 z60e*%5+U6F`m18tcJ>VE`FDfgn38R?2CG%ZHt*ac-OD&kHCb6V|- z)dvTLF!zR=uJryB_P(C+!?ypZuKHTy$`n^OSJraRm2Ur>>x`)6yst-wrQ6+DfACG) zo?9blPbCzX%;j`8>|UV0G=Bxyqy8=FdjB|uw|{RvH;Kv2F1O~r?gi?L(})yjv!~+w zTU6iq#K#q&h6IAY;*Jg+bV0@cRD`;I`HEBnt)T`4BEgqGyg1&Qj6_~dUk4l%f%LvG z(QiBeJ1}T!zYsKErU0dslfN|e(I23WLI@z@ygc3S-Td|%AqoR~Nq^Ik)@S8Lxq2-* zgdNpb;Fth8t<0?tb7#giU<0xnSr#|~GVnp`cZ-FNu~7TBLz9b3Z-2EYN&eY%o;(6H z2naO>KpK1gW)Bk+UW>xqS;k*IsZ%rezQ20+>DYL;n=%Rj4GOS%w0csfX4bE$>wo18 zk%lS#I$O|Ye$%MF(Ojc`x*Qr>y+-vIvhG%8bvs~G?Q002ovPDHLk FV1i#EaX0Z0ML0)YmYAl*J39QY`y)z;PmO>#D3mQ*>n zk{MM}?an6C(q7kCyfmwupjDmn=#jtnll4x+*R~-6WYev>iIpA~so^J8*E<#;z7&l& z&-}QjS6#W}M9mC+w>-UrWuTA!8VKU-?w&l@Qz1Ikt`gq1sYEnX5dfcG{eO*>UGBrc z-qN?a*g8;V>^QWdtWpA%GiCnp-1J+2*i~8+iKo4ll{?j*)Iz4MQ+&I>+CSLPx?EbR z#%}up@pLZSVT_M%7JPFf6RoFP><*S9USI8cEz40?pwiG?E;8 Date: Sat, 29 Mar 2025 00:47:34 +0800 Subject: [PATCH 03/12] Upd: Battle UI Nurse --- assets/cn/combat_ui/PAUSE_Nurse.png | Bin 0 -> 5753 bytes assets/cn/combat_ui/QUIT_Nurse.png | Bin 0 -> 7564 bytes module/combat/combat.py | 6 ++++++ module/combat_ui/assets.py | 2 ++ module/exercise/hp_daemon.py | 13 +++---------- 5 files changed, 11 insertions(+), 10 deletions(-) create mode 100644 assets/cn/combat_ui/PAUSE_Nurse.png create mode 100644 assets/cn/combat_ui/QUIT_Nurse.png diff --git a/assets/cn/combat_ui/PAUSE_Nurse.png b/assets/cn/combat_ui/PAUSE_Nurse.png new file mode 100644 index 0000000000000000000000000000000000000000..7147350aedb0d113cf36d1b6017413ea7f49366f GIT binary patch literal 5753 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

W`(L%*uMZX4DH|m literal 0 HcmV?d00001 diff --git a/assets/cn/combat_ui/QUIT_Nurse.png b/assets/cn/combat_ui/QUIT_Nurse.png new file mode 100644 index 0000000000000000000000000000000000000000..98484586a8422c712928b011d0038245009074a5 GIT binary patch literal 7564 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 literal 0 HcmV?d00001 diff --git a/module/combat/combat.py b/module/combat/combat.py index 0b62b6e9b..97a6d74a7 100644 --- a/module/combat/combat.py +++ b/module/combat/combat.py @@ -106,6 +106,8 @@ class Combat(Level, HPBalancer, Retirement, SubmarineCall, CombatAuto, CombatMan # PAUSE_Pharaoh has random animation, assets should avoid the area in the middle and use match_luma if PAUSE_Pharaoh.match_luma(self.device.image, offset=(10, 10)): return PAUSE_Pharaoh + if PAUSE_Nurse.match_luma(self.device.image, offset=(10, 10)): + return PAUSE_Nurse return False def handle_combat_quit(self, offset=(20, 20), interval=3): @@ -135,6 +137,10 @@ class Combat(Level, HPBalancer, Retirement, SubmarineCall, CombatAuto, CombatMan self.device.click(QUIT_Pharaoh) timer.reset() return True + if QUIT_Nurse.match_luma(self.device.image, offset=offset): + self.device.click(QUIT_Nurse) + timer.reset() + return True return False def ensure_combat_oil_loaded(self): diff --git a/module/combat_ui/assets.py b/module/combat_ui/assets.py index 4b19da285..f09e00967 100644 --- a/module/combat_ui/assets.py +++ b/module/combat_ui/assets.py @@ -12,9 +12,11 @@ PAUSE_HolyLight = Button(area={'cn': (1233, 35, 1250, 57), 'en': (1233, 35, 1250 PAUSE_Iridescent_Fantasy = Button(area={'cn': (1232, 33, 1252, 57), 'en': (1232, 33, 1252, 57), 'jp': (1232, 33, 1252, 57), 'tw': (1232, 33, 1252, 57)}, color={'cn': (124, 139, 190), 'en': (124, 139, 190), 'jp': (124, 139, 190), 'tw': (124, 139, 190)}, button={'cn': (1232, 33, 1252, 57), 'en': (1232, 33, 1252, 57), 'jp': (1232, 33, 1252, 57), 'tw': (1232, 33, 1252, 57)}, file={'cn': './assets/cn/combat_ui/PAUSE_Iridescent_Fantasy.png', 'en': './assets/en/combat_ui/PAUSE_Iridescent_Fantasy.png', 'jp': './assets/jp/combat_ui/PAUSE_Iridescent_Fantasy.png', 'tw': './assets/tw/combat_ui/PAUSE_Iridescent_Fantasy.png'}) PAUSE_Neon = Button(area={'cn': (1228, 32, 1250, 59), 'en': (1228, 32, 1250, 59), 'jp': (1228, 32, 1250, 59), 'tw': (1228, 32, 1250, 59)}, color={'cn': (106, 137, 80), 'en': (106, 137, 80), 'jp': (106, 137, 80), 'tw': (106, 137, 80)}, button={'cn': (1228, 32, 1250, 59), 'en': (1228, 32, 1250, 59), 'jp': (1228, 32, 1250, 59), 'tw': (1228, 32, 1250, 59)}, file={'cn': './assets/cn/combat_ui/PAUSE_Neon.png', 'en': './assets/cn/combat_ui/PAUSE_Neon.png', 'jp': './assets/cn/combat_ui/PAUSE_Neon.png', 'tw': './assets/cn/combat_ui/PAUSE_Neon.png'}) PAUSE_New = Button(area={'cn': (1231, 29, 1253, 56), 'en': (1231, 29, 1253, 56), 'jp': (1231, 29, 1253, 56), 'tw': (1231, 29, 1253, 56)}, color={'cn': (156, 158, 166), 'en': (156, 158, 166), 'jp': (156, 158, 166), 'tw': (156, 158, 166)}, button={'cn': (1231, 29, 1253, 56), 'en': (1231, 29, 1253, 56), 'jp': (1231, 29, 1253, 56), 'tw': (1231, 29, 1253, 56)}, file={'cn': './assets/cn/combat_ui/PAUSE_New.png', 'en': './assets/en/combat_ui/PAUSE_New.png', 'jp': './assets/jp/combat_ui/PAUSE_New.png', 'tw': './assets/tw/combat_ui/PAUSE_New.png'}) +PAUSE_Nurse = Button(area={'cn': (1236, 33, 1251, 50), 'en': (1236, 33, 1251, 50), 'jp': (1236, 33, 1251, 50), 'tw': (1236, 33, 1251, 50)}, color={'cn': (200, 206, 209), 'en': (200, 206, 209), 'jp': (200, 206, 209), 'tw': (200, 206, 209)}, button={'cn': (1236, 33, 1251, 50), 'en': (1236, 33, 1251, 50), 'jp': (1236, 33, 1251, 50), 'tw': (1236, 33, 1251, 50)}, file={'cn': './assets/cn/combat_ui/PAUSE_Nurse.png', 'en': './assets/cn/combat_ui/PAUSE_Nurse.png', 'jp': './assets/cn/combat_ui/PAUSE_Nurse.png', 'tw': './assets/cn/combat_ui/PAUSE_Nurse.png'}) PAUSE_Pharaoh = Button(area={'cn': (1229, 55, 1259, 62), 'en': (1229, 55, 1259, 62), 'jp': (1229, 55, 1259, 62), 'tw': (1229, 55, 1259, 62)}, color={'cn': (164, 119, 78), 'en': (164, 119, 78), 'jp': (164, 119, 78), 'tw': (164, 119, 78)}, button={'cn': (1229, 55, 1259, 62), 'en': (1229, 55, 1259, 62), 'jp': (1229, 55, 1259, 62), 'tw': (1229, 55, 1259, 62)}, file={'cn': './assets/cn/combat_ui/PAUSE_Pharaoh.png', 'en': './assets/cn/combat_ui/PAUSE_Pharaoh.png', 'jp': './assets/cn/combat_ui/PAUSE_Pharaoh.png', 'tw': './assets/cn/combat_ui/PAUSE_Pharaoh.png'}) QUIT = Button(area={'cn': (420, 490, 593, 548), 'en': (473, 508, 567, 532), 'jp': (433, 490, 606, 547), 'tw': (433, 490, 606, 547)}, color={'cn': (199, 122, 114), 'en': (216, 168, 164), 'jp': (196, 120, 113), 'tw': (200, 126, 118)}, button={'cn': (420, 490, 593, 548), 'en': (473, 508, 567, 532), 'jp': (433, 490, 606, 547), 'tw': (433, 490, 606, 547)}, file={'cn': './assets/cn/combat_ui/QUIT.png', 'en': './assets/en/combat_ui/QUIT.png', 'jp': './assets/jp/combat_ui/QUIT.png', 'tw': './assets/tw/combat_ui/QUIT.png'}) QUIT_Christmas = Button(area={'cn': (400, 506, 477, 525), 'en': (410, 507, 469, 524), 'jp': (400, 506, 477, 525), 'tw': (400, 506, 477, 525)}, color={'cn': (195, 139, 166), 'en': (207, 166, 185), 'jp': (195, 139, 166), 'tw': (195, 139, 166)}, button={'cn': (400, 506, 477, 525), 'en': (410, 507, 469, 524), 'jp': (400, 506, 477, 525), 'tw': (400, 506, 477, 525)}, file={'cn': './assets/cn/combat_ui/QUIT_Christmas.png', 'en': './assets/en/combat_ui/QUIT_Christmas.png', 'jp': './assets/cn/combat_ui/QUIT_Christmas.png', 'tw': './assets/cn/combat_ui/QUIT_Christmas.png'}) QUIT_Iridescent_Fantasy = Button(area={'cn': (391, 522, 464, 540), 'en': (402, 507, 460, 523), 'jp': (391, 522, 464, 540), 'tw': (391, 522, 464, 540)}, color={'cn': (121, 73, 79), 'en': (255, 174, 164), 'jp': (108, 60, 70), 'tw': (121, 73, 79)}, button={'cn': (391, 522, 464, 540), 'en': (402, 507, 460, 523), 'jp': (391, 522, 464, 540), 'tw': (391, 522, 464, 540)}, file={'cn': './assets/cn/combat_ui/QUIT_Iridescent_Fantasy.png', 'en': './assets/en/combat_ui/QUIT_Iridescent_Fantasy.png', 'jp': './assets/jp/combat_ui/QUIT_Iridescent_Fantasy.png', 'tw': './assets/cn/combat_ui/QUIT_Iridescent_Fantasy.png'}) QUIT_New = Button(area={'cn': (394, 506, 467, 524), 'en': (404, 506, 463, 523), 'jp': (394, 506, 467, 524), 'tw': (394, 506, 467, 524)}, color={'cn': (255, 180, 171), 'en': (255, 195, 187), 'jp': (255, 180, 171), 'tw': (255, 180, 171)}, button={'cn': (394, 506, 467, 524), 'en': (404, 506, 463, 523), 'jp': (394, 506, 467, 524), 'tw': (394, 506, 467, 524)}, file={'cn': './assets/cn/combat_ui/QUIT_New.png', 'en': './assets/en/combat_ui/QUIT_New.png', 'jp': './assets/cn/combat_ui/QUIT_New.png', 'tw': './assets/cn/combat_ui/QUIT_New.png'}) +QUIT_Nurse = Button(area={'cn': (400, 507, 477, 525), 'en': (400, 507, 477, 525), 'jp': (400, 507, 477, 525), 'tw': (400, 507, 477, 525)}, color={'cn': (254, 193, 170), 'en': (254, 193, 170), 'jp': (254, 193, 170), 'tw': (254, 193, 170)}, button={'cn': (400, 507, 477, 525), 'en': (400, 507, 477, 525), 'jp': (400, 507, 477, 525), 'tw': (400, 507, 477, 525)}, file={'cn': './assets/cn/combat_ui/QUIT_Nurse.png', 'en': './assets/cn/combat_ui/QUIT_Nurse.png', 'jp': './assets/cn/combat_ui/QUIT_Nurse.png', 'tw': './assets/cn/combat_ui/QUIT_Nurse.png'}) QUIT_Pharaoh = Button(area={'cn': (400, 507, 477, 525), 'en': (400, 507, 477, 525), 'jp': (400, 507, 477, 525), 'tw': (400, 507, 477, 525)}, color={'cn': (204, 132, 108), 'en': (204, 132, 108), 'jp': (204, 132, 108), 'tw': (204, 132, 108)}, button={'cn': (400, 507, 477, 525), 'en': (400, 507, 477, 525), 'jp': (400, 507, 477, 525), 'tw': (400, 507, 477, 525)}, file={'cn': './assets/cn/combat_ui/QUIT_Pharaoh.png', 'en': './assets/cn/combat_ui/QUIT_Pharaoh.png', 'jp': './assets/cn/combat_ui/QUIT_Pharaoh.png', 'tw': './assets/cn/combat_ui/QUIT_Pharaoh.png'}) diff --git a/module/exercise/hp_daemon.py b/module/exercise/hp_daemon.py index f9f62d6f6..3ab53d96a 100644 --- a/module/exercise/hp_daemon.py +++ b/module/exercise/hp_daemon.py @@ -1,16 +1,8 @@ from module.base.base import ModuleBase from module.base.timer import Timer from module.base.utils import color_bar_percentage -from module.combat_ui.assets import ( - PAUSE, - PAUSE_Christmas, - PAUSE_Cyber, - PAUSE_HolyLight, - PAUSE_Iridescent_Fantasy, - PAUSE_Neon, - PAUSE_New, - PAUSE_Pharaoh, -) +from module.combat_ui.assets import (PAUSE, PAUSE_Christmas, PAUSE_Cyber, PAUSE_HolyLight, PAUSE_Iridescent_Fantasy, + PAUSE_Neon, PAUSE_New, PAUSE_Nurse, PAUSE_Pharaoh) from module.exercise.assets import * from module.logger import logger @@ -78,6 +70,7 @@ class HpDaemon(ModuleBase): PAUSE_Cyber, PAUSE_HolyLight, PAUSE_Pharaoh, + PAUSE_Nurse, ]: self.attacker_hp = self._calculate_hp(image, area=ATTACKER_HP_AREA_New.area, reverse=True) self.defender_hp = self._calculate_hp(image, area=DEFENDER_HP_AREA_New.area, reverse=True) From 2f81c226e9e2075d22064b80a2637d4d75ab7d2d Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sat, 29 Mar 2025 01:01:41 +0800 Subject: [PATCH 04/12] Upd: [EN] use the "vesti" word as TEMPLATE_INVEST because word "Start" may have a tick icon over it --- assets/en/event_hospital/TEMPLATE_INVEST.png | Bin 1633 -> 1547 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/en/event_hospital/TEMPLATE_INVEST.png b/assets/en/event_hospital/TEMPLATE_INVEST.png index eff961e782b5bb3d2d0b587a5776258a8f5d0875..374a225575bcd03366b098fc5be8089eb5a2bc2c 100644 GIT binary patch delta 689 zcmV;i0#5zm42ujQiBL{Q4GJ0x0000DNk~Le0000q0000D2mk;80K;YyQn4Xv1AlBu zL_t(|+C+~_PZLo9M!&iDGM&=52<2gGZFvMrTZ+6KNievP#ISH-LYiP?qZ|H(3x9(# z(S^o^n&?8nu&@otBg7(LX$1rbH{}kzvVeOi%%E|LwS8bQQL>&SQetc zyV_jHPOSi7zHYCi?4{=TwQye*-+zo3zbstsFpBS4MNHv~QxN5tayA07V<2dyJRf|X z0`WhH+9?XD%F&))8_4CDVk|{tIGiH$ibHA#q^L>S*b}|?W!rHk@47x9Lh8TaP?BpT zP$ZI#073}^D>0cQu)UOQ3Q>WY1(=qQiMR5w-VJ4%S#OfU`Xcf>ZmD^Js8q1lEYI7IF}XTSi@Ps?Qcyr(TJoUG z9GYk$e07UhJurD68pN%W3aX4>G1uK&PsbUEhX2#R3cKHBF)|lLaTc&pklNaRa?IB2 z^Le9zXDjD~X#l_oqiRAHBY*aQuFL?Zoajrl41l@-ZU0p!5@~exoIYX{`gy2Rfaz-T z)Kb&w;2T*Lzq3Apj~x>3Jkr3d)t}O{gKefRk+XtXaEcrN1vqsYTF67nE0hm2fN>!z z*d?nwEdR<8>280SL7Fb{gSUpz>oNpVPLEroI2mVDS0`js6O_*Ylz)%mHcNlIW^tMy z!+*!!u%p&NO{h?3{KP$&8+`+Sf$30_c?aR!KLB9!>3*lnrwiLhz*(}j$^|_)`3_7Q z@pljxY5e<12msP;GmNbXfCJ3e>~W|^Khs%=o?x{TC)uxAMmT9|yU6mHL?{R){$~I= XOZwOzQhe6{0000rDU~RxLeUbMMyb(Mj}3|TWEvWK&=?yD z@#dAQ@kDH5h%v^4rs+YlH7ce^I5gUzv{hB`&V~ocRKT14=g7y@AAnvFE7t} z$~pYfGjUV+ZQ_FfNwDb0O`eg0g1aT-8^ z|2N>gHCbPp0&y?Ijkj%LUCsg9R|yPA00CkvYJ^(C<{o8yyB7=~$6dT1(~U!8=2KGf zL#XH=rFDsRcJxFl_qXx{IuWO^;~s2Zt0q$r9i(Jke1p==f^R#W8Ry;CVt*>M>b1}3 z60e*%5+U6F`m18tcJ>VE`FDfgn38R?2CG%ZHt*ac-OD&kHCb6V|- z)dvTLF!zR=uJryB_P(C+!?ypZuKHTy$`n^OSJraRm2Ur>>x`)6yst-wrQ6+DfACG) zo?9blPbCzX%;j`8>|UV0G=Bxyqy8=FdjB|uw|{RvH;Kv2F1O~r?gi?L(})yjv!~+w zTU6iq#K#q&h6IAY;*Jg+bV0@cRD`;I`HEBnt)T`4BEgqGyg1&Qj6_~dUk4l%f%LvG z(QiBeJ1}T!zYsKErU0dslfN|e(I23WLI@z@ygc3S-Td|%AqoR~Nq^Ik)@S8Lxq2-* zgdNpb;Fth8t<0?tb7#giU<0xnSr#|~GVnp`cZ-FNu~7TBLz9b3Z-2EYN&eY%o;(6H z2naO>KpK1gW)Bk+UW>xqS;k*IsZ%rezQ20+>DYL;n=%Rj4GOS%w0csfX4bE$>wo18 zk%lS#I$O|Ye$%MF(Ojc`x*Qr>y+-vIvhG%8bvs~G?Q002ovPDHLk FV1h-4aN_^~ From c4013aaea9c6d5194e8b70e617ce7fd17d8559da Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sat, 29 Mar 2025 01:09:54 +0800 Subject: [PATCH 05/12] Upd: [TW] QUIT_New --- assets/tw/combat_ui/QUIT_New.png | Bin 0 -> 7608 bytes module/combat_ui/assets.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 assets/tw/combat_ui/QUIT_New.png diff --git a/assets/tw/combat_ui/QUIT_New.png b/assets/tw/combat_ui/QUIT_New.png new file mode 100644 index 0000000000000000000000000000000000000000..076a00e630d4c8ac2b39923cfe5975f44cad4314 GIT binary patch literal 7608 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 L>D?>Uw0r&^CW785 literal 0 HcmV?d00001 diff --git a/module/combat_ui/assets.py b/module/combat_ui/assets.py index f09e00967..9e076c42f 100644 --- a/module/combat_ui/assets.py +++ b/module/combat_ui/assets.py @@ -17,6 +17,6 @@ PAUSE_Pharaoh = Button(area={'cn': (1229, 55, 1259, 62), 'en': (1229, 55, 1259, QUIT = Button(area={'cn': (420, 490, 593, 548), 'en': (473, 508, 567, 532), 'jp': (433, 490, 606, 547), 'tw': (433, 490, 606, 547)}, color={'cn': (199, 122, 114), 'en': (216, 168, 164), 'jp': (196, 120, 113), 'tw': (200, 126, 118)}, button={'cn': (420, 490, 593, 548), 'en': (473, 508, 567, 532), 'jp': (433, 490, 606, 547), 'tw': (433, 490, 606, 547)}, file={'cn': './assets/cn/combat_ui/QUIT.png', 'en': './assets/en/combat_ui/QUIT.png', 'jp': './assets/jp/combat_ui/QUIT.png', 'tw': './assets/tw/combat_ui/QUIT.png'}) QUIT_Christmas = Button(area={'cn': (400, 506, 477, 525), 'en': (410, 507, 469, 524), 'jp': (400, 506, 477, 525), 'tw': (400, 506, 477, 525)}, color={'cn': (195, 139, 166), 'en': (207, 166, 185), 'jp': (195, 139, 166), 'tw': (195, 139, 166)}, button={'cn': (400, 506, 477, 525), 'en': (410, 507, 469, 524), 'jp': (400, 506, 477, 525), 'tw': (400, 506, 477, 525)}, file={'cn': './assets/cn/combat_ui/QUIT_Christmas.png', 'en': './assets/en/combat_ui/QUIT_Christmas.png', 'jp': './assets/cn/combat_ui/QUIT_Christmas.png', 'tw': './assets/cn/combat_ui/QUIT_Christmas.png'}) QUIT_Iridescent_Fantasy = Button(area={'cn': (391, 522, 464, 540), 'en': (402, 507, 460, 523), 'jp': (391, 522, 464, 540), 'tw': (391, 522, 464, 540)}, color={'cn': (121, 73, 79), 'en': (255, 174, 164), 'jp': (108, 60, 70), 'tw': (121, 73, 79)}, button={'cn': (391, 522, 464, 540), 'en': (402, 507, 460, 523), 'jp': (391, 522, 464, 540), 'tw': (391, 522, 464, 540)}, file={'cn': './assets/cn/combat_ui/QUIT_Iridescent_Fantasy.png', 'en': './assets/en/combat_ui/QUIT_Iridescent_Fantasy.png', 'jp': './assets/jp/combat_ui/QUIT_Iridescent_Fantasy.png', 'tw': './assets/cn/combat_ui/QUIT_Iridescent_Fantasy.png'}) -QUIT_New = Button(area={'cn': (394, 506, 467, 524), 'en': (404, 506, 463, 523), 'jp': (394, 506, 467, 524), 'tw': (394, 506, 467, 524)}, color={'cn': (255, 180, 171), 'en': (255, 195, 187), 'jp': (255, 180, 171), 'tw': (255, 180, 171)}, button={'cn': (394, 506, 467, 524), 'en': (404, 506, 463, 523), 'jp': (394, 506, 467, 524), 'tw': (394, 506, 467, 524)}, file={'cn': './assets/cn/combat_ui/QUIT_New.png', 'en': './assets/en/combat_ui/QUIT_New.png', 'jp': './assets/cn/combat_ui/QUIT_New.png', 'tw': './assets/cn/combat_ui/QUIT_New.png'}) +QUIT_New = Button(area={'cn': (394, 506, 467, 524), 'en': (404, 506, 463, 523), 'jp': (394, 506, 467, 524), 'tw': (393, 506, 470, 524)}, color={'cn': (255, 180, 171), 'en': (255, 195, 187), 'jp': (255, 180, 171), 'tw': (255, 198, 190)}, button={'cn': (394, 506, 467, 524), 'en': (404, 506, 463, 523), 'jp': (394, 506, 467, 524), 'tw': (393, 506, 470, 524)}, file={'cn': './assets/cn/combat_ui/QUIT_New.png', 'en': './assets/en/combat_ui/QUIT_New.png', 'jp': './assets/cn/combat_ui/QUIT_New.png', 'tw': './assets/tw/combat_ui/QUIT_New.png'}) QUIT_Nurse = Button(area={'cn': (400, 507, 477, 525), 'en': (400, 507, 477, 525), 'jp': (400, 507, 477, 525), 'tw': (400, 507, 477, 525)}, color={'cn': (254, 193, 170), 'en': (254, 193, 170), 'jp': (254, 193, 170), 'tw': (254, 193, 170)}, button={'cn': (400, 507, 477, 525), 'en': (400, 507, 477, 525), 'jp': (400, 507, 477, 525), 'tw': (400, 507, 477, 525)}, file={'cn': './assets/cn/combat_ui/QUIT_Nurse.png', 'en': './assets/cn/combat_ui/QUIT_Nurse.png', 'jp': './assets/cn/combat_ui/QUIT_Nurse.png', 'tw': './assets/cn/combat_ui/QUIT_Nurse.png'}) QUIT_Pharaoh = Button(area={'cn': (400, 507, 477, 525), 'en': (400, 507, 477, 525), 'jp': (400, 507, 477, 525), 'tw': (400, 507, 477, 525)}, color={'cn': (204, 132, 108), 'en': (204, 132, 108), 'jp': (204, 132, 108), 'tw': (204, 132, 108)}, button={'cn': (400, 507, 477, 525), 'en': (400, 507, 477, 525), 'jp': (400, 507, 477, 525), 'tw': (400, 507, 477, 525)}, file={'cn': './assets/cn/combat_ui/QUIT_Pharaoh.png', 'en': './assets/cn/combat_ui/QUIT_Pharaoh.png', 'jp': './assets/cn/combat_ui/QUIT_Pharaoh.png', 'tw': './assets/cn/combat_ui/QUIT_Pharaoh.png'}) From 4768c9e46c6b47e3a439a4aec67154eeb226ec47 Mon Sep 17 00:00:00 2001 From: sui_feng <115386623+sui-feng-cb@users.noreply.github.com> Date: Sat, 29 Mar 2025 01:11:26 +0800 Subject: [PATCH 06/12] Add: War Archives 20220324 (#4698) --- .../war_archives/TEMPLATE_VIRTUAL_TOWER.png | Bin 0 -> 7081 bytes campaign/Readme.md | 1 + campaign/war_archives_20220324_cn/sp1.py | 97 ++++++++++++++++++ campaign/war_archives_20220324_cn/sp2.py | 80 +++++++++++++++ campaign/war_archives_20220324_cn/sp3.py | 81 +++++++++++++++ campaign/war_archives_20220324_cn/sp4.py | 81 +++++++++++++++ config/template.json | 2 +- module/config/argument/args.json | 11 +- module/config/i18n/en-US.json | 1 + module/config/i18n/ja-JP.json | 1 + module/config/i18n/zh-CN.json | 1 + module/config/i18n/zh-TW.json | 1 + module/war_archives/assets.py | 1 + module/war_archives/dictionary.py | 1 + 14 files changed, 353 insertions(+), 6 deletions(-) create mode 100644 assets/cn/war_archives/TEMPLATE_VIRTUAL_TOWER.png create mode 100644 campaign/war_archives_20220324_cn/sp1.py create mode 100644 campaign/war_archives_20220324_cn/sp2.py create mode 100644 campaign/war_archives_20220324_cn/sp3.py create mode 100644 campaign/war_archives_20220324_cn/sp4.py diff --git a/assets/cn/war_archives/TEMPLATE_VIRTUAL_TOWER.png b/assets/cn/war_archives/TEMPLATE_VIRTUAL_TOWER.png new file mode 100644 index 0000000000000000000000000000000000000000..5edc39da9ce5bfb75d00fdf9b1c7c6c4b0e10cac GIT binary patch literal 7081 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 Date: Sun, 30 Mar 2025 00:00:03 +0800 Subject: [PATCH 07/12] Fix: [JP] Handle overflowed text in hospital aside --- module/event_hospital/clue.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/module/event_hospital/clue.py b/module/event_hospital/clue.py index 19b924d57..dce244e6d 100644 --- a/module/event_hospital/clue.py +++ b/module/event_hospital/clue.py @@ -260,7 +260,8 @@ class HospitalClue(HospitalUI): area = button.area search = CLUE_LIST.area # Search if there's any cyan - area = (search[0], area[1], search[2], area[3]) + # JP has text overflowed, set right to 308 + area = (search[0], area[1], 308, area[3]) return self.image_color_count(area, color=(74, 130, 148), threshold=221, count=20) def iter_aside(self): From 3a1e851cad6c5fe7df5c27ea9a06b33e2ec2cd79 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sun, 30 Mar 2025 00:22:42 +0800 Subject: [PATCH 08/12] Fix: [EN] Smaller invest button EN has 2 kinds of button, first and second button have smaller text and third button has normal size text --- assets/cn/event_hospital/TEMPLATE_INVEST2.png | Bin 0 -> 1306 bytes module/event_hospital/assets.py | 1 + module/event_hospital/clue.py | 2 ++ 3 files changed, 3 insertions(+) create mode 100644 assets/cn/event_hospital/TEMPLATE_INVEST2.png diff --git a/assets/cn/event_hospital/TEMPLATE_INVEST2.png b/assets/cn/event_hospital/TEMPLATE_INVEST2.png new file mode 100644 index 0000000000000000000000000000000000000000..c7fba8b89e26e69ac9e02c33e6de5d3665cb19a9 GIT binary patch literal 1306 zcmV+#1?BpQP)KLZ*U+lnSp_Ufq@}0xwybFAi#%#fq@|}KQEO51AM#2z{tSB zz;IdD(Z$J?fi%FHTu@ZPz`$^Tfq}s&CAB!2fq~%*0|P^Pc}YPD0|R3W0|SFdQg%TJ z0|R3L0|SFdc1Vyj0|R3V0|OIJNoqw20|NttbACZ(QD%BZiGrb}rKN&nN`6wRLU3hq zNosDff@fZGeo;YwQDRAI3IhWJ)D8v)1_oZ2{1OHC#LPSeLsL}-Dual~C?)grM$+xhxmf|p7B=;2nnnfbQ63e)F`Yd zd{`u1lvi}CSe!Vg_*RJ&Nny#OQWes=(obaO$cD-Z%AJ+(QSedZRlJ}yML9}EN#(Wb zR<%ZTKMh%px0?I3CTgeZSnCSuzS29QKi{CnFv`f%Skm~n$vxA{#r++CO)=?RdfInDbtjt*-0cR=O|sSme3TYk~JdpT)k*{8ss|57-*GH|SXK z`H)+o&%(Y$FhvSRDMcH{xWz`r<;Axo%ud{#bT;{UDpQ(Vx=lt@W>wa#>^(X6@|g0~ z3w#QTi)I%eE_qufQSMSvSUIoiZ1vw-y}J1NNe#yue>WSnq_@s%yWSz#>D|@deYlsQ z&%VEI!oG?BCp%7QoqA$A?~LG?vt~V-qcyi=-o6D~3&R#IUi@*X!?Fp>AFecB)w=rT zTHSR`>u+u}*wnH4!B(qnQ@4NE>AP#y9*(`~`;H$_KiGNb^%1|Ln~#g1s6F}QwD*}U z=VZ^fU-)z>?((Ut7T1>D5WU%Y>+7BLyEpIqJUH;k^zrJaiqB@g5PaG7n)yxL+n?`C zKYaRB@cG@>yl?M*RI+y?e7jKeZ#YO-C0eeYAK~#9!Ow3P+jBy;t@wZQpr>Aw1 zElhK;rg%o;+1(hMA}Os4PRdDZi=1qmQvMt!dtejV)3zr!PPnPbzZoeg!~U777Jf7O#wF*Op1Uj73b|**Md40W-ue@Szh^)F8}|2_mP&+{xxrTe2zQ+X=k*FPH=%{bb(hL-sp7|(&w z6Q<+kb3+53k}bRcnnKz)Aa+}4xa(ePVWxh0epby#GYP&0J1DzM?UA_+NRR_ zI_O1v=l-aMu!dhq6CJaOdw>v4pat;pc+(`>UEjI6B$1t-H6~I+8vy07*qoM6N<$g68sU&j0`b literal 0 HcmV?d00001 diff --git a/module/event_hospital/assets.py b/module/event_hospital/assets.py index 4dc4ba643..0ea4c9341 100644 --- a/module/event_hospital/assets.py +++ b/module/event_hospital/assets.py @@ -21,5 +21,6 @@ INVEST_SEARCH = Button(area={'cn': (261, 135, 1187, 636), 'en': (261, 135, 1187, TAB_CHARACTER = Button(area={'cn': (263, 93, 409, 122), 'en': (263, 93, 409, 122), 'jp': (263, 93, 409, 122), 'tw': (263, 93, 409, 122)}, color={'cn': (185, 180, 191), 'en': (185, 180, 191), 'jp': (185, 180, 191), 'tw': (185, 180, 191)}, button={'cn': (263, 93, 409, 122), 'en': (263, 93, 409, 122), 'jp': (263, 93, 409, 122), 'tw': (263, 93, 409, 122)}, file={'cn': './assets/cn/event_hospital/TAB_CHARACTER.png', 'en': './assets/cn/event_hospital/TAB_CHARACTER.png', 'jp': './assets/cn/event_hospital/TAB_CHARACTER.png', 'tw': './assets/cn/event_hospital/TAB_CHARACTER.png'}) TAB_LOCATION = Button(area={'cn': (89, 93, 240, 121), 'en': (89, 93, 240, 121), 'jp': (89, 93, 240, 121), 'tw': (89, 93, 240, 121)}, color={'cn': (73, 108, 198), 'en': (73, 108, 198), 'jp': (73, 108, 198), 'tw': (73, 108, 198)}, button={'cn': (89, 93, 240, 121), 'en': (89, 93, 240, 121), 'jp': (89, 93, 240, 121), 'tw': (89, 93, 240, 121)}, file={'cn': './assets/cn/event_hospital/TAB_LOCATION.png', 'en': './assets/cn/event_hospital/TAB_LOCATION.png', 'jp': './assets/cn/event_hospital/TAB_LOCATION.png', 'tw': './assets/cn/event_hospital/TAB_LOCATION.png'}) TEMPLATE_INVEST = Template(file={'cn': './assets/cn/event_hospital/TEMPLATE_INVEST.png', 'en': './assets/en/event_hospital/TEMPLATE_INVEST.png', 'jp': './assets/jp/event_hospital/TEMPLATE_INVEST.png', 'tw': './assets/cn/event_hospital/TEMPLATE_INVEST.png'}) +TEMPLATE_INVEST2 = Template(file={'cn': './assets/cn/event_hospital/TEMPLATE_INVEST2.png', 'en': './assets/cn/event_hospital/TEMPLATE_INVEST2.png', 'jp': './assets/cn/event_hospital/TEMPLATE_INVEST2.png', 'tw': './assets/cn/event_hospital/TEMPLATE_INVEST2.png'}) TEMPLATE_REMAIN_CURRENT = Template(file={'cn': './assets/cn/event_hospital/TEMPLATE_REMAIN_CURRENT.png', 'en': './assets/en/event_hospital/TEMPLATE_REMAIN_CURRENT.png', 'jp': './assets/jp/event_hospital/TEMPLATE_REMAIN_CURRENT.png', 'tw': './assets/cn/event_hospital/TEMPLATE_REMAIN_CURRENT.png'}) TEMPLATE_REMAIN_TIMES = Template(file={'cn': './assets/cn/event_hospital/TEMPLATE_REMAIN_TIMES.png', 'en': './assets/en/event_hospital/TEMPLATE_REMAIN_TIMES.png', 'jp': './assets/jp/event_hospital/TEMPLATE_REMAIN_TIMES.png', 'tw': './assets/cn/event_hospital/TEMPLATE_REMAIN_TIMES.png'}) diff --git a/module/event_hospital/clue.py b/module/event_hospital/clue.py index dce244e6d..850ce4023 100644 --- a/module/event_hospital/clue.py +++ b/module/event_hospital/clue.py @@ -111,6 +111,8 @@ class HospitalClue(HospitalUI): # Search INVEST buttons = TEMPLATE_INVEST.match_multi(image) + buttons += TEMPLATE_INVEST2.match_multi(image) + buttons = sorted(buttons, key=lambda b: b.area[1]) count = len(buttons) if count == 0: return None From 3133eaa2f72177df19daf3b810dd29fb5d9128cc Mon Sep 17 00:00:00 2001 From: iceynano <34570144+iceynano@users.noreply.github.com> Date: Tue, 1 Apr 2025 00:56:05 +0800 Subject: [PATCH 09/12] Add: Event Reaction (#4706) * Add: Event Reaction * Update widgets.py * Update widgets.py * Update widgets.py * Update widgets.py * Update widgets.py --- assets/gui/css/alas.css | 10 +++++++ module/webui/app.py | 63 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/assets/gui/css/alas.css b/assets/gui/css/alas.css index 735dc2012..94e696480 100644 --- a/assets/gui/css/alas.css +++ b/assets/gui/css/alas.css @@ -314,6 +314,16 @@ pre.rich-traceback-code { margin: 0; } +.anim-rotate { + animation: rotate-keyframes 0.4s linear infinite +} + +@keyframes rotate-keyframes { + 100% { + transform: rotate(360deg) + } +} + #pywebio-scope-contents { margin-top: 0; overflow-y: auto; diff --git a/module/webui/app.py b/module/webui/app.py index 0d8d0f31b..56e1766be 100644 --- a/module/webui/app.py +++ b/module/webui/app.py @@ -113,6 +113,11 @@ class AlasGUI(Frame): self.alas_mod = "alas" self.alas_config = AzurLaneConfig("template") self.initial() + # rendered state cache + self.rendered_cache = [] + self.inst_cache = [] + self.load_home = False + self.af_flag = False @use_scope("aside", clear=True) def set_aside(self) -> None: @@ -122,12 +127,11 @@ class AlasGUI(Frame): buttons=[{"label": t("Gui.Aside.Home"), "value": "Home", "color": "aside"}], onclick=[self.ui_develop], ) - for name in alas_instance(): - put_icon_buttons( - Icon.RUN, - buttons=[{"label": name, "value": name, "color": "aside"}], - onclick=self.ui_alas, - ) + put_scope("aside_instance",[ + put_scope(f"alas-instance-{i}",[]) + for i, _ in enumerate(alas_instance()) + ]) + self.set_aside_status() put_icon_buttons( Icon.SETTING, buttons=[ @@ -140,6 +144,51 @@ class AlasGUI(Frame): onclick=[lambda: go_app("manage", new_window=False)], ) + current_date = datetime.now().date() + if current_date.month == 4 and current_date.day == 1: + self.af_flag = True + + @use_scope("aside_instance") + def set_aside_status(self) -> None: + flag = True + def update(name, seq): + with use_scope(f"alas-instance-{seq}", clear=True): + icon_html = Icon.RUN + rendered_state = ProcessManager.get_manager(inst).state + if rendered_state == 1 and self.af_flag: + icon_html = icon_html[:31] + ' anim-rotate' + icon_html[31:] + put_icon_buttons( + icon_html, + buttons=[{"label": name, "value": name, "color": "aside"}], + onclick=self.ui_alas, + ) + return rendered_state + + if not len(self.rendered_cache) or self.load_home: + # Reload when add/delete new instance | first start app.py | go to HomePage (HomePage load call force reload) + flag = False + self.inst_cache.clear() + self.inst_cache = alas_instance() + if flag: + for index, inst in enumerate(self.inst_cache): + # Check for state change + state = ProcessManager.get_manager(inst).state + if state != self.rendered_cache[index]: + self.rendered_cache[index] = update(inst, index) + flag = False + else: + self.rendered_cache.clear() + clear("aside_instance") + for index, inst in enumerate(self.inst_cache): + self.rendered_cache.append(update(inst, index)) + self.load_home = False + if not flag: + # Redraw lost focus, now focus on aside button + aside_name = get_localstorage("aside") + self.active_button("aside", aside_name) + + return + @use_scope("header_status") def set_status(self, state: int) -> None: """ @@ -1049,6 +1098,7 @@ class AlasGUI(Frame): def show(self) -> None: self._show() + self.load_home = True self.set_aside() self.init_aside(name="Home") self.dev_set_menu() @@ -1196,6 +1246,7 @@ class AlasGUI(Frame): ) self.task_handler.add(self.state_switch.g(), 2) + self.task_handler.add(self.set_aside_status, 2) self.task_handler.add(visibility_state_switch.g(), 15) self.task_handler.add(update_switch.g(), 1) self.task_handler.start() From e917cbd47ff9543ee36573c41abc331626867e4f Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Thu, 27 Mar 2025 02:41:27 +0800 Subject: [PATCH 10/12] Fix: Handle AdbError(rest) after granted root access --- module/device/method/utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/module/device/method/utils.py b/module/device/method/utils.py index 6f12a26c1..1256c8471 100644 --- a/module/device/method/utils.py +++ b/module/device/method/utils.py @@ -238,6 +238,11 @@ def handle_adb_error(e): # Raised by uiautomator2 when current adb service is killed by another version of adb service. logger.error(e) return True + elif text == 'rest': + # AdbError(rest) + # Response telling adbd service has reset, client should reconnect + logger.error(e) + return True else: # AdbError() logger.exception(e) From 19db66157e8f068a0266f1bd39b1105baadbe62a Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Thu, 27 Mar 2025 02:58:23 +0800 Subject: [PATCH 11/12] Fix: Wait emulator-* devices appear after reconnect --- module/device/connection.py | 57 +++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/module/device/connection.py b/module/device/connection.py index b7e5f92bb..889c528d4 100644 --- a/module/device/connection.py +++ b/module/device/connection.py @@ -11,6 +11,7 @@ from adbutils import AdbClient, AdbDevice, AdbTimeout, ForwardItem, ReverseItem from adbutils.errors import AdbError from module.base.decorator import Config, cached_property, del_cached_property, run_once +from module.base.timer import Timer from module.base.utils import ensure_time from module.config.server import VALID_CHANNEL_PACKAGE, VALID_PACKAGE, set_server from module.device.connection_attr import ConnectionAttr @@ -113,7 +114,7 @@ class Connection(ConnectionAttr): self.detect_device() # Connect - self.adb_connect() + self.adb_connect(wait_device=False) logger.attr('AdbDevice', self.adb) # Package @@ -673,8 +674,40 @@ class Connection(ConnectionAttr): cmd = ['push', local, remote] return self.adb_command(cmd) + def _wait_device_appear(self, serial, first_devices=None): + """ + Args: + serial: + first_devices (list[AdbDeviceWithStatus]): + + Returns: + bool: If appear + """ + # Wait a little longer than 5s + timeout = Timer(5.2).start() + first_log = True + while 1: + if first_devices is not None: + devices = first_devices + first_devices = None + else: + devices = self.list_device() + # Check if device appear + for device in devices: + if device.serial == serial and device.status == 'device': + return True + # Delay and check later + if timeout.reached(): + break + if first_log: + logger.info(f'Waiting device appear: {serial}') + first_log = False + time.sleep(0.05) + + return False + @Config.when(DEVICE_OVER_HTTP=False) - def adb_connect(self): + def adb_connect(self, wait_device=True): """ Connect to a serial, try 3 times at max. If there's an old ADB server running while Alas is using a newer one, which happens on Chinese emulators, @@ -682,12 +715,14 @@ class Connection(ConnectionAttr): Args: serial (str): + wait_device: True to wait emulator-* and android devices appear Returns: bool: If success """ # Disconnect offline device before connecting - for device in self.list_device(): + devices = self.list_device() + for device in devices: if device.status == 'offline': logger.warning(f'Device {device.serial} is offline, disconnect it before connecting') msg = self.adb_client.disconnect(device.serial) @@ -700,11 +735,23 @@ class Connection(ConnectionAttr): else: logger.warning(f'Device {device.serial} is is having a unknown status: {device.status}') - # Skip for emulator-5554 + # Skip connecting emulator-5554 and android phones, as they should be auto connected once plugged in if 'emulator-' in self.serial: + if wait_device: + if self._wait_device_appear(self.serial, first_devices=devices): + logger.info(f'Serial {self.serial} connected') + return True + else: + logger.info(f'Serial {self.serial} is not connected') logger.info(f'"{self.serial}" is a `emulator-*` serial, skip adb connect') return True if re.match(r'^[a-zA-Z0-9]+$', self.serial): + if wait_device: + if self._wait_device_appear(self.serial, first_devices=devices): + logger.info(f'Serial {self.serial} connected') + return True + else: + logger.info(f'Serial {self.serial} is not connected') logger.info(f'"{self.serial}" seems to be a Android serial, skip adb connect') return True @@ -769,7 +816,7 @@ class Connection(ConnectionAttr): ev.close() @Config.when(DEVICE_OVER_HTTP=True) - def adb_connect(self): + def adb_connect(self, wait_device=True): # No adb connect if over http return True From 7a782138b68ef766e0a37a7bc5556f031f085fc1 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 31 Mar 2025 11:49:01 +0800 Subject: [PATCH 12/12] Upd: [ALAS] atomic_failure_cleanup --- deploy/atomic.py | 259 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 213 insertions(+), 46 deletions(-) diff --git a/deploy/atomic.py b/deploy/atomic.py index 41b6ca76b..b64ce8a53 100644 --- a/deploy/atomic.py +++ b/deploy/atomic.py @@ -5,8 +5,13 @@ import string import time from typing import Union +# Max attempt if another process is reading/writing, effective only on Windows +WINDOWS_MAX_ATTEMPT = 5 +# Base time to wait between retries (seconds) +WINDOWS_RETRY_DELAY = 0.05 -def random_id(length=6): + +def random_id(length: int = 6) -> str: """ Args: length (int): 6 random letter (62^6 combinations) would be enough @@ -17,11 +22,44 @@ def random_id(length=6): return ''.join(random.sample(string.ascii_letters + string.digits, length)) +def is_tmp_file(filename: str) -> bool: + """ + Check if a filename is tmp file + """ + # Check suffix first to reduce regex calls + if not filename.endswith('.tmp'): + return False + # Check temp file format + res = re.match(r'.*\.[a-zA-Z0-9]{6,}\.tmp$', filename) + if not res: + return False + return True + + +def to_tmp_file(filename: str) -> str: + """ + Convert a filename or directory name to tmp + """ + suffix = random_id(6) + return f'{filename}.{suffix}.tmp' + + +def windows_attempt_delay(attempt: int) -> float: + """ + Exponential Backoff if file is in use on Windows + + Args: + attempt: Current attempt, starting from 0 + + Returns: + float: Seconds to wait + """ + return 2 ** attempt * WINDOWS_RETRY_DELAY + + def atomic_write( file: str, data: Union[str, bytes], - max_attempt=5, - retry_delay=0.05, ): """ Atomic file write with minimal IO operation @@ -33,12 +71,8 @@ def atomic_write( Args: file: data: - max_attempt: Max attempt if another process is reading, - effective only on Windows - retry_delay: Base time to wait between retries (seconds) """ - suffix = random_id(6) - temp = f'{file}.{suffix}.tmp' + temp = to_tmp_file(file) if isinstance(data, str): mode = 'w' encoding = 'utf-8' @@ -74,9 +108,7 @@ def atomic_write( if os.name == 'nt': # PermissionError on Windows if another process is reading last_error = None - if max_attempt < 1: - max_attempt = 1 - for trial in range(max_attempt): + for attempt in range(WINDOWS_MAX_ATTEMPT): try: # Atomic operation os.replace(temp, file) @@ -84,7 +116,111 @@ def atomic_write( return except PermissionError as e: last_error = e - delay = 2 ** trial * retry_delay + delay = windows_attempt_delay(attempt) + time.sleep(delay) + continue + except Exception as e: + last_error = e + break + else: + # Linux and Mac allow existing reading + try: + # Atomic operation + os.replace(temp, file) + # success + return + except Exception as e: + last_error = e + + # Clean up temp file on failure + try: + os.unlink(temp) + except: + pass + if last_error is not None: + raise last_error from None + + +def atomic_stream_write( + file: str, + data_generator, +): + """ + Atomic file write with streaming data support. + Handles cases where file might be read by another process. + os.replace() is an atomic operation among all OS, + we write to temp file then do os.replace() + + Only creates a file if the generator yields at least one data chunk. + Automatically determines write mode based on the type of first chunk. + + Args: + file: Target file path + data_generator: An iterable that yields data chunks (str or bytes) + """ + # Convert generator to iterator to ensure we can peek at first chunk + data_iter = iter(data_generator) + + # Try to get the first chunk + try: + first_chunk = next(data_iter) + except StopIteration: + # Generator is empty, no file will be created + return + + # Create temp file path + temp = to_tmp_file(file) + + # Determine mode, encoding and newline from first chunk + if isinstance(first_chunk, str): + mode = 'w' + encoding = 'utf-8' + newline = '' + elif isinstance(first_chunk, bytes): + mode = 'wb' + encoding = None + newline = None + else: + # Default to text mode for other types + mode = 'w' + encoding = 'utf-8' + newline = '' + + try: + # Write temp file + with open(temp, mode=mode, encoding=encoding, newline=newline) as f: + f.write(first_chunk) + for chunk in data_iter: + f.write(chunk) + # Ensure data flush to disk + f.flush() + os.fsync(f.fileno()) + except FileNotFoundError: + # Create parent directory + directory = os.path.dirname(file) + if directory: + os.makedirs(directory, exist_ok=True) + # Write again + with open(temp, mode=mode, encoding=encoding, newline=newline) as f: + f.write(first_chunk) + for chunk in data_iter: + f.write(chunk) + # Ensure data flush to disk + f.flush() + os.fsync(f.fileno()) + + last_error = None + if os.name == 'nt': + # PermissionError on Windows if another process is reading + for attempt in range(WINDOWS_MAX_ATTEMPT): + try: + # Atomic operation + os.replace(temp, file) + # success + return + except PermissionError as e: + last_error = e + delay = windows_attempt_delay(attempt) time.sleep(delay) continue except Exception as e: @@ -113,8 +249,6 @@ def atomic_read( file: str, mode: str = 'r', errors: str = 'strict', - max_attempt=5, - retry_delay=0.05, ): """ Atomic file read with minimal IO operation @@ -124,9 +258,6 @@ def atomic_read( file: mode: 'r' or 'rb' errors: 'strict', 'ignore', 'replace' and any other errors mode in open() - max_attempt: Max attempt if another process is reading, - effective only on Windows - retry_delay: Base time to wait between retries (seconds) Returns: str if mode is 'r' @@ -141,9 +272,7 @@ def atomic_read( if os.name == 'nt': # PermissionError on Windows if another process is replacing last_error = None - if max_attempt < 1: - max_attempt = 1 - for trial in range(max_attempt): + for attempt in range(WINDOWS_MAX_ATTEMPT): try: with open(file, mode=mode, encoding=encoding, errors=errors) as f: # success @@ -152,12 +281,9 @@ def atomic_read( return '' except PermissionError as e: last_error = e - delay = 2 ** trial * retry_delay + delay = windows_attempt_delay(attempt) time.sleep(delay) continue - except Exception as e: - last_error = e - break if last_error is not None: raise last_error from None else: @@ -170,7 +296,39 @@ def atomic_read( return '' -def atomic_failure_cleanup(path: str): +def atomic_remove(file: str): + """ + Atomic file remove + + Args: + file: + """ + if os.name == 'nt': + # PermissionError on Windows if another process is replacing + last_error = None + for attempt in range(WINDOWS_MAX_ATTEMPT): + try: + os.unlink(file) + except FileNotFoundError: + return + except PermissionError as e: + last_error = e + delay = windows_attempt_delay(attempt) + time.sleep(delay) + continue + if last_error is not None: + raise last_error from None + else: + # Linux and Mac allow deleting while another process is reading + # The directory entry is removed but the storage allocated to the file is not made available + # until the original file is no longer in use. + try: + os.unlink(file) + except FileNotFoundError: + return + + +def atomic_failure_cleanup(directory: str, recursive: bool = False): """ Cleanup remaining temp file under given path. In most cases there should be no remaining temp files unless write process get interrupted. @@ -178,24 +336,33 @@ def atomic_failure_cleanup(path: str): This method should only be called at startup to avoid deleting temp files that another process is writing. """ - with os.scandir(path) as entries: - for entry in entries: - if not entry.is_file(): - continue - # Check suffix first to reduce regex calls - name = entry.name - if not name.endswith('.tmp'): - continue - # Check temp file format - res = re.match(r'.*\.[a-zA-Z0-9]{6,}\.tmp$', name) - if not res: - continue - # Delete temp file - file = f'{path}{os.sep}{name}' - try: - os.unlink(file) - except PermissionError: - # Another process is reading/writing - pass - except: - pass + try: + with os.scandir(directory) as entries: + for entry in entries: + if is_tmp_file(entry.name): + # Delete temp file or directory + if entry.is_dir(follow_symlinks=False): + import shutil + shutil.rmtree(entry.path, ignore_errors=True) + else: + try: + os.unlink(entry.path) + except PermissionError: + # Another process is reading/writing + pass + except FileNotFoundError: + # Another process removed current file while iterating + pass + except: + pass + else: + if entry.is_dir(follow_symlinks=False): + # Normal directory + if recursive: + atomic_failure_cleanup(entry.path, recursive=True) + # Normal file + # else: + # pass + except FileNotFoundError: + # directory to clean up does not exist, no need to clean up + pass