Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ dependencies {
exclude group: "com.viaversion.mcstructs", module: "snbt"
exclude group: "com.viaversion.mcstructs", module: "converter"
}
api "org.apache.commons:commons-compress:1.28.0"
implementation "commons-io:commons-io:2.20.0"
api "com.vdurmont:semver4j:3.1.0"
api "com.mojang:brigadier:1.3.10"
api "at.yawk.lz4:lz4-java:1.12.0"
Expand All @@ -101,6 +103,16 @@ dependencies {
toolImplementation "org.jsoup:jsoup:1.23.2"
toolImplementation "org.ow2.asm:asm-tree:9.11"
toolImplementation "net.lenni0451.commons:asm:1.9.2"

testImplementation "com.viaversion:viaversion-common:5.12.0"
testImplementation "com.google.guava:guava:33.7.1-jre"
testImplementation "io.netty:netty-handler:4.2.18.Final"
testImplementation "org.junit.jupiter:junit-jupiter:6.1.3"
testRuntimeOnly "org.junit.platform:junit-platform-launcher:6.1.3"
}

test {
useJUnitPlatform()
}

def registerToolTask(String name, String toolMainClass, String toolDescription) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ private void spawn() {
final StructuredItem item = new StructuredItem(BedrockProtocol.MAPPINGS.getJavaItems().get("minecraft:paper"), 1, data);
javaEntityData.add(new EntityData(partEntity.getJavaEntityDataIndex(EntityDataFields.ITEM_STACK), VersionedTypes.V26_3.entityDataTypes.itemType, item));

final float scale = (float) resourcePackStorage.getConverterData().get("ce_" + converterKey + "_scale");
final float scale = ((Number) resourcePackStorage.getConverterData().get("ce_" + converterKey + "_scale")).floatValue();
javaEntityData.add(new EntityData(partEntity.getJavaEntityDataIndex(EntityDataFields.SCALE), VersionedTypes.V26_3.entityDataTypes.vector3FType, new Vector3f(scale, scale, scale)));
javaEntityData.add(new EntityData(partEntity.getJavaEntityDataIndex(EntityDataFields.TRANSLATION), VersionedTypes.V26_3.entityDataTypes.vector3FType, new Vector3f(0F, scale * 0.5F, 0F)));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock
* Copyright (C) 2026 RK_01/RaphiMC and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package net.raphimc.viabedrock.api.resourcepack;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/** Bounded background work that never falls back to the connection's event loop. */
public final class ResourcePackExecutor {

private final ThreadPoolExecutor executor;
private final Set<CompletableFuture<?>> tasks = new HashSet<>();

public ResourcePackExecutor(final int workers, final int queuedTasks, final String threadName) {
final int threads = Math.max(1, Math.min(workers, Runtime.getRuntime().availableProcessors()));
this.executor = new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(queuedTasks), task -> {
final Thread thread = new Thread(task, threadName);
thread.setDaemon(true);
return thread;
}, new ThreadPoolExecutor.AbortPolicy());
}

public synchronized <T> CompletableFuture<T> submit(final Supplier<T> work) {
final CompletableFuture<T> result = new CompletableFuture<>();
this.tasks.add(result);
result.whenComplete((value, error) -> {
synchronized (this) {
this.tasks.remove(result);
}
});
try {
this.executor.execute(() -> {
if (result.isDone()) {
return;
}
try {
result.complete(work.get());
} catch (final Throwable error) {
result.completeExceptionally(error);
}
});
} catch (final RejectedExecutionException error) {
result.completeExceptionally(error);
}
return result;
}

public void stop() {
final Set<CompletableFuture<?>> waiting;
synchronized (this) {
this.executor.shutdownNow();
waiting = Set.copyOf(this.tasks);
}
for (final CompletableFuture<?> task : waiting) {
task.completeExceptionally(new CancellationException("Resource pack executor stopped"));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,25 @@
import com.viaversion.viaversion.util.GsonUtil;
import net.raphimc.viabedrock.api.util.JsonUtil;

import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public abstract class Content {

private final Map<String, Map<String, String>> langCache = new HashMap<>();
private final Set<String> storedZipEntries = new HashSet<>();

private final Map<String, Map<String, String>> langCache = new ConcurrentHashMap<>();

public abstract List<String> getFilesShallow(final String path, final String extension);

Expand All @@ -59,7 +62,24 @@ public String getFullPath(final String shortNamePath, final String... extensions

public abstract byte[] get(final String path);

public abstract boolean put(final String path, final byte[] data);
public final boolean put(final String path, final byte[] data) {
final boolean replaced = this.putBytes(path, data);
this.storedZipEntries.remove(path);
return replaced;
}

/** Stores an already compressed payload without another deflation pass when exporting the ZIP. */
public final boolean putStored(final String path, final byte[] data) {
final boolean replaced = this.putBytes(path, data);
this.storedZipEntries.add(path);
return replaced;
}

public final boolean isStored(final String path) {
return this.storedZipEntries.contains(path);
}

protected abstract boolean putBytes(final String path, final byte[] data);

public String getString(final String path) {
final byte[] bytes = this.get(path);
Expand Down Expand Up @@ -157,22 +177,22 @@ public void putAll(final Content content) {
}

public void copyFrom(final Content content, final String sourcePath, final String targetPath) {
this.put(targetPath, content.get(sourcePath));
if (content.isStored(sourcePath)) {
this.putStored(targetPath, content.get(sourcePath));
} else {
this.put(targetPath, content.get(sourcePath));
}
}

public byte[] toZip() throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream(4 * 1024 * 1024);
final ZipOutputStream zipOutputStream = new ZipOutputStream(baos);
zipOutputStream.setLevel(Deflater.BEST_SPEED);
for (String path : this.getFilesDeep("", "")) {
final ZipEntry entry = new ZipEntry(path);
entry.setTime(0L);
zipOutputStream.putNextEntry(entry);
zipOutputStream.write(this.get(path));
zipOutputStream.closeEntry();
}
zipOutputStream.close();
return baos.toByteArray();
final var output = UnsynchronizedByteArrayOutputStream.builder().setBufferSize(4 * 1024 * 1024).get();
this.writeZip(output);
return output.toByteArray();
}

/** Writes the deterministic archive and closes the supplied output. */
public void writeZip(final OutputStream output) throws IOException {
ResourcePackZipWriter.write(this, output);
}

public static class LazyImage {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public byte[] get(final String path) {
}

@Override
public boolean put(final String path, final byte[] data) {
protected boolean putBytes(final String path, final byte[] data) {
final boolean exists = this.contains(path);
try {
Files.write(this.resolvePath(this.dir, Path.of(path)), data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public byte[] get(final String path) {
}

@Override
public boolean put(final String path, final byte[] data) {
protected boolean putBytes(final String path, final byte[] data) {
return this.content.put(path, data) != null;
}

Expand Down
Loading