mirror of
https://github.com/plexusorg/Module-HTTPD.git
synced 2026-06-04 09:06:54 +00:00
fuck it we're rendering blocks using webgl
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
package dev.plex.assets;
|
||||
|
||||
import dev.plex.util.PlexLog;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.time.Duration;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class MinecraftAssetsManager
|
||||
{
|
||||
private static final URI VERSION_MANIFEST = URI.create("https://piston-meta.mojang.com/mc/game/version_manifest_v2.json");
|
||||
private static final Pattern MINECRAFT_VERSION = Pattern.compile("\\d+\\.\\d+(?:\\.\\d+)?(?:-(?:pre|rc)\\d+)?");
|
||||
private static final Pattern VERSION_STRING_MC = Pattern.compile("\\(MC: ([^)]+)\\)");
|
||||
|
||||
private final Path root;
|
||||
private final Path versionFile;
|
||||
private final HttpClient client;
|
||||
private final AtomicBoolean ready = new AtomicBoolean(false);
|
||||
private final AtomicBoolean refreshStarted = new AtomicBoolean(false);
|
||||
private final String minecraftVersion;
|
||||
|
||||
public MinecraftAssetsManager(Path dataFolder)
|
||||
{
|
||||
this.root = dataFolder.resolve("minecraft-assets");
|
||||
this.versionFile = root.resolve("version.txt");
|
||||
this.minecraftVersion = detectMinecraftVersion();
|
||||
this.client = HttpClient.newBuilder()
|
||||
.followRedirects(HttpClient.Redirect.NORMAL)
|
||||
.connectTimeout(Duration.ofSeconds(20))
|
||||
.build();
|
||||
}
|
||||
|
||||
public void refreshAsync()
|
||||
{
|
||||
if (!refreshStarted.compareAndSet(false, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CompletableFuture.runAsync(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
refreshIfNeeded();
|
||||
ready.set(true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
PlexLog.log("Unable to download Minecraft assets for HTTPD inventory view: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Path resolve(String category, String relativePath)
|
||||
{
|
||||
if (!ready.get())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Path file = root.resolve(category).resolve(relativePath).normalize();
|
||||
Path categoryRoot = root.resolve(category).normalize();
|
||||
if (!file.startsWith(categoryRoot) || !Files.isRegularFile(file))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private void refreshIfNeeded() throws IOException, InterruptedException
|
||||
{
|
||||
Files.createDirectories(root);
|
||||
String cachedVersion = Files.exists(versionFile) ? Files.readString(versionFile).trim() : "";
|
||||
if (minecraftVersion.equals(cachedVersion) && hasAssets())
|
||||
{
|
||||
PlexLog.debug("HTTPD Minecraft assets are already cached for {0}", minecraftVersion);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cachedVersion.isEmpty() && !minecraftVersion.equals(cachedVersion))
|
||||
{
|
||||
PlexLog.log("Minecraft version changed from " + cachedVersion + " to " + minecraftVersion + "; recreating HTTPD asset cache");
|
||||
}
|
||||
recreateCache();
|
||||
}
|
||||
|
||||
private boolean hasAssets()
|
||||
{
|
||||
return Files.isDirectory(root.resolve("textures"))
|
||||
&& Files.isDirectory(root.resolve("models"))
|
||||
&& Files.isDirectory(root.resolve("items"))
|
||||
&& Files.isRegularFile(root.resolve("textures/entity/shield/shield_base_nopattern.png"));
|
||||
}
|
||||
|
||||
private void recreateCache() throws IOException, InterruptedException
|
||||
{
|
||||
deleteDirectory(root);
|
||||
Files.createDirectories(root);
|
||||
|
||||
PlexLog.log("Downloading Minecraft " + minecraftVersion + " client assets for HTTPD inventory view");
|
||||
JSONObject version = findVersionJson();
|
||||
String clientUrl = version.getJSONObject("downloads").getJSONObject("client").getString("url");
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder(URI.create(clientUrl))
|
||||
.timeout(Duration.ofMinutes(5))
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<InputStream> response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
||||
if (response.statusCode() / 100 != 2)
|
||||
{
|
||||
throw new IOException("client jar download returned HTTP " + response.statusCode());
|
||||
}
|
||||
|
||||
try (InputStream in = response.body(); ZipInputStream zip = new ZipInputStream(in))
|
||||
{
|
||||
ZipEntry entry;
|
||||
while ((entry = zip.getNextEntry()) != null)
|
||||
{
|
||||
if (!entry.isDirectory())
|
||||
{
|
||||
copyAsset(entry.getName(), zip);
|
||||
}
|
||||
zip.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
Files.writeString(versionFile, minecraftVersion + System.lineSeparator());
|
||||
PlexLog.log("HTTPD Minecraft assets cached for " + minecraftVersion);
|
||||
}
|
||||
|
||||
private JSONObject findVersionJson() throws IOException, InterruptedException
|
||||
{
|
||||
JSONObject manifest = getJson(VERSION_MANIFEST);
|
||||
JSONArray versions = manifest.getJSONArray("versions");
|
||||
for (int i = 0; i < versions.length(); i++)
|
||||
{
|
||||
JSONObject version = versions.getJSONObject(i);
|
||||
if (minecraftVersion.equals(version.getString("id")))
|
||||
{
|
||||
return getJson(URI.create(version.getString("url")));
|
||||
}
|
||||
}
|
||||
throw new IOException("Minecraft version " + minecraftVersion + " was not found in Mojang's manifest");
|
||||
}
|
||||
|
||||
private JSONObject getJson(URI uri) throws IOException, InterruptedException
|
||||
{
|
||||
HttpRequest request = HttpRequest.newBuilder(uri)
|
||||
.timeout(Duration.ofSeconds(30))
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
if (response.statusCode() / 100 != 2)
|
||||
{
|
||||
throw new IOException(uri + " returned HTTP " + response.statusCode());
|
||||
}
|
||||
return new JSONObject(response.body());
|
||||
}
|
||||
|
||||
private void copyAsset(String name, ZipInputStream zip) throws IOException
|
||||
{
|
||||
String prefix = "assets/minecraft/";
|
||||
if (!name.startsWith(prefix))
|
||||
{
|
||||
return;
|
||||
}
|
||||
String path = name.substring(prefix.length());
|
||||
Path target = null;
|
||||
|
||||
if (path.startsWith("textures/item/") || path.startsWith("textures/block/") || path.equals("textures/entity/shield/shield_base_nopattern.png"))
|
||||
{
|
||||
target = root.resolve(path);
|
||||
}
|
||||
else if (path.startsWith("models/item/") || path.startsWith("models/block/"))
|
||||
{
|
||||
target = root.resolve(path);
|
||||
}
|
||||
else if (path.startsWith("items/"))
|
||||
{
|
||||
target = root.resolve(path);
|
||||
}
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Files.createDirectories(target.getParent());
|
||||
Files.copy(zip, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
private static String detectMinecraftVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
Object version = Bukkit.class.getMethod("getMinecraftVersion").invoke(null);
|
||||
if (version instanceof String stringVersion && isMinecraftVersion(stringVersion))
|
||||
{
|
||||
return stringVersion;
|
||||
}
|
||||
}
|
||||
catch (ReflectiveOperationException ignored)
|
||||
{
|
||||
}
|
||||
|
||||
Matcher versionMatcher = VERSION_STRING_MC.matcher(Bukkit.getVersion());
|
||||
if (versionMatcher.find() && isMinecraftVersion(versionMatcher.group(1)))
|
||||
{
|
||||
return versionMatcher.group(1);
|
||||
}
|
||||
|
||||
String bukkitVersion = Bukkit.getBukkitVersion();
|
||||
int dash = bukkitVersion.indexOf('-');
|
||||
String trimmed = dash == -1 ? bukkitVersion : bukkitVersion.substring(0, dash);
|
||||
if (isMinecraftVersion(trimmed))
|
||||
{
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Could not determine Minecraft version from Bukkit version strings: getMinecraftVersion unavailable, getVersion='"
|
||||
+ Bukkit.getVersion() + "', getBukkitVersion='" + bukkitVersion + "'");
|
||||
}
|
||||
|
||||
private static boolean isMinecraftVersion(String version)
|
||||
{
|
||||
return version != null && MINECRAFT_VERSION.matcher(version).matches();
|
||||
}
|
||||
|
||||
private static void deleteDirectory(Path path) throws IOException
|
||||
{
|
||||
if (!Files.exists(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
try (var stream = Files.walk(path))
|
||||
{
|
||||
for (Path file : stream.sorted(Comparator.reverseOrder()).toList())
|
||||
{
|
||||
Files.deleteIfExists(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user