world и client закрывают обычные задачи. Но иногда нужного метода там просто нет, и тогда лезут напрямую в игру через mc. Это отмычка на такие случаи.
mc это сама игра. Через него достаётся всё: игрок, мир, настройки управления, аккаунт, текущий экран.
| Обращение | Что это |
|---|---|
mc.player | Твой игрок. None, если ты не в мире |
mc.world | Текущий мир. None в главном меню |
mc.options | Настройки игры: клавиши, чувствительность мыши, обзор |
mc.currentScreen | Открытый экран: инвентарь, чат, меню. None, если ничего не открыто |
mc.crosshairTarget | Во что сейчас упирается прицел |
mc.getSession().getUsername() | Ник аккаунта |
mc.getCurrentFps() | Текущий FPS |
Заметь разницу: player, world, options читаются без скобок, это поля. А getSession() и getCurrentFps() это методы, тут скобки нужны.
el = Hud("McInfo", width=110, height=17, x=8, y=8)
@el.render
def draw(d):
fps = int(mc.getCurrentFps())
name = str(mc.getSession().getUsername())
d.rounded_rect(0, 0, d.width, d.height, radius=6, color=Color(9, 9, 11, 200))
d.text("%s · %d fps" % (name, fps), 6, 5, size=7, color=Color(207, 215, 251))Иногда нужен не объект из игры, а её класс: собрать коробку для рисования, взять готовую константу. Достают их двумя способами, оба дают одно и то же.
from net.minecraft.util import Hand # как обычный import
from net.minecraft.entity import LivingEntity
Box = jimport("net.minecraft.util.math.Box") # то же самое, но строкойИмена пишутся полностью и по-человечески, так же, как их называют в самой игре. Дальше класс можно вызывать как обычный конструктор и читать у него готовые значения.
Box = jimport("net.minecraft.util.math.Box")
mod = Module("Zone", "Visuals")
mod.setDesc("Рисует квадрат 6x6 вокруг игрока")
@events.render_3d
def draw(event):
if not mod.isEnabled() or not world.ingame():
return
me = world.self()
x, y, z = float(me.getX()), float(me.getY()), float(me.getZ())
# Box берёт две противоположные точки коробки, всего 6 чисел
zone = Box(x - 3.0, y, z - 3.0, x + 3.0, y + 0.1, z + 3.0)
render3d.box(event, zone, Color(120, 200, 255, 160))В собранном клиенте методы игры называются не так, как в исходниках: getX() там зовётся method_23317. Чтобы скрипт писался человеческими именами, клиент подставляет настоящие имена сам. Все объекты, которые выдаёт API, к этой подстановке уже подключены: и то, что вернуло событие, и mc, и world, и inventory.stack(). Готовить их не нужно, просто зови методы.
@events.attack
def on_attack(event):
victim = event.getEntity()
client.msg("Бью %s (%.1f м)" % (
victim.getNameForScoreboard(),
world.self().distanceTo(victim),
))| Откуда объект | Что делать |
|---|---|
| event.getEntity(), event.getPacket() и всё из событий | Ничего, зови методы сразу |
| mc.player, world.players(), world.target(), inventory.stack(), jimport(...) | Ничего, зови методы сразу |
| сам event, client, hud, модули клиента | Ничего, это части клиента |
Три вещи, на которые уходят часы, если про них не знать заранее.
Ошибка «нет метода getX (0 арг.)». Она значит ровно одно: метода с таким именем и таким числом аргументов нет. Пересчитай аргументы, обычно дело именно в этом. Классический случай это пакет движения: у него координаты читают полем, а не методом — packet.x, а не packet.getX().
Имя сущности. Привычное getName().getString() не сработает. Имя берут через getNameForScoreboard().
Проверка типа. Обычный isinstance тут бесполезен, тип проверяют по полному имени класса. Как именно, показано на странице Пакеты.