191 lines
6.6 KiB
Java
191 lines
6.6 KiB
Java
package net.minecraft.client.renderer.texture;
|
|
|
|
import com.mojang.blaze3d.platform.NativeImage;
|
|
import com.mojang.logging.LogUtils;
|
|
import com.mojang.realmsclient.gui.screens.AddRealmPopupScreen;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.IOException;
|
|
import java.io.UncheckedIOException;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.Executor;
|
|
import net.fabricmc.api.EnvType;
|
|
import net.fabricmc.api.Environment;
|
|
import net.minecraft.CrashReport;
|
|
import net.minecraft.CrashReportCategory;
|
|
import net.minecraft.ReportedException;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
|
import net.minecraft.server.packs.resources.ResourceManager;
|
|
import org.slf4j.Logger;
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public class TextureManager implements PreparableReloadListener, Tickable, AutoCloseable {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
public static final ResourceLocation INTENTIONAL_MISSING_TEXTURE = ResourceLocation.withDefaultNamespace("");
|
|
private final Map<ResourceLocation, AbstractTexture> byPath = new HashMap();
|
|
private final Set<Tickable> tickableTextures = new HashSet();
|
|
private final ResourceManager resourceManager;
|
|
|
|
public TextureManager(ResourceManager resourceManager) {
|
|
this.resourceManager = resourceManager;
|
|
NativeImage nativeImage = MissingTextureAtlasSprite.generateMissingImage();
|
|
this.register(MissingTextureAtlasSprite.getLocation(), new DynamicTexture(() -> "(intentionally-)Missing Texture", nativeImage));
|
|
}
|
|
|
|
public void registerAndLoad(ResourceLocation textureId, ReloadableTexture texture) {
|
|
try {
|
|
texture.apply(this.loadContentsSafe(textureId, texture));
|
|
} catch (Throwable var6) {
|
|
CrashReport crashReport = CrashReport.forThrowable(var6, "Uploading texture");
|
|
CrashReportCategory crashReportCategory = crashReport.addCategory("Uploaded texture");
|
|
crashReportCategory.setDetail("Resource location", texture.resourceId());
|
|
crashReportCategory.setDetail("Texture id", textureId);
|
|
throw new ReportedException(crashReport);
|
|
}
|
|
|
|
this.register(textureId, texture);
|
|
}
|
|
|
|
private TextureContents loadContentsSafe(ResourceLocation textureId, ReloadableTexture texture) {
|
|
try {
|
|
return loadContents(this.resourceManager, textureId, texture);
|
|
} catch (Exception var4) {
|
|
LOGGER.error("Failed to load texture {} into slot {}", texture.resourceId(), textureId, var4);
|
|
return TextureContents.createMissing();
|
|
}
|
|
}
|
|
|
|
public void registerForNextReload(ResourceLocation textureId) {
|
|
this.register(textureId, new SimpleTexture(textureId));
|
|
}
|
|
|
|
public void register(ResourceLocation path, AbstractTexture texture) {
|
|
AbstractTexture abstractTexture = (AbstractTexture)this.byPath.put(path, texture);
|
|
if (abstractTexture != texture) {
|
|
if (abstractTexture != null) {
|
|
this.safeClose(path, abstractTexture);
|
|
}
|
|
|
|
if (texture instanceof Tickable tickable) {
|
|
this.tickableTextures.add(tickable);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void safeClose(ResourceLocation path, AbstractTexture texture) {
|
|
this.tickableTextures.remove(texture);
|
|
|
|
try {
|
|
texture.close();
|
|
} catch (Exception var4) {
|
|
LOGGER.warn("Failed to close texture {}", path, var4);
|
|
}
|
|
}
|
|
|
|
public AbstractTexture getTexture(ResourceLocation path) {
|
|
AbstractTexture abstractTexture = (AbstractTexture)this.byPath.get(path);
|
|
if (abstractTexture != null) {
|
|
return abstractTexture;
|
|
} else {
|
|
SimpleTexture simpleTexture = new SimpleTexture(path);
|
|
this.registerAndLoad(path, simpleTexture);
|
|
return simpleTexture;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
for (Tickable tickable : this.tickableTextures) {
|
|
tickable.tick();
|
|
}
|
|
}
|
|
|
|
public void release(ResourceLocation path) {
|
|
AbstractTexture abstractTexture = (AbstractTexture)this.byPath.remove(path);
|
|
if (abstractTexture != null) {
|
|
this.safeClose(path, abstractTexture);
|
|
}
|
|
}
|
|
|
|
public void close() {
|
|
this.byPath.forEach(this::safeClose);
|
|
this.byPath.clear();
|
|
this.tickableTextures.clear();
|
|
}
|
|
|
|
@Override
|
|
public CompletableFuture<Void> reload(
|
|
PreparableReloadListener.PreparationBarrier preparationBarrier, ResourceManager resourceManager, Executor executor, Executor executor2
|
|
) {
|
|
List<TextureManager.PendingReload> list = new ArrayList();
|
|
this.byPath.forEach((resourceLocation, abstractTexture) -> {
|
|
if (abstractTexture instanceof ReloadableTexture reloadableTexture) {
|
|
list.add(scheduleLoad(resourceManager, resourceLocation, reloadableTexture, executor));
|
|
}
|
|
});
|
|
return CompletableFuture.allOf((CompletableFuture[])list.stream().map(TextureManager.PendingReload::newContents).toArray(CompletableFuture[]::new))
|
|
.thenCompose(preparationBarrier::wait)
|
|
.thenAcceptAsync(void_ -> {
|
|
AddRealmPopupScreen.updateCarouselImages(this.resourceManager);
|
|
|
|
for (TextureManager.PendingReload pendingReload : list) {
|
|
pendingReload.texture.apply((TextureContents)pendingReload.newContents.join());
|
|
}
|
|
}, executor2);
|
|
}
|
|
|
|
public void dumpAllSheets(Path path) {
|
|
try {
|
|
Files.createDirectories(path);
|
|
} catch (IOException var3) {
|
|
LOGGER.error("Failed to create directory {}", path, var3);
|
|
return;
|
|
}
|
|
|
|
this.byPath.forEach((resourceLocation, abstractTexture) -> {
|
|
if (abstractTexture instanceof Dumpable dumpable) {
|
|
try {
|
|
dumpable.dumpContents(resourceLocation, path);
|
|
} catch (IOException var5) {
|
|
LOGGER.error("Failed to dump texture {}", resourceLocation, var5);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private static TextureContents loadContents(ResourceManager resourceManager, ResourceLocation textureId, ReloadableTexture texture) throws IOException {
|
|
try {
|
|
return texture.loadContents(resourceManager);
|
|
} catch (FileNotFoundException var4) {
|
|
if (textureId != INTENTIONAL_MISSING_TEXTURE) {
|
|
LOGGER.warn("Missing resource {} referenced from {}", texture.resourceId(), textureId);
|
|
}
|
|
|
|
return TextureContents.createMissing();
|
|
}
|
|
}
|
|
|
|
private static TextureManager.PendingReload scheduleLoad(
|
|
ResourceManager resourceManager, ResourceLocation textureId, ReloadableTexture texture, Executor executor
|
|
) {
|
|
return new TextureManager.PendingReload(texture, CompletableFuture.supplyAsync(() -> {
|
|
try {
|
|
return loadContents(resourceManager, textureId, texture);
|
|
} catch (IOException var4) {
|
|
throw new UncheckedIOException(var4);
|
|
}
|
|
}, executor));
|
|
}
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
record PendingReload(ReloadableTexture texture, CompletableFuture<TextureContents> newContents) {
|
|
}
|
|
}
|