diff --git a/build.gradle.kts b/build.gradle.kts index feea36bea..330064ac0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,8 +5,8 @@ import net.modificationstation.stationapi.gradle.SubprojectHelpers.addDependency plugins { id("maven-publish") - id("fabric-loom") version "1.7.2" - id("babric-loom-extension") version "1.7.3" + id("fabric-loom") version "1.8.8" + id("babric-loom-extension") version "1.8.5" } // https://stackoverflow.com/a/40101046 - Even with kotlin, gradle can't get it's shit together. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a4413138c..9355b4155 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle.kts b/settings.gradle.kts index 737ddf9bb..c83e2150c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -46,3 +46,4 @@ include(":station-gui-api-v0") include(":station-transitive-access-wideners-v0") include(":station-maths-v0") include(":station-worldgen-api-v0") +include(":station-celestial-events-v0") diff --git a/src/test/java/net/modificationstation/sltest/block/Blocks.java b/src/test/java/net/modificationstation/sltest/block/Blocks.java index a378b1806..4fe7810b3 100644 --- a/src/test/java/net/modificationstation/sltest/block/Blocks.java +++ b/src/test/java/net/modificationstation/sltest/block/Blocks.java @@ -16,14 +16,15 @@ public enum Blocks { - TEST_BLOCK("test_block", "testBlock", id -> new TemplateBlock(id, Material.CLAY).setHardness(1)), + TEST_BLOCK("test_block", "testBlock", id -> new TemplateBlock(id, Material.STONE).setHardness(1)), TEST_ANIMATED_BLOCK("test_animated_block", "testAnimatedBlock", id -> new ModdedMetaBlock(id, Material.NETHER_PORTAL)), CUSTOM_MODEL_BLOCK("farlands_block", "farlands_block", id -> new ModdedModelBlock(id, Material.SOIL).setHardness(1)), FREEZER("freezer", "freezer", id -> new BlockFreezer(id).setHardness(2.5F).setSoundGroup(TemplateBlock.DEFAULT_SOUND_GROUP)), ALTAR("altar", "altar", id -> new BlockAltar(id, Material.STONE).setHardness(3)), VARIATION_BLOCK("variation_block", "variationBlock", id -> new VariationBlock(id, Material.STONE).setHardness(.5F).setSoundGroup(Block.DEFAULT_SOUND_GROUP).disableAutoItemRegistration()), EMISSION_CHECKER("emission_checker", "emissionChecker", LampBlock::new), - INDISPENSABLE_BLOCK("indispensable_block", "indispensableBlock", IndispensableBlock::new); + INDISPENSABLE_BLOCK("indispensable_block", "indispensableBlock", IndispensableBlock::new), + TIME_MACHINE_BLOCK("time_machine_block", "timeMachineBlock", TimeMachineBlock::new); private final Runnable register; private Block block; diff --git a/src/test/java/net/modificationstation/sltest/block/TimeMachineBlock.java b/src/test/java/net/modificationstation/sltest/block/TimeMachineBlock.java new file mode 100644 index 000000000..3b560608c --- /dev/null +++ b/src/test/java/net/modificationstation/sltest/block/TimeMachineBlock.java @@ -0,0 +1,34 @@ +package net.modificationstation.sltest.block; + +import net.minecraft.block.Material; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.modificationstation.sltest.celestial.CelestialListener; +import net.modificationstation.stationapi.api.template.block.TemplateBlock; +import net.modificationstation.stationapi.api.util.Identifier; + +import java.util.Random; + +public class TimeMachineBlock extends TemplateBlock { + public TimeMachineBlock(Identifier identifier) { + super(identifier, Material.METAL); + this.setTickRandomly(true); + } + + @Override + public boolean onUse(World world, int x, int y, int z, PlayerEntity player) { + world.setTime(world.getTime() + 1000); + return true; + } + + @Override + public void onTick(World world, int x, int y, int z, Random random) { + super.onTick(world, x, y, z, random); + if (CelestialListener.flyingDimando.isActive()) { + world.method_287(new ItemEntity(world, x + 0.5, y + 1, z + 0.5, new ItemStack(Item.DIAMOND))); + } + } +} diff --git a/src/test/java/net/modificationstation/sltest/celestial/CelestialListener.java b/src/test/java/net/modificationstation/sltest/celestial/CelestialListener.java new file mode 100644 index 000000000..19b0172cc --- /dev/null +++ b/src/test/java/net/modificationstation/sltest/celestial/CelestialListener.java @@ -0,0 +1,38 @@ +package net.modificationstation.sltest.celestial; + +import net.mine_diver.unsafeevents.listener.EventListener; +import net.modificationstation.sltest.SLTest; +import net.modificationstation.stationapi.api.celestial.CelestialActivityStateManager; +import net.modificationstation.stationapi.api.celestial.DayQuarter; +import net.modificationstation.stationapi.api.event.celestial.CelestialEvent; +import net.modificationstation.stationapi.api.util.Identifier; + +public class CelestialListener { + + public static net.modificationstation.stationapi.api.celestial.CelestialEvent flyingDimando; + public static net.modificationstation.stationapi.api.celestial.CelestialEvent fallingDimando; + public static net.modificationstation.stationapi.api.celestial.CelestialEvent crashingDimando; + public static net.modificationstation.stationapi.api.celestial.CelestialEvent spinningDimando; + public static net.modificationstation.stationapi.api.celestial.CelestialEvent burningDimando; + public static net.modificationstation.stationapi.api.celestial.CelestialEvent longDimando; + + @EventListener + public void registerCelestialEvents(CelestialEvent event) { + SLTest.LOGGER.info("Register celestial events for testing"); + flyingDimando = new FlyingDimando(4, Identifier.of(SLTest.NAMESPACE, "flying_dimando")); + fallingDimando = new DebugCelestialEvent(2, Identifier.of(SLTest.NAMESPACE, "falling_dimando")); + crashingDimando = new DebugCelestialEvent(2, Identifier.of(SLTest.NAMESPACE, "crashing_dimando")); + spinningDimando = new DebugCelestialEvent(4, Identifier.of(SLTest.NAMESPACE, "spinning_dimando")).setDayOffset(event.world, 1); + burningDimando = new DebugCelestialEvent(2, Identifier.of(SLTest.NAMESPACE, "burning_dimando")).setDayOffset(event.world, 1); + longDimando = new DebugCelestialEvent(12, Identifier.of(SLTest.NAMESPACE, "long_dimando")).setExtraDays(event.world, 4); + flyingDimando.addIncompatibleEvent(fallingDimando); + spinningDimando.addIncompatibleEvent(burningDimando); + crashingDimando.addDependency(longDimando); + ((CelestialActivityStateManager) event.world).getCelestialTimeManager().addCelestialEvent(flyingDimando, DayQuarter.MORNING, DayQuarter.MORNING); + ((CelestialActivityStateManager) event.world).getCelestialTimeManager().addCelestialEvent(fallingDimando, DayQuarter.NOON, DayQuarter.MIDNIGHT); + ((CelestialActivityStateManager) event.world).getCelestialTimeManager().addCelestialEvent(crashingDimando, DayQuarter.NOON, DayQuarter.NOON); + ((CelestialActivityStateManager) event.world).getCelestialTimeManager().addCelestialEvent(spinningDimando, DayQuarter.EVENING, DayQuarter.NOON); + ((CelestialActivityStateManager) event.world).getCelestialTimeManager().addCelestialEvent(burningDimando, DayQuarter.MIDNIGHT, DayQuarter.EVENING); + ((CelestialActivityStateManager) event.world).getCelestialTimeManager().addCelestialEvent(longDimando, DayQuarter.MIDNIGHT, DayQuarter.MIDNIGHT); + } +} diff --git a/src/test/java/net/modificationstation/sltest/celestial/DebugCelestialEvent.java b/src/test/java/net/modificationstation/sltest/celestial/DebugCelestialEvent.java new file mode 100644 index 000000000..d75f194fa --- /dev/null +++ b/src/test/java/net/modificationstation/sltest/celestial/DebugCelestialEvent.java @@ -0,0 +1,23 @@ +package net.modificationstation.sltest.celestial; + +import net.minecraft.world.World; +import net.modificationstation.stationapi.api.celestial.CelestialEvent; +import net.modificationstation.stationapi.api.util.Identifier; + +public class DebugCelestialEvent extends CelestialEvent { + public DebugCelestialEvent(int frequency, Identifier name) { + super(frequency, name); + } + + @Override + public void onActivation(World world) { + super.onActivation(world); + System.out.println(this.getIdentifier() + " has begun"); + } + + @Override + public void onDeactivation(World world) { + super.onDeactivation(world); + System.out.println(this.getIdentifier() + " is over"); + } +} diff --git a/src/test/java/net/modificationstation/sltest/celestial/FlyingDimando.java b/src/test/java/net/modificationstation/sltest/celestial/FlyingDimando.java new file mode 100644 index 000000000..a03b3d37e --- /dev/null +++ b/src/test/java/net/modificationstation/sltest/celestial/FlyingDimando.java @@ -0,0 +1,17 @@ +package net.modificationstation.sltest.celestial; + +import net.minecraft.world.World; +import net.modificationstation.stationapi.api.util.Identifier; + +public class FlyingDimando extends DebugCelestialEvent { + public FlyingDimando(int frequency, Identifier name) { + super(frequency, name); + } + + @Override + public void onActivation(World world) { + super.onActivation(world); + world.method_262().setRaining(true); + world.method_262().setRainTime(200); + } +} diff --git a/src/test/java/net/modificationstation/sltest/item/CelestialTestItem.java b/src/test/java/net/modificationstation/sltest/item/CelestialTestItem.java new file mode 100644 index 000000000..3c5807852 --- /dev/null +++ b/src/test/java/net/modificationstation/sltest/item/CelestialTestItem.java @@ -0,0 +1,21 @@ +package net.modificationstation.sltest.item; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.modificationstation.sltest.celestial.CelestialListener; +import net.modificationstation.stationapi.api.template.item.TemplateItem; +import net.modificationstation.stationapi.api.util.Identifier; + +public class CelestialTestItem extends TemplateItem { + public CelestialTestItem(Identifier identifier) { + super(identifier); + } + + @Override + public ItemStack use(ItemStack item, World world, PlayerEntity player) { + if (CelestialListener.flyingDimando.isActive()) System.out.println("Event is happening"); + else System.out.println("No event"); + return super.use(item, world, player); + } +} diff --git a/src/test/java/net/modificationstation/sltest/item/CelestialToggleItem.java b/src/test/java/net/modificationstation/sltest/item/CelestialToggleItem.java new file mode 100644 index 000000000..c6c9c723a --- /dev/null +++ b/src/test/java/net/modificationstation/sltest/item/CelestialToggleItem.java @@ -0,0 +1,35 @@ +package net.modificationstation.sltest.item; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.modificationstation.sltest.celestial.CelestialListener; +import net.modificationstation.stationapi.api.template.item.TemplateItem; +import net.modificationstation.stationapi.api.util.Identifier; + +public class CelestialToggleItem extends TemplateItem { + public CelestialToggleItem(Identifier identifier) { + super(identifier); + } + + @Override + public ItemStack use(ItemStack item, World world, PlayerEntity player) { + if (CelestialListener.fallingDimando.isActive()) { + CelestialListener.fallingDimando.stopEvent(world); + System.out.println("Stopping Falling Dimando"); + } else if (CelestialListener.fallingDimando.activateEvent(world, world.getTime(), random)) { + System.out.println("Activating Falling Dimando"); + } else { + System.out.println("Falling Dimando not activated"); + } + if (CelestialListener.flyingDimando.isActive()) { + CelestialListener.flyingDimando.stopEvent(world); + System.out.println("Stopping Flying Dimando"); + } else if (CelestialListener.flyingDimando.activateEvent(world, world.getTime(), random)) { + System.out.println("Activating Flying Dimando"); + } else { + System.out.println("Flying Dimando not activated"); + } + return super.use(item, world, player); + } +} diff --git a/src/test/java/net/modificationstation/sltest/item/ItemListener.java b/src/test/java/net/modificationstation/sltest/item/ItemListener.java index ee725ed40..23a01b182 100644 --- a/src/test/java/net/modificationstation/sltest/item/ItemListener.java +++ b/src/test/java/net/modificationstation/sltest/item/ItemListener.java @@ -6,9 +6,12 @@ import net.modificationstation.sltest.block.Blocks; import net.modificationstation.sltest.block.VariationBlock; import net.modificationstation.stationapi.api.event.registry.ItemRegistryEvent; +import net.modificationstation.stationapi.api.item.tool.MiningLevelManager; import net.modificationstation.stationapi.api.item.tool.ToolMaterialFactory; +import net.modificationstation.stationapi.api.registry.BlockRegistry; import net.modificationstation.stationapi.api.registry.ItemRegistry; import net.modificationstation.stationapi.api.registry.Registry; +import net.modificationstation.stationapi.api.tag.TagKey; import net.modificationstation.stationapi.api.template.item.BlockStateItem; import static net.modificationstation.sltest.SLTest.NAMESPACE; @@ -17,8 +20,13 @@ public class ItemListener { @EventListener public void registerItems(ItemRegistryEvent event) { + MiningLevelManager.LevelNode moddedNode = new MiningLevelManager.LevelNode(TagKey.of(BlockRegistry.KEY, NAMESPACE.id("needs_tool_level_modded"))); + MiningLevelManager.GRAPH.putEdge(ToolMaterial.STONE.getMiningLevelNode(), moddedNode); + MiningLevelManager.GRAPH.putEdge(moddedNode, ToolMaterial.IRON.getMiningLevelNode()); + MiningLevelManager.invalidateCache(); + testItem = new ModdedItem(NAMESPACE.id("test_item")).setTranslationKey(NAMESPACE, "testItem"); //8475 - testMaterial = ToolMaterialFactory.create("testMaterial", 3, Integer.MAX_VALUE, Float.MAX_VALUE, Integer.MAX_VALUE - 2); + testMaterial = ToolMaterialFactory.create("testMaterial", 3, Integer.MAX_VALUE, Float.MAX_VALUE, Integer.MAX_VALUE - 2).miningLevelNode(moddedNode); testPickaxe = new ModdedPickaxeItem(NAMESPACE.id("test_pickaxe"), testMaterial).setTranslationKey(NAMESPACE, "testPickaxe"); //8476 testNBTItem = new NBTItem(NAMESPACE.id("nbt_item")).setTranslationKey(NAMESPACE, "nbt_item"); //8477 testModelItem = new ModelItem(NAMESPACE.id("model_item")).setMaxCount(1).setTranslationKey(NAMESPACE, "idkSomething"); @@ -30,6 +38,8 @@ public void registerItems(ItemRegistryEvent event) { testShears = new TestShearsItem(NAMESPACE.id("test_shears")).setTranslationKey(NAMESPACE, "test_shears"); pacifistSword = new PacifistSwordItem(NAMESPACE.id("pacifist_sword")).setTranslationKey(NAMESPACE, "pacifist_sword"); dullPickaxe = new DullPickaxeItem(NAMESPACE.id("dull_pickaxe")).setTranslationKey(NAMESPACE, "dull_pickaxe"); + celestialTestItem = new CelestialTestItem(NAMESPACE.id("test_celestial")).setTranslationKey(NAMESPACE, "test_celestial"); + celestialToggleItem = new CelestialToggleItem(NAMESPACE.id("toggle_celestial")).setTranslationKey(NAMESPACE, "toggle_celestial"); } public static Item testItem; @@ -45,4 +55,6 @@ public void registerItems(ItemRegistryEvent event) { public static Item testShears; public static Item pacifistSword; public static Item dullPickaxe; + public static Item celestialTestItem; + public static Item celestialToggleItem; } diff --git a/src/test/java/net/modificationstation/sltest/mixin/EntityMixin.java b/src/test/java/net/modificationstation/sltest/mixin/EntityMixin.java new file mode 100644 index 000000000..c9a3352cd --- /dev/null +++ b/src/test/java/net/modificationstation/sltest/mixin/EntityMixin.java @@ -0,0 +1,48 @@ +package net.modificationstation.sltest.mixin; + +import net.minecraft.entity.Entity; +import net.modificationstation.sltest.celestial.CelestialListener; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Entity.class) +public class EntityMixin { + + @Shadow public float yaw; + + @Shadow public float pitch; + + @Shadow public double velocityY; + + @Shadow public double velocityX; + + @Shadow public double velocityZ; + + @Inject( + method = "tick", + at = @At("HEAD") + ) + public void spinEntity(CallbackInfo ci) { + if (CelestialListener.spinningDimando.isActive()) { + this.yaw = (this.yaw + 0.1F) % 360.0F; + this.pitch = (this.pitch + 0.1F) % 360.0F; + } + if (CelestialListener.flyingDimando.isActive()) { + if (this.velocityY < 0.5F) { + this.velocityY += 0.05F; + } + } + if (CelestialListener.fallingDimando.isActive()) { + if (this.velocityY > -0.1F) { + this.velocityY -= 0.05F; + } + } + if (CelestialListener.longDimando.isActive()) { + this.velocityX *= 1.1F; + this.velocityZ *= 1.1F; + } + } +} diff --git a/src/test/java/net/modificationstation/sltest/mixin/GrassBlockMixin.java b/src/test/java/net/modificationstation/sltest/mixin/GrassBlockMixin.java new file mode 100644 index 000000000..ba743a569 --- /dev/null +++ b/src/test/java/net/modificationstation/sltest/mixin/GrassBlockMixin.java @@ -0,0 +1,26 @@ +package net.modificationstation.sltest.mixin; + +import net.minecraft.block.Block; +import net.minecraft.block.GrassBlock; +import net.minecraft.world.World; +import net.modificationstation.sltest.celestial.CelestialListener; +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; + +import java.util.Random; + +@Mixin(GrassBlock.class) +public class GrassBlockMixin { + @Inject( + method = "onTick", + at = @At("TAIL") + ) + public void setStuffOnFire(World world, int x, int y, int z, Random random, CallbackInfo ci) { + if (CelestialListener.burningDimando.isActive()) { + if (random.nextInt(100) == 0 && world.getBlockId(x, y + 1, z) == 0) world.method_200(x, y + 1, z, Block.FIRE.id); + } + } + +} diff --git a/src/test/java/net/modificationstation/sltest/texture/TextureListener.java b/src/test/java/net/modificationstation/sltest/texture/TextureListener.java index 1b35b2a02..76c06827d 100644 --- a/src/test/java/net/modificationstation/sltest/texture/TextureListener.java +++ b/src/test/java/net/modificationstation/sltest/texture/TextureListener.java @@ -26,6 +26,7 @@ public void registerTextures(TextureRegisterEvent event) { TEST_ANIMATED_BLOCK.get().textureId = terrain.addTexture(of(NAMESPACE, "blocks/testAnimatedBlock")).index; FREEZER.get().textureId = terrain.addTexture(of(NAMESPACE, "blocks/FreezerTop")).index; ((BlockFreezer) FREEZER.get()).sideTexture = terrain.addTexture(of(NAMESPACE, "blocks/FreezerSide")).index; + TIME_MACHINE_BLOCK.get().textureId = terrain.addTexture(of(NAMESPACE, "blocks/timeMachineBlock")).index; altarTextures[Direction.DOWN.ordinal()] = terrain.addTexture(of(NAMESPACE, "blocks/altar_bottom")).index; altarTextures[Direction.UP.ordinal()] = terrain.addTexture(of(NAMESPACE, "blocks/altar_top")).index; diff --git a/src/test/resources/assets/sltest/stationapi/lang/en_US.lang b/src/test/resources/assets/sltest/stationapi/lang/en_US.lang index 7193fc06f..79aad2055 100644 --- a/src/test/resources/assets/sltest/stationapi/lang/en_US.lang +++ b/src/test/resources/assets/sltest/stationapi/lang/en_US.lang @@ -18,4 +18,5 @@ item.@.variationBlockPassive.name=Variation Block Passive item.@.variationBlockActive.name=Variation Block Active item.@.test_shears.name=Test Shears item.@.pacifist_sword.name=Pacifist Sword -item.@.dull_pickaxe.name=Dull Pickaxe \ No newline at end of file +item.@.dull_pickaxe.name=Dull Pickaxe +tile.@.timeMachineBlock.name=Time Machine diff --git a/src/test/resources/assets/sltest/stationapi/textures/blocks/timeMachineBlock.png b/src/test/resources/assets/sltest/stationapi/textures/blocks/timeMachineBlock.png new file mode 100644 index 000000000..e8743cf6e Binary files /dev/null and b/src/test/resources/assets/sltest/stationapi/textures/blocks/timeMachineBlock.png differ diff --git a/src/test/resources/data/minecraft/stationapi/tags/blocks/mineable/pickaxe.json b/src/test/resources/data/minecraft/stationapi/tags/blocks/mineable/pickaxe.json new file mode 100644 index 000000000..04f9bde83 --- /dev/null +++ b/src/test/resources/data/minecraft/stationapi/tags/blocks/mineable/pickaxe.json @@ -0,0 +1,5 @@ +{ + "values": [ + "sltest:test_block" + ] +} \ No newline at end of file diff --git a/src/test/resources/data/sltest/stationapi/tags/blocks/needs_tool_level_modded.json b/src/test/resources/data/sltest/stationapi/tags/blocks/needs_tool_level_modded.json new file mode 100644 index 000000000..04f9bde83 --- /dev/null +++ b/src/test/resources/data/sltest/stationapi/tags/blocks/needs_tool_level_modded.json @@ -0,0 +1,5 @@ +{ + "values": [ + "sltest:test_block" + ] +} \ No newline at end of file diff --git a/src/test/resources/fabric.mod.json b/src/test/resources/fabric.mod.json index 3ef1d5b6f..b3db2af61 100644 --- a/src/test/resources/fabric.mod.json +++ b/src/test/resources/fabric.mod.json @@ -33,7 +33,8 @@ "net.modificationstation.sltest.datafixer.DataFixerListener", "net.modificationstation.sltest.worldgen.TestWorldgenListener", "net.modificationstation.sltest.bonemeal.BonemealListener", - "net.modificationstation.sltest.dispenser.DispenserListener" + "net.modificationstation.sltest.dispenser.DispenserListener", + "net.modificationstation.sltest.celestial.CelestialListener" ], "stationapi:event_bus_client": [ "net.modificationstation.sltest.gui.GuiListener", diff --git a/src/test/resources/sltest.mixins.json b/src/test/resources/sltest.mixins.json index 483f20575..8268c2b53 100644 --- a/src/test/resources/sltest.mixins.json +++ b/src/test/resources/sltest.mixins.json @@ -8,7 +8,9 @@ "MixinLevel", "MixinNetherLevelSource", "MixinObsidian", - "OverworldTestMixin" + "OverworldTestMixin", + "GrassBlockMixin", + "EntityMixin" ], "server": [ ], diff --git a/station-celestial-events-v0/build.gradle.kts b/station-celestial-events-v0/build.gradle.kts new file mode 100644 index 000000000..52c0fbfe8 --- /dev/null +++ b/station-celestial-events-v0/build.gradle.kts @@ -0,0 +1,9 @@ +import net.modificationstation.stationapi.gradle.SubprojectHelpers.getSubprojectVersion +import net.modificationstation.stationapi.gradle.SubprojectHelpers.addModuleDependencies +base.archivesName.set("station-celestial-events-v0") +version = getSubprojectVersion(project, "1.0.0") + +addModuleDependencies(project, + "station-api-base", + "station-registry-api-v0", +) \ No newline at end of file diff --git a/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialActivityStateManager.java b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialActivityStateManager.java new file mode 100644 index 000000000..1d14898bf --- /dev/null +++ b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialActivityStateManager.java @@ -0,0 +1,7 @@ +package net.modificationstation.stationapi.api.celestial; + +public interface CelestialActivityStateManager { + + CelestialEventActivityState getCelestialEvents(); + CelestialTimeManager getCelestialTimeManager(); +} diff --git a/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialEvent.java b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialEvent.java new file mode 100644 index 000000000..e06a14654 --- /dev/null +++ b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialEvent.java @@ -0,0 +1,266 @@ +package net.modificationstation.stationapi.api.celestial; + +import lombok.Getter; +import net.minecraft.world.World; +import net.modificationstation.stationapi.api.registry.Registry; +import net.modificationstation.stationapi.api.util.Identifier; + +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +/** + * Contains the most important logic for automatically starting and stopping the event. + * It is recommended to add the event to CelestialTimeManger for automatic management. + * Inheritance is possible to add custom logic. + */ +public class CelestialEvent { + @Getter + private final Identifier identifier; + private final int frequency; + private float chance = 1; + private int dayLength = 24000; + private int dayOffset = 0; + private int startingDaytime = 0; + private int endingDaytime = 0; + private int extraDays = 0; + @Getter + private boolean active; + private boolean initializationNeeded = true; + private final List incompatibleEvents = new LinkedList<>(); + private final List dependencies = new LinkedList<>(); + + /** + * Primary constructor for the event, containing the required parameters. + * + * @param frequency Specifies how many day apart the events should be. The bigger the number, the longer the distance. + * @param identifier Event identifier. + */ + public CelestialEvent(int frequency, Identifier identifier) { + this.frequency = frequency; + this.identifier = identifier; + Registry.register(CelestialEventRegistry.INSTANCE, identifier,this); + } + + /** + * Builder method for optional chance value. + * + * @param world The world this event is being called on. + * @param chance Value ranging from 0.0F to 1.0F, representing percentage chance. + * @return The object itself. + */ + public CelestialEvent setChance(World world, float chance) { + this.chance = chance; + return this; + } + + /** + * Builder method for optional day length value. + * Intended to be used in dimensions where day time is different. + * This will however need a custom time manager to account for the different day length. + * + * @param world The world this event is being called on. + * @param dayLength Length of day specified in ticks. + * @return The object itself. + */ + public CelestialEvent setDayLength(World world, int dayLength) { + this.dayLength = dayLength; + return this; + } + + /** + * Builder method for optional day offset. + * + * @param world The world this event is being called on. + * @param dayOffset Offsets the frequency by the specified number of days. + * @return The object itself. + */ + public CelestialEvent setDayOffset(World world, int dayOffset) { + this.dayOffset = dayOffset; + return this; + } + + /** + * Builder method for optional extra days. + * + * @param world The world this event is being called on. + * @param extraDays Extends the event duration by the specified number of days. + * @return The object itself. + */ + public CelestialEvent setExtraDays(World world, int extraDays) { + this.extraDays = extraDays; + return this; + } + + /** + * Attempts to activate the event. Checks if the activation already was attempted within the valid time frame. + * Takes activation chance and incompatible events into account. + * Method gets called by CelestialTimeManager in case the event is added to it. + * Can be called manually as well. + * + * @param world The world this event is being called on. + * @param worldTime Current time in the world measured in ticks. + * @param random Locally used randomizer, seed is irrelevant. + * @return True if the event met the criteria to be activated, otherwise false. + */ + public boolean activateEvent(World world, long worldTime, Random random) { + CelestialEventActivityState activityState = ((CelestialActivityStateManager) world).getCelestialEvents(); + if (initializationNeeded) { + activityState = initializeEvent(world); + } + if (active) { + activityState.set(identifier, true, true); + return true; + } + for (CelestialEvent otherEvent : incompatibleEvents) { + if (otherEvent == null) continue; + if (otherEvent.isActive()) { + activityState.set(identifier, false, true); + return false; + } + } + for (CelestialEvent otherEvent : dependencies) { + if (otherEvent == null) continue; + if (!otherEvent.isActive()) { + activityState.set(identifier, false, true); + return false; + } + } + long days = (world.getTime() / dayLength) + dayOffset; + if (days % frequency > extraDays) { + activityState.setAttemptedActivate(identifier, false); + return false; + } + if (!activityState.hasAttemptedActivation(identifier)) { + active = days % frequency <= extraDays && random.nextFloat() <= chance; + if (active) { + activityState.set(identifier, true, true); + onActivation(world); + } + } + return active; + } + + /** + * Called precisely on event activation. + * Can be customized by inheriting from this class. + * Has access to the world object for more versatility. + * + * @param world The world this event is being called on. + */ + public void onActivation(World world) { + } + + /** + * Called precisely on event deactivation. + * Can be customized by inheriting from this class. + * Has access to the world object for more versatility. + * + * @param world The world this event is being called on. + */ + public void onDeactivation(World world) { + } + + /** + * Updates the activity status of an already active event. + * Used by CelestialTimeManager 4 times per day. + * Checks for validity of the time interval but not for chance and incompatibility. + * + * @param world The world this event is being called on. + * @param worldTime Current time in the world measured in ticks. + */ + public void updateEvent(World world, long worldTime) { + CelestialEventActivityState activityState = ((CelestialActivityStateManager) world).getCelestialEvents(); + if (initializationNeeded) { + activityState = initializeEvent(world); + } + if (!active && !activityState.isActive(identifier)) return; + worldTime -= startingDaytime; + worldTime += endingDaytime; + long days = (worldTime / dayLength) + dayOffset; + active = days % frequency <= extraDays; + activityState.setActive(identifier, active); + if (!active) { + onDeactivation(world); + activityState.setAttemptedActivate(identifier, false); + } + activityState.markDirty(); + } + + /** + * Initializes the event by synchronizing it with NBT data. + * Only used internally. + * + * @param world The world this event is being called on. + * @return The initialized CelestialEventActivityState. + */ + private CelestialEventActivityState initializeEvent(World world) { + initializationNeeded = false; + CelestialEventActivityState activityState = ((CelestialActivityStateManager) world).getCelestialEvents(); + active = activityState.isActive(identifier); + return activityState; + } + + /** + * Manual way of stopping event. + * Intended for command systems. + * + * @param world The world this event is being called on. + */ + public void stopEvent(World world) { + onDeactivation(world); + if (active) { + CelestialEventActivityState activityState = ((CelestialActivityStateManager) world).getCelestialEvents(); + if (initializationNeeded) { + activityState = initializeEvent(world); + } + activityState.setActive(identifier, false); + System.out.println("Stopping event " + identifier); + active = false; + } + } + + /** + * Called by CelestialTimeManager to specify the valid time frame of the event. + * Time values can be specified as 4 values representing ticks in a day: + * Morning: 0, Noon: 6000, Evening: 12000, Midnight: 18000 + * + * @param startingDaytime Beginning daytime of the event. + * @param endingDaytime Ending daytime of the event. + */ + public void setInterval(int startingDaytime, int endingDaytime) { + this.startingDaytime = startingDaytime; + this.endingDaytime = endingDaytime; + } + + /** + * Adds events to prevent them from happening simultaneously. + * Automatically adds incompatibility for both directions. + * + * @param otherEvent Event to be added to the incompatibility list. + */ + public void addIncompatibleEvent(CelestialEvent otherEvent) { + if (incompatibleEvents.contains(otherEvent)) return; + incompatibleEvents.add(otherEvent); + otherEvent.addIncompatibleEvent(this); + } + + /** + * Adds dependency to an event, meaning another event has to happen for this one to happen. + * Dependency only gets added into one direction. + * + * @param dependency Event to be added to the dependencies list. + */ + public void addDependency(CelestialEvent dependency) { + if (dependencies.contains(dependency)) return; + dependencies.add(dependency); + } + + /** + * Used by CelestialTimeManager to handle an edge-case where events would not be loaded when reloading the same world. + */ + public void markForInitialization(World world) { + initializeEvent(world); + initializationNeeded = false; + } +} diff --git a/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialEventActivityState.java b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialEventActivityState.java new file mode 100644 index 000000000..7d811f4f7 --- /dev/null +++ b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialEventActivityState.java @@ -0,0 +1,73 @@ +package net.modificationstation.stationapi.api.celestial; + +import net.minecraft.nbt.NbtByte; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.world.PersistentState; +import net.modificationstation.stationapi.api.util.Identifier; +import net.modificationstation.stationapi.mixin.celestial.NbtCompoundAccessor; + +import java.util.HashMap; + +/** + * Manages important NBT data to ensure correct saving and loading of active celestial events. + */ +public class CelestialEventActivityState extends PersistentState { + + public static final String ID = "stationapi_celestial_events"; + + private HashMap activeEvents = new HashMap<>(); + private HashMap attemptedActivatedEvents = new HashMap<>(); + + public CelestialEventActivityState(String id) { + super(id); + } + + public void set(Identifier identifier, boolean active, boolean attemptedActivate) { + activeEvents.put(identifier, active); + attemptedActivatedEvents.put(identifier, attemptedActivate); + markDirty(); + } + + public void setActive(Identifier identifier, boolean active) { + activeEvents.put(identifier, active); + markDirty(); + } + + public void setAttemptedActivate(Identifier identifier, boolean attemptedActivate) { + attemptedActivatedEvents.put(identifier, attemptedActivate); + markDirty(); + } + + public boolean isActive(Identifier identifier) { + return activeEvents.containsKey(identifier) && activeEvents.get(identifier); + } + + public boolean hasAttemptedActivation(Identifier identifier) { + return attemptedActivatedEvents.containsKey(identifier) && attemptedActivatedEvents.get(identifier); + } + + @Override + public void readNbt(NbtCompound nbt) { + HashMap activeEvents = new HashMap<>(); + //noinspection unchecked + ((NbtCompoundAccessor) nbt.getCompound("activeEvents")).getEntries().forEach((ide, nbtBoolean) -> activeEvents.put(Identifier.of((String) ide), ((NbtByte) nbtBoolean).value == 1)); + this.activeEvents = activeEvents; + + HashMap attemptedActivatedEvents = new HashMap<>(); + //noinspection unchecked + ((NbtCompoundAccessor) nbt.getCompound("activeEvents")).getEntries().forEach((ide, nbtBoolean) -> activeEvents.put(Identifier.of((String) ide), ((NbtByte) nbtBoolean).value == 1)); + this.attemptedActivatedEvents = attemptedActivatedEvents; + } + + @Override + public void writeNbt(NbtCompound nbt) { + NbtCompound compound = new NbtCompound(); + activeEvents.forEach((identifier, aBoolean) -> compound.putBoolean(String.valueOf(identifier), aBoolean)); + nbt.put("activeEvents", compound); + + NbtCompound compound1 = new NbtCompound(); + attemptedActivatedEvents.forEach((identifier, aBoolean) -> compound1.putBoolean(String.valueOf(identifier), aBoolean)); + nbt.put("attemptedActivatedEvents", (NbtElement) compound); + } +} diff --git a/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialEventRegistry.java b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialEventRegistry.java new file mode 100644 index 000000000..7b6b2b06b --- /dev/null +++ b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialEventRegistry.java @@ -0,0 +1,34 @@ +package net.modificationstation.stationapi.api.celestial; + +import com.mojang.serialization.Lifecycle; +import net.minecraft.world.World; +import net.modificationstation.stationapi.api.StationAPI; +import net.modificationstation.stationapi.api.registry.Registry; +import net.modificationstation.stationapi.api.registry.RegistryKey; +import net.modificationstation.stationapi.api.registry.SimpleRegistry; +import net.modificationstation.stationapi.api.util.Identifier; + +/** + * Automatically handles initialization of celestial events. + * Ensures that all celestial events are loaded correctly. + */ +public class CelestialEventRegistry extends SimpleRegistry { + public static final Identifier IDENTIFIER = Identifier.of(StationAPI.NAMESPACE, "celestial_events"); + public static final RegistryKey> KEY = RegistryKey.ofRegistry(IDENTIFIER); + + public static final CelestialEventRegistry INSTANCE = new CelestialEventRegistry(); + + private CelestialEventRegistry() { + super(KEY, Lifecycle.stable()); + } + + /** + * Initializes all events when the world is loaded, ensures correct loading of active events. + */ + public void initializeEvents(World world) { + stream().forEach(celestialEvent -> { + if (celestialEvent == null) return; + celestialEvent.markForInitialization(world); + }); + } +} diff --git a/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialTimeManager.java b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialTimeManager.java new file mode 100644 index 000000000..f3d8113fe --- /dev/null +++ b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/CelestialTimeManager.java @@ -0,0 +1,148 @@ +package net.modificationstation.stationapi.api.celestial; + +import net.minecraft.world.World; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +/** + * Manages the activation and deactivation of celestial events. + * Has been injected into WorldProperties using the WorldPropertiesMixin in station-world-events, does not need to be handled by mods. + * Events need to be added during the registering process. + * Uses lists to schedule event updates during the correct time of day. + * Called four times per day. + */ +public class CelestialTimeManager { + private final List MORNING_START = new ArrayList<>(); + private final List NOON_START = new LinkedList<>(); + private final List EVENING_START = new LinkedList<>(); + private final List MIDNIGHT_START = new LinkedList<>(); + private final List ALL_EVENTS = new LinkedList<>(); + + private boolean morningActivation = false; + private boolean noonActivation = false; + private boolean eveningActivation = false; + private boolean midnightActivation = false; + + private long lastCheckedDay = 0; + + private final Random RANDOM = new Random(); + + private final World world; + + public CelestialTimeManager(World world) { + this.world = world; + } + + /** + * Add a celestial event to the time manager. + * + * @param celestialEvent Event to be added. + * @param start Time of day at which the event begins. + * @param stop Time of day at which the event ends. + */ + public void addCelestialEvent(CelestialEvent celestialEvent, DayQuarter start, DayQuarter stop) { + switch (start) { + case MORNING -> MORNING_START.add(celestialEvent); + case NOON -> NOON_START.add(celestialEvent); + case EVENING -> EVENING_START.add(celestialEvent); + case MIDNIGHT -> MIDNIGHT_START.add(celestialEvent); + } + ALL_EVENTS.add(celestialEvent); + celestialEvent.setInterval(start.ordinal() * 6000, Math.abs(stop.ordinal() * 6000 - start.ordinal() * 6000)); + } + + /** + * Attempts to start all morning events. Called once per day. + * + * @param time World time in ticks. + * @param currentDay Current day in the world. + */ + public void startMorningEvents(long time, long currentDay) { + if (morningActivation && lastCheckedDay == currentDay) return; + lastCheckedDay = currentDay; + morningActivation = true; + noonActivation = false; + eveningActivation = false; + midnightActivation = false; + updateEvents(time); + for (CelestialEvent celestialEvent : MORNING_START) { + if (celestialEvent == null) continue; + celestialEvent.activateEvent(world, time, RANDOM); + } + } + + /** + * Attempts to start all noon events. Called once per day. + * + * @param time World time in ticks. + * @param currentDay Current day in the world. + */ + public void startNoonEvents(long time, long currentDay) { + if (noonActivation && lastCheckedDay == currentDay) return; + lastCheckedDay = currentDay; + morningActivation = false; + noonActivation = true; + eveningActivation = false; + midnightActivation = false; + updateEvents(time); + for (CelestialEvent celestialEvent : NOON_START) { + if (celestialEvent == null) continue; + celestialEvent.activateEvent(world, time, RANDOM); + } + } + + /** + * Attempts to start all evening events. Called once per day. + * + * @param time World time in ticks. + * @param currentDay Current day in the world. + */ + public void startEveningEvents(long time, long currentDay) { + if (eveningActivation && lastCheckedDay == currentDay) return; + lastCheckedDay = currentDay; + morningActivation = false; + noonActivation = false; + eveningActivation = true; + midnightActivation = false; + updateEvents(time); + for (CelestialEvent celestialEvent : EVENING_START) { + if (celestialEvent == null) continue; + celestialEvent.activateEvent(world, time, RANDOM); + } + } + + /** + * Attempts to start all midnight events. Called once per day. + * + * @param time World time in ticks. + * @param currentDay Current day in the world. + */ + public void startMidnightEvents(long time, long currentDay) { + if (midnightActivation && lastCheckedDay == currentDay) return; + lastCheckedDay = currentDay; + morningActivation = false; + noonActivation = false; + eveningActivation = false; + midnightActivation = true; + updateEvents(time); + for (CelestialEvent celestialEvent : MIDNIGHT_START) { + if (celestialEvent == null) continue; + celestialEvent.activateEvent(world, time, RANDOM); + } + } + + /** + * Updates activity state of all events. Called four times per day. + * + * @param time World time in ticks. + */ + public void updateEvents(long time) { + for (CelestialEvent celestialEvent : ALL_EVENTS) { + if (celestialEvent == null) continue; + celestialEvent.updateEvent(world, time); + } + } +} diff --git a/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/DayQuarter.java b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/DayQuarter.java new file mode 100644 index 000000000..69ddfdda9 --- /dev/null +++ b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/DayQuarter.java @@ -0,0 +1,8 @@ +package net.modificationstation.stationapi.api.celestial; + +/** + * Used for celestial event time management. + */ +public enum DayQuarter { + MORNING, NOON, EVENING, MIDNIGHT +} diff --git a/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/WorldPropertiesWithWorld.java b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/WorldPropertiesWithWorld.java new file mode 100644 index 000000000..b54ca16d5 --- /dev/null +++ b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/celestial/WorldPropertiesWithWorld.java @@ -0,0 +1,9 @@ +package net.modificationstation.stationapi.api.celestial; + +import net.minecraft.world.World; + +public interface WorldPropertiesWithWorld { + + void setWorld(World world); + World getWorld(); +} diff --git a/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/event/celestial/CelestialRegisterEvent.java b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/event/celestial/CelestialRegisterEvent.java new file mode 100644 index 000000000..57caf7b61 --- /dev/null +++ b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/api/event/celestial/CelestialRegisterEvent.java @@ -0,0 +1,6 @@ +package net.modificationstation.stationapi.api.event.celestial; + +import net.mine_diver.unsafeevents.Event; + +public class CelestialRegisterEvent extends Event { +} diff --git a/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/NbtCompoundAccessor.java b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/NbtCompoundAccessor.java new file mode 100644 index 000000000..cd651e53e --- /dev/null +++ b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/NbtCompoundAccessor.java @@ -0,0 +1,17 @@ +package net.modificationstation.stationapi.mixin.celestial; + +import net.minecraft.nbt.NbtCompound; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(NbtCompound.class) +public interface NbtCompoundAccessor { + + @Accessor + Map getEntries(); + + @Accessor + void setEntries(Map value); +} diff --git a/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/WorldMixin.java b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/WorldMixin.java new file mode 100644 index 000000000..67126315b --- /dev/null +++ b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/WorldMixin.java @@ -0,0 +1,55 @@ +package net.modificationstation.stationapi.mixin.celestial; + +import net.minecraft.world.PersistentState; +import net.minecraft.world.World; +import net.minecraft.world.WorldProperties; +import net.modificationstation.stationapi.api.celestial.*; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(World.class) +abstract +class WorldMixin implements CelestialActivityStateManager { + + @Shadow public abstract PersistentState getOrCreateState(Class stateClass, String id); + @Shadow public abstract void setState(String id, PersistentState state); + + @Shadow protected WorldProperties properties; + @Unique + private CelestialEventActivityState celestialEventActivityState; + @Unique + private CelestialTimeManager celestialTimeManager; + + @Inject( + method = { + "(Lnet/minecraft/world/dimension/DimensionData;Ljava/lang/String;Lnet/minecraft/world/dimension/Dimension;J)V", + "(Lnet/minecraft/world/World;Lnet/minecraft/world/dimension/Dimension;)V", + "(Lnet/minecraft/world/dimension/DimensionData;Ljava/lang/String;JLnet/minecraft/world/dimension/Dimension;)V" + }, + at = @At("RETURN") + ) + private void stationapi_onCor1(CallbackInfo ci) { + ((WorldPropertiesWithWorld) properties).setWorld((World) (Object) this); + celestialEventActivityState = (CelestialEventActivityState) getOrCreateState(CelestialEventActivityState.class, CelestialEventActivityState.ID); + if (celestialEventActivityState == null) { + celestialEventActivityState = new CelestialEventActivityState(CelestialEventActivityState.ID); + } + celestialTimeManager = new CelestialTimeManager((World) (Object) this); + setState(CelestialEventActivityState.ID, celestialEventActivityState); + CelestialEventRegistry.INSTANCE.initializeEvents((World) (Object) this); + } + + @Override + public CelestialEventActivityState getCelestialEvents() { + return celestialEventActivityState; + } + + @Override + public CelestialTimeManager getCelestialTimeManager() { + return celestialTimeManager; + } +} diff --git a/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/WorldPropertiesMixin.java b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/WorldPropertiesMixin.java new file mode 100644 index 000000000..09374c4b7 --- /dev/null +++ b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/WorldPropertiesMixin.java @@ -0,0 +1,51 @@ +package net.modificationstation.stationapi.mixin.celestial; + +import net.minecraft.world.World; +import net.minecraft.world.WorldProperties; +import net.modificationstation.stationapi.api.celestial.CelestialActivityStateManager; +import net.modificationstation.stationapi.api.celestial.WorldPropertiesWithWorld; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(WorldProperties.class) +public class WorldPropertiesMixin implements WorldPropertiesWithWorld { + @Shadow private long time; + + @Unique + private World world; + + @Inject( + method = "setTime", + at = @At("HEAD") + ) + private void stationapi_celestialEventTimeManager(CallbackInfo ci) { + if (((CelestialActivityStateManager) world).getCelestialTimeManager() == null) { + return; + } + long daytime = time % 24000; + long currentDay = time / 24000; + if (daytime >= 0 && daytime < 6000) { + ((CelestialActivityStateManager) world).getCelestialTimeManager().startMorningEvents(time, currentDay); + } else if (daytime >= 6000 && daytime < 12000) { + ((CelestialActivityStateManager) world).getCelestialTimeManager().startNoonEvents(time, currentDay); + } else if (daytime >= 12000 && daytime < 18000) { + ((CelestialActivityStateManager) world).getCelestialTimeManager().startEveningEvents(time, currentDay); + } else if (daytime >= 18000) { + ((CelestialActivityStateManager) world).getCelestialTimeManager().startMidnightEvents(time, currentDay); + } + } + + @Override + public void setWorld(World world) { + this.world = world; + } + + @Override + public World getWorld() { + return world; + } +} diff --git a/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/client/MinecraftMixin.java b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/client/MinecraftMixin.java new file mode 100644 index 000000000..fdddcc82e --- /dev/null +++ b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/client/MinecraftMixin.java @@ -0,0 +1,20 @@ +package net.modificationstation.stationapi.mixin.celestial.client; + +import net.minecraft.client.Minecraft; +import net.modificationstation.stationapi.api.StationAPI; +import net.modificationstation.stationapi.api.event.celestial.CelestialRegisterEvent; +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(value = Minecraft.class, priority = 799) +class MinecraftMixin { + @Inject( + method = "run", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;init()V", shift = At.Shift.AFTER) + ) + private void stationapi_endInit(CallbackInfo ci) { + StationAPI.EVENT_BUS.post(new CelestialRegisterEvent()); + } +} diff --git a/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/server/MinecraftServerMixin.java b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/server/MinecraftServerMixin.java new file mode 100644 index 000000000..ef877bcb9 --- /dev/null +++ b/station-celestial-events-v0/src/main/java/net/modificationstation/stationapi/mixin/celestial/server/MinecraftServerMixin.java @@ -0,0 +1,25 @@ +package net.modificationstation.stationapi.mixin.celestial.server; + +import net.minecraft.server.MinecraftServer; +import net.modificationstation.stationapi.api.StationAPI; +import net.modificationstation.stationapi.api.event.celestial.CelestialRegisterEvent; +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(value = MinecraftServer.class, priority = 799) +class MinecraftServerMixin { + @Inject( + method = "method_2166", + at = @At( + value = "INVOKE", + target = "Ljava/util/logging/Logger;info(Ljava/lang/String;)V", + ordinal = 3, + shift = At.Shift.AFTER + ) + ) + private void stationapi_endInit(CallbackInfoReturnable cir) { + StationAPI.EVENT_BUS.post(new CelestialRegisterEvent()); + } +} \ No newline at end of file diff --git a/station-celestial-events-v0/src/main/resources/assets/station-celestial-events-v0/icon.png b/station-celestial-events-v0/src/main/resources/assets/station-celestial-events-v0/icon.png new file mode 100644 index 000000000..0affee829 Binary files /dev/null and b/station-celestial-events-v0/src/main/resources/assets/station-celestial-events-v0/icon.png differ diff --git a/station-celestial-events-v0/src/main/resources/fabric.mod.json b/station-celestial-events-v0/src/main/resources/fabric.mod.json new file mode 100644 index 000000000..ad1caed31 --- /dev/null +++ b/station-celestial-events-v0/src/main/resources/fabric.mod.json @@ -0,0 +1,38 @@ +{ + "schemaVersion": 1, + "id": "station-celestial-events-v0", + "version": "${version}", + + "name": "Station Celestial Events (v0)", + "description": "Provides some events for time-related events.", + "authors": [ + "Modification Station" + ], + "contact": { + "homepage": "https://glass-launcher.net/repo/mod/stationapi", + "sources": "https://github.com/ModificationStation/StationAPI", + "issues": "https://github.com/ModificationStation/StationAPI/issues" + }, + + "license": "MIT", + "icon": "assets/station-celestial-events-v0/icon.png", + + "environment": "*", + "mixins": [ + "station-celestial-events-v0.mixins.json" + ], + + "depends": { + "fabricloader": "*", + "minecraft": "1.0.0-beta.7.3" + }, + + "custom": { + "modmenu:api": true, + + "loom:injected_interfaces": { + "net/minecraft/class_18": ["net/modificationstation/stationapi/api/celestial/CelestialActivityStateManager"], + "net/minecraft/class_7": ["net/modificationstation/stationapi/api/celestial/WorldPropertiesWithWorld"] + } + } +} \ No newline at end of file diff --git a/station-celestial-events-v0/src/main/resources/station-celestial-events-v0.mixins.json b/station-celestial-events-v0/src/main/resources/station-celestial-events-v0.mixins.json new file mode 100644 index 000000000..b8b381412 --- /dev/null +++ b/station-celestial-events-v0/src/main/resources/station-celestial-events-v0.mixins.json @@ -0,0 +1,20 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "net.modificationstation.stationapi.mixin.celestial", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "NbtCompoundAccessor", + "WorldMixin", + "WorldPropertiesMixin" + ], + "client": [ + "client.MinecraftMixin" + ], + "server": [ + "server.MinecraftServerMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/station-items-v0/src/main/java/net/modificationstation/stationapi/api/client/event/gui/screen/container/TooltipRenderEvent.java b/station-items-v0/src/main/java/net/modificationstation/stationapi/api/client/event/gui/screen/container/TooltipRenderEvent.java index 910373f32..a52aba416 100644 --- a/station-items-v0/src/main/java/net/modificationstation/stationapi/api/client/event/gui/screen/container/TooltipRenderEvent.java +++ b/station-items-v0/src/main/java/net/modificationstation/stationapi/api/client/event/gui/screen/container/TooltipRenderEvent.java @@ -21,13 +21,6 @@ public class TooltipRenderEvent extends ItemStackEvent { public final HandledScreen container; public final TextRenderer textManager; public final PlayerInventory inventory; - /** - * These will be removed in a3, due to the fact the container var already exists. - */ - @Deprecated(forRemoval = true) - public final int - containerX, - containerY; public final int mouseX, mouseY; diff --git a/station-items-v0/src/main/java/net/modificationstation/stationapi/impl/client/gui/screen/container/CustomTooltipRendererImpl.java b/station-items-v0/src/main/java/net/modificationstation/stationapi/impl/client/gui/screen/container/CustomTooltipRendererImpl.java index edfe77658..ac3f3173b 100644 --- a/station-items-v0/src/main/java/net/modificationstation/stationapi/impl/client/gui/screen/container/CustomTooltipRendererImpl.java +++ b/station-items-v0/src/main/java/net/modificationstation/stationapi/impl/client/gui/screen/container/CustomTooltipRendererImpl.java @@ -1,19 +1,23 @@ package net.modificationstation.stationapi.impl.client.gui.screen.container; import net.mine_diver.unsafeevents.listener.EventListener; +import net.minecraft.client.gui.DrawContext; import net.modificationstation.stationapi.api.StationAPI; import net.modificationstation.stationapi.api.client.TooltipHelper; import net.modificationstation.stationapi.api.client.event.gui.screen.container.TooltipRenderEvent; import net.modificationstation.stationapi.api.mod.entrypoint.Entrypoint; import net.modificationstation.stationapi.api.mod.entrypoint.EventBusPolicy; import net.modificationstation.stationapi.mixin.item.client.DrawContextAccessor; +import net.modificationstation.stationapi.mixin.item.client.HandledScreenAccessor; -import java.util.*; +import java.util.ArrayList; import java.util.stream.IntStream; @Entrypoint(eventBus = @EventBusPolicy(registerInstance = false)) @EventListener(phase = StationAPI.INTERNAL_PHASE) public final class CustomTooltipRendererImpl { + private static final DrawContext CONTEXT = new DrawContext(); + @EventListener private static void renderCustomTooltips(TooltipRenderEvent event) { if(event.isCanceled()) { @@ -24,9 +28,9 @@ private static void renderCustomTooltips(TooltipRenderEvent event) { if (!newTooltip.isEmpty()) { newTooltip.stream().mapToInt(event.textManager::getWidth).max().ifPresent(tooltipWidth -> { - int tooltipX = event.mouseX - event.containerX + 12; - int tooltipY = event.mouseY - event.containerY - 12; - ((DrawContextAccessor) event.container).invokeFillGradient(tooltipX - 3, tooltipY - 3, tooltipX + tooltipWidth + 3, tooltipY + (8 * newTooltip.size()) + (3 * newTooltip.size()), -1073741824, -1073741824); + int tooltipX = event.mouseX - (event.container == null ? 0 : ((event.container.width - ((HandledScreenAccessor) event.container).stationapi_getBackgroundWidth()) / 2)) + 12; + int tooltipY = event.mouseY - (event.container == null ? 0 : ((event.container.height - ((HandledScreenAccessor) event.container).stationapi_getBackgroundHeight()) / 2)) - 12; + ((DrawContextAccessor) CONTEXT).invokeFillGradient(tooltipX - 3, tooltipY - 3, tooltipX + tooltipWidth + 3, tooltipY + (8 * newTooltip.size()) + (3 * newTooltip.size()), -1073741824, -1073741824); IntStream.range(0, newTooltip.size()).forEach(currentTooltip -> event.textManager.drawWithShadow(newTooltip.get(currentTooltip), tooltipX, tooltipY + (8 * currentTooltip) + (3 * currentTooltip), -1)); }); event.cancel(); diff --git a/station-items-v0/src/main/java/net/modificationstation/stationapi/mixin/item/client/ContainerScreenMixin.java b/station-items-v0/src/main/java/net/modificationstation/stationapi/mixin/item/client/ContainerScreenMixin.java index 1f2fc98e5..d69cf53ef 100644 --- a/station-items-v0/src/main/java/net/modificationstation/stationapi/mixin/item/client/ContainerScreenMixin.java +++ b/station-items-v0/src/main/java/net/modificationstation/stationapi/mixin/item/client/ContainerScreenMixin.java @@ -36,7 +36,6 @@ private void stationapi_renderTooltip(int mouseX, int mouseY, float delta, Callb .container((HandledScreen) (Object) this) .textManager(this.textRenderer) .inventory(inventory) - .containerX(containerX).containerY(containerY) .mouseX(mouseX).mouseY(mouseY) .delta(delta) .originalTooltip(originalTooltip) diff --git a/station-items-v0/src/main/java/net/modificationstation/stationapi/mixin/item/client/HandledScreenAccessor.java b/station-items-v0/src/main/java/net/modificationstation/stationapi/mixin/item/client/HandledScreenAccessor.java new file mode 100644 index 000000000..495231844 --- /dev/null +++ b/station-items-v0/src/main/java/net/modificationstation/stationapi/mixin/item/client/HandledScreenAccessor.java @@ -0,0 +1,14 @@ +package net.modificationstation.stationapi.mixin.item.client; + +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(HandledScreen.class) +public interface HandledScreenAccessor { + @Accessor("backgroundWidth") + int stationapi_getBackgroundWidth(); + + @Accessor("backgroundHeight") + int stationapi_getBackgroundHeight(); +} diff --git a/station-items-v0/src/main/resources/station-items-v0.mixins.json b/station-items-v0/src/main/resources/station-items-v0.mixins.json index 77cd0e61a..2fd54f374 100644 --- a/station-items-v0/src/main/resources/station-items-v0.mixins.json +++ b/station-items-v0/src/main/resources/station-items-v0.mixins.json @@ -30,6 +30,7 @@ "client.ContainerScreenMixin", "client.DrawContextAccessor", "client.GameRendererMixin", + "client.HandledScreenAccessor", "client.InteractionManagerMixin", "client.ItemRendererMixin", "client.MultiplayerInteractionManagerMixin", diff --git a/station-lifecycle-events-v0/src/main/resources/station-lifecycle-events-v0.mixins.json b/station-lifecycle-events-v0/src/main/resources/station-lifecycle-events-v0.mixins.json index bdfda5905..93d37150f 100644 --- a/station-lifecycle-events-v0/src/main/resources/station-lifecycle-events-v0.mixins.json +++ b/station-lifecycle-events-v0/src/main/resources/station-lifecycle-events-v0.mixins.json @@ -3,12 +3,10 @@ "minVersion": "0.8", "package": "net.modificationstation.stationapi.mixin.lifecycle", "compatibilityLevel": "JAVA_17", - "mixins": [ - "server.ServerPlayNetworkHandlerMixin" - ], "server": [ "server.MinecraftServerMixin", - "server.ServerLoginNetworkHandlerMixin" + "server.ServerLoginNetworkHandlerMixin", + "server.ServerPlayNetworkHandlerMixin" ], "client": [ "client.ClientNetworkHandlerMixin", diff --git a/station-recipes-v0/src/main/java/net/modificationstation/stationapi/api/recipe/StationRecipe.java b/station-recipes-v0/src/main/java/net/modificationstation/stationapi/api/recipe/StationRecipe.java deleted file mode 100644 index 047a084fb..000000000 --- a/station-recipes-v0/src/main/java/net/modificationstation/stationapi/api/recipe/StationRecipe.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.modificationstation.stationapi.api.recipe; - -import net.minecraft.item.ItemStack; - -/** - * This entire interface is slated for removal in alpha 3. - * Stop using it, it's shit and was made specifically for HMI, for all the wrong reasons. - * - calm - */ -@Deprecated(forRemoval = true) -public interface StationRecipe { - - @Deprecated(forRemoval = true) - ItemStack[] getIngredients(); - - @Deprecated(forRemoval = true) - ItemStack[] getOutputs(); -} diff --git a/station-recipes-v0/src/main/java/net/modificationstation/stationapi/impl/recipe/StationShapedRecipe.java b/station-recipes-v0/src/main/java/net/modificationstation/stationapi/impl/recipe/StationShapedRecipe.java index bd90c04fd..d57e08eac 100644 --- a/station-recipes-v0/src/main/java/net/modificationstation/stationapi/impl/recipe/StationShapedRecipe.java +++ b/station-recipes-v0/src/main/java/net/modificationstation/stationapi/impl/recipe/StationShapedRecipe.java @@ -5,17 +5,11 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.recipe.CraftingRecipe; -import net.modificationstation.stationapi.api.recipe.StationRecipe; -import net.modificationstation.stationapi.api.registry.ItemRegistry; import net.modificationstation.stationapi.api.tag.TagKey; import java.util.*; -import java.util.function.Function; - -public class StationShapedRecipe implements CraftingRecipe, StationRecipe { - - private static final Random RANDOM = new Random(); +public class StationShapedRecipe implements CraftingRecipe { public final int width, height; private final Either, ItemStack>[] grid; public final ItemStack output; @@ -84,25 +78,6 @@ public ItemStack getOutput() { return output.copy(); } - @Override - public ItemStack[] getIngredients() { - ItemStack[] stacks = new ItemStack[9]; - for (int h = 0; h < height; h++) - for (int w = 0; w < width; w++) { - int localId = (h * width) + w; - Either, ItemStack> ingredient = grid[localId]; - if (ingredient == null) continue; - int id = (h * 3) + w; - stacks[id] = ingredient.map(tag -> new ItemStack(ItemRegistry.INSTANCE.getEntryList(tag).orElseThrow(() -> new RuntimeException("Identifier ingredient \"" + tag.id() + "\" has no entry in the tag registry!")).getRandom(RANDOM).orElseThrow().value()), Function.identity()); - } - return stacks; - } - - @Override - public ItemStack[] getOutputs() { - return new ItemStack[] { output }; - } - public Either, ItemStack>[] getGrid() { //noinspection unchecked return (Either, ItemStack>[]) Arrays.stream(grid).map(entry -> entry == null ? null : entry.mapRight(ItemStack::copy)).toArray(Either[]::new); diff --git a/station-recipes-v0/src/main/java/net/modificationstation/stationapi/impl/recipe/StationShapelessRecipe.java b/station-recipes-v0/src/main/java/net/modificationstation/stationapi/impl/recipe/StationShapelessRecipe.java index 8ba42a7b5..d981a7bca 100644 --- a/station-recipes-v0/src/main/java/net/modificationstation/stationapi/impl/recipe/StationShapelessRecipe.java +++ b/station-recipes-v0/src/main/java/net/modificationstation/stationapi/impl/recipe/StationShapelessRecipe.java @@ -5,14 +5,11 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.recipe.CraftingRecipe; -import net.modificationstation.stationapi.api.recipe.StationRecipe; -import net.modificationstation.stationapi.api.registry.ItemRegistry; import net.modificationstation.stationapi.api.tag.TagKey; import java.util.*; -import java.util.function.Function; -public class StationShapelessRecipe implements CraftingRecipe, StationRecipe { +public class StationShapelessRecipe implements CraftingRecipe { private static final Random RANDOM = new Random(); @@ -78,24 +75,7 @@ public ItemStack getOutput() { return output.copy(); } - @Override - public ItemStack[] getIngredients() { - ItemStack[] inputs = new ItemStack[ingredients.length]; - for (int i = 0, ingredientsLength = ingredients.length; i < ingredientsLength; i++) - inputs[i] = ingredients[i].map(tag -> new ItemStack(ItemRegistry.INSTANCE.getEntryList(tag).orElseThrow(() -> new RuntimeException("Identifier ingredient \"" + tag.id() + "\" has no entry in the tag registry!")).getRandom(RANDOM).orElseThrow().value()), Function.identity()); - return inputs; - } - - @Override - public ItemStack[] getOutputs() { - return new ItemStack[] { output }; - } - - /** - * To be renamed to getIngredients in a3, use with caution. - */ - @Deprecated - public Either, ItemStack>[] getInputs() { + public Either, ItemStack>[] getIngredients() { //noinspection unchecked return (Either, ItemStack>[]) Arrays.stream(ingredients).map(entry -> entry == null ? null : entry.mapRight(ItemStack::copy)).toArray(Either[]::new); } diff --git a/station-recipes-v0/src/main/java/net/modificationstation/stationapi/mixin/recipe/CraftingRecipeMixin.java b/station-recipes-v0/src/main/java/net/modificationstation/stationapi/mixin/recipe/CraftingRecipeMixin.java deleted file mode 100644 index ccd3ac845..000000000 --- a/station-recipes-v0/src/main/java/net/modificationstation/stationapi/mixin/recipe/CraftingRecipeMixin.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.modificationstation.stationapi.mixin.recipe; - -import net.minecraft.item.ItemStack; -import net.minecraft.recipe.CraftingRecipe; -import net.modificationstation.stationapi.api.recipe.StationRecipe; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(CraftingRecipe.class) -public interface CraftingRecipeMixin extends StationRecipe { - @Override - default ItemStack[] getIngredients() { - throw new UnsupportedOperationException("Your custom recipe registry needs to implement the methods found in \"net.modificationstation.stationapi.api.recipe.StationRecipe\"!"); - } - - @Override - default ItemStack[] getOutputs() { - throw new UnsupportedOperationException("Your custom recipe registry needs to implement the methods found in \"net.modificationstation.stationapi.api.recipe.StationRecipe\"!"); - } -} diff --git a/station-recipes-v0/src/main/java/net/modificationstation/stationapi/mixin/recipe/ShapedRecipeMixin.java b/station-recipes-v0/src/main/java/net/modificationstation/stationapi/mixin/recipe/ShapedRecipeMixin.java deleted file mode 100644 index 9f9ed3f84..000000000 --- a/station-recipes-v0/src/main/java/net/modificationstation/stationapi/mixin/recipe/ShapedRecipeMixin.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.modificationstation.stationapi.mixin.recipe; - -import net.minecraft.ShapedRecipe; -import net.minecraft.item.ItemStack; -import net.modificationstation.stationapi.api.recipe.StationRecipe; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; - -@Mixin(ShapedRecipe.class) -class ShapedRecipeMixin implements StationRecipe { - @Shadow private int width; - @Shadow private int height; - @Shadow private ItemStack output; - - @Shadow private ItemStack[] input; - - @Override - public ItemStack[] getIngredients() { - ItemStack[] stacks = new ItemStack[9]; - for (int h = 0; h < height; h++) { - for (int w = 0; w < width; w++) { - int localId = (h * width) + w; - int id = (h * 3) + w; - stacks[id] = input[localId]; - } - } - return stacks; - } - - @Override - public ItemStack[] getOutputs() { - return new ItemStack[] { output }; - } -} diff --git a/station-recipes-v0/src/main/java/net/modificationstation/stationapi/mixin/recipe/ShapelessRecipeMixin.java b/station-recipes-v0/src/main/java/net/modificationstation/stationapi/mixin/recipe/ShapelessRecipeMixin.java deleted file mode 100644 index 2270e4279..000000000 --- a/station-recipes-v0/src/main/java/net/modificationstation/stationapi/mixin/recipe/ShapelessRecipeMixin.java +++ /dev/null @@ -1,27 +0,0 @@ -package net.modificationstation.stationapi.mixin.recipe; - -import net.minecraft.item.ItemStack; -import net.minecraft.recipe.ShapelessRecipe; -import net.modificationstation.stationapi.api.recipe.StationRecipe; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; - -import java.util.List; - -@Mixin(ShapelessRecipe.class) -class ShapelessRecipeMixin implements StationRecipe { - @Shadow @Final private List input; - - @Shadow @Final private ItemStack output; - - @Override - public ItemStack[] getIngredients() { - return input.toArray(ItemStack[]::new); - } - - @Override - public ItemStack[] getOutputs() { - return new ItemStack[] { output }; - } -} diff --git a/station-recipes-v0/src/main/resources/station-recipes-v0.mixins.json b/station-recipes-v0/src/main/resources/station-recipes-v0.mixins.json index ff2373bb4..cb789d3bc 100644 --- a/station-recipes-v0/src/main/resources/station-recipes-v0.mixins.json +++ b/station-recipes-v0/src/main/resources/station-recipes-v0.mixins.json @@ -5,12 +5,9 @@ "compatibilityLevel": "JAVA_17", "mixins": [ "CraftingRecipeManagerMixin", - "CraftingRecipeMixin", "CraftingResultMixin", "FurnaceBlockEntityMixin", "RecipeComparatorMixin", - "ShapedRecipeMixin", - "ShapelessRecipeMixin", "SmeltingRecipeManagerAccessor", "SmeltingRecipeManagerMixin", "StatsMixin" diff --git a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/MiningLevelManager.java b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/MiningLevelManager.java new file mode 100644 index 000000000..205f3b7db --- /dev/null +++ b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/MiningLevelManager.java @@ -0,0 +1,33 @@ +package net.modificationstation.stationapi.api.item.tool; + +import com.google.common.graph.GraphBuilder; +import com.google.common.graph.MutableGraph; +import it.unimi.dsi.fastutil.objects.Object2BooleanMap; +import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; +import lombok.val; +import net.minecraft.block.Block; +import net.modificationstation.stationapi.api.block.BlockState; +import net.modificationstation.stationapi.api.tag.TagKey; + +import java.util.stream.Collectors; + +public final class MiningLevelManager { + public record LevelNode(TagKey blockTag) {} + private record CacheKey(LevelNode levelNode, BlockState state) {} + + public static final MutableGraph GRAPH = GraphBuilder.directed().build(); + private static final Object2BooleanMap CACHE = new Object2BooleanOpenHashMap<>(); + + public static boolean isSuitable(LevelNode levelNode, BlockState state) { + return CACHE.computeIfAbsent(new CacheKey(levelNode, state), (CacheKey key) -> { + val nodes = GRAPH.nodes().stream().filter(node -> key.state.isIn(node.blockTag)).collect(Collectors.toSet()); + if (nodes.isEmpty()) return true; + val pred = GRAPH.predecessors(key.levelNode); + return nodes.stream().anyMatch(node -> key.levelNode.equals(node) || pred.contains(node)); + }); + } + + public static void invalidateCache() { + CACHE.clear(); + } +} diff --git a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationHoeItem.java b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationHoeItem.java index 5f6ca48dc..9115fe99e 100644 --- a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationHoeItem.java +++ b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationHoeItem.java @@ -7,7 +7,6 @@ import net.modificationstation.stationapi.api.util.Util; public interface StationHoeItem extends ToolLevel { - @Override default void setEffectiveBlocks(TagKey effectiveBlocks) { Util.assertImpl(); diff --git a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationShearsItem.java b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationShearsItem.java index f0d24982d..b6904d204 100644 --- a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationShearsItem.java +++ b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationShearsItem.java @@ -7,7 +7,6 @@ import net.modificationstation.stationapi.api.util.Util; public interface StationShearsItem extends ToolLevel { - @Override default void setEffectiveBlocks(TagKey effectiveBlocks) { Util.assertImpl(); diff --git a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationSwordItem.java b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationSwordItem.java index 2cac4fec5..0f648e8d9 100644 --- a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationSwordItem.java +++ b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationSwordItem.java @@ -7,7 +7,6 @@ import net.modificationstation.stationapi.api.util.Util; public interface StationSwordItem extends ToolLevel { - @Override default void setEffectiveBlocks(TagKey effectiveBlocks) { Util.assertImpl(); diff --git a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationToolItem.java b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationToolItem.java index 57998fd5a..f23cdbc75 100644 --- a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationToolItem.java +++ b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationToolItem.java @@ -7,7 +7,6 @@ import net.modificationstation.stationapi.api.util.Util; public interface StationToolItem extends ToolLevel { - @Override default void setEffectiveBlocks(TagKey effectiveBlocks) { Util.assertImpl(); diff --git a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationToolMaterial.java b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationToolMaterial.java index b9676af8a..a8aa7d932 100644 --- a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationToolMaterial.java +++ b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationToolMaterial.java @@ -1,48 +1,14 @@ package net.modificationstation.stationapi.api.item.tool; -import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; -import it.unimi.dsi.fastutil.objects.ReferenceSet; -import net.minecraft.block.Block; import net.minecraft.item.ToolMaterial; -import net.modificationstation.stationapi.api.block.BlockState; -import net.modificationstation.stationapi.api.util.Identifier; -import net.modificationstation.stationapi.api.tag.TagKey; import net.modificationstation.stationapi.api.util.Util; -import java.util.function.BiPredicate; -import java.util.function.Predicate; - public interface StationToolMaterial { - - ReferenceSet> ALL_TOOL_MATERIAL_TAGS = new ReferenceOpenHashSet<>(); - - default ToolMaterial inheritsFrom(ToolMaterial... toolMaterials) { + default ToolMaterial miningLevelNode(MiningLevelManager.LevelNode levelNode) { return Util.assertImpl(); } - default ToolMaterial requiredBlockTag(Identifier tag) { + default MiningLevelManager.LevelNode getMiningLevelNode() { return Util.assertImpl(); } - - default ReferenceSet getParentMaterials() { - return Util.assertImpl(); - } - - default TagKey getRequiredBlockTag() { - return Util.assertImpl(); - } - - default boolean matches(BlockState state) { - return ALL_TOOL_MATERIAL_TAGS.stream().noneMatch(state::isIn) || matches0(state); - } - - private boolean matches0(BlockState state) { - TagKey tag = getRequiredBlockTag(); - if (tag != null) { - if (state.isIn(tag)) return true; - BiPredicate matches0 = StationToolMaterial::matches0; - Predicate matchesThis = t -> matches0.test(t, state); - return getParentMaterials().stream().anyMatch(matchesThis); - } else return false; - } } diff --git a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/ToolLevel.java b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/ToolLevel.java index 84bb3fb87..d12cf4528 100644 --- a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/ToolLevel.java +++ b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/ToolLevel.java @@ -6,7 +6,6 @@ import net.modificationstation.stationapi.api.tag.TagKey; public interface ToolLevel { - void setEffectiveBlocks(TagKey effectiveBlocks); TagKey getEffectiveBlocks(ItemStack stack); diff --git a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/ToolMaterialFactory.java b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/ToolMaterialFactory.java index e0b52131a..8ecfecadd 100644 --- a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/ToolMaterialFactory.java +++ b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/ToolMaterialFactory.java @@ -4,7 +4,6 @@ import net.modificationstation.stationapi.api.factory.EnumFactory; public class ToolMaterialFactory { - public static ToolMaterial create(String materialName, int miningLevel, int durability, float miningSpeed, int attackDamage) { return EnumFactory.addEnum( ToolMaterial.class, materialName, diff --git a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/impl/item/HijackShearsImplV1.java b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/impl/item/HijackShearsImplV1.java index ef0f81f1a..873ee30c3 100644 --- a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/impl/item/HijackShearsImplV1.java +++ b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/impl/item/HijackShearsImplV1.java @@ -7,7 +7,6 @@ @EventListener(phase = StationAPI.INTERNAL_PHASE) public class HijackShearsImplV1 { - //TODO: Make this match anything that has shear tool properties. Not sure how to go around this at the moment. @EventListener private static void hijackShearsEvent(ShearsOverrideEvent event) { diff --git a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/impl/item/ToolEffectivenessImplV1.java b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/impl/item/ToolEffectivenessImplV1.java index 258205d1d..9f4d161d6 100644 --- a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/impl/item/ToolEffectivenessImplV1.java +++ b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/impl/item/ToolEffectivenessImplV1.java @@ -1,5 +1,6 @@ package net.modificationstation.stationapi.impl.item; +import com.google.common.graph.GraphBuilder; import net.mine_diver.unsafeevents.listener.EventListener; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; @@ -8,6 +9,7 @@ import net.modificationstation.stationapi.api.event.item.IsItemSuitableForStateEvent; import net.modificationstation.stationapi.api.event.item.ItemMiningSpeedMultiplierOnStateEvent; import net.modificationstation.stationapi.api.event.registry.ItemRegistryEvent; +import net.modificationstation.stationapi.api.item.tool.MiningLevelManager; import net.modificationstation.stationapi.api.item.tool.ToolLevel; import net.modificationstation.stationapi.api.mod.entrypoint.Entrypoint; import net.modificationstation.stationapi.api.mod.entrypoint.EventBusPolicy; @@ -25,7 +27,6 @@ @Entrypoint(eventBus = @EventBusPolicy(registerInstance = false)) @EventListener(phase = StationAPI.INTERNAL_PHASE) public class ToolEffectivenessImplV1 { - public static final List VANILLA_TOOLS = new ArrayList<>(); @EventListener(priority = LOW) @@ -55,15 +56,30 @@ private static void getItems(ItemRegistryEvent event) { @EventListener private static void isEffective(IsItemSuitableForStateEvent event) { - event.suitable = event.suitable || isSuitable(event.itemStack, event.state); + // Disable custom tool logic if both the block and the tool are vanilla + // This is done to preserve the vanilla mining speeds + if (VANILLA_TOOLS.contains(ItemRegistry.INSTANCE.getId(event.itemStack.getItem())) + && Objects.requireNonNull(BlockRegistry.INSTANCE.getId(event.state.getBlock())).namespace == Namespace.MINECRAFT) return; + + event.suitable = isSuitable(event.itemStack, event.state); } @EventListener private static void getStrength(ItemMiningSpeedMultiplierOnStateEvent event) { - if (!(VANILLA_TOOLS.contains(ItemRegistry.INSTANCE.getId(event.itemStack.getItem())) && Objects.requireNonNull(BlockRegistry.INSTANCE.getId(event.state.getBlock())).namespace == Namespace.MINECRAFT) && isSuitable(event.itemStack, event.state)) event.miningSpeedMultiplier = ((ToolLevel) event.itemStack.getItem()).getMaterial(event.itemStack).getMiningSpeedMultiplier(); + // Disable custom tool logic if both the block and the tool are vanilla + // This is done to preserve the vanilla mining speeds + if (VANILLA_TOOLS.contains(ItemRegistry.INSTANCE.getId(event.itemStack.getItem())) + && Objects.requireNonNull(BlockRegistry.INSTANCE.getId(event.state.getBlock())).namespace == Namespace.MINECRAFT) return; + + if (!isSuitable(event.itemStack, event.state)) return; + + GraphBuilder.directed().allowsSelfLoops(true).build(); + event.miningSpeedMultiplier = ((ToolLevel) event.itemStack.getItem()).getMaterial(event.itemStack).getMiningSpeedMultiplier(); } private static boolean isSuitable(ItemStack item, BlockState state) { - return item.getItem() instanceof ToolLevel toolLevel && state.isIn(toolLevel.getEffectiveBlocks(item)) && toolLevel.getMaterial(item).matches(state); + return item.getItem() instanceof ToolLevel toolLevel + && state.isIn(toolLevel.getEffectiveBlocks(item)) + && MiningLevelManager.isSuitable(toolLevel.getMaterial(item).getMiningLevelNode(), state); } } diff --git a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/mixin/tools/ToolMaterialMixin.java b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/mixin/tools/ToolMaterialMixin.java index 3429c86b5..aa4265349 100644 --- a/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/mixin/tools/ToolMaterialMixin.java +++ b/station-tools-api-v1/src/main/java/net/modificationstation/stationapi/mixin/tools/ToolMaterialMixin.java @@ -1,64 +1,26 @@ package net.modificationstation.stationapi.mixin.tools; -import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; -import it.unimi.dsi.fastutil.objects.ReferenceSet; -import it.unimi.dsi.fastutil.objects.ReferenceSets; -import net.minecraft.block.Block; import net.minecraft.item.ToolMaterial; +import net.modificationstation.stationapi.api.item.tool.MiningLevelManager; import net.modificationstation.stationapi.api.item.tool.StationToolMaterial; -import net.modificationstation.stationapi.api.registry.BlockRegistry; -import net.modificationstation.stationapi.api.tag.TagKey; -import net.modificationstation.stationapi.api.util.Identifier; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.Collections; @Mixin(ToolMaterial.class) class ToolMaterialMixin implements StationToolMaterial { @Unique - private TagKey stationapi_requiredBlockTag; - @Unique - private ReferenceSet stationapi_parentMaterials; - @Unique - private ReferenceSet stationapi_parentMaterialsView; - - @Inject( - method = "(Ljava/lang/String;IIIFI)V", - at = @At("RETURN") - ) - private void stationapi_init(String i, int j, int k, int f, float l, int par6, CallbackInfo ci) { - stationapi_parentMaterials = new ReferenceOpenHashSet<>(); - stationapi_parentMaterialsView = ReferenceSets.unmodifiable(stationapi_parentMaterials); - } - - @Override - @Unique - public ToolMaterial inheritsFrom(ToolMaterial... toolMaterials) { - Collections.addAll(stationapi_parentMaterials, toolMaterials); - return ToolMaterial.class.cast(this); - } + private MiningLevelManager.LevelNode stationapi_levelNode; @Override @Unique - public ToolMaterial requiredBlockTag(Identifier tag) { - ALL_TOOL_MATERIAL_TAGS.remove(stationapi_requiredBlockTag); - ALL_TOOL_MATERIAL_TAGS.add(stationapi_requiredBlockTag = TagKey.of(BlockRegistry.INSTANCE.getKey(), tag)); + public ToolMaterial miningLevelNode(MiningLevelManager.LevelNode levelNode) { + stationapi_levelNode = levelNode; return ToolMaterial.class.cast(this); } @Override @Unique - public ReferenceSet getParentMaterials() { - return stationapi_parentMaterialsView; - } - - @Override - @Unique - public TagKey getRequiredBlockTag() { - return stationapi_requiredBlockTag; + public MiningLevelManager.LevelNode getMiningLevelNode() { + return stationapi_levelNode; } } diff --git a/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/impl/vanillafix/item/tool/VanillaToolFixImpl.java b/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/impl/vanillafix/item/tool/VanillaToolFixImpl.java index c9cffd6bc..f51cceb4c 100644 --- a/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/impl/vanillafix/item/tool/VanillaToolFixImpl.java +++ b/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/impl/vanillafix/item/tool/VanillaToolFixImpl.java @@ -4,8 +4,11 @@ import net.minecraft.item.ToolMaterial; import net.modificationstation.stationapi.api.StationAPI; import net.modificationstation.stationapi.api.event.registry.ItemRegistryEvent; +import net.modificationstation.stationapi.api.item.tool.MiningLevelManager; import net.modificationstation.stationapi.api.mod.entrypoint.Entrypoint; import net.modificationstation.stationapi.api.mod.entrypoint.EventBusPolicy; +import net.modificationstation.stationapi.api.registry.BlockRegistry; +import net.modificationstation.stationapi.api.tag.TagKey; import static net.modificationstation.stationapi.api.util.Identifier.of; @@ -14,14 +17,14 @@ public final class VanillaToolFixImpl { @EventListener private static void fixToolMaterials(ItemRegistryEvent event) { - ToolMaterial stone = ToolMaterial.STONE; - ToolMaterial iron = ToolMaterial.IRON; - ToolMaterial diamond = ToolMaterial.DIAMOND; - stone.inheritsFrom(ToolMaterial.WOOD, ToolMaterial.GOLD); - stone.requiredBlockTag(of("needs_stone_tool")); - iron.inheritsFrom(ToolMaterial.STONE); - iron.requiredBlockTag(of("needs_iron_tool")); - diamond.inheritsFrom(ToolMaterial.IRON); - diamond.requiredBlockTag(of("needs_diamond_tool")); + MiningLevelManager.LevelNode stoneNode = new MiningLevelManager.LevelNode(TagKey.of(BlockRegistry.KEY, of("needs_stone_tool"))); + MiningLevelManager.LevelNode ironNode = new MiningLevelManager.LevelNode(TagKey.of(BlockRegistry.KEY, of("needs_iron_tool"))); + MiningLevelManager.LevelNode diamondNode = new MiningLevelManager.LevelNode(TagKey.of(BlockRegistry.KEY, of("needs_diamond_tool"))); + MiningLevelManager.GRAPH.putEdge(stoneNode, ironNode); + MiningLevelManager.GRAPH.putEdge(ironNode, diamondNode); + MiningLevelManager.invalidateCache(); + ToolMaterial.STONE.miningLevelNode(stoneNode); + ToolMaterial.IRON.miningLevelNode(ironNode); + ToolMaterial.DIAMOND.miningLevelNode(diamondNode); } } diff --git a/station-world-events-v0/build.gradle.kts b/station-world-events-v0/build.gradle.kts index 254b0113b..70701748b 100644 --- a/station-world-events-v0/build.gradle.kts +++ b/station-world-events-v0/build.gradle.kts @@ -4,5 +4,5 @@ base.archivesName.set("station-world-events-v0") version = getSubprojectVersion(project, "1.0.0") addModuleDependencies(project, - "station-api-base" + "station-api-base", ) \ No newline at end of file diff --git a/station-world-events-v0/src/main/java/net/modificationstation/stationapi/mixin/world/WorldMixin.java b/station-world-events-v0/src/main/java/net/modificationstation/stationapi/mixin/world/WorldMixin.java index 8e41280c1..36e6fe1ad 100644 --- a/station-world-events-v0/src/main/java/net/modificationstation/stationapi/mixin/world/WorldMixin.java +++ b/station-world-events-v0/src/main/java/net/modificationstation/stationapi/mixin/world/WorldMixin.java @@ -1,15 +1,26 @@ package net.modificationstation.stationapi.mixin.world; +import net.minecraft.world.PersistentState; +import net.minecraft.world.PersistentStateManager; import net.minecraft.world.World; import net.modificationstation.stationapi.api.StationAPI; import net.modificationstation.stationapi.api.event.world.WorldEvent; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(World.class) +abstract class WorldMixin { + @Shadow public PersistentStateManager persistentStateManager; + + @Shadow public abstract PersistentState getOrCreateState(Class stateClass, String id); + + @Shadow public abstract void setState(String id, PersistentState state); + @Inject( method = { "(Lnet/minecraft/world/dimension/DimensionData;Ljava/lang/String;Lnet/minecraft/world/dimension/Dimension;J)V", diff --git a/station-world-events-v0/src/main/java/net/modificationstation/stationapi/mixin/world/WorldPropertiesMixin.java b/station-world-events-v0/src/main/java/net/modificationstation/stationapi/mixin/world/WorldPropertiesMixin.java index f45b3006d..69482e9e4 100644 --- a/station-world-events-v0/src/main/java/net/modificationstation/stationapi/mixin/world/WorldPropertiesMixin.java +++ b/station-world-events-v0/src/main/java/net/modificationstation/stationapi/mixin/world/WorldPropertiesMixin.java @@ -11,6 +11,7 @@ @Mixin(WorldProperties.class) public class WorldPropertiesMixin { + @Inject( method = "(Lnet/minecraft/nbt/NbtCompound;)V", at = @At("RETURN")