Add biome modifier to remove managed spawns

* add formatters for biome modifiers
* streamline building writers in the runner
This commit is contained in:
Andrew Miner
2025-10-15 23:21:10 -06:00
parent 919c3d7312
commit 2da1a38c69
15 changed files with 207 additions and 26 deletions
@@ -0,0 +1,11 @@
from mcpacker.json import JsonBlob
# Classes ##########################################################################################
class BiomeModifier:
def asJsonBlob(self) -> JsonBlob:
return {
"type": "neoforge:none",
}
@@ -0,0 +1,28 @@
from collections.abc import Iterable
from mcpacker.format.datapack.biomemodifier import BiomeModifier
from mcpacker.format.datapack.biomemodifier.generationstep import GenerationStep
from mcpacker.json import JsonBlob
# Class ############################################################################################
class AddFeatureBiomeModifier(BiomeModifier):
def __init__(self, biomes:Iterable[str]|str, features:Iterable[str]|str, step:GenerationStep):
if isinstance(biomes, str):
biomes = [biomes]
if isinstance(features, str):
features = [features]
self.biomes = biomes
self.features = features
self.step = step
def asJsobBlob(self) -> JsonBlob:
return {
"type": "neoforge:add_features",
"biomes": list(self.biomes),
"features": list(self.features),
"step": str(self.step),
}
@@ -0,0 +1,52 @@
from typing import Any
# Class ############################################################################################
class GenerationStep:
def __init__(self, gameId:str):
self.gameId = gameId
def __eq__(self, other:Any) -> bool:
if type(self) != type(other): return False
if self.gameId != other.gameId: return False
return True
def __hash__(self) -> int:
return hash(self.gameId)
def __repr__(self) -> str:
return f"GenerationStep(gameId={repr(self.gameId)})"
def __str__(self) -> str:
return self.gameId
# Constants ########################################################################################
RAW_GENERATION = GenerationStep("raw_generation")
LAKES = GenerationStep("lakes")
LOCAL_MODIFICATIONS = GenerationStep("local_modifications")
UNDERGROUND_STRUCTURES = GenerationStep("underground_structures")
SURFACE_STRUCTURES = GenerationStep("surface_structures")
STRONGHOLDS = GenerationStep("strongholds")
UNDERGROUND_ORES = GenerationStep("underground_ores")
UNDERGROUND_DECORATION = GenerationStep("underground_decoration")
FLUID_SPRINGS = GenerationStep("fluid_springs")
VEGETAL_DECORATION = GenerationStep("vegetal_decoration")
TOP_LAYER_MODIFICATION = GenerationStep("top_layer_modification")
ALL = [
RAW_GENERATION,
LAKES,
LOCAL_MODIFICATIONS,
UNDERGROUND_STRUCTURES,
SURFACE_STRUCTURES,
STRONGHOLDS,
UNDERGROUND_ORES,
UNDERGROUND_DECORATION,
FLUID_SPRINGS,
VEGETAL_DECORATION,
TOP_LAYER_MODIFICATION,
]
@@ -0,0 +1,37 @@
from collections.abc import Iterable
from mcpacker.format.datapack.biomemodifier import BiomeModifier
from mcpacker.format.datapack.biomemodifier.generationstep import GenerationStep
from mcpacker.json import JsonBlob
# Classes ##########################################################################################
class RemoveFeatureBiomeModifier(BiomeModifier):
def __init__(
self,
biomes:Iterable[str]|str,
features:Iterable[str]|str,
step:GenerationStep|None=None
):
if isinstance(biomes, str):
biomes = [biomes]
if isinstance(features, str):
features = [features]
self.biomes = biomes
self.features = features
self.step = step
def asJsobBlob(self) -> JsonBlob:
result:dict[str,JsonBlob] = {
"type": "neoforge:remove_features",
"biomes": list(self.biomes),
"features": list(self.features),
}
if self.step:
result["step"] = str(self.step)
return result
@@ -0,0 +1,22 @@
from collections.abc import Iterable
from mcpacker.format.datapack.biomemodifier import BiomeModifier
from mcpacker.json import JsonBlob
# Classes ##########################################################################################
class RemoveSpawnBiomeModifier(BiomeModifier):
def __init__(self, biomes:Iterable[str]|str, entityTypes:Iterable[str]):
if isinstance(biomes, str):
biomes = [biomes]
self.biomes = biomes
self.entityTypes = entityTypes
def asJsonBlob(self) -> JsonBlob:
return {
"type": "neoforge:remove_spawns",
"biomes": list(self.biomes),
"entity_types": list(self.entityTypes),
}
+20 -12
View File
@@ -4,6 +4,7 @@ from mcpacker.format.report.mineralreport import MineralReport
from mcpacker.format.report.mobspawnreport import MobSpawnReport
from mcpacker.model.modpack import ModPack
from mcpacker.write.compositewriter import CompositeWriter
from mcpacker.write.datapack.disablespawnwriter import DisableSpawnWriter
from mcpacker.write.datapack.metawriter import DataPackMetaWriter
from mcpacker.write.datapack.writer import DataPackWriter
from mcpacker.write.incontrol.spawnerwriter import SpawnerWriter
@@ -49,21 +50,28 @@ class Runner:
# Commands #################################################################
def _command_writeReports(self):
CompositeWriter(self.pack, self.outputDir, [
ReportWriter(self.pack, BiomeReport, self.outputDir, "biomes.txt"),
ReportWriter(self.pack, DepositReport, self.outputDir, "deposits.txt"),
ReportWriter(self.pack, MineralReport, self.outputDir, "minerals.txt"),
ReportWriter(self.pack, MobSpawnReport, self.outputDir, "mobspawns.txt"),
p = self.pack
o = self.outputDir
CompositeWriter(p, o, [
ReportWriter(p, BiomeReport, o, "biomes.txt"),
ReportWriter(p, DepositReport, o, "deposits.txt"),
ReportWriter(p, MineralReport, o, "minerals.txt"),
ReportWriter(p, MobSpawnReport, o, "mobspawns.txt"),
]).write()
def _command_writeModPack(self):
CompositeWriter(self.pack, self.outputDir, [
StaticWriter(self.pack, self.outputDir),
SpawnerWriter(self.pack, self.outputDir),
DataPackWriter(self.pack, self.outputDir, [
DataPackMetaWriter(self.pack, self.outputDir, 48),
p = self.pack
o = self.outputDir
CompositeWriter(p, o, [
StaticWriter(p, o),
SpawnerWriter(p, o),
DataPackWriter(p, o, [
DataPackMetaWriter(p, o, 48),
DisableSpawnWriter(p, o),
]),
ResourcePackWriter(self.pack, self.outputDir, [
ResourcePackMetaWriter(self.pack, self.outputDir, 34),
ResourcePackWriter(p, o, [
ResourcePackMetaWriter(p, o, 34),
]),
]).write()
+2
View File
@@ -92,9 +92,11 @@ def createReportRunner(tmp_path:Path, pack:ModPack):
def test_writeModPack(tmp_path:Path, modPackRunner:Runner):
assert (tmp_path/"testpack"/"config"/"incontrol"/"spawner.json").exists()
assert (tmp_path/"testpack"/"datapacks"/"testpack.jar").exists()
assert (tmp_path/"testpack"/"test.md").exists()
def test_writeReports(tmp_path:Path, reportRunner:Runner):
assert (tmp_path/"testpack"/"reports"/"biomes.txt").exists()
assert (tmp_path/"testpack"/"reports"/"deposits.txt").exists()
assert (tmp_path/"testpack"/"reports"/"minerals.txt").exists()
assert (tmp_path/"testpack"/"reports"/"mobspawns.txt").exists()
+3 -5
View File
@@ -1,5 +1,6 @@
from collections.abc import Iterable
from mcpacker.model.modpack import ModPack
from mcpacker.write.locator import Locator
from mcpacker.write.writer import Writer
from pathlib import Path
@@ -12,13 +13,10 @@ class CompositeWriter(Writer):
self,
pack:ModPack,
outputDir:Path,
writers:Iterable[Writer]|None=None
writers:Iterable[Writer]|None=None,
):
super().__init__(pack, outputDir)
self._writers:list[Writer] = []
for writer in (writers or []):
self._writers.append(writer)
self._writers:list[Writer] = list(writers or [])
def doWrite(self):
for writer in self._writers:
@@ -0,0 +1,19 @@
from mcpacker.format.datapack.biomemodifier.removespawn import RemoveSpawnBiomeModifier
from mcpacker.write.writer import Writer
import mcpacker.json as json
# Class ############################################################################################
class DisableSpawnWriter(Writer):
"""
Writes files to disable vanilla spawns for mobs who are to be spawned using custom rules.
"""
def doWrite(self):
entityTypes = [m.gameId for m in self.pack.world.mobs]
modifier = RemoveSpawnBiomeModifier("#c:is_overworld", entityTypes)
path = self.locator.biomeModifiers() / "remove_managed_mob_spawns.json"
self.resetOutputFile(path)
path.write_text(json.dumps(modifier.asJsonBlob(), indent=json.INDENT))
+2 -2
View File
@@ -56,7 +56,7 @@ class Locator:
return self.dataPackMod(modName, dataPackName) / "worldgen" / "configured_feature"
def dataPack(self, dataPackName:str|None=None):
return self.dataPacks() / (dataPackName or f"{self.pack.name}_override")
return self.dataPacks() / (dataPackName or f"{self.pack.name}")
def dataPackMod(self, modName:str|None=None, dataPackName:str|None=None):
return self.dataPack(dataPackName) / "data" / (modName or self.pack.name)
@@ -83,7 +83,7 @@ class Locator:
return self.dataPackMod(modName, dataPackName) / "recipes"
def resourcePack(self, resourcePackName:str|None=None) -> Path:
return self.resourcePacks() / (resourcePackName or f"{self.pack.name}_override")
return self.resourcePacks() / (resourcePackName or f"{self.pack.name}")
def resourcePackMod(self, modName:str|None=None, resourcePackName:str|None=None) -> Path:
return self.resourcePack(resourcePackName) / "assets" / (modName or self.pack.name)
+3 -3
View File
@@ -17,7 +17,7 @@ def test_blockStates(locator:Locator):
assert (
locator.blockStates() ==
Path(
"output/testPack/resourcepacks/testPack_override/" +
"output/testPack/resourcepacks/testPack/" +
"assets/testPack/blockstates"
)
)
@@ -26,7 +26,7 @@ def test_configuredFeatures(locator:Locator):
assert (
locator.configuredFeatures() ==
Path(
"output/testPack/datapacks/testPack_override/" +
"output/testPack/datapacks/testPack/" +
"data/testPack/worldgen/configured_feature"
)
)
@@ -44,7 +44,7 @@ def test_configuredFeaturesSpecificMod(locator:Locator):
assert (
locator.configuredFeatures("farmersdelight") ==
Path(
"output/testPack/datapacks/testPack_override/" +
"output/testPack/datapacks/testPack/" +
"data/farmersdelight/worldgen/configured_feature"
)
)
+1 -1
View File
@@ -14,7 +14,7 @@ class ReportWriter(Writer):
pack:ModPack,
composerClass:type[ReportComposer],
outputDir:Path,
name:str|Path
name:str|Path,
):
super().__init__(pack, outputDir)
self.composerClass = composerClass
+7
View File
@@ -0,0 +1,7 @@
import mcpacker.write.report.writer
# Tests ############################################################################################
def test_syntax():
pass
-3
View File
@@ -14,9 +14,6 @@ SOURCE_BASE = Path("mcpacker") / "pack"
class StaticWriter(Writer):
def __init__(self, pack:ModPack, outputDir:Path):
super().__init__(pack, outputDir)
def doWrite(self):
sourcePath = SOURCE_BASE / self.pack.name / "static"
targetPath = self.locator.root()