Upload via GUI (194 Dateien)

This commit is contained in:
2026-10-07 07:32:07 +00:00
parent 5611c5883a
commit aae442ae48
13 changed files with 803 additions and 103 deletions
+141
View File
@@ -0,0 +1,141 @@
package de.aeromc.capes;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.function.Function;
/**
* AeroMC-Look im Spiel (Minecraft 1.14 … 26.x):
*
* – Ladebildschirm: AeroMC-Nachtblau statt Mojang-Rot, hellblauer Balken und
* AeroMC-Logo statt Mojang-Logo. Das Logo lädt Minecraft am Ressourcenpaket
* vorbei direkt aus dem Spiel – deshalb tauscht der Agent den Datenstrom aus.
* – Fenster: Titel "AeroMC - Minecraft …" und AeroMC-Symbol.
*
* Titel-Logo, Untertitel, Sprüche und Knöpfe kommen über ein Ressourcenpaket,
* das der Launcher anlegt (src/spieldesign.js).
*
* Die umgebauten Stellen im Spiel holen sich ihre Helfer – wie beim Umhang –
* über die System-Properties und nur über JDK-Typen (java.util.function.Function),
* damit es mit jedem Classloader klappt (Vanilla, Fabric, Forge, NeoForge).
*/
final class Design {
static final String LOGO = "aeromc.design.logo";
static final String LOGO_ALT = "aeromc.design.logoAlt";
static final String TITEL = "aeromc.design.titel";
static final String ICON = "aeromc.design.icon";
static final String MODS = "aeromc.mods";
/** Hintergrund des Ladebildschirms (wie das AeroMC-Design im Launcher). */
static final int HINTERGRUND = 0x0A0E1A;
/** Fortschrittsbalken. */
static final int BALKEN = 0x4FD2FF;
/** Breite des Balkens als Anteil der Bildschirmbreite (das Logo ist jetzt bildschirmbreit). */
static final float BALKEN_ANTEIL = 0.56f;
private static final int[] ICON_GROESSEN = {16, 32, 48, 64, 128, 256};
/** Pixelspeicher der Symbole festhalten, bis GLFW sie übernommen hat. */
private static final List<ByteBuffer> FESTHALTEN = new ArrayList<>();
private Design() {}
static void installiere(String ordner, Instrumentation inst) {
File dir = new File(ordner);
Properties p = System.getProperties();
p.put(LOGO, strom(new File(dir, "ladelogo.png")));
p.put(LOGO_ALT, strom(new File(dir, "ladelogo_alt.png")));
p.put(TITEL, (Function<Object, Object>) Design::titel);
p.put(ICON, (Function<Object, Object>) bilder -> symbol(dir, bilder));
inst.addTransformer(new DesignUmbau());
System.out.println("[AeroMC] Spieldesign aktiv");
}
/**
* Mod-Schalter: Klick auf „Mods verwalten“ im Pausemenü (Adresse aeromc://mods)
* meldet sich beim Launcher, der dann das Mod-Fenster über dem Spiel öffnet.
* Rückgabe TRUE = erledigt (Minecraft öffnet keinen Link), sonst FALSE.
*/
static void installiereMods(String adresse, Instrumentation inst) {
System.getProperties().put(MODS, (Function<Object, Object>) link -> {
if (link == null || !link.toString().startsWith("aeromc:")) return Boolean.FALSE;
Thread t = new Thread(() -> {
try {
java.net.HttpURLConnection c = (java.net.HttpURLConnection) new java.net.URL(adresse).openConnection();
c.setConnectTimeout(2000);
c.setReadTimeout(4000);
c.getResponseCode();
c.disconnect();
} catch (Throwable e) {
System.out.println("[AeroMC] Launcher fuer den Mod-Schalter nicht erreichbar: " + e);
}
}, "AeroMC-Mod-Schalter");
t.setDaemon(true);
t.start();
return Boolean.TRUE;
});
inst.addTransformer(new ModsUmbau());
System.out.println("[AeroMC] Mod-Schalter aktiv");
}
/** Ersetzt den Datenstrom des Mojang-Logos durch das AeroMC-Logo (bei Fehlern bleibt das Original). */
private static Function<Object, Object> strom(File datei) {
return original -> {
try {
return new ByteArrayInputStream(Files.readAllBytes(datei.toPath()));
} catch (Throwable t) {
System.out.println("[AeroMC] Ladelogo nicht lesbar: " + t);
return original;
}
};
}
private static Object titel(Object original) {
if (original == null) return null;
String s = original.toString();
return s.startsWith("AeroMC") ? original : "AeroMC - " + s;
}
/**
* Baut aus den mitgelieferten Rohdaten (icon_N.rgba) einen neuen
* GLFWImage.Buffer. LWJGL wird über den Classloader des übergebenen Buffers
* angesprochen – der Agent selbst kennt LWJGL nicht.
*/
private static Object symbol(File dir, Object original) {
if (original == null) return null;
try {
List<int[]> groessen = new ArrayList<>();
List<byte[]> daten = new ArrayList<>();
for (int n : ICON_GROESSEN) {
File f = new File(dir, "icon_" + n + ".rgba");
if (!f.isFile()) continue;
byte[] b = Files.readAllBytes(f.toPath());
if (b.length != n * n * 4) continue;
groessen.add(new int[] {n, n});
daten.add(b);
}
if (daten.isEmpty()) return original;
ClassLoader cl = original.getClass().getClassLoader();
Class<?> bild = Class.forName("org.lwjgl.glfw.GLFWImage", true, cl);
Object puffer = bild.getMethod("malloc", int.class).invoke(null, daten.size());
Method hole = puffer.getClass().getMethod("get", int.class);
Method setze = bild.getMethod("set", int.class, int.class, ByteBuffer.class);
for (int i = 0; i < daten.size(); i++) {
ByteBuffer px = ByteBuffer.allocateDirect(daten.get(i).length);
px.put(daten.get(i)).flip();
synchronized (FESTHALTEN) { FESTHALTEN.add(px); }
setze.invoke(hole.invoke(puffer, i), groessen.get(i)[0], groessen.get(i)[1], px);
}
return puffer;
} catch (Throwable t) {
System.out.println("[AeroMC] Fenstersymbol nicht gesetzt: " + t);
return original;
}
}
}