Comments (18)
我和 @NekoCaffeine 进行了讨论,找到了不兼容1.7.10的原因:
net.minecraft.util.Session
负责创建GameProfile
对象,每次进服(包括本地游戏)时,它就创建一个。其创建的GameProfile
不包含properties。- 创建的
GameProfile
被放入C00PacketLoginStart
中,不经过序列化直接被传递到本地服务器。 - 本地服务器取出
C00PacketLoginStart
中的GameProfile
对象。因为进入本地游戏时不经过KEY
阶段,直接跳到READY_TO_ACCEPT
阶段,所以不会去调用hasJoined接口,所以本地服务器使用的GameProfile
对象直接就是一开始Session
所创建的对象。 - 因为
GameProfile
不包含properties,所以发送的S0CPacketSpawnPlayer
中也不包含properties。 SkinManager
中存在逻辑:加载皮肤时,如果properties为空,并且加载的是自己的皮肤,就去查询皮肤。所以Host可以看到自己的皮肤,而Guest不能看到Host皮肤。
解决方法:
- 修改
Session
,对其创建的GameProfile
对象进行标记。 - 修改
S0CPacketSpawnPlayer
,当包被序列化时,如果其中的GameProfile
是被标记过的,就查询properties并加入其中。
from authlib-injector.
Mojang对于这个的解决方案是在服务器没有回发texture时,使用uuid前往mojang获取皮肤
不知道ai准备怎么修复
from authlib-injector.
Jira 上 bug 描述最后一段写的
I assume this could be fixed by having the method
net.minecraft.client.Minecraft.getProfileProperties()
call the methodcom.mojang.authlib.minecraft.MinecraftSessionService.fillProfileProperties(GameProfile, boolean)
withtrue
asrequireSecure
parameter value. There should probably not be any downsides.
不行吗?
from authlib-injector.
getProfileProperties
仅对玩家自己有效,无法用这个方法补全房主皮肤
from authlib-injector.
数字签名我觉得是有必要的,还是不要绕过为好。
我发现在 https://sessionserver.mojang.com/session/minecraft/profile/
里强行返回数字签名就可以解决这个问题,现在我在寻找检测 MC 版本的方法(只对特定版本开启这个 workaround)。
from authlib-injector.
getProfileProperties
仅对玩家自己有效,无法用这个方法补全房主皮肤
我检查了一下 mcp,这个 bug 的原理看起来的确是如 Jira 里描述的那样,是因为单人模式下玩家的 GameProfile
中皮肤信息没有签名,当其他玩家加入游戏时(net.minecraft.server.management.ServerConfigurationManager.playerLoggedIn()
)发送的 Player List Item
包(S38PacketPlayerListItem
)里的数据也就不会有签名,因此不会被客户端正常显示。结论:问题的症结在于单人游戏下获取 GameProfile
的 properties 时没有包括签名。
而 GameProfile
里的皮肤信息确实是通过 YggdrasilMinecraftSessionService.fillProfileProperties(GameProfile profile, boolean requireSecure)
方法获取的,而该方法会请求 https://sessionserver.mojang.com/session/minecraft/profile/
这个 API。结论:当第二个参数传入 true
的时候,API 返回的结果就会带上签名,因此 GameProfile
里的数据也就会有签名。
这个 fillProfileProperties()
方法我唯一搜索到的调用点就是在 SkinManager
里。综上所述,只要把这个方法调用的参数从 false
改为 true
,我实在看不出为什么这样不能解决问题……用 ASM 改个 boolean
参数应该很简单才对(虽然实际上定位到这个方法在哪就是另一说了,考虑到混淆的问题),而且既然唯一的区别就是多了个签名,我也看不出会导致啥副作用。
我第一时间能想到的修改方法是类似于这样子的:
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (opcode == INVOKEINTERFACE &&
"com/mojang/authlib/minecraft/MinecraftSessionService".equals(owner) &&
"fillProfileProperties".equals(name)) {
visitInsn(POP); // 把原来已经在栈上的参数值 false 给 pop 掉
visitInsn(ICONST_1); // 然后替换为 true
}
super.visitMethodInsn(opcode, owner, name, desc, itf); // 再进行方法调用
}
from authlib-injector.
@andylizi 我已经写了一个 workaround,不过我的代码是类似这样的:
diff --git a/src/main/java/moe/yushi/authlibinjector/httpd/QueryProfileFilter.java b/src/main/java/moe/yushi/authlibinjector/httpd/QueryProfileFilter.java
index 34a65e8..de77fa0 100644
--- a/src/main/java/moe/yushi/authlibinjector/httpd/QueryProfileFilter.java
+++ b/src/main/java/moe/yushi/authlibinjector/httpd/QueryProfileFilter.java
@@ -24,6 +24,9 @@ public class QueryProfileFilter implements URLFilter {
private YggdrasilClient mojangClient;
private YggdrasilClient customClient;
+ // see <https://github.com/yushijinhun/authlib-injector/issues/30>
+ private boolean mc52974WorkaroundEnabled;
+
public QueryProfileFilter(YggdrasilClient mojangClient, YggdrasilClient customClient) {
this.mojangClient = mojangClient;
this.customClient = customClient;
@@ -55,6 +58,10 @@ public class QueryProfileFilter implements URLFilter {
withSignature = true;
}
+ if (mc52974WorkaroundEnabled) {
+ withSignature = true;
+ }
+
Optional<GameProfile> response;
if (QueryUUIDsFilter.isMaskedUUID(uuid)) {
response = mojangClient.queryProfile(QueryUUIDsFilter.unmaskUUID(uuid), withSignature);
@@ -73,4 +80,11 @@ public class QueryProfileFilter implements URLFilter {
}
}
+ public boolean isMc52974WorkaroundEnabled() {
+ return mc52974WorkaroundEnabled;
+ }
+
+ public void setMc52974WorkaroundEnabled(boolean mc52974WorkaroundEnabled) {
+ this.mc52974WorkaroundEnabled = mc52974WorkaroundEnabled;
+ }
}
我觉得可能你的方法更好。
from authlib-injector.
@andylizi 我的想法是只对 1.7.9 到 1.12.2 之间的版本开启这个 workaround。
目前我是通过拦截 main 方法中传入的参数来判断当前版本的,因此只有执行了 main 方法才能获取到 MC 版本。而在此之前,要修改的类可能已经加载了,但那时候我无法判定我需不需要 workaround。
所以我觉得可能我的方法更好一些,因为我可以实时控制要不要返回数字签名。
from authlib-injector.
其实我在想,即使 1.13 没有这个问题,这个 workaround 启用了又会怎么样?反正又不会有啥副作用,还能省去检查版本的麻烦。
因为我可以实时控制要不要返回数字签名。
又没有哪种情况是“必须不能有数字签名”的。在任何时候都带上签名又不是啥缺点。
from authlib-injector.
在任何时候都带上签名又不是啥缺点。
我觉得真正的缺点,是程序没有按照约定工作。因此如果没有必要,就不要随意改变 API 的行为。对于 1.13 而言,既然它没有这个问题,就不需要去 workaround 了。如果改了的话,分明调用者没有指定 requireSecure=true
,但 API 却返回了数字签名,这算不算 bug?
from authlib-injector.
@andylizi 我开了一个PR #31 ,望您能 review。
from authlib-injector.
我觉得真正的缺点,是程序没有按照约定工作。因此如果没有必要,就不要随意改变 API 的行为。
好吧,这个的确有道理。
我本来想参考一下 1.13 是怎么修复这个问题的,结果发现 1.13 的 MCP 还没发布……
关于 PR 的话,除了为一个小功能写了一大堆逻辑这一点感觉有点别扭外(毕竟代码越多潜在的 bug 越多),其他就没什么了。
from authlib-injector.
@andylizi 不知您是否熟悉 1.7.10 的局域网进服流程?我发现我这个 workaround 对于 1.7.10 无效,还没能找出原因。
from authlib-injector.
查了一下 MCP 和 1.7 最后一个版本的 Protocol Specification,发现在 1.7 里携带 GameProfile
的数据包不是 Player List Item
而是 Spawn Player
(class S0CPacketSpawnPlayer
) 负责的。这个包不像 Player List Item
一样是在有新玩家加入服务器时发送,而是在玩家实体进入客户端可视范围时发送的。
S0CPacketSpawnPlayer
构造器唯一被调用了的地方是在 net.minecraft.entity.EntityTrackerEntry
类的 func_151260_c()
方法(根据代码来看这方法原名应该是 addPacket
,用途是构造这个 tracker entry 类所负责的实体对应的 spawn 包):
private Packet func_151260_c() {
if (this.myEntity.isDead) {
logger.warn("Fetching addPacket for removed entity");
}
if (this.myEntity instanceof EntityPlayerMP) {
return new S0CPacketSpawnPlayer((EntityPlayer) this.myEntity);
}
// ...
}
构造的数据包将会在同一类下的 tryStartWachingThis()
方法中被发送出去。
所以虽然网络协议这部分跟 1.8 以后有挺大的不同,但关于 GameProfile
的部分还是一样的——在数据包里携带 EntityPlayer.getGameProfile()
所返回的信息。
而我又搜了一下 fillProfileProperties()
方法的调用点,发现跟 1.8 一样仍然只有三处: SkinManager
、领域相关代码,和 TileEntitySkull
(多半是用来显示骷髅头的自定义皮肤)。SkinManager
甚至代码都没怎么变化,因此没理由认为是获取带签名的 properties
这一步出了问题。
from authlib-injector.
至于究竟是什么问题导致的,我现在能想出以下两种假说:
- bug 的产生原理在 1.7.10 跟在 1.8 以上是不一样的,并不是(或者并不仅仅是)因为签名的原因,因此通过加签名的方法无法修复。
- 构造
S0CPacketSpawnPlayer
时使用的GameProfile
(来自EntityPlayer.getGameProfile()
),跟SkinManager
里fillProfileProperties()
里填充的不是同一个,因此在fillProfileProperties()
里填充签名没效果。
其他可能性好像都属于假说 1 的变体,在没有更多信息的情况下还真想不到还能是因为啥了…
排查方法:
- Jira 的 bug 报告不是说客户端会有日志输出
Signature is missing from textures payload
么。这行 log 是在YggdrasilMinecraftSessionService.getTextures()
方法产生的,所以如果真是签名问题的话 1.7.10 也会产生这行日志。那么如果看到了这个输出,就能确定以下两点:- bug 至少有一部分是因为签名导致的。但这并不能完全排除假说1,因为签名可能只是部分原因。
- 服务端发来的
GameProfile
里没签名。大大提升了假说 2 的可能性。
- 通过拦截数据包的方法(无论是在客户端还是服务端),确认
Spawn Player
包有没有签名。至于怎么实现的话,测试用代码自然不需要考虑多版本兼容,因此硬编码进 notch name 都没事。但仔细想想,这种方法能够证实/证伪的可能性,方法 1 好像也能做到…所以好像没必要这么麻烦的样子… - 这个 bug 不是只有房主的皮肤不能显示,而其他人的可以么?也许能通过某种方法对比房主的玩家实体产生的相关数据包(保险起见最好不止
Spawn Player
,谁知道旧版本协议里会不会有啥奇奇怪怪的逻辑我们没有发现),和其他能正常显示皮肤的玩家的相关数据包,看看区别究竟在哪里。这个方法可以确认假说 1 和假说 2。但实现起来恐怕会很麻烦。
from authlib-injector.
我昨天在测试的时候没有看到 Signature is missing from textures payload
这条信息(已 workaround)。等我下下周期末考试之后,我弄一个 MCP 调试下吧。
from authlib-injector.
除 1.7.9 及 1.7.10 外,已在 #31 中修复。
from authlib-injector.
我为不兼容 1.7.10 的问题新开了一个 issue #35 。目前不打算解决不兼容 1.7.9 的问题。
from authlib-injector.
Related Issues (20)
- Failed to retrieve profile key pair HOT 1
- 不支持 Java 21? HOT 1
- 1.20.2 无法显示皮肤等等等等 HOT 1
- 提示我SSL证书错误,但是我排查之后没有问题 HOT 2
- 最新的 Minecraft Bedrock 不支持使用 Mojang 账号登录,故无法借助 Geyser 和本项目实现基岩版用户外置登录 HOT 3
- mkdir(): Permission denied HOT 1
- 1.20版本中白名单存在问题 HOT 2
- 在文档中更新每个API在客户端/服务器常见(Vanilla/Nochian)实现中的使用情况
- 如果在velocity-3.3.0-SNAPSHOT-325中使用authlib-injector-1.2.4会出现身份验证服务宕机问题 HOT 2
- Httpd failed to start HOT 11
- 关于api错误返回的信息问题
- 无法使用游戏内聊天:由于个人信息公钥丢失,聊天已被禁用。请尝试重新连接。 HOT 1
- Authlib-injector not having a velocity-plugin.json file HOT 1
- The skin is used as a cape HOT 1
- 1.7.2-1.7.5 cannot display capes
- 玩家尝试进入服务器时报错 HOT 1
- 貌似是因为java信任库的原因,部分验证服务器的ssl证书无法验证,导致站点无法访问
- Minecraft 1.20.5 and 1.20.6 support HOT 2
- using multiple authservers HOT 7
- 启动后,客户端无法加入服务器,已在客户端设置好 littleskin 了。 HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from authlib-injector.