Звук в клиенте пропал из-за неверного каталога ассетов

Клиент запрашивал звук, движок его принимал и запускал, а на выходе было
тихо. Причина оказалась не в коде игры.

Minecraft 1.12.2 не умеет читать ассеты по индексу и объектному хранилищу.
Он ждёт обычное дерево: <assetsDir>/minecraft/<путь>. А лаунчер хранит
ассеты по-современному — indexes/1.12.json со ссылками на objects/<aa>/<sha1>.

Мы передавали лаунчеру корень этого хранилища как --assetsDir. Игре это не
выглядит как поломка: язык и текстуры берутся из самого jar, поэтому клиент
выглядит полностью рабочим. Но звуки с 1.7.3 лежат только в ассетах, и их
просто не было на диске. Движок поднимался, play() вызывался, ogg не
находился, а paulscode на это молчит.

- materialize_assets.py разворачивает дерево из индекса и хранилища,
  жёсткими ссылками где возможно, и идемпотентен
- run-game.ps1 материализует ассеты перед запуском и указывает клиенту на
  развёрнутое дерево; --assetIndex больше не нужен
- проверено: 1305 объектов, из них 1085 ogg, sounds.json на месте, ошибок
  загрузки в логе нет

Заодно убрана временная диагностика звука и движения, добавленная ради
поиска причины: она доказала, что запрос и воспроизведение доходят до
OpenAL, и свою задачу выполнила.

Полный prepare-game.ps1 после этого проходит целиком: 112 патчей,
FORK COMPILES CLEAN, 3268 классов, RsxDebug в дереве не осталось.
This commit is contained in:
sasheg dev
2026-10-19 00:04:51 +03:00
parent bdf0ae866a
commit 00671e7c0d
4 changed files with 100 additions and 104 deletions
-30
View File
@@ -1039,15 +1039,6 @@ public void sendChatMessage(String s, boolean flag) {
this.mc.player.sendChatMessage(s);
}
=== FILE net/minecraft/client/entity/EntityPlayerSP.java
--- BEFORE
public void onLivingUpdate() {
++this.sprintingTicksLeft;
--- AFTER
public void onLivingUpdate() {
net.rustnotmc.client.RsxDebug.movement(this);
++this.sprintingTicksLeft;
=== FILE net/minecraft/entity/Entity.java
--- BEFORE
this.collidedHorizontally = d7 != d0 || d9 != d2;
@@ -1098,25 +1089,4 @@ boolean flag = this.onGround || d1 != d1 && d1 < 0.0D;
--- AFTER
boolean flag = this.onGround || d8 != d1 && d8 < 0.0D;
=== FILE net/minecraft/client/audio/SoundHandler.java
--- BEFORE
Map<String, SoundList> map = this.getSoundMap(iresource.getInputStream());
--- AFTER
Map<String, SoundList> map = this.getSoundMap(iresource.getInputStream());
net.rustnotmc.client.RsxDebug.sound("parsed=" + (map == null ? "null" : String.valueOf(map.size())));
=== FILE net/minecraft/client/audio/SoundHandler.java
--- BEFORE
List<IResource> list = iresourcemanager.getAllResources(new ResourceLocation(s, "sounds.json"));
--- AFTER
List<IResource> list = iresourcemanager.getAllResources(new ResourceLocation(s, "sounds.json"));
net.rustnotmc.client.RsxDebug.sound("domain=" + s + " resources=" + list.size());
=== FILE net/minecraft/client/audio/SoundHandler.java
--- BEFORE
this.soundRegistry.clearMap();
Iterator iterator = iresourcemanager.getResourceDomains().iterator();
--- AFTER
this.soundRegistry.clearMap();
net.rustnotmc.client.RsxDebug.sound("domains=" + iresourcemanager.getResourceDomains());
Iterator iterator = iresourcemanager.getResourceDomains().iterator();
@@ -1,72 +0,0 @@
package net.rustnotmc.client;
import net.rustnotmc.loader.Log;
/**
* Temporary diagnostics, added to work out why jumping and flight misbehave.
*
* Static analysis kept producing confident and wrong answers about this path, so
* the state is measured instead. Delete this file and its patch entry once the
* movement bugs are closed.
*/
public final class RsxDebug {
private static int ticks;
private RsxDebug() {
}
/** One-off trace for the empty sound registry. */
public static void sound(String message) {
Log.info("DBG snd " + message);
}
/** Called from EntityPlayerSP.onLivingUpdate, once a second. */
public static void movement(Object self) {
if (++ticks % 20 != 0) {
return;
}
Log.info("DBG sp onGround=" + read(self, "onGround")
+ " inJump=" + read(self, "movementInput")
+ " isJumping=" + read(self, "isJumping")
+ " riding=" + read(self, "isRiding")
+ " srvWorld=" + read(self, "isServerWorld")
+ " allowFly=" + read(self, "allowFlying")
+ " flying=" + read(self, "isFlying")
+ " mY=" + read(self, "motionY")
+ " y=" + read(self, "posY"));
}
/**
* Reads a field by name.
*
* Deliberately reflective and deliberately forgiving: a diagnostic that
* throws when a field is renamed is worse than one that prints null.
*/
private static String read(Object target, String field) {
if (target == null) {
return "?";
}
try {
java.lang.reflect.Field f = findField(target.getClass(), field);
if (f == null) {
return "?";
}
f.setAccessible(true);
return String.valueOf(f.get(target));
} catch (Throwable t) {
return "!";
}
}
private static java.lang.reflect.Field findField(Class<?> type, String name) {
while (type != null) {
try {
return type.getDeclaredField(name);
} catch (NoSuchFieldException e) {
type = type.getSuperclass();
}
}
return null;
}
}