File size: 8,024 Bytes
d46f4a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package net.minecraft.client.renderer.texture;

import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.systems.RenderSystem;
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.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 net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.slf4j.Logger;

@OnlyIn(Dist.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 p_118474_) {
        this.resourceManager = p_118474_;
        NativeImage nativeimage = MissingTextureAtlasSprite.generateMissingImage();
        this.register(MissingTextureAtlasSprite.getLocation(), new DynamicTexture(nativeimage));
    }

    public void registerAndLoad(ResourceLocation p_377323_, ReloadableTexture p_376843_) {
        try {
            p_376843_.apply(this.loadContentsSafe(p_377323_, p_376843_));
        } catch (Throwable throwable) {
            CrashReport crashreport = CrashReport.forThrowable(throwable, "Uploading texture");
            CrashReportCategory crashreportcategory = crashreport.addCategory("Uploaded texture");
            crashreportcategory.setDetail("Resource location", p_376843_.resourceId());
            crashreportcategory.setDetail("Texture id", p_377323_);
            throw new ReportedException(crashreport);
        }

        this.register(p_377323_, p_376843_);
    }

    private TextureContents loadContentsSafe(ResourceLocation p_378160_, ReloadableTexture p_378623_) {
        try {
            return loadContents(this.resourceManager, p_378160_, p_378623_);
        } catch (Exception exception) {
            LOGGER.error("Failed to load texture {} into slot {}", p_378623_.resourceId(), p_378160_, exception);
            return TextureContents.createMissing();
        }
    }

    public void registerForNextReload(ResourceLocation p_377796_) {
        this.register(p_377796_, new SimpleTexture(p_377796_));
    }

    public void register(ResourceLocation p_118496_, AbstractTexture p_118497_) {
        AbstractTexture abstracttexture = this.byPath.put(p_118496_, p_118497_);
        if (abstracttexture != p_118497_) {
            if (abstracttexture != null) {
                this.safeClose(p_118496_, abstracttexture);
            }

            if (p_118497_ instanceof Tickable tickable) {
                this.tickableTextures.add(tickable);
            }
        }
    }

    private void safeClose(ResourceLocation p_118509_, AbstractTexture p_118510_) {
        this.tickableTextures.remove(p_118510_);

        try {
            p_118510_.close();
        } catch (Exception exception) {
            LOGGER.warn("Failed to close texture {}", p_118509_, exception);
        }

        p_118510_.releaseId();
    }

    public AbstractTexture getTexture(ResourceLocation p_118507_) {
        AbstractTexture abstracttexture = this.byPath.get(p_118507_);
        if (abstracttexture != null) {
            return abstracttexture;
        } else {
            SimpleTexture simpletexture = new SimpleTexture(p_118507_);
            this.registerAndLoad(p_118507_, simpletexture);
            return simpletexture;
        }
    }

    @Override
    public void tick() {
        for (Tickable tickable : this.tickableTextures) {
            tickable.tick();
        }
    }

    public void release(ResourceLocation p_118514_) {
        AbstractTexture abstracttexture = this.byPath.remove(p_118514_);
        if (abstracttexture != null) {
            this.safeClose(p_118514_, abstracttexture);
        }
    }

    @Override
    public void close() {
        this.byPath.forEach(this::safeClose);
        this.byPath.clear();
        this.tickableTextures.clear();
    }

    @Override
    public CompletableFuture<Void> reload(
        PreparableReloadListener.PreparationBarrier p_118476_, ResourceManager p_118477_, Executor p_118480_, Executor p_118481_
    ) {
        List<TextureManager.PendingReload> list = new ArrayList<>();
        this.byPath.forEach((p_374670_, p_374671_) -> {
            if (p_374671_ instanceof ReloadableTexture reloadabletexture) {
                list.add(scheduleLoad(p_118477_, p_374670_, reloadabletexture, p_118480_));
            }
        });
        return CompletableFuture.allOf(list.stream().map(TextureManager.PendingReload::newContents).toArray(CompletableFuture[]::new))
            .thenCompose(p_118476_::wait)
            .thenAcceptAsync(p_374677_ -> {
                AddRealmPopupScreen.updateCarouselImages(this.resourceManager);

                for (TextureManager.PendingReload texturemanager$pendingreload : list) {
                    texturemanager$pendingreload.texture.apply(texturemanager$pendingreload.newContents.join());
                }
            }, p_118481_);
    }

    public void dumpAllSheets(Path p_276129_) {
        if (!RenderSystem.isOnRenderThread()) {
            RenderSystem.recordRenderCall(() -> this._dumpAllSheets(p_276129_));
        } else {
            this._dumpAllSheets(p_276129_);
        }
    }

    private void _dumpAllSheets(Path p_276128_) {
        try {
            Files.createDirectories(p_276128_);
        } catch (IOException ioexception) {
            LOGGER.error("Failed to create directory {}", p_276128_, ioexception);
            return;
        }

        this.byPath.forEach((p_276101_, p_276102_) -> {
            if (p_276102_ instanceof Dumpable dumpable) {
                try {
                    dumpable.dumpContents(p_276101_, p_276128_);
                } catch (IOException ioexception1) {
                    LOGGER.error("Failed to dump texture {}", p_276101_, ioexception1);
                }
            }
        });
    }

    private static TextureContents loadContents(ResourceManager p_375654_, ResourceLocation p_378136_, ReloadableTexture p_377917_) throws IOException {
        try {
            return p_377917_.loadContents(p_375654_);
        } catch (FileNotFoundException filenotfoundexception) {
            if (p_378136_ != INTENTIONAL_MISSING_TEXTURE) {
                LOGGER.warn("Missing resource {} referenced from {}", p_377917_.resourceId(), p_378136_);
            }

            return TextureContents.createMissing();
        }
    }

    private static TextureManager.PendingReload scheduleLoad(
        ResourceManager p_377119_, ResourceLocation p_377352_, ReloadableTexture p_377978_, Executor p_376135_
    ) {
        return new TextureManager.PendingReload(p_377978_, CompletableFuture.supplyAsync(() -> {
            try {
                return loadContents(p_377119_, p_377352_, p_377978_);
            } catch (IOException ioexception) {
                throw new UncheckedIOException(ioexception);
            }
        }, p_376135_));
    }

    @OnlyIn(Dist.CLIENT)
    static record PendingReload(ReloadableTexture texture, CompletableFuture<TextureContents> newContents) {
    }
}