diff --git a/build.gradle b/build.gradle index 5795603e..86cbcc83 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,7 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. // modCompile "net.fabricmc:fabric:${project.fabric_version}" + modCompileOnly fabricApi.module("fabric-rendering-v1", project.fabric_version) // PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs. // You may need to force-disable transitiveness on them. diff --git a/docs/scarpet/api/Events.md b/docs/scarpet/api/Events.md index e3888543..08bc40b3 100644 --- a/docs/scarpet/api/Events.md +++ b/docs/scarpet/api/Events.md @@ -208,7 +208,7 @@ ingested by the player. The exact position of these items is unknown as technica items could be spread all across the inventory. ### `__on_player_connects(player)` -Triggered when the player has successfully logged in and was placed in the gaem. +Triggered when the player has successfully logged in and was placed in the game. ### `__on_player_disconnects(player, reason)` Triggered when a player sends a disconnect package or is forcefully disconnected from the server. diff --git a/gradle.properties b/gradle.properties index 67d6a200..e0aa382d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,6 +7,7 @@ org.gradle.jvmargs=-Xmx1G minecraft_version=21w03a yarn_mappings=21w03a+build.1 loader_version=0.11.1 + fabric_version=0.29.4+1.16 # Mod Properties mod_version = 1.4.24 @@ -26,7 +27,3 @@ org.gradle.jvmargs=-Xmx1G # This is needed because CF uses too vague names for snapshots # Can also be the version ID directly coming from https://minecraft.curseforge.com/api/game/versions?token=[API_TOKEN] release-extra-curse-version = 1.17 - -# Dependencies - # currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api - #fabric_version=0.3.0-pre+build.166 diff --git a/src/main/java/carpet/CarpetServer.java b/src/main/java/carpet/CarpetServer.java index c8cae167..a08e3e66 100644 --- a/src/main/java/carpet/CarpetServer.java +++ b/src/main/java/carpet/CarpetServer.java @@ -25,6 +25,7 @@ import carpet.logging.LoggerRegistry; import carpet.script.CarpetScriptServer; import carpet.settings.SettingsManager; import carpet.logging.HUDController; +import carpet.utils.FabricAPIHooks; import carpet.utils.MobAI; import carpet.utils.SpawnReporter; import com.mojang.brigadier.CommandDispatcher; @@ -80,6 +81,7 @@ public class CarpetServer implements ClientModInitializer,DedicatedServerModInit settingsManager = new SettingsManager(CarpetSettings.carpetVersion, "carpet", "Carpet Mod"); settingsManager.parseSettingsClass(CarpetSettings.class); extensions.forEach(CarpetExtension::onGameStarted); + FabricAPIHooks.initialize(); } public static void onServerLoaded(MinecraftServer server) diff --git a/src/main/java/carpet/CarpetSettings.java b/src/main/java/carpet/CarpetSettings.java index c651c188..b596f7ac 100644 --- a/src/main/java/carpet/CarpetSettings.java +++ b/src/main/java/carpet/CarpetSettings.java @@ -7,20 +7,25 @@ import carpet.settings.Validator; import carpet.utils.Translations; import carpet.utils.Messenger; import carpet.utils.SpawnChunks; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; import net.minecraft.server.MinecraftServer; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Box; import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.Vec3d; +import net.minecraft.util.registry.Registry; import net.minecraft.world.World; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.Iterator; import java.util.Locale; +import java.util.Optional; import static carpet.settings.RuleCategory.BUGFIX; import static carpet.settings.RuleCategory.COMMAND; @@ -46,6 +51,7 @@ public class CarpetSettings public static int runPermissionLevel = 2; public static boolean doChainStone = false; public static boolean chainStoneStickToAll = false; + public static Block structureBlockIgnoredBlock = Blocks.STRUCTURE_VOID; private static class LanguageValidator extends Validator { @Override public String validate(ServerCommandSource source, ParsedRule currentRule, String newValue, String string) { @@ -796,4 +802,62 @@ public class CarpetSettings category = {SURVIVAL, CLIENT} ) public static boolean cleanLogs = false; + + public static class StructureBlockLimitValidator extends Validator { + + @Override public Integer validate(ServerCommandSource source, ParsedRule currentRule, Integer newValue, String string) { + return (newValue >= 48) ? newValue : null; + } + + @Override + public String description() { + return "You have to choose a value greater or equal to 48"; + } + } + @Rule( + desc = "Customizable structure block limit of each axis", + extra = {"WARNING: Needs to be permanent for correct loading.", + "Setting 'structureBlockIgnored' to air is recommended", + "when saving massive structures.", + "Required on client of player editing the Structure Block.", + "'structureBlockOutlineDistance' may be required for", + "correct rendering of long structures."}, + options = {"48", "96", "192", "256"}, + category = {CREATIVE, CLIENT}, + validate = StructureBlockLimitValidator.class, + strict = false + ) + public static int structureBlockLimit = 48; + + public static class StructureBlockIgnoredValidator extends Validator { + + @Override + public String validate(ServerCommandSource source, ParsedRule currentRule, String newValue, String string) { + Optional ignoredBlock = Registry.BLOCK.getOrEmpty(Identifier.tryParse(newValue)); + if (!ignoredBlock.isPresent()) { + Messenger.m(source, "r Unknown block '" + newValue + "'."); + return null; + } + structureBlockIgnoredBlock = ignoredBlock.get(); + return newValue; + } + } + @Rule( + desc = "Changes the block ignored by the Structure Block", + options = {"minecraft:structure_void", "minecraft:air"}, + category = CREATIVE, + validate = StructureBlockIgnoredValidator.class, + strict = false + ) + public static String structureBlockIgnored = "minecraft:structure_void"; + + @Rule( + desc = "Customizable Structure Block outline render distance", + extra = "Required on client to work properly", + options = {"96", "192", "2048"}, + category = {CREATIVE, CLIENT}, + strict = false, + validate = Validator.NONNEGATIVE_NUMBER.class + ) + public static double structureBlockOutlineDistance = 96d; } diff --git a/src/main/java/carpet/commands/LogCommand.java b/src/main/java/carpet/commands/LogCommand.java index fdfafb48..e347885b 100644 --- a/src/main/java/carpet/commands/LogCommand.java +++ b/src/main/java/carpet/commands/LogCommand.java @@ -43,7 +43,7 @@ public class LogCommand c.getSource(), c.getSource().getName(), getString(c, "log name")))). - then(CommandManager.argument("option", StringArgumentType.greedyString()). + then(CommandManager.argument("option", StringArgumentType.string()). suggests( (c, b) -> suggestMatching( (LoggerRegistry.getLogger(getString(c, "log name"))==null ?new String[]{} diff --git a/src/main/java/carpet/mixins/ClientPlayerEntity_clientCommandMixin.java b/src/main/java/carpet/mixins/ClientPlayerEntity_clientCommandMixin.java index 62999f0d..b5bbb703 100644 --- a/src/main/java/carpet/mixins/ClientPlayerEntity_clientCommandMixin.java +++ b/src/main/java/carpet/mixins/ClientPlayerEntity_clientCommandMixin.java @@ -15,7 +15,7 @@ public class ClientPlayerEntity_clientCommandMixin @Inject(method = "sendChatMessage", at = @At("HEAD")) private void inspectMessage(String string, CallbackInfo ci) { - if (string.startsWith("/call")) + if (string.startsWith("/call ")) { String command = string.substring(6); CarpetClient.sendClientCommand(command); diff --git a/src/main/java/carpet/mixins/StructureBlockBlockEntity_fillUpdatesMixin.java b/src/main/java/carpet/mixins/StructureBlockBlockEntity_fillUpdatesMixin.java index 1bf98277..627d221f 100644 --- a/src/main/java/carpet/mixins/StructureBlockBlockEntity_fillUpdatesMixin.java +++ b/src/main/java/carpet/mixins/StructureBlockBlockEntity_fillUpdatesMixin.java @@ -32,4 +32,4 @@ public abstract class StructureBlockBlockEntity_fillUpdatesMixin CarpetSettings.impendingFillSkipUpdates = false; } } -} +} \ No newline at end of file diff --git a/src/main/java/carpet/mixins/StructureBlockBlockEntity_limitsMixin.java b/src/main/java/carpet/mixins/StructureBlockBlockEntity_limitsMixin.java new file mode 100644 index 00000000..c676ae56 --- /dev/null +++ b/src/main/java/carpet/mixins/StructureBlockBlockEntity_limitsMixin.java @@ -0,0 +1,53 @@ +package carpet.mixins; + +import carpet.CarpetSettings; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.block.Block; +import net.minecraft.block.entity.StructureBlockBlockEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +@Mixin(StructureBlockBlockEntity.class) +public abstract class StructureBlockBlockEntity_limitsMixin +{ + @ModifyConstant( + method = "fromTag", + constant = @Constant(intValue = 48) + ) + private int positiveLimit(int original) { + return CarpetSettings.structureBlockLimit; + } + + @ModifyConstant( + method = "fromTag", + constant = @Constant(intValue = -48) + ) + private int negativeLimit(int original) { + return -CarpetSettings.structureBlockLimit; + } + + @ModifyArg( + method = "saveStructure(Z)Z", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/structure/Structure;saveFromWorld(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/BlockPos;ZLnet/minecraft/block/Block;)V" + ), + index = 4 + ) + private Block ignoredBlock(Block original) { + return CarpetSettings.structureBlockIgnoredBlock; + } + + @Environment(EnvType.CLIENT) + @ModifyConstant( + method = "getSquaredRenderDistance", + constant = @Constant(doubleValue = 96d) + ) + private double outlineRenderDistanceLimit(double original) { + return CarpetSettings.structureBlockOutlineDistance; + } +} diff --git a/src/main/java/carpet/mixins/UpdateStructureBlockC2SPacketMixin.java b/src/main/java/carpet/mixins/UpdateStructureBlockC2SPacketMixin.java new file mode 100644 index 00000000..da3d391a --- /dev/null +++ b/src/main/java/carpet/mixins/UpdateStructureBlockC2SPacketMixin.java @@ -0,0 +1,45 @@ +package carpet.mixins; + +import carpet.CarpetSettings; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.packet.c2s.play.UpdateStructureBlockC2SPacket; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +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(UpdateStructureBlockC2SPacket.class) +public class UpdateStructureBlockC2SPacketMixin { + @Shadow + private BlockPos offset; + @Shadow + private BlockPos size; + + @Inject( + method = "read", + at = @At("TAIL") + ) + private void structureBlockLimitsRead(PacketByteBuf buf, CallbackInfo ci) { + if (buf.readableBytes() > 0) { + // This will throw an exception if carpet is not installed on client + offset = new BlockPos(MathHelper.clamp(buf.readInt(), -CarpetSettings.structureBlockLimit, CarpetSettings.structureBlockLimit), MathHelper.clamp(buf.readInt(), -CarpetSettings.structureBlockLimit, CarpetSettings.structureBlockLimit), MathHelper.clamp(buf.readInt(), -CarpetSettings.structureBlockLimit, CarpetSettings.structureBlockLimit)); + size = new BlockPos(MathHelper.clamp(buf.readInt(), 0, CarpetSettings.structureBlockLimit), MathHelper.clamp(buf.readInt(), 0, CarpetSettings.structureBlockLimit), MathHelper.clamp(buf.readInt(), 0, CarpetSettings.structureBlockLimit)); + } + } + + @Inject( + method = "write", + at = @At("TAIL") + ) + private void structureBlockLimitsWrite(PacketByteBuf buf, CallbackInfo ci) { + buf.writeInt(this.offset.getX()); + buf.writeInt(this.offset.getY()); + buf.writeInt(this.offset.getZ()); + buf.writeInt(this.size.getX()); + buf.writeInt(this.size.getY()); + buf.writeInt(this.size.getZ()); + } +} diff --git a/src/main/java/carpet/mixins/WorldRenderer_scarpetRenderMixin.java b/src/main/java/carpet/mixins/WorldRenderer_scarpetRenderMixin.java index 61b342b2..4eb54527 100644 --- a/src/main/java/carpet/mixins/WorldRenderer_scarpetRenderMixin.java +++ b/src/main/java/carpet/mixins/WorldRenderer_scarpetRenderMixin.java @@ -2,6 +2,7 @@ package carpet.mixins; import carpet.network.CarpetClient; import carpet.script.utils.ShapesRenderer; +import carpet.utils.FabricAPIHooks; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.BufferBuilderStorage; @@ -28,7 +29,7 @@ public class WorldRenderer_scarpetRenderMixin @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/debug/DebugRenderer;render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider$Immediate;DDD)V")) private void renderScarpetThings(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo ci) { - if (CarpetClient.shapes != null) + if (!FabricAPIHooks.WORLD_RENDER_EVENTS && CarpetClient.shapes != null) { RenderSystem.pushMatrix(); CarpetClient.shapes.render(camera, tickDelta); diff --git a/src/main/java/carpet/script/value/NullValue.java b/src/main/java/carpet/script/value/NullValue.java index e2a1f3a7..7dcec512 100644 --- a/src/main/java/carpet/script/value/NullValue.java +++ b/src/main/java/carpet/script/value/NullValue.java @@ -40,6 +40,21 @@ public class NullValue extends NumericValue // TODO check nonsingleton code return o instanceof NullValue; } + @Override + public Value slice(long fromDesc, Long toDesc) { + return Value.NULL; + } + + @Override + public NumericValue opposite() { + return Value.NULL; + } + + @Override + public int length() { + return 0; + } + @Override public int compareTo(Value o) { diff --git a/src/main/java/carpet/utils/FabricAPIHooks.java b/src/main/java/carpet/utils/FabricAPIHooks.java new file mode 100644 index 00000000..b9c49bd5 --- /dev/null +++ b/src/main/java/carpet/utils/FabricAPIHooks.java @@ -0,0 +1,44 @@ +package carpet.utils; + +import carpet.network.CarpetClient; +import com.mojang.blaze3d.systems.RenderSystem; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.SemanticVersion; +import net.fabricmc.loader.api.Version; +import net.fabricmc.loader.api.VersionParsingException; + +public class FabricAPIHooks { + + public static final boolean WORLD_RENDER_EVENTS = hasMod("fabric-rendering-v1", "1.5.0"); + + private FabricAPIHooks() { + } + + public static void initialize() { + if (WORLD_RENDER_EVENTS) { + WorldRenderEvents.BEFORE_DEBUG_RENDER.register(context -> { + if (CarpetClient.shapes != null) { + RenderSystem.pushMatrix(); + CarpetClient.shapes.render(context.camera(), context.tickDelta()); + RenderSystem.popMatrix(); + } + }); + } + } + + private static boolean hasMod(String id, String minimumVersion) { + return FabricLoader.getInstance().getModContainer(id).map(m -> { + Version version = m.getMetadata().getVersion(); + + if (version instanceof SemanticVersion) { + try { + return ((SemanticVersion) version).compareTo(SemanticVersion.parse(minimumVersion)) >= 0; + } catch (VersionParsingException ignored) { + } + } + + return false; + }).orElse(false); + } +} diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index d231060c..d4edc11c 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -18,6 +18,8 @@ "World_fillUpdatesMixin", "WorldChunk_fillUpdatesMixin", "StructureBlockBlockEntity_fillUpdatesMixin", + "StructureBlockBlockEntity_limitsMixin", + "UpdateStructureBlockC2SPacketMixin", "Structure_fillUpdatesMixin", "ServerPlayNetworkHandler_interactionUpdatesMixin", @@ -38,12 +40,12 @@ "ChunkHolder_scarpetChunkCreationMixin", "ServerLightingProvider_scarpetMixin", "LevelPropagator_resetChunkInterface", - "ChunkTicketManager_scarpetChunkCreationMixin", - "LightingProvider_scarpetChunkCreationMixin", - "ChunkLightProvider_scarpetChunkCreationMixin", - "LightStorage_scarpetChunkCreationMixin", - "BlockLightStorage_scarpetChunkCreationMixin", - "SkyLightStorage_scarpetChunkCreationMixin", + "ChunkTicketManager_scarpetChunkCreationMixin", + "LightingProvider_scarpetChunkCreationMixin", + "ChunkLightProvider_scarpetChunkCreationMixin", + "LightStorage_scarpetChunkCreationMixin", + "BlockLightStorage_scarpetChunkCreationMixin", + "SkyLightStorage_scarpetChunkCreationMixin", "ChunkTicketManager_spawnChunksMixin", "ServerWorld_spawnChunksMixin",