Как подключить HikariCP к моему моду?

Версия Minecraft
1.12.2
API
Forge
16
0
Мой мод на стороне сервера работает с HikariCP. Как работать с самой либой я знаю. Проблемы возникают, когда я через gradle создаю банку с модом (jar).

При работе мода возникает ошибка java.lang.NoClassDefFoundError: com/zaxxer/hikari/HikariConfig. Импорты настроены, Intellij все видит.

Я понимаю, что моду нужно подать либу, но как?

Я перепробовал кучу параметров и даже плагин shade, этот плагин добавил исходники либы в мою банку, но ошибка осталась. Пробовал я добавлять classpath в манифест - тоже самое.

И да, я знаю, что я могу подавать библиотеку при запуске сервера, но мне нужно, чтобы ничего указывать не нужно было. Идеально было бы, если мод во время старта сервера выгружал из себя или из репозитория библиотеку по указанному пути и знал, что она лежит там. Я знаю, что такое делают, но не могу найти как. Нормально было бы и просто включать исходники библиотеки в банку с модом, но так, чтобы мод их там видел.

Наверное, я делаю что-то не так, ведь я плохо знаком с gradle. Подскажите знающие люди, пожалуйста, мучаюсь с этим второй день. Если можно, то распишите все по порядочку.
 
Решение
Gradle (Groovy):
plugins {
    id 'com.github.johnrengelman.shadow' version '6.0.0' // тут зависит от версии gralde
}

shadowJar {
    configurations = [project.configurations.shadow]
    archiveClassifier.set('')
}

dependencies {
    shadow 'com.zaxxer:HikariCP:3.4.2' // версию сам смотри уже
    shadow 'com.j256.ormlite:ormlite-jdbc:5.1' // то, о чем я говорил выше, вдруг тебе полезно будет
}

build.dependsOn shadowJar
16
0
не всегда помогает. иногда надо прямо в -cp закидывать
Ну тогда вопрос: как добавить в cp библиотеку прямо из мода? Чтобы не нужно было прописывать в строке запуска сервера.

Вначале имени джарника библиотеки добавь букву a и фордж подгрузит ее самой первой, а потом уже твой мод.
Такое знаю, с миксинами так делал, но это не выход, ведь это придется объяснять тем, кто захочет поставить мой мод. Я ищу путь, как сделать все автоматически, чтобы просто закинул мод в mods и запустил сервер.

Допустим, я могу скачать либу по ссылке в нужную директорию прямо из кода мода, но как мне сделать, чтобы мод нашел ее (то есть куда и как прописывать класспач) и как проверить наличие загруженной библиотеки при такой системе?

Если решить эти вопросы, то я сделаю так: после загрузки сервера (в post inite) мод проверяет наличие библиотеки, если ее нет, то он ее скачивает и выключает сервер (может тут можно как-то и без выключения, но я не знаю).
 

will0376

Токсичная личность
2,059
55
572
Ну тогда вопрос: как добавить в cp библиотеку прямо из мода?
а никак.

Максимум, что ты можешь - попросить shadow запаковать HCP в отдельный пакет твоего мода(reloacate)
 
214
11
59
Если либа - мод, то можно юзать @Mod(dependencies = "after:MODID")
Если @mod класса в нем нет, то выноси логику использования этой либы за свой @mod класс. Если и это не подходит, дополни либу FMLPlugin'ом, и в манифест добавь:
Java:
'FMLCorePlugin': 'путь.до.моего.FMLPLUGIN',
'FMLCorePluginContainsFMLMod': 'true',

А для совсем поехавших, можно загрузить все классы из нужного jar в ClassLoader форжа вручную.

P.s. кто вообще называет jar банкой ?
 
16
0
P.s. кто вообще называет jar банкой ?
Я. Заразился от адептов спринга.
Если @mod класса в нем нет, то выноси логику использования этой либы за свой @mod класс. Если и это не подходит, дополни либу FMLPlugin'ом, и в манифест добавь:
Интересно, попробую, спасибо за наводку.
А для совсем поехавших, можно загрузить все классы из нужного jar в ClassLoader форжа вручную.
А это вообще как? Да и там ведь зависимости есть (в hikaricp).
 
214
11
59
Очевидно, тебе придется изобрести чего-то для своей задачи самому. Я могу лишь подсказать как можно, но не нужно, грузить джарники в рантайме.

Я это дело писал для иной задачи, но как пример для тебя сойдет
Java:
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.function.Predicate;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class Test {

    static Map<String, ZipEntry> namedEntries = new HashMap<>();
    static List<ZipFile> zipFiles = new ArrayList<>();
    static MyClassLoader classLoader = new MyClassLoader();
    static List<String> unloadedClasses = new ArrayList<>();
    static List<String> allLoadedClasses = new ArrayList<>();

    public static void main(String[] args) throws IOException {
        zipFiles.add(new ZipFile(new File("test.jar")));
        zipFiles.add(new ZipFile(new File("test1.jar")));
        zipFiles.add(new ZipFile(new File("test2.jar")));
        zipFiles.add(new ZipFile(new File("test3.jar")));
        zipFiles.forEach(zipFile -> forEach(zipFile.entries(), zipEntry -> {
            if(zipEntry.getName().endsWith(".class")) {
                unloadedClasses.add(zipEntry.getName());
            }
            return true;
        }));
        while (!unloadedClasses.isEmpty()) {
            loadClass(null);
        }
        System.out.println("DONE");
    }

    private static void loadClass(String className) {
        for (ZipFile zipFile : zipFiles) {
            Enumeration<? extends ZipEntry> entries = zipFile.entries();
            forEach(entries, zipEntry -> {
                if(!zipEntry.getName().endsWith(".class")) return true;
                if(className != null && !zipEntry.getName().endsWith(className)) return true;
                namedEntries.put(zipEntry.getName(), zipEntry);
                try {
                    InputStream inputStream = zipFile.getInputStream(zipEntry);
                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                    byte[] b = new byte[1024];
                    int numberOfBytesRead;
                    while ((numberOfBytesRead = inputStream.read(b)) >= 0) {
                        byteArrayOutputStream.write(b, 0, numberOfBytesRead);
                    }
                    String s = zipEntry.getName();
                    System.out.println("loaded " + s);
                    String formatClassName = formatClassName(s);
                    try {
                        Class.forName(formatClassName, false, classLoader);
                        return true;
                    } catch (ClassNotFoundException ignored) {}
                    classLoader.defineClassBytes(formatClassName, byteArrayOutputStream.toByteArray());
                    unloadedClasses.remove(s);
                    allLoadedClasses.add(formatClassName);
                } catch (NoClassDefFoundError e) {
                    System.out.println("err " + e.getMessage());
                    loadClass(e.getMessage());
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return true;
            });
        }
    }

    private static String formatClassName(String name) {
        if (name == null) {
            return null;
        } else {
            if (name.endsWith(".class")) {
                name = name.replace(".class", "");
            }
            return name.replace("/", ".");
        }
    }

    private static void forEach(Enumeration<? extends ZipEntry> enumeration, Predicate<ZipEntry> predicate) {
        while (enumeration.hasMoreElements()) {
            if (!predicate.test(enumeration.nextElement())) {
                break;
            }
        }
    }

    public static class MyClassLoader extends ClassLoader {

        public void defineClassBytes(String fullClassName, byte[] bytes) {
            defineClass(fullClassName, bytes, 0, bytes.length);
        }
    }
}
 
16
0
Постоянно использую HikariCP и ORMLite, вшив их через shadowgradle в coremod
Я пытался настроить конфиг, так, чтобы он цеплял только нужные мне 2 либы, но не смог.
Скинь, плиз, кусок с конфигурацией этого самого shadowgradle.
 
170
2
53
Gradle (Groovy):
plugins {
    id 'com.github.johnrengelman.shadow' version '6.0.0' // тут зависит от версии gralde
}

shadowJar {
    configurations = [project.configurations.shadow]
    archiveClassifier.set('')
}

dependencies {
    shadow 'com.zaxxer:HikariCP:3.4.2' // версию сам смотри уже
    shadow 'com.j256.ormlite:ormlite-jdbc:5.1' // то, о чем я говорил выше, вдруг тебе полезно будет
}

build.dependsOn shadowJar
 
170
2
53
Если вдруг тебе нужно изменить расположение пакетов зависимостей в твоей "банке" после configurations = [project.configurations.shadow] добавь relocate('com.example', 'my.project.internal').
Что и куда подставить в relocate уже сам думай, их можно сколько душе угодно запихивать
 
7,099
324
1,509
Ну тогда вопрос: как добавить в cp библиотеку прямо из мода? Чтобы не нужно было прописывать в строке запуска сервера.
Можно вот так:
Java:
File libFile = ...
URL libUrl = libFile.toURI().toURL();

final ClassLoader classLoader = getClass().getClassLoader();
if (classLoader instanceof URLClassLoader) {
    final URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
    try {
        final Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
        addURL.setAccessible(true);
        addURL.invoke(urlClassLoader, libUrl);
        getLogger().info("Injected " + libUrl + " into ClassLoader");
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}else
    System.out.println("Cannot add '" + libUrl + "' to classloader because it's not an URL classloader");
 
7,099
324
1,509
Для майна рекомендованная версия джавки - 8. А чего там патчить? Relocate имен пакетов, при добавлении разных версий одной и той же либы?
 

tox1cozZ

aka Agravaine
8,454
598
2,890
Для майна рекомендованная версия джавки - 8. А чего там патчить? Relocate имен пакетов, при добавлении разных версий одной и той же либы?
В девятой и выше джаве загрузчик класс не URLClassLoader, а AppClassLoader или как-то так.
Ну были ж тут энтузиасты которые такие вещи патчили в фордже и модах чтобы запустить игру на 11.
 
Сверху