- 7,109
- 326
- 1,512
Данный урок писался для 1.7.10 и код тестировался на этой версии. Но в библиотеке не изменился api в 1.8, наверное, тоже пойдет
Update! Юзаю эту либу на 1.11.2 - интерфейс не поменялся, только несколько новых методов добавилось для записи данных в пакет
Update! Юзаю эту либу на 1.14.4 и 1.15.2 - поменялась регистрация хандлеров, апи в целом осталось тем же
Update! Юзаю эту либу на 1.11.2 - интерфейс не поменялся, только несколько новых методов добавилось для записи данных в пакет
Update! Юзаю эту либу на 1.14.4 и 1.15.2 - поменялась регистрация хандлеров, апи в целом осталось тем же
Введение
Предполагаю, что тему могут читать совсем новички в моддинге, поэтому поясню, что такое пакеты и зачем они могут быть нужны.
Майнкрафт состоит из сервера и клиента(даже в синглплеере), которым соответствует клиентская и серверная стороны кода. Пакет - это сообщение, отправляемое с одной стороны на другую.Каждая сторона имеет какую-то свою информацию. Например, клиент знает, что игрок нажимает левую кнопку мыши, но сервер не знает об этом. Поэтому клиент должен отправить на сервер пакет о действии атаки. Сервер примет пакет и соответствующе отреагирует.
Подобное поведение мы и будет реализовывать для примера.
Для начала расскажу о плюсах и минусах использования пакетной системы CodeChickenLib
Плюсы:
- Простота использования
- Каждый пакет не требуется регистрировать отдельно
- Хороша для небольшого количества пакетов
- Получается мало кода
- CCL на первой странице курса по популярности и на второй по закачкам, библиотека уже содержится во множестве сборок
- Апи не зависит от версии майнкрафта - ваши моды легче портировать
- При большом количестве пакетов обработчики сильно распухают
Архитектура пакетной системы CodeChickenLib
Реализации интерфейсов
IClientPacketHandler
и IServerPacketHandler
- сущности, отвечающие за обработку принятых пакетов со стороны клиента и со стороны сервера.PacketCustom
- класс пакетов. Каждый пакет имеет канал назначения("строка") и тип-идентификатор(целое число >=1). В пакет могут быть записаны какие-то данные при помощи write-методов и прочтены при помощи read-методов
Java:
//Каждому write-методу в списке соответствует read-метод
writeBoolean
writeByte
writeShort
writeInt
writeFloat
writeDouble
writeLong
writeChar
writeVarInt //формат переменной длины(в битах), подробнее тут https://wiki.vg/Data_types
writeVarShort //формат переменной длины
writeVarLong//формат переменной длины
writeArray //записывает массив байтов byte[]
writeString
writeUUID
writeEnum
writeResourceLocation
writePos //записывает BlockPos
writeVector
writeNBTTagCompound
writeItemStack
writeFluidStack
Добавление библиотеки в проект
Тут есть два варианта.Скачать можно тут: CodeChicken Lib 1.8.+ или из репозитория. Выберите версию deobf для разработки, для релиза - universal.
После того, как скачали, помещаем библиотеку в папку ./libs/ проекта и обновляем/пересобираем gradle-проект. В intellij idea достаточно нажать на Rrefresh all gradle projects. В эклипсе не пробовал, но пересборка проекта и повторный импорт точно подействуют.
Для старых версий forge может потребоваться добавить в build.gradle зависимость от папки libs
После того, как скачали, помещаем библиотеку в папку ./libs/ проекта и обновляем/пересобираем gradle-проект. В intellij idea достаточно нажать на Rrefresh all gradle projects. В эклипсе не пробовал, но пересборка проекта и повторный импорт точно подействуют.
Для старых версий forge может потребоваться добавить в build.gradle зависимость от папки libs
Gradle (Groovy):
dependencies {
compile fileTree(dir: './libs/', include: '*.jar')
}
Спасибо @Agravaine :j за совет
значения <version>, например, 1.12.2-3.2.3.358
Этот способ может не работать на последних версиях mdk для 1.12.2. Используйте mdk версии 14.23.5.2847 или первый способ добавления.
Gradle (Groovy):
repositories {
maven {
name = "chickenbones"
url = "https://chickenbones.net/maven" //или https://maven.covers1624.net/
}
}
dependencies {
compile "codechicken:CodeChickenLib:<version>:deobf"
}
Этот способ может не работать на последних версиях mdk для 1.12.2. Используйте mdk версии 14.23.5.2847 или первый способ добавления.
Реализуемая фича состоит в том, чтобы хоткеем [H]+[1..9] заюзать зелья, лежащие в девяти верхних слотах инвентаря.
Поэтому в пакет нужно будет записать номер нажатой клавиши, чтобы сервер знал, из какого слота нужно юзать зельку.Для отслеживания нажатия клавиш будем использовать обработчик
InputEvent.KeyInputEvent
Подробнее о событиях: Что такое события и как их ловить. Короткая и простая тема.
Обработчик события:
@SideOnly(Side.CLIENT)
@Mod.EventBusSubscriber(modid = Main.modid, value = Side.CLIENT)
public class ClientUpdate {
private static KeyBinding key = new KeyBinding("potionUse", Keyboard.KEY_H, "HotKeys");
public static void init() { //нужно вызвать это в preinit клиента
ClientRegistry.registerKeyBinding(key);
}
@SubscribeEvent
public static void onKeyPress(InputEvent.KeyInputEvent e) {
if (Keyboard.isKeyDown(key.getKeyCode())) {
KeyBinding[] keyBindsHotbar = Minecraft.getMinecraft().gameSettings.keyBindsHotbar;
for (int key = 0; key < keyBindsHotbar.length; key++)
if (Keyboard.isKeyDown(keyBindsHotbar[key].getKeyCode()))
new PacketCustom(Main.modid, 1)//Создаем пакет с типом-идентификатором 1
.writeInt(key)//Записываем в него индекс хоткея слота инвентаря
.sendToServer();//Отправляем
}
}
}
new PacketCustom(Main.modid, 1)
- выглядит несколько громоздко, если у нас будет больше одного пакета. Поэтому сделаем вспомогательный метод:
Java:
public static PacketCustom createPacket(int type) {
return new PacketCustom(Main.modid, type);
}
В
ClientProxy
в обработчик FMLPreInitializationEvent
добавим ClientUpdate.init();
, чтобы зарегистрировать хоткей.Отправка пакета у нас готова, теперь нужно написать логику приема.
Для этого делаем реализации
IClientPacketHandler
и IServerPacketHandler
:
Серверный обработчик пакетов:
public class ServerPacketHandler implements IServerPacketHandler {
@Override
public void handlePacket(PacketCustom packetCustom, EntityPlayerMP player, INetHandlerPlayServer iNetHandlerPlayServer) {
switch (packetCustom.getType()) {//чтобы определить тип-идентификатор пакета
case 1:
int potionHotSlot = packetCustom.readInt();//читаем из пакета слот, из которого нужно выпить зелье
ItemStack stack = player.inventory.getStackInSlot(9 + potionHotSlot);
if (stack.getItem() == Items.POTIONITEM)//если в слоте зелька, то Стив выпьет ее одним глотком :D
{
ItemStack stack1 = Items.POTIONITEM.onItemUseFinish(stack, player.world, player);
System.out.println(stack1.getCount());
player.inventory.setInventorySlotContents(9 + potionHotSlot,
stack1);
}
break;
default:
}
}
}
Клиентский обработчик пакетов:
public class ClientPacketHandler implements IClientPacketHandler {
@Override
public void handlePacket(PacketCustom packetCustom, Minecraft minecraft, INetHandlerPlayClient iNetHandlerPlayClient) {
switch (packetCustom.getType()) {//Определяем тип пакета
default:
}
}
}
FMLPreInitializationEvent
Java:
public class CommonProxy {
public void preinit(FMLPreInitializationEvent e) {
//Даже, если у вас есть отдельный класс для серверного прокси, все равно регистрацию серверного обработчика делайте здесь
PacketCustom.assignHandler(Main.modid/*имя канала не длиннее 20*/, new ServerPacketHandler());
}
}
public class ClientProxy extends CommonProxy {
public void preinit(FMLPreInitializationEvent e) {
super.preinit(e);
PacketCustom.assignHandler(Main.modid, new ClientPacketHandler());
ClientUpdate.init();
}
}
На новых версиях нужно в конструкторе главного класса мода вызвать
PacketCustomChannelBuilder
Java:
PacketCustomChannelBuilder
.named(name)//имя канала
.assignClientHandler(() -> ClientPacketHandler::new)
.assignServerHandler(() -> ServerPacketHandler::new)
.build();
Запускаем, заходим в локальный мир, или на локальный сервер, тестируем.
Ссылки
Github: hohserg1/CCL-example
На этом все, надеюсь, вам понравилось)
Последнее редактирование: