project-yuki / YUKI

YUKI Galgame Translator
GNU General Public License v3.0
1.51k stars 145 forks source link

[question]关于游戏多线程的问题 #8

Closed luojunyuan closed 5 years ago

luojunyuan commented 5 years ago

我在玩消えた世界と月と少女时游戏会启动四个进程

PS C:\Users\ljy77> tasklist /nh /fo csv /fi "imagename eq KIETASEKAI.EXE"
"KIETASEKAI.EXE","12228","Console","9","121,140 K"
"KIETASEKAI.EXE","11624","Console","9","49,628 K"
"KIETASEKAI.EXE","13936","Console","9","102,744 K"
"KIETASEKAI.EXE","2900","Console","9","32,120 K"

然后发现每次都是第三个才是应该hook的那个,在parsePidFrom函数下临时补救了一下

if (value.replace(/"/g, '').split(',')[0] === 'KIETASEKAI.EXE') {
  return parseInt(value.replace(/"/g, '').split(',')[11], 10)
}

有啥更好的通用的处理方法嘛

tinyAdapter commented 5 years ago

确实没考虑过这个问题……

我想了一下,本身Textractor是支持hook多个进程的,所以其实把所有pid全部记录下来到this.pids然后把injectProcessByPid改成

  private injectProcessByPid () {
    this.pids.map((pid) => Hooker.getInstance().injectProcess(pid))
  }

就可以都hook到啦。

然后注册退出回调这块,registerProcessExitCallback改成

  private registerProcessExitCallback () {
    let exitedProcessCount = 0
    this.pids.map((pid) => {
      registerProcessExitCallback(pid, () => {
        exitedProcessCount++
        if (exitedProcessCount >= this.pids.length) this.emit('exited', this)
      })
    })
  }

应该就OK。

然后主要就是怎么获取到全部pid的问题,我去看看这个正则表达式怎么写……

还有就是,我这边没有这种一次开好几个进程的游戏……所以要不请你写好测试好后提交个PR?🙏

tinyAdapter commented 5 years ago

/"[^"]*"/g这个正则表达式应该就行,然后取所有 i / 5 %= 1 的匹配项,去掉引号,再parseInt就成啦。

luojunyuan commented 5 years ago

按照你的指示Textractor hook成功了!

  yuki:hooker injecting process 704... +23s
  yuki:hooker process 704 injected +1ms
  yuki:hooker injecting process 13296... +1ms
  yuki:hooker process 13296 injected +1ms
  yuki:hooker injecting process 13352... +1ms
  yuki:hooker process 13352 injected +1ms
  yuki:hooker injecting process 3172... +4ms
  yuki:hooker process 3172 injected

但是特殊码依旧只是注入了第一个进程,看代码牵扯到ipc层我不知道该如何修改妥当(:з)∠)

  yuki:hooker inserting hook /HWN-10@2ABE69:kietasekai.exe to process 704... +5s
  yuki:hooker hook /HWN-10@2ABE69:kietasekai.exe inserted into process 704

另外偶尔可能会遇上第一个进程启动了,剩余进程还没启动,导致没能读取完整。。

  yuki:game finding pid of KIETASEKAI.EXE... +34ms
  yuki:game pid 1 14052 +2s
  yuki:game pid 2 undefined +5ms
  yuki:game pid 3 undefined +2ms
  yuki:game pid 4 undefined
tinyAdapter commented 5 years ago

哦这里,去setup\ipc.ts下,找到

ipcMain.on(
    IpcTypes.REQUEST_INSERT_HOOK,
    (event: Electron.Event, code: string) => {
      if (code !== '') {
        Hooker.getInstance().insertHook(runningGame.getPid(), code)
      }
    }
  )

这段,改成

ipcMain.on(
    IpcTypes.REQUEST_INSERT_HOOK,
    (event: Electron.Event, code: string) => {
      if (code !== '') {
        // 这里改下Game类的getPid()接口,换成getPids(),返回pids数组
        runningGame.getPids().map((pid) => {
          Hooker.getInstance().insertHook(pid, code)
        })
      }
    }
  )

应该就可以了。

整个启动游戏的流程是这样的:

              Main Process                                        TranslatorWindow
+---------------------------------------+             +---------------------------------------+
|                                       |             |                                       |
|  new Game().start()                   |             |                                       |
|          +                            |             |                                       |
|          |                            |             |                                       |
|          v                            |  Loading    |                                       |
|  new TranslatorWindow().setGame(Game) +------------->  App.vue::mounted()         +         |
|                                       |  Window...  |                             |         |
|         +-------------------------------------------+  requestConfig("game") <----+         |
|         v                             |             |                                       |
|  sendConfig("game")                   +------------------------------+                      |
|                                       |             |                |                      |
|                                       |             |                v                      |
|         +-------------------------------------------+  ipcRenderer request insert hook      |
|         |                             |             |                                       |
|         |                             |             |                                       |
|  ipcMain::on(REQUEST_INSERT_HOOK)     |             |                                       |
|                                       |             |                                       |
+---------------------------------------+             +---------------------------------------+

之后我大概会找个时间把整个项目的架构写成Wiki 💪

luojunyuan commented 5 years ago

怎么都解决不掉这bug,其他游戏一切正常,就是多线程的这个 image

translationManager.ts

  public translate (
    text: string,
    callback: (translation: yuki.Translations['translations']) => void
  ) {
    debug('arrive 1')
    let toTranslateCount = 0
    for (const key in this.apis) {
      if (this.apis[key].isEnable()) {
        toTranslateCount++
        this.apis[key].translate(text, (translation) => {
          //this not work, till game close
          debug('[%s] -> %s', this.apis[key].getName(), translation)
          callback({
            [this.apis[key].getName()]: translation
          })
        })
      }
    }
    if (toTranslateCount === 0) {
      callback({})
    }
  }

原始语句mecab(日语出得来) 翻译却不显示 image

关闭游戏后,堆积的翻译一下子涌了出来、并伴有翻译窗口已关闭的error image

其他游戏一切正常。。

tinyAdapter commented 5 years ago

……看来是个大工程了啊 🥀

我这几天比较忙,6月份之后再着手解决一下好了。

消えた世界と月と少女 这个游戏是吧,我先去搞一个,然后看看能不能把VSCode调试Electron给整起来,这样打断点的话显然能看得更清楚点。

要是你能搞的话,也可以看看怎么把调试模式搭起来,然后在那部分打些断点瞧一瞧(

tinyAdapter commented 5 years ago

OK,我搞定了。

具体的原因是发生在Win32.ts中,使用Win32 API注册进程退出的callback时发生的问题。

还是我这部分代码没写好,只考虑到了单进程的情况,一旦一次性注册两个及以上PID就会把整个主进程I/O循环堵死,后面再怎么发IPC请求都收不到了。

解决方案是用类似中间件模式的方式一个一个注册callback,这样就不会阻塞I/O了。

我这边测试通过↓

image

贴一下特殊码在这边,以防有人搜不到

/HWN-10@2ABE69:kietasekai.exe

另外我也把调试搞起来了,不过有个比较麻烦的问题……因为之前想把所有文件名都改成大写的,结果git搞崩了,现在有可能导致大小写混乱对VSCode的断点识别出现问题,注意一下吧 (¬‿¬)

PonyCheng commented 2 years ago

同样的问题在YUKI-minori-v0.14.3 版本中仍旧出现了。读取了多个task list