Crying Ghasts (Forge)

Crying Ghasts (Forge)

273k Downloads

[Mods with collective as a dependency] Unable to compile

GanerCodes opened this issue · 2 comments

commented

Minecraft version: 1.21
Modloader: Fabric
Fabric loader version: 0.51.11

Hi, I'm having some trouble getting some of your mods compiled when they depend on collective. When I execute ./gradlew build, there are no failures and I have three folders for the three respective mod platforms [as well as the Common folder]. If I transfer the produced lib/….jar file from the relevant folder to my mods (using the public collective jar file alongside it), the game crashes during startup saying it cannot find DuckConfig classes.
If I compile collective locally as well, it is able to detect it. I am unsure why this is. I assume I am missing instructions for how to combine the libraries all together, which may fix the issue.
Crash Log:
image

[also, if it's of any additional encouragement once I get things running I have a pull request prepared for randommodeffects adding optional RNG dependence on number effects and effect level :)]

commented

To do local testing you'll need to compile Collective similar to how you do the mod, or use one of the non-compiled Collective files from the maven: https://github.com/Serilum/.maven/tree/maven/com/natamus/collective-ml

This is due to how I merge the mod loaders into one jar, by splitting the common source. If you'd like to do a PR (thank you!), I'll build the mod correctly with the merging.

commented

hey, thanks for your reply. I already ended up just making my own mod with my implementation, however feel free to copy paste anything from this into your mod. The important parts are the Statistics class and the last dozen or so lines which use the distribution to select the effects.

packagePKG_BASE;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.*;
import java.nio.*;
import java.nio.charset.*;
import java.nio.file.*;
import java.util.*;
import net.fabricmc.loader.api.*;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.*;
import net.minecraft.entity.*;
import net.minecraft.entity.effect.*;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.registry.*;
import net.minecraft.registry.entry.*;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.*;

public class ExampleMod implements ModInitializer {
	public static String fabConfDir, dirpath, blacklistFile, configFile;
	public static File modConfigDir;
	
	public static Random rand;
	
	public class Config {
		public static double effectChance = 0.5;
		public static Statistics.Distribution effectLevelDistribution = Statistics.Distribution.FUNKY;
		public static Statistics.Distribution effectCountDistribution = Statistics.Distribution.FUNKY;
		public static int maxPotionEffectLevel = 255;
		public static int maxPotionEffectCount = 1000;
		public static double effectLevelDistributionParameter = 0.5;
		public static double effectCountDistributionParameter = 0.5;
		
		public static ArrayList<StatusEffect> potionEffects = new ArrayList<StatusEffect>();
		public static ArrayList<String> blacklist = new ArrayList<String>();
		
		public static void init() {
			fabConfDir   = FabricLoader.getInstance().getConfigDir().toString();
			dirpath      = fabConfDir + File.separator + "MODID_config";
			configFile   = dirpath    + File.separator + "config.json";
			modConfigDir = new File(dirpath);
			
			if(!modConfigDir.isDirectory()) modConfigDir.mkdirs();
			updateFromJson(configFile);
			setupPotions(); }
		
		public static void updateFromJson(String fp) {
	        Gson gson = new Gson();
			var jstr = "";
			try { jstr = new String(Files.readAllBytes(Paths.get(fp))); }
			catch(IOException e) {}
            var jo = JsonParser.parseString(jstr).getAsJsonObject();
            if (jo.has("effectChance"                    )) effectChance                     =               jo.get("effectChance"                    ).getAsDouble();
            if (jo.has("effectLevelDistribution"         )) effectLevelDistribution          = gson.fromJson(jo.get("effectLevelDistribution"         ), Statistics.Distribution.class);
            if (jo.has("effectCountDistribution"         )) effectCountDistribution          = gson.fromJson(jo.get("effectCountDistribution"         ), Statistics.Distribution.class);
            if (jo.has("maxPotionEffectLevel"            )) maxPotionEffectLevel             =               jo.get("maxPotionEffectLevel"            ).getAsInt();
            if (jo.has("maxPotionEffectCount"            )) maxPotionEffectCount             =               jo.get("maxPotionEffectCount"            ).getAsInt();
            if (jo.has("effectLevelDistributionParameter")) effectLevelDistributionParameter =               jo.get("effectLevelDistributionParameter").getAsDouble();
            if (jo.has("effectCountDistributionParameter")) effectCountDistributionParameter =               jo.get("effectCountDistributionParameter").getAsDouble();
			if (jo.has("blacklist")) {
                var ja = jo.getAsJsonArray("blacklist"); blacklist.clear();
		        for(var e : ja) blacklist.add(e.getAsString()); } }
		
		public static void setupPotions() {
			for (StatusEffect effect : Registries.STATUS_EFFECT) {
				var n = effect.getName().getString();
				if (!blacklist.contains(n)) potionEffects.add(effect); } } }

	public class Statistics {
	    public enum Distribution { UNIFORM, GEOMETRIC, FUNKY }

	    public static int mapGeometric(double u, double p) {
	    	return (int) Math.min(
	    		Math.ceil(Math.log(1-u) / Math.log(1-p)),
	            Integer.MAX_VALUE); }
		public static int mapFunky(double u) {
	    	return (int) Math.min(
	    		Math.ceil(1 / (10 * Math.pow(1-u, 2))),
	            Integer.MAX_VALUE); }
	    public static int drawDistribution(Distribution t, double p, int min, int max) {
	        var u = rand.nextDouble();
	        var r = 0;
	        switch(t) {
	            case   UNIFORM: { r = (int)(min + u*(max-min)       ); } break;
	            case GEOMETRIC: { r = (int)(min + mapGeometric(u, p)); } break;
	            case     FUNKY: { r = (int)(min + mapFunky(u))       ; } break; }
	        return Math.min(r, max); }

	    // Reservoir Sampling Algorithm
	    public static <T> List<T> chooseN(List<T> l, int n) {
	        if (n >= l.size()) return l;
	        var r = new ArrayList<T>(n);
	        for (var i = 0; i < n; i++) r.add(l.get(i));
	        for (var i = n; i < l.size(); i++) {
	            int o = rand.nextInt(i + 1);
	            if (o < n) r.set(o, l.get(i)); }
	        return r; } }
	
    public void onInitialize() {
		Config.init();
		rand = new Random();
        ServerEntityEvents.ENTITY_LOAD.register(this::onMobSpawn); }

    public void onMobSpawn(Entity entity, ServerWorld world) {
		if(!(entity instanceof MobEntity)) return;
		
		var dat = entity.writeNbt(new NbtCompound());
		if (dat.contains("MODIDtag")) return;
		dat.putBoolean("MODIDtag", true); // 󰤱 this doesn't work fix it
		entity.readNbt(dat);
		
		if (rand.nextDouble() > Config.effectChance) return;
		
		var l = (LivingEntity)entity;
		var n = Statistics.drawDistribution(
			Config.effectCountDistribution,
			Config.effectCountDistributionParameter,
			1, Config.maxPotionEffectCount);
		for (var effect : Statistics.chooseN(Config.potionEffects, n)) {
			var lvl = Statistics.drawDistribution(
				Config.effectLevelDistribution,
				Config.effectLevelDistributionParameter,
				1, Config.maxPotionEffectLevel);
			var effectInst = new StatusEffectInstance(
				Registries.STATUS_EFFECT.getEntry(effect),
				Integer.MAX_VALUE, lvl-1);
			l.addStatusEffect(effectInst); } } }