Skip to content

Instantly share code, notes, and snippets.

@mworzala
Last active December 30, 2025 17:11
Show Gist options
  • Select an option

  • Save mworzala/3063965073a848ce04b380aab853f89e to your computer and use it in GitHub Desktop.

Select an option

Save mworzala/3063965073a848ce04b380aab853f89e to your computer and use it in GitHub Desktop.
package net.hollowcube.multipart.resourcepack;
import com.google.gson.JsonObject;
import net.kyori.adventure.key.Key;
/// Remaps resource pack keys from server data to the client. This should be used if you remap
/// resource pack entries inside a [ResourcePackWriter] implementation.
///
/// @see ResourcePackWriter#writeItem(Key, JsonObject)
public interface ResourcePackMapper {
/**
* A no-op mapper that does not change the key.
*/
static ResourcePackMapper noop() {
return (key) -> key;
}
Key remapItemKey(Key key);
}
package net.hollowcube.multipart.resourcepack;
import com.google.gson.JsonObject;
import net.kyori.adventure.key.Key;
import java.io.IOException;
import java.nio.file.Path;
/// Interface for writing resources into a resource pack-like structure.
///
/// May be implemented to do key remapping or any other required processing.
///
/// @see #direct(Path) direct(Path) for a reference implementation that writes to a resource pack directly.
public interface ResourcePackWriter {
/// Creates a writer that creates files in a vanilla resource pack structure.
///
/// This is the default writer implementation and does not require a [ResourcePackMapper].
///
/// @param resourcePackRoot The root directory of the resource pack, where the `assets` directory is located.
/// @return A new [ResourcePackWriter] that writes directly to the specified path.
static ResourcePackWriter direct(Path resourcePackRoot) {
return new DirectResourcePackWriter(resourcePackRoot);
}
/// Creates a new texture in the resource pack with the following content.
///
/// It is valid to return a different key which will be used to reference this texture in other data.
///
/// @param key The key for the texture, eg `my_server:item/my_model/my_texture.png`
/// @param data The data for the texture
/// @return The key for the texture, which may be different from the input key if
/// the resource pack has a different naming scheme.
Key writeTexture(Key key, byte[] data) throws IOException;
/// Create a new model in the resource pack with the following content.
///
/// It is valid to return a different key which will be used to reference this model in other data.
///
/// @param key The key for the model, eg `my_server:item/my_model/my_model.json`
/// @param model The model data in JSON format.
/// @return The key for the model, which may be different from the input key if
/// the resource pack has a different naming scheme.
Key writeModel(Key key, JsonObject model) throws IOException;
/// Create a new item model in the resource pack with the following content.
///
/// Remapping the key for this model is OK, however you must keep track of the mapping
/// of key into this function to the resulting key of the model on the client. Multipart
/// will request the remapped key via a [ResourcePackMapper] at runtime.
///
/// @param key The key for the item model, eg `my_server:item/my_model/my_item.json`
/// @param item The item model data in JSON format.
/// @return The key for the model, which may be different from the input key if
/// the resource pack has a different naming scheme.
/// @see ResourcePackMapper
Key writeItem(Key key, JsonObject item) throws IOException;
}
package net.hollowcube.multipart.demo;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.hollowcube.multipart.resourcepack.ResourcePackWriter;
import net.kyori.adventure.key.Key;
import net.minestom.server.MinecraftServer;
import net.minestom.server.utils.MajorMinorVersion;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipResourcePackWriter implements ResourcePackWriter, AutoCloseable {
private final ByteArrayOutputStream output = new ByteArrayOutputStream();
private final ZipOutputStream zipOutput = new ZipOutputStream(output);
private boolean closed = false;
public ZipResourcePackWriter(String packName) throws IOException {
final JsonObject packMeta = new JsonObject();
final JsonObject pack = new JsonObject();
pack.add("min_format", packVersion(MinecraftServer.RESOURCE_PACK_VERSION));
pack.add("max_format", packVersion(MinecraftServer.RESOURCE_PACK_VERSION));
pack.addProperty("description", packName);
packMeta.add("pack", pack);
writeEntry("pack.mcmeta", packMeta.toString().getBytes(StandardCharsets.UTF_8));
}
public byte[] collect() throws IOException {
if (!closed) zipOutput.close();
closed = true;
return output.toByteArray();
}
@Override
public Key writeTexture(Key key, byte[] data) throws IOException {
final var filePath = "assets/%s/textures/%s".formatted(key.namespace(), key.value());
writeEntry(filePath, data);
return key;
}
@Override
public Key writeModel(Key key, JsonObject model) throws IOException {
final var filePath = "assets/%s/models/%s.json".formatted(key.namespace(), key.value());
writeEntry(filePath, model.toString().getBytes(StandardCharsets.UTF_8));
return key;
}
@Override
public Key writeItem(Key key, JsonObject item) throws IOException {
final var filePath = "assets/%s/items/%s.json".formatted(key.namespace(), key.value());
writeEntry(filePath, item.toString().getBytes(StandardCharsets.UTF_8));
return key;
}
@Override
public void close() throws Exception {
if (!closed) zipOutput.close();
closed = true;
}
private void writeEntry(String path, byte[] data) throws IOException {
zipOutput.putNextEntry(new ZipEntry(path));
zipOutput.write(data);
zipOutput.closeEntry();
}
private JsonElement packVersion(MajorMinorVersion version) {
final var array = new JsonArray();
array.add(version.major());
array.add(version.minor());
return array;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment