Skip to content

Commit 2f557d0

Browse files
Add support for cloud gaming
1. Add support custom window name 2. Scroll latency will depend on measured game response time 3. Show game response time while scanning
1 parent 8da1a55 commit 2f557d0

File tree

3 files changed

+67
-18
lines changed

3 files changed

+67
-18
lines changed

ArtScanner/art_scanner_logic.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import win32gui
44
import mouse, math
55
from PIL import ImageChops
6+
import numpy as np
67
from utils import captureWindow
78

89
class GameInfo:
@@ -51,20 +52,25 @@ class ArtScannerLogic:
5152
def __init__(self, game_info):
5253
self.game_info = game_info
5354
self.stopped = False
55+
self.avg_response_time = 1/60
5456

5557
def interrupt(self):
5658
self.stopped = True
5759

5860

5961
def waitSwitched(self, art_center_x, art_center_y, min_wait=0.1, max_wait=3, condition=lambda pix:sum(pix)/3>200):
62+
start = time.time()
6063
total_wait = 0
6164
while True:
65+
mouse.move(self.game_info.left+art_center_x, self.game_info.top+art_center_y)
66+
mouse.click()
6267
pix = captureWindow(self.game_info.hwnd, (
6368
art_center_x-self.game_info.art_width/2-self.game_info.art_expand,
6469
art_center_y,
6570
art_center_x-self.game_info.art_width/2-self.game_info.art_expand+1.5,
6671
art_center_y+1.5))
6772
if condition(pix.getpixel((0,0))):
73+
self.avg_response_time = 0.5*self.avg_response_time + 0.5*(time.time()-start)
6874
return True
6975
else:
7076
time.sleep(min_wait)
@@ -113,12 +119,13 @@ def scanRows(self, rows, callback):
113119

114120
def alignFirstRow(self):
115121
mouse.move(self.game_info.left+self.game_info.first_art_x, self.game_info.top+self.game_info.first_art_y)
122+
mouse.click()
116123
pix = captureWindow(self.game_info.hwnd, (
117124
self.game_info.scroll_fin_keypt_x,
118125
self.game_info.scroll_fin_keypt_y,
119126
self.game_info.scroll_fin_keypt_x+1.5,
120127
self.game_info.scroll_fin_keypt_y+1.5))
121-
if pix.getpixel((0,0))[0]!=233 or pix.getpixel((0,0))[1]!=229 or pix.getpixel((0,0))[2]!=220:
128+
if abs(pix.getpixel((0,0))[0]-233)>5 or abs(pix.getpixel((0,0))[1]-229)>5 or abs(pix.getpixel((0,0))[2]-220)>5:
122129
for _ in range(3):
123130
mouse.wheel(1)
124131
time.sleep(0.1)
@@ -135,7 +142,7 @@ def scrollToRow(self, target_row, max_scrolls=20, extra_scroll=0, interval=0.05)
135142
self.game_info.scroll_fin_keypt_y,
136143
self.game_info.scroll_fin_keypt_x+1.5,
137144
self.game_info.scroll_fin_keypt_y+1.5))
138-
if pix.getpixel((0,0))[0]!=233 or pix.getpixel((0,0))[1]!=229 or pix.getpixel((0,0))[2]!=220:
145+
if abs(pix.getpixel((0,0))[0]-233)>5 or abs(pix.getpixel((0,0))[1]-229)>5 or abs(pix.getpixel((0,0))[2]-220)>5:
139146
# if in_between_row==False:
140147
# print('到行之间了')
141148
in_between_row = True
@@ -150,21 +157,22 @@ def scrollToRow(self, target_row, max_scrolls=20, extra_scroll=0, interval=0.05)
150157
return rows_scrolled
151158
if lines_scrolled > max_scrolls:
152159
return rows_scrolled
153-
get_first_art = lambda : captureWindow(self.game_info.hwnd, (
160+
get_first_art = lambda : np.array(captureWindow(self.game_info.hwnd, (
154161
self.game_info.first_art_x+self.game_info.art_width/2-1,
155162
self.game_info.first_art_y+self.game_info.art_height/2,
156163
self.game_info.first_art_x+self.game_info.art_width/2+1,
157-
self.game_info.first_art_y+self.game_info.art_height))
164+
self.game_info.first_art_y+self.game_info.art_height)))
158165
first_art = get_first_art()
159166
for _ in range(7 if lines_scrolled==0 and target_row>0 else 1):
160167
mouse.wheel(-1)
161168
lines_scrolled += 1
162169
# print('翻一下')
170+
time.sleep(self.avg_response_time)
163171
total_waited = 0
164172
while True:
165-
time.sleep(interval)
166-
total_waited += interval
167173
if total_waited>5:
168174
break
169-
if ImageChops.difference(get_first_art(), first_art).getbbox():
175+
if np.max(np.abs(get_first_art()-first_art))>5:
170176
break
177+
time.sleep(interval)
178+
total_waited += interval

ArtScanner/main.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,34 @@ def is_admin():
3131
import ocr
3232
from art_saver import ArtDatabase
3333
from art_scanner_logic import ArtScannerLogic, GameInfo
34-
from utils import decodeValue
34+
from utils import decodeValue, findWindowsByName, calcFormatWidth, setWindowToForeground
3535

3636
if len(sys.argv)>1:
3737
bundle_dir = sys.argv[1]
3838
else:
3939
bundle_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__)))
4040

41+
import win32gui
4142

4243
window_name_cn = '原神'
43-
hwnd = win32gui.FindWindow(None, window_name_cn)
44-
45-
if hwnd is None or hwnd==0:
46-
input('未找到在运行的原神')
47-
sys.exit(0)
44+
while True:
45+
windows = findWindowsByName(window_name_cn)
46+
if len(windows) == 0:
47+
window_name_cn = input('未找到在运行的原神,请在这里输入窗口标题(留空则列出所有尺寸非0窗口):').strip().lower()
48+
elif len(windows) == 1:
49+
hwnd = windows[0][0]
50+
break
51+
else:
52+
print('-----------------------------')
53+
print(f'发现多个窗口名为{window_name_cn},请从以下列表中选择:')
54+
print('{0:^{4}} {1:^{5}} {2:^{6}} {3:^{7}}'.format('编号','窗口标题', '进程名', '窗口内部分辨率',
55+
calcFormatWidth('编号', 5), calcFormatWidth('窗口标题', 25), calcFormatWidth('进程名', 25), calcFormatWidth('窗口内部分辨率', 15)))
56+
for i, window in enumerate(windows):
57+
print('{0:^5} {1:<{4}.{4}} {2:<{5}.{5}} {3:<15}'.format(i+1, window[1], os.path.basename(window[2]), str(window[3]),
58+
calcFormatWidth(window[1], 25), calcFormatWidth(window[2], 25)))
59+
hwnd = windows[int(input('请输入要选择的编号:'))-1][0]
60+
print('-----------------------------')
61+
break
4862

4963
game_info = GameInfo(hwnd)
5064

@@ -117,10 +131,7 @@ def is_admin():
117131
except:
118132
scroll_interval = 0.05
119133

120-
keyboard.press('alt')
121-
win32gui.ShowWindow(hwnd,5)
122-
win32gui.SetForegroundWindow(hwnd)
123-
keyboard.release('alt')
134+
setWindowToForeground(hwnd)
124135

125136
time.sleep(5)
126137

@@ -154,7 +165,7 @@ def artscannerCallback(art_img):
154165
f.write(s)
155166
failed += 1
156167
art_id += 1
157-
print(f"\r已扫描{art_id}个圣遗物,已保存{saved}个,已跳过{skipped}个", end='')
168+
print(f"\r已扫描{art_id}个圣遗物,已保存{saved}个,已跳过{skipped},游戏平均响应时间{art_scanner.avg_response_time*1000:3.0f}毫秒", end='')
158169

159170
try:
160171
while True:

ArtScanner/utils.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,36 @@
11
from mss import mss
22
from PIL import Image
33
import win32gui
4+
import win32process
5+
import win32api
6+
import win32con
7+
import unicodedata
8+
import keyboard
9+
10+
def calcFormatWidth(text, target):
11+
return target - sum(unicodedata.east_asian_width(c) in 'WF' for c in text)
12+
13+
def setWindowToForeground(hwnd):
14+
keyboard.press('alt')
15+
win32gui.ShowWindow(hwnd,5)
16+
win32gui.SetForegroundWindow(hwnd)
17+
keyboard.release('alt')
18+
19+
def getProcessName(hwnd):
20+
pid = win32process.GetWindowThreadProcessId(hwnd)
21+
handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, pid[1])
22+
return win32process.GetModuleFileNameEx(handle, 0)
23+
24+
def findWindowsByName(name):
25+
windows = []
26+
def winEnumHandler( hwnd, ctx ):
27+
win_name = win32gui.GetWindowText( hwnd )
28+
win_size = win32gui.GetClientRect(hwnd)[2:]
29+
if win32gui.IsWindowVisible( hwnd ) and name in win_name and ((win_size[0]*win_size[1])!=0 or len(name)>0):
30+
windows.append((hwnd, win_name, getProcessName(hwnd), win_size))
31+
win32gui.EnumWindows( winEnumHandler, None )
32+
return windows
33+
434

535
def captureRect(rect):
636
with mss() as sct:

0 commit comments

Comments
 (0)