Files
RustNotMC/rsx-client/tools/find_callers.py
T
sasheg dev bdf0ae866a Форк клиента собирается из исходников, модзагрузчик встроен в игру
Проект начинается с двух вещей: клиент, который мы действительно собрали, и
загрузчик, который является его частью, а не чем-то рядом.

Форк
- prepare-game.ps1 пересобирает клиент целиком: ванильный jar, SRG и MCP,
  SpecialSource, FernFlower, патчи, автофиксеры, javac. Идемпотентен.
- Свой код игры лежит в src-overlay, потому что src пересоздаётся при каждой
  сборке и всё написанное вручную там погибает.
- 116 патчей в patches/decompiler-fixes.txt. Каждый сверялся с оригинальным
  байткодом через javap: компиляция не ловит ошибки декомпиляции.
- struct_diff.py сравнивает исходный байткод с нашей сборкой по классам,
  интерфейсам, суперклассам, методам и полям, и отдельно проверяет, что
  пропавшие классы действительно никому не нужны.

Загрузчик
- Собирается и тестируется без игры: build.ps1 не использует classpath
  вообще, поэтому SelfTest из 128 проверок проходит без запущенного форка.
- Клиентский API не содержит ни одного игрового типа, реализацию даёт игра.
- Политика: мод меняет то, что клиент показывает, но никогда то, что истинно
  в мире. Мир, предметы, игроки и сеть принадлежат серверу.
- Capability приведены в соответствие с этой политикой: осталось шесть, и
  за каждым стоит реальная проверка. Прежние WORLD, PLAYER, NETWORK, TEXT,
  INPUT и COMMAND выдавались, но ничего не проверяли.
- Команды модов имеют область CLIENT, SERVER или BOTH. Серверные не
  выполняются на клиенте никогда.
- Свой jar загрузчика лежит в classpath игры и сборки форка.

Инструменты
- add_patch.py, чтобы каждое исправление переживало передекомпиляцию
- find_self_comparisons.py, find_remove_index_bugs.py, find_callers.py
- stage_run.py и fetch_missing.py для запуска клиента
2026-10-18 21:31:27 +03:00

57 lines
1.6 KiB
Python

"""Find every class whose constant pool references a named method.
Used to answer "who actually calls this" across a whole jar, where grepping
source would only show the decompiled view and javap would need one command per
class.
python find_callers.py <classes-dir> <method-name>
"""
import os
import struct
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from struct_diff import Reader, read_constant_pool, CONSTANT_UTF8, SKIP_TWO_SLOT
def method_names(path):
with open(path, 'rb') as handle:
data = handle.read()
reader = Reader(data)
if reader.u4() != 0xCAFEBABE:
return None
reader.u2()
reader.u2()
pool = read_constant_pool(reader)
names = []
for index, entry in enumerate(pool):
if isinstance(entry, str):
names.append(entry)
return names
def main():
root, needle = sys.argv[1], sys.argv[2]
hits = []
for base, _dirs, files in os.walk(root):
for name in files:
if not name.endswith('.class'):
continue
full = os.path.join(base, name)
key = os.path.relpath(full, root).replace(os.sep, '/')
try:
names = method_names(full)
except Exception:
continue
if names is None:
continue
if any(n == needle or n.startswith(needle) for n in names):
hits.append(key)
print('%d class(es) reference %s' % (len(hits), needle))
for key in hits:
print(' %s' % key)
if __name__ == '__main__':
main()