Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix path loading when working with optifine data. #94

Closed
wants to merge 1 commit into from
Closed
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
82 changes: 56 additions & 26 deletions src/main/java/net/dorianpb/cem/internal/util/CemFairy.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.*;

/** Helps with internal stuff, all you need to know is that it keeps track of the renderers and files */
public class CemFairy{
private static final Set<EntityType<? extends Entity>> supportedEntities = new HashSet<>();
private static final Set<EntityType<? extends Entity>> supportedEntities = new HashSet<>();
private static final Set<BlockEntityType<? extends BlockEntity>> supportedBlockEntities = new HashSet<>();
private static final Set<String> supportedOthers = new HashSet<>();
private static final Logger LOGGER = LogManager.getLogger("Custom Entity Models");
Expand Down Expand Up @@ -55,33 +53,65 @@ public static String getEntityNameFromId(Identifier identifier){
String id = identifier.toString();
return id.substring(id.lastIndexOf("/") + 1, id.lastIndexOf(".jem"));
}

//file stuff
public static Identifier transformPath(String path, Identifier location){
//relative to current folder
if(path.startsWith("./")){
return new Identifier(location.getNamespace(), location.getPath().substring(0, location.getPath().lastIndexOf('/') + 1) + path.substring(2));

public static Identifier transformPath(String path, Identifier location) {
var pathChunks = new LinkedList<>(Arrays.asList(path.split("/")));
var firstChunk = pathChunks.get(0);

// Remove the file name from the location path or trailing slash
var locationPath = new LinkedList<>(Arrays.asList(location.getPath().split("/")));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strips the end of the location path if it is a path to a file with extension, and removes trailing stashes.

var lastLocationChunk = locationPath.get(locationPath.size() - 1);
if (lastLocationChunk.equals("") || lastLocationChunk.matches(".+\\..+")) {
locationPath.remove(locationPath.size() - 1);
location = new Identifier(location.getNamespace(), String.join("/", locationPath));
}
//go up a folder
else if(path.startsWith("../")){
return transformPath(path.substring(3), new Identifier(location.getNamespace(), location.getPath().substring(0, location.getPath().lastIndexOf('/'))));

// Uses a explicit namespace
if (pathChunks.get(0).contains(":")) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handles loading from namespaces other than 'minecraft' or 'dorianpb'.

var nsAndChunk = firstChunk.split(":");
location = new Identifier(nsAndChunk[0], "");
firstChunk = nsAndChunk[1];
pathChunks.set(0, firstChunk);
}
//relative to "assets/dorianpb/cem"
else if(path.startsWith("~/")){
return new Identifier("dorianpb", "cem/" + path.substring(2));

// Specifies an absolute path
if (firstChunk.equals("")) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the path had a leading slash then start the path from the root of the location namespace.

location = new Identifier(location.getNamespace(), "");
pathChunks.removeFirst();
}
//relative to "assets/namespace/"
else if(path.chars().filter(ch -> ch == ':').count() == 1){
String path2 = path.substring(path.indexOf(":") + 1);
if(path2.startsWith("/")){
path2 = path2.replaceFirst("/", "");
}
return transformPath(path2, new Identifier(path.substring(0, path.indexOf(":")), ""));

// Specifies a relative path
else if (firstChunk.equals(".")) {
pathChunks.removeFirst();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the path is relative (starts with "./") then the path starts from the path of location.

}
//look for file in current folder
else{
return new Identifier(location.getNamespace(), location.getPath().substring(0, location.getPath().lastIndexOf('/') + 1) + path);

// Specifies an optifine folder or dorianpb folder path
else if (firstChunk.equals("~")) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit tricky here. If we are loading a resource from within the "dorianpb" namespace, then we follow the existing logic for paths starting with "", however in the event of handling an optifine resource pack, it is expected that "" refers to "minecraft/optifine" (excluding the cem folder). I double checked this logic by referring to Optifine source code.

pathChunks.removeFirst();
location = location.getNamespace().equals("dorianpb")
? new Identifier(location.getNamespace(), "cem")
: new Identifier("minecraft", "optifine");
}

// Move the location path up for each ".." chunk at the beginning of the path
else if (firstChunk.equals("..")) {
var basePathChunks = new LinkedList<>(Arrays.asList(location.getPath().split("/")));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the path starts with parent folder path expressions ("..") we walk up the path from parent to parent for each ".." chunk in the path. This is the expected behavior when working with *nix path expressions.

while (pathChunks.get(0).equals("..")) {
if (basePathChunks.size() == 0) { break; }
pathChunks.removeFirst();
basePathChunks.remove(basePathChunks.size() - 1);
}
location = new Identifier(location.getNamespace(), String.join("/", basePathChunks));

// Deal with Optifine's rather strange pathing behaviour
// When Optifine loads a texture path does not contain "/", it loads relative to the file.
} else if (location.getNamespace().equals("minecraft") && path.contains("/")) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was added because of some nuts behavior I found in the optifine source code. If a path contains slashes then the path is assumed to be relative to the namespace root. I assume you don't want to do with for cem data in the "dorianpb" namespace, so I only perform the path adjustment if we are in the "minecraft" namespace.

location = new Identifier(location.getNamespace(), "");
}

// Finalize the transformed path as a new Identifier
if (!location.getPath().equals("")) { pathChunks.addFirst(location.getPath()); }
return new Identifier(location.getNamespace(), String.join("/", pathChunks));
}

public static void postReadError(Exception exception, Identifier id){
Expand Down