Files
PixelPics/src/main/java/eu/mhsl/minecraft/pixelpics/Main.java
T

192 lines
8.6 KiB
Java

package eu.mhsl.minecraft.pixelpics;
import eu.mhsl.minecraft.pixelpics.assets.AssetReader;
import eu.mhsl.minecraft.pixelpics.assets.BlockModelRegistry;
import eu.mhsl.minecraft.pixelpics.assets.ResourcePack;
import eu.mhsl.minecraft.pixelpics.assets.ResourcePackLoader;
import eu.mhsl.minecraft.pixelpics.assets.SkinCache;
import eu.mhsl.minecraft.pixelpics.assets.TextureCache;
import eu.mhsl.minecraft.pixelpics.assets.font.BitmapFont;
import eu.mhsl.minecraft.pixelpics.assets.font.FontLoader;
import eu.mhsl.minecraft.pixelpics.commands.PixelPicsCommand;
import eu.mhsl.minecraft.pixelpics.listeners.OnMapInitialize;
import eu.mhsl.minecraft.pixelpics.render.RenderManager;
import eu.mhsl.minecraft.pixelpics.render.entity.cem.BlockEntityBaker;
import eu.mhsl.minecraft.pixelpics.render.entity.cem.CemBaker;
import eu.mhsl.minecraft.pixelpics.render.entity.cem.CemModelLoader;
import eu.mhsl.minecraft.pixelpics.render.entity.cem.DatapackVariants;
import eu.mhsl.minecraft.pixelpics.render.render.DefaultScreenRenderer;
import eu.mhsl.minecraft.pixelpics.render.tint.BiomeTintProvider;
import eu.mhsl.minecraft.pixelpics.survival.CameraListener;
import eu.mhsl.minecraft.pixelpics.survival.CraftingListener;
import eu.mhsl.minecraft.pixelpics.survival.ItemGuardListener;
import eu.mhsl.minecraft.pixelpics.survival.JoinListener;
import eu.mhsl.minecraft.pixelpics.survival.PendingPhotos;
import eu.mhsl.minecraft.pixelpics.survival.SurvivalRecipes;
import eu.mhsl.minecraft.pixelpics.utils.MapColorPalette;
import eu.mhsl.minecraft.pixelpics.utils.MapManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
public final class Main extends JavaPlugin {
private static Main instance;
private ResourcePack resourcePack;
private DefaultScreenRenderer screenRenderer;
private RenderManager renderManager;
private PendingPhotos pendingPhotos;
// PDC keys: photo picture id (STRING), camera/film markers (BYTE), loaded film on a camera (INTEGER)
public final NamespacedKey pictureIdFlag = new NamespacedKey(this, "imageid");
public final NamespacedKey cameraMarker = new NamespacedKey(this, "camera");
public final NamespacedKey filmCountKey = new NamespacedKey(this, "filmcount");
public final NamespacedKey filmMarker = new NamespacedKey(this, "film");
@Override
public void onEnable() {
instance = this;
saveDefaultConfig();
MapManager.init(getServer().getWorlds().getFirst().getUID());
pendingPhotos = new PendingPhotos(getDataFolder());
pendingPhotos.recoverAbandoned();
initRenderManager();
Objects.requireNonNull(getCommand("pixelPic")).setExecutor(new PixelPicsCommand());
for (Listener listener : List.of(
new OnMapInitialize(),
new CameraListener(),
new CraftingListener(),
new ItemGuardListener(),
new JoinListener())) {
Bukkit.getPluginManager().registerEvents(listener, this);
}
SurvivalRecipes.register();
MapManager.reattachAfterReload(OnMapInitialize::attach);
initRenderer();
}
private void initRenderManager() {
int cores = Runtime.getRuntime().availableProcessors();
int threads = getConfig().getInt("render.threads", 0);
if (threads <= 0) threads = Math.max(1, cores - 2);
threads = Math.min(threads, cores);
int maxConcurrent = Math.max(1, getConfig().getInt("render.max-concurrent", 2));
int queueSize = Math.max(0, getConfig().getInt("render.queue-size", 8));
int timeoutSeconds = Math.max(1, getConfig().getInt("render.timeout-seconds", 30));
this.renderManager = new RenderManager(this, threads, maxConcurrent, queueSize, timeoutSeconds);
getLogger()
.info("Render pool: " + threads + " core(s), max " + maxConcurrent + " concurrent, queue " + queueSize
+ ", timeout " + timeoutSeconds + "s.");
}
private void initRenderer() {
File resourcePackDir = new File(getDataFolder(), "resourcepack");
if (!resourcePackDir.exists() && !resourcePackDir.mkdirs()) {
getLogger().warning("Could not create resource pack directory: " + resourcePackDir);
}
Optional<ResourcePack> pack = ResourcePackLoader.load(resourcePackDir, getLogger());
if (pack.isEmpty()) {
getLogger()
.severe("No resource pack found in " + resourcePackDir.getPath()
+ " — place a vanilla resource pack (directory with assets/minecraft/... or a .zip) there. "
+ "/pixelPic is disabled until a pack is available.");
return;
}
this.resourcePack = pack.get();
AssetReader reader = new AssetReader(resourcePack);
TextureCache textures = new TextureCache(resourcePack);
BlockModelRegistry registry = new BlockModelRegistry(reader, textures);
BiomeTintProvider tintProvider = new BiomeTintProvider(textures);
CemModelLoader cemLoader = new CemModelLoader();
try (InputStream in = getResource("cem/cem_template_models.json")) {
int n = in == null ? 0 : cemLoader.load(in, getLogger());
getLogger().info("Loaded " + n + " CEM entity models.");
} catch (Exception e) {
getLogger().severe("Failed to load CEM entity models: " + e.getMessage());
}
// Vanilla models dumped from the client jar (tools/VanillaModelDump.java) override the CEM set: an
// optional newer dump in the data folder (no recompile on MC updates), else the bundled copy.
File dump = new File(getDataFolder(), "vanilla_entity_models.json");
try (InputStream in =
dump.isFile() ? new FileInputStream(dump) : getResource("cem/vanilla_entity_models.json")) {
if (in != null) {
long t0 = System.nanoTime();
int n = cemLoader.load(in, getLogger());
getLogger()
.info("Loaded vanilla entity models" + (dump.isFile() ? " from " + dump.getName() : "") + " ("
+ n + " models total, " + (System.nanoTime() - t0) / 1_000_000 + " ms).");
}
} catch (Exception e) {
getLogger().severe("Failed to load vanilla entity models: " + e.getMessage());
}
List<File> worldFolders =
getServer().getWorlds().stream().map(World::getWorldFolder).toList();
int datapackVariants = DatapackVariants.load(cemLoader, worldFolders, getLogger());
if (datapackVariants > 0) getLogger().info("Loaded " + datapackVariants + " datapack mob variants.");
SkinCache skinCache = new SkinCache();
BitmapFont font = FontLoader.load(resourcePack, textures, getLogger());
getLogger().info("Loaded sign font (" + (font.isEmpty() ? "no glyphs — text disabled" : "ok") + ").");
CemBaker entityBaker = new CemBaker(cemLoader, textures, skinCache);
BlockEntityBaker blockEntityBaker = new BlockEntityBaker(cemLoader, textures, skinCache, font);
this.screenRenderer = new DefaultScreenRenderer(
registry,
tintProvider,
textures,
entityBaker,
blockEntityBaker,
getLogger(),
renderManager.tracePool());
// Initialise the map palette on the main thread (MapPalette) before any off-thread dithering.
MapColorPalette.size();
getLogger().info("PixelPics renderer initialized with resource pack assets.");
}
@Override
public void onDisable() {
SurvivalRecipes.unregister();
if (renderManager != null) {
renderManager.shutdown(); // delivers or fails every open job synchronously (film refunds)
renderManager = null;
}
MapManager.detachAll();
if (resourcePack != null) {
resourcePack.close();
resourcePack = null;
}
}
/** The renderer, or {@code null} when no resource pack is available (degraded mode). */
public DefaultScreenRenderer getScreenRenderer() {
return this.screenRenderer;
}
public PendingPhotos getPendingPhotos() {
return this.pendingPhotos;
}
public RenderManager getRenderManager() {
return this.renderManager;
}
public static Main getInstance() {
return instance;
}
}