施工中,不定期更新
- 关于指令1:随着游戏版本更新,指令可能会失效。如果失效了,多数情况下重装IPA就可以还原修改。
- 关于指令2:提供的指令主要以国服为准,其他服需要自行替换Bundle ID。右键游戏图标,在访达中查看,其文件名即为Bundle ID。
- 本站的目的在于整合和分享最关键的部分,有些步骤不会写得太细。如有意愿制作保姆级教程,可随意使用本站的内容。
- 如有问题可通过B站评论或私信交流。
游戏列表
- 堡垒之夜
- 碧蓝航线
- 边狱巴士
- 蛋仔派对
- 第五人格
- 巅峰极速
- 二重螺旋
- 幻塔
- 火环
- 火炬之光:无限
- 金铲铲之战
- 开放空间
- 恋与深空
- 空灵诗篇
- 锚点降临
- 鸣潮
- 命运:群星
- 逆战:未来
- 女神异闻录:夜幕魅影
- 七大罪:光与暗之交战
- 七骑士 Re:BIRTH
- 三国志·战略版
- 少女前线2:追放
- 失落之剑
- 实况足球(eFootball)
- 天涯明月刀手游
- 无限暖暖
- 星痕共鸣
- 星塔旅人
- 学园偶像大师
- 燕云十六声
- 永恒之塔2
- 永劫无间手游
- 棕色尘埃2
- 最终幻想14:水晶世界
- NIKKE
- VRChat
堡垒之夜
PlayCover
1. 启动闪退 ISSUE-009
打开第一个目录:右键游戏图标,选择“在访达中查看”,返回上一级目录,进入Entitlements。
打开第二个目录:右键游戏图标,选择“在访达中查看”,右键选择“显示包内容”。
将第一个目录中的com.epicgames.FortniteGame.plist拷贝至第二个目录,重命名为embedded.mobileprovision。
2. 启动闪退2 ISSUE-011
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.epicgames.FortniteGame.app/FortniteClient-IOS-Shipping
FUNC_ADDR=$(otool -Iv "$EXECUTABLE" | grep _os_proc_available_memory | head -n1 | awk '{print $1}')
printf '\x20\x00\xC0\xD2\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR-0x100000000)) conv=notrunc
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
碧蓝航线
PlayCover
1. 按键映射失灵 ISSUE-014
WASD和鼠标同时按下后,按键映射失灵,重启游戏后恢复正常。
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.bilibili.azurlane.app/Frameworks/UnityFramework.framework/UnityFramework
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/SessionProvider/{found=1} found && /init/{f=1} f && /imp/{print $2; exit}')
printf '\x1F\x20\x03\xD5' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR+0x1C4)) conv=notrunc
printf '\x1F\x20\x03\xD5' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR+0x374)) conv=notrunc
codesign -fs- $EXECUTABLE
边狱巴士
PlayCover
1. 启动闪退 ISSUE-010
右键游戏图标 - 设置 - 绕过设置,启用“绕过越狱检测”选项。
2. 屏幕方向错误 ISSUE-013
按command+R可以旋转屏幕方向,或通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.ProjectMoon.LimbusCompany.app/Frameworks/UnityFramework.framework/UnityFramework
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/UnityAppController/{found=1} found && /createRootViewController$/{f=1} f && /imp/{print $2; exit}')
printf '\x1F\x20\x03\xD5' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR+0x28)) conv=notrunc
printf '\x82\x00\x80\xD2' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR+0x34)) conv=notrunc
codesign -fs- $EXECUTABLE
3. 自动登录闪退 ISSUE-005
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.ProjectMoon.LimbusCompany.app/Frameworks/UnityFramework.framework/UnityFramework
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/FIRAuthKeychainServices/{found=1} found && /itemWithQuery:error:/{f=1} f && /imp/{print $2; exit}')
printf '\x44\x00\x00\x14' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR+0x110)) conv=notrunc
printf '\x1F\x20\x03\xD5' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR+0x2A4)) conv=notrunc
codesign -fs- $EXECUTABLE
4. CPU占用异常偏高 ISSUE-004
使用PlayCover Nightly版,右键游戏图标 - 设置 - 杂项设置,启用“limitMotionUpdateFrequency”选项。
蛋仔派对
PlayCover
1. 启动闪退 ISSUE-018
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.netease.party.app/client
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\x00\x2F\x70\x72\x69\x76\x61\x74\x65\x00"); exit 1 if $i < 0; seek F, $i, 0; print F "\x00\x2F\x00\x00\x00\x00\x00\x00\x00\x00"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
第五人格
PlayCover
1. 启动闪退 ISSUE-018
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.netease.id5.app/client
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\x00\x2F\x70\x72\x69\x76\x61\x74\x65\x00"); exit 1 if $i < 0; seek F, $i, 0; print F "\x00\x2F\x00\x00\x00\x00\x00\x00\x00\x00"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
2. 摇杆映射问题 ISSUE-007
使用PlayCover Nightly版,在编辑模式中选中WASD控件,更改为Floating Joystick模式。
巅峰极速
PlayCover
1. 启动阶段异常卡顿 ISSUE-001
下载完最开始的200MB资源后,通过终端指令修复:
BUNDLE_ID=com.netease.rc
rm -r /Users/$USER/Library/Containers/$BUNDLE_ID/Data/Library/Users/$USER/Documents/Containers/$BUNDLE_ID/Data && ln -sf /Users/$USER/Library/Containers/$BUNDLE_ID/Data /Users/$USER/Library/Containers/$BUNDLE_ID/Data/Library/Users/$USER/Documents/Containers/$BUNDLE_ID/Data
rm -r /Users/$USER/Library/Containers/$BUNDLE_ID/Data/Library/Users/$USER/Library/Containers/$BUNDLE_ID/Data && ln -sf /Users/$USER/Library/Containers/$BUNDLE_ID/Data /Users/$USER/Library/Containers/$BUNDLE_ID/Data/Library/Users/$USER/Library/Containers/$BUNDLE_ID/Data
2. 提示“设备不支持更高画质” ISSUE-011
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.netease.rc.app/g112
FUNC_ADDR=$(otool -Iv "$EXECUTABLE" | grep _os_proc_available_memory | head -n1 | awk '{print $1}')
printf '\x20\x00\xC0\xD2\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR-0x100000000)) conv=notrunc
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
3. 日服版本登录界面不显示 ISSUE-015
TODO:暂时下载不到 IPA
二重螺旋
PlayCover
1. 短暂运行后闪退 ISSUE-012
通过终端指令修复:
国服
TARGET=$(echo -n "YWNlcnQy" | base64 -d)
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.hero.dna.ios.app/Frameworks/$TARGET.framework/$TARGET
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\x77\x05\x00\xB4\xF4\x03\x02\xAA\xF5\x03\x01\xAA\xF3\x03\x00\xAA\x16\x00\x80\x52\x28\x00\x80\x52"); exit 1 if $i < 0; seek F, $i + 168, 0; print F "\x1F\x20\x03\xD5"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE >/dev/null 2>&1 && echo "\n\033[0;32mDone\033[0m\n"
国际服
TARGET=$(echo -n "YW5vcnQ=" | base64 -d)
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.panstudio.duetnightabyss.arpg.global.app/Frameworks/$TARGET.framework/$TARGET
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\x77\x05\x00\xB4\xF4\x03\x02\xAA\xF5\x03\x01\xAA\xF3\x03\x00\xAA\x16\x00\x80\x52\x28\x00\x80\x52"); exit 1 if $i < 0; seek F, $i + 168, 0; print F "\x1F\x20\x03\xD5"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE >/dev/null 2>&1 && echo "\n\033[0;32mDone\033[0m\n"
2. 存储空间不足报错 ISSUE-001
通过终端指令修复:
国服
rm -r /Users/$USER/Library/Containers/com.hero.dna.ios/Data/Library/Users/$USER/Library/Containers/com.hero.dna.ios/Data && ln -sf /Users/$USER/Library/Containers/com.hero.dna.ios/Data /Users/$USER/Library/Containers/com.hero.dna.ios/Data/Library/Users/$USER/Library/Containers/com.hero.dna.ios/Data
国际服
rm -r /Users/$USER/Library/Containers/com.panstudio.duetnightabyss.arpg.global/Data/Library/Users/$USER/Library/Containers/com.panstudio.duetnightabyss.arpg.global/Data && ln -sf /Users/$USER/Library/Containers/com.panstudio.duetnightabyss.arpg.global/Data /Users/$USER/Library/Containers/com.panstudio.duetnightabyss.arpg.global/Data/Library/Users/$USER/Library/Containers/com.panstudio.duetnightabyss.arpg.global/Data
3. 鼠标点击异常 ISSUE-003
使用PlayCover Nightly版,右键游戏图标 - 设置 - 键盘映射设置,启用“disableBuiltinMouse”选项。
4. 解锁60帧
右键游戏图标,选择“显示应用数据”,进入二重螺旋/Data/Library/EM/Saved/Config/IOS,用文本编辑打开Engine.ini,在文件末尾添加:
[/Script/IOSRuntimeSettings.IOSRuntimeSettings]
FrameRateLock=PUFRL_60
5. 客服界面屏幕方向错误 ISSUE-019
通过终端指令修复:
国服
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.hero.dna.ios.app/Frameworks/Game.framework/Game
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/HMServiceViewController/{found=1} found && /supportedInterfaceOrientations/{f=1} f && /imp/{print $2; exit}')
printf '\x00\x03\x80\xD2\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR)) conv=notrunc
codesign -fs- $EXECUTABLE
国际服
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.panstudio.duetnightabyss.arpg.global.app/Frameworks/HeroGlobalSDK.framework/HeroGlobalSDK
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/HGKFVC/{found=1} found && /supportedInterfaceOrientations/{f=1} f && /imp/{print $2; exit}')
printf '\x00\x03\x80\xD2\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR)) conv=notrunc
codesign -fs- $EXECUTABLE
6. 运行帧率低/发热严重
右键游戏图标,选择“显示应用数据”,进入二重螺旋/Data/Library/EM/Saved/Config/IOS,用文本编辑打开DeviceProfiles.ini,在文件末尾添加:
[IPadPro DeviceProfile]
CVars=r.MobileContentScaleFactor=1.0
可任意修改等号后面的数值。数值越小,画面越糊,运行越流畅,发热越少。
7. 剧情过场演出卡死
原因不明。可以尝试以下方法:
- 画质设置 - 显示 - 特效细节改为“低”或“极低”
- 快速点击跳过按钮
- 用云游戏过掉这段剧情
幻塔
App Store
Mac原生版本,非高配芯片不建议尝试。
PlayCover
1. 游戏跳过更新资源直接到达登录界面 ISSUE-001
PlayCover Nightly版已修复此问题。
2. 角色建模渲染问题 ISSUE-011
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.pwrd.huanta.app/QRSL
FUNC_ADDR=$(otool -Iv "$EXECUTABLE" | grep _os_proc_available_memory | head -n1 | awk '{print $1}')
printf '\x20\x00\xC0\xD2\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR-0x100000000)) conv=notrunc
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
修复前后的对比:
火环
App Store
有上架iOS版。
商店页面显示“未针对macOS验证”,即开发者并未对Mac进行适配。
- 键盘WASD卡手,无法连点普攻。
- 手柄支持不完全。
PlayCover
1. 闪退问题 ISSUE-020
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.dimcroon.hh.app/Frameworks/UnityFramework.framework/UnityFramework
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/o0_ooo0o0/{found=1} found && /o0_oaoao0/{f=1} f && /imp/{print $2; exit}')
printf '\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR)) conv=notrunc
codesign -fs- $EXECUTABLE
2. 默认键位卡手问题 ISSUE-008
通过按键映射去覆盖默认键位即可。
火炬之光:无限
PlayCover
1. 启动闪退 ISSUE-001
通过终端指令修复:
rm -r /Users/$USER/Library/Containers/com.xindong.torchlight/Data/Library/Users/$USER/Library/Containers/com.xindong.torchlight/Data && ln -sf /Users/$USER/Library/Containers/com.xindong.torchlight/Data /Users/$USER/Library/Containers/com.xindong.torchlight/Data/Library/Users/$USER/Library/Containers/com.xindong.torchlight/Data
2. 鼠标点击异常 ISSUE-003
使用PlayCover Nightly版,右键游戏图标 - 设置 - 键盘映射设置,启用“disableBuiltinMouse”选项。
金铲铲之战
PlayCover
1. 麦克风权限问题
游戏提示“您拒绝了麦克风权限,请在MacOS系统设置,隐私和安全设置当中手动打开金铲铲之战麦克风权限”。
原因分析
游戏通过UnityEngine.Application.HasUserAuthorization(UserAuthorization.Microphone)查询麦克风权限。不过经过逆向分析,此接口返回的结果总是false。推测是因为游戏打包时未设置Preprocessor.h中的宏定义UNITY_USES_MICROPHONE,导致并没有包含麦克风权限处理的相应代码。
写入补丁,强制HasUserAuthorization()返回true可暂时解决。
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.tencent.jkchess.app/jkchess
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\x7F\x0A\x00\x71\x93\x02\x88\x1A\xE0\x03\x13\xAA"); exit 1 if $i < 0; seek F, $i + 8, 0; print F "\x20\x00\x80\xD2"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
2. 麦克风权限问题2 ISSUE-016
游戏提示“没有麦克风权限,请先开启麦克风权限哦~“。
解决方法:使用PlayCover Nightly版,右键游戏图标 - 设置 - 绕过设置,启用“checkMicPermissionSync”选项。
Warning
如果同时通过Sideloadly和PlayCover安装了游戏,麦克风的授权记录可能会出现错乱的情况。
两边都卸载干净,再通过PlayCover重新安装一次即可解决问题。
3. 阵容推荐屏幕方向错误 ISSUE-019
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.tencent.jkchess.app/Frameworks/MSDKWebView.framework/MSDKWebView
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/MSDKBaseWebViewController/{found=1} found && /supportedInterfaceOrientations/{f=1} f && /imp/{print $2; exit}')
printf '\x00\x03\x80\xD2\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR)) conv=notrunc
codesign -fs- $EXECUTABLE
4. 阵容推荐打不了字 ISSUE-017
临时解决办法:右键游戏图标 - 设置 - 键盘映射设置,禁用“智能按键映射”。
开放空间
App Store
有上架iOS版。
商店页面显示“未针对macOS验证”,即开发者并未对Mac进行适配。
-
键盘只支持WASD移动,其余键位不支持。
-
存在CPU占用异常偏高的问题。
PlayCover
1. 闪退问题 ISSUE-020
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.Nekootan.kfkj.apple.app/Frameworks/UnityFramework.framework/UnityFramework
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/o0_ooo0o0/{found=1} found && /o0_oaoao0/{f=1} f && /imp/{print $2; exit}')
printf '\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR)) conv=notrunc
codesign -fs- $EXECUTABLE
2. 默认键位卡手问题 ISSUE-008
通过按键映射去覆盖默认键位即可。
3. CPU占用异常偏高 ISSUE-004
使用PlayCover Nightly版,右键游戏图标 - 设置 - 杂项设置,启用“limitMotionUpdateFrequency”选项。
CrossCover
1. 登录界面加载不出来 ISSUE-021
点CrossOver界面右上角的“运行命令”,执行以下命令:
reg add "HKCU\Environment" /v QT_QUICK_BACKEND /t REG_SZ /d "software" /f
2. 更新界面不显示文字
点CrossOver界面左下角的“安装”,搜索“思源黑体”。
恋与深空
PlayCover
1. 画面显示不全
使用PlayCover Nightly版,右键游戏图标 - 设置 - 图像设置,分辨率选择“settings.picker.adaptiveRes.6(Resizable)”。
2. 登录界面打不了字
右键游戏图标 - 设置 - 键盘映射设置,禁用“按键映射布局”。
登录完成后再重新打开这个选项。
3. 切换横屏
保存下面的脚本代码至文件lysk.scpt,在终端中运行osascript lysk.scpt可以快速调整至横屏。
脚本中的 w 是窗口宽度,h 是窗口高度,窗口的长宽比要和显示器的保持一致。
tell application "Finder"
set screenBounds to bounds of window of desktop
set screenWidth to item 3 of screenBounds
set screenHeight to item 4 of screenBounds
end tell
tell application "System Events"
tell process "恋与深空"
set w to 1280
set h to 800
set x to (screenWidth - w) / 2
set y to (screenHeight - h) / 2
set position of window 1 to {x, y}
set size of window 1 to {w, h}
end tell
end tell
tell application "恋与深空" to activate
delay 0.1
tell application "System Events"
keystroke "k" using command down
delay 0.1
keystroke "k" using command down
delay 0.1
keystroke "a" using option down
end tell
Note
如果提示找不到“恋与深空“,请改成“com.papegames.lysk“。
Note
如果提示没有操作权限,请打开系统设置 - 隐私与安全性,授予”终端“辅助功能和自动化的权限。
空灵诗篇
App Store
有上架iOS版。
商店页面显示“未针对macOS验证”,即开发者并未对Mac进行适配。
- 存在CPU占用异常偏高的问题。
PlayCover
1. CPU占用异常偏高 ISSUE-004
使用PlayCover Nightly版,右键游戏图标 - 设置 - 杂项设置,启用“limitMotionUpdateFrequency”选项。
2. 解锁高分辨率
游戏锁1080P分辨率,使用下面的终端指令进行解锁,解锁后会以PlayCover中设置的分辨率来运行游戏。
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.zlongame.eversoul.cn.app/Frameworks/UnityFramework.framework/UnityFramework
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\x03\x50\x2C\x1E\x40\x20\x23\x1E"); exit 1 if $i < 0; seek F, $i - 0x130, 0; print F "\x13\x00\x80\xD2"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE
锚点降临
App Store
加载卡住,玩不了。
PlayCover
1. 加载卡住问题 ISSUE-022
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.leiyan.mdjl.app/Frameworks/UnityFramework.framework/UnityFramework
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/InlandSDKManager/{found=1} found && /_reqATTracking:/{f=1} f && /imp/{print $2; exit}')
printf '\x00\x00\x80\xD2' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR+0x38)) conv=notrunc
codesign -fs- $EXECUTABLE
2. CPU占用异常偏高 ISSUE-004
使用PlayCover Nightly版,右键游戏图标 - 设置 - 杂项设置,启用“limitMotionUpdateFrequency”选项。
鸣潮
App Store
Important
如果之前安装过iOS版,请先清除应用数据缓存以避免冲突:
点击访达菜单 - 前往 - 前往文件夹 - 输入~/Library/Containers/鸣潮,删除此文件夹。
Mac原生版本,可正常游玩。
PlayCover
Important
如果之前安装过Mac版,请先清除应用数据缓存以避免冲突:
右键游戏图标,选择“显示应用数据”,删除“鸣潮”文件夹。
1. 中文系统下启动游戏闪退 ISSUE-002
PlayCover Nightly版已修复此问题。
2. 文件重命名失败 ISSUE-001
PlayCover Nightly版已修复此问题。
3. 拉海洛地图异常卡顿 ISSUE-011
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.kurogame.mingchao.app/Client
FUNC_ADDR=$(otool -Iv "$EXECUTABLE" | grep _os_proc_available_memory | head -n1 | awk '{print $1}')
printf '\x20\x00\xC0\xD2\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR-0x100000000)) conv=notrunc
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
4. 打不了字 ISSUE-006
临时解决办法:右键游戏图标 - 设置 - 键盘映射设置,禁用“按键映射布局” 或 禁用“智能按键映射”。
5. 键鼠用不了快捷轮盘
进入映射编辑模式,在钩索按钮的位置上点一下,选择左下角鼠标图案的按钮,会创建一个Mouse映射。
选中这个Mouse映射,按下键盘上的Tab键。
退出映射编辑模式,按住Tab键触发快捷轮盘,此时挪动鼠标可以调整选中项。
当然,键鼠玩家还是推荐用Mac版。
6. 键鼠开不了摩托车
PlayCover Nightly版已支持多组映射方案切换。创建多组映射方案,在游戏中通过command + [和command + ]切换。
当然,键鼠玩家还是推荐用Mac版。
7. 运行帧率低/发热严重
右键游戏图标,选择“显示应用数据”,进入鸣潮/Data/Library/Client/Saved/Config/IOS,用文本编辑打开DeviceProfiles.ini,在文件末尾添加:
[iPadPro5_129 DeviceProfile]
CVars=r.MobileContentScaleFactor=1.0
右键游戏图标 - 设置 - 图像设置,查看选定的iOS机型:
- 如果是M1 iPad,则保持不变
- 如果是M2 iPad,则将
iPadPro5_129改为iPadPro6_129 - 如果是M4 iPad,则将
iPadPro5_129改为iPadPro7_129
可任意修改等号后面的数值。数值越小,画面越糊,运行越流畅,发热越少。
命运:群星
👍 极其少有的对iOS版做了完整键鼠适配的游戏。
PlayCover
- 手柄方面正常。
- 键鼠方面因为PlayCover的代码默认会拦截隐藏鼠标指针的操作,导致游戏自带的键鼠适配无法正常工作。
Sideloadly
完美运行。
逆战:未来
PlayCover
1. 用户协议界面闪退
右键游戏图标 - 设置 - 键盘映射设置,禁用“按键映射布局”。
过掉用户协议后再重新打开这个选项。
2. 卡在重复检查资源 ISSUE-001
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.tencent.tmgp.nz.app/NZMClient
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\x2F\x00\x76\x00\x61\x00\x72\x00\x2F\x00\x00\x00"); exit 1 if $i < 0; seek F, $i, 0; print F "\x2F\x00\x55\x00\x73\x00\x65\x00\x72\x00\x00\x00"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
3. 摇杆映射问题 ISSUE-007
使用PlayCover Nightly版,在编辑模式中选中WASD控件,更改为Floating Joystick模式。
4. 麦克风问题 ISSUE-016
使用PlayCover Nightly版,右键游戏图标 - 设置 - 绕过设置,启用“checkMicPermissionSync”选项。
5. 调整全局UI缩放
右键游戏图标,选择“显示应用数据”,进入逆战:未来/Data/Documents/NZM/Saved/Config/IOS,用文本编辑打开Engine.ini,在文件末尾添加:
[/Script/Engine.UserInterfaceSettings]
UIScaleRule=ShortestSide
UIScaleCurve=(EditorCurveData=(Keys=((Time=1440.000000,Value=0.70000000)),PreInfinityExtrap=RCCE_Constant,PostInfinityExtrap=RCCE_Constant,DefaultValue=0),ExternalCurve=None)
其中Time=1440表示分辨率高度1440,Value=0.7表示缩放值0.7,可随意修改。
女神异闻录:夜幕魅影
PlayCover
1. 国服启动闪退 ISSUE-024
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.pwrd.persona5x.pw.app/Frameworks/UnityFramework.framework/UnityFramework
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/KeyboardDelegate/{found=1} found && /Initialize/{f=1} f && /imp/{print $2; exit}')
printf '\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR)) conv=notrunc
codesign -fs- $EXECUTABLE
2. CPU占用异常偏高 ISSUE-004
使用PlayCover Nightly版,右键游戏图标 - 设置 - 杂项设置,启用“limitMotionUpdateFrequency”选项。
七大罪:光与暗之交战
PlayCover
1. 画面显示不全
使用PlayCover Nightly版,右键游戏图标 - 设置 - 图像设置,分辨率选择“settings.picker.adaptiveRes.6(Resizable)”。
2. 无限Loading ISSUE-015
通过终端指令修复:
修改-[NMGGameCenterManager returnCallbackOrRegisterAuthenticateHandler:],跳过Game Center Login步骤。
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.netmarble.nanagb.app/Frameworks/UnityFramework.framework/UnityFramework
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/NMGGameCenterManager/{found=1} found && /baseMethods/{f=1; i=0} f && /imp/{i++; if(i==28){print substr($3,2,length($3)-2); exit}}')
printf '\xB5' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR+0x9B)) conv=notrunc
codesign -fs- $EXECUTABLE
七骑士 Re:BIRTH
PlayCover
1. 鼠标点击异常 ISSUE-003
使用PlayCover Nightly版,右键游戏图标 - 设置 - 键盘映射设置,启用“disableBuiltinMouse”选项。
CrossCover
正常运行,播放视频时会黑屏。
添加命令行参数--in-process-gpu可打开Netmarble启动器。
三国志·战略版
PlayCover
1. 加载界面卡死无响应 ISSUE-023
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.s3.sgzzlb.cn.app/cn_appstore
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\x00\x00\xC0\x3D\x60\x02\x80\x3D\x00\x84\x40\xAD\x02\x8C\x41\xAD\x62\x8E\x01\xAD\x60\x86\x00\xAD\x00\x84\x42\xAD\x02\x1C\xC0\x3D"); exit 1 if $i < 0; seek F, $i - 4, 0; print F "\x1F\x00\x00\xB9"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
2. 登录后验证码界面不显示
暂时无法解决。
原因分析
游戏登录后会访问https://general.aligames.com/ieu-sdk-h5/challenge.html来显示验证码。但是由于WebKit在Mac上是默认通过桌面模式打开网页的,navigator.platform = “MacIntel”,navigator.maxTouchPoints = 0。
challenge.html判断设备类型时发现不满足iOS的条件,所以后续代码都没有执行。
临时解决方法:
PlayTools NSObject+Swizzle.m
强制WebKit以移动端模式打开网页。
#import <WebKit/WebKit.h>
- (WKWebView *) hook_WKWebView_initWithFrame:(CGRect) frame
configuration:(WKWebViewConfiguration *) config {
WKWebView *webView = [self hook_WKWebView_initWithFrame:frame configuration:config];
webView.configuration.defaultWebpagePreferences.preferredContentMode = WKContentModeMobile;
return webView;
}
[objc_getClass("WKWebView") swizzleInstanceMethod:@selector(initWithFrame:configuration:) withMethod:@selector(hook_WKWebView_initWithFrame:configuration:)];
少女前线2:追放
PlayCover
1. 神秘弹窗
通过终端指令绕过:
TARGET=$(echo -n "dGVyc2FmZTI=" | base64 -d)
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.Sunborn.SnqxExilium.app/Frameworks/$TARGET.framework/$TARGET
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/ShowMessageBoxWithTitle:Message:LeftBtn:RightBtn:/{found=1} found && /imp/{print $2; exit}')
printf '\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR)) conv=notrunc
codesign -fs- $EXECUTABLE
2. CPU占用异常偏高 ISSUE-004
使用PlayCover Nightly版,右键游戏图标 - 设置 - 杂项设置,启用“limitMotionUpdateFrequency”选项。
失落之剑
PlayCover
1. 无限Loading ISSUE-022
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.wemadeconnect.ios.lostdgl.app/Frameworks/UnityFramework.framework/UnityFramework
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/TrackingAuthorizationManager/{found=1} found && /getTrackingAuthorizationStatus/{f=1} f && /imp/{print $2; exit}')
printf '\x40\x00\x80\xD2\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR)) conv=notrunc
codesign -fs- $EXECUTABLE
2. 自动登录闪退 ISSUE-005
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.wemadeconnect.ios.lostdgl.app/Frameworks/UnityFramework.framework/UnityFramework
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/FIRAuthKeychainServices/{found=1} found && /itemWithQuery:error:/{f=1} f && /imp/{print $2; exit}')
printf '\x44\x00\x00\x14' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR+0x110)) conv=notrunc
printf '\x1F\x20\x03\xD5' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR+0x2A4)) conv=notrunc
codesign -fs- $EXECUTABLE
实况足球(eFootball)
PlayCover
1. 启动后完全黑屏 ISSUE-023
通过终端指令修复:
国服
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.netease.pes.app/PesMobile-Shipping-Master
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\x00\x00\xC0\x3D\x60\x02\x80\x3D\x00\x84\x40\xAD\x02\x8C\x41\xAD\x62\x8E\x01\xAD\x60\x86\x00\xAD\x00\x84\x42\xAD\x02\x1C\xC0\x3D"); exit 1 if $i < 0; seek F, $i - 4, 0; print F "\x1F\x00\x00\xB9"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
国际服
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/jp.konami.pesactionmobile.app/PESmobile
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\x00\x00\xC0\x3D\x60\x02\x80\x3D\x00\x84\x40\xAD\x02\x8C\x41\xAD\x62\x8E\x01\xAD\x60\x86\x00\xAD\x00\x84\x42\xAD\x02\x1C\xC0\x3D"); exit 1 if $i < 0; seek F, $i - 4, 0; print F "\x1F\x00\x00\xB9"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
天涯明月刀手游
PlayCover
1. 屏幕方向错误 ISSUE-013
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.tencent.wuxia.app/wuxia
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/UnityAppController/{found=1} found && /createRootViewController$/{f=1} f && /imp/{print $2; exit}')
printf '\x00\x00\x80\xD2' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR-0x100000000+0x18)) conv=notrunc
printf '\x82\x00\x80\xD2' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR-0x100000000+0x6C)) conv=notrunc
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
无限暖暖
PlayCover
1. 中文系统下启动游戏闪退 ISSUE-002
PlayCover Nightly版已修复此问题。
2. 无限播放CG ISSUE-001
PlayCover Nightly版已修复此问题。如果问题依旧存在,请清除应用数据后再试。
3. 鼠标点击异常 ISSUE-003
使用PlayCover Nightly版,右键游戏图标 - 设置 - 键盘映射设置,启用“disableBuiltinMouse”选项。
4. 国际服登录闪退
右键游戏图标,选择“显示应用数据”,进入无限暖暖/Data/Library/Application Support/Google/Measurement。
清空该目录下的所有文件。
返回上一级目录,右键Measurement显示简介,修改“本用户“的权限为”只读”。
星痕共鸣
PlayCover
1. 默认键位卡手问题 ISSUE-008
通过按键映射去覆盖默认键位即可。
2. 麦克风权限问题 ISSUE-016
使用PlayCover Nightly版,右键游戏图标 - 设置 - 绕过设置,启用“checkMicPermissionSync”选项。
星塔旅人
App Store
仅国区商店有上架iOS版,外区未上架。
商店页面显示“未针对macOS验证”,即开发者并未对Mac进行适配。
-
存在默认键位卡手问题,且部分技能没有绑定按键,手柄则一切正常。
-
存在CPU占用异常偏高的问题。
PlayCover
1. 短暂运行后闪退 ISSUE-012
通过终端指令修复:
国服
TARGET=$(echo -n "YWNlcnR4" | base64 -d)
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.RoamingStar.StellaSora.app/Frameworks/$TARGET.framework/$TARGET
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\xF3\x03\x00\xAA\x16\x00\x80\x52\x14\xE2\x84\x52\x35\x48\x88\x52\xF5\x01\xA0\x72\xDF\x02\x14\x6B\x60\x00\x00\x54"); exit 1 if $i < 0; seek F, $i - 28, 0; print F "\x20\x00\x80\xD2\xC0\x03\x5F\xD6"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE >/dev/null 2>&1 && echo "\n\033[0;32mDone\033[0m\n"
其他服(自行替换Bundle ID):
BUNDLE_ID=com.Stargazer.StellaSora
TARGET=$(echo -n "YW5vcnR4" | base64 -d)
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/$BUNDLE_ID.app/Frameworks/$TARGET.framework/$TARGET
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\xF3\x03\x00\xAA\x16\x00\x80\x52\x14\xE2\x84\x52\x35\x48\x88\x52\xF5\x01\xA0\x72\xDF\x02\x14\x6B\x60\x00\x00\x54"); exit 1 if $i < 0; seek F, $i - 28, 0; print F "\x20\x00\x80\xD2\xC0\x03\x5F\xD6"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE >/dev/null 2>&1 && echo "\n\033[0;32mDone\033[0m\n"
2. 默认键位卡手问题 ISSUE-008
通过按键映射去覆盖默认键位即可。
3. CPU占用异常偏高 ISSUE-004
使用PlayCover Nightly版,右键游戏图标 - 设置 - 杂项设置,启用“limitMotionUpdateFrequency”选项。
学园偶像大师
PlayCover
1. 画面显示不全
使用PlayCover Nightly版,右键游戏图标 - 设置 - 图像设置,分辨率选择“settings.picker.adaptiveRes.6(Resizable)”。
2. 黑屏问题
拖拽窗口边缘,调整至对应横屏或竖屏的大小即可。
3. 登录状态丢失 ISSUE-005
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/jp.co.bandainamcoent.BNEI0421.app/Frameworks/UnityFramework.framework/UnityFramework
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\xB3\x05\x80\xD2\x13\x00\xFA\xF2\x60\x6E\x00\xD1") - 0x54; exit 1 if $i < 0; seek F, $i, 0; my $val = unpack("V", do { read(F, my $buf, 4); $buf }); my $val2 = unpack("V", do { read(F, my $buf, 4); $buf }); my $offset = ((($val >> 5) & 0x7FFFF) << 2) | (($val >> 29) & 0x3); my $page = ($i >> 12) + $offset; my $i = index($d, "\x00\x00\x80\xD2\x01\x00\xFE\xD2\x12\x00\x00\x14") - 0x318; exit 1 if $i < 0; my $j = $i + 0x2A0; seek F, $j, 0; my $offset = $page - ($j >> 12); my $val = (1 << 31) | (($offset & 0x3) << 29) | (0x10 << 24) | ((($offset >> 2) & 0x7FFFF) << 5); print F pack("V", $val); print F pack("V", $val2); my $j = $i + 0x2E4; seek F, $j, 0; print F "\xFB\x03\x00\xF9\xF4\x03\x16\xAA\xD3\x00\x00\x14"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE
或者保存以下内容为script.sh,通过终端指令sh script.sh运行:
#!/bin/zsh
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/jp.co.bandainamcoent.BNEI0421.app/Frameworks/UnityFramework.framework/UnityFramework
perl -e '
open F, "+<:raw", $ARGV[0] or die $!;
local $/;
my $d = <F>;
my $i = index($d, "\xB3\x05\x80\xD2\x13\x00\xFA\xF2\x60\x6E\x00\xD1") - 0x54;
exit 1 if $i < 0;
seek F, $i, 0;
my $val = unpack("V", do { read(F, my $buf, 4); $buf });
my $val2 = unpack("V", do { read(F, my $buf, 4); $buf });
my $offset = ((($val >> 5) & 0x7FFFF) << 2) | (($val >> 29) & 0x3);
my $page = ($i >> 12) + $offset;
my $i = index($d, "\x00\x00\x80\xD2\x01\x00\xFE\xD2\x12\x00\x00\x14") - 0x318;
exit 1 if $i < 0;
my $j = $i + 0x2A0;
seek F, $j, 0;
my $offset = $page - ($j >> 12);
my $val = (1 << 31) | (($offset & 0x3) << 29) | (0x10 << 24) | ((($offset >> 2) & 0x7FFFF) << 5);
print F pack("V", $val);
print F pack("V", $val2);
my $j = $i + 0x2E4;
seek F, $j, 0;
print F "\xFB\x03\x00\xF9\xF4\x03\x16\xAA\xD3\x00\x00\x14";
close F;
' $EXECUTABLE
codesign -fs- $EXECUTABLE
燕云十六声
PlayCover
1. 画面比例异常
右键游戏图标 - 设置 - 图像设置,启用“修复窗口化显示问题”选项。
2. 键鼠用不了快捷轮盘
进入映射编辑模式,在轮盘按钮的位置上点一下,选择左下角鼠标图案的按钮,会创建一个Mouse映射。
选中这个Mouse映射,按下键盘上的Tab键。
退出映射编辑模式,按住Tab键触发快捷轮盘,此时挪动鼠标可以调整选中项。
永恒之塔2
App Store
有上架iOS版,可正常游玩。
目前的问题:不支持隐藏鼠标,无法绑定技能到鼠标左右键。
Tip
访达菜单 - 前往 - 前往文件夹,输入
~/Library/Containers/AION2可打开应用数据目录。解锁60帧和减少发热的方法请看下文。
PlayCover
1. 鼠标点击异常 ISSUE-003
使用PlayCover Nightly版,右键游戏图标 - 设置 - 键盘映射设置,启用“disableBuiltinMouse”选项。
2. 不停弹出崩溃报告 ISSUE-010
使用PlayCover Nightly版即可解决。
3. 解锁60帧
右键游戏图标,选择“显示应用数据”,进入AION2/Data/Library/AION2/Saved/Config/IOS,创建Engine.ini,然后用文本编辑打开Engine.ini:
[/Script/IOSRuntimeSettings.IOSRuntimeSettings]
FrameRateLock=PUFRL_60
如果需要更高的帧率,可改成:
[/Script/IOSRuntimeSettings.IOSRuntimeSettings]
FrameRateLock=PUFRL_None
4. 运行帧率低/发热严重
右键游戏图标,选择“显示应用数据”,进入AION2/Data/Library/AION2/Saved/Config/IOS,创建DeviceProfiles.ini,然后用文本编辑打开DeviceProfiles.ini:
[iPadPro5_129 DeviceProfile]
CVars=r.MobileContentScaleFactor=1.0
可任意修改等号后面的数值。数值越小,画面越糊,运行越流畅,发热越少。
CrossCover
因反作弊无法运行。
添加命令行参数--in-process-gpu可打开PURPLE启动器。
永劫无间手游
PlayCover
1. 进入大厅后没声音
在大厅等待一分钟再操作。
2. 摇杆映射问题
游戏设置 - 操作布局 - 摇杆与轮盘 - 移动摇杆操作方式,改为“固定摇杆”。
2. 按钮拖拽释放问题
游戏设置 - 操作布局 - 自定义布局,改为“点击按钮方案”。
棕色尘埃2
PlayCover
1. 谷歌登录报错
登录时提示“HAS_NO_AUTH_IN_KEYCHAIN”。
可通过终端指令修复:
defaults delete com.neowizgames.game.browndust2ios neo_google_user_id
2. 登录状态丢失 ISSUE-005
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.neowizgames.game.browndust2ios.app/Frameworks/FirebaseAuth.framework/FirebaseAuth
printf '\x20\x07\x00\xF0\x00\x20\x0A\x91\xE1\x04\x00\xD0\x21\x60\x22\x91' | dd of=$EXECUTABLE bs=1 seek=$((0x36A94)) conv=notrunc
printf '\xF4\x03\x17\xAA\xAA\x00\x00\x14' | dd of=$EXECUTABLE bs=1 seek=$((0x36AE4)) conv=notrunc
codesign -fs- $EXECUTABLE
3. 使用苹果帐号登录
请严格按照以下步骤操作:
- 确认是否已执行上述第二条中的指令
- 确认是否启用了PlayChain(右键游戏图标 - 设置 - 绕过设置 - 启用PlayChain)
- 禁用SIP
- 正常载入系统,终端执行
sudo nvram boot-args="amfi_get_out_of_my_way=0x1 ipc_control_port_options=0",重启电脑 - 正常载入系统,从PlayCover界面打开游戏并登录苹果帐号,关闭游戏后等待10秒
- 启用SIP
- 正常载入系统,测试苹果帐号登录状态
Warning
受限于PlayCover保存PlayChain数据的实现方式。
- 关闭游戏后,距离下一次打开最好超过10秒,否则会造成PlayChain数据丢失。
- 每次都从PlayCover界面启动游戏,不要从程序坞、启动台或访达界面启动游戏。
4. 方向键映射问题 ISSUE-007
使用PlayCover Nightly版,在编辑模式中选中WASD控件,更改为Floating Joystick模式。
5. CPU占用异常偏高 ISSUE-004
使用PlayCover Nightly版,右键游戏图标 - 设置 - 杂项设置,启用“limitMotionUpdateFrequency”选项。
CrossCover
目前无法运行,logo界面会卡住。
Wine Devel
可正常运行,需搭配DXMT 0.72或以上的版本。
最终幻想14:水晶世界
PlayCover
1. 进入登录界面时黑屏 ISSUE-001
通过终端指令修复:
rm -r /Users/$USER/Library/Containers/com.tencent.tmgp.fmgame/Data/Documents/Users/$USER/Library/Containers/com.tencent.tmgp.fmgame/Data && ln -sf /Users/$USER/Library/Containers/com.tencent.tmgp.fmgame/Data /Users/$USER/Library/Containers/com.tencent.tmgp.fmgame/Data/Documents/Users/$USER/Library/Containers/com.tencent.tmgp.fmgame/Data
2. 摇杆映射问题 ISSUE-007
使用PlayCover Nightly版,在编辑模式中选中WASD控件,更改为Floating Joystick模式。
3. 技能拖拽释放问题
方法一:进入映射编辑模式,在技能按钮的位置上点一下,选择左下角鼠标图案的按钮,会创建一个Mouse映射。选中这个Mouse映射,按下键盘上的Tab键。退出映射编辑模式,按住Tab键打开技能轮盘,此时挪动鼠标可以调整选中项。
方法二:点击按钮的边缘也可以直接触发技能,在边缘上放置按键映射即可。
4. 打不了字
临时解决办法:右键游戏图标 - 设置 - 键盘映射设置,禁用“按键映射布局” 或 禁用“智能按键映射”。
5. 麦克风权限问题 ISSUE-016
使用PlayCover Nightly版,右键游戏图标 - 设置 - 绕过设置,启用“checkMicPermissionSync”选项。
6. 运行帧率低/发热严重
右键游戏图标,选择“显示应用数据”,进入最终幻想14:水晶世界/Data/Documents/FGame/Saved/Config/IOS,创建DeviceProfiles.ini,然后用文本编辑打开DeviceProfiles.ini:
[iPadPro5_129 DeviceProfile]
CVars=r.MobileContentScaleFactor=1.5
右键游戏图标 - 设置 - 图像设置,查看选定的iOS机型:
- 如果是M1 iPad,则保持不变
- 如果是M2 iPad,则将
iPadPro5_129改为iPadPro6_129 - 如果是M4 iPad,则将
iPadPro5_129改为iPadPro7_13
可任意修改等号后面的数值。数值越小,画面越糊,运行越流畅,发热越少。
NIKKE
PlayCover
Caution
国服版本可能会封号,本页面仅讨论国际服。
1. 国际服启动卡死
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.proximabeta.nikke.app/Frameworks/UnityFramework.framework/UnityFramework
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/INTLUtilsIOS/{found=1} found && /swizzlingOriginalClass:swizzledClass:originalSEL:swizzledSEL:/{f=1} f && /imp/{print $2; exit}')
printf '\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR)) conv=notrunc
codesign -fs- $EXECUTABLE
2. 画面显示不全
通过终端指令修复:
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/com.proximabeta.nikke.app/Frameworks/UnityFramework.framework/UnityFramework
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/UnityDefaultViewController/{found=1} found && /supportedInterfaceOrientations/{f=1} f && /imp/{print $2; exit}')
printf '\x00\x03\x80\xD2\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR)) conv=notrunc
codesign -fs- $EXECUTABLE
3. CPU占用异常偏高 ISSUE-004
使用PlayCover Nightly版,右键游戏图标 - 设置 - 杂项设置,启用“limitMotionUpdateFrequency”选项。
VRChat
PlayCover
需要移除PlayTools才能运行:右键游戏图标 - 设置 - 杂项 - 移除PlayTools。
问题列表
- 001 虚幻引擎文件路径错误
- 002 虚幻引擎字符串解码失败导致闪退
- 003 虚幻引擎鼠标点击异常
- 004 CPU占用异常偏高
- 005 FirebaseSDK读取登录状态失败
- 006 虚幻引擎打不了字
- 007 摇杆映射问题
- 008 Unity默认键位卡手问题
- 009 虚幻引擎缺少Entitlement
- 010 Process Forked报错
- 011 查询可用运行内存的API返回错误结果
- 012 神秘闪退
- 013 Unity游戏屏幕方向错误
- 014 上层View导致按键映射失灵
- 015 Game Center登录界面不显示
- 016 腾讯游戏麦克风权限问题
- 017 WebView界面打不了字
- 018 NeoX引擎文件路径错误
- 019 UI界面屏幕方向错误
- 020 神秘闪退
- 021 QTWebEngine界面加载问题
- 022 App跟踪询问窗口不显示
- 023 CRIWARE多线程死锁问题
- 024 Unity KeyboardDelegate重复初始化
ISSUE-001
虚幻引擎文件路径错误
常见表现
加载进度卡住,加载过程中提示存储空间不足,加载过程中提示文件操作失败,加载过程中闪退。
相关源码
虚幻引擎 IOSPlatformFile.cpp
FString FIOSPlatformFile::ConvertToPlatformPath(const FString& Filename, bool bForWrite, bool bIsPublicWrite)
{
FString Result = Filename;
if (Result.StartsWith(TEXT("/var/")))
{
return Result;
}
if (bForWrite)
{
static FString PublicWritePathBase = FString([NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]) + TEXT("/");
static FString PrivateWritePathBase = FString([NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]) + TEXT("/");
return (bIsPublicWrite ? PublicWritePathBase : PrivateWritePathBase) + Result;
}
else
{
static FString ReadPathBase = FString([[NSBundle mainBundle] bundlePath]) + TEXT("/cookeddata/");
return ReadPathBase + Result.ToLower();
}
}
原因分析
源码中根据字符串是否以/var/开头,来判断字符串是否绝对路径。如果不是绝对路径,则会在字符串前面附加Documents目录或Library目录的路径。
在iOS上,绝对路径的格式为:/var/mobile/Containers/Data/Application/{GUID}/Documents
在macOS上,绝对路径的格式为:/Users/$USER/Containers/{BUNDLE_ID}/Data/Documents
macOS的绝对路径并不是以/var/开头的,调用ConvertToPlatformPath()后会生成错误的路径。
如果所有文件操作都经过一遍ConvertToPlatformPath()处理的话,是不会导致报错的,只有在读写操作不一致时才会出问题。
例如通过虚幻引擎API写入文件,间接调用了ConvertToPlatformPath(),但在读取文件时却直接使用NSFileManager之类的系统API,就会出现路径不一致,从而导致各种报错。
解决方法
文件链接法
BUNDLE_ID=com.companyname.appname
rm -r /Users/$USER/Library/Containers/$BUNDLE_ID/Data/Documents/Users/$USER/Library/Containers/$BUNDLE_ID/Data && ln -sf /Users/$USER/Library/Containers/$BUNDLE_ID/Data /Users/$USER/Library/Containers/$BUNDLE_ID/Data/Documents/Users/$USER/Library/Containers/$BUNDLE_ID/Data
rm -r /Users/$USER/Library/Containers/$BUNDLE_ID/Data/Library/Users/$USER/Library/Containers/$BUNDLE_ID/Data && ln -sf /Users/$USER/Library/Containers/$BUNDLE_ID/Data /Users/$USER/Library/Containers/$BUNDLE_ID/Data/Library/Users/$USER/Library/Containers/$BUNDLE_ID/Data
补丁法
修改常量字符串/var/为/User(保持字符串长度不变)
BUNDLE_ID=com.companyname.appname
BUNDLE_PATH=~/Library/Containers/io.playcover.PlayCover/Applications/$BUNDLE_ID.app
EXECUTABLE=$BUNDLE_PATH/$(/usr/libexec/PlistBuddy -c "Print :CFBundleExecutable" $BUNDLE_PATH/Info.plist)
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\x2F\x00\x76\x00\x61\x00\x72\x00\x2F\x00\x00\x00"); exit 1 if $i < 0; seek F, $i, 0; print F "\x2F\x00\x55\x00\x73\x00\x65\x00\x72\x00\x00\x00"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
ISSUE-002
虚幻引擎字符串解码失败导致闪退
常见表现
系统语言为中文时,启动游戏立刻闪退。
相关源码
虚幻引擎 IOSPlatformMisc.cpp
typedef struct __Blob {
uint32_t magic;
uint32_t length;
char data[];
} CS_GenericBlob;
extern NSString *EntitlementsData(void)
{
//...
CS_GenericBlob blob;
Count = fread(&blob, sizeof(CS_GenericBlob), 1, file);
if (__builtin_bswap32(blob.magic) == 0xfade7171)
{
uint32 blobLength = ntohl(blob.length);
char data[blobLength];
fread(&data[0], sizeof(char) * blobLength, 1, file);
NSString *stringFromData = [NSString stringWithFormat: @"%s", data];
NSLog(@"%@", stringFromData);
fclose(file);
return stringFromData;
}
//...
}
原因分析
data的长度应为blobLength减去8,而不是等于blobLength。由于fread()多读取了8个字节,最终调用[NSString stringWithFormat:]时字符串解码失败导致闪退。
解决方法
方法一:PlayCover Nightly版已修复此问题。
方法二:打开系统配置文件~/.CFUserTextEncoding,修改冒号前的部分为0x0,修改后重启电脑。
ISSUE-003
虚幻引擎鼠标点击异常
常见表现
鼠标点击UI无反应,屏幕上出现两个点击特效。
原因分析
游戏同时捕获了触屏输入和鼠标输入,且由于iOS并没有提供获取鼠标位置的API,所以游戏记录的鼠标位置始终为屏幕左上角 (0, 0)。
每当玩家按下鼠标,都会在鼠标所在位置和屏幕左上角执行一次点击,从而出现点击冲突的情况。
解决方法
使用PlayCover Nightly版,右键游戏图标 - 设置 - 键盘映射设置,启用“disableBuiltinMouse”选项。
ISSUE-004
CPU占用异常偏高
常见表现
CPU占用异常偏高,电脑发热厉害。
此问题通常会在Unity游戏和使用了重力感应功能(摇一摇)的App中出现。
相关源码
UIKitMacHelper.framework
Mac运行iOS应用时,CMMotionManager的底层实现为UINSVirtualMotionDevice。
void -[UINSVirtualMotionDevice startAccelerometerUpdates]
{
if (!_accelerometerDispatchIsActive)
{
dispatch_resume(_accelerometerDispatchSource);
_accelerometerDispatchIsActive = YES;
}
}
id -[UINSVirtualMotionDevice accelerometerData]
{
if (!_accelerometerDispatchIsActive)
{
dispatch_resume(_accelerometerDispatchSource);
_accelerometerDispatchIsActive = YES;
}
return _accelerometerData;
}
Unity引擎 iPhone_Sensors.mm
extern "C" void UnityCoreMotionStart()
{
if (sMotionManager.accelerometerAvailable)
{
const int frequency = UnityGetAccelerometerFrequency();
if (frequency > 0)
{
sMotionManager.accelerometerUpdateInterval = 1.0f / frequency;
[sMotionManager startAccelerometerUpdates];
}
}
}
extern "C" void UnityUpdateAccelerometerData()
{
CMAccelerometerData* data = sMotionManager.accelerometerData;
if (data != nil)
{
Vector3f res = UnityReorientVector3(data.acceleration.x, data.acceleration.y, data.acceleration.z);
UnityDidAccelerate(res.x, res.y, res.z, data.timestamp);
}
}
原因分析
第一个问题:CMMotionManager的默认更新间隔为0秒,如果不修改更新间隔,直接调用startAccelerometerUpdates就会导致频繁更新,从而占用大量CPU时间。
第二个问题:访问CMMotionManager的accelerometerData数据时,会自动触发startAccelerometerUpdates,也就是说这个接口有副作用。
而Unity引擎在某种情况下,实例化了CMMotionManager,没有修改更新间隔,也没有主动调用startAccelerometerUpdates。
但是因为之后读取了accelerometerData,间接触发了startAccelerometerUpdates,从而导致更新间隔0秒的频繁更新。
解决方法
玩家方面:使用PlayCover Nightly版,右键游戏图标 - 设置 - 杂项设置,启用“limitMotionUpdateFrequency”选项。
开发者方面:调整默认的更新间隔,避免使用0秒间隔。
extern "C" void UnityCoreMotionStart()
{
//...
bool initMotionManager = (sMotionManager == nil);
if (initMotionManager) {
sMotionManager = [[CMMotionManager alloc] init];
sMotionManager.accelerometerUpdateInterval = 0.01;
sMotionManager.deviceMotionUpdateInterval = 0.01;
sMotionManager.gyroUpdateInterval = 0.01;
}
//...
}
ISSUE-005
FirebaseSDK读取登录状态失败
常见表现
使用谷歌帐号登录的游戏,第二次打开时出现闪退,或是需要重新登录。
相关源码
- (nullable NSData *)itemWithQuery:(NSDictionary *)query error:(NSError **_Nullable)error {
NSMutableDictionary *returningQuery = [query mutableCopy];
returningQuery[(__bridge id)kSecReturnData] = @YES;
returningQuery[(__bridge id)kSecReturnAttributes] = @YES;
returningQuery[(__bridge id)kSecMatchLimit] = @2;
CFArrayRef result = NULL;
OSStatus status =
SecItemCopyMatching((__bridge CFDictionaryRef)returningQuery, (CFTypeRef *)&result);
if (status == noErr && result != NULL) {
NSArray *items = (__bridge_transfer NSArray *)result;
if (items.count == 0) {
return nil;
}
//...
}
}
原因分析
FirebaseSDK传入参数kSecMatchLimit=2,并期望返回结果是一个NSArray。
但目前PlayCover的代码并没有正确处理kSecMatchLimit=2的情况,PlayCover返回的是一个NSDictionary,类型不匹配导致出错。
解决方法
根本解决方法:修改PlayCover代码。
临时解决方法一:每次打开游戏之前,右键游戏图标,清除PlayChain数据可解决闪退。
临时解决方法二:插入跳转指令,从A处直接跳转到B处。
Firebase SDK旧版本(Objcetive-C):
- (nullable NSData *)itemWithQuery:(NSDictionary *)query error:(NSError **_Nullable)error {
CFArrayRef result = NULL;
OSStatus status =
SecItemCopyMatching((__bridge CFDictionaryRef)returningQuery, (CFTypeRef *)&result);
if (status == noErr && result != NULL) {
NSArray *items = (__bridge_transfer NSArray *)result;
/* --------- A --------- */
if (items.count == 0) {
// throw error
} else if (items.count > 1) {
// log("Keychain query returned multiple results")
}
NSDictionary *item = items[0];
/* --------- B --------- */
return item[(__bridge id)kSecValueData];
}
}
0x4C8A04 STR XZR, [SP,#0x130+result]
0x4C8A08 ADD X1, SP, #0x130+result ; result
0x4C8A0C MOV X0, X19 ; query
0x4C8A10 BL _SecItemCopyMatching
0x4C8A14 MOV X3, X0
0x4C8A18 CBNZ W0, loc_4C8B4C
0x4C8A1C LDR X20, [SP,#0x130+result]
0x4C8A20 CBZ X20, loc_4C8B4C
0x4C8A24 MOV X0, X20
0x4C8A28 BL _objc_msgSend$count ; 改为B #0x110,跳转至0x4C8B38
0x4C8A2C CBZ X0, loc_4C8BCC
0x4C8A30 MOV X0, X20
0x4C8A34 BL _objc_msgSend$count
0x4C8A38 CMP X0, #2
0x4C8A3C B.CC loc_4C8A64
0x4C8B1C loc_4C8B1C
0x4C8B1C MOV X0, X21
0x4C8B20 BL _objc_release
0x4C8B24 MOV X0, X21
0x4C8B28 MOV X2, #0
0x4C8B2C BL _objc_msgSend$objectAtIndexedSubscript_
0x4C8B30 MOV X29, X29
0x4C8B34 BL _objc_retainAutoreleasedReturnValue
0x4C8B38 MOV X21, X0 ; 跳转目标位置
0x4C8B3C ADRP X8, #_kSecValueData_ptr@PAGE
0x4C8B40 LDR X8, [X8,#_kSecValueData_ptr@PAGEOFF]
0x4C8B44 LDR X2, [X8]
0x4C8B48 B loc_4C8BA8
0x4C8BA8 loc_4C8BA8
0x4C8BA8 BL _objc_msgSend$objectForKeyedSubscript_
0x4C8BAC MOV X29, X29
0x4C8BB0 BL _objc_retainAutoreleasedReturnValue
0x4C8BB4 MOV X22, X0
0x4C8BB8 MOV X0, X21
0x4C8BBC BL _objc_release ; 屏蔽这条指令,避免重复调用_objc_release
0x4C8BC0 B loc_4C8C04
Firebase SDK新版本(Swift):
func getItemLegacy(query: [String: Any]) throws -> Data? {
var returningQuery = query
returningQuery[kSecReturnData as String] = true
returningQuery[kSecReturnAttributes as String] = true
returningQuery[kSecMatchLimit as String] = 2
var result: AnyObject?
let status = keychainStorage.get(query: returningQuery, result: &result)
if let items = result as? [[String: Any]], status == noErr {
/* --------- A --------- */
if items.isEmpty {
// throw error
} else if items.count > 1 {
// log("Keychain query returned multiple results")
}
let item = items[0];
/* --------- B --------- */
return item[kSecValueData as String] as? Data
}
}
情况一:存在FirebaseAuth.framework
0x36A94 ADRL X0, _$sSaySDySSypGGMd ; 修改这两行,将[[String: Any]]类型改为[String: Any]类型
0x36A9C ADRL X1, _$sSaySDySSypGGMR ; 在AuthErrorUtils.extractJSONObjectFromString()中能找到[String: Any]的type_metadata
0x36AA4 BL ___swift_instantiateConcreteTypeFromMangledNameV2
0x36AA8 MOV X3, X0
0x36AAC ADRP X8, #_$syXlN_ptr@PAGE
0x36AB0 LDR X8, [X8,#_$syXlN_ptr@PAGEOFF]
0x36AB4 ADD X0, SP, #0xB0+var_90
0x36AB8 ADD X1, SP, #0xB0+var_70
0x36ABC ADD X2, X8, #8
0x36AC0 MOV W4, #6
0x36AC4 BL _swift_dynamicCast
0x36AC8 TBZ W0, #0, loc_36B08
0x36ACC LDR X23, [SP,#0xB0+var_90]
0x36AD0 BL _$s6Darwin5noErrs5Int32Vvg ; noErr.getter
0x36AD4 CMP W20, W0
0x36AD8 B.NE loc_36B00
0x36ADC LDR X8, [X23,#0x10]
0x36AE0 STP X26, X8, [SP,#0xB0+var_A0]
0x36AE4 CMP X8, #1 ; 改为MOV X20, X23
0x36AE8 B.EQ loc_36CB0 ; 跳转至0x36D90
0x36D70 loc_36D70
0x36D70 LDR X8, [X23,#0x10]
0x36D74 LDR X19, [SP,#0xB0+var_A8]
0x36D78 CBZ X8, loc_36F14
0x36D7C LDR X20, [X23,#0x20]
0x36D80 MOV X0, X20
0x36D84 BL _swift_bridgeObjectRetain
0x36D88 MOV X0, X23
0x36D8C BL _swift_bridgeObjectRelease
0x36D90 ADRP X8, #_kSecValueData_ptr@PAGE ; 跳转目标位置
0x36D94 LDR X8, [X8,#_kSecValueData_ptr@PAGEOFF]
0x36D98 LDR X0, [X8]
0x36D9C BL _$sSS10FoundationE36_unconditionallyBridgeFromObjectiveCySSSo8NSStringCSgFZ
情况二:不存在FirebaseAuth.framework,和其他代码编译在一起了
0x6D96C ADRL X0, unk_BD971F8 ; 修改此行,将[[String: Any]]类型改为[String: Any]类型
0x6D974 BL sub_35660 ; 搜索字符串Error while converting assumed plist to dictionary,往前找dynamicCast,可找到[String: Any]的type_metadata
0x6D978 MOV X3, X0
0x6D97C ADRP X8, #_$syXlN_ptr@PAGE
0x6D980 LDR X8, [X8,#_$syXlN_ptr@PAGEOFF]
0x6D984 ADD X2, X8, #8
0x6D988 ADD X0, SP, #0xB0+var_A0
0x6D98C ADD X1, SP, #0xB0+var_80
0x6D990 MOV W4, #6
0x6D994 BL j__swift_dynamicCast
0x6D998 TBZ W0, #0, loc_6D9D0
0x6D99C LDR X22, [SP,#0xB0+var_A0]
0x6D9A0 BL j__$s6Darwin5noErrs5Int32Vvg ; noErr.getter
0x6D9A4 CMP W20, W0
0x6D9A8 B.NE loc_6D9C8
0x6D9AC LDR X28, [X22,#0x10]
0x6D9B0 CMP X28, #1 ; 改为STR X27, [SP](暂存Exception信息)
0x6D9B4 B.EQ loc_6DB88 ; 改为MOV X20, X22
0x6D9B8 CBNZ X28, loc_6DA70 ; 跳转至0x6DD04
0x6DCE4 loc_6DCE4
0x6DCE4 MOV X0, X22
0x6DCE8 BL j__swift_bridgeObjectRelease
0x6DCEC ADRL X1, unk_BD96718
0x6DCF4 ADD X0, SP, #0xB0+var_80
0x6DCF8 BL sub_35728
0x6DCFC MOV X0, X22
0x6DD00 BL j__swift_bridgeObjectRelease
0x6DD04 ADRP X8, #_kSecValueData_ptr@PAGE ; 跳转目标位置
0x6DD08 LDR X8, [X8,#_kSecValueData_ptr@PAGEOFF]
0x6DD0C LDR X0, [X8]
0x6DD10 BL j__$sSS10FoundationE36_unconditionallyBridgeFromObjectiveCySSSo8NSStringCSgFZ
ISSUE-006
虚幻引擎打不了字
常见表现
启用按键映射的情况下,打不了字。
相关源码
虚幻引擎 IOSView.h
@interface FIOSView : UIView <UIKeyInput, UITextInput>
{
//...
}
PlayTools ControlMode.swift
if PlaySettings.shared.noKMOnInput {
NotificationCenter.default.addObserver(forName: UITextField.textDidEndEditingNotification, object: nil, queue: .main) { _ in
ModeAutomaton.onUITextInputEndEdit()
}
NotificationCenter.default.addObserver(forName: UITextField.textDidBeginEditingNotification, object: nil, queue: .main) { _ in
ModeAutomaton.onUITextInputBeginEdit()
}
}
原因分析
目前PlayCover的智能按键映射只会监听UITextField和UITextView的开始输入、停止输入事件。
但虚幻引擎启用Use Integrated Keyboard后,文本输入将不会调用UITextField,而是直接通过UIKeyInput接口实现,也就不会产生UITextField的相关事件广播。
解决方法
临时解决方法:
右键游戏图标 - 设置 - 键盘映射设置,禁用“按键映射布局” 或 禁用“智能按键映射”。
临时修复代码:
PlayTools NSObject+Swizzle.m
手动发送相关事件广播。
- (BOOL)hook_FIOSView_becomeFirstResponder {
BOOL ret = [self hook_FIOSView_becomeFirstResponder];
[[NSNotificationCenter defaultCenter] postNotificationName:UITextFieldTextDidBeginEditingNotification
object:nil];
return ret;
}
- (BOOL)hook_FIOSView_resignFirstResponder {
BOOL ret = [self hook_FIOSView_resignFirstResponder];
[[NSNotificationCenter defaultCenter] postNotificationName:UITextFieldTextDidEndEditingNotification
object:nil];
return ret;
}
[objc_getClass("FIOSView") swizzleInstanceMethod:@selector(becomeFirstResponder) withMethod:@selector(hook_FIOSView_becomeFirstResponder)];
[objc_getClass("FIOSView") swizzleInstanceMethod:@selector(resignFirstResponder) withMethod:@selector(hook_FIOSView_resignFirstResponder)];
ISSUE-007
摇杆映射问题
PlayCover默认支持固定摇杆模式,请在游戏设置中查找是否有“固定摇杆”的选项。
或者调整为跟随式摇杆模式:
- PlayCover 稳定版:在编辑模式中选中WASD控件,放大至覆盖半个窗口。
- PlayCover Nightly版:在编辑模式中选中WASD控件,更改为Floating Joystick模式。
ISSUE-008
Unity默认键位卡手问题
常见表现
游戏自带键位支持但存在一些问题,例如:WASD走不动路、按键无法长按、按键无法连点。
相关源码
Unity引擎 UnityView+Keyboard.mm
static const double kKeyTimeoutInSeconds = 0.5;
- (void)processKeyboard
{
KeyMap& map = GetKeyMap();
if (map.size() == 0)
return;
std::vector<int> keysToUnpress;
double nowTime = GetTimeInSeconds();
for (KeyMap::iterator item = map.begin();
item != map.end();
item++)
{
// Key has expired, register it for key up event
if (nowTime - item->second > kKeyTimeoutInSeconds)
keysToUnpress.push_back(item->first);
}
for (std::vector<int>::iterator item = keysToUnpress.begin();
item != keysToUnpress.end();
item++)
{
map.erase(*item);
UnitySetKeyboardKeyState(*item, false);
}
}
原因分析
在Unity引擎中可以通过两种方式去捕获键盘输入,一种是使用旧的API,即Input.GetKeyDown(),另一种是使用最新的Input System Package方式。如果游戏选择使用旧的API,就会出现键位卡手的问题。
iOS系统从14.0开始才有提供访问键盘设备的API,即GCKeyboard。而在此之前Unity就已经写好了UnityView+Keyboard.mm,只不过Unity是通过UIKeyCommand取巧的方式来实现的。
UIKeyCommand只支持监听按键按下事件,并不支持按键抬起事件。Unity则通过“0.5秒后自动松开”来模拟按键抬起,也就造成了游戏过程中按键卡手的情况。
解决方法
玩家方面:通过PlayCover的按键映射去覆盖默认键位。
开发者方面:使用GCKeyboard API重写UnityView+Keyboard.mm。
有一份现成的版本:gist,但这个版本不支持设备断开重连,通过GCKeyboardDidConnectNotification和GCKeyboardDidDisconnectNotification可以监听到设备断开重连。
ISSUE-009
虚幻引擎缺少Entitlement
常见表现
启动闪退,无崩溃报告。
通过终端运行游戏,日志停留在Mobile Provision not found。
相关源码
虚幻引擎 IOSPlatformMisc.cpp
void FIOSPlatformMisc::PlatformInit()
{
//...
// Check for required entitlements
TArray<FString> RequiredEntitlements;
GConfig->GetArray(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("RequiredEntitlements"), RequiredEntitlements, GEngineIni);
for (const FString& Entitlement : RequiredEntitlements)
{
if (!FIOSPlatformMisc::IsEntitlementEnabled(TCHAR_TO_ANSI(*Entitlement)))
{
UE_LOG(LogInit, Fatal, TEXT("App does not have required entitlement %s."), *Entitlement);
}
}
//...
}
原因分析
虚幻引擎会检查是否有对应的Entitlement,如果没有的话,会触发UE_LOG(Fatal)直接闪退。
解决方法
虚幻引擎会先从可执行文件中读取Entitlement,然后尝试读取应用本体内的embedded.mobileprovision文件,所以伪造一个embedded.mobileprovision文件即可:
- 打开第一个目录:右键游戏图标,选择“在访达中查看”,返回上一级目录,进入Entitlements。
- 打开第二个目录:右键游戏图标,选择“在访达中查看”,右键选择“显示包内容”。
- 将第一个目录中对应游戏的
plist文件拷贝至第二个目录,重命名为embedded.mobileprovision。
ISSUE-010
Process Forked报错
常见表现
游戏运行时不停弹出崩溃报告,显示“multi-threaded process forked”。
原因分析
游戏在检测越狱环境。
解决方法
方法一:使用PlayCover Nightly版。
方法二:右键游戏图标 - 设置 - 绕过设置,启用“绕过越狱检测”选项。
ISSUE-011
查询可用运行内存的API返回错误结果
常见表现
游戏提示“设备性能不达标”。
游戏采用最保守的内存管理策略,导致渲染距离极低、材质质量极低。
原因分析
PlayCover中的应用都是以Mac Catalyst模式运行的。
iOS上有一个用于查询可用运行内存的函数os_proc_available_memory,此函数在Mac Catalyst环境下总是返回 0 。
解决方法
固定返回4个G的可用运行内存。
BUNDLE_ID=com.companyname.appname
BUNDLE_PATH=~/Library/Containers/io.playcover.PlayCover/Applications/$BUNDLE_ID.app
EXECUTABLE=$BUNDLE_PATH/$(/usr/libexec/PlistBuddy -c "Print :CFBundleExecutable" $BUNDLE_PATH/Info.plist)
FUNC_ADDR=$(otool -Iv "$EXECUTABLE" | grep _os_proc_available_memory | head -n1 | awk '{print $1}')
printf '\x20\x00\xC0\xD2\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR-0x100000000)) conv=notrunc
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
Tip
将指令中第一个字节
\x20改为\x40,即可变为8个G。
ISSUE-012
我不好说
ISSUE-013
Unity游戏屏幕方向错误
常见表现
屏幕方向错误。
相关源码
Unity引擎 UnityAppController+ViewHandling.mm
- (UIViewController*)createRootViewController
{
UIViewController* ret = nil;
if (!UNITY_SUPPORT_ROTATION || UnityShouldAutorotate())
ret = [self createUnityViewControllerDefault];
if (ret == nil)
ret = [self createRootViewControllerForOrientation: ConvertToIosScreenOrientation((ScreenOrientation)UnityRequestedScreenOrientation())];
return ret;
}
原因分析
原因不明。
解决方法
方法一:尝试通过command+R旋转屏幕方向。
方法二:写入补丁,屏蔽源码中对createUnityViewControllerDefault的调用,createRootViewControllerForOrientation的传入参数修改为UIInterfaceOrientationLandscapeLeft (4)。
ISSUE-014
上层View导致按键映射失灵
常见表现
同时按下WASD和鼠标后(触发多点触控),按键映射失灵。
相关源码
UIKitCore.framework
void -[UIResponder touchesBegan:withEvent:] {
forwardTouchMethod(self, SEL, touches, event, 0LL);
}
void forwardTouchMethod(UIResponder *responder, SEL selector, NSSet *touches, UIEvent *event, int flag) {
UIResponder *nextResponder = responder.nextResponder;
NSMutableSet *newTouches = [[NSMutableSet alloc] init];
for (UITouch *touch in touches) {
if (UIResponderForwarderWantsForwardingFromResponder(touch, selector, responder, nextResponder, event)) {
[newTouches addObject: touch];
}
}
if (newTouches.count > 0) {
[nextResponder performSelector:selector with: newTouches, event];
}
}
BOOL UIResponderForwarderWantsForwardingFromResponder(UITouch *touch, SEL selector, UIResponder *curResponder, UIResponder *nextResponder, UITouchesEvent *event) {
// ...
NSSet *touches = [event touchesForWindow:touch.window];
if (touches.count > 1) {
return NO;
}
// ...
}
PlayTools PTFakeMetaTouch.m
void eventSendCallback() {
UIEvent *event = [[UIApplication sharedApplication] _touchesEvent];
[event _clearTouches];
[livingTouchAry enumerateObjectsUsingBlock:^(UITouch *aTouch, NSUInteger idx, BOOL *stop) {
[event _addTouch:aTouch forDelayedDelivery:NO];
}];
[[UIApplication sharedApplication] sendEvent:event];
}
原因分析
Root View(UnityView)的上层存在另一个View(ARCoachingOverlayView),同时上层View的isUserInteractionEnabled属性为true,但没有重写touchesBegan:withEvent:。
PlayCover的按键映射代码,将多个UITouch放入一个UIEvent中,然后通过[UIApplication sendEvent:]发送事件。
首先上层View(ARCoachingOverlayView)接收到此事件,但因为其本身没有重写touchesBegan:withEvent:,所以交由基类UIResponder的touchesBegan:withEvent:来处理。
正常来说,UIResponder会将事件转发给下一个UIResponder(即UnityView)。
但UIResponderForwarderWantsForwardingFromResponder函数发现UIEvent包含多个UITouch,拒绝转发。
解决方法
方法一:阻止创建上层View。
方法二:将上层View的isUserInteractionEnabled属性改为false。
方法三(临时):修改PlayCover代码,确保UIEvent只包含一个UITouch。
PlayTools PTFakeMetaTouch.m
void eventSendCallback() {
UIEvent *event = [[UIApplication sharedApplication] _touchesEvent];
[livingTouchAry enumerateObjectsUsingBlock:^(UITouch *aTouch, NSUInteger idx, BOOL *stop) {
[event _clearTouches];
[event _addTouch:aTouch forDelayedDelivery:NO];
[[UIApplication sharedApplication] sendEvent:event];
}];
}
ISSUE-015
Game Center登录界面不显示
常见表现
游戏加载时卡住,游戏登录过程卡住。
原因分析
游戏与Game Center登录强绑定,需要等Game Center登录API -[GKLocalPlayer setAuthenticateHandler:]返回结果后才会继续下一步。
但由于签名限制,PlayCover中的应用是无法拉起Game Center登录界面的,Game Center登录API也不会有任何反应,甚至不会通过回调函数返回错误信息。
解决方法
方法一:禁用SIP并修改boot-args,可正常显示Game Center登录界面。
方法二:伪造一个用户已取消操作的Game Center登录结果。
PlayTools NSObject+Swizzle.m
#import <GameKit/GameKit.h>
- (void) hook_setAuthenticateHandler:(void (^)(UIViewController *, NSError *))handler {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
NSError *error = [NSError errorWithDomain:GKErrorDomain
code:GKErrorCancelled
userInfo:@{
NSLocalizedDescriptionKey: @"The requested operation has been cancelled or disabled by the user."
}];
if (handler != nil) {
handler(nil, error);
}
});
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[objc_getClass("GKLocalPlayer") swizzleInstanceMethod:@selector(setAuthenticateHandler:) withMethod:@selector(hook_setAuthenticateHandler:)];
});
ISSUE-016
腾讯游戏麦克风权限问题
常见表现
已授予麦克风权限,但游戏依旧提示没有权限。
相关源码
libGCloudVoice.a CProcessingGraph.o
bool ApolloTVE::CProcessingGraph::IsMicEnable() {
bool bRet;
if (this->m_bM1Process) { // Mac
int64_t TimeMs = GetTimeMs();
bool bWait = true;
while (bWait) {
if (GetTimeMs() - TimeMs > 100) {
bWait = false;
}
[[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
bRet = granted;
bWait = false;
}];
}
}
else {
[[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
bRet = granted;
}];
}
return bRet;
}
原因分析
语音SDK在查询麦克风权限时,期望游戏处于暂停状态,并在暂停状态结束后立即获得查询结果。
但是通过PlayCover运行的游戏,查询麦克风权限时并不会被暂停,而是处于完全异步的状态。
解决方法
使用PlayCover Nightly版,右键游戏图标 - 设置 - 绕过设置,启用“checkMicPermissionSync”选项。
ISSUE-017
WebView界面打不了字
常见表现
启用按键映射的情况下,打不了字。
原因分析
与ISSUE-006类似,只不过出问题的地方变成WebKit的WKContentView了。
解决方法
临时解决方法:
右键游戏图标 - 设置 - 键盘映射设置,禁用“按键映射布局” 或 禁用“智能按键映射”。
临时修复代码:
PlayTools NSObject+Swizzle.m
手动发送相关事件广播。
- (BOOL)hook_WKContentView_becomeFirstResponder {
BOOL ret = [self hook_WKContentView_becomeFirstResponder];
[[NSNotificationCenter defaultCenter] postNotificationName:UITextFieldTextDidBeginEditingNotification
object:nil];
return ret;
}
- (BOOL)hook_WKContentView_resignFirstResponder {
BOOL ret = [self hook_WKContentView_resignFirstResponder];
[[NSNotificationCenter defaultCenter] postNotificationName:UITextFieldTextDidEndEditingNotification
object:nil];
return ret;
}
[objc_getClass("WKContentView") swizzleInstanceMethod:@selector(becomeFirstResponder) withMethod:@selector(hook_WKContentView_becomeFirstResponder)];
[objc_getClass("WKContentView") swizzleInstanceMethod:@selector(resignFirstResponder) withMethod:@selector(hook_WKContentView_resignFirstResponder)];
ISSUE-018
NeoX引擎文件路径错误
常见表现
启动闪退,无崩溃报告。
通过终端运行游戏,日志显示“neox.xml not exist!”。
原因分析
NeoX引擎给所有文件路径都加上了前缀/private,导致找不到文件。
解决方法
修改常量字符串/private为/
BUNDLE_ID=com.companyname.appname
BUNDLE_PATH=~/Library/Containers/io.playcover.PlayCover/Applications/$BUNDLE_ID.app
EXECUTABLE=$BUNDLE_PATH/$(/usr/libexec/PlistBuddy -c "Print :CFBundleExecutable" $BUNDLE_PATH/Info.plist)
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\x00\x2F\x70\x72\x69\x76\x61\x74\x65\x00"); exit 1 if $i < 0; seek F, $i, 0; print F "\x00\x2F\x00\x00\x00\x00\x00\x00\x00\x00"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements
ISSUE-019
UI界面屏幕方向错误
常见表现
屏幕方向错误。
原因分析
有两种情况:
- 该界面本身只支持竖屏。
- 该界面支持横屏,但是
supportedInterfaceOrientations第一次返回的值仅包含竖屏。
Mac上运行的iOS应用受限于supportedInterfaceOrientations第一次返回的值,如果第一次返回的值里面不包含横屏,就永远无法切换成横屏显示,
解决方法
找出出问题的UIViewController,强制supportedInterfaceOrientations返回UIInterfaceOrientationMaskLandscape (24)。
ISSUE-020
我不好说
ISSUE-021
QTWebEngine界面加载问题
常见表现
登录界面报错,登录界面加载不出来。
游戏目录中存在Qt5WebEngineCore.dll文件。
原因分析
原因不明,与Chromimum内核相关。
解决方法
在Wine容器中执行命令,添加环境变量QT_QUICK_BACKEND=software:
reg add "HKCU\Environment" /v QT_QUICK_BACKEND /t REG_SZ /d "software" /f
ISSUE-022
App跟踪询问窗口不显示
常见表现
加载卡住,登录卡住。
游戏提示接下来会弹出App跟踪询问窗口,但没有出现任何界面。
相关源码
PlayTools PlayShadow.m
+ (void) load {
//...
[objc_getClass("ATTrackingManager") swizzleClassMethod:@selector(requestTrackingAuthorizationWithCompletionHandler:) withMethod:@selector(pm_return_2_with_completion_handler:)];
[objc_getClass("ATTrackingManager") swizzleClassMethod:@selector(trackingAuthorizationStatus) withMethod:@selector(pm_return_2)];
//...
}
原因分析
游戏请求弹出App跟踪询问窗口,等待窗口关闭后才会继续下一步加载或登录操作。但是在Mac上运行的iOS应用是不会弹出这个窗口的,所以应用会一直等待结果,故而卡住了。
如上面的源码所示,其实PlayCover早已对这个问题做了处理。但是在运行某些游戏时,由于AppTrackingTransparency.framework加载得太慢,源码中的objc_getClass("ATTrackingManager")为nil,导致这个处理被跳过了。
解决方法
方法一:修改PlayCover代码,提前加载AppTrackingTransparency.framework。
方法二:写入补丁,让游戏跳过App跟踪这个步骤。
ISSUE-023
CRIWARE多线程死锁问题
常见表现
加载过程中卡死,程序转圈无响应。
原因分析
CRIWARE启用SonicSYNC后出问题。
解决方法
写入补丁,初始化CRIWARE时不要启用SonicSYNC。
案例一(CRIWARE C/C++ Native版本):
查找字符串The linked library version is more recent than CRI Atom Ex header version,所在函数即为criAtomEx_global.o的criAtomEx_CalculateWorkSizeInternal函数,其中thread_model控制是否启用SonicSYNC。
typedef struct CriAtomExConfigTag {
int thread_model;
// ...
} CriAtomExConfig;
typedef struct CriAtomExConfigTag_IOS {
CriAtomExConfig atom_ex;
CriAtomExAsrConfig asr;
CriAtomExHcaMxConfig hca_mx;
// ...
} CriAtomExConfig_IOS;
int criAtomEx_CalculateWorkSizeInternal(CriAtomExConfig_IOS *config, void *a2) {
if (config) {
// 必定进入此分支
// ...
} else {
// ...
}
}
把 if 判断的CBZ指令改成STR WZR, [X0],即可将thread_model改为0,从而不启用SonicSYNC。
案例二(CRIWARE Unity版本):
TODO
ISSUE-024
Unity KeyboardDelegate重复初始化
常见表现
启动闪退。
通过终端运行游戏,日志显示“[KeyboardDelegate Initialize] called after creating keyboard”。
相关源码
Unity引擎 Keyboard.mm
@implementation KeyboardDelegate
+ (void)Initialize
{
NSAssert(_keyboard == nil, @"[KeyboardDelegate Initialize] called after creating keyboard");
if (!_keyboard)
_keyboard = [[KeyboardDelegate alloc] init];
}
+ (KeyboardDelegate*)Instance
{
if (!_keyboard)
_keyboard = [[KeyboardDelegate alloc] init];
return _keyboard;
}
@end
原因分析
游戏重复初始化KeyboardDelegate,触发断言失败。
解决方法
这个断言失败并不影响游戏运行,直接屏蔽+[KeyboardDelegate Initialize]即可。
BUNDLE_ID=com.companyname.appname
EXECUTABLE=~/Library/Containers/io.playcover.PlayCover/Applications/$BUNDLE_ID.app/Frameworks/UnityFramework.framework/UnityFramework
FUNC_ADDR=$(otool -oV $EXECUTABLE | awk '/KeyboardDelegate/{found=1} found && /Initialize/{f=1} f && /imp/{print $2; exit}')
printf '\xC0\x03\x5F\xD6' | dd of=$EXECUTABLE bs=1 seek=$(($FUNC_ADDR)) conv=notrunc
codesign -fs- $EXECUTABLE