Skip to content

Commit e9a064d

Browse files
committed
Move to .patch.json system
1 parent 6a77da2 commit e9a064d

23 files changed

+208
-194
lines changed

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ Download: [GeyserOptionalPack.mcpack](https://download.geysermc.org/v2/projects/
2323
### Manually compiling the pack
2424

2525
1. Clone the repo to your computer
26-
2. Run `gradlew build`.
27-
3. Run the pack compiler using `java -jar build/libs/GeyserOptionalPackCompiler.jar`
28-
4. When it finishes compiling, it will output the `GeyserOptionalPack.mcpack`.
26+
2. Run `gradlew run`.
27+
3. When it finishes compiling, it will output the `GeyserOptionalPack.mcpack`.
2928

3029
### Legal
3130

developer_documentation.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ The GeyserOptionalPack is compiled using a program written in Java. It contains
2626

2727
Entity data and entity flags (known as queries in Molang) are pieces of metadata that store various pieces of information about an entity on the Bedrock Edition of Minecraft. You can query for an entity's health, for example (a number query or an entity data), and can query for if an entity is angry (an entity flag, which is either 1.0 or 0.0 in Molang). Not all entities use every query, but every entity has access to most queries, though Bedrock by default ignores these. These queries can be sent by Geyser and change how an entity looks. We use this to our advantage in this resource pack.
2828

29+
### Patches
30+
There is a system within the compiler to apply patches to the vanilla Bedrock json files. This is done by placing a `.patch.json` in the `patches` resource folder, with the same path as the file you want to patch. The patch file will be merged with the original file, replacing any existing keys. This is useful for small changes to vanilla files, such as adding an extra texture to an entity.
31+
32+
The source for these files is https://github.com/Mojang/bedrock-samples/tree/main/resource_pack with each patch being the same name and path as the original file but with `.json` replaced with `.patch.json`.
33+
2934
### Armor stands
3035

3136
#### Part visibility and rotation encoding

src/main/java/org/geysermc/optionalpack/BedrockResourcesWrapper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ public class BedrockResourcesWrapper {
66
private static final String BEDROCK_RESOURCES_URL = "https://raw.githubusercontent.com/Mojang/bedrock-samples/refs/tags/v" + Constants.BEDROCK_TARGET_VERSION + "/resource_pack/%s";
77

88
public static String getResourceAsString(String path) {
9-
return HTTP.getAsString(BEDROCK_RESOURCES_URL.formatted(path));
9+
return WebUtils.getAsString(BEDROCK_RESOURCES_URL.formatted(path));
1010
}
1111

1212
public static InputStream getResource(String path) {
13-
return HTTP.request(BEDROCK_RESOURCES_URL.formatted(path));
13+
return WebUtils.request(BEDROCK_RESOURCES_URL.formatted(path));
1414
}
1515
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package org.geysermc.optionalpack;
2+
3+
import java.io.File;
4+
import java.io.FileOutputStream;
5+
import java.io.IOException;
6+
import java.nio.file.FileVisitResult;
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
9+
import java.nio.file.SimpleFileVisitor;
10+
import java.nio.file.attribute.BasicFileAttributes;
11+
import java.util.zip.ZipEntry;
12+
import java.util.zip.ZipOutputStream;
13+
14+
public class FileUtils {
15+
/**
16+
* Delete a directory and all files within it
17+
* From: https://www.geeksforgeeks.org/java/java-program-to-delete-a-directory/
18+
*
19+
* @param directory The directory to remove
20+
*/
21+
public static void deleteDirectory(File directory) {
22+
File[] files = directory.listFiles();
23+
if (files != null) {
24+
for (File subfile : directory.listFiles()) {
25+
if (subfile.isDirectory()) {
26+
deleteDirectory(subfile);
27+
}
28+
subfile.delete();
29+
}
30+
}
31+
32+
directory.delete();
33+
}
34+
35+
/**
36+
* @see #deleteDirectory(File)
37+
*/
38+
public static void deleteDirectory(Path directory) {
39+
deleteDirectory(directory.toFile());
40+
}
41+
42+
/**
43+
* Zip a folder
44+
* From: https://stackoverflow.com/a/57997601
45+
*
46+
* @param sourceFolderPath Folder to zip
47+
* @param zipPath Output path for the zip
48+
*/
49+
public static void zipFolder(Path sourceFolderPath, Path zipPath) throws Exception {
50+
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipPath.toFile()));
51+
Files.walkFileTree(sourceFolderPath, new SimpleFileVisitor<>() {
52+
@Override
53+
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
54+
zos.putNextEntry(new ZipEntry(sourceFolderPath.relativize(file).toString()));
55+
Files.copy(file, zos);
56+
zos.closeEntry();
57+
return FileVisitResult.CONTINUE;
58+
}
59+
});
60+
zos.close();
61+
}
62+
}

src/main/java/org/geysermc/optionalpack/JavaResources.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import javax.imageio.ImageIO;
2929
import java.awt.image.BufferedImage;
30+
import java.io.File;
3031
import java.io.IOException;
3132
import java.io.InputStream;
3233
import java.nio.charset.Charset;
@@ -60,12 +61,15 @@ public static void extract(ZipFile clientJar) {
6061
String assetFileName = Path.of(jarAssetPath).toFile().getName();
6162
Path destination = OptionalPack.WORKING_PATH.resolve(destinationPath).resolve(assetFileName);
6263

63-
if (destination.toFile().mkdirs()) {
64-
Files.copy(asset, destination, StandardCopyOption.REPLACE_EXISTING);
65-
}
66-
else {
67-
OptionalPack.log("Could not make directories for copying " + jarAssetPath + " to " + destinationPath + "!");
64+
File destinationFolder = OptionalPack.WORKING_PATH.resolve(destinationPath).toFile();
65+
if (!destinationFolder.exists()) {
66+
if (!destinationFolder.mkdirs()) {
67+
OptionalPack.log("Could not make directories for copying " + jarAssetPath + " to " + destinationPath + "!");
68+
continue;
69+
}
6870
}
71+
72+
Files.copy(asset, destination, StandardCopyOption.REPLACE_EXISTING);
6973
}
7074

7175
} catch (IOException e) {

src/main/java/org/geysermc/optionalpack/LauncherMetaWrapper.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.geysermc.optionalpack;
22

3-
import com.google.gson.Gson;
4-
53
import java.io.InputStream;
64
import java.nio.file.Files;
75
import java.nio.file.Path;
@@ -16,15 +14,15 @@ public class LauncherMetaWrapper {
1614
public static Path getLatest() {
1715
OptionalPack.log("Downloading " + Constants.JAVA_TARGET_VERSION + " client.jar from Mojang...");
1816

19-
VersionManifest versionManifest = Constants.GSON.fromJson(HTTP.getAsString(LAUNCHER_META_URL), VersionManifest.class);
17+
VersionManifest versionManifest = Constants.GSON.fromJson(WebUtils.getAsString(LAUNCHER_META_URL), VersionManifest.class);
2018

2119
for (Version version : versionManifest.versions()) {
2220
if (version.id().equals(Constants.JAVA_TARGET_VERSION)) {
23-
VersionInfo versionInfo = Constants.GSON.fromJson(HTTP.getAsString(version.url()), VersionInfo.class);
21+
VersionInfo versionInfo = Constants.GSON.fromJson(WebUtils.getAsString(version.url()), VersionInfo.class);
2422
VersionDownload client = versionInfo.downloads().get("client");
2523
if (!Files.exists(CLIENT_JAR) || !client.sha1.equals(getSha1(CLIENT_JAR))) {
2624
// Download the client jar
27-
try (InputStream in = HTTP.request(client.url())) {
25+
try (InputStream in = WebUtils.request(client.url())) {
2826
Files.copy(in, CLIENT_JAR);
2927
} catch (Exception e) {
3028
throw new RuntimeException("Could not download client jar", e);

src/main/java/org/geysermc/optionalpack/OptionalPack.java

Lines changed: 3 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public static void main(String[] args) {
7474

7575
log("Extracting pre-made optional pack data to folder...");
7676
// there are probably better ways to do this, but this is the way im doing it
77-
unzipPack(Resources.get("optionalpack"), WORKING_PATH);
77+
Resources.extractFolder("optionalpack", WORKING_PATH);
7878

7979
// Step 2: Download the 1.21.8 client.jar and copy all files needed to working folder
8080
File jarFile = LauncherMetaWrapper.getLatest().toFile();
@@ -96,12 +96,12 @@ public static void main(String[] args) {
9696

9797
// Step 4: Compile pack folder into a mcpack.
9898
log("Zipping as GeyserOptionalPack.mcpack...");
99-
zipFolder(WORKING_PATH, Path.of("GeyserOptionalPack.mcpack"));
99+
FileUtils.zipFolder(WORKING_PATH, Path.of("GeyserOptionalPack.mcpack"));
100100

101101
// Step 5: Cleanup temporary folders and files
102102
log("Clearing temporary files...");
103103
clientJar.close();
104-
deleteDirectory(WORKING_PATH.toFile());
104+
FileUtils.deleteDirectory(WORKING_PATH);
105105

106106
// Step 6: Finish!!
107107
DecimalFormat r3 = new DecimalFormat("0.000");
@@ -113,106 +113,6 @@ public static void main(String[] args) {
113113
}
114114
}
115115

116-
/**
117-
* Delete a directory and all files within it
118-
* From: https://www.geeksforgeeks.org/java/java-program-to-delete-a-directory/
119-
*
120-
* @param directory The directory to remove
121-
*/
122-
public static void deleteDirectory(File directory) {
123-
File[] files = directory.listFiles();
124-
if (files != null) {
125-
for (File subfile : directory.listFiles()) {
126-
if (subfile.isDirectory()) {
127-
deleteDirectory(subfile);
128-
}
129-
subfile.delete();
130-
}
131-
}
132-
133-
directory.delete();
134-
}
135-
136-
/**
137-
* Zip a folder
138-
* From: https://stackoverflow.com/a/57997601
139-
*
140-
* @param sourceFolderPath Folder to zip
141-
* @param zipPath Output path for the zip
142-
*/
143-
private static void zipFolder(Path sourceFolderPath, Path zipPath) throws Exception {
144-
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipPath.toFile()));
145-
Files.walkFileTree(sourceFolderPath, new SimpleFileVisitor<>() {
146-
@Override
147-
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
148-
zos.putNextEntry(new ZipEntry(sourceFolderPath.relativize(file).toString()));
149-
Files.copy(file, zos);
150-
zos.closeEntry();
151-
return FileVisitResult.CONTINUE;
152-
}
153-
});
154-
zos.close();
155-
}
156-
157-
/**
158-
* Extract a zip to a given directory
159-
*
160-
* @param file The zip to extract
161-
* @param destDir THe destination to put all the files
162-
*/
163-
private static void unzipPack(URL file, Path destDir) {
164-
File dir = destDir.toFile();
165-
// create output directory if it doesn't exist
166-
if (!dir.exists()) dir.mkdirs();
167-
168-
try {
169-
if (file.getProtocol().equals("file")) {
170-
Path resourceDir = Paths.get(file.toURI());
171-
Files.walk(resourceDir)
172-
.filter(Files::isRegularFile)
173-
.forEach(source -> {
174-
try {
175-
Path relative = resourceDir.relativize(source);
176-
Path target = destDir.resolve(relative);
177-
Files.createDirectories(target.getParent());
178-
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
179-
} catch (IOException e) {
180-
throw new UncheckedIOException(e);
181-
}
182-
});
183-
} else {
184-
byte[] buffer = new byte[1024];
185-
FileInputStream fileStream = new FileInputStream(new File(file.toURI()));
186-
ZipInputStream zipStream = new ZipInputStream(fileStream);
187-
ZipEntry entry = zipStream.getNextEntry();
188-
while (entry != null) {
189-
if (!entry.isDirectory()) {
190-
String fileName = entry.getName();
191-
File newFile = new File(destDir + File.separator + fileName);
192-
// create directories for subdirectories in zip
193-
new File(newFile.getParent()).mkdirs();
194-
FileOutputStream extractedFile = new FileOutputStream(newFile);
195-
int len;
196-
while ((len = zipStream.read(buffer)) > 0) {
197-
extractedFile.write(buffer, 0, len);
198-
}
199-
extractedFile.close();
200-
}
201-
// close this ZipEntry
202-
203-
zipStream.closeEntry();
204-
entry = zipStream.getNextEntry();
205-
}
206-
// close the last ZipEntry
207-
zipStream.closeEntry();
208-
zipStream.close();
209-
fileStream.close();
210-
}
211-
} catch (IOException | URISyntaxException e) {
212-
throw new RuntimeException("Unable to unzip pack!", e);
213-
}
214-
}
215-
216116
/**
217117
* Prints a message to the console.
218118
*

src/main/java/org/geysermc/optionalpack/Resources.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,21 @@
2727

2828
import javax.imageio.ImageIO;
2929
import java.awt.image.BufferedImage;
30+
import java.io.File;
31+
import java.io.FileInputStream;
32+
import java.io.FileOutputStream;
3033
import java.io.IOException;
3134
import java.io.InputStream;
35+
import java.io.UncheckedIOException;
36+
import java.net.URISyntaxException;
3237
import java.net.URL;
3338
import java.nio.charset.Charset;
39+
import java.nio.file.Files;
40+
import java.nio.file.Path;
41+
import java.nio.file.Paths;
42+
import java.nio.file.StandardCopyOption;
43+
import java.util.zip.ZipEntry;
44+
import java.util.zip.ZipInputStream;
3445

3546
public class Resources {
3647
/**
@@ -89,4 +100,64 @@ public static BufferedImage getAsImage(String resourcePath) throws IOException {
89100
is.close();
90101
return image;
91102
}
103+
104+
/**
105+
* Extract a resource folder to a given directory
106+
*
107+
* @param folder The resource folder to extract
108+
* @param destDir The destination to put all the files
109+
*/
110+
public static void extractFolder(String folder, Path destDir) {
111+
URL file = get(folder);
112+
File dir = destDir.toFile();
113+
// create output directory if it doesn't exist
114+
if (!dir.exists()) dir.mkdirs();
115+
116+
try {
117+
if (file.getProtocol().equals("file")) {
118+
Path resourceDir = Paths.get(file.toURI());
119+
Files.walk(resourceDir)
120+
.filter(Files::isRegularFile)
121+
.forEach(source -> {
122+
try {
123+
Path relative = resourceDir.relativize(source);
124+
Path target = destDir.resolve(relative);
125+
Files.createDirectories(target.getParent());
126+
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
127+
} catch (IOException e) {
128+
throw new UncheckedIOException(e);
129+
}
130+
});
131+
} else {
132+
byte[] buffer = new byte[1024];
133+
FileInputStream fileStream = new FileInputStream(new File(file.toURI()));
134+
ZipInputStream zipStream = new ZipInputStream(fileStream);
135+
ZipEntry entry = zipStream.getNextEntry();
136+
while (entry != null) {
137+
if (!entry.isDirectory()) {
138+
String fileName = entry.getName();
139+
File newFile = new File(destDir + File.separator + fileName);
140+
// create directories for subdirectories in zip
141+
new File(newFile.getParent()).mkdirs();
142+
FileOutputStream extractedFile = new FileOutputStream(newFile);
143+
int len;
144+
while ((len = zipStream.read(buffer)) > 0) {
145+
extractedFile.write(buffer, 0, len);
146+
}
147+
extractedFile.close();
148+
}
149+
// close this ZipEntry
150+
151+
zipStream.closeEntry();
152+
entry = zipStream.getNextEntry();
153+
}
154+
// close the last ZipEntry
155+
zipStream.closeEntry();
156+
zipStream.close();
157+
fileStream.close();
158+
}
159+
} catch (IOException | URISyntaxException e) {
160+
throw new RuntimeException("Unable to unzip pack!", e);
161+
}
162+
}
92163
}

src/main/java/org/geysermc/optionalpack/HTTP.java renamed to src/main/java/org/geysermc/optionalpack/WebUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import java.net.*;
3131
import java.nio.charset.StandardCharsets;
3232

33-
public class HTTP {
33+
public class WebUtils {
3434

3535
/**
3636
* Requests a URL and returns the InputStream.

0 commit comments

Comments
 (0)