diff --git a/README.md b/README.md index a9976d0..ed1eef9 100644 --- a/README.md +++ b/README.md @@ -17,16 +17,20 @@ On first start, the mod generates a configuration file at `/con ```yaml # The main configuration file for Resource Pack Local Path. -# Whether local resource-pack hosting (resource-pack= pointing to a relative -# file path instead of a URL in server.properties) is enabled at all. -# @default: true -resource-pack-hosting-enabled: true -# The host/IP that will be advertised to clients when downloading a locally +# The host/IP that will be used for clients when downloading a locally # hosted resource-pack. This must be reachable from the client's network -# (your public IP/domain, with port forwarding if needed) — the mod cannot +# (your public IP/domain, with port forwarding if needed) - the mod cannot # detect this automatically. # # The port used for hosting is the same as the server's port. # @default: '127.0.0.1' -resource-pack-host: 127.0.0.1 +host: 127.0.0.1 +# Whether resource-pack= pointing to a directory (instead of a .zip file) +# should automatically be zipped on the fly and cached. +# @default: true +auto-zip: true +# Directory (relative to the server root, or absolute) in which cached +# zipped resource packs are stored. +# @default: './config/resource_pack_local_path/cache' +cache-directory: ./config/resource_pack_local_path/cache ``` diff --git a/gradle.properties b/gradle.properties index 091f8e8..7e7dd89 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ loom_version=1.16-SNAPSHOT fabric_api_version=0.145.1+26.1 # Mod Properties -mod_version=0.1.1 +mod_version=1.0.0 maven_group=dev.loat archives_base_name=resource-pack-local-path diff --git a/src/main/java/dev/loat/resource_pack_local_path/config/Config.java b/src/main/java/dev/loat/resource_pack_local_path/config/Config.java index 132858e..b01003d 100644 --- a/src/main/java/dev/loat/resource_pack_local_path/config/Config.java +++ b/src/main/java/dev/loat/resource_pack_local_path/config/Config.java @@ -8,14 +8,25 @@ public class Config { private Config() {} + /** The root directory for the configuration files. */ private static final String ROOT_DIRECTORY = "resource_pack_local_path"; + /** The ConfigManager instance for managing configuration files. */ private static final ConfigManager CONFIG_MANAGER = new ConfigManager(ROOT_DIRECTORY); + /** + * Registers the configuration files with the ConfigManager. + * This method should be called during the mod initialization phase. + */ public static void register() { CONFIG_MANAGER.add("config.yml", ResourcePackLocalPathConfigFile.class); } + /** + * Retrieves the ResourcePackLocalPathConfigFile instance from the ConfigManager. + * + * @return The ResourcePackLocalPathConfigFile instance. + */ public static ResourcePackLocalPathConfigFile getConfig() { return CONFIG_MANAGER.get(ResourcePackLocalPathConfigFile.class); diff --git a/src/main/java/dev/loat/resource_pack_local_path/config/files/ResourcePackLocalPathConfigFile.java b/src/main/java/dev/loat/resource_pack_local_path/config/files/ResourcePackLocalPathConfigFile.java index 8871885..1430ad4 100644 --- a/src/main/java/dev/loat/resource_pack_local_path/config/files/ResourcePackLocalPathConfigFile.java +++ b/src/main/java/dev/loat/resource_pack_local_path/config/files/ResourcePackLocalPathConfigFile.java @@ -9,13 +9,6 @@ public class ResourcePackLocalPathConfigFile { private ResourcePackLocalPathConfigFile() {} - @Annotation.Comment(""" - Whether local resource-pack hosting (resource-pack= pointing to a relative - file path instead of a URL in server.properties) is enabled at all. - """) - @Annotation.Key("resource-pack-hosting-enabled") - public boolean resourcePackHostingEnabled = true; - @Annotation.Comment(""" The host/IP that will be used for clients when downloading a locally hosted resource-pack. This must be reachable from the client's network @@ -24,6 +17,20 @@ private ResourcePackLocalPathConfigFile() {} The port used for hosting is the same as the server's port. """) - @Annotation.Key("resource-pack-host") - public String resourcePackHost = "127.0.0.1"; + @Annotation.Key("host") + public String host = "127.0.0.1"; + + @Annotation.Comment(""" + Whether resource-pack= pointing to a directory (instead of a .zip file) + should automatically be zipped on the fly and cached. + """) + @Annotation.Key("auto-zip") + public boolean autoZip = true; + + @Annotation.Comment(""" + Directory (relative to the server root, or absolute) in which cached + zipped resource packs are stored. + """) + @Annotation.Key("cache-directory") + public String cacheDirectory = "./config/resource_pack_local_path/cache"; } diff --git a/src/main/java/dev/loat/resource_pack_local_path/hashing/Hash.java b/src/main/java/dev/loat/resource_pack_local_path/hashing/Hash.java new file mode 100644 index 0000000..5111ec2 --- /dev/null +++ b/src/main/java/dev/loat/resource_pack_local_path/hashing/Hash.java @@ -0,0 +1,81 @@ +package dev.loat.resource_pack_local_path.hashing; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * Central hashing helper methods for local resource pack support. + * Bundles SHA-1 calculations for files and arbitrary strings so the + * hex encoding logic is not duplicated across the project. + */ +public final class Hash { + + private Hash() {} + + /** + * Creates a new SHA-1 {@link MessageDigest} instance. + * + * @return A fresh, unused SHA-1 {@link MessageDigest}. + * @throws IllegalStateException If the SHA-1 algorithm is not available in + * this JVM (practically never the case, + * since SHA-1 is part of every standard JVM). + */ + private static MessageDigest newSha1Digest() { + try { + return MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("SHA-1 algorithm not available", e); + } + } + + /** + * Converts a byte array to its hexadecimal string representation. + * + * @param bytes The bytes to encode. + * @return The hex string (lowercase, two characters per byte). + */ + private static String toHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(bytes.length * 2); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } + + /** + * Computes the SHA-1 hash of a file's contents. + * + * @param file Path to the file whose contents should be hashed. + * @return The SHA-1 hash as a 40-character lowercase hex string. + * @throws IOException If the file cannot be read. + */ + public static String sha1HexOfFile(Path file) throws IOException { + MessageDigest digest = newSha1Digest(); + + try (var in = Files.newInputStream(file)) { + byte[] buffer = new byte[8192]; + int read; + while ((read = in.read(buffer)) != -1) { + digest.update(buffer, 0, read); + } + } + + return toHex(digest.digest()); + } + + /** + * Computes the SHA-1 hash of a string (UTF-8 encoded). + * + * @param input The string to hash. + * @return The SHA-1 hash as a 40-character lowercase hex string. + */ + public static String sha1HexOfString(String input) { + MessageDigest digest = newSha1Digest(); + digest.update(input.getBytes(StandardCharsets.UTF_8)); + return toHex(digest.digest()); + } +} diff --git a/src/main/java/dev/loat/resource_pack_local_path/http/DirectoryResourcePackZipper.java b/src/main/java/dev/loat/resource_pack_local_path/http/DirectoryResourcePackZipper.java new file mode 100644 index 0000000..08ff090 --- /dev/null +++ b/src/main/java/dev/loat/resource_pack_local_path/http/DirectoryResourcePackZipper.java @@ -0,0 +1,148 @@ +package dev.loat.resource_pack_local_path.http; + +import dev.loat.resource_pack_local_path.logging.Logger; +import dev.loat.resource_pack_local_path.hashing.Hash; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + + +/** + * Zips a resource pack directory when needed. + * + *

The cache filename is derived from a fast metadata fingerprint (relative + * paths + size + modification time of each file) so unchanged directories + * can be recognized and re-zipping skipped, without reading file contents. + * + *

The returned {@link ZippedResult#contentHash()}, however, is always the + * actual SHA-1 of the finished zip file's bytes (computed via a cheap hash + * pass over the - possibly reused - zip, not by re-zipping). This is what + * Minecraft clients verify the download against via resource-pack-sha1, so + * it must be the real file hash, not just a stable fingerprint of the source + * directory. + * + *

Old cache entries (from previous, different directory contents) are not + * automatically cleaned up. + */ +public final class DirectoryResourcePackZipper { + + private DirectoryResourcePackZipper() {} + + /** + * Computes a fast metadata fingerprint for a directory based on the + * relative path, size, and modification time of each contained file + * (recursive, sorted for deterministic results). The files are + * intentionally not read to keep this fast even for large resource + * packs. Used only to decide the cache filename / whether re-zipping is + * necessary - NOT exposed as the resource-pack-sha1 (see class Javadoc). + * + * @param dir The directory to fingerprint + * @return SHA-1 hex hash of the fingerprint + * @throws IOException If the directory cannot be traversed. + */ + private static String computeCacheKey(Path dir) throws IOException { + List entries = new ArrayList<>(); + + try (Stream stream = Files.walk(dir)) { + stream.filter(Files::isRegularFile).sorted().forEach(file -> { + try { + String relativePath = dir.relativize(file).toString().replace('\\', '/'); + long size = Files.size(file); + long lastModified = Files.getLastModifiedTime(file).toMillis(); + entries.add("%s:%d:%d".formatted(relativePath, size, lastModified)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + + return Hash.sha1HexOfString(String.join("\n", entries)); + } + + /** + * Packs all files from a directory recursively into a zip file. + * Writes first to a temporary file and renames it to the target path only + * after successful completion, so incomplete zip files are not left behind + * in case of failure. + * + * @param sourceDir The directory to zip + * @param zipFile Target path for the finished zip file + * @throws IOException If the directory cannot be read or the zip cannot be + * written. + */ + private static void zipDirectory( + Path sourceDir, + Path zipFile + ) throws IOException { + Path tempZip = zipFile.resolveSibling("%s.tmp".formatted(zipFile.getFileName())); + + try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(tempZip))) { + try (Stream stream = Files.walk(sourceDir)) { + List files = stream.filter(Files::isRegularFile).sorted().toList(); + for (Path file : files) { + String relativePath = sourceDir.relativize(file).toString().replace('\\', '/'); + zipOut.putNextEntry(new ZipEntry(relativePath)); + Files.copy(file, zipOut); + zipOut.closeEntry(); + } + } + } + + Files.move(tempZip, zipFile, StandardCopyOption.REPLACE_EXISTING); + } + + /** + * Result of a zip operation. + * + * @param zipFile Path to the finished zip file (newly created or reused + * from cache) + * @param contentHash Actual SHA-1 of the zip file's bytes - safe to use + * directly as resource-pack-sha1 + */ + public record ZippedResult( + Path zipFile, + String contentHash + ) {} + + /** + * Zips the given directory if needed, reusing a cached result if the + * directory's content hasn't changed since the last call. + * + * @param sourceDir The resource pack directory to zip + * @param cacheDir Directory where cached zips are stored (created if + * necessary) + * @return The {@link ZippedResult} with the path to the zip file and the + * actual SHA-1 of its bytes. + * @throws IOException If the directory cannot be read, the zip cannot be + * written, or the resulting zip cannot be hashed. + */ + public static ZippedResult zip( + Path sourceDir, + Path cacheDir + ) throws IOException { + String cacheKey = DirectoryResourcePackZipper.computeCacheKey(sourceDir); + + Files.createDirectories(cacheDir); + Path zipFile = cacheDir.resolve("%s.zip".formatted(cacheKey)); + + if (Files.exists(zipFile)) { + Logger.debug("Resource pack unchanged, reusing cached zip: '%s'".formatted(zipFile)); + } else { + Logger.info("Resource pack changed (or not cached yet), zipping to: '%s'".formatted(zipFile)); + DirectoryResourcePackZipper.zipDirectory(sourceDir, zipFile); + } + + String contentHash = Hash.sha1HexOfFile(zipFile); + return new DirectoryResourcePackZipper.ZippedResult(zipFile, contentHash); + } +} diff --git a/src/main/java/dev/loat/resource_pack_local_path/http/LocalResourcePackResolver.java b/src/main/java/dev/loat/resource_pack_local_path/http/LocalResourcePackResolver.java index 44938c6..4cef439 100644 --- a/src/main/java/dev/loat/resource_pack_local_path/http/LocalResourcePackResolver.java +++ b/src/main/java/dev/loat/resource_pack_local_path/http/LocalResourcePackResolver.java @@ -1,119 +1,126 @@ package dev.loat.resource_pack_local_path.http; import dev.loat.resource_pack_local_path.config.Config; +import dev.loat.resource_pack_local_path.hashing.Hash; import dev.loat.resource_pack_local_path.logging.Logger; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.Optional; - +/** + * Resolves a configured resource-pack value from server.properties. + * + *

If the value is a local path rather than an external URL, it resolves + * it to a URL that can be served by the embedded HTTP multiplex handler. + * + *

Supports both direct .zip files and directories, the latter of which + * are zipped automatically via {@link DirectoryResourcePackZipper} and + * optionally cached. + */ public final class LocalResourcePackResolver { - private static volatile LocalResourcePackResolver.Resolved active; - private LocalResourcePackResolver() {} + private static volatile Optional activePack = Optional.empty(); + /** - * Calculates the SHA-1 hash of the given file and returns it as a hexadecimal string. - * - * @param file The path to the file for which to calculate the SHA-1 hash. - * @return The SHA-1 hash of the file as a hexadecimal string. - * @throws IOException If an I/O error occurs while reading the file. - * @throws NoSuchAlgorithmException If the SHA-1 algorithm is not available. + * Result of a successful resolution. + * + * @param url The download URL that clients should use + * @param sha1 The SHA-1 hash of the pack contents (for resource-pack-sha1) + * @param packFile The path to the zip file that will be served + * @param contextPath The context path part of the URL (starting with "/") + * under which the HTTP multiplex handler serves the file */ - private static String sha1Hex(Path file) throws IOException, NoSuchAlgorithmException { - MessageDigest digest = MessageDigest.getInstance("SHA-1"); - try (var in = Files.newInputStream(file)) { - byte[] buffer = new byte[8192]; - int read; - while ((read = in.read(buffer)) != -1) { - digest.update(buffer, 0, read); - } - } - StringBuilder sb = new StringBuilder(); - for (byte b : digest.digest()) { - sb.append(String.format("%02x", b)); - } - return sb.toString(); - } + public record Resolved( + String url, + String sha1, + Path packFile, + String contextPath + ) {} /** - * Resolves a local resource pack path to a URL that can be served by the server. - * - * @param rawResourcePackValue The raw value of the resource-pack property from server.properties. - * @param serverPort The port on which the server is running. - * @return An Optional containing the resolved resource pack information, or empty if resolution failed. + * Resolves the raw resource-pack value from server.properties. + * + *

Returns {@link Optional#empty()} if the value is empty, already an + * external URL, local hosting is disabled in the config, or the path could + * not be resolved (each with a corresponding log entry). + * + * @param rawResourcePackValue The raw value of resource-pack= from + * `server.properties` + * @param serverPort The actual server-port from `server.properties`, + * on which the HTTP multiplex handler also runs + * @return The {@link Resolved} result, or empty if no resolution was + * necessary or possible */ - public static Optional resolve( + public static Optional resolve( String rawResourcePackValue, int serverPort ) { if (rawResourcePackValue == null || rawResourcePackValue.isBlank()) { return Optional.empty(); } - + if (rawResourcePackValue.startsWith("http://") || rawResourcePackValue.startsWith("https://")) { return Optional.empty(); } var config = Config.getConfig(); - if (!config.resourcePackHostingEnabled) { - Logger.warning("resource-pack property points to a local path ('%s') but local resource pack hosting is disabled in the config.".formatted(rawResourcePackValue)); - return Optional.empty(); - } Path serverDir = Path.of("").toAbsolutePath(); - Path resourcePackPath = serverDir.resolve(rawResourcePackValue).normalize(); + Path packPath = serverDir.resolve(rawResourcePackValue).normalize(); - if (!Files.exists(resourcePackPath)) { - Logger.error("resource-pack property points to a non-existent path: '%s'".formatted(resourcePackPath)); + if (!Files.exists(packPath)) { + Logger.error("Configured resource-pack path does not exist: %s".formatted(packPath)); return Optional.empty(); } - if (Files.isDirectory(resourcePackPath)) { - // TODO: zip on the fly... - Logger.error("resource-pack property points to a directory ('%s'). Only zipped resource packs are supported for now.".formatted(resourcePackPath)); - return Optional.empty(); - } + String sha1; - try { - String sha1 = LocalResourcePackResolver.sha1Hex(resourcePackPath); - String contextPath = "/pack-" + sha1 + ".zip"; - String url = "http://%s:%d%s".formatted(config.resourcePackHost, serverPort, contextPath); + if (Files.isDirectory(packPath)) { + if (!config.autoZip) { + Logger.error("resource-pack points to a directory (%s), but resource-pack-auto-zip is disabled in the config. " + + "Either point to a .zip file directly, or enable auto-zipping.".formatted(packPath)); + return Optional.empty(); + } + + try { + var cacheDir = Path.of(config.cacheDirectory); + var result = DirectoryResourcePackZipper.zip(packPath, cacheDir); + packPath = result.zipFile(); + sha1 = result.contentHash(); + } catch (IOException e) { + Logger.error("Failed to zip resource pack directory: %s".formatted(e.getMessage())); + return Optional.empty(); + } + } else { + try { + sha1 = Hash.sha1HexOfFile(packPath); + } catch (IOException e) { + Logger.error("Failed to hash resource pack file: %s".formatted(e.getMessage())); + return Optional.empty(); + } + } - LocalResourcePackResolver.Resolved resolved = new LocalResourcePackResolver.Resolved(url, sha1, resourcePackPath, contextPath); - LocalResourcePackResolver.active = resolved; + String contextPath = "/pack-%s.zip".formatted(sha1); + String url = "http://%s:%d%s".formatted(config.host, serverPort, contextPath); - Logger.info("Serving local resource pack '%s' as %s.".formatted(rawResourcePackValue, url)); + Logger.info("Serving local resource pack '%s' as '%s'.".formatted(rawResourcePackValue, url)); - return Optional.of(resolved); - } catch (IOException | NoSuchAlgorithmException e) { - Logger.error("Failed to prepare local resource pack: %s".formatted(e.getMessage())); - return Optional.empty(); - } + var resolved = Optional.of(new Resolved(url, sha1, packPath, contextPath)); + LocalResourcePackResolver.activePack = resolved; + return resolved; } /** - * Returns the currently active resolved resource pack, if any. - * - * @return An Optional containing the active resolved resource pack, or empty if none is active. + * Returns the currently active locally hosted resource pack, if one is configured. + * + * @return Optional containing the active resolved pack, or empty if the server is not serving + * a local resource pack. */ - public static Optional getActivePack() { - return Optional.ofNullable(active); + public static Optional getActivePack() { + return LocalResourcePackResolver.activePack; } - - /** - * Represents a resolved local resource pack. - * - * @param url The URL at which the resource pack is accessible. - * @param sha1 The SHA-1 hash of the resource pack file. - * @param resourcePackFile The path to the resource pack file. - * @param contextPath The context path for the resource pack. - */ - public record Resolved(String url, String sha1, Path resourcePackFile, String contextPath) {} - } diff --git a/src/main/java/dev/loat/resource_pack_local_path/http/ResourcePackMultiplexHandler.java b/src/main/java/dev/loat/resource_pack_local_path/http/ResourcePackMultiplexHandler.java index c81a236..be2bf21 100644 --- a/src/main/java/dev/loat/resource_pack_local_path/http/ResourcePackMultiplexHandler.java +++ b/src/main/java/dev/loat/resource_pack_local_path/http/ResourcePackMultiplexHandler.java @@ -14,31 +14,22 @@ public class ResourcePackMultiplexHandler extends ByteToMessageDecoder { + /** Known HTTP method prefixes for detection. */ private static final byte[][] HTTP_METHOD_PREFIXES = { "GET ".getBytes(StandardCharsets.US_ASCII), "HEAD ".getBytes(StandardCharsets.US_ASCII), }; + /** The path to the resource pack file to be served. */ private final Path resourcePackFile; - /** Ex. "/pack-.zip */ + /** The context path for the resource pack (Ex., "/pack-.zip"). */ private final String contextPath; - /** - * Constructs a new ResourcePackMultiplexHandler. - * - * @param resourcePackFile The path to the resource pack file to be served. - * @param contextPath The context path for the resource pack (e.g., "/pack-.zip"). - */ - public ResourcePackMultiplexHandler(Path resourcePackFile, String contextPath) { - this.resourcePackFile = resourcePackFile; - this.contextPath = contextPath; - } - /** * Checks if the incoming ByteBuf starts with the specified prefix. * - * @param in The ByteBuf to check. - * @param prefix The byte array representing the prefix to match. + * @param in The ByteBuf to check + * @param prefix The byte array representing the prefix to match * @return true if the ByteBuf starts with the prefix, false otherwise. */ private boolean matchesPrefix(ByteBuf in, byte[] prefix) { @@ -56,11 +47,11 @@ private boolean matchesPrefix(ByteBuf in, byte[] prefix) { /** * Determines if the incoming ByteBuf appears to be an HTTP request based on known HTTP method prefixes. * - * @param in The ByteBuf to check. + * @param in The ByteBuf to check * @return true if the ByteBuf looks like an HTTP request, false otherwise. */ private boolean looksLikeHttp(ByteBuf in) { - for (byte[] prefix : HTTP_METHOD_PREFIXES) { + for (byte[] prefix : ResourcePackMultiplexHandler.HTTP_METHOD_PREFIXES) { if (this.matchesPrefix(in, prefix)) { return true; } @@ -71,7 +62,7 @@ private boolean looksLikeHttp(ByteBuf in) { /** * Switches the pipeline to handle HTTP requests by adding the necessary HTTP handlers and removing this handler. * - * @param channelHandlerContext The ChannelHandlerContext for the current channel. + * @param channelHandlerContext The ChannelHandlerContext for the current channel */ private void switchToHttp(ChannelHandlerContext channelHandlerContext) { ChannelPipeline pipeline = channelHandlerContext.pipeline(); @@ -85,6 +76,17 @@ private void switchToHttp(ChannelHandlerContext channelHandlerContext) { Logger.debug("Incoming connection detected as HTTP, switched to resource pack file handler."); } + /** + * Constructs a new ResourcePackMultiplexHandler. + * + * @param resourcePackFile The path to the resource pack file to be served + * @param contextPath The context path for the resource pack (Ex., "/pack-.zip") + */ + public ResourcePackMultiplexHandler(Path resourcePackFile, String contextPath) { + this.resourcePackFile = resourcePackFile; + this.contextPath = contextPath; + } + /** * Decodes incoming ByteBuf data to determine if it is an HTTP request. If it is, switches the pipeline to handle HTTP requests. * diff --git a/src/main/java/dev/loat/resource_pack_local_path/http/SimpleHttpFileHandler.java b/src/main/java/dev/loat/resource_pack_local_path/http/SimpleHttpFileHandler.java index 2160097..d1e76be 100644 --- a/src/main/java/dev/loat/resource_pack_local_path/http/SimpleHttpFileHandler.java +++ b/src/main/java/dev/loat/resource_pack_local_path/http/SimpleHttpFileHandler.java @@ -19,14 +19,16 @@ */ public class SimpleHttpFileHandler extends SimpleChannelInboundHandler { + /** The path to the resource pack file to be served. */ private final Path resourcePackFile; + /** The context path for the resource pack (Ex., "/pack-.zip"). */ private final String contextPath; /** * Constructs a new SimpleHttpFileHandler. * - * @param resourcePackFile The path to the resource pack file to be served. - * @param contextPath The context path for the resource pack (e.g., "/pack-.zip"). + * @param resourcePackFile The path to the resource pack file to be served + * @param contextPath The context path for the resource pack (e.g., "/pack-.zip") */ public SimpleHttpFileHandler(Path resourcePackFile, String contextPath) { this.resourcePackFile = resourcePackFile; @@ -36,8 +38,8 @@ public SimpleHttpFileHandler(Path resourcePackFile, String contextPath) { /** * Handles the incoming HTTP request and serves the resource pack file if the URI matches the context path. * - * @param channelHandlerContext The ChannelHandlerContext for the current channel. - * @param request The incoming HTTP request. + * @param channelHandlerContext The ChannelHandlerContext for the current channel + * @param request The incoming HTTP request * @throws Exception If an error occurs while processing the request. */ @Override diff --git a/src/main/java/dev/loat/resource_pack_local_path/logging/Logger.java b/src/main/java/dev/loat/resource_pack_local_path/logging/Logger.java index 14cef45..ea4bde6 100644 --- a/src/main/java/dev/loat/resource_pack_local_path/logging/Logger.java +++ b/src/main/java/dev/loat/resource_pack_local_path/logging/Logger.java @@ -13,7 +13,7 @@ public class Logger { /** * This static function is used to set the class for logging. * - * @param classInstance The instance to use for logging. + * @param classInstance The instance to use for logging */ public static void setLoggerClass(Class classInstance) { Logger.INSTANCE = LoggerFactory.getLogger(classInstance); @@ -22,7 +22,7 @@ public static void setLoggerClass(Class classInstance) /** * This static function is used for logging a message at debug level. * - * @param message The message to log. + * @param message The message to log */ public static void debug(String message) { Logger.INSTANCE.debug(message); @@ -31,7 +31,7 @@ public static void debug(String message) { /** * This static function is used for logging a message at info level. * - * @param message The message to log. + * @param message The message to log */ public static void info(String message) { Logger.INSTANCE.info(message); @@ -40,7 +40,7 @@ public static void info(String message) { /** * This static function is used for logging a message at warning level. * - * @param message The message to log. + * @param message The message to log */ public static void warning(String message) { Logger.INSTANCE.warn(message); @@ -49,7 +49,7 @@ public static void warning(String message) { /** * This static function is used for logging a message at error level. * - * @param message The message to log. + * @param message The message to log */ public static void error(String message) { Logger.INSTANCE.error(message); diff --git a/src/main/java/dev/loat/resource_pack_local_path/mixin/DedicatedServerPropertiesMixin.java b/src/main/java/dev/loat/resource_pack_local_path/mixin/DedicatedServerPropertiesMixin.java index c55822f..8f5df42 100644 --- a/src/main/java/dev/loat/resource_pack_local_path/mixin/DedicatedServerPropertiesMixin.java +++ b/src/main/java/dev/loat/resource_pack_local_path/mixin/DedicatedServerPropertiesMixin.java @@ -26,11 +26,17 @@ @Mixin(DedicatedServerProperties.class) public abstract class DedicatedServerPropertiesMixin { + /** + * The serverResourcePackInfo field from DedicatedServerProperties, which may be modified by this mixin. + */ @Mutable @Shadow @Final public Optional serverResourcePackInfo; + /** + * The serverPort field from DedicatedServerProperties, which may be modified by this mixin. + */ @Shadow @Final public int serverPort; @@ -39,8 +45,8 @@ public abstract class DedicatedServerPropertiesMixin { * Overrides the local resource-pack path with the URL provided by the embedded HTTP server. * This method is injected at the end of the DedicatedServerProperties constructor. * - * @param properties The Properties object containing server settings. - * @param callbackInfo The CallbackInfo object for this injection. + * @param properties The Properties object containing server settings + * @param callbackInfo The CallbackInfo object for this injection */ @Inject(method = "(Ljava/util/Properties;)V", at = @At("TAIL")) private void templateServerMod$overrideLocalResourcePack( diff --git a/src/main/java/dev/loat/resource_pack_local_path/mixin/ServerConnectionListenerChannelInitializerMixin.java b/src/main/java/dev/loat/resource_pack_local_path/mixin/ServerConnectionListenerChannelInitializerMixin.java index e77603e..843689e 100644 --- a/src/main/java/dev/loat/resource_pack_local_path/mixin/ServerConnectionListenerChannelInitializerMixin.java +++ b/src/main/java/dev/loat/resource_pack_local_path/mixin/ServerConnectionListenerChannelInitializerMixin.java @@ -32,7 +32,7 @@ public abstract class ServerConnectionListenerChannelInitializerMixin { LocalResourcePackResolver.getActivePack().ifPresent(active -> channel.pipeline().addFirst( "resourcepack_multiplex", - new ResourcePackMultiplexHandler(active.resourcePackFile(), active.contextPath()) + new ResourcePackMultiplexHandler(active.packFile(), active.contextPath()) ) ); }