1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2026-01-06 15:42:03 +00:00

Gradle 8, platform isolation, bundled libraries, work on local linking

This commit is contained in:
Tim203
2023-04-24 00:18:03 +02:00
parent ef7251b933
commit 184389f11f
61 changed files with 753 additions and 1119 deletions

View File

@@ -26,7 +26,6 @@
package org.geysermc.floodgate.core;
import io.micronaut.context.ApplicationContext;
import java.nio.file.Paths;
import java.util.Map;
import java.util.UUID;
import org.geysermc.floodgate.api.FloodgateApi;
@@ -43,18 +42,23 @@ import org.geysermc.floodgate.core.database.entity.LinkedPlayer;
import org.geysermc.floodgate.core.event.EventBus;
import org.geysermc.floodgate.core.event.lifecycle.PostEnableEvent;
import org.geysermc.floodgate.core.event.lifecycle.ShutdownEvent;
import org.geysermc.floodgate.core.library.Library;
import org.geysermc.floodgate.core.library.LibraryManager;
import org.geysermc.floodgate.core.library.Repository;
import org.geysermc.floodgate.core.library.info.DependencyInfoLoader;
import org.geysermc.floodgate.core.util.EagerSingleton;
import org.geysermc.floodgate.isolation.library.Library;
import org.geysermc.floodgate.isolation.library.LibraryManager;
import org.geysermc.floodgate.isolation.library.Repository;
import org.geysermc.floodgate.isolation.library.info.DependencyInfoLoader;
public abstract class FloodgatePlatform {
private static final UUID KEY = UUID.randomUUID();
private final LibraryManager manager;
private ApplicationContext context;
private PlatformInjector injector;
protected FloodgatePlatform(LibraryManager manager) {
this.manager = manager;
}
protected void onContextCreated(ApplicationContext context) {
}
@@ -65,19 +69,28 @@ public abstract class FloodgatePlatform {
getClass().getClassLoader().getResource("dependencyInfo.txt")
);
new LibraryManager(ClassLoader.getSystemClassLoader().getParent(), Paths.get("./libs"))
manager
// .addLibrary(
// Library.builder(infoLoader)
// .id("guava")
// .repository(Repository.MAVEN_CENTRAL)
// .groupId("com.google.guava")
// .artifactId("guava")
// .build()
// )
.addLibrary(
Library.builder(infoLoader)
.id("guava")
.repository(Repository.MAVEN_CENTRAL)
.groupId("com.google.guava")
.artifactId("guava")
Library.builder()
.id("local-linking")
.repository(Repository.OPEN_COLLAB)
.groupId("org.geysermc.floodgate")
.artifactId("database")
.version("a-version")
.build()
)
.apply();
//noinspection unchecked
context = ApplicationContext.builder()
context = ApplicationContext.builder(manager.classLoader())
.properties(Map.of(
"platform.proxy", isProxy()
))
@@ -140,8 +153,6 @@ public abstract class FloodgatePlatform {
throw new RuntimeException("Failed to inject the packet listener!", exception);
}
// this.guice = guice.createChildInjector(new PostEnableModules(postEnableStageModules()));
context.getBean(EventBus.class).fire(new PostEnableEvent());
}

View File

@@ -1,159 +0,0 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Floodgate
*/
package org.geysermc.floodgate.core.library;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.floodgate.core.library.info.DependencyInfo;
import org.geysermc.floodgate.core.library.info.DependencyInfoLoader;
import org.geysermc.floodgate.core.util.Utils;
public record Library(
@NonNull String id,
@NonNull Repository repository,
@NonNull String groupId,
@NonNull String artifactId,
@NonNull String version,
byte[] sha256
) {
public Library {
Objects.requireNonNull(id);
Objects.requireNonNull(repository);
Objects.requireNonNull(groupId);
Objects.requireNonNull(artifactId);
Objects.requireNonNull(version);
}
public String path() {
return "%s/%s/%s/%s-%s.jar".formatted(
groupId.replace('.', '/'),
artifactId,
version,
artifactId,
version
);
}
public static LibraryBuilder builder() {
return builder(null);
}
public static LibraryBuilder builder(DependencyInfoLoader info) {
return new LibraryBuilder(info);
}
public Path filePath() {
return Path.of(id(), id() + "-" + version() + ".jar");
}
public void validateChecksum(byte[] data) {
byte[] hash;
try {
hash = MessageDigest.getInstance("SHA-256").digest(data);
} catch (NoSuchAlgorithmException exception) {
throw new RuntimeException(exception);
}
if (Arrays.equals(hash, sha256)) {
return;
}
throw new IllegalStateException(String.format(
"Downloaded library hash (%s) didn't match expected hash (%s)!",
Utils.base64Encode(data),
Utils.base64Encode(sha256())
));
}
public static class LibraryBuilder {
private final DependencyInfoLoader dependencyInfoLoader;
private String id;
private Repository repository;
private String groupId;
private String artifactId;
private String version;
private byte[] sha256;
LibraryBuilder(DependencyInfoLoader info) {
this.dependencyInfoLoader = info;
}
public LibraryBuilder id(String id) {
this.id = id;
return this;
}
public LibraryBuilder repository(@NonNull Repository repository) {
this.repository = Objects.requireNonNull(repository);
return this;
}
public LibraryBuilder groupId(String groupId) {
this.groupId = groupId;
return this;
}
public LibraryBuilder artifactId(String artifactId) {
this.artifactId = artifactId;
return this;
}
public LibraryBuilder version(String version) {
this.version = version;
return this;
}
public LibraryBuilder sha256(byte[] sha256) {
this.sha256 = sha256;
return this;
}
public LibraryBuilder sha256(String sha256) {
return sha256(Base64.getDecoder().decode(sha256));
}
public Library build() {
if (version == null || sha256 == null) {
if (dependencyInfoLoader == null) {
throw new IllegalStateException(
"Provide a version and hash or provide a DependencyInfoLoader"
);
}
DependencyInfo dependencyInfo = dependencyInfoLoader.byCombinedId(groupId, artifactId);
version = dependencyInfo.version();
sha256 = dependencyInfo.sha256();
}
return new Library(id, repository, groupId, artifactId, version, sha256);
}
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Floodgate
*/
package org.geysermc.floodgate.core.library;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import org.geysermc.floodgate.core.library.classloader.LibraryClassLoader;
// Credits for the idea of downloading dependencies on runtime go to LuckPerms
public final class LibraryManager {
private final LibraryClassLoader classLoader;
private final Path cacheDirectory;
private final Set<Library> toApply = new HashSet<>();
public LibraryManager(ClassLoader parent, Path cacheDirectory) {
this.classLoader = new LibraryClassLoader(parent);
this.cacheDirectory = cacheDirectory;
}
public LibraryManager addLibrary(Library library) {
toApply.add(library);
return this;
}
public void apply() {
CompletableFuture.allOf(
toApply.stream()
.map(library -> CompletableFuture.runAsync(() -> loadLibrary(library)))
.toArray(CompletableFuture[]::new)
).join();
}
private void loadLibrary(Library library) {
var libPath = cacheDirectory.resolve(library.filePath());
if (classLoader.isLoaded(libPath)) {
return;
}
if (!Files.exists(libPath)) {
library.repository().downloadTo(library, libPath);
}
classLoader.addPath(libPath);
}
}

View File

@@ -1,74 +0,0 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Floodgate
*/
package org.geysermc.floodgate.core.library;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.geysermc.floodgate.core.util.HttpClient;
import org.geysermc.floodgate.core.util.HttpClient.HttpResponse;
public enum Repository {
MAVEN_CENTRAL("https://repo1.maven.org/maven2/");
private final String baseUrl;
Repository(String baseUrl) {
this.baseUrl = baseUrl;
}
private byte[] justDownload(Library library) {
try {
HttpResponse<byte[]> response = new HttpClient().getRawData(baseUrl + library.path());
if (!response.isCodeOk()) {
throw new RuntimeException(String.format(
"Got an invalid response code (%s) while downloading library %s",
response.getHttpCode(), library.id()
));
}
return response.getResponse();
} catch (IOException exception) {
throw new IllegalStateException("Failed to download library " + library.id(), exception);
}
}
public byte[] download(Library library) {
byte[] data = justDownload(library);
library.validateChecksum(data);
return data;
}
public void downloadTo(Library library, Path location) {
try {
Files.createDirectories(location.getParent());
Files.write(location, download(library));
} catch (IOException exception) {
throw new IllegalStateException("Failed to save library!", exception);
}
}
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Floodgate
*/
package org.geysermc.floodgate.core.library.classloader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class LibraryClassLoader extends URLClassLoader {
private final Set<Path> loadedPaths = new HashSet<>();
public LibraryClassLoader(ClassLoader parent) {
super(new URL[0], parent);
}
@Override
public void addURL(URL url) {
super.addURL(url);
}
public void addPath(Path path) {
try {
addURL(path.toUri().toURL());
loadedPaths.add(path);
} catch (MalformedURLException e) {
throw new IllegalArgumentException(e);
}
}
public Set<Path> loadedPaths() {
return Collections.unmodifiableSet(loadedPaths);
}
public boolean isLoaded(Path path) {
return loadedPaths.contains(path);
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Floodgate
*/
package org.geysermc.floodgate.core.library.info;
import java.util.Base64;
public record DependencyInfo(String groupId, String artifactId, String version, byte[] sha256) {
public static DependencyInfo fromString(String string) {
var split = string.split(":");
if (split.length != 4) {
throw new IllegalArgumentException("Invalid LibraryInfo string: " + string);
}
return new DependencyInfo(split[0], split[1], split[2], Base64.getDecoder().decode(split[3]));
}
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Floodgate
*/
package org.geysermc.floodgate.core.library.info;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
public final class DependencyInfoLoader {
private final Map<String, DependencyInfo> infoMap = new HashMap<>();
private DependencyInfoLoader() {}
public static DependencyInfoLoader load(URL infoUrl) {
var loader = new DependencyInfoLoader();
try (var reader = new BufferedReader(new InputStreamReader(infoUrl.openStream()))) {
reader.lines().forEach(line -> {
var info = DependencyInfo.fromString(line);
loader.infoMap.put(info.groupId() + ":" + info.artifactId(), info);
});
} catch (IOException exception) {
throw new RuntimeException(exception);
}
return loader;
}
public DependencyInfo byCombinedId(String groupId, String artifactId) {
return infoMap.get(groupId + ":" + artifactId);
}
}

View File

@@ -30,12 +30,9 @@ import com.google.gson.JsonObject;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
@@ -97,26 +94,6 @@ public class HttpClient {
return connection;
}
@NonNull
public HttpResponse<byte[]> getRawData(String urlString) throws IOException {
HttpURLConnection connection = request(urlString);
try (InputStream inputStream = connection.getInputStream()) {
int responseCode = connection.getResponseCode();
byte[] buffer = new byte[8196];
int len;
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
while ((len = inputStream.read(buffer, 0, buffer.length)) != -1) {
outputStream.write(buffer, 0, len);
}
return new HttpResponse<>(responseCode, outputStream.toByteArray());
}
} catch (SocketTimeoutException | NullPointerException exception) {
return new HttpResponse<>(-1, null);
}
}
@NonNull
private <T> HttpResponse<T> readResponse(HttpURLConnection connection, Class<T> clazz) {
InputStreamReader streamReader = createReader(connection);

View File

@@ -34,7 +34,6 @@ import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Locale;
import java.util.Properties;
import java.util.UUID;
@@ -134,8 +133,4 @@ public class Utils {
future.completeExceptionally(ex);
return future;
}
public static String base64Encode(byte[] data) {
return Base64.getEncoder().encodeToString(data);
}
}

View File

@@ -1,9 +1,10 @@
org.geysermc.configutils:configutils:1.0-SNAPSHOT:Xyh6oCOT1RwP5hw1gRM77M0mOJ86nNPf4J9Hs9PL4Ek=
org.geysermc.configutils:configutils:2.0-SNAPSHOT:HiYf2qgXat5WOQOScSXPk8FBIKZDWYowCJPVJ+Uuv+0=
com.google.inject:guice:5.1.0:QTDlC/rEgJnIYPDZA7kYYMgaJJyQ84JF+P7Vj8gXvCY=
com.nukkitx.fastutil:fastutil-short-object-maps:8.5.3:nTF5o2PCLMeBm8gaPeU4Q41FG2CNpmC9PxeJVHZot+U=
com.nukkitx.fastutil:fastutil-int-object-maps:8.5.3:Z0iKxf9OmoIcQtmqV9Lo9Z39XSpBXKjL8tavAdVCKBY=
org.java-websocket:Java-WebSocket:1.5.2:/4adGYqNxdAJZzkuGHrtqTuDO3zdO8zvs9f7WDQJi4U=
cloud.commandframework:cloud-core:1.5.0:+hDJlLwU84P6sf0gkDq+xStIqAHhMXUCI+Wb8VsD1Zk=
io.micronaut.data:micronaut-data-model:3.9.6:Hdt2G2LRuAtIs1K2DayRz7OoiqZcbIfCUEstVIpQnUE=
io.micronaut.sql:micronaut-jdbc-hikari:4.7.2:w2N1dH4vY/QjjQGsViMOwgVYxAnZNGgMm1doCa19wOA=
io.micronaut.data:micronaut-data-hibernate-jpa:3.9.6:kFYugtQeXfLjqUCWrf3MFFDoUdYhXrPQmSh0pu2Zxzo=
io.micronaut:micronaut-context:3.8.7:ir020Tq3UavnPZhXOUNHCl/XdGcVrOzhM1lU5NT6YEc=