Гайд Клиент с нуля | Автобус евентов

PoC Life
Пользователь
Статус
Оффлайн
Регистрация
22 Авг 2022
Сообщения
331
Реакции[?]
47
Поинты[?]
37K

Перед прочтением основного контента ниже, пожалуйста, обратите внимание на обновление внутри секции Майна на нашем форуме. У нас появились:

  • бесплатные читы для Майнкрафт — любое использование на свой страх и риск;
  • маркетплейс Майнкрафт — абсолютно любая коммерция, связанная с игрой, за исключением продажи читов (аккаунты, предоставления услуг, поиск кодеров читов и так далее);
  • приватные читы для Minecraft — в этом разделе только платные хаки для игры, покупайте группу "Продавец" и выставляйте на продажу свой софт;
  • обсуждения и гайды — всё тот же раздел с вопросами, но теперь модернизированный: поиск нужных хаков, пати с игроками-читерами и другая полезная информация.

Спасибо!

Список:
1. Создаём главный класс + снимаем ограничения - https://yougame.biz/threads/325113
2. Автобус евентов - https://yougame.biz/threads/325114
3. OpenGL + Шейдеры + Рендер - https://yougame.biz/threads/325115
4. Аим - https://yougame.biz/threads/325188
5. GlowESP aka OpenGL Framebuffer - https://yougame.biz/threads/325211/
6. Текст, шрифты, атлас - https://yougame.biz/threads/325292

Теория
Сейчас расскажу про систему евентов и её полезность. Представим есть функция рендера:
Java:
public void render() {
    // Do render
}
Чтобы не писать такой грязный код:
Java:
public void render() {
    watermark.render();
    staffalert.render();
}
Вы можете просто вызвать евент. Евент - это просто класс, который содержит в себе полезные данные, чтобы вы в дальнейшем использовали их в listener'е. Listener (дословно слушатель) - это класс, который обрабатывает один или несколько евентов. Когда вызывается евент, он собирает все зарегистрированные listener'ы и вызывает обработку евента в них.
Frame 2.png

Практика
Можно приступать к написанию кода.
Сделаем интерфейс listener'а:
interface Listener {}
Автобус евентов прям так и назовём:
class EventBus {}
В нём будет список listener'ов:
Java:
class EventBus {

    // Зарегистрированные listener'ы
    private final Set<Listener> listeners;

    public EventBus() {
        this.listeners = Sets.newLinkedSet();
    }

    /*
        Регистрация listener'а
    */
    public void registerListener(Listener listener) {
        this.listeners.add(listener);
    }
}
| 🧐 Изучаем библиотеки
| Майнкрафт использует библиотеку guava, в которой есть удобные классы для работы с коллекциями: Lists, Sets, Maps

Интерфейс евента:
interface Event {}
А также вызов евента:
Java:
ublic void callEvent(Event event) {
    this.listeners.stream()
        .flatMap(listener -> Stream.of(listener.getClass().getDeclaredMethods()) // Собриаем все методы
            .map(method -> Map.entry(listener, method)) // И преобразуем в Entry<Listener, Method>, т.к. экземпляр listener'а нам понадобится, чтобы вызвать метод
        )
        .filter(entry -> entry.getValue().getParameterCount() == 1) // Проверяем имеет ли метод ровно 1 параметр
        .filter(entry -> entry.getValue().getParameterTypes()[0].equals(event.getClass())) // Проверяем совпадает ли класс данного параметра с нашим евентом
        .forEach(entry -> {
            try {
                entry.getValue().invoke(entry.getKey(), event); // Пытаемся вызвать евент
            } catch (IllegalAccessException | InvocationTargetException exception) {
                exception.printStackTrace(); // В случае ошибки выводим её
            }
        });
}
Не забываем, что существует другой тип евентов, которые можно отменять. Для этого создадим новый класс для отменяемых евентов:
Java:
class CancellableEvent implements Event {

    private boolean cancelled;

    public boolean isCancelled() {
        return this.cancelled;
    }

    public void cancel() {
        this.cancelled = true;
    }
}
Ну теперь всё, для проверки создадим тестовый event, listener и попробуем вызвать:
Java:
public class TestEvent implements Event {}

public class TestListener implements Listener {

    public void onTest(TestEvent event) {
    
    }
}

...
eventBus.registerListener(new TestListener());
eventBus.callEvent(new TestEvent());
Ставим бряк на onTest и проверяем:
Pasted image 20240705004222.png
Поздравляю, всё работает. Но НО есть одно НО, если я захочу написать вот так:
Pasted image 20240705004328.png
То евент вызовется два раза, потому что в первый раз его вызовет onTest, а второй раз его вызовет автобус, т.к. он также имеет TestEvent в аргументах. Поэтому я предлагаю помечать методы по аннотации:
Java:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventHandler {}

@EventHandler
public void onTest(TestEvent event) {
    handleTest(event);
}
В автобусе для stream'а добавим ещё один фильтр:
.filter(entry -> entry.getValue().isAnnotationPresent(EventHandler.class))
Теперь проверяем, ставим бряк на handleTest и при срабатывании нажимаем на кнопку продолжить, если далее ничего не произошло и начался запускаться майнкрафт, то handler (наш метод) вызвался один раз и вы всё сделали правильно.

Теперь, чтобы удобно обращаться к автобусу евентов определим его в нашем клиенте:
Java:
private final EventBus eventBus;

public LearnClient() {
    this.eventBus = new EventBus();
}
 
Последнее редактирование:
Пользователь
Статус
Оффлайн
Регистрация
23 Авг 2021
Сообщения
521
Реакции[?]
53
Поинты[?]
20K
Список:
1. Создаём главный класс + снимаем ограничения - https://yougame.biz/threads/325113
2. Автобус евентов - https://yougame.biz/threads/325114
3. OpenGL + Шейдеры + Рендер - https://yougame.biz/threads/325115

Теория
Сейчас расскажу про систему евентов и её полезность. Представим есть функция рендера:
Java:
public void render() {
    // Do render
}
Чтобы не писать такой грязный код:
Java:
public void render() {
    watermark.render();
    staffalert.render();
}
Вы можете просто вызвать евент. Евент - это просто класс, который содержит в себе полезные данные, чтобы вы в дальнейшем использовали их в listener'е. Listener (дословно слушатель) - это класс, который обрабатывает один или несколько евентов. Когда вызывается евент, он собирает все зарегистрированные listener'ы и вызывает обработку евента в них.
Посмотреть вложение 281646

Практика
Можно приступать к написанию кода.
Сделаем интерфейс listener'а:
interface Listener {}
Автобус евентов прям так и назовём:
class EventBus {}
В нём будет список listener'ов:
Java:
class EventBus {

    // Зарегистрированные listener'ы
    private final Set<Listener> listeners;

    public EventBus() {
        this.listeners = Sets.newLinkedSet();
    }

    /*
        Регистрация listener'а
    */
    public void registerListener(Listener listener) {
        this.listeners.add(listener);
    }
}
| 🧐 Изучаем библиотеки
| Майнкрафт использует библиотеку guava, в которой есть удобные классы для работы с коллекциями: Lists, Sets, Maps

Интерфейс евента:
interface Event {}
А также вызов евента:
Java:
ublic void callEvent(Event event) {
    this.listeners.stream()
        .flatMap(listener -> Stream.of(listener.getClass().getDeclaredMethods()) // Собриаем все методы
            .map(method -> Map.entry(listener, method)) // И преобразуем в Entry<Listener, Method>, т.к. экземпляр listener'а нам понадобится, чтобы вызвать метод
        )
        .filter(entry -> entry.getValue().getParameterCount() == 1) // Проверяем имеет ли метод ровно 1 параметр
        .filter(entry -> entry.getValue().getParameterTypes()[0].equals(event.getClass())) // Проверяем совпадает ли класс данного параметра с нашим евентом
        .forEach(entry -> {
            try {
                entry.getValue().invoke(entry.getKey(), event); // Пытаемся вызвать евент
            } catch (IllegalAccessException | InvocationTargetException exception) {
                exception.printStackTrace(); // В случае ошибки выводим её
            }
        });
}
Не забываем, что существует другой тип евентов, которые можно отменять. Для этого создадим новый класс для отменяемых евентов:
Java:
class CancellableEvent implements Event {

    private boolean cancelled;

    public boolean isCancelled() {
        return this.cancelled;
    }

    public void cancel() {
        this.cancelled = true;
    }
}
Ну теперь всё, для проверки создадим тестовый event, listener и попробуем вызвать:
Java:
public class TestEvent implements Event {}

public class TestListener implements Listener {

    public void onTest(TestEvent event) {
     
    }
}

...
eventBus.registerListener(new TestListener());
eventBus.callEvent(new TestEvent());
Ставим бряк на onTest и проверяем:
Посмотреть вложение 281647
Поздравляю, всё работает. Но НО есть одно НО, если я захочу написать вот так:
Посмотреть вложение 281648
То евент вызовется два раза, потому что в первый раз его вызовет onTest, а второй раз его вызовет автобус, т.к. он также имеет TestEvent в аргументах. Поэтому я предлагаю помечать методы по аннотации:
Java:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventHandler {}

@EventHandler
public void onTest(TestEvent event) {
    handleTest(event);
}
В автобусе для stream'а добавим ещё один фильтр:
.filter(entry -> entry.getValue().isAnnotationPresent(EventHandler.class))
Теперь проверяем, ставим бряк на handleTest и при срабатывании нажимаем на кнопку продолжить, если далее ничего не произошло и начался запускаться майнкрафт, то handler (наш метод) вызвался один раз и вы всё сделали правильно.

Теперь, чтобы удобно обращаться к автобусу евентов определим его в нашем клиенте:
Java:
private final EventBus eventBus;

public LearnClient() {
    this.eventBus = new EventBus();
}
блять это прошлый век уже чел, знаешь что такое лямбды? а ещё знаешь, что можно создать специальный интерфейс и с помощью него это будет всё вызываться намного быстрее за счёт того что ты просто можешь кешировать значения филдов с классов-слушателей с типом твоего интерфейса и еще типом дженерика какое именно событие должно вызываться ака UpdateEvent и т.д. вместо вызова метода через рефлексию которая довольно медленная за счёт своих чеков на акцесс

если тема конечно является чисто для ознакомления с событиями то ладно, но если это всё что ты хотел показать - плохо..
 
Сверху Снизу