diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index cea2a0f..accca14 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -37,5 +37,8 @@ tasks.shadowJar { relocate("org.yaml.snakeyaml", "dev.xhyrom.lighteco.libraries.org.yaml.snakeyaml") - relocate("com.zaxxer.hikari", "dev.xhyrom.lighteco.libraries.com.zaxxer.hikari") + relocate("org.mariadb.jdbc", "dev.xhyrom.lighteco.libraries.mariadb") + relocate("com.mysql", "dev.xhyrom.lighteco.libraries.mysql") + relocate("org.postgresql", "dev.xhyrom.lighteco.libraries.postgresql") + relocate("com.zaxxer.hikari", "dev.xhyrom.lighteco.libraries.hikari") } \ No newline at end of file diff --git a/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/BukkitLightEcoPlugin.java b/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/BukkitLightEcoPlugin.java index acbdbf3..5eee4cb 100644 --- a/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/BukkitLightEcoPlugin.java +++ b/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/BukkitLightEcoPlugin.java @@ -52,7 +52,7 @@ public class BukkitLightEcoPlugin extends AbstractLightEcoPlugin { @Override protected void registerPlatformHooks() { - Hooks.register(); + Hooks.register(this); } @Override diff --git a/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/BukkitSchedulerAdapter.java b/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/BukkitSchedulerAdapter.java index add1c98..fd1c8d4 100644 --- a/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/BukkitSchedulerAdapter.java +++ b/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/BukkitSchedulerAdapter.java @@ -2,18 +2,28 @@ package dev.xhyrom.lighteco.bukkit; import dev.xhyrom.lighteco.common.plugin.scheduler.SchedulerAdapter; import dev.xhyrom.lighteco.common.plugin.scheduler.SchedulerTask; +import lombok.Getter; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; public class BukkitSchedulerAdapter implements SchedulerAdapter { + private final Executor async; + private final BukkitLightEcoBootstrap bootstrap; private final BukkitScheduler scheduler; public BukkitSchedulerAdapter(BukkitLightEcoBootstrap bootstrap) { this.bootstrap = bootstrap; this.scheduler = bootstrap.getServer().getScheduler(); + + this.async = runnable -> this.scheduler.runTaskAsynchronously(this.bootstrap.getLoader(), runnable); + } + + public Executor async() { + return this.async; } @Override diff --git a/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/hooks/Hooks.java b/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/hooks/Hooks.java index b90cdb8..9721f71 100644 --- a/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/hooks/Hooks.java +++ b/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/hooks/Hooks.java @@ -1,13 +1,16 @@ package dev.xhyrom.lighteco.bukkit.hooks; +import dev.xhyrom.lighteco.bukkit.BukkitLightEcoPlugin; import org.bukkit.Bukkit; public class Hooks { private static PlaceholderAPIExpansion placeholderAPIExpansion; - public static void register() { - if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) + public static void register(BukkitLightEcoPlugin plugin) { + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { + placeholderAPIExpansion = new PlaceholderAPIExpansion(plugin); placeholderAPIExpansion.register(); + } } public static void unregister() { diff --git a/common/build.gradle.kts b/common/build.gradle.kts index b69042c..91998ab 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -9,13 +9,16 @@ dependencies { exclude(module = "checker-qual") exclude(module = "annotations") } - api("net.kyori:adventure-text-minimessage:4.14.0") + api("net.kyori:adventure-text-minimessage:4.14.0") { + exclude(module = "adventure-bom") + exclude(module = "adventure-api") + } api("com.google.guava:guava:32.1.2-jre") implementation("eu.okaeri:okaeri-configs-yaml-snakeyaml:5.0.0-beta.5") implementation("eu.okaeri:okaeri-configs-validator-okaeri:5.0.0-beta.5") - implementation("com.zaxxer:HikariCP:5.0.1") + compileOnly("com.zaxxer:HikariCP:5.0.1") compileOnly("org.projectlombok:lombok:1.18.28") annotationProcessor("org.projectlombok:lombok:1.18.28") diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/Dependency.java b/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/Dependency.java index f4a77c0..7939c8c 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/Dependency.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/Dependency.java @@ -1,55 +1,104 @@ package dev.xhyrom.lighteco.common.dependencies; +import com.google.common.collect.ImmutableList; +import dev.xhyrom.lighteco.common.dependencies.relocation.Relocation; import lombok.Getter; +import java.util.List; + @Getter public enum Dependency { + /** + * Somewhere we use brackets instad of dots, so we need to rewrite them + * This is because gradle's shadow plugin relocates using replacing full paths (dots) + */ + + ASM( + "org.ow2.asm", + "asm", + "9.1" + ), + ASM_COMMONS( + "org.ow2.asm", + "asm-commons", + "9.1" + ), + JAR_RELOCATOR( + "me.lucko", + "jar-relocator", + "1.7" + ), + HIKARI( + "com{}zaxxer", + "HikariCP", + "5.0.1", + Relocation.of("hikari", "com{}zaxxer{}hikari") + ), H2_DRIVER( - "com.h2database", - "h2", - "2.1.214" + "com.h2database", + "h2", + "2.1.214" ), SQLITE_DRIVER( - "org.xerial", - "sqlite-jdbc", - "3.28.0" + "org.xerial", + "sqlite-jdbc", + "3.28.0" ), MARIADB_DRIVER( - "org.mariadb.jdbc", - "mariadb-java-client", - "3.1.3" + "org{}mariadb{}jdbc", + "mariadb-java-client", + "3.1.3", + Relocation.of("mariadb", "org{}mariadb{}jdbc") ), MYSQL_DRIVER( - "mysql", - "mysql-connector-java", - "8.0.23" + "mysql", + "mysql-connector-java", + "8.0.23", + Relocation.of("mysql", "com{}mysql") ), POSTGRESQL_DRIVER( - "org.postgresql", - "postgresql", - "42.6.0" + "org{}postgresql", + "postgresql", + "42.6.0", + Relocation.of("postgresql", "org{}postgresql") ); private final String fullPath; private final String version; + @Getter + private final List relocations; private static final String MAVEN_FORMAT = "%s/%s/%s/%s-%s.jar"; Dependency(String groupId, String artifactId, String version) { + this(groupId, artifactId, version, new Relocation[0]); + } + + Dependency(String groupId, String artifactId, String version, Relocation... relocations) { this.fullPath = String.format(MAVEN_FORMAT, - groupId.replace('.', '/'), - artifactId, + rewriteEscape(groupId).replace('.', '/'), + rewriteEscape(artifactId), version, - artifactId, + rewriteEscape(artifactId), version ); this.version = version; + this.relocations = ImmutableList.copyOf(relocations); + } + + private static String rewriteEscape(String path) { + return path.replace("{}", "."); } public String getFileName() { - String name = name().toLowerCase().replace('_', '-'); + return getFileName(null); + } - return String.format("%s-%s.jar", name, this.version); + public String getFileName(String classifier) { + String name = name().toLowerCase().replace('_', '-'); + String extra = classifier == null ? "" : "-" + classifier; + + return String.format("%s-%s.jar", name, this.version + extra); } } diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyManagerImpl.java b/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyManagerImpl.java index 1f19284..737a1f9 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyManagerImpl.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyManagerImpl.java @@ -2,22 +2,23 @@ package dev.xhyrom.lighteco.common.dependencies; import com.google.common.collect.ImmutableSet; import com.google.common.io.MoreFiles; +import dev.xhyrom.lighteco.common.dependencies.relocation.Relocation; +import dev.xhyrom.lighteco.common.dependencies.relocation.RelocationHandler; import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; import dev.xhyrom.lighteco.common.plugin.logger.PluginLogger; import dev.xhyrom.lighteco.common.util.URLClassLoaderAccess; import dev.xhyrom.lighteco.common.storage.StorageType; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; public class DependencyManagerImpl implements DependencyManager { private final EnumMap loaded = new EnumMap<>(Dependency.class); @@ -27,6 +28,7 @@ public class DependencyManagerImpl implements DependencyManager { private final DependencyRegistry registry; private final Path cacheDirectory; private final URLClassLoaderAccess classLoader; + private @MonotonicNonNull RelocationHandler relocationHandler; public DependencyManagerImpl(LightEcoPlugin plugin) { this.logger = plugin.getBootstrap().getLogger(); @@ -35,9 +37,18 @@ public class DependencyManagerImpl implements DependencyManager { this.classLoader = URLClassLoaderAccess.create((URLClassLoader) plugin.getBootstrap().getClass().getClassLoader()); } + private synchronized RelocationHandler getRelocationHandler() { + if (this.relocationHandler == null) { + this.relocationHandler = new RelocationHandler(this); + } + + return this.relocationHandler; + } + @Override public void loadDependencies(Set dependencies) { CountDownLatch latch = new CountDownLatch(dependencies.size()); + this.logger.info("Loading dependencies: " + dependencies); for (Dependency dependency : dependencies) { if (this.loaded.containsKey(dependency)) { @@ -46,18 +57,22 @@ public class DependencyManagerImpl implements DependencyManager { } CompletableFuture.runAsync(() -> { + System.out.println("Loading dependency " + dependency); try { loadDependency(dependency); } catch (Exception e) { throw new RuntimeException("Failed to load dependency " + dependency, e); } finally { latch.countDown(); + System.out.println("Loaded dependency " + dependency); } }); } try { latch.await(); + + this.logger.info("Loaded dependencies: " + dependencies); } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -68,7 +83,7 @@ public class DependencyManagerImpl implements DependencyManager { return; } - Path file = downloadDependency(dependency); + Path file = remapDependency(dependency, downloadDependency(dependency)); this.loaded.put(dependency, file); @@ -95,6 +110,24 @@ public class DependencyManagerImpl implements DependencyManager { return file; } + private Path remapDependency(Dependency dependency, Path normalFile) throws Exception { + List rules = new ArrayList<>(dependency.getRelocations()); + + if (rules.isEmpty()) { + return normalFile; + } + + Path remappedFile = this.cacheDirectory.resolve(dependency.getFileName("remapped")); + + // if the remapped source exists already, just use that. + if (Files.exists(remappedFile)) { + return remappedFile; + } + + this.getRelocationHandler().remap(normalFile, remappedFile, rules); + return remappedFile; + } + @Override public void loadStorageDependencies(Set types) { loadDependencies(this.registry.resolveStorageDependencies(types)); diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyRegistry.java b/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyRegistry.java index 966350f..b948bcb 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyRegistry.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyRegistry.java @@ -9,11 +9,11 @@ import java.util.Set; public class DependencyRegistry { private static final SetMultimap STORAGE_DEPENDENCIES = ImmutableSetMultimap.builder() - .putAll(StorageType.SQLITE, Dependency.SQLITE_DRIVER) - .putAll(StorageType.H2, Dependency.H2_DRIVER) - .putAll(StorageType.MYSQL, Dependency.MYSQL_DRIVER) - .putAll(StorageType.MARIADB, Dependency.MARIADB_DRIVER) - .putAll(StorageType.POSTGRESQL, Dependency.POSTGRESQL_DRIVER) + .putAll(StorageType.SQLITE, Dependency.SQLITE_DRIVER) + .putAll(StorageType.H2, Dependency.H2_DRIVER) + .putAll(StorageType.MYSQL, Dependency.MYSQL_DRIVER, Dependency.HIKARI) + .putAll(StorageType.MARIADB, Dependency.MARIADB_DRIVER, Dependency.HIKARI) + .putAll(StorageType.POSTGRESQL, Dependency.POSTGRESQL_DRIVER, Dependency.HIKARI) .build(); public Set resolveStorageDependencies(Set types) { diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/relocation/Relocation.java b/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/relocation/Relocation.java new file mode 100644 index 0000000..e1679a0 --- /dev/null +++ b/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/relocation/Relocation.java @@ -0,0 +1,21 @@ +package dev.xhyrom.lighteco.common.dependencies.relocation; + +import lombok.Getter; + +@Getter +public class Relocation { + private static final String RELOCATION_PREFIX = "dev.xhyrom.lighteco.libraries."; + + public static Relocation of(String id, String pattern) { + return new Relocation(pattern.replace("{}", "."), RELOCATION_PREFIX + id); + } + + private final String pattern; + private final String relocatedPattern; + + private Relocation(String pattern, String relocatedPattern) { + this.pattern = pattern; + this.relocatedPattern = relocatedPattern; + } + +} diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/relocation/RelocationHandler.java b/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/relocation/RelocationHandler.java new file mode 100644 index 0000000..15ff554 --- /dev/null +++ b/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/relocation/RelocationHandler.java @@ -0,0 +1,63 @@ +package dev.xhyrom.lighteco.common.dependencies.relocation; + +import dev.xhyrom.lighteco.common.dependencies.Dependency; +import dev.xhyrom.lighteco.common.dependencies.DependencyManager; +import dev.xhyrom.lighteco.common.dependencies.IsolatedClassLoader; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.util.*; + +public class RelocationHandler { + public static final Set DEPENDENCIES = EnumSet.of(Dependency.ASM, Dependency.ASM_COMMONS, Dependency.JAR_RELOCATOR); + private static final String JAR_RELOCATOR_CLASS = "me.lucko.jarrelocator.JarRelocator"; + private static final String JAR_RELOCATOR_RUN_METHOD = "run"; + + private final Constructor jarRelocatorConstructor; + private final Method jarRelocatorRunMethod; + + public RelocationHandler(DependencyManager dependencyManager) { + ClassLoader classLoader = null; + try { + // download the required dependencies for remapping + dependencyManager.loadDependencies(DEPENDENCIES); + // get a classloader containing the required dependencies as sources + classLoader = dependencyManager.obtainClassLoaderWith(DEPENDENCIES); + + // load the relocator class + Class jarRelocatorClass = classLoader.loadClass(JAR_RELOCATOR_CLASS); + + // prepare the reflected constructor & method instances + this.jarRelocatorConstructor = jarRelocatorClass.getDeclaredConstructor(File.class, File.class, Map.class); + this.jarRelocatorConstructor.setAccessible(true); + + this.jarRelocatorRunMethod = jarRelocatorClass.getDeclaredMethod(JAR_RELOCATOR_RUN_METHOD); + this.jarRelocatorRunMethod.setAccessible(true); + } catch (Exception e) { + try { + if (classLoader instanceof IsolatedClassLoader) { + ((IsolatedClassLoader) classLoader).close(); + } + } catch (IOException ex) { + e.addSuppressed(ex); + } + + throw new RuntimeException(e); + } + } + + public void remap(Path input, Path output, List relocations) throws Exception { + Map mappings = new HashMap<>(); + for (Relocation relocation : relocations) { + mappings.put(relocation.getPattern(), relocation.getRelocatedPattern()); + } + + // create and invoke a new relocator + Object relocator = this.jarRelocatorConstructor.newInstance(input.toFile(), output.toFile(), mappings); + this.jarRelocatorRunMethod.invoke(relocator); + } + +} diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/plugin/scheduler/SchedulerAdapter.java b/common/src/main/java/dev/xhyrom/lighteco/common/plugin/scheduler/SchedulerAdapter.java index f751406..514ab6e 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/plugin/scheduler/SchedulerAdapter.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/plugin/scheduler/SchedulerAdapter.java @@ -1,8 +1,11 @@ package dev.xhyrom.lighteco.common.plugin.scheduler; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; public interface SchedulerAdapter { + Executor async(); + SchedulerTask asyncLater(Runnable runnable, long delay, TimeUnit unit); SchedulerTask asyncRepeating(Runnable runnable, long interval, TimeUnit unit);