Compare commits

...
14 changed files with 410 additions and 4 deletions
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.block.entity;
import com.cobblemon.mod.common.Cobblemon;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.entity.SculkShriekerBlockEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(SculkShriekerBlockEntity.class)
public abstract class SculkShriekerBlockEntityMixin {
@Inject(method = "tryShriek", at = @At("HEAD"), cancellable = true)
// Cancel warden spawn by preventing the shrieker from activating
void cobblemon$cancelWardenSpawn(ServerLevel level, ServerPlayer player, CallbackInfo ci) {
if (Cobblemon.spawnConfig.isBlacklisted(EntityType.WARDEN)) ci.cancel();
}
}
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.entity;
import com.cobblemon.mod.common.Cobblemon;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.world.level.LevelReader;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(SpawnPlacements.class)
public class SpawnPlacementsMixin {
@Inject(method = "isSpawnPositionOk", at = @At("HEAD"), cancellable = true)
// Cancel general mob spawn by returning false
private static void cobblemon$cancelMobSpawns(EntityType<?> entityType, LevelReader level, BlockPos pos, CallbackInfoReturnable<Boolean> cir) {
if (Cobblemon.spawnConfig.isBlacklisted(entityType)) cir.setReturnValue(false);
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.structure;
import com.cobblemon.mod.common.Cobblemon;
import com.google.common.collect.Iterators;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureEntityInfo;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import java.util.Iterator;
@Mixin(StructureTemplate.class)
public abstract class StructureTemplateMixin {
@ModifyExpressionValue(
method = "placeEntities",
at = @At(value = "INVOKE",
target = "Ljava/util/List;iterator()Ljava/util/Iterator;"
)) // Modifies the iterator to ignore entities that are blacklisted
private Iterator<StructureEntityInfo> cobblemon$cancelStructureSpawn(Iterator<StructureEntityInfo> original) {
return Iterators.filter(original, info ->
Cobblemon.spawnConfig.isBlacklisted(info.nbt.getString("id"))
);
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.structure.structures;
import com.cobblemon.mod.common.Cobblemon;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.structures.EndCityPieces;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(EndCityPieces.EndCityPiece.class)
public abstract class EndCityPieceMixin {
@Inject(
method = "handleDataMarker",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/EntityType;create(Lnet/minecraft/world/level/Level;)Lnet/minecraft/world/entity/Entity;"
),
cancellable = true) // Cancel the function if name == "Sentry" to prevent Shulker from spawning
void cobblemon$cancelShulkerSpawn(String name, BlockPos pos, ServerLevelAccessor level, RandomSource random, BoundingBox box, CallbackInfo ci) {
if (Cobblemon.spawnConfig.isBlacklisted(EntityType.SHULKER)) ci.cancel();
}
}
@@ -0,0 +1,29 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.structure.structures;
import com.cobblemon.mod.common.Cobblemon;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.structures.OceanMonumentPieces;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(OceanMonumentPieces.OceanMonumentPiece.class)
public abstract class OceanMonumentPieceMixin {
@Inject(method = "spawnElder", at = @At("HEAD"), cancellable = true)
void cobblemon$cancelElderGuardianSpawn(WorldGenLevel level, BoundingBox box, int x, int y, int z, CallbackInfo ci) {
if (Cobblemon.spawnConfig.isBlacklisted(EntityType.ELDER_GUARDIAN)) ci.cancel();
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.structure.structures;
import com.cobblemon.mod.common.Cobblemon;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.structures.OceanRuinPieces;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(OceanRuinPieces.OceanRuinPiece.class)
public abstract class OceanRuinPieceMixin {
@Inject(
method = "handleDataMarker",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/EntityType;create(Lnet/minecraft/world/level/Level;)Lnet/minecraft/world/entity/Entity;"
),
cancellable = true) // Cancel the function if name == "drowned" to prevent Drowned from spawning
void cobblemon$cancelDrownedSpawn(String name, BlockPos pos, ServerLevelAccessor level, RandomSource random, BoundingBox box, CallbackInfo ci) {
if (Cobblemon.spawnConfig.isBlacklisted(EntityType.DROWNED)) ci.cancel();
}
}
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.structure.structures;
import com.cobblemon.mod.common.Cobblemon;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.core.Vec3i;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.structures.SwampHutPiece;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(SwampHutPiece.class)
public abstract class SwampHutPieceMixin {
@WrapOperation(
method = "postProcess",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/levelgen/structure/BoundingBox;isInside(Lnet/minecraft/core/Vec3i;)Z"
)) // Prevent witch spawn by making box.isInside return false
boolean cobblemon$cancelWitchSpawn(BoundingBox box, Vec3i vector, Operation<Boolean> original) {
return !Cobblemon.spawnConfig.isBlacklisted(EntityType.WITCH) && original.call(box, vector);
}
@WrapOperation(method = "postProcess",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/levelgen/structure/structures/SwampHutPiece;spawnCat(Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/world/level/levelgen/structure/BoundingBox;)V"
)) // Prevent cat spawn by calling nothing
void cobblemon$cancelWitchCatSpawn(SwampHutPiece piece, ServerLevelAccessor level, BoundingBox box, Operation<Void> original) {
if (Cobblemon.spawnConfig.isBlacklisted(EntityType.CAT)) return;
original.call(piece, level, box);
}
}
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.structure.structures;
import com.cobblemon.mod.common.Cobblemon;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.level.levelgen.structure.structures.WoodlandMansionPieces;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import java.util.List;
@Mixin(WoodlandMansionPieces.WoodlandMansionPiece.class)
public abstract class WoodlandMansionPieceMixin {
@WrapOperation(
method = "handleDataMarker",
at = @At(
value = "INVOKE",
target = "Ljava/util/List;add(Ljava/lang/Object;)Z"
)) // Wraps calls to add to the list and ignores it if the mob to add is blacklisted
boolean cobblemon$cancelMansionSpawns(List instance, Object entity, Operation<Boolean> original) {
if (Cobblemon.spawnConfig.isBlacklisted(((Mob) entity).getType())) {
return true;
}
return original.call(instance, entity);
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.worldgen;
import com.cobblemon.mod.common.Cobblemon;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.levelgen.feature.treedecorators.BeehiveDecorator;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import java.util.Optional;
import java.util.function.Consumer;
@Mixin(BeehiveDecorator.class)
public abstract class BeeHiveDecoratorMixin {
@WrapOperation(
method = "place",
at = @At(
value = "INVOKE",
target = "Ljava/util/Optional;ifPresent(Ljava/util/function/Consumer;)V"
)
)
<T> void cobblemon$cancelBeeSpawn(Optional instance, Consumer<? super @NotNull T> action, Operation<Void> original) {
if (Cobblemon.spawnConfig.isBlacklisted(EntityType.BEE)) return;
original.call(instance, action);
}
}
@@ -73,6 +73,7 @@ import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.client.CobblemonPack
import com.cobblemon.mod.common.command.argument.*
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.CobblemonSpawnConfig
import com.cobblemon.mod.common.config.LastChangedVersion
import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.config.starter.StarterConfig
@@ -160,6 +161,7 @@ object Cobblemon {
var isDedicatedServer = false
val showdownThread = ShowdownThread()
lateinit var config: CobblemonConfig
lateinit var spawnConfig: CobblemonSpawnConfig
var spawningZoneGenerator: SpawningZoneGenerator = CobblemonSpawningZoneGenerator
var areaSpawnablePositionResolver: AreaSpawnablePositionResolver = object : AreaSpawnablePositionResolver {}
val bestSpawner = BestSpawner
@@ -466,11 +468,13 @@ object Cobblemon {
fun loadConfig() {
initializeConfig()
this.spawnConfig = CobblemonSpawnConfig("config/$MODID/blacklisted_mobs.json")
bestSpawner.init()
}
fun reloadConfig() {
initializeConfig()
spawnConfig.load()
bestSpawner.reloadConfig()
}
@@ -302,6 +302,9 @@ class CobblemonConfig {
@CobblemonConfigField(Category.Spawning, lang = "despawner_max_age_ticks")
var despawnerMaxAgeTicks = 3600
@CobblemonConfigField(Category.World, lang = "blacklist_vanilla_mobs")
var blacklistVanillaMobs = false
fun clone(): CobblemonConfig {
val newConfig = CobblemonConfig()
CobblemonConfig::class.memberProperties.forEach { property ->
@@ -0,0 +1,69 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.config
import com.cobblemon.mod.common.Cobblemon
import com.google.gson.GsonBuilder
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.MobCategory
import java.io.File
import java.io.FileReader
import java.io.FileWriter
class CobblemonSpawnConfig(configPath: String) {
private val file = File(configPath)
private var blacklistedMobs: MutableList<String> = mutableListOf()
init {
if (Cobblemon.config.blacklistVanillaMobs) {
load()
save()
}
}
fun load() {
if (!file.exists()) {
blacklistedMobs = mutableListOf()
return
}
val reader = FileReader(file)
blacklistedMobs = GSON.fromJson(reader, blacklistedMobs::class.java)
}
fun save() {
val writer = FileWriter(file)
GSON.toJson(blacklistedMobs, writer)
writer.close()
}
fun <T: Entity> isBlacklisted(type: EntityType<T>): Boolean {
// Non-mobs can always spawn
if (!Cobblemon.config.blacklistVanillaMobs || type.category == MobCategory.MISC) return false
val location = type.builtInRegistryHolder().key().location()
return isBlacklisted(location.toString())
}
fun isBlacklisted(id: String): Boolean {
return id.substringBefore(":").equals("minecraft") && blacklistedMobs.contains(id.substringAfter(":"))
}
companion object {
val GSON = GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.create()
}
}
@@ -142,4 +142,8 @@ accessible field net/minecraft/client/gui/screens/recipebook/RecipeBookComponent
accessible method net/minecraft/world/level/block/AbstractCandleBlock addParticlesAndSound (Lnet/minecraft/world/level/Level;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/util/RandomSource;)V
transitive-accessible method net/minecraft/world/entity/ai/behavior/RandomStroll getTargetSwimPos (Lnet/minecraft/world/entity/PathfinderMob;)Lnet/minecraft/world/phys/Vec3;
transitive-accessible method net/minecraft/world/entity/ai/behavior/RandomStroll getTargetSwimPos (Lnet/minecraft/world/entity/PathfinderMob;)Lnet/minecraft/world/phys/Vec3;
# Vanilla Spawns
# Structures
accessible class net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces$OceanMonumentPiece
@@ -22,7 +22,6 @@
"client.AbstractClientPlayerMixin",
"client.CameraMixin",
"client.ClientRecipeBookMixin",
"client.SearchTreeMixin",
"client.EntryListWidgetMixin",
"client.GameRendererMixin",
"client.HumanoidModelMixin",
@@ -33,11 +32,12 @@
"client.ModelPartMixin",
"client.MouseHandlerMixin",
"client.PlayerRendererMixin",
"client.StringRenderOutputMixin",
"client.RecipeBookCategoriesMixin",
"client.RecipeBookComponentMixin",
"client.RecipeBookTabButtonMixin",
"client.RecipeCollectionMixin",
"client.SearchTreeMixin",
"client.StringRenderOutputMixin",
"invoker.ClientPlayNetworkHandlerInvoker",
"riding.JetMixin"
],
@@ -76,7 +76,9 @@
"accessor.ChunkMapAccessor",
"accessor.StatusEffectInstanceAccessor",
"accessor.TrackedEntityAccessor",
"block.entity.SculkShriekerBlockEntityMixin",
"entity.EntityMixin",
"entity.SpawnPlacementsMixin",
"entity.TameableEntityMixin",
"invoker.AdornRegisterInvoker",
"invoker.BlocksInvoker",
@@ -86,6 +88,13 @@
"invoker.PressurePlateBlockInvoker",
"invoker.StairsBlockInvoker",
"invoker.TrapdoorBlockInvoker",
"pokeball.ThrownEntityMixin"
"pokeball.ThrownEntityMixin",
"structure.StructureTemplateMixin",
"structure.structures.EndCityPieceMixin",
"structure.structures.OceanMonumentPieceMixin",
"structure.structures.OceanRuinPieceMixin",
"structure.structures.SwampHutPieceMixin",
"structure.structures.WoodlandMansionPieceMixin",
"worldgen.BeeHiveDecoratorMixin"
]
}