Skip to content
Open
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
run: ./gradlew build
- name: capture build artifacts
if: ${{ runner.os == 'Linux' && matrix.java == '17' }} # Only upload artifacts built from latest java on one OS
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: Artifacts
path: build/libs/
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ disableVillages: prevents village structures from spawning in the world
curedZombieLoot: if this option is set to null, curing zombie villagers is impossible.
By supplying lootTable data (for example generated on https://misode.github.io/loot-table/ ), zombie villagers die when they are completely healed and drop the configured loot.

tradeCycling: if this option is set to true, villagers trade will be set in stone when the villagers are spawned.

Original file line number Diff line number Diff line change
Expand Up @@ -10,107 +10,107 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;

public class DisableVillagersMod implements ModInitializer {

public static final Logger LOGGER = LoggerFactory.getLogger("disable.villagers");
private static final String CONFIG_FOLDER = "config/";
private static final String CONFIG_FILE = CONFIG_FOLDER + "disable-villagers.json";
private static final String DEFAULT_CONFIG = """
{
"killVillagers": true,
"disableWanderingTrader": true,
"blockTrading": true,
"spareExperiencedVillagers": true,
"breeding": false,
"disableVillages": true,
"disableZombies": false,
"curableZombies": true,
"curedZombieLoot": {
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:emerald_block"
}
]
}
]
}
}""";
private static final String DEFAULT_CONFIG_PATH = "data/disable-villagers/default_config.json";
private static final Gson GSON = LootGsons.getTableGsonBuilder().create();
private static boolean isInitialized = false;

public static boolean killVillagers = false;
public static boolean disableWanderingTrader = false;
public static boolean blockTrading = false;
public static boolean spareExperiencedVillagers = false;
public static boolean breeding = false;
private static boolean disableZombies = true;
public static boolean getDisabledZombies() {
loadConfig();
return disableZombies;
}
public static boolean curableZombies = true;
private static JsonElement curedZombieLootJson = null;
public static LootTable curedZombieLoot = null;
private static boolean disableVillages = false;
public static boolean getDisabledVillages() {
loadConfig();
return disableVillages;
}

private static ModConfig config;

@Override
public void onInitialize() {
loadConfig();
if (curedZombieLootJson != null)
curedZombieLoot = GSON.fromJson(curedZombieLootJson, LootTable.class);
LOGGER.info("DisableVillagers mod initialized with configuration: {}", config);
}

/**
* Loads the mod configuration from file or creates default if not present
*/
private static void loadConfig() {
if (isInitialized)
return;
isInitialized = true;
File file = new File(CONFIG_FILE);
String data;
if (!file.exists()) {
file.getParentFile().mkdirs();
data = DEFAULT_CONFIG;
try (FileWriter writer = new FileWriter(CONFIG_FILE)) {
writer.write(data);
try {
Path configPath = Path.of(CONFIG_FILE);
String configData;

if (!Files.exists(configPath)) {
LOGGER.info("Creating default configuration file");
Files.createDirectories(configPath.getParent());
configData = loadDefaultConfig();
Files.writeString(configPath, configData);
} else {
configData = Files.readString(configPath);
}
catch (IOException e) {
e.printStackTrace();

JsonObject jsonConfig = JsonParser.parseString(configData).getAsJsonObject();
config = new ModConfig(jsonConfig);
LOGGER.info("Configuration loaded successfully");
} catch (IOException e) {
LOGGER.error("Failed to load configuration", e);
// Fallback to default config
try {
String defaultConfig = loadDefaultConfig();
config = new ModConfig(JsonParser.parseString(defaultConfig).getAsJsonObject());
} catch (IOException ex) {
LOGGER.error("Failed to load default configuration", ex);
throw new RuntimeException("Failed to load configuration", ex);
}
}
else {
try (Scanner scanner = new Scanner(file)) {
StringBuilder builder = new StringBuilder();
while (scanner.hasNextLine())
builder.append(scanner.nextLine());
data = builder.toString();
}
catch (FileNotFoundException e) {
e.printStackTrace();
data = DEFAULT_CONFIG;
}

private static String loadDefaultConfig() throws IOException {
try (InputStream is = DisableVillagersMod.class.getClassLoader().getResourceAsStream(DEFAULT_CONFIG_PATH)) {
if (is == null) {
throw new IOException("Default configuration file not found in resources");
}
return new String(is.readAllBytes());
}
JsonObject config = JsonParser.parseString(data).getAsJsonObject();
killVillagers = config.get("killVillagers").getAsBoolean();
disableWanderingTrader = config.has("disableWanderingTrader") && config.get("disableWanderingTrader").getAsBoolean();
blockTrading = config.get("blockTrading").getAsBoolean();
spareExperiencedVillagers = config.get("spareExperiencedVillagers").getAsBoolean();
breeding = config.get("breeding").getAsBoolean();
disableZombies = config.has("disableZombies") && config.get("disableZombies").getAsBoolean();
curableZombies = config.get("curableZombies").getAsBoolean();
disableVillages = config.get("disableVillages").getAsBoolean();
curedZombieLootJson = config.get("curedZombieLoot");
}

// Configuration getters
public static boolean isKillVillagers() {
return config.killVillagers;
}

public static boolean isDisableWanderingTrader() {
return config.disableWanderingTrader;
}

public static boolean isBlockTrading() {
return config.blockTrading;
}

public static boolean isSpareExperiencedVillagers() {
return config.spareExperiencedVillagers;
}

public static boolean isBreeding() {
return config.breeding;
}

public static boolean isDisableZombies() {
return config.disableZombies;
}

public static boolean isCurableZombies() {
return config.curableZombies;
}

public static boolean isDisableVillages() {
return config.disableVillages;
}

public static LootTable getCuredZombieLoot() {
return config.curedZombieLoot;
}

public static boolean isTradeCycling() {
return config.tradeCycling;
}
}
40 changes: 40 additions & 0 deletions src/main/java/com/stratecide/disable/villagers/ModConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.stratecide.disable.villagers;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.minecraft.loot.LootGsons;
import net.minecraft.loot.LootTable;

/**
* Configuration class to hold all mod settings
*/
public class ModConfig {
private static final Gson GSON = LootGsons.getTableGsonBuilder().create();

public final boolean killVillagers;
public final boolean disableWanderingTrader;
public final boolean blockTrading;
public final boolean spareExperiencedVillagers;
public final boolean breeding;
public final boolean disableZombies;
public final boolean curableZombies;
public final boolean disableVillages;
public final LootTable curedZombieLoot;
public final boolean tradeCycling;

public ModConfig(JsonObject json) {
this.killVillagers = json.get("killVillagers").getAsBoolean();
this.disableWanderingTrader = json.has("disableWanderingTrader") && json.get("disableWanderingTrader").getAsBoolean();
this.blockTrading = json.get("blockTrading").getAsBoolean();
this.spareExperiencedVillagers = json.get("spareExperiencedVillagers").getAsBoolean();
this.breeding = json.get("breeding").getAsBoolean();
this.disableZombies = json.has("disableZombies") && json.get("disableZombies").getAsBoolean();
this.curableZombies = json.get("curableZombies").getAsBoolean();
this.disableVillages = json.get("disableVillages").getAsBoolean();
this.tradeCycling = json.get("tradeCycling").getAsBoolean();

JsonElement lootJson = json.get("curedZombieLoot");
this.curedZombieLoot = lootJson != null ? GSON.fromJson(lootJson, LootTable.class) : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ public class DefaultBiomeFeaturesMixin {
*/
@ModifyVariable(method = "addMonsters", at = @At("HEAD"), ordinal = 0)
private static int fixZombieChance(int weight, SpawnSettings.Builder builder, int zombieWeight, int zombieVillagerWeight, int skeletonWeight) {
if (DisableVillagersMod.getDisabledZombies())
if (DisableVillagersMod.isDisableZombies()) {
return weight + zombieVillagerWeight;
}
return weight;
}

Expand All @@ -26,8 +27,9 @@ private static int fixZombieChance(int weight, SpawnSettings.Builder builder, in
*/
@Redirect(method = "addMonsters", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/biome/SpawnSettings$Builder;spawn(Lnet/minecraft/entity/SpawnGroup;Lnet/minecraft/world/biome/SpawnSettings$SpawnEntry;)Lnet/minecraft/world/biome/SpawnSettings$Builder;", ordinal = 2))
private static SpawnSettings.Builder removeZombieVillagers(SpawnSettings.Builder builder, SpawnGroup spawnGroup, SpawnSettings.SpawnEntry spawnEntry) {
if (!DisableVillagersMod.getDisabledZombies())
if (!DisableVillagersMod.isDisableZombies()) {
return builder.spawn(spawnGroup, spawnEntry);
}
return builder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
public class MerchantEntityMixin {

@Inject(method = "getOffers", at = @At("HEAD"), cancellable = true)
private void injectGetOffers(CallbackInfoReturnable<TradeOfferList> cir) {
if (DisableVillagersMod.blockTrading && ((Object) this) instanceof VillagerEntity
|| DisableVillagersMod.disableWanderingTrader && ((Object) this) instanceof WanderingTraderEntity) {
private void onGetOffersInject(CallbackInfoReturnable<TradeOfferList> cir) {
Object self = this;
if ((self instanceof VillagerEntity && DisableVillagersMod.isBlockTrading()) ||
(self instanceof WanderingTraderEntity && DisableVillagersMod.isDisableWanderingTrader())) {
cir.setReturnValue(new TradeOfferList());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,17 @@

@Mixin(StructurePlacementCalculator.class)
public class StructurePlacementCalculatorMixin {

@ModifyVariable(method = "create(Lnet/minecraft/world/gen/noise/NoiseConfig;JLnet/minecraft/world/biome/source/BiomeSource;Ljava/util/stream/Stream;)Lnet/minecraft/world/gen/chunk/placement/StructurePlacementCalculator;", at = @At("HEAD"), argsOnly = true)
private static Stream<RegistryEntry<StructureSet>> removeVillages1(Stream<RegistryEntry<StructureSet>> structureSets) {
if (DisableVillagersMod.getDisabledVillages()) {
return structureSets.filter(entry ->
!entry.matchesKey(StructureSetKeys.VILLAGES)
);
} else {
return structureSets;
private static Stream<RegistryEntry<StructureSet>> filterVillageStructures(Stream<RegistryEntry<StructureSet>> structureSets) {
if (DisableVillagersMod.isDisableVillages()) {
return structureSets.filter(entry -> !entry.matchesKey(StructureSetKeys.VILLAGES));
}
return structureSets;
}

@Redirect(method = "create(Lnet/minecraft/world/gen/noise/NoiseConfig;JLnet/minecraft/world/biome/source/BiomeSource;Lnet/minecraft/registry/RegistryWrapper;)Lnet/minecraft/world/gen/chunk/placement/StructurePlacementCalculator;", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;collect(Ljava/util/stream/Collector;)Ljava/lang/Object;"))
private static Object removeVillages2(Stream<RegistryEntry<StructureSet>> stream, Collector collector) {
return removeVillages1(stream).collect(collector);
private static Object filterAndCollectStructures(Stream<RegistryEntry<StructureSet>> stream, Collector collector) {
return filterVillageStructures(stream).collect(collector);
}
}
Loading