Skip to content

Commit

Permalink
Behold... weird things
Browse files Browse the repository at this point in the history
  • Loading branch information
Matyrobbrt committed Nov 5, 2024
1 parent 44d7e3f commit ff39e65
Show file tree
Hide file tree
Showing 31 changed files with 814 additions and 70 deletions.
5 changes: 5 additions & 0 deletions common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ dependencies {
annotationProcessor group: 'io.github.llamalad7', name: 'mixinextras-common', version: '0.3.5'

compileOnly("mezz.jei:jei-${minecraft_version}-common-api:${jei_version}")

compileOnly('org.ow2.asm:asm:9.6')
compileOnly('org.ow2.asm:asm-tree:9.6')
compileOnly('org.ow2.asm:asm-util:9.6')
compileOnly('org.ow2.asm:asm-commons:9.6')
}

configurations {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ private static IIOMetadataNode getNode(IIOMetadataNode rootNode, String nodeName

public void writeToSequence(NativeImage image) throws IOException {
writeToSequence(ImageIO.read(new ByteArrayInputStream(image.asByteArray())));
image.close();
}

public void writeToSequence(RenderedImage img) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ public static CompletableFuture<File> save(List<NativeImage> frames, File imageF
}

public static CompletableFuture<File> save(NativeImage image, File imageFile) {
return save(image::writeToFile, imageFile);
return save(out -> {
image.writeToFile(out);
image.close();
}, imageFile);
}

public static CompletableFuture<File> save(IoWriter image, File imageFile) {
Expand Down
68 changes: 45 additions & 23 deletions common/src/main/java/org/sinytra/assetexport/CommonClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraft.util.thread.ReentrantBlockableEventLoop;
import org.sinytra.assetexport.dumper.AssetDump;
import org.sinytra.assetexport.dumper.Identifiable;
import org.sinytra.assetexport.dumper.IdentifiableSelector;
import org.sinytra.assetexport.dumper.IdentifiableType;
import org.sinytra.assetexport.platform.Services;

import java.io.File;
Expand All @@ -15,7 +17,7 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
Expand All @@ -25,64 +27,84 @@
public class CommonClass {
public static final String CONFIG_FILE = "item_asset_export.render.config.file";
public static final String OUTPUT_PROPERTY = "item_asset_export.render.output";
public static boolean render;
public static Runnable mainThread;
public static Thread mainTh;

public static void runRender() {
public static void startRender() {
if (shouldRender()) {
try {
String outputProperty = System.getProperty(OUTPUT_PROPERTY);
Path path = outputProperty != null ? Path.of(outputProperty) : Services.PLATFORM.getGameDirectory();
render = true;
}
}

var dumps = getDumps();
for (int i = 0; i < dumps.size(); i++) {
var dump = dumps.get(i);
Constants.LOG.info("Running dump {} ({} selecting {})", i, dump, dump.selectors().stream()
.map(Object::toString).collect(Collectors.joining(", ")));
public static void queueTasks(ReentrantBlockableEventLoop<Runnable> thread) {
try {
String outputProperty = System.getProperty(OUTPUT_PROPERTY);
Path path = outputProperty != null ? Path.of(outputProperty) : Services.PLATFORM.getGameDirectory();

runDump(path, dump);
var dumps = getDumps();
for (int i = 0; i < dumps.size(); i++) {
var dump = dumps.get(i);
Constants.LOG.info("Running dump {} ({} selecting {})", i, dump, dump.selectors().stream()
.map(Object::toString).collect(Collectors.joining(", ")));

Constants.LOG.info("Finished dump {}", i);
}
runDump(dump.outputLocation().map(path::resolve).orElse(path), (AssetDump) dump, thread);

Constants.LOG.info("Render complete, shutting down");
Minecraft.getInstance().stop();
} catch (IOException e) {
throw new RuntimeException("Failed to read dumps file", e);
Constants.LOG.info("Finished dump {}", i);
}

Constants.LOG.info("Render complete in {}ms, shutting down", System.currentTimeMillis() - ProgressTracker.start);
Minecraft.getInstance().stop();
} catch (IOException e) {
throw new RuntimeException("Failed to read dumps file", e);
}
}

public static boolean shouldRender() {
return System.getProperty(CONFIG_FILE) != null;
}

private static List<AssetDump<?>> getDumps() throws IOException {
private static List<AssetDump<?, ?>> getDumps() throws IOException {
var file = Path.of(System.getProperty(CONFIG_FILE));
var dumps = AssetDump.CODEC.listOf()
.decode(JsonOps.INSTANCE, GsonHelper.parseArray(Files.readString(file)))
.getOrThrow();
return dumps.getFirst();
}

private static <T extends Identifiable> void runDump(Path basePath, AssetDump<T> dump) {
var selected = new HashSet<T>();
private static <Z extends Identifiable<Z>, T extends IdentifiableType<Z>> void runDump(Path basePath, AssetDump<T, Z> dump, ReentrantBlockableEventLoop<Runnable> thread) {
var selected = new HashMap<T, Z>();
var universe = dump.type().getSource();
for (IdentifiableSelector selector : dump.selectors()) {
selector.select(universe, selected);
}

selected.removeIf(Predicate.not(dump.type()::canDump));
selected.values().removeIf(Predicate.not(dump.type()::canDump));
if (selected.isEmpty()) return;

Function<ResourceLocation, Path> pathFunction = location -> basePath.resolve(location.getNamespace() + "/" + location.getPath());

ProgressTracker.generated = new ProgressTracker.Counter(selected.size());
ProgressTracker.dumped = new ProgressTracker.Counter(selected.size());
ProgressTracker.start = System.currentTimeMillis();

Constants.LOG.info("Dumping {} objects...", selected.size());

var output = Collections.synchronizedList(new ArrayList<CompletableFuture<File>>());
CompletableFuture.allOf(selected.stream().map(s -> Minecraft.getInstance().submit(() ->
dump.type().dump(pathFunction, s, output::add)))
CompletableFuture.allOf(selected.values().stream().map(s -> thread.submit(() ->
{
ProgressTracker.currentRender = s;
dump.type().dump(pathFunction, s, fu -> output.add(fu.thenApply(f -> {
ProgressTracker.dumped.increment();
return f;
})));
ProgressTracker.generated.increment();
}))
.toArray(CompletableFuture[]::new)).join();

CompletableFuture.allOf(output.toArray(CompletableFuture[]::new)).join();

ProgressTracker.currentRender = null;
}

}
26 changes: 26 additions & 0 deletions common/src/main/java/org/sinytra/assetexport/ProgressTracker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.sinytra.assetexport;

import org.sinytra.assetexport.dumper.Identifiable;

import java.util.concurrent.atomic.AtomicInteger;

public class ProgressTracker {
public static long start;
public static Identifiable<?> currentRender;
public static Counter generated;
public static Counter dumped;

public record Counter(AtomicInteger done, int target) {
public Counter(int target) {
this(new AtomicInteger(), target);
}

public void increment() {
done.incrementAndGet();
}

public float get() {
return (float)done.get() / target;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import com.mojang.serialization.codecs.RecordCodecBuilder;

import java.util.List;
import java.util.Optional;

public record AssetDump<T extends Identifiable>(AssetDumper<T> type, List<IdentifiableSelector> selectors) {
public static final Codec<AssetDump<?>> CODEC = RecordCodecBuilder.create(in -> in.group(
public record AssetDump<T extends IdentifiableType<Z>, Z extends Identifiable<Z>>(AssetDumper<T, Z> type, List<IdentifiableSelector> selectors, Optional<String> outputLocation) {
public static final Codec<AssetDump<?, ?>> CODEC = RecordCodecBuilder.create(in -> in.group(
AssetDumper.CODEC.fieldOf("dumper").forGetter(AssetDump::type),
IdentifiableSelector.CODEC.listOf().fieldOf("selectors").forGetter(AssetDump::selectors)
IdentifiableSelector.CODEC.listOf().fieldOf("selectors").forGetter(AssetDump::selectors),
Codec.STRING.optionalFieldOf("location").forGetter(AssetDump::outputLocation)
).apply(in, AssetDump::new));
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@
import java.util.function.Consumer;
import java.util.function.Function;

public interface AssetDumper<T extends Identifiable> {
BiMap<String, MapCodec<? extends AssetDumper<?>>> REGISTRY = ImmutableBiMap.of(
public interface AssetDumper<T extends IdentifiableType<Z>, Z extends Identifiable<Z>> {
BiMap<String, MapCodec<? extends AssetDumper<?, ?>>> REGISTRY = ImmutableBiMap.of(
"item", ItemAssetDumper.CODEC
);

Codec<AssetDumper<?>> CODEC = Codec.STRING.dispatch(
Codec<AssetDumper<?, ?>> CODEC = Codec.STRING.dispatch(
"type",
d -> REGISTRY.inverse().get(d.codec()),
REGISTRY::get
);

ObjectSource<T> getSource();
ObjectSource<T, Z> getSource();

void dump(Function<ResourceLocation, Path> file, T object, Consumer<CompletableFuture<File>> out);
void dump(Function<ResourceLocation, Path> file, Z object, Consumer<CompletableFuture<File>> out);

boolean canDump(T object);
boolean canDump(Z object);

MapCodec<? extends AssetDumper<T>> codec();
MapCodec<? extends AssetDumper<T, Z>> codec();
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package org.sinytra.assetexport.dumper;

import net.minecraft.resources.ResourceLocation;
public interface Identifiable<T extends Identifiable<T>> {
default IdentifiableType<T> getIdentifiableType() {
return null;
}

public interface Identifiable {
// TODO - we're injecting this interface and it could conflict... but it's just in dev so maybe not
default ResourceLocation getId() {
default String representAsString() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public interface IdentifiableSelector {
BiMap<String, MapCodec<? extends IdentifiableSelector>> REGISTRY = ImmutableBiMap.of(
"all", All.CODEC,
"exclude", Exclude.CODEC,
"fixed", Fixed.CODEC,
"with_namespace", WithNamespace.CODEC
);

Expand All @@ -23,16 +26,18 @@ public interface IdentifiableSelector {
REGISTRY::get
);

<T extends Identifiable> void select(ObjectSource<T> selector, Set<T> output);
<Z extends Identifiable<Z>, T extends IdentifiableType<Z>> void select(ObjectSource<T, Z> selector, Map<T, Z> output);

MapCodec<? extends IdentifiableSelector> codec();

record All() implements IdentifiableSelector {
public static final MapCodec<All> CODEC = MapCodec.unit(All::new);

@Override
public <T extends Identifiable> void select(ObjectSource<T> selector, Set<T> output) {
output.addAll(selector.getAll());
public <Z extends Identifiable<Z>, T extends IdentifiableType<Z>> void select(ObjectSource<T, Z> selector, Map<T, Z> output) {
for (T t : selector.getAll()) {
output.put(t, t.defaultIdentifiable());
}
}

@Override
Expand All @@ -46,8 +51,26 @@ record Exclude(Set<ResourceLocation> ids) implements IdentifiableSelector {
.listOf().fieldOf("ids").xmap(l -> new Exclude(new HashSet<>(l)), exclude -> new ArrayList<>(exclude.ids()));

@Override
public <T extends Identifiable> void select(ObjectSource<T> selector, Set<T> output) {
output.removeIf(o -> ids.contains(o.getId()));
public <Z extends Identifiable<Z>, T extends IdentifiableType<Z>> void select(ObjectSource<T, Z> selector, Map<T, Z> output) {
output.keySet().removeIf(k -> ids.contains(k.getId()));
}

@Override
public MapCodec<? extends IdentifiableSelector> codec() {
return CODEC;
}
}

record Fixed(List<ResourceLocation> ids) implements IdentifiableSelector {
public static final MapCodec<Fixed> CODEC = ResourceLocation.CODEC
.listOf().fieldOf("ids").xmap(Fixed::new, Fixed::ids);

@Override
public <Z extends Identifiable<Z>, T extends IdentifiableType<Z>> void select(ObjectSource<T, Z> selector, Map<T, Z> output) {
for (ResourceLocation id : ids) {
var type = selector.byId(id);
output.put(type, type.defaultIdentifiable());
}
}

@Override
Expand All @@ -61,9 +84,11 @@ record WithNamespace(String namespace) implements IdentifiableSelector {
.fieldOf("namespace").xmap(WithNamespace::new, WithNamespace::namespace);

@Override
public <T extends Identifiable> void select(ObjectSource<T> selector, Set<T> output) {
public <Z extends Identifiable<Z>, T extends IdentifiableType<Z>> void select(ObjectSource<T, Z> selector, Map<T, Z> output) {
for (T t : selector.getAll()) {
if (t.getId().getNamespace().equals(namespace)) output.add(t);
if (t.getId().getNamespace().equals(namespace)) {
output.put(t, t.defaultIdentifiable());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.sinytra.assetexport.dumper;

import net.minecraft.resources.ResourceLocation;

public interface IdentifiableType<T extends Identifiable<T>> {
// TODO - we're injecting this interface and it could conflict... but it's just in dev so maybe not
default ResourceLocation getId() {
return null;
}

default T defaultIdentifiable() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

import java.util.Collection;

public interface ObjectSource<T extends Identifiable> {
public interface ObjectSource<T extends IdentifiableType<Z>, Z extends Identifiable<Z>> {
T byId(ResourceLocation id);

Collection<T> getAll();

static <T extends Identifiable> ObjectSource<T> fromRegistry(Registry<T> reg) {
static <Z extends Identifiable<Z>, T extends IdentifiableType<Z>> ObjectSource<T, Z> fromRegistry(Registry<T> reg) {
return new ObjectSource<>() {
@Override
public T byId(ResourceLocation id) {
Expand Down
Loading

0 comments on commit ff39e65

Please sign in to comment.