Skip to content

Commit

Permalink
Merge master (2.0.0)
Browse files Browse the repository at this point in the history
  • Loading branch information
MacaylaMarvelous81 committed Aug 5, 2024
2 parents 75cd8ce + f9571ad commit ee087dd
Show file tree
Hide file tree
Showing 12 changed files with 263 additions and 74 deletions.
55 changes: 35 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,42 @@
# Datapack Installer
A Minecraft (Fabric) mod that allows you to put your datapacks in a folder and
be able to apply them at world creation without copying them to the temporary
folder or the world folder.
The mod should be installed on the client.
A Minecraft (Fabric) mod. When installed on the client, data packs put into the
data pack folder will be available for selection during world creation as well
as while paused in Singleplayer. When installed on the server, data packs put
into the data pack folder will be copied to the active world when the server
starts.

### How does it work?
It's very simple, all it does is copy the files and folders from the
installed_datapacks folder inside the game data directory to the temporary
folder for the datapack selector when creating the world.
### Advantages
On the client, this mod works with the ingame resource and data pack selector.
While creating a world, or while paused in Singleplayer, you can choose which
data packs to enable or disable with an interactive screen.

### Why should I use this over other "global datapack" mods?
This mod supports both compressed datapacks (.zip) and uncompressed datapacks
(folders), so you won't have trouble with multiple types of datapacks. You also
get to choose what datapacks to apply during world creation instead of having
all of the datapacks in the folder applied when you create the world.
On the server, all that happens is that the data packs are copied from the
mod's data pack folder to the world data pack folder whenever the server
starts. If you are switching worlds or deleting them to regenerate often, all
you will need to do is switch or delete the world, and the data packs will
carry on if you leave the mod's folder intact.

### Disadvantages
This mod cannot keep data packs that require feature flags enabled or disabled.
For example, if you did not create a world with the built-in bundle data pack
enabled, and you enable the data pack with the mod, the data pack will only be
temporarily enabled until you quit the world.

### Usage
Step-by-step instructions to use this mod.
1. Insert your desired datapack(s) into .minecraft/installed_datapacks
1. If you have started the game with this mod once, it should already be
in the game+launcher data directory (.minecraft).
#### Client
1. Insert your desired data pack(s) into `datapacks` in the game's data folder
1. If you have started the game with this mod installed, it should already
be present.
2. Begin world creation
1. In the game, click Singleplayer, then click Create New World.
3. Manage datapacks
1. Click the Data Packs button. If you've done everything correctly, the
datapacks will appear on the list on the left!
2. Alternatively, if you already have a world, open and pause the game.
3. Manage data packs
1. Click the Data Packs button. On later versions, you may need to check
the 'More' tab.
2. The data packs should be available on the selector!
#### Server
1. Insert your desired data pack(s) into `datapacks` in the server data folder.
1. This is probably the root, where eula.txt and server.properties are
located.
2. Start the server
1. The data packs will be copied to the active world.
20 changes: 18 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
id 'fabric-loom' version '1.2-SNAPSHOT'
id 'fabric-loom' version '1.7-SNAPSHOT'
id 'maven-publish'
}

Expand Down Expand Up @@ -30,7 +30,7 @@ processResources {
}
}

def targetJavaVersion = 17
def targetJavaVersion = 21
tasks.withType(JavaCompile).configureEach {
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly
Expand Down Expand Up @@ -76,3 +76,19 @@ publishing {
// retrieving dependencies.
}
}

loom {
runs {
client {
client()

runDir = "run/client"
}

server {
server()

runDir = "run/server"
}
}
}
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ org.gradle.jvmargs=-Xmx1G
# check these on https://fabricmc.net/develop/
minecraft_version=1.20.3
yarn_mappings=1.20.3+build.1
loader_version=0.14.21
loader_version=0.15.0
# Mod Properties
mod_version=1.1.0
mod_version=2.0.0
maven_group=ml.unbreakinggold
archives_base_name=DatapackInstaller
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,32 @@
import net.fabricmc.loader.api.FabricLoader;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;

@Environment(EnvType.CLIENT)
public class DatapackInstallerClient implements ClientModInitializer {
private static final Logger LOGGER = LogManager.getLogger(DatapackInstallerClient.class);
private static final Path MAIN_PATH = FabricLoader.getInstance().getGameDir().resolve("datapacks");
private static final Path LEGACY_PATH = FabricLoader.getInstance().getGameDir().resolve("installed_datapacks");
public static Path[] SearchPaths = new Path[]{ MAIN_PATH, LEGACY_PATH };
public static final Path MAIN_PATH = FabricLoader.getInstance().getGameDir().resolve("datapacks");
public static final Path LEGACY_PATH = FabricLoader.getInstance().getGameDir().resolve("installed_datapacks");

@Override
public void onInitializeClient() {
File mainFile = MAIN_PATH.toFile();
if (!mainFile.exists() && !mainFile.mkdirs())
LOGGER.warn("User intervention recommended: default search path 'datapacks' could not be created.");
File legacyFile = LEGACY_PATH.toFile();
if (!mainFile.exists() && !legacyFile.exists() && legacyFile.isDirectory() && !mainFile.mkdirs())
LOGGER.warn("User intervention recommended: default search path 'datapacks' could not be created. Please create it manually.");

if (legacyFile.exists() && legacyFile.isDirectory()) {
try {
FileUtils.moveDirectory(legacyFile, mainFile);
LOGGER.info("Migrated from installed_datapacks to datapacks.");
} catch (IOException e) {
LOGGER.error("User intervention recommended: Failed to migrate from installed_datapacks to datapacks. Please move datapacks manually.", e);
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ml.unbreakinggold.datapackinstaller.mixin;

import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.storage.LevelStorage;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

@Mixin(MinecraftServer.class)
public interface MinecraftServerAccessor {
@Accessor
LevelStorage.Session getSession();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package ml.unbreakinggold.datapackinstaller.mixin.client;

import ml.unbreakinggold.datapackinstaller.client.DatapackInstallerClient;
import net.minecraft.client.gui.screen.world.CreateWorldScreen;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;

import java.nio.file.Path;

@Mixin(CreateWorldScreen.class)
public abstract class CreateWorldScreenMixin {
@Unique
private static final Logger LOGGER = LogManager.getLogger(CreateWorldScreenMixin.class);
@Shadow
@Nullable
private Path dataPackTempDir;

/**
* @author Jomar Milan - July 31st, 2024 - Minecraft 1.20.5
* @reason Use persistent mod directory instead of vanilla temporary directory
*/
@Overwrite
@Nullable
private Path getDataPackTempDir() {
this.dataPackTempDir = DatapackInstallerClient.MAIN_PATH;

return this.dataPackTempDir;
}

/**
* @author Jomar Milan - July 31st, 2024 - Minecraft 1.20.5
* @reason Because dataPackTempDir is now persistent and not temporary, the directory should no longer be cleared.
*/
@Overwrite
private void clearDataPackTempDir() {
LOGGER.info("Suppressing attempt to clear data pack directory");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package ml.unbreakinggold.datapackinstaller.mixin.client;

import ml.unbreakinggold.datapackinstaller.client.DatapackInstallerClient;
import ml.unbreakinggold.datapackinstaller.mixin.MinecraftServerAccessor;
import net.minecraft.client.gui.screen.GameMenuScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.pack.PackScreen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.GridWidget;
import net.minecraft.text.Text;
import net.minecraft.util.WorldSavePath;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.function.Supplier;

@Mixin(GameMenuScreen.class)
public abstract class GameMenuScreenMixin extends Screen {
@Unique private final Logger LOGGER = LogManager.getLogger(GameMenuScreenMixin.class);
@Unique private static final Text DATA_PACK_TEXT = Text.translatable("dataPack.title");

@Shadow private ButtonWidget createButton(Text text, Supplier<Screen> screenSupplier) { return null; };

private GameMenuScreenMixin(Text title) {
super(title);
}

@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;isInSingleplayer()Z"), method = "initWidgets", locals = LocalCapture.CAPTURE_FAILHARD)
private void onInitWidgets(CallbackInfo ci, GridWidget gridWidget, GridWidget.Adder adder) {
if (!this.client.isIntegratedServerRunning() || this.client.getServer().isRemote()) return;

adder.add(this.createButton(DATA_PACK_TEXT, () -> {
Path worldDataPackPath = ((MinecraftServerAccessor)this.client.getServer()).getSession().getDirectory(WorldSavePath.DATAPACKS);
File modDatapackDir = DatapackInstallerClient.MAIN_PATH.toFile();
File worldDataPackDir = worldDataPackPath.toFile();
try {
FileUtils.copyDirectory(modDatapackDir, worldDataPackDir);
} catch(IOException exception) {
LOGGER.error("Unable to install new data packs to world. Data packs not yet seen by this world may be missing.", exception);
}

return new PackScreen(this.client.getServer().getDataPackManager(), (dataPackManager) -> {
Collection<String> enabledProfiles = dataPackManager.getEnabledIds();

this.client.getServer().reloadResources(enabledProfiles);

this.client.setScreen(this);
}, DatapackInstallerClient.MAIN_PATH, DATA_PACK_TEXT);
}));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package ml.unbreakinggold.datapackinstaller.mixin.server;

import com.llamalad7.mixinextras.sugar.Local;
import ml.unbreakinggold.datapackinstaller.server.DatapackInstallerServer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.server.Main;
import net.minecraft.util.WorldSavePath;
import net.minecraft.world.level.storage.LevelStorage;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;

@Mixin(Main.class)
@Environment(EnvType.SERVER)
public class ServerMainMixin {
@Unique
private static final Logger LOGGER = LogManager.getLogger(ServerMainMixin.class);

@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/resource/VanillaDataPackProvider;createManager(Lnet/minecraft/world/level/storage/LevelStorage$Session;)Lnet/minecraft/resource/ResourcePackManager;"), method = "main")
private static void onMain(CallbackInfo ci, @Local LevelStorage.Session session) {
Path worldDataPackPath = session.getDirectory(WorldSavePath.DATAPACKS);
File worldDataPackDir = worldDataPackPath.toFile();
File modDataPackDir = DatapackInstallerServer.MAIN_PATH.toFile();
try {
FileUtils.copyDirectory(modDataPackDir, worldDataPackDir);
} catch(IOException exception) {
LOGGER.error("Failed to copy data packs to the world.", exception);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ml.unbreakinggold.datapackinstaller.server;

import net.fabricmc.api.DedicatedServerModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.loader.api.FabricLoader;

import java.io.File;
import java.nio.file.Path;

@Environment(EnvType.SERVER)
public class DatapackInstallerServer implements DedicatedServerModInitializer {
public static final Path MAIN_PATH = FabricLoader.getInstance().getGameDir().resolve("datapacks");

@Override
public void onInitializeServer() {
File mainFile = MAIN_PATH.toFile();

if (!mainFile.exists()) mainFile.mkdirs();
}
}
9 changes: 7 additions & 2 deletions src/main/resources/DatapackInstaller.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@
"package": "ml.unbreakinggold.datapackinstaller.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"MinecraftServerAccessor"
],
"client": [
"CreateWorldScreenMixin"
"client.CreateWorldScreenMixin",
"client.GameMenuScreenMixin"
],
"injectors": {
"defaultRequire": 1
}
},
"server": [
"server.ServerMainMixin"
]
}
Loading

0 comments on commit ee087dd

Please sign in to comment.