Commit of all current code.

This commit is contained in:
2024-11-29 11:28:59 -07:00
commit be5245dcfb
16 changed files with 1037 additions and 0 deletions
@@ -0,0 +1,255 @@
package dev.bigduckie.staticdata;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import com.google.common.collect.ImmutableSet;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.minecraft.util.Identifier;
@ParametersAreNonnullByDefault
public class StaticData {
public static final String GLOBAL_DATA_NAMESPACE = "g";
//"global_static_data_with_a_long_name_to_eliminate_name_collisions";
// ^ This long namespace was a fantastic idea that looks terrible in log statements and
// debug strings. It will not be used, but is archived here for posterity.
/**
* Gets all data available. All of it. You should probably use a different method instead. The data
* is returned in no particular order.
*/
/* The data within each mod is returned in depth-first order at the moment but that can change at any time */
@Nonnull
public static ImmutableSet<StaticDataItem> getAll() {
ImmutableSet.Builder<StaticDataItem> builder = ImmutableSet.builder();
for (ModContainer container : FabricLoader.getInstance().getAllMods()) {
Optional<Path> staticDataPath = container.findPath("static_data");
if (staticDataPath.isPresent() && Files.isDirectory(staticDataPath.get())) {
try (Stream<Path> files = Files.walk(staticDataPath.orElse(null))) {
files.forEach((it) -> {
if (Files.isDirectory(it)) return;
builder.add(new StaticDataItem(toIdentifier(container, it), it));
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
Path globalStaticDataFolder = new File(FabricLoader.getInstance().getGameDir().toFile(), "static_data").toPath();
if (Files.isDirectory(globalStaticDataFolder)) {
try (Stream<Path> files = Files.walk(globalStaticDataFolder)) {
files.forEach((it) -> {
if (Files.isDirectory(it)) return;
builder.add(new StaticDataItem(toGlobalIdentifier(globalStaticDataFolder, it), it));
});
} catch (IOException e) {
e.printStackTrace();
}
}
return builder.build();
}
/**
* Gets all data with the specified name from any mod that offers it. The data is returned in no particular order.
*/
@Nonnull
public static ImmutableSet<StaticDataItem> getAll(String name) {
ImmutableSet.Builder<StaticDataItem> builder = ImmutableSet.builder();
for (ModContainer container : FabricLoader.getInstance().getAllMods()) {
Optional<Path> staticDataPath = container.findPath("static_data");
if (staticDataPath.isPresent() && Files.isDirectory(staticDataPath.get())) {
Path data = staticDataPath.get().resolve(name);
if (Files.exists(data) && !Files.isDirectory(data)) {
builder.add(new StaticDataItem(toIdentifier(container, data), data));
}
}
}
Path globalStaticDataFolder = new File(FabricLoader.getInstance().getGameDir().toFile(), "static_data").toPath();
if (Files.isDirectory(globalStaticDataFolder)) {
Path data = globalStaticDataFolder.resolve(name);
if (Files.exists(data) && !Files.isDirectory(data)) {
builder.add(new StaticDataItem(toGlobalIdentifier(globalStaticDataFolder, data), data));
}
}
return builder.build();
}
/**
* Lists all data in the specified directory from any mod that offers it.
*
* @param dirname the name of a directory within the "static_data" folder. Can be nested, e.g. "definitions/blocks"
*/
@Nonnull
public static ImmutableSet<StaticDataItem> getAllInDirectory(String dirname) {
ImmutableSet.Builder<StaticDataItem> builder = ImmutableSet.builder();
for (ModContainer container : FabricLoader.getInstance().getAllMods()) {
Optional<Path> staticDataPath = container.findPath("static_data");
if (staticDataPath.isPresent() && Files.isDirectory(staticDataPath.get())) {
Path dataDir = staticDataPath.get().resolve(dirname);
if (Files.isDirectory(dataDir)) {
try (Stream<Path> files = Files.walk(dataDir)) {
files.forEach((it) -> {
if (Files.isDirectory(it)) return;
builder.add(new StaticDataItem(toIdentifier(container, it), it));
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Path globalStaticDataFolder = new File(FabricLoader.getInstance().getGameDir().toFile(), "static_data").toPath();
if (Files.isDirectory(globalStaticDataFolder)) {
Path datadir = globalStaticDataFolder.resolve(dirname);
if (Files.isDirectory(datadir)) {
try (Stream<Path> files = Files.walk(datadir)) {
files.forEach((it) -> {
if (Files.isDirectory(it)) return;
builder.add(new StaticDataItem(toGlobalIdentifier(globalStaticDataFolder, it), it));
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
return builder.build();
}
/**
* Gets a single DataItem from the specified mod
*
* @param modid the mod to search for data
* @param name the exact filename (e.g. "test/test.json") of the file inside the static_data folder
* @return the DataItem, or Optional.EMPTY if the data was not found.
*/
@Nonnull
public static Optional<StaticDataItem> get(String modid, String name) {
if (modid.equals(GLOBAL_DATA_NAMESPACE)) {
Path globalStaticDataFolder = new File(FabricLoader.getInstance().getGameDir().toFile(), "static_data").toPath();
if (Files.isDirectory(globalStaticDataFolder)) {
Path data = globalStaticDataFolder.resolve(name);
if (Files.exists(data) && !Files.isDirectory(data)) {
return Optional.of(new StaticDataItem(new Identifier(GLOBAL_DATA_NAMESPACE, name), data));
}
}
return Optional.empty();
}
Optional<ModContainer> containerOpt = FabricLoader.getInstance().getModContainer(modid);
if (containerOpt.isEmpty()) return Optional.empty();
return containerOpt.map(container -> {
Optional<Path> staticDataPath = container.findPath("static_data");
if (staticDataPath.isPresent() && Files.isDirectory(staticDataPath.get())) {
Path data = staticDataPath.get().resolve(name);
if (Files.exists(data)) {
return new StaticDataItem(toIdentifier(container, data), data);
}
}
return null; //gets optional-wrapped by map()
});
}
/**
* Identifier version of {@link #get(String, String)}
*/
@Nonnull
public static Optional<StaticDataItem> get(Identifier id) {
return get(id.getNamespace(), id.getPath());
}
/**
* Gets all data from the specified mod and directory.
*
* @param modid the mod to search for data
* @param dirname the directory to discover data in
* @return a list of data discovered
*/
@Nonnull
public static ImmutableSet<StaticDataItem> getInDirectory(String modid, String dirname) {
ImmutableSet.Builder<StaticDataItem> builder = ImmutableSet.builder();
if (modid.equals(GLOBAL_DATA_NAMESPACE)) {
Path globalStaticDataFolder = new File(FabricLoader.getInstance().getGameDir().toFile(), "static_data").toPath();
if (Files.isDirectory(globalStaticDataFolder)) {
Path datadir = globalStaticDataFolder.resolve(dirname);
if (Files.isDirectory(datadir)) {
try (Stream<Path> files = Files.walk(datadir)) {
files.forEach((it) -> {
if (Files.isDirectory(it)) return;
builder.add(new StaticDataItem(toGlobalIdentifier(globalStaticDataFolder, it), it));
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
return builder.build();
}
Optional<ModContainer> containerOpt = FabricLoader.getInstance().getModContainer(modid);
if (containerOpt.isEmpty()) return builder.build();
ModContainer container = containerOpt.get();
Optional<Path> staticDataPath = container.findPath("static_data");
if (staticDataPath.isPresent() && Files.isDirectory(staticDataPath.get())) {
Path datadir = staticDataPath.get().resolve(dirname);
if (Files.isDirectory(datadir)) {
try (Stream<Path> files = Files.walk(datadir)) {
files.forEach((it) -> {
if (Files.isDirectory(it)) return;
builder.add(new StaticDataItem(toIdentifier(container, it), it));
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
return builder.build();
}
/**
* Identifier version of {@link #getInDirectory(String, String)}
*/
@Nonnull
public static ImmutableSet<StaticDataItem> getInDirectory(Identifier id) {
return getInDirectory(id.getNamespace(), id.getPath());
}
private static String getRelative(Path parent, Path child) {
return parent.toAbsolutePath().relativize(child).toString().replace(File.separatorChar, '/').toLowerCase(Locale.ROOT).replace(' ', '_');
}
private static Identifier toIdentifier(ModContainer container, Path path) {
// This needs to be fixed. It causes runtime errors when running a dependent through Idea.
String rel = getRelative(container.getRootPath(), path);
if (rel.startsWith("static_data/")) { //Should always be true
rel = rel.substring("static_data/".length());
}
return new Identifier(container.getMetadata().getId(), rel);
}
private static Identifier toGlobalIdentifier(Path root, Path path) {
return new Identifier(GLOBAL_DATA_NAMESPACE, getRelative(root, path));
}
}
@@ -0,0 +1,15 @@
package dev.bigduckie.staticdata;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.loader.api.FabricLoader;
import java.io.File;
public class StaticDataInitializer implements ModInitializer {
public static final String MODID = "staticdata";
@Override
public void onInitialize() {
new File(FabricLoader.getInstance().getGameDir().toFile(), "static_data").mkdirs();
}
}
@@ -0,0 +1,53 @@
package dev.bigduckie.staticdata;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import net.minecraft.util.Identifier;
/**
* Represents one file retrieved from static_data
*/
public class StaticDataItem {
private final Identifier id;
private final Path path;
private volatile String readCache = null;
public StaticDataItem(Identifier id, Path path) {
this.id = id;
this.path = path;
}
public InputStream createInputStream() throws IOException {
if (!Files.exists(path)) throw new FileNotFoundException(); //Should never happen
return Files.newInputStream(path, StandardOpenOption.READ);
}
/**
* Reads this data in as a UTF-8 String and returns it. Caches the data for later accesses.
*
* <p>This method is threadsafe.
*
* @return a String representing the contents of this static data item
* @throws IOException
*/
public String getAsString() throws IOException {
if (readCache != null) return readCache; //DCL
synchronized (this) {
if (readCache != null) return readCache;
if (!Files.exists(path)) throw new FileNotFoundException(); //Should never happen
readCache = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
return readCache;
}
}
public Identifier getIdentifier() {
return id;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

+29
View File
@@ -0,0 +1,29 @@
{
"schemaVersion": 1,
"id": "static-data",
"version": "${version}",
"name": "Static Data",
"description": "This library gives you access to an additional data pool alongside assets and data, called static_data. This pool is available immediately in your ModInitializer, and cannot be overridden or reloaded, so you can use it to bootstrap blocks and items.",
"authors": [
"Big Duckie",
"CottonMC"
],
"contact": {
"homepage": "https://git.bigduckie.dev/big-duckie/staticdata/",
"sources": "https://git.bigduckie.dev/big-duckie/staticdata/"
},
"license": "MIT",
"icon": "assets/static-data/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"dev.bigduckie.staticdata.StaticDataInitializer"
]
},
"depends": {
"fabricloader": ">=0.16.7",
"minecraft": "~1.20.1",
"java": ">=17",
"fabric-api": "*"
}
}