diff --git a/mcpacker/format/composer.py b/mcpacker/format/composer.py new file mode 100644 index 0000000..abf1b96 --- /dev/null +++ b/mcpacker/format/composer.py @@ -0,0 +1,16 @@ +from typing import Self + + +# Class ############################################################################################ + +class Composer: + + def __str__(self) -> str: + raise NotImplementedError() + + def compose(self) -> Self: + self.doCompose() + return self + + def doCompose(self): + raise NotImplementedError() diff --git a/mcpacker/format/composer_test.py b/mcpacker/format/composer_test.py new file mode 100644 index 0000000..4e7a293 --- /dev/null +++ b/mcpacker/format/composer_test.py @@ -0,0 +1,7 @@ +import mcpacker.format.composer + + +# Tests ############################################################################################ + +def test_syntax(): + pass diff --git a/mcpacker/format/largeoredeposit/__init__.py b/mcpacker/format/largeoredeposit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mcpacker/format/largeoredeposit/mineraldepositcomposer.py b/mcpacker/format/largeoredeposit/mineraldepositcomposer.py new file mode 100644 index 0000000..c49e75a --- /dev/null +++ b/mcpacker/format/largeoredeposit/mineraldepositcomposer.py @@ -0,0 +1,127 @@ +from collections.abc import Iterator +from collections.abc import Mapping +from mcpacker.model.core.geology.inclusion import Inclusion +from mcpacker.format.templatecomposer import TemplateComposer +from mcpacker.model.core.geology.deposit.mineraldeposit import MineralDeposit + + +# Constants ######################################################################################## + +TEMPLATE = """ +Config {{ + B:enabled = true +}} + +Deposit {{ + S:customReplacements < + {customReplacements} + > + S:ores < + {ores} + > + I:rarity={rarity} + S:replaceableBlocks < + {replaceableBlocks} + > + B:vanilla=false + Dimensions {{ + S:blackList < + > + S:whiteList < + > + }} + Biomes {{ + S:blackList < + > + S:whiteList < + > + }} + Altitude {{ + I:min={altitudeMin} + I:max={altitudeMax} + }} + Miscellaneous {{ + B:exposed=true + S:proportions={proportions} + B:strictBounds=false + B:strictStart=false + }} + Indicator {{ + S:circles < + > + S:continuity=0 + I:distortion=0 + S:id={indicatorId} + S:threshold=100 + }} + Size {{ + i:min={sizeMin} + I:max={sizeMax} + }} +}} +""".strip() + +# Class ############################################################################################ + +class MineralDepositComposer(TemplateComposer): + + def __init__(self, deposit:MineralDeposit): + super().__init__(TEMPLATE) + self._deposit = deposit + + def gatherData(self) -> dict[str,str]: + return { + "altitudeMin": f"{self._deposit.altitude.bottom}", + "altitudeMax": f"{self._deposit.altitude.top}", + "customReplacements": "\n ".join(self._gatherCustomReplacements()), + "indicatorId": f"deposit/{self._deposit.gameId}_marker", + "ores": "\n ".join(self._gatherOres()), + "proportions": f"{self._deposit.proportion.ratio:0.2f}", + "rarity": f"{self._gatherRarity()}", + "replaceableBlocks": "\n ".join(self._gatherReplaceableBlocks()), + "sizeMin": f"{self._deposit.bulk.smallest}", + "sizeMax": f"{self._deposit.bulk.largest}", + } + + # Private Methods ########################################################## + + def _gatherCustomReplacements(self) -> list[str]: + result = {} + + for inclusion in self._deposit.inclusions: + if len(inclusion.mineral.replacements) < 2: continue + for replacement in inclusion.mineral.replacements[1:]: + result[f"{replacement.source}->{replacement.target},{inclusion.weight}"] = True + + return list(result.keys()) + + def _gatherOres(self) -> list[str]: + result = {} + + for inclusion in self._deposit.inclusions: + if len(inclusion.mineral.replacements) < 1: continue + for replacement in inclusion.mineral.replacements[0:1]: + result[f"{replacement.target},{inclusion.weight}"] = True + + return list(result.keys()) + + def _gatherReplaceableBlocks(self) -> list[str]: + result = {} + + for inclusion in self._deposit.inclusions: + if len(inclusion.mineral.replacements) < 1: continue + for replacement in inclusion.mineral.replacements[0:1]: + result[f"{replacement.source}"] = True + + return list(result.keys()) + + def _gatherRarity(self) -> int: + return { + "absent": 1000**2, + "rare": 21**2, + "unusual": 13**2, + "sparse": 8**2, + "uncommon": 5**2, + "common": 3**2, + "carpet": 1**2, + }[self._deposit.scarcity.name] diff --git a/mcpacker/format/largeoredeposit/mineraldepositcomposer_test.py b/mcpacker/format/largeoredeposit/mineraldepositcomposer_test.py new file mode 100644 index 0000000..36d01e9 --- /dev/null +++ b/mcpacker/format/largeoredeposit/mineraldepositcomposer_test.py @@ -0,0 +1,107 @@ +from mcpacker.model.core.ecology.biomefilter import BiomeFilter +from mcpacker.model.core.geology.deposit.mineraldeposit import MineralDeposit +from mcpacker.model.core.geology.inclusion import Inclusion +from mcpacker.model.core.geology.mineral import Mineral +from mcpacker.model.core.geology.replacement import Replacement +from pytest import fixture +from mcpacker.format.largeoredeposit.mineraldepositcomposer import MineralDepositComposer +from pytest import fixture + +import mcpacker.model.core.altitude as AL +import mcpacker.model.core.ecology.flora as FL +import mcpacker.model.core.geology.bulk as BU +import mcpacker.model.core.geology.proportion as PR +import mcpacker.model.core.scarcity as SC +import textwrap + + +# Fixtures ######################################################################################### + +@fixture(name="quartz") +def createQuartz(): + yield Mineral("quartz", [ + Replacement.inStone("quartz_ore"), + Replacement.inDeepslate("deepslate_quartz_ore"), + ]) + +@fixture(name="iron") +def createIron(): + yield Mineral("iron", [ + Replacement.inStone("iron_ore"), + Replacement.inDeepslate("deepslate_iron_ore"), + ]) + +@fixture(name="bifIron") +def createBifIron(iron, quartz): + yield MineralDeposit( + name = "bifiron", + altitude = AL.CRUST, + biomeFilters = BiomeFilter([FL.FOREST]), + bulk = BU.LARGE, + inclusions = [Inclusion(iron, 60), Inclusion(quartz, 40)], + proportion = PR.LENS, + scarcity = SC.COMMON, + ) + +@fixture(name="composer") +def createComposer(bifIron): + yield MineralDepositComposer(bifIron).compose() + +# Tests ############################################################################################ + +def test_str(composer): + assert str(composer) == textwrap.dedent(""" + Config { + B:enabled = true + } + + Deposit { + S:customReplacements < + #minecraft:deepslate_ore_replaceables->minecraft:deepslate_iron_ore,60 + #minecraft:deepslate_ore_replaceables->minecraft:deepslate_quartz_ore,40 + > + S:ores < + minecraft:iron_ore,60 + minecraft:quartz_ore,40 + > + I:rarity=9 + S:replaceableBlocks < + #minecraft:stone_ore_replaceables + > + B:vanilla=false + Dimensions { + S:blackList < + > + S:whiteList < + > + } + Biomes { + S:blackList < + > + S:whiteList < + > + } + Altitude { + I:min=-32 + I:max=32 + } + Miscellaneous { + B:exposed=true + S:proportions=0.40 + B:strictBounds=false + B:strictStart=false + } + Indicator { + S:circles < + > + S:continuity=0 + I:distortion=0 + S:id=deposit/bifiron_marker + S:threshold=100 + } + Size { + i:min=1250 + I:max=1500 + } + } + """).strip() diff --git a/mcpacker/format/templatecomposer.py b/mcpacker/format/templatecomposer.py new file mode 100644 index 0000000..1cdc1d0 --- /dev/null +++ b/mcpacker/format/templatecomposer.py @@ -0,0 +1,21 @@ +from collections.abc import Mapping +from mcpacker.format.composer import Composer + + +# Class ############################################################################################ + +class TemplateComposer(Composer): + + def __init__(self, template:str): + self._template = template + self._text:str|None = None + + def __str__(self) -> str: + if not self._text: raise RuntimeError("must call compose before str") + return self._text + + def doCompose(self): + self._text = self._template.format(**self.gatherData()) + + def gatherData(self) -> Mapping[str,str]: + raise NotImplementedError() diff --git a/mcpacker/format/textcomposer.py b/mcpacker/format/textcomposer.py index 08bcd3d..7e9e75f 100644 --- a/mcpacker/format/textcomposer.py +++ b/mcpacker/format/textcomposer.py @@ -1,10 +1,11 @@ +from mcpacker.format.composer import Composer from typing import Any from typing import Self # Class ############################################################################################ -class TextComposer: +class TextComposer(Composer): def __init__(self, indentText:str=" "): self._indent = 0 @@ -15,10 +16,6 @@ class TextComposer: def __str__(self) -> str: return "\n".join(self._lines) - def compose(self) -> Self: - self.doCompose() - return self - # Helper Methods ########################################################### def doCompose(self): diff --git a/mcpacker/model/core/geology/deposit/__init___test.py b/mcpacker/model/core/geology/deposit/__init___test.py index e55288a..76ba822 100644 --- a/mcpacker/model/core/geology/deposit/__init___test.py +++ b/mcpacker/model/core/geology/deposit/__init___test.py @@ -5,4 +5,3 @@ import mcpacker.model.core.geology.deposit def test_syntax(): pass - diff --git a/mcpacker/model/core/geology/deposit/mineraldeposit.py b/mcpacker/model/core/geology/deposit/mineraldeposit.py index 3563be5..76c2a41 100644 --- a/mcpacker/model/core/geology/deposit/mineraldeposit.py +++ b/mcpacker/model/core/geology/deposit/mineraldeposit.py @@ -30,7 +30,7 @@ class MineralDeposit(Deposit): super().__init__(name, altitude, biomeFilters, scarcity) self.bulk = bulk - self.inclusions = inclusions or [] + self.inclusions = list(inclusions or []) self.proportion = proportion def __repr__(self) -> str: diff --git a/mcpacker/ui/runner.py b/mcpacker/ui/runner.py index fa1dee4..165189f 100644 --- a/mcpacker/ui/runner.py +++ b/mcpacker/ui/runner.py @@ -9,6 +9,7 @@ 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 +from mcpacker.write.largeoredeposits.depositwriter import DepositWriter from mcpacker.write.report.writer import ReportWriter from mcpacker.write.resourcepack.metawriter import ResourcePackMetaWriter from mcpacker.write.resourcepack.writer import ResourcePackWriter @@ -68,6 +69,7 @@ class Runner: CompositeWriter(p, o, [ StaticWriter(p, o), SpawnerWriter(p, o), + DepositWriter(p, o), DataPackWriter(p, o, [ DataPackMetaWriter(p, o, 48), DisableFeatureWriter(p, o), diff --git a/mcpacker/ui/runner_test.py b/mcpacker/ui/runner_test.py index 21f58ec..6009b78 100644 --- a/mcpacker/ui/runner_test.py +++ b/mcpacker/ui/runner_test.py @@ -10,6 +10,7 @@ from mcpacker.ui.runner import Runner from mcpacker.write.incontrol.spawnerwriter import SpawnerWriter from pathlib import Path from pytest import fixture +from zipfile import ZipFile import mcpacker.json as json import mcpacker.model.core.altitude as AL @@ -92,9 +93,12 @@ 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() + with ZipFile(tmp_path/"testpack"/"datapacks"/"testpack.jar") as archive: + nameList = archive.namelist() + assert "testpack/pack.mcmeta" in nameList + def test_writeReports(tmp_path:Path, reportRunner:Runner): assert (tmp_path/"testpack"/"reports"/"biomes.txt").exists() assert (tmp_path/"testpack"/"reports"/"deposits.txt").exists() diff --git a/mcpacker/write/largeoredeposits/__init__.py b/mcpacker/write/largeoredeposits/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mcpacker/write/largeoredeposits/depositwriter.py b/mcpacker/write/largeoredeposits/depositwriter.py new file mode 100644 index 0000000..2b7eadf --- /dev/null +++ b/mcpacker/write/largeoredeposits/depositwriter.py @@ -0,0 +1,19 @@ +from mcpacker.format.largeoredeposit.mineraldepositcomposer import MineralDepositComposer +from mcpacker.model.core.geology.deposit.mineraldeposit import MineralDeposit +from mcpacker.model.modpack import ModPack +from mcpacker.write.writer import Writer +from pathlib import Path + + +# Class ############################################################################################ + +class DepositWriter(Writer): + + def doWrite(self): + for deposit in self.pack.world.deposits: + if not isinstance(deposit, MineralDeposit): continue + + path = self.locator.lod_deposits() / f"{deposit.name}.cfg" + self.resetOutputFile(path) + + path.write_text(str(MineralDepositComposer(deposit).compose())) diff --git a/mcpacker/write/textwriter.py b/mcpacker/write/textwriter.py index d331adc..8163f5b 100644 --- a/mcpacker/write/textwriter.py +++ b/mcpacker/write/textwriter.py @@ -1,4 +1,4 @@ -from mcpacker.format.textcomposer import TextComposer +from mcpacker.format.composer import Composer from mcpacker.model.modpack import ModPack from mcpacker.write.writer import Writer from pathlib import Path @@ -8,7 +8,7 @@ from pathlib import Path class TextWriter(Writer): - def __init__(self, pack:ModPack, outputDir:Path, filePath:Path, composer:TextComposer): + def __init__(self, pack:ModPack, outputDir:Path, filePath:Path, composer:Composer): super().__init__(pack, outputDir) self.composer = composer self.filePath = filePath