diff --git a/README.md b/README.md index 83a70ad..ba4741e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ This Minecraft mod allows you to prevent the world generator from generating new chunks past a defined limit. You can set the limit by changing the gamerule `chunkGenerationLimit`. +This mod also adds an alternative ending. You can turn this off with `/gamerule truncatedAlternativeEnding false`. + Requires Fabric API! ## Known Incompatibilities diff --git a/src/main/java/net/chaoticbyte/truncated/PlayerReachedLimitCriterion.java b/src/main/java/net/chaoticbyte/truncated/PlayerReachedLimitCriterion.java new file mode 100644 index 0000000..bd1c983 --- /dev/null +++ b/src/main/java/net/chaoticbyte/truncated/PlayerReachedLimitCriterion.java @@ -0,0 +1,35 @@ +package net.chaoticbyte.truncated; + +import com.mojang.serialization.Codec; +import net.minecraft.advancement.criterion.AbstractCriterion; +import net.minecraft.predicate.entity.LootContextPredicate; +import net.minecraft.predicate.entity.LootContextPredicateValidator; +import net.minecraft.server.network.ServerPlayerEntity; + +import java.util.Optional; + +public class PlayerReachedLimitCriterion extends AbstractCriterion { + + public void trigger(ServerPlayerEntity player) { + this.trigger(player, conditions -> true); + } + + // practically unnecessary boilerplate: + + @Override + public Codec getConditionsCodec() { + return PlayerReachedLimitCriterion.Conditions.CODEC; + } + + public record Conditions() implements AbstractCriterion.Conditions { + public static final Codec CODEC = Codec.unit(new PlayerReachedLimitCriterion.Conditions()); + + @Override + public void validate(LootContextPredicateValidator validator) {} + + @Override + public Optional player() { + return Optional.empty(); + } + } +} diff --git a/src/main/java/net/chaoticbyte/truncated/Truncated.java b/src/main/java/net/chaoticbyte/truncated/Truncated.java index 98e5d52..8454232 100644 --- a/src/main/java/net/chaoticbyte/truncated/Truncated.java +++ b/src/main/java/net/chaoticbyte/truncated/Truncated.java @@ -3,21 +3,43 @@ package net.chaoticbyte.truncated; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.gamerule.v1.GameRuleFactory; import net.fabricmc.fabric.api.gamerule.v1.GameRuleRegistry; +import net.minecraft.advancement.criterion.Criteria; import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameRules; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; + public class Truncated implements ModInitializer { public static final String MOD_ID = "truncated"; - public static final GameRules.Key CHUNK_GEN_LIMIT_KEY = GameRuleRegistry.register( - "chunkGenerationLimit", GameRules.Category.MISC, GameRuleFactory.createIntRule((28_000_000 / 16) - 1 )); public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); + // gamerules + public static final GameRules.Key CHUNK_GEN_LIMIT_KEY = GameRuleRegistry.register( + "chunkGenerationLimit", + GameRules.Category.MISC, + GameRuleFactory.createIntRule((28_000_000 / 16) - 1 ) + ); + public static final GameRules.Key TRUNCATED_ALT_ENDING = GameRuleRegistry.register( + "truncatedAlternativeEnding", + GameRules.Category.MISC, + GameRuleFactory.createBooleanRule (true) + ); + + // advancement criterion + public static PlayerReachedLimitCriterion PLAYER_REACHED_LIMIT = Criteria.register( + MOD_ID + "/reached_limit", + new PlayerReachedLimitCriterion()); + + // variable temporary holding the server reference later private static MinecraftServer server; + // get the limit set by the gamerule public static int getLimit() { return server.getGameRules().getInt(CHUNK_GEN_LIMIT_KEY); } @@ -27,6 +49,30 @@ public class Truncated implements ModInitializer { ServerLifecycleEvents.SERVER_STARTING.register(minecraftServer -> { server = minecraftServer; }); + ServerTickEvents.START_WORLD_TICK.register(minecraftServer -> { + if (minecraftServer.getGameRules().getBoolean(TRUNCATED_ALT_ENDING)) { + float currentBlockLimit = getLimit() * 16; + java.util.List playerEntitiesReachedEnd = new ArrayList<>(); + minecraftServer.getPlayers().forEach(playerEntity -> { + if (!playerEntity.isSpectator()) { + Vec3d playerPos = playerEntity.getPos(); + if ( + playerPos.x > currentBlockLimit + 16 + || playerPos.x < -currentBlockLimit + || playerPos.z > currentBlockLimit + 16 + || playerPos.z < -currentBlockLimit + ) { + playerEntitiesReachedEnd.add(playerEntity); + } + } + }); + // trigger the end credits for all players out of bounds + playerEntitiesReachedEnd.forEach(playerEntity -> { + PLAYER_REACHED_LIMIT.trigger(playerEntity); // trigger advancement + playerEntity.detachForDimensionChange(); // end credits + }); + } + }); } } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/advancement/truncated/alternative_ending.json b/src/main/resources/data/minecraft/advancement/truncated/alternative_ending.json new file mode 100644 index 0000000..5076d7d --- /dev/null +++ b/src/main/resources/data/minecraft/advancement/truncated/alternative_ending.json @@ -0,0 +1,22 @@ +{ + "parent": "minecraft:story/root", + "criteria": { + "reached_limit": {"conditions": {}, "trigger": "minecraft:truncated/reached_limit"} + }, + "display": { + "announce_to_chat": true, + "description": { + "text": "You have traveled beyond the natural limits of this world (alternative ending)" + }, + "frame": "goal", + "hidden": false, + "icon": { + "id": "minecraft:compass" + }, + "title": { + "text": "The Limits of this World" + }, + "show_toast": true + }, + "requirements": [["reached_limit"]] +} \ No newline at end of file