From 064973b07b8394a6a35bd233d871a7907da99c8b Mon Sep 17 00:00:00 2001 From: Christian Knaapen Date: Sun, 15 Jan 2023 22:21:50 +0100 Subject: [PATCH 1/5] Added code from Create. Added Flywheel dependency. Restructured client and mod events to match create. Copied accesstransformer. Added Create, CreateClient, ClientEvents and CommonEvents proxies. --- build.gradle | 10 + .../effortlessbuilding/ClientEvents.java | 337 +++++++++++++ .../{EventHandler.java => CommonEvents.java} | 21 +- .../EffortlessBuilding.java | 44 +- .../EffortlessBuildingClient.java | 21 + .../ModClientEventHandler.java | 51 -- .../buildmode/ModeOptions.java | 5 +- .../create/AllSpecialTextures.java | 33 ++ .../effortlessbuilding/create/Create.java | 16 + .../create/CreateClient.java | 15 + .../create/events/ClientEvents.java | 96 ++++ .../create/events/CommonEvents.java | 39 ++ .../ClientResourceReloadListener.java | 16 + .../foundation/ModFilePackResources.java | 25 + .../block/render/SpriteShiftEntry.java | 49 ++ .../render/BakedModelRenderHelper.java | 29 ++ .../foundation/render/CachedBufferer.java | 88 ++++ .../foundation/render/ForcedDiffuseState.java | 30 ++ .../create/foundation/render/RenderTypes.java | 160 ++++++ .../foundation/render/ShadowRenderHelper.java | 112 +++++ .../foundation/render/SuperByteBuffer.java | 464 ++++++++++++++++++ .../render/SuperByteBufferCache.java | 54 ++ .../render/SuperRenderTypeBuffer.java | 94 ++++ .../render/TileEntityRenderHelper.java | 113 +++++ .../utility/AbstractBlockBreakQueue.java | 36 ++ .../foundation/utility/AngleHelper.java | 52 ++ .../utility/AnimationTickHolder.java | 56 +++ .../create/foundation/utility/BBHelper.java | 20 + .../create/foundation/utility/BlockFace.java | 52 ++ .../foundation/utility/BlockHelper.java | 337 +++++++++++++ .../utility/CameraAngleAnimationService.java | 90 ++++ .../create/foundation/utility/Color.java | 310 ++++++++++++ .../foundation/utility/ColorHandlers.java | 26 + .../create/foundation/utility/Components.java | 33 ++ .../create/foundation/utility/Couple.java | 151 ++++++ .../foundation/utility/CreateRegistry.java | 102 ++++ .../create/foundation/utility/Debug.java | 67 +++ .../create/foundation/utility/DyeHelper.java | 75 +++ .../foundation/utility/DynamicComponent.java | 94 ++++ .../foundation/utility/FilesHelper.java | 101 ++++ .../foundation/utility/FluidFormatter.java | 26 + .../create/foundation/utility/FontHelper.java | 87 ++++ .../foundation/utility/ICoordinate.java | 8 + .../utility/IInteractionChecker.java | 7 + .../foundation/utility/IPartialSafeNBT.java | 8 + .../foundation/utility/IntAttached.java | 61 +++ .../create/foundation/utility/Iterate.java | 48 ++ .../create/foundation/utility/Lang.java | 91 ++++ .../foundation/utility/LangBuilder.java | 165 +++++++ .../foundation/utility/LangNumberFormat.java | 33 ++ .../create/foundation/utility/NBTHelper.java | 106 ++++ .../foundation/utility/NBTProcessors.java | 84 ++++ .../create/foundation/utility/Pair.java | 68 +++ .../create/foundation/utility/Pointing.java | 35 ++ .../foundation/utility/RaycastHelper.java | 197 ++++++++ .../foundation/utility/RegisteredObjects.java | 66 +++ .../foundation/utility/ResetableLazy.java | 32 ++ .../foundation/utility/UniqueLinkedList.java | 102 ++++ .../create/foundation/utility/VecHelper.java | 343 +++++++++++++ .../foundation/utility/VoxelShaper.java | 142 ++++++ .../foundation/utility/WorldAttached.java | 101 ++++ .../foundation/utility/WorldHelper.java | 13 + .../foundation/utility/animation/Force.java | 102 ++++ .../utility/animation/LerpedFloat.java | 149 ++++++ .../utility/animation/PhysicalFloat.java | 90 ++++ .../utility/ghost/GhostBlockParams.java | 50 ++ .../utility/ghost/GhostBlockRenderer.java | 140 ++++++ .../foundation/utility/ghost/GhostBlocks.java | 83 ++++ .../utility/outliner/AABBOutline.java | 100 ++++ .../utility/outliner/BlockClusterOutline.java | 178 +++++++ .../utility/outliner/ChasingAABBOutline.java | 41 ++ .../utility/outliner/LineOutline.java | 65 +++ .../foundation/utility/outliner/Outline.java | 246 ++++++++++ .../foundation/utility/outliner/Outliner.java | 179 +++++++ .../worldWrappers/DummyLevelEntityGetter.java | 41 ++ .../worldWrappers/DummyStatusListener.java | 23 + .../PlacementSimulationServerWorld.java | 62 +++ .../utility/worldWrappers/RayTraceWorld.java | 47 ++ .../worldWrappers/WrappedClientWorld.java | 140 ++++++ .../worldWrappers/WrappedServerWorld.java | 120 +++++ .../utility/worldWrappers/WrappedWorld.java | 250 ++++++++++ .../utility/worldWrappers/package-info.java | 6 + .../effortlessbuilding/create/license.txt | 12 + .../gui/buildmode/RadialMenu.java | 7 +- .../buildmodifier/ModifierSettingsGui.java | 3 +- .../network/RequestLookAtMessage.java | 5 +- .../effortlessbuilding/proxy/ClientProxy.java | 314 +----------- .../effortlessbuilding/proxy/IProxy.java | 6 - .../effortlessbuilding/proxy/ServerProxy.java | 13 +- .../render/BlockPreviewRenderer.java | 16 +- .../render/RenderHandler.java | 37 +- .../resources/META-INF/accesstransformer.cfg | 48 +- 92 files changed, 7479 insertions(+), 441 deletions(-) create mode 100644 src/main/java/nl/requios/effortlessbuilding/ClientEvents.java rename src/main/java/nl/requios/effortlessbuilding/{EventHandler.java => CommonEvents.java} (93%) create mode 100644 src/main/java/nl/requios/effortlessbuilding/EffortlessBuildingClient.java delete mode 100644 src/main/java/nl/requios/effortlessbuilding/ModClientEventHandler.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/AllSpecialTextures.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/Create.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/CreateClient.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/events/ClientEvents.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/events/CommonEvents.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/ClientResourceReloadListener.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/ModFilePackResources.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/block/render/SpriteShiftEntry.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/render/BakedModelRenderHelper.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/render/CachedBufferer.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/render/ForcedDiffuseState.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/render/RenderTypes.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/render/ShadowRenderHelper.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/render/SuperByteBuffer.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/render/SuperByteBufferCache.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/render/SuperRenderTypeBuffer.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/render/TileEntityRenderHelper.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/AbstractBlockBreakQueue.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/AngleHelper.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/AnimationTickHolder.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/BBHelper.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/BlockFace.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/BlockHelper.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/CameraAngleAnimationService.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Color.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ColorHandlers.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Components.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Couple.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/CreateRegistry.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Debug.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/DyeHelper.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/DynamicComponent.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/FilesHelper.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/FluidFormatter.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/FontHelper.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ICoordinate.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/IInteractionChecker.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/IPartialSafeNBT.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/IntAttached.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Iterate.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Lang.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/LangBuilder.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/LangNumberFormat.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/NBTHelper.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/NBTProcessors.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Pair.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Pointing.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/RaycastHelper.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/RegisteredObjects.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ResetableLazy.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/UniqueLinkedList.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/VecHelper.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/VoxelShaper.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/WorldAttached.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/WorldHelper.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/animation/Force.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/animation/LerpedFloat.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/animation/PhysicalFloat.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockParams.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockRenderer.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlocks.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/AABBOutline.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/BlockClusterOutline.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/ChasingAABBOutline.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/LineOutline.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/Outline.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/Outliner.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/DummyLevelEntityGetter.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/DummyStatusListener.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/PlacementSimulationServerWorld.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/RayTraceWorld.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/WrappedClientWorld.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/WrappedServerWorld.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/WrappedWorld.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/package-info.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/license.txt diff --git a/build.gradle b/build.gradle index f32675c..ce6f92c 100644 --- a/build.gradle +++ b/build.gradle @@ -109,6 +109,12 @@ repositories { // flatDir { // dir 'libs' // } + + //Flywheel + maven { + name "tterrag maven" + url "https://maven.tterrag.com/" + } } dependencies { @@ -128,6 +134,10 @@ dependencies { // For more info... // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html // http://www.gradle.org/docs/current/userguide/dependency_management.html + + //Flywheel + //Versions: https://maven.tterrag.com/com/jozufozu/flywheel/flywheel-forge-1.19.2/ + implementation fg.deobf("com.jozufozu.flywheel:flywheel-forge-1.19.2:0.6.8-13") } // Example for how to get properties into the manifest for reading by the runtime.. diff --git a/src/main/java/nl/requios/effortlessbuilding/ClientEvents.java b/src/main/java/nl/requios/effortlessbuilding/ClientEvents.java new file mode 100644 index 0000000..120f6c3 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/ClientEvents.java @@ -0,0 +1,337 @@ +package nl.requios.effortlessbuilding; + +import com.mojang.blaze3d.platform.InputConstants; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import net.minecraft.ChatFormatting; +import net.minecraft.client.KeyMapping; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.InputEvent; +import net.minecraftforge.client.event.RegisterKeyMappingsEvent; +import net.minecraftforge.client.event.RegisterShadersEvent; +import net.minecraftforge.client.event.ScreenEvent; +import net.minecraftforge.client.settings.KeyConflictContext; +import net.minecraftforge.client.settings.KeyModifier; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import nl.requios.effortlessbuilding.buildmode.BuildModes; +import nl.requios.effortlessbuilding.buildmode.ModeOptions; +import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; +import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; +import nl.requios.effortlessbuilding.compatibility.CompatHelper; +import nl.requios.effortlessbuilding.gui.buildmode.PlayerSettingsGui; +import nl.requios.effortlessbuilding.gui.buildmode.RadialMenu; +import nl.requios.effortlessbuilding.gui.buildmodifier.ModifierSettingsGui; +import nl.requios.effortlessbuilding.helper.ReachHelper; +import nl.requios.effortlessbuilding.network.*; +import nl.requios.effortlessbuilding.render.BuildRenderTypes; +import org.lwjgl.glfw.GLFW; + +import java.io.IOException; + +@EventBusSubscriber(Dist.CLIENT) +public class ClientEvents { + + public static KeyMapping[] keyBindings; + public static HitResult previousLookAt; + public static HitResult currentLookAt; + public static int ticksInGame = 0; + private static int placeCooldown = 0; + private static int breakCooldown = 0; + + //Mod Bus Events + @EventBusSubscriber(value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD) + public static class ModBusEvents { + + @SubscribeEvent + public static void registerKeyMappings(RegisterKeyMappingsEvent event) { + EffortlessBuilding.log("Registering KeyMappings!"); + + // register key bindings + keyBindings = new KeyMapping[6]; + + // instantiate the key bindings + keyBindings[0] = new KeyMapping("key.effortlessbuilding.hud.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_KP_ADD, 0), "key.effortlessbuilding.category"); + keyBindings[1] = new KeyMapping("key.effortlessbuilding.replace.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_KP_SUBTRACT, 0), "key.effortlessbuilding.category"); + keyBindings[2] = new KeyMapping("key.effortlessbuilding.mode.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_LEFT_ALT, 0), "key.effortlessbuilding.category"); + keyBindings[3] = new KeyMapping("key.effortlessbuilding.undo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Z, 0), "key.effortlessbuilding.category"); + keyBindings[4] = new KeyMapping("key.effortlessbuilding.redo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Y, 0), "key.effortlessbuilding.category"); + keyBindings[5] = new KeyMapping("key.effortlessbuilding.altplacement.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_LEFT_CONTROL, 0), "key.effortlessbuilding.category"); + + for (KeyMapping keyBinding : keyBindings) { + event.register(keyBinding); + } + } + + @SubscribeEvent + public static void registerShaders(RegisterShadersEvent event) throws IOException { + event.registerShader(new ShaderInstance(event.getResourceManager(), + new ResourceLocation(EffortlessBuilding.MODID, "dissolve"), + DefaultVertexFormat.BLOCK), + shaderInstance -> BuildRenderTypes.dissolveShaderInstance = shaderInstance); + } + } + + @SubscribeEvent + public static void onClientTick(TickEvent.ClientTickEvent event) { + if (event.phase == TickEvent.Phase.START) { + onMouseInput(); + + //Update previousLookAt + HitResult objectMouseOver = Minecraft.getInstance().hitResult; + //Checking for null is necessary! Even in vanilla when looking down ladders it is occasionally null (instead of Type MISS) + if (objectMouseOver == null) return; + + if (currentLookAt == null) { + currentLookAt = objectMouseOver; + previousLookAt = objectMouseOver; + return; + } + + if (objectMouseOver.getType() == HitResult.Type.BLOCK) { + if (currentLookAt.getType() != HitResult.Type.BLOCK) { + currentLookAt = objectMouseOver; + previousLookAt = objectMouseOver; + } else { + if (((BlockHitResult) currentLookAt).getBlockPos() != ((BlockHitResult) objectMouseOver).getBlockPos()) { + previousLookAt = currentLookAt; + currentLookAt = objectMouseOver; + } + } + } + } else if (event.phase == TickEvent.Phase.END) { + Screen gui = Minecraft.getInstance().screen; + if (gui == null || !gui.isPauseScreen()) { + ticksInGame++; + } + } + + } + + private static void onMouseInput() { + Minecraft mc = Minecraft.getInstance(); + LocalPlayer player = mc.player; + if (player == null) return; + BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode(); + + if (mc.screen != null || + buildMode == BuildModes.BuildModeEnum.NORMAL || + RadialMenu.instance.isVisible()) { + return; + } + + if (mc.options.keyUse.isDown()) { + + //KeyBinding.setKeyBindState(mc.gameSettings.keyBindUseItem.getKeyCode(), false); + + if (placeCooldown <= 0) { + placeCooldown = 4; + + ItemStack currentItemStack = player.getItemInHand(InteractionHand.MAIN_HAND); + if (currentItemStack.getItem() instanceof BlockItem || + (CompatHelper.isItemBlockProxy(currentItemStack) && !player.isShiftKeyDown())) { + + ItemStack itemStack = CompatHelper.getItemBlockFromStack(currentItemStack); + + //find position in distance + HitResult lookingAt = getLookingAt(player); + if (lookingAt != null && lookingAt.getType() == HitResult.Type.BLOCK) { + BlockHitResult blockLookingAt = (BlockHitResult) lookingAt; + + BuildModes.onBlockPlacedMessage(player, new BlockPlacedMessage(blockLookingAt, true)); + PacketHandler.INSTANCE.sendToServer(new BlockPlacedMessage(blockLookingAt, true)); + + //play sound if further than normal + if ((blockLookingAt.getLocation().subtract(player.getEyePosition(1f))).lengthSqr() > 25f && + itemStack.getItem() instanceof BlockItem) { + + BlockState state = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState(); + BlockPos blockPos = blockLookingAt.getBlockPos(); + SoundType soundType = state.getBlock().getSoundType(state, player.level, blockPos, player); + player.level.playSound(player, player.blockPosition(), soundType.getPlaceSound(), SoundSource.BLOCKS, + 0.4f, soundType.getPitch()); + player.swing(InteractionHand.MAIN_HAND); + } + } else { + BuildModes.onBlockPlacedMessage(player, new BlockPlacedMessage()); + PacketHandler.INSTANCE.sendToServer(new BlockPlacedMessage()); + } + } + } else if (buildMode == BuildModes.BuildModeEnum.NORMAL_PLUS) { + placeCooldown--; + if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) placeCooldown = 0; + } + } else { + placeCooldown = 0; + } + + if (mc.options.keyAttack.isDown()) { + + //Break block in distance in creative (or survival if enabled in config) + if (breakCooldown <= 0) { + breakCooldown = 4; + + HitResult lookingAt = getLookingAt(player); + if (lookingAt != null && lookingAt.getType() == HitResult.Type.BLOCK) { + BlockHitResult blockLookingAt = (BlockHitResult) lookingAt; + + BuildModes.onBlockBrokenMessage(player, new BlockBrokenMessage(blockLookingAt)); + PacketHandler.INSTANCE.sendToServer(new BlockBrokenMessage(blockLookingAt)); + + //play sound if further than normal + if ((blockLookingAt.getLocation().subtract(player.getEyePosition(1f))).lengthSqr() > 25f) { + + BlockPos blockPos = blockLookingAt.getBlockPos(); + BlockState state = player.level.getBlockState(blockPos); + SoundType soundtype = state.getBlock().getSoundType(state, player.level, blockPos, player); + player.level.playSound(player, player.blockPosition(), soundtype.getBreakSound(), SoundSource.BLOCKS, + 0.4f, soundtype.getPitch()); + player.swing(InteractionHand.MAIN_HAND); + } + } else { + BuildModes.onBlockBrokenMessage(player, new BlockBrokenMessage()); + PacketHandler.INSTANCE.sendToServer(new BlockBrokenMessage()); + } + } else if (buildMode == BuildModes.BuildModeEnum.NORMAL_PLUS) { + breakCooldown--; + if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) breakCooldown = 0; + } + + //EffortlessBuilding.packetHandler.sendToServer(new CancelModeMessage()); + + } else { + breakCooldown = 0; + } + } + + @SubscribeEvent(receiveCanceled = true) + public static void onKeyPress(InputEvent.Key event) { + LocalPlayer player = Minecraft.getInstance().player; + if (player == null) + return; + + //Remember to send packet to server if necessary + //Show Modifier Settings GUI + if (keyBindings[0].consumeClick()) { + openModifierSettings(); + } + + //QuickReplace toggle + if (keyBindings[1].consumeClick()) { + ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); + modifierSettings.setQuickReplace(!modifierSettings.doQuickReplace()); + EffortlessBuilding.log(player, "Set " + ChatFormatting.GOLD + "Quick Replace " + ChatFormatting.RESET + ( + modifierSettings.doQuickReplace() ? "on" : "off")); + PacketHandler.INSTANCE.sendToServer(new ModifierSettingsMessage(modifierSettings)); + } + + //Radial menu + if (keyBindings[2].isDown()) { + if (ReachHelper.getMaxReach(player) > 0) { + if (!RadialMenu.instance.isVisible()) { + Minecraft.getInstance().setScreen(RadialMenu.instance); + } + } else { + EffortlessBuilding.log(player, "Build modes are disabled until your reach has increased. Increase your reach with craftable reach upgrades."); + } + } + + //Undo (Ctrl+Z) + if (keyBindings[3].consumeClick()) { + ModeOptions.ActionEnum action = ModeOptions.ActionEnum.UNDO; + ModeOptions.performAction(player, action); + PacketHandler.INSTANCE.sendToServer(new ModeActionMessage(action)); + } + + //Redo (Ctrl+Y) + if (keyBindings[4].consumeClick()) { + ModeOptions.ActionEnum action = ModeOptions.ActionEnum.REDO; + ModeOptions.performAction(player, action); + PacketHandler.INSTANCE.sendToServer(new ModeActionMessage(action)); + } + + //Change placement mode + if (keyBindings[5].consumeClick()) { + //Toggle between first two actions of the first option of the current build mode + BuildModes.BuildModeEnum currentBuildMode = ModeSettingsManager.getModeSettings(player).getBuildMode(); + if (currentBuildMode.options.length > 0) { + ModeOptions.OptionEnum option = currentBuildMode.options[0]; + if (option.actions.length >= 2) { + if (ModeOptions.getOptionSetting(option) == option.actions[0]) { + ModeOptions.performAction(player, option.actions[1]); + PacketHandler.INSTANCE.sendToServer(new ModeActionMessage(option.actions[1])); + } else { + ModeOptions.performAction(player, option.actions[0]); + PacketHandler.INSTANCE.sendToServer(new ModeActionMessage(option.actions[0])); + } + } + } + } + + } + + public static void openModifierSettings() { + Minecraft mc = Minecraft.getInstance(); + LocalPlayer player = mc.player; + if (player == null) return; + + //Disabled if max reach is 0, might be set in the config that way. + if (ReachHelper.getMaxReach(player) == 0) { + EffortlessBuilding.log(player, "Build modifiers are disabled until your reach has increased. Increase your reach with craftable reach upgrades."); + } else { + mc.setScreen(new ModifierSettingsGui()); + } + } + + public static void openPlayerSettings() { + Minecraft mc = Minecraft.getInstance(); + mc.setScreen(new PlayerSettingsGui()); + } + + @SubscribeEvent + public static void onGuiOpen(ScreenEvent event) { + Player player = Minecraft.getInstance().player; + if (player != null) { + BuildModes.initializeMode(player); + } + } + + public static boolean isKeybindDown(int keybindIndex) { + return InputConstants.isKeyDown( + Minecraft.getInstance().getWindow().getWindow(), + keyBindings[2].getKey().getValue()); + } + + public static HitResult getLookingAt(Player player) { + Level world = player.level; + + //base distance off of player ability (config) + float raytraceRange = ReachHelper.getPlacementReach(player); + + Vec3 look = player.getLookAngle(); + Vec3 start = new Vec3(player.getX(), player.getY() + player.getEyeHeight(), player.getZ()); + Vec3 end = new Vec3(player.getX() + look.x * raytraceRange, player.getY() + player.getEyeHeight() + look.y * raytraceRange, player.getZ() + look.z * raytraceRange); +// return player.rayTrace(raytraceRange, 1f, RayTraceFluidMode.NEVER); + //TODO 1.14 check if correct + return world.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player)); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/EventHandler.java b/src/main/java/nl/requios/effortlessbuilding/CommonEvents.java similarity index 93% rename from src/main/java/nl/requios/effortlessbuilding/EventHandler.java rename to src/main/java/nl/requios/effortlessbuilding/CommonEvents.java index 3aec2e2..8c00125 100644 --- a/src/main/java/nl/requios/effortlessbuilding/EventHandler.java +++ b/src/main/java/nl/requios/effortlessbuilding/CommonEvents.java @@ -8,13 +8,13 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.server.level.ServerPlayer; import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.event.AttachCapabilitiesEvent; -import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.level.BlockEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; import net.minecraftforge.network.PacketDistributor; import nl.requios.effortlessbuilding.buildmode.BuildModes; import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; @@ -29,8 +29,19 @@ import nl.requios.effortlessbuilding.network.ClearUndoMessage; import nl.requios.effortlessbuilding.network.PacketHandler; import nl.requios.effortlessbuilding.network.RequestLookAtMessage; -@Mod.EventBusSubscriber(modid = EffortlessBuilding.MODID, bus = Mod.EventBusSubscriber.Bus.FORGE) -public class EventHandler { +@EventBusSubscriber +public class CommonEvents { + + //Mod Bus Events + @EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD) + public static class ModBusEvents { + + @SubscribeEvent + public void registerCapabilities(RegisterCapabilitiesEvent event){ + event.register(ModifierCapabilityManager.IModifierCapability.class); + event.register(ModeCapabilityManager.IModeCapability.class); + } + } @SubscribeEvent public static void attachCapabilities(AttachCapabilitiesEvent event) { @@ -172,4 +183,6 @@ public class EventHandler { ModifierSettingsManager.setModifierSettings(newPlayer, ModifierSettingsManager.getModifierSettings(oldPlayer)); ModeSettingsManager.setModeSettings(newPlayer, ModeSettingsManager.getModeSettings(oldPlayer)); } + + } diff --git a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java index 4331413..3dd7e3f 100644 --- a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java +++ b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java @@ -5,21 +5,19 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.item.Item; -import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.config.ModConfig; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.network.IContainerFactory; -import net.minecraftforge.registries.RegistryObject; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; -import nl.requios.effortlessbuilding.capability.ModeCapabilityManager; -import nl.requios.effortlessbuilding.capability.ModifierCapabilityManager; +import net.minecraftforge.registries.RegistryObject; import nl.requios.effortlessbuilding.compatibility.CompatHelper; import nl.requios.effortlessbuilding.gui.DiamondRandomizerBagContainer; import nl.requios.effortlessbuilding.gui.GoldenRandomizerBagContainer; @@ -39,7 +37,7 @@ public class EffortlessBuilding { public static final Logger logger = LogManager.getLogger(); public static EffortlessBuilding instance; - public static IProxy proxy = DistExecutor.runForDist(() -> ClientProxy::new, () -> ServerProxy::new); + public static IProxy proxy = DistExecutor.safeRunForDist(() -> ClientProxy::new, () -> ServerProxy::new); //Registration private static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID); @@ -59,8 +57,13 @@ public class EffortlessBuilding { public EffortlessBuilding() { instance = this; - // Register ourselves for server and other game events we are interested in - FMLJavaModLoadingContext.get().getModEventBus().register(this); + ModLoadingContext modLoadingContext = ModLoadingContext.get(); + IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + IEventBus forgeEventBus = MinecraftForge.EVENT_BUS; + + modEventBus.addListener(EffortlessBuilding::setup); + + DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> () -> EffortlessBuildingClient.onConstructorClient(modEventBus, forgeEventBus)); ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus()); CONTAINERS.register(FMLJavaModLoadingContext.get().getModEventBus()); @@ -69,29 +72,15 @@ public class EffortlessBuilding { ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, BuildConfig.spec); } - public static MenuType registerContainer(IContainerFactory fact){ - MenuType type = new MenuType(fact); - return type; - } - - @SubscribeEvent - public void setup(final FMLCommonSetupEvent event) { + public static void setup(final FMLCommonSetupEvent event) { PacketHandler.register(); - proxy.setup(event); - CompatHelper.setup(); } - @SubscribeEvent - public void clientSetup(final FMLClientSetupEvent event) { - proxy.clientSetup(event); - } - - @SubscribeEvent - public void registerCapabilities(RegisterCapabilitiesEvent event){ - event.register(ModifierCapabilityManager.IModifierCapability.class); - event.register(ModeCapabilityManager.IModeCapability.class); + public static MenuType registerContainer(IContainerFactory fact){ + MenuType type = new MenuType(fact); + return type; } public static void log(String msg) { @@ -110,4 +99,5 @@ public class EffortlessBuilding { public static void logTranslate(Player player, String prefix, String translationKey, String suffix, boolean actionBar) { proxy.logTranslate(player, prefix, translationKey, suffix, actionBar); } + } diff --git a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuildingClient.java b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuildingClient.java new file mode 100644 index 0000000..91d64d0 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuildingClient.java @@ -0,0 +1,21 @@ +package nl.requios.effortlessbuilding; + +import net.minecraft.client.gui.screens.MenuScreens; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import nl.requios.effortlessbuilding.gui.DiamondRandomizerBagScreen; +import nl.requios.effortlessbuilding.gui.GoldenRandomizerBagScreen; +import nl.requios.effortlessbuilding.gui.RandomizerBagScreen; + +public class EffortlessBuildingClient { + + public static void onConstructorClient(IEventBus modEventBus, IEventBus forgeEventBus) { + modEventBus.addListener(EffortlessBuildingClient::clientSetup); + } + + public static void clientSetup(final FMLClientSetupEvent event) { + MenuScreens.register(EffortlessBuilding.RANDOMIZER_BAG_CONTAINER.get(), RandomizerBagScreen::new); + MenuScreens.register(EffortlessBuilding.GOLDEN_RANDOMIZER_BAG_CONTAINER.get(), GoldenRandomizerBagScreen::new); + MenuScreens.register(EffortlessBuilding.DIAMOND_RANDOMIZER_BAG_CONTAINER.get(), DiamondRandomizerBagScreen::new); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/ModClientEventHandler.java b/src/main/java/nl/requios/effortlessbuilding/ModClientEventHandler.java deleted file mode 100644 index 2218e4f..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/ModClientEventHandler.java +++ /dev/null @@ -1,51 +0,0 @@ -package nl.requios.effortlessbuilding; - -import com.mojang.blaze3d.platform.InputConstants; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import net.minecraft.client.KeyMapping; -import net.minecraft.client.renderer.ShaderInstance; -import net.minecraft.resources.ResourceLocation; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.RegisterKeyMappingsEvent; -import net.minecraftforge.client.event.RegisterShadersEvent; -import net.minecraftforge.client.settings.KeyConflictContext; -import net.minecraftforge.client.settings.KeyModifier; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import nl.requios.effortlessbuilding.proxy.ClientProxy; -import nl.requios.effortlessbuilding.render.BuildRenderTypes; -import org.lwjgl.glfw.GLFW; - -import java.io.IOException; - -@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD, value = {Dist.CLIENT}) -public class ModClientEventHandler { - - @SubscribeEvent - public static void registerKeyMappings(RegisterKeyMappingsEvent event) { - EffortlessBuilding.log("Registering KeyMappings!"); - - // register key bindings - ClientProxy.keyBindings = new KeyMapping[6]; - - // instantiate the key bindings - ClientProxy.keyBindings[0] = new KeyMapping("key.effortlessbuilding.hud.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_KP_ADD, 0), "key.effortlessbuilding.category"); - ClientProxy.keyBindings[1] = new KeyMapping("key.effortlessbuilding.replace.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_KP_SUBTRACT, 0), "key.effortlessbuilding.category"); - ClientProxy.keyBindings[2] = new KeyMapping("key.effortlessbuilding.mode.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_LEFT_ALT, 0), "key.effortlessbuilding.category"); - ClientProxy.keyBindings[3] = new KeyMapping("key.effortlessbuilding.undo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Z, 0), "key.effortlessbuilding.category"); - ClientProxy.keyBindings[4] = new KeyMapping("key.effortlessbuilding.redo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Y, 0), "key.effortlessbuilding.category"); - ClientProxy.keyBindings[5] = new KeyMapping("key.effortlessbuilding.altplacement.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_LEFT_CONTROL, 0), "key.effortlessbuilding.category"); - - for (KeyMapping keyBinding : ClientProxy.keyBindings) { - event.register(keyBinding); - } - } - - @SubscribeEvent - public static void registerShaders(RegisterShadersEvent event) throws IOException { - event.registerShader(new ShaderInstance(event.getResourceManager(), - new ResourceLocation(EffortlessBuilding.MODID, "dissolve"), - DefaultVertexFormat.BLOCK), - shaderInstance -> BuildRenderTypes.dissolveShaderInstance = shaderInstance); - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java index 101c6d5..dc75454 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java @@ -2,6 +2,7 @@ package nl.requios.effortlessbuilding.buildmode; import net.minecraft.world.entity.player.Player; import net.minecraft.ChatFormatting; +import nl.requios.effortlessbuilding.ClientEvents; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; import nl.requios.effortlessbuilding.buildmodifier.UndoRedo; @@ -78,11 +79,11 @@ public class ModeOptions { break; case OPEN_MODIFIER_SETTINGS: if (player.level.isClientSide) - ClientProxy.openModifierSettings(); + ClientEvents.openModifierSettings(); break; case OPEN_PLAYER_SETTINGS: if (player.level.isClientSide) - ClientProxy.openPlayerSettings(); + ClientEvents.openPlayerSettings(); break; case NORMAL_SPEED: diff --git a/src/main/java/nl/requios/effortlessbuilding/create/AllSpecialTextures.java b/src/main/java/nl/requios/effortlessbuilding/create/AllSpecialTextures.java new file mode 100644 index 0000000..0808f30 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/AllSpecialTextures.java @@ -0,0 +1,33 @@ +package nl.requios.effortlessbuilding.create; + +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.resources.ResourceLocation; + +public enum AllSpecialTextures { + + BLANK("blank.png"), + CHECKERED("checkerboard.png"), + THIN_CHECKERED("thin_checkerboard.png"), + CUTOUT_CHECKERED("cutout_checkerboard.png"), + HIGHLIGHT_CHECKERED("highlighted_checkerboard.png"), + SELECTION("selection.png"), + GLUE("glue.png"), + + ; + + public static final String ASSET_PATH = "textures/special/"; + private ResourceLocation location; + + private AllSpecialTextures(String filename) { + location = Create.asResource(ASSET_PATH + filename); + } + + public void bind() { + RenderSystem.setShaderTexture(0, location); + } + + public ResourceLocation getLocation() { + return location; + } + +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/create/Create.java b/src/main/java/nl/requios/effortlessbuilding/create/Create.java new file mode 100644 index 0000000..70d417c --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/Create.java @@ -0,0 +1,16 @@ +package nl.requios.effortlessbuilding.create; + +import com.mojang.logging.LogUtils; +import net.minecraft.resources.ResourceLocation; +import nl.requios.effortlessbuilding.EffortlessBuilding; +import org.slf4j.Logger; + +public class Create { + public static final String ID = EffortlessBuilding.MODID; + + public static final Logger LOGGER = LogUtils.getLogger(); + + public static ResourceLocation asResource(String path) { + return new ResourceLocation(EffortlessBuilding.MODID, path); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/CreateClient.java b/src/main/java/nl/requios/effortlessbuilding/create/CreateClient.java new file mode 100644 index 0000000..a983c25 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/CreateClient.java @@ -0,0 +1,15 @@ +package nl.requios.effortlessbuilding.create; + +import nl.requios.effortlessbuilding.create.foundation.render.SuperByteBufferCache; +import nl.requios.effortlessbuilding.create.foundation.utility.ghost.GhostBlocks; +import nl.requios.effortlessbuilding.create.foundation.utility.outliner.Outliner; + +public class CreateClient { + public static final SuperByteBufferCache BUFFER_CACHE = new SuperByteBufferCache(); + public static final Outliner OUTLINER = new Outliner(); + public static final GhostBlocks GHOST_BLOCKS = new GhostBlocks(); + + public static void invalidateRenderers() { + CreateClient.BUFFER_CACHE.invalidate(); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/events/ClientEvents.java b/src/main/java/nl/requios/effortlessbuilding/create/events/ClientEvents.java new file mode 100644 index 0000000..0a13b2c --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/events/ClientEvents.java @@ -0,0 +1,96 @@ +package nl.requios.effortlessbuilding.create.events; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.RenderLevelLastEvent; +import net.minecraftforge.client.event.ViewportEvent; +import net.minecraftforge.event.TickEvent.ClientTickEvent; +import net.minecraftforge.event.level.LevelEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import nl.requios.effortlessbuilding.create.Create; +import nl.requios.effortlessbuilding.create.CreateClient; +import nl.requios.effortlessbuilding.create.foundation.render.SuperRenderTypeBuffer; +import nl.requios.effortlessbuilding.create.foundation.utility.AnimationTickHolder; +import nl.requios.effortlessbuilding.create.foundation.utility.CameraAngleAnimationService; +import nl.requios.effortlessbuilding.create.foundation.utility.worldWrappers.WrappedClientWorld; + +@EventBusSubscriber(Dist.CLIENT) +public class ClientEvents { + + private static final String ITEM_PREFIX = "item." + Create.ID; + private static final String BLOCK_PREFIX = "block." + Create.ID; + + @SubscribeEvent + public static void onTick(ClientTickEvent event) { + if (!isGameActive()) + return; + + Level world = Minecraft.getInstance().level; + AnimationTickHolder.tick(); + + CreateClient.OUTLINER.tickOutlines(); + CreateClient.GHOST_BLOCKS.tickGhosts(); + CameraAngleAnimationService.tick(); + } + + @SubscribeEvent + public static void onLoadWorld(LevelEvent.Load event) { + LevelAccessor world = event.getLevel(); + if (world.isClientSide() && world instanceof ClientLevel && !(world instanceof WrappedClientWorld)) { + CreateClient.invalidateRenderers(); + AnimationTickHolder.reset(); + } + } + + @SubscribeEvent + public static void onUnloadWorld(LevelEvent.Unload event) { + if (!event.getLevel() + .isClientSide()) + return; + CreateClient.invalidateRenderers(); + AnimationTickHolder.reset(); + } + + @SubscribeEvent + public static void onRenderWorld(RenderLevelLastEvent event) { + Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera() + .getPosition(); + float pt = AnimationTickHolder.getPartialTicks(); + + PoseStack ms = event.getPoseStack(); + ms.pushPose(); + ms.translate(-cameraPos.x(), -cameraPos.y(), -cameraPos.z()); + SuperRenderTypeBuffer buffer = SuperRenderTypeBuffer.getInstance(); + + CreateClient.GHOST_BLOCKS.renderAll(ms, buffer); + + CreateClient.OUTLINER.renderOutlines(ms, buffer, pt); + buffer.draw(); + RenderSystem.enableCull(); + + ms.popPose(); + } + + @SubscribeEvent + public static void onCameraSetup(ViewportEvent.ComputeCameraAngles event) { + float partialTicks = AnimationTickHolder.getPartialTicks(); + + if (CameraAngleAnimationService.isYawAnimating()) + event.setYaw(CameraAngleAnimationService.getYaw(partialTicks)); + + if (CameraAngleAnimationService.isPitchAnimating()) + event.setPitch(CameraAngleAnimationService.getPitch(partialTicks)); + } + + protected static boolean isGameActive() { + return !(Minecraft.getInstance().level == null || Minecraft.getInstance().player == null); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/events/CommonEvents.java b/src/main/java/nl/requios/effortlessbuilding/create/events/CommonEvents.java new file mode 100644 index 0000000..5e83123 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/events/CommonEvents.java @@ -0,0 +1,39 @@ +package nl.requios.effortlessbuilding.create.events; + +import net.minecraft.world.level.LevelAccessor; +import net.minecraftforge.event.level.LevelEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import nl.requios.effortlessbuilding.create.foundation.utility.WorldAttached; + +@EventBusSubscriber +public class CommonEvents { + + @SubscribeEvent + public static void onUnloadWorld(LevelEvent.Unload event) { + LevelAccessor world = event.getLevel(); + WorldAttached.invalidateWorld(world); + } + + + @EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD) + public static class ModBusEvents { + +// @SubscribeEvent +// public static void addPackFinders(AddPackFindersEvent event) { +// if (event.getPackType() == PackType.CLIENT_RESOURCES) { +// IModFileInfo modFileInfo = ModList.get().getModFileById(Create.ID); +// if (modFileInfo == null) { +// Create.LOGGER.error("Could not find Create mod file info; built-in resource packs will be missing!"); +// return; +// } +// IModFile modFile = modFileInfo.getFile(); +// event.addRepositorySource((consumer, constructor) -> { +// consumer.accept(Pack.create(Create.asResource("legacy_copper").toString(), false, () -> new ModFilePackResources("Create Legacy Copper", modFile, "resourcepacks/legacy_copper"), constructor, Pack.Position.TOP, PackSource.DEFAULT)); +// }); +// } +// } + + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/ClientResourceReloadListener.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/ClientResourceReloadListener.java new file mode 100644 index 0000000..f3ec8d0 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/ClientResourceReloadListener.java @@ -0,0 +1,16 @@ +package nl.requios.effortlessbuilding.create.foundation; + +import nl.requios.effortlessbuilding.create.foundation.utility.LangNumberFormat; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.packs.resources.ResourceManagerReloadListener; + +public class ClientResourceReloadListener implements ResourceManagerReloadListener { + + @Override + public void onResourceManagerReload(ResourceManager resourceManager) { +// CreateClient.invalidateRenderers(); +// SoundScapes.invalidateAll(); + LangNumberFormat.numberFormat.update(); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/ModFilePackResources.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/ModFilePackResources.java new file mode 100644 index 0000000..e3715db --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/ModFilePackResources.java @@ -0,0 +1,25 @@ +package nl.requios.effortlessbuilding.create.foundation; + +import net.minecraftforge.forgespi.locating.IModFile; +import net.minecraftforge.resource.PathPackResources; + +import java.nio.file.Path; + +public class ModFilePackResources extends PathPackResources { + protected final IModFile modFile; + protected final String sourcePath; + + public ModFilePackResources(String name, IModFile modFile, String sourcePath) { + super(name, modFile.findResource(sourcePath)); + this.modFile = modFile; + this.sourcePath = sourcePath; + } + + @Override + protected Path resolve(String... paths) { + String[] allPaths = new String[paths.length + 1]; + allPaths[0] = sourcePath; + System.arraycopy(paths, 0, allPaths, 1, paths.length); + return modFile.findResource(allPaths); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/block/render/SpriteShiftEntry.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/block/render/SpriteShiftEntry.java new file mode 100644 index 0000000..334dafe --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/block/render/SpriteShiftEntry.java @@ -0,0 +1,49 @@ +package nl.requios.effortlessbuilding.create.foundation.block.render; + +import com.jozufozu.flywheel.core.StitchedSprite; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.resources.ResourceLocation; + +public class SpriteShiftEntry { + protected StitchedSprite original; + protected StitchedSprite target; + + public void set(ResourceLocation originalTextureLocation, ResourceLocation targetTextureLocation) { + original = new StitchedSprite(originalTextureLocation); + target = new StitchedSprite(targetTextureLocation); + } + + public ResourceLocation getOriginalResourceLocation() { + return original.getLocation(); + } + + public ResourceLocation getTargetResourceLocation() { + return target.getLocation(); + } + + public TextureAtlasSprite getOriginal() { + return original.get(); + } + + public TextureAtlasSprite getTarget() { + return target.get(); + } + + public float getTargetU(float localU) { + return getTarget().getU(getUnInterpolatedU(getOriginal(), localU)); + } + + public float getTargetV(float localV) { + return getTarget().getV(getUnInterpolatedV(getOriginal(), localV)); + } + + public static float getUnInterpolatedU(TextureAtlasSprite sprite, float u) { + float f = sprite.getU1() - sprite.getU0(); + return (u - sprite.getU0()) / f * 16.0F; + } + + public static float getUnInterpolatedV(TextureAtlasSprite sprite, float v) { + float f = sprite.getV1() - sprite.getV0(); + return (v - sprite.getV0()) / f * 16.0F; + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/BakedModelRenderHelper.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/BakedModelRenderHelper.java new file mode 100644 index 0000000..695c31c --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/BakedModelRenderHelper.java @@ -0,0 +1,29 @@ +package nl.requios.effortlessbuilding.create.foundation.render; + +import com.jozufozu.flywheel.core.model.ModelUtil; +import com.jozufozu.flywheel.util.Pair; +import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.block.BlockRenderDispatcher; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.world.level.block.state.BlockState; + +public class BakedModelRenderHelper { + + public static SuperByteBuffer standardBlockRender(BlockState renderedState) { + BlockRenderDispatcher dispatcher = Minecraft.getInstance() + .getBlockRenderer(); + return standardModelRender(dispatcher.getBlockModel(renderedState), renderedState); + } + + public static SuperByteBuffer standardModelRender(BakedModel model, BlockState referenceState) { + return standardModelRender(model, referenceState, new PoseStack()); + } + + public static SuperByteBuffer standardModelRender(BakedModel model, BlockState referenceState, PoseStack ms) { + Pair pair = ModelUtil.getBufferBuilder(model, referenceState, ms); + return new SuperByteBuffer(pair.first(), pair.second()); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/CachedBufferer.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/CachedBufferer.java new file mode 100644 index 0000000..aca56b4 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/CachedBufferer.java @@ -0,0 +1,88 @@ +package nl.requios.effortlessbuilding.create.foundation.render; + +import com.jozufozu.flywheel.core.PartialModel; +import com.jozufozu.flywheel.util.transform.TransformStack; +import com.mojang.blaze3d.vertex.PoseStack; +import nl.requios.effortlessbuilding.EffortlessBuildingClient; +import nl.requios.effortlessbuilding.create.CreateClient; +import nl.requios.effortlessbuilding.create.foundation.render.SuperByteBufferCache.Compartment; +import nl.requios.effortlessbuilding.create.foundation.utility.AngleHelper; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.state.BlockState; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.function.Supplier; + +import static net.minecraft.world.level.block.state.properties.BlockStateProperties.FACING; + +public class CachedBufferer { + + public static final Compartment GENERIC_TILE = new Compartment<>(); + public static final Compartment PARTIAL = new Compartment<>(); + public static final Compartment> DIRECTIONAL_PARTIAL = new Compartment<>(); + + public static SuperByteBuffer block(BlockState toRender) { + return block(GENERIC_TILE, toRender); + } + + public static SuperByteBuffer block(Compartment compartment, BlockState toRender) { + return CreateClient.BUFFER_CACHE.get(compartment, toRender, () -> BakedModelRenderHelper.standardBlockRender(toRender)); + } + + public static SuperByteBuffer partial(PartialModel partial, BlockState referenceState) { + return CreateClient.BUFFER_CACHE.get(PARTIAL, partial, + () -> BakedModelRenderHelper.standardModelRender(partial.get(), referenceState)); + } + + public static SuperByteBuffer partial(PartialModel partial, BlockState referenceState, + Supplier modelTransform) { + return CreateClient.BUFFER_CACHE.get(PARTIAL, partial, + () -> BakedModelRenderHelper.standardModelRender(partial.get(), referenceState, modelTransform.get())); + } + + public static SuperByteBuffer partialFacing(PartialModel partial, BlockState referenceState) { + Direction facing = referenceState.getValue(FACING); + return partialFacing(partial, referenceState, facing); + } + + public static SuperByteBuffer partialFacing(PartialModel partial, BlockState referenceState, Direction facing) { + return partialDirectional(partial, referenceState, facing, + rotateToFace(facing)); + } + + public static SuperByteBuffer partialFacingVertical(PartialModel partial, BlockState referenceState, Direction facing) { + return partialDirectional(partial, referenceState, facing, + rotateToFaceVertical(facing)); + } + + public static SuperByteBuffer partialDirectional(PartialModel partial, BlockState referenceState, Direction dir, + Supplier modelTransform) { + return CreateClient.BUFFER_CACHE.get(DIRECTIONAL_PARTIAL, Pair.of(dir, partial), + () -> BakedModelRenderHelper.standardModelRender(partial.get(), referenceState, modelTransform.get())); + } + + public static Supplier rotateToFace(Direction facing) { + return () -> { + PoseStack stack = new PoseStack(); + TransformStack.cast(stack) + .centre() + .rotateY(AngleHelper.horizontalAngle(facing)) + .rotateX(AngleHelper.verticalAngle(facing)) + .unCentre(); + return stack; + }; + } + + public static Supplier rotateToFaceVertical(Direction facing) { + return () -> { + PoseStack stack = new PoseStack(); + TransformStack.cast(stack) + .centre() + .rotateY(AngleHelper.horizontalAngle(facing)) + .rotateX(AngleHelper.verticalAngle(facing) + 90) + .unCentre(); + return stack; + }; + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/ForcedDiffuseState.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/ForcedDiffuseState.java new file mode 100644 index 0000000..8e1b34c --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/ForcedDiffuseState.java @@ -0,0 +1,30 @@ +package nl.requios.effortlessbuilding.create.foundation.render; + +import com.jozufozu.flywheel.util.DiffuseLightCalculator; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; + +import javax.annotation.Nullable; + +public final class ForcedDiffuseState { + private static final ThreadLocal> FORCED_DIFFUSE = ThreadLocal.withInitial(ObjectArrayList::new); + + private ForcedDiffuseState() { + } + + public static void pushCalculator(DiffuseLightCalculator calculator) { + FORCED_DIFFUSE.get().push(calculator); + } + + public static void popCalculator() { + FORCED_DIFFUSE.get().pop(); + } + + @Nullable + public static DiffuseLightCalculator getForcedCalculator() { + ObjectArrayList stack = FORCED_DIFFUSE.get(); + if (stack.isEmpty()) { + return null; + } + return stack.top(); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/RenderTypes.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/RenderTypes.java new file mode 100644 index 0000000..2ac1860 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/RenderTypes.java @@ -0,0 +1,160 @@ +package nl.requios.effortlessbuilding.create.foundation.render; + +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.VertexFormat; +import nl.requios.effortlessbuilding.create.AllSpecialTextures; +import nl.requios.effortlessbuilding.create.Create; +import net.minecraft.client.renderer.RenderStateShard; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.RegisterShadersEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; + +import java.io.IOException; + +// TODO 1.17: use custom shaders instead of vanilla ones +public class RenderTypes extends RenderStateShard { + + public static final ShaderStateShard GLOWING_SHADER = new ShaderStateShard(() -> Shaders.glowingShader); + + private static final RenderType OUTLINE_SOLID = + RenderType.create(createLayerName("outline_solid"), DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, false, + false, RenderType.CompositeState.builder() + .setShaderState(RENDERTYPE_ENTITY_SOLID_SHADER) + .setTextureState(new TextureStateShard(AllSpecialTextures.BLANK.getLocation(), false, false)) + .setCullState(CULL) + .setLightmapState(LIGHTMAP) + .setOverlayState(OVERLAY) + .createCompositeState(false)); + + public static RenderType getOutlineSolid() { + return OUTLINE_SOLID; + } + + public static RenderType getOutlineTranslucent(ResourceLocation texture, boolean cull) { + return RenderType.create(createLayerName("outline_translucent" + (cull ? "_cull" : "")), + DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, false, true, RenderType.CompositeState.builder() + .setShaderState(cull ? RENDERTYPE_ENTITY_TRANSLUCENT_CULL_SHADER : RENDERTYPE_ENTITY_TRANSLUCENT_SHADER) + .setTextureState(new TextureStateShard(texture, false, false)) + .setTransparencyState(TRANSLUCENT_TRANSPARENCY) + .setCullState(cull ? CULL : NO_CULL) + .setLightmapState(LIGHTMAP) + .setOverlayState(OVERLAY) + .setWriteMaskState(COLOR_WRITE) + .createCompositeState(false)); + } + + public static RenderType getGlowingSolid(ResourceLocation texture) { + return RenderType.create(createLayerName("glowing_solid"), DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, + true, false, RenderType.CompositeState.builder() + .setShaderState(GLOWING_SHADER) + .setTextureState(new TextureStateShard(texture, false, false)) + .setCullState(CULL) + .setLightmapState(LIGHTMAP) + .setOverlayState(OVERLAY) + .createCompositeState(true)); + } + + private static final RenderType GLOWING_SOLID_DEFAULT = getGlowingSolid(InventoryMenu.BLOCK_ATLAS); + + public static RenderType getGlowingSolid() { + return GLOWING_SOLID_DEFAULT; + } + + public static RenderType getGlowingTranslucent(ResourceLocation texture) { + return RenderType.create(createLayerName("glowing_translucent"), DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, + 256, true, true, RenderType.CompositeState.builder() + .setShaderState(GLOWING_SHADER) + .setTextureState(new TextureStateShard(texture, false, false)) + .setTransparencyState(TRANSLUCENT_TRANSPARENCY) + .setLightmapState(LIGHTMAP) + .setOverlayState(OVERLAY) + .createCompositeState(true)); + } + + private static final RenderType ADDITIVE = RenderType.create(createLayerName("additive"), DefaultVertexFormat.BLOCK, + VertexFormat.Mode.QUADS, 256, true, true, RenderType.CompositeState.builder() + .setShaderState(BLOCK_SHADER) + .setTextureState(new TextureStateShard(InventoryMenu.BLOCK_ATLAS, false, false)) + .setTransparencyState(ADDITIVE_TRANSPARENCY) + .setCullState(NO_CULL) + .setLightmapState(LIGHTMAP) + .setOverlayState(OVERLAY) + .createCompositeState(true)); + + public static RenderType getAdditive() { + return ADDITIVE; + } + + private static final RenderType GLOWING_TRANSLUCENT_DEFAULT = getGlowingTranslucent(InventoryMenu.BLOCK_ATLAS); + + public static RenderType getGlowingTranslucent() { + return GLOWING_TRANSLUCENT_DEFAULT; + } + + private static final RenderType ITEM_PARTIAL_SOLID = + RenderType.create(createLayerName("item_partial_solid"), DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, true, + false, RenderType.CompositeState.builder() + .setShaderState(RENDERTYPE_ENTITY_SOLID_SHADER) + .setTextureState(BLOCK_SHEET) + .setCullState(CULL) + .setLightmapState(LIGHTMAP) + .setOverlayState(OVERLAY) + .createCompositeState(true)); + + public static RenderType getItemPartialSolid() { + return ITEM_PARTIAL_SOLID; + } + + private static final RenderType ITEM_PARTIAL_TRANSLUCENT = RenderType.create(createLayerName("item_partial_translucent"), + DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, true, true, RenderType.CompositeState.builder() + .setShaderState(RENDERTYPE_ENTITY_TRANSLUCENT_CULL_SHADER) + .setTextureState(BLOCK_SHEET) + .setTransparencyState(TRANSLUCENT_TRANSPARENCY) + .setLightmapState(LIGHTMAP) + .setOverlayState(OVERLAY) + .createCompositeState(true)); + + public static RenderType getItemPartialTranslucent() { + return ITEM_PARTIAL_TRANSLUCENT; + } + + private static final RenderType FLUID = RenderType.create(createLayerName("fluid"), + DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, false, true, RenderType.CompositeState.builder() + .setShaderState(RENDERTYPE_ENTITY_TRANSLUCENT_CULL_SHADER) + .setTextureState(BLOCK_SHEET_MIPPED) + .setTransparencyState(TRANSLUCENT_TRANSPARENCY) + .setLightmapState(LIGHTMAP) + .setOverlayState(OVERLAY) + .createCompositeState(true)); + + public static RenderType getFluid() { + return FLUID; + } + + private static String createLayerName(String name) { + return Create.ID + ":" + name; + } + + // Mmm gimme those protected fields + private RenderTypes() { + super(null, null, null); + } + + @EventBusSubscriber(value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD) + private static class Shaders { + private static ShaderInstance glowingShader; + + @SubscribeEvent + public static void onRegisterShaders(RegisterShadersEvent event) throws IOException { + ResourceManager resourceManager = event.getResourceManager(); + event.registerShader(new ShaderInstance(resourceManager, Create.asResource("glowing_shader"), DefaultVertexFormat.NEW_ENTITY), shader -> glowingShader = shader); + } + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/ShadowRenderHelper.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/ShadowRenderHelper.java new file mode 100644 index 0000000..d81b292 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/ShadowRenderHelper.java @@ -0,0 +1,112 @@ +package nl.requios.effortlessbuilding.create.foundation.render; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.VoxelShape; + +/** + * Taken from EntityRendererManager + */ +public class ShadowRenderHelper { + + private static final RenderType SHADOW_LAYER = + RenderType.entityNoOutline(new ResourceLocation("textures/misc/shadow.png")); + + public static void renderShadow(PoseStack matrixStack, MultiBufferSource buffer, float opacity, float radius) { + PoseStack.Pose entry = matrixStack.last(); + VertexConsumer builder = buffer.getBuffer(SHADOW_LAYER); + + opacity /= 2; + shadowVertex(entry, builder, opacity, -1 * radius, 0, -1 * radius, 0, 0); + shadowVertex(entry, builder, opacity, -1 * radius, 0, 1 * radius, 0, 1); + shadowVertex(entry, builder, opacity, 1 * radius, 0, 1 * radius, 1, 1); + shadowVertex(entry, builder, opacity, 1 * radius, 0, -1 * radius, 1, 0); + } + + public static void renderShadow(PoseStack matrixStack, MultiBufferSource buffer, LevelReader world, + Vec3 pos, float opacity, float radius) { + float f = radius; + + double d2 = pos.x(); + double d0 = pos.y(); + double d1 = pos.z(); + int i = Mth.floor(d2 - (double) f); + int j = Mth.floor(d2 + (double) f); + int k = Mth.floor(d0 - (double) f); + int l = Mth.floor(d0); + int i1 = Mth.floor(d1 - (double) f); + int j1 = Mth.floor(d1 + (double) f); + PoseStack.Pose entry = matrixStack.last(); + VertexConsumer builder = buffer.getBuffer(SHADOW_LAYER); + + for (BlockPos blockpos : BlockPos.betweenClosed(new BlockPos(i, k, i1), new BlockPos(j, l, j1))) { + renderBlockShadow(entry, builder, world, blockpos, d2, d0, d1, f, + opacity); + } + } + + private static void renderBlockShadow(PoseStack.Pose entry, VertexConsumer builder, + LevelReader world, BlockPos pos, double x, double y, double z, + float radius, float opacity) { + BlockPos blockpos = pos.below(); + BlockState blockstate = world.getBlockState(blockpos); + if (blockstate.getRenderShape() != RenderShape.INVISIBLE && world.getMaxLocalRawBrightness(pos) > 3) { + if (blockstate.isCollisionShapeFullBlock(world, blockpos)) { + VoxelShape voxelshape = blockstate.getShape(world, pos.below()); + if (!voxelshape.isEmpty()) { + float brightness = LightTexture.getBrightness(world.dimensionType(), world.getMaxLocalRawBrightness(pos)); + float f = (float) ((opacity - (y - pos.getY()) / 2.0D) * 0.5D * brightness); + if (f >= 0.0F) { + if (f > 1.0F) { + f = 1.0F; + } + + AABB AABB = voxelshape.bounds(); + double d0 = (double) pos.getX() + AABB.minX; + double d1 = (double) pos.getX() + AABB.maxX; + double d2 = (double) pos.getY() + AABB.minY; + double d3 = (double) pos.getZ() + AABB.minZ; + double d4 = (double) pos.getZ() + AABB.maxZ; + float f1 = (float) (d0 - x); + float f2 = (float) (d1 - x); + float f3 = (float) (d2 - y + 0.015625D); + float f4 = (float) (d3 - z); + float f5 = (float) (d4 - z); + float f6 = -f1 / 2.0F / radius + 0.5F; + float f7 = -f2 / 2.0F / radius + 0.5F; + float f8 = -f4 / 2.0F / radius + 0.5F; + float f9 = -f5 / 2.0F / radius + 0.5F; + shadowVertex(entry, builder, f, f1, f3, f4, f6, f8); + shadowVertex(entry, builder, f, f1, f3, f5, f6, f9); + shadowVertex(entry, builder, f, f2, f3, f5, f7, f9); + shadowVertex(entry, builder, f, f2, f3, f4, f7, f8); + } + } + } + } + } + + private static void shadowVertex(PoseStack.Pose entry, VertexConsumer builder, float alpha, + float x, float y, float z, float u, float v) { + builder.vertex(entry.pose(), x, y, z) + .color(1.0F, 1.0F, 1.0F, alpha) + .uv(u, v) + .overlayCoords(OverlayTexture.NO_OVERLAY) + .uv2(LightTexture.FULL_BRIGHT) + .normal(entry.normal(), 0.0F, 1.0F, 0.0F) + .endVertex(); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/SuperByteBuffer.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/SuperByteBuffer.java new file mode 100644 index 0000000..fff4a6f --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/SuperByteBuffer.java @@ -0,0 +1,464 @@ +package nl.requios.effortlessbuilding.create.foundation.render; + +import com.jozufozu.flywheel.api.vertex.ShadedVertexList; +import com.jozufozu.flywheel.backend.ShadersModHandler; +import com.jozufozu.flywheel.core.vertex.BlockVertexList; +import com.jozufozu.flywheel.util.DiffuseLightCalculator; +import com.jozufozu.flywheel.util.transform.TStack; +import com.jozufozu.flywheel.util.transform.Transform; +import com.mojang.blaze3d.vertex.BufferBuilder.DrawState; +import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.*; +import nl.requios.effortlessbuilding.create.foundation.block.render.SpriteShiftEntry; +import nl.requios.effortlessbuilding.create.foundation.utility.Color; +import it.unimi.dsi.fastutil.longs.Long2IntMap; +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.Mth; +import net.minecraft.world.level.Level; + +public class SuperByteBuffer implements Transform, TStack { + + private final ShadedVertexList template; + + // Vertex Position + private final PoseStack transforms; + + // Vertex Coloring + private boolean shouldColor; + private int r, g, b, a; + private boolean disableDiffuseMult; + private DiffuseLightCalculator diffuseCalculator; + + // Vertex Texture Coords + private SpriteShiftFunc spriteShiftFunc; + + // Vertex Overlay Color + private boolean hasOverlay; + private int overlay = OverlayTexture.NO_OVERLAY; + + // Vertex Lighting + private boolean useWorldLight; + private Matrix4f lightTransform; + private boolean hasCustomLight; + private int packedLightCoords; + private boolean hybridLight; + + // Vertex Normals + private boolean fullNormalTransform; + + // Temporary + private static final Long2IntMap WORLD_LIGHT_CACHE = new Long2IntOpenHashMap(); + + public SuperByteBuffer(RenderedBuffer buf, int unshadedStartVertex) { + DrawState drawState = buf.drawState(); + template = new BlockVertexList.Shaded(buf.vertexBuffer(), drawState.vertexCount(), drawState.format().getVertexSize(), unshadedStartVertex); + + transforms = new PoseStack(); + transforms.pushPose(); + } + + public void renderInto(PoseStack input, VertexConsumer builder) { + if (isEmpty()) + return; + + Matrix4f modelMat = input.last() + .pose() + .copy(); + Matrix4f localTransforms = transforms.last() + .pose(); + modelMat.multiply(localTransforms); + + Matrix3f normalMat; + if (fullNormalTransform) { + normalMat = input.last() + .normal() + .copy(); + Matrix3f localNormalTransforms = transforms.last() + .normal(); + normalMat.mul(localNormalTransforms); + } else { + normalMat = transforms.last() + .normal() + .copy(); + } + + if (useWorldLight) { + WORLD_LIGHT_CACHE.clear(); + } + + final Vector4f pos = new Vector4f(); + final Vector3f normal = new Vector3f(); + final Vector4f lightPos = new Vector4f(); + + DiffuseLightCalculator diffuseCalculator = ForcedDiffuseState.getForcedCalculator(); + final boolean disableDiffuseMult = + this.disableDiffuseMult || (ShadersModHandler.isShaderPackInUse() && diffuseCalculator == null); + if (diffuseCalculator == null) { + diffuseCalculator = this.diffuseCalculator; + if (diffuseCalculator == null) { + diffuseCalculator = DiffuseLightCalculator.forCurrentLevel(); + } + } + + final int vertexCount = template.getVertexCount(); + for (int i = 0; i < vertexCount; i++) { + float x = template.getX(i); + float y = template.getY(i); + float z = template.getZ(i); + + pos.set(x, y, z, 1F); + pos.transform(modelMat); + builder.vertex(pos.x(), pos.y(), pos.z()); + + float normalX = template.getNX(i); + float normalY = template.getNY(i); + float normalZ = template.getNZ(i); + + normal.set(normalX, normalY, normalZ); + normal.transform(normalMat); + float nx = normal.x(); + float ny = normal.y(); + float nz = normal.z(); + + byte r, g, b, a; + if (shouldColor) { + r = (byte) this.r; + g = (byte) this.g; + b = (byte) this.b; + a = (byte) this.a; + } else { + r = template.getR(i); + g = template.getG(i); + b = template.getB(i); + a = template.getA(i); + } + if (disableDiffuseMult) { + builder.color(r, g, b, a); + } else { + float instanceDiffuse = diffuseCalculator.getDiffuse(nx, ny, nz, template.isShaded(i)); + int colorR = transformColor(r, instanceDiffuse); + int colorG = transformColor(g, instanceDiffuse); + int colorB = transformColor(b, instanceDiffuse); + builder.color(colorR, colorG, colorB, a); + } + + float u = template.getU(i); + float v = template.getV(i); + if (spriteShiftFunc != null) { + spriteShiftFunc.shift(builder, u, v); + } else { + builder.uv(u, v); + } + + if (hasOverlay) { + builder.overlayCoords(overlay); + } + + int light; + if (useWorldLight) { + lightPos.set(((x - .5f) * 15 / 16f) + .5f, (y - .5f) * 15 / 16f + .5f, (z - .5f) * 15 / 16f + .5f, 1f); + lightPos.transform(localTransforms); + if (lightTransform != null) { + lightPos.transform(lightTransform); + } + + light = getLight(Minecraft.getInstance().level, lightPos); + if (hasCustomLight) { + light = maxLight(light, packedLightCoords); + } + } else if (hasCustomLight) { + light = packedLightCoords; + } else { + light = template.getLight(i); + } + + if (hybridLight) { + builder.uv2(maxLight(light, template.getLight(i))); + } else { + builder.uv2(light); + } + + builder.normal(nx, ny, nz); + + builder.endVertex(); + } + + reset(); + } + + public SuperByteBuffer reset() { + while (!transforms.clear()) + transforms.popPose(); + transforms.pushPose(); + + shouldColor = false; + r = 0; + g = 0; + b = 0; + a = 0; + disableDiffuseMult = false; + diffuseCalculator = null; + spriteShiftFunc = null; + hasOverlay = false; + overlay = OverlayTexture.NO_OVERLAY; + useWorldLight = false; + lightTransform = null; + hasCustomLight = false; + packedLightCoords = 0; + hybridLight = false; + fullNormalTransform = false; + return this; + } + + public boolean isEmpty() { + return template.isEmpty(); + } + + public PoseStack getTransforms() { + return transforms; + } + + @Override + public SuperByteBuffer translate(double x, double y, double z) { + transforms.translate(x, y, z); + return this; + } + + @Override + public SuperByteBuffer multiply(Quaternion quaternion) { + transforms.mulPose(quaternion); + return this; + } + + @Override + public SuperByteBuffer scale(float factorX, float factorY, float factorZ) { + transforms.scale(factorX, factorY, factorZ); + return this; + } + + @Override + public SuperByteBuffer pushPose() { + transforms.pushPose(); + return this; + } + + @Override + public SuperByteBuffer popPose() { + transforms.popPose(); + return this; + } + + @Override + public SuperByteBuffer mulPose(Matrix4f pose) { + transforms.last() + .pose() + .multiply(pose); + return this; + } + + @Override + public SuperByteBuffer mulNormal(Matrix3f normal) { + transforms.last() + .normal() + .mul(normal); + return this; + } + + public SuperByteBuffer transform(PoseStack stack) { + transforms.last() + .pose() + .multiply(stack.last() + .pose()); + transforms.last() + .normal() + .mul(stack.last() + .normal()); + return this; + } + + public SuperByteBuffer rotateCentered(Direction axis, float radians) { + translate(.5f, .5f, .5f).rotate(axis, radians) + .translate(-.5f, -.5f, -.5f); + return this; + } + + public SuperByteBuffer rotateCentered(Quaternion q) { + translate(.5f, .5f, .5f).multiply(q) + .translate(-.5f, -.5f, -.5f); + return this; + } + + public SuperByteBuffer color(int r, int g, int b, int a) { + shouldColor = true; + this.r = r; + this.g = g; + this.b = b; + this.a = a; + return this; + } + + public SuperByteBuffer color(int color) { + shouldColor = true; + r = ((color >> 16) & 0xFF); + g = ((color >> 8) & 0xFF); + b = (color & 0xFF); + a = 255; + return this; + } + + public SuperByteBuffer color(Color c) { + return color(c.getRGB()); + } + + /** + * Prevents vertex colors from being multiplied by the diffuse value calculated + * from the final transformed normal vector. Useful for entity rendering, when + * diffuse is applied automatically later. + */ + public SuperByteBuffer disableDiffuse() { + disableDiffuseMult = true; + return this; + } + + public SuperByteBuffer diffuseCalculator(DiffuseLightCalculator diffuseCalculator) { + this.diffuseCalculator = diffuseCalculator; + return this; + } + + public SuperByteBuffer shiftUV(SpriteShiftEntry entry) { + this.spriteShiftFunc = (builder, u, v) -> { + builder.uv(entry.getTargetU(u), entry.getTargetV(v)); + }; + return this; + } + + public SuperByteBuffer shiftUVScrolling(SpriteShiftEntry entry, float scrollV) { + return this.shiftUVScrolling(entry, 0, scrollV); + } + + public SuperByteBuffer shiftUVScrolling(SpriteShiftEntry entry, float scrollU, float scrollV) { + this.spriteShiftFunc = (builder, u, v) -> { + float targetU = u - entry.getOriginal() + .getU0() + entry.getTarget() + .getU0() + + scrollU; + float targetV = v - entry.getOriginal() + .getV0() + entry.getTarget() + .getV0() + + scrollV; + builder.uv(targetU, targetV); + }; + return this; + } + + public SuperByteBuffer shiftUVtoSheet(SpriteShiftEntry entry, float uTarget, float vTarget, int sheetSize) { + this.spriteShiftFunc = (builder, u, v) -> { + float targetU = entry.getTarget() + .getU((SpriteShiftEntry.getUnInterpolatedU(entry.getOriginal(), u) / sheetSize) + uTarget * 16); + float targetV = entry.getTarget() + .getV((SpriteShiftEntry.getUnInterpolatedV(entry.getOriginal(), v) / sheetSize) + vTarget * 16); + builder.uv(targetU, targetV); + }; + return this; + } + + public SuperByteBuffer overlay() { + hasOverlay = true; + return this; + } + + public SuperByteBuffer overlay(int overlay) { + hasOverlay = true; + this.overlay = overlay; + return this; + } + + public SuperByteBuffer light() { + useWorldLight = true; + return this; + } + + public SuperByteBuffer light(Matrix4f lightTransform) { + useWorldLight = true; + this.lightTransform = lightTransform; + return this; + } + + public SuperByteBuffer light(int packedLightCoords) { + hasCustomLight = true; + this.packedLightCoords = packedLightCoords; + return this; + } + + public SuperByteBuffer light(Matrix4f lightTransform, int packedLightCoords) { + light(lightTransform); + light(packedLightCoords); + return this; + } + + /** + * Uses max light from calculated light (world light or custom light) and vertex + * light for the final light value. Ineffective if any other light method was + * not called. + */ + public SuperByteBuffer hybridLight() { + hybridLight = true; + return this; + } + + /** + * Transforms normals not only by the local matrix stack, but also by the passed + * matrix stack. + */ + public SuperByteBuffer fullNormalTransform() { + fullNormalTransform = true; + return this; + } + + public SuperByteBuffer forEntityRender() { + disableDiffuse(); + overlay(); + fullNormalTransform(); + return this; + } + + public static int transformColor(byte component, float scale) { + return Mth.clamp((int) (Byte.toUnsignedInt(component) * scale), 0, 255); + } + + public static int transformColor(int component, float scale) { + return Mth.clamp((int) (component * scale), 0, 255); + } + + public static int maxLight(int packedLight1, int packedLight2) { + int blockLight1 = LightTexture.block(packedLight1); + int skyLight1 = LightTexture.sky(packedLight1); + int blockLight2 = LightTexture.block(packedLight2); + int skyLight2 = LightTexture.sky(packedLight2); + return LightTexture.pack(Math.max(blockLight1, blockLight2), Math.max(skyLight1, skyLight2)); + } + + private static int getLight(Level world, Vector4f lightPos) { + BlockPos pos = new BlockPos(lightPos.x(), lightPos.y(), lightPos.z()); + return WORLD_LIGHT_CACHE.computeIfAbsent(pos.asLong(), $ -> LevelRenderer.getLightColor(world, pos)); + } + + @FunctionalInterface + public interface SpriteShiftFunc { + void shift(VertexConsumer builder, float u, float v); + } + + @FunctionalInterface + public interface VertexLighter { + int getPackedLight(float x, float y, float z); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/SuperByteBufferCache.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/SuperByteBufferCache.java new file mode 100644 index 0000000..00b4052 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/SuperByteBufferCache.java @@ -0,0 +1,54 @@ +package nl.requios.effortlessbuilding.create.foundation.render; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +public class SuperByteBufferCache { + + protected final Map, Cache> caches = new HashMap<>(); + + public synchronized void registerCompartment(Compartment compartment) { + caches.put(compartment, CacheBuilder.newBuilder() + .build()); + } + + public synchronized void registerCompartment(Compartment compartment, long ticksUntilExpired) { + caches.put(compartment, CacheBuilder.newBuilder() + .expireAfterAccess(ticksUntilExpired * 50, TimeUnit.MILLISECONDS) + .build()); + } + + public SuperByteBuffer get(Compartment compartment, T key, Callable callable) { + Cache cache = caches.get(compartment); + if (cache != null) { + try { + return cache.get(key, callable); + } catch (ExecutionException e) { + e.printStackTrace(); + } + } + return null; + } + + public void invalidate(Compartment compartment, T key) { + caches.get(compartment).invalidate(key); + } + + public void invalidate(Compartment compartment) { + caches.get(compartment).invalidateAll(); + } + + public void invalidate() { + caches.forEach((compartment, cache) -> cache.invalidateAll()); + } + + public static class Compartment { + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/SuperRenderTypeBuffer.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/SuperRenderTypeBuffer.java new file mode 100644 index 0000000..b586c4a --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/SuperRenderTypeBuffer.java @@ -0,0 +1,94 @@ +package nl.requios.effortlessbuilding.create.foundation.render; + +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.VertexConsumer; +import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; +import net.minecraft.Util; +import net.minecraft.client.renderer.ChunkBufferBuilderPack; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.Sheets; +import net.minecraft.client.resources.model.ModelBakery; + +import java.util.SortedMap; + +public class SuperRenderTypeBuffer implements MultiBufferSource { + + private static final SuperRenderTypeBuffer INSTANCE = new SuperRenderTypeBuffer(); + + public static SuperRenderTypeBuffer getInstance() { + return INSTANCE; + } + + private SuperRenderTypeBufferPhase earlyBuffer; + private SuperRenderTypeBufferPhase defaultBuffer; + private SuperRenderTypeBufferPhase lateBuffer; + + public SuperRenderTypeBuffer() { + earlyBuffer = new SuperRenderTypeBufferPhase(); + defaultBuffer = new SuperRenderTypeBufferPhase(); + lateBuffer = new SuperRenderTypeBufferPhase(); + } + + public VertexConsumer getEarlyBuffer(RenderType type) { + return earlyBuffer.bufferSource.getBuffer(type); + } + + @Override + public VertexConsumer getBuffer(RenderType type) { + return defaultBuffer.bufferSource.getBuffer(type); + } + + public VertexConsumer getLateBuffer(RenderType type) { + return lateBuffer.bufferSource.getBuffer(type); + } + + public void draw() { + earlyBuffer.bufferSource.endBatch(); + defaultBuffer.bufferSource.endBatch(); + lateBuffer.bufferSource.endBatch(); + } + + public void draw(RenderType type) { + earlyBuffer.bufferSource.endBatch(type); + defaultBuffer.bufferSource.endBatch(type); + lateBuffer.bufferSource.endBatch(type); + } + + private static class SuperRenderTypeBufferPhase { + + // Visible clones from RenderBuffers + private final ChunkBufferBuilderPack fixedBufferPack = new ChunkBufferBuilderPack(); + private final SortedMap fixedBuffers = Util.make(new Object2ObjectLinkedOpenHashMap<>(), map -> { + map.put(Sheets.solidBlockSheet(), fixedBufferPack.builder(RenderType.solid())); + map.put(Sheets.cutoutBlockSheet(), fixedBufferPack.builder(RenderType.cutout())); + map.put(Sheets.bannerSheet(), fixedBufferPack.builder(RenderType.cutoutMipped())); + map.put(Sheets.translucentCullBlockSheet(), fixedBufferPack.builder(RenderType.translucent())); + put(map, Sheets.shieldSheet()); + put(map, Sheets.bedSheet()); + put(map, Sheets.shulkerBoxSheet()); + put(map, Sheets.signSheet()); + put(map, Sheets.chestSheet()); + put(map, RenderType.translucentNoCrumbling()); + put(map, RenderType.armorGlint()); + put(map, RenderType.armorEntityGlint()); + put(map, RenderType.glint()); + put(map, RenderType.glintDirect()); + put(map, RenderType.glintTranslucent()); + put(map, RenderType.entityGlint()); + put(map, RenderType.entityGlintDirect()); + put(map, RenderType.waterMask()); + put(map, RenderTypes.getOutlineSolid()); + ModelBakery.DESTROY_TYPES.forEach((p_173062_) -> { + put(map, p_173062_); + }); + }); + private final BufferSource bufferSource = MultiBufferSource.immediateWithBuffers(fixedBuffers, new BufferBuilder(256)); + + private static void put(Object2ObjectLinkedOpenHashMap map, RenderType type) { + map.put(type, new BufferBuilder(type.bufferSize())); + } + + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/TileEntityRenderHelper.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/TileEntityRenderHelper.java new file mode 100644 index 0000000..a3a9bcb --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/render/TileEntityRenderHelper.java @@ -0,0 +1,113 @@ +package nl.requios.effortlessbuilding.create.foundation.render; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; +import com.jozufozu.flywheel.config.BackendType; +import com.jozufozu.flywheel.core.virtual.VirtualRenderWorld; +import com.jozufozu.flywheel.util.transform.TransformStack; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Matrix4f; +import com.mojang.math.Vector4f; +import nl.requios.effortlessbuilding.create.Create; +import nl.requios.effortlessbuilding.create.foundation.utility.AnimationTickHolder; +import nl.requios.effortlessbuilding.create.foundation.utility.RegisteredObjects; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; + +import javax.annotation.Nullable; +import java.util.Iterator; + +public class TileEntityRenderHelper { + + public static void renderTileEntities(Level world, Iterable customRenderTEs, PoseStack ms, + MultiBufferSource buffer) { + renderTileEntities(world, null, customRenderTEs, ms, null, buffer); + } + + public static void renderTileEntities(Level world, Iterable customRenderTEs, PoseStack ms, + MultiBufferSource buffer, float pt) { + renderTileEntities(world, null, customRenderTEs, ms, null, buffer, pt); + } + + public static void renderTileEntities(Level world, @Nullable VirtualRenderWorld renderWorld, + Iterable customRenderTEs, PoseStack ms, @Nullable Matrix4f lightTransform, MultiBufferSource buffer) { + renderTileEntities(world, renderWorld, customRenderTEs, ms, lightTransform, buffer, + AnimationTickHolder.getPartialTicks()); + } + + public static void renderTileEntities(Level world, @Nullable VirtualRenderWorld renderWorld, + Iterable customRenderTEs, PoseStack ms, @Nullable Matrix4f lightTransform, MultiBufferSource buffer, + float pt) { + Iterator iterator = customRenderTEs.iterator(); + while (iterator.hasNext()) { + BlockEntity tileEntity = iterator.next(); + if (Backend.getBackendType() == BackendType.INSTANCING && Backend.isFlywheelWorld(renderWorld) && InstancedRenderRegistry.shouldSkipRender(tileEntity)) + continue; + + BlockEntityRenderer renderer = Minecraft.getInstance().getBlockEntityRenderDispatcher().getRenderer(tileEntity); + if (renderer == null) { + iterator.remove(); + continue; + } + + BlockPos pos = tileEntity.getBlockPos(); + ms.pushPose(); + TransformStack.cast(ms) + .translate(pos); + + try { + int worldLight = getCombinedLight(world, getLightPos(lightTransform, pos), renderWorld, pos); + + if (renderWorld != null) { + // Swap the real world for the render world so that the renderer gets contraption-local information + tileEntity.setLevel(renderWorld); + renderer.render(tileEntity, pt, ms, buffer, worldLight, OverlayTexture.NO_OVERLAY); + tileEntity.setLevel(world); + } else { + renderer.render(tileEntity, pt, ms, buffer, worldLight, OverlayTexture.NO_OVERLAY); + } + + } catch (Exception e) { + iterator.remove(); + + String message = "BlockEntity " + RegisteredObjects.getKeyOrThrow(tileEntity.getType()) + .toString() + " could not be rendered virtually."; +// if (AllConfigs.CLIENT.explainRenderErrors.get()) + Create.LOGGER.error(message, e); +// else +// Create.LOGGER.error(message); + } + + ms.popPose(); + } + } + + private static BlockPos getLightPos(@Nullable Matrix4f lightTransform, BlockPos contraptionPos) { + if (lightTransform != null) { + Vector4f lightVec = new Vector4f(contraptionPos.getX() + .5f, contraptionPos.getY() + .5f, contraptionPos.getZ() + .5f, 1); + lightVec.transform(lightTransform); + return new BlockPos(lightVec.x(), lightVec.y(), lightVec.z()); + } else { + return contraptionPos; + } + } + + public static int getCombinedLight(Level world, BlockPos worldPos, @Nullable VirtualRenderWorld renderWorld, + BlockPos renderWorldPos) { + int worldLight = LevelRenderer.getLightColor(world, worldPos); + + if (renderWorld != null) { + int renderWorldLight = LevelRenderer.getLightColor(renderWorld, renderWorldPos); + return SuperByteBuffer.maxLight(worldLight, renderWorldLight); + } + + return worldLight; + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/AbstractBlockBreakQueue.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/AbstractBlockBreakQueue.java new file mode 100644 index 0000000..5408b96 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/AbstractBlockBreakQueue.java @@ -0,0 +1,36 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraftforge.event.ForgeEventFactory; + +import javax.annotation.Nullable; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public abstract class AbstractBlockBreakQueue { + protected Consumer makeCallbackFor(Level world, float effectChance, ItemStack toDamage, + @Nullable Player playerEntity, BiConsumer drop) { + return pos -> { + ItemStack usedTool = toDamage.copy(); + BlockHelper.destroyBlockAs(world, pos, playerEntity, toDamage, effectChance, + stack -> drop.accept(pos, stack)); + if (toDamage.isEmpty() && !usedTool.isEmpty()) + ForgeEventFactory.onPlayerDestroyItem(playerEntity, usedTool, InteractionHand.MAIN_HAND); + }; + } + + public void destroyBlocks(Level world, @Nullable LivingEntity entity, BiConsumer drop) { + Player playerEntity = entity instanceof Player ? ((Player) entity) : null; + ItemStack toDamage = + playerEntity != null && !playerEntity.isCreative() ? playerEntity.getMainHandItem() : ItemStack.EMPTY; + destroyBlocks(world, toDamage, playerEntity, drop); + } + + public abstract void destroyBlocks(Level world, ItemStack toDamage, @Nullable Player playerEntity, + BiConsumer drop); +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/AngleHelper.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/AngleHelper.java new file mode 100644 index 0000000..3855074 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/AngleHelper.java @@ -0,0 +1,52 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.core.Direction; +import net.minecraft.core.Direction.Axis; +import net.minecraft.util.Mth; + +public class AngleHelper { + + public static float horizontalAngle(Direction facing) { + if (facing.getAxis().isVertical()) + return 0; + float angle = facing.toYRot(); + if (facing.getAxis() == Axis.X) + angle = -angle; + return angle; + } + + public static float verticalAngle(Direction facing) { + return facing == Direction.UP ? -90 : facing == Direction.DOWN ? 90 : 0; + } + + public static float rad(double angle) { + if (angle == 0) + return 0; + return (float) (angle / 180 * Math.PI); + } + + public static float deg(double angle) { + if (angle == 0) + return 0; + return (float) (angle * 180 / Math.PI); + } + + public static float angleLerp(double pct, double current, double target) { + return (float) (current + getShortestAngleDiff(current, target) * pct); + } + + public static float getShortestAngleDiff(double current, double target) { + current = current % 360; + target = target % 360; + return (float) (((((target - current) % 360) + 540) % 360) - 180); + } + + public static float getShortestAngleDiff(double current, double target, float hint) { + float diff = getShortestAngleDiff(current, target); + if (Mth.equal(Math.abs(diff), 180) && Math.signum(diff) != Math.signum(hint)) { + return diff + 360*Math.signum(hint); + } + return diff; + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/AnimationTickHolder.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/AnimationTickHolder.java new file mode 100644 index 0000000..6269bad --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/AnimationTickHolder.java @@ -0,0 +1,56 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import nl.requios.effortlessbuilding.create.foundation.utility.worldWrappers.WrappedClientWorld; +import net.minecraft.client.Minecraft; +import net.minecraft.world.level.LevelAccessor; + +public class AnimationTickHolder { + + private static int ticks; + private static int pausedTicks; + + public static void reset() { + ticks = 0; + pausedTicks = 0; + } + + public static void tick() { + if (!Minecraft.getInstance() + .isPaused()) { + ticks = (ticks + 1) % 1_728_000; // wrap around every 24 hours so we maintain enough floating point precision + } else { + pausedTicks = (pausedTicks + 1) % 1_728_000; + } + } + + public static int getTicks() { + return getTicks(false); + } + + public static int getTicks(boolean includePaused) { + return includePaused ? ticks + pausedTicks : ticks; + } + + public static float getRenderTime() { + return getTicks() + getPartialTicks(); + } + + public static float getPartialTicks() { + Minecraft mc = Minecraft.getInstance(); + return (mc.isPaused() ? mc.pausePartialTick : mc.getFrameTime()); + } + + public static int getTicks(LevelAccessor world) { + if (world instanceof WrappedClientWorld) + return getTicks(((WrappedClientWorld) world).getWrappedWorld()); + return getTicks(); + } + + public static float getRenderTime(LevelAccessor world) { + return getTicks(world) + getPartialTicks(world); + } + + public static float getPartialTicks(LevelAccessor world) { + return getPartialTicks(); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/BBHelper.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/BBHelper.java new file mode 100644 index 0000000..7b9ee4a --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/BBHelper.java @@ -0,0 +1,20 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.levelgen.structure.BoundingBox; + +public class BBHelper { + + public static BoundingBox encapsulate(BoundingBox bb, BlockPos pos) { + return new BoundingBox(Math.min(bb.minX(), pos.getX()), Math.min(bb.minY(), pos.getY()), + Math.min(bb.minZ(), pos.getZ()), Math.max(bb.maxX(), pos.getX()), Math.max(bb.maxY(), pos.getY()), + Math.max(bb.maxZ(), pos.getZ())); + } + + public static BoundingBox encapsulate(BoundingBox bb, BoundingBox bb2) { + return new BoundingBox(Math.min(bb.minX(), bb2.minX()), Math.min(bb.minY(), bb2.minY()), + Math.min(bb.minZ(), bb2.minZ()), Math.max(bb.maxX(), bb2.maxX()), Math.max(bb.maxY(), bb2.maxY()), + Math.max(bb.maxZ(), bb2.maxZ())); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/BlockFace.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/BlockFace.java new file mode 100644 index 0000000..925af11 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/BlockFace.java @@ -0,0 +1,52 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; + +public class BlockFace extends Pair { + + public BlockFace(BlockPos first, Direction second) { + super(first, second); + } + + public boolean isEquivalent(BlockFace other) { + if (equals(other)) + return true; + return getConnectedPos().equals(other.getPos()) && getPos().equals(other.getConnectedPos()); + } + + public BlockPos getPos() { + return getFirst(); + } + + public Direction getFace() { + return getSecond(); + } + + public Direction getOppositeFace() { + return getSecond().getOpposite(); + } + + public BlockFace getOpposite() { + return new BlockFace(getConnectedPos(), getOppositeFace()); + } + + public BlockPos getConnectedPos() { + return getPos().relative(getFace()); + } + + public CompoundTag serializeNBT() { + CompoundTag compoundNBT = new CompoundTag(); + compoundNBT.put("Pos", NbtUtils.writeBlockPos(getPos())); + NBTHelper.writeEnum(compoundNBT, "Face", getFace()); + return compoundNBT; + } + + public static BlockFace fromNBT(CompoundTag compound) { + return new BlockFace(NbtUtils.readBlockPos(compound.getCompound("Pos")), + NBTHelper.readEnum(compound, "Face", Direction.class)); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/BlockHelper.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/BlockHelper.java new file mode 100644 index 0000000..11e130f --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/BlockHelper.java @@ -0,0 +1,337 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Registry; +import net.minecraft.core.SectionPos; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.stats.Stats; +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.FluidTags; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.GameRules; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.Property; +import net.minecraft.world.level.block.state.properties.SlabType; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Material; +import net.minecraftforge.common.IPlantable; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.level.BlockEvent; + +import javax.annotation.Nullable; +import java.util.function.Consumer; + +public class BlockHelper { + + public static BlockState setZeroAge(BlockState blockState) { + if (blockState.hasProperty(BlockStateProperties.AGE_1)) + return blockState.setValue(BlockStateProperties.AGE_1, 0); + if (blockState.hasProperty(BlockStateProperties.AGE_2)) + return blockState.setValue(BlockStateProperties.AGE_2, 0); + if (blockState.hasProperty(BlockStateProperties.AGE_3)) + return blockState.setValue(BlockStateProperties.AGE_3, 0); + if (blockState.hasProperty(BlockStateProperties.AGE_5)) + return blockState.setValue(BlockStateProperties.AGE_5, 0); + if (blockState.hasProperty(BlockStateProperties.AGE_7)) + return blockState.setValue(BlockStateProperties.AGE_7, 0); + if (blockState.hasProperty(BlockStateProperties.AGE_15)) + return blockState.setValue(BlockStateProperties.AGE_15, 0); + if (blockState.hasProperty(BlockStateProperties.AGE_25)) + return blockState.setValue(BlockStateProperties.AGE_25, 0); + if (blockState.hasProperty(BlockStateProperties.LEVEL_HONEY)) + return blockState.setValue(BlockStateProperties.LEVEL_HONEY, 0); + if (blockState.hasProperty(BlockStateProperties.HATCH)) + return blockState.setValue(BlockStateProperties.HATCH, 0); + if (blockState.hasProperty(BlockStateProperties.STAGE)) + return blockState.setValue(BlockStateProperties.STAGE, 0); + if (blockState.is(BlockTags.CAULDRONS)) + return Blocks.CAULDRON.defaultBlockState(); + if (blockState.hasProperty(BlockStateProperties.LEVEL_COMPOSTER)) + return blockState.setValue(BlockStateProperties.LEVEL_COMPOSTER, 0); + if (blockState.hasProperty(BlockStateProperties.EXTENDED)) + return blockState.setValue(BlockStateProperties.EXTENDED, false); + return blockState; + } + + public static int findAndRemoveInInventory(BlockState block, Player player, int amount) { + int amountFound = 0; + Item required = getRequiredItem(block).getItem(); + + boolean needsTwo = block.hasProperty(BlockStateProperties.SLAB_TYPE) + && block.getValue(BlockStateProperties.SLAB_TYPE) == SlabType.DOUBLE; + + if (needsTwo) + amount *= 2; + + if (block.hasProperty(BlockStateProperties.EGGS)) + amount *= block.getValue(BlockStateProperties.EGGS); + + if (block.hasProperty(BlockStateProperties.PICKLES)) + amount *= block.getValue(BlockStateProperties.PICKLES); + + { + // Try held Item first + int preferredSlot = player.getInventory().selected; + ItemStack itemstack = player.getInventory() + .getItem(preferredSlot); + int count = itemstack.getCount(); + if (itemstack.getItem() == required && count > 0) { + int taken = Math.min(count, amount - amountFound); + player.getInventory() + .setItem(preferredSlot, new ItemStack(itemstack.getItem(), count - taken)); + amountFound += taken; + } + } + + // Search inventory + for (int i = 0; i < player.getInventory() + .getContainerSize(); ++i) { + if (amountFound == amount) + break; + + ItemStack itemstack = player.getInventory() + .getItem(i); + int count = itemstack.getCount(); + if (itemstack.getItem() == required && count > 0) { + int taken = Math.min(count, amount - amountFound); + player.getInventory() + .setItem(i, new ItemStack(itemstack.getItem(), count - taken)); + amountFound += taken; + } + } + + if (needsTwo) { + // Give back 1 if uneven amount was removed + if (amountFound % 2 != 0) + player.getInventory() + .add(new ItemStack(required)); + amountFound /= 2; + } + + return amountFound; + } + + public static ItemStack getRequiredItem(BlockState state) { + ItemStack itemStack = new ItemStack(state.getBlock()); + Item item = itemStack.getItem(); + if (item == Items.FARMLAND || item == Items.DIRT_PATH) + itemStack = new ItemStack(Items.DIRT); + return itemStack; + } + + public static void destroyBlock(Level world, BlockPos pos, float effectChance) { + destroyBlock(world, pos, effectChance, stack -> Block.popResource(world, pos, stack)); + } + + public static void destroyBlock(Level world, BlockPos pos, float effectChance, + Consumer droppedItemCallback) { + destroyBlockAs(world, pos, null, ItemStack.EMPTY, effectChance, droppedItemCallback); + } + + public static void destroyBlockAs(Level world, BlockPos pos, @Nullable Player player, ItemStack usedTool, + float effectChance, Consumer droppedItemCallback) { + FluidState fluidState = world.getFluidState(pos); + BlockState state = world.getBlockState(pos); + + if (world.random.nextFloat() < effectChance) + world.levelEvent(2001, pos, Block.getId(state)); + BlockEntity tileentity = state.hasBlockEntity() ? world.getBlockEntity(pos) : null; + + if (player != null) { + BlockEvent.BreakEvent event = new BlockEvent.BreakEvent(world, pos, state, player); + MinecraftForge.EVENT_BUS.post(event); + if (event.isCanceled()) + return; + + if (event.getExpToDrop() > 0 && world instanceof ServerLevel) + state.getBlock() + .popExperience((ServerLevel) world, pos, event.getExpToDrop()); + + usedTool.mineBlock(world, state, pos, player); + player.awardStat(Stats.BLOCK_MINED.get(state.getBlock())); + } + + if (world instanceof ServerLevel && world.getGameRules() + .getBoolean(GameRules.RULE_DOBLOCKDROPS) && !world.restoringBlockSnapshots + && (player == null || !player.isCreative())) { + for (ItemStack itemStack : Block.getDrops(state, (ServerLevel) world, pos, tileentity, player, usedTool)) + droppedItemCallback.accept(itemStack); + + // Simulating IceBlock#playerDestroy. Not calling method directly as it would drop item + // entities as a side-effect + if (state.getBlock() instanceof IceBlock && usedTool.getEnchantmentLevel(Enchantments.SILK_TOUCH) == 0) { + if (world.dimensionType() + .ultraWarm()) + return; + + Material material = world.getBlockState(pos.below()) + .getMaterial(); + if (material.blocksMotion() || material.isLiquid()) + world.setBlockAndUpdate(pos, Blocks.WATER.defaultBlockState()); + return; + } + + state.spawnAfterBreak((ServerLevel) world, pos, ItemStack.EMPTY, true); + } + + world.setBlockAndUpdate(pos, fluidState.createLegacyBlock()); + } + + public static boolean isSolidWall(BlockGetter reader, BlockPos fromPos, Direction toDirection) { + return hasBlockSolidSide(reader.getBlockState(fromPos.relative(toDirection)), reader, + fromPos.relative(toDirection), toDirection.getOpposite()); + } + + public static boolean noCollisionInSpace(BlockGetter reader, BlockPos pos) { + return reader.getBlockState(pos) + .getCollisionShape(reader, pos) + .isEmpty(); + } + + private static void placeRailWithoutUpdate(Level world, BlockState state, BlockPos target) { + LevelChunk chunk = world.getChunkAt(target); + int idx = chunk.getSectionIndex(target.getY()); + LevelChunkSection chunksection = chunk.getSection(idx); + if (chunksection == null) { + chunksection = new LevelChunkSection(chunk.getSectionYFromSectionIndex(idx), world.registryAccess() + .registryOrThrow(Registry.BIOME_REGISTRY)); + chunk.getSections()[idx] = chunksection; + } + BlockState old = chunksection.setBlockState(SectionPos.sectionRelative(target.getX()), + SectionPos.sectionRelative(target.getY()), SectionPos.sectionRelative(target.getZ()), state); + chunk.setUnsaved(true); + world.markAndNotifyBlock(target, chunk, old, state, 82, 512); + + world.setBlock(target, state, 82); + world.neighborChanged(target, world.getBlockState(target.below()) + .getBlock(), target.below()); + } + + public static void placeSchematicBlock(Level world, BlockState state, BlockPos target, ItemStack stack, + @Nullable CompoundTag data) { + BlockEntity existingTile = world.getBlockEntity(target); + + // Piston + if (state.hasProperty(BlockStateProperties.EXTENDED)) + state = state.setValue(BlockStateProperties.EXTENDED, Boolean.FALSE); + if (state.hasProperty(BlockStateProperties.WATERLOGGED)) + state = state.setValue(BlockStateProperties.WATERLOGGED, Boolean.FALSE); + +// if (AllBlocks.BELT.has(state)) { +// world.setBlock(target, state, 2); +// return; +// } else if (state.getBlock() == Blocks.COMPOSTER) +// state = Blocks.COMPOSTER.defaultBlockState(); +// else if (state.getBlock() != Blocks.SEA_PICKLE && state.getBlock() instanceof IPlantable) +// state = ((IPlantable) state.getBlock()).getPlant(world, target); +// else if (state.is(BlockTags.CAULDRONS)) +// state = Blocks.CAULDRON.defaultBlockState(); + + if (world.dimensionType() + .ultraWarm() && state.getFluidState().is(FluidTags.WATER)) { + int i = target.getX(); + int j = target.getY(); + int k = target.getZ(); + world.playSound(null, target, SoundEvents.FIRE_EXTINGUISH, SoundSource.BLOCKS, 0.5F, + 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F); + + for (int l = 0; l < 8; ++l) { + world.addParticle(ParticleTypes.LARGE_SMOKE, i + Math.random(), j + Math.random(), k + Math.random(), + 0.0D, 0.0D, 0.0D); + } + Block.dropResources(state, world, target); + return; + } + + if (state.getBlock() instanceof BaseRailBlock) { + placeRailWithoutUpdate(world, state, target); + } else { + world.setBlock(target, state, 18); + } + + if (data != null) { +// if (existingTile instanceof IMergeableTE mergeable) { +// BlockEntity loaded = BlockEntity.loadStatic(target, state, data); +// if (existingTile.getType() +// .equals(loaded.getType())) { +// mergeable.accept(loaded); +// return; +// } +// } + BlockEntity tile = world.getBlockEntity(target); + if (tile != null) { + data.putInt("x", target.getX()); + data.putInt("y", target.getY()); + data.putInt("z", target.getZ()); +// if (tile instanceof KineticTileEntity) +// ((KineticTileEntity) tile).warnOfMovement(); + tile.load(data); + } + } + + try { + state.getBlock() + .setPlacedBy(world, target, state, null, stack); + } catch (Exception e) { + } + } + + public static double getBounceMultiplier(Block block) { + if (block instanceof SlimeBlock) + return 0.8D; + if (block instanceof BedBlock) + return 0.66 * 0.8D; + return 0; + } + + public static boolean hasBlockSolidSide(BlockState p_220056_0_, BlockGetter p_220056_1_, BlockPos p_220056_2_, + Direction p_220056_3_) { + return !p_220056_0_.is(BlockTags.LEAVES) + && Block.isFaceFull(p_220056_0_.getCollisionShape(p_220056_1_, p_220056_2_), p_220056_3_); + } + + public static boolean extinguishFire(Level world, @Nullable Player p_175719_1_, BlockPos p_175719_2_, + Direction p_175719_3_) { + p_175719_2_ = p_175719_2_.relative(p_175719_3_); + if (world.getBlockState(p_175719_2_) + .getBlock() == Blocks.FIRE) { + world.levelEvent(p_175719_1_, 1009, p_175719_2_, 0); + world.removeBlock(p_175719_2_, false); + return true; + } else { + return false; + } + } + + public static BlockState copyProperties(BlockState fromState, BlockState toState) { + for (Property property : fromState.getProperties()) { + toState = copyProperty(property, fromState, toState); + } + return toState; + } + + public static > BlockState copyProperty(Property property, BlockState fromState, + BlockState toState) { + if (fromState.hasProperty(property) && toState.hasProperty(property)) { + return toState.setValue(property, fromState.getValue(property)); + } + return toState; + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/CameraAngleAnimationService.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/CameraAngleAnimationService.java new file mode 100644 index 0000000..5148b13 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/CameraAngleAnimationService.java @@ -0,0 +1,90 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import nl.requios.effortlessbuilding.create.foundation.utility.animation.LerpedFloat; +import net.minecraft.client.Minecraft; +import net.minecraft.util.Mth; + +public class CameraAngleAnimationService { + + private static final LerpedFloat yRotation = LerpedFloat.angular().startWithValue(0); + private static final LerpedFloat xRotation = LerpedFloat.angular().startWithValue(0); + + private static Mode animationMode = Mode.LINEAR; + private static float animationSpeed = -1; + + public static void tick() { + + yRotation.tickChaser(); + xRotation.tickChaser(); + + if (Minecraft.getInstance().player != null) { + if (!yRotation.settled()) + Minecraft.getInstance().player.setYRot(yRotation.getValue(1)); + + if (!xRotation.settled()) + Minecraft.getInstance().player.setXRot(xRotation.getValue(1)); + } + } + + public static boolean isYawAnimating() { + return !yRotation.settled(); + } + + public static boolean isPitchAnimating() { + return !xRotation.settled(); + } + + public static float getYaw(float partialTicks) { + return yRotation.getValue(partialTicks); + } + + public static float getPitch(float partialTicks) { + return xRotation.getValue(partialTicks); + } + + public static void setAnimationMode(Mode mode) { + animationMode = mode; + } + + public static void setAnimationSpeed(float speed) { + animationSpeed = speed; + } + + public static void setYawTarget(float yaw) { + float currentYaw = getCurrentYaw(); + yRotation.startWithValue(currentYaw); + setupChaser(yRotation, currentYaw + AngleHelper.getShortestAngleDiff(currentYaw, Mth.wrapDegrees(yaw))); + } + + public static void setPitchTarget(float pitch) { + float currentPitch = getCurrentPitch(); + xRotation.startWithValue(currentPitch); + setupChaser(xRotation, currentPitch + AngleHelper.getShortestAngleDiff(currentPitch, Mth.wrapDegrees(pitch))); + } + + private static float getCurrentYaw() { + if (Minecraft.getInstance().player == null) + return 0; + return Mth.wrapDegrees(Minecraft.getInstance().player.getYRot()); + } + + private static float getCurrentPitch() { + if (Minecraft.getInstance().player == null) + return 0; + + return Mth.wrapDegrees(Minecraft.getInstance().player.getXRot()); + } + + private static void setupChaser(LerpedFloat rotation, float target) { + if (animationMode == Mode.LINEAR) { + rotation.chase(target, animationSpeed > 0 ? animationSpeed : 2, LerpedFloat.Chaser.LINEAR); + } else if (animationMode == Mode.EXPONENTIAL) { + rotation.chase(target, animationSpeed > 0 ? animationSpeed : 0.25, LerpedFloat.Chaser.EXP); + } + } + + public enum Mode { + LINEAR, + EXPONENTIAL + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Color.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Color.java new file mode 100644 index 0000000..ed4a220 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Color.java @@ -0,0 +1,310 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import com.google.common.hash.Hashing; +import com.mojang.math.Vector3f; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; + +import javax.annotation.Nonnull; +import java.util.function.UnaryOperator; + +public class Color { + public final static Color TRANSPARENT_BLACK = new Color(0, 0, 0, 0).setImmutable(); + public final static Color BLACK = new Color(0, 0, 0).setImmutable(); + public final static Color WHITE = new Color(255, 255, 255).setImmutable(); + public final static Color RED = new Color(255, 0, 0).setImmutable(); + public final static Color GREEN = new Color(0, 255, 0).setImmutable(); + public final static Color SPRING_GREEN = new Color(0, 255, 187).setImmutable(); + + protected boolean mutable = true; + protected int value; + + public Color(int r, int g, int b) { + this(r, g, b, 0xff); + } + + public Color(int r, int g, int b, int a) { + value = ((a & 0xff) << 24) | + ((r & 0xff) << 16) | + ((g & 0xff) << 8) | + ((b & 0xff) << 0); + } + + public Color(float r, float g, float b, float a) { + this( + (int) (0.5 + 0xff * Mth.clamp(r, 0, 1)), + (int) (0.5 + 0xff * Mth.clamp(g, 0, 1)), + (int) (0.5 + 0xff * Mth.clamp(b, 0, 1)), + (int) (0.5 + 0xff * Mth.clamp(a, 0, 1)) + ); + } + + public Color(int rgba) { + value = rgba; + } + + public Color(int rgb, boolean hasAlpha) { + if (hasAlpha) { + value = rgb; + } else { + value = rgb | 0xff_000000; + } + } + + public Color copy() { + return copy(true); + } + + public Color copy(boolean mutable) { + if (mutable) + return new Color(value); + else + return new Color(value).setImmutable(); + } + + /** + * Mark this color as immutable. Attempting to mutate this color in the future + * will instead cause a copy to be created that can me modified. + */ + public Color setImmutable() { + this.mutable = false; + return this; + } + + /** + * @return the red component in the range 0-255. + * @see #getRGB + */ + public int getRed() { + return (getRGB() >> 16) & 0xff; + } + + /** + * @return the green component in the range 0-255. + * @see #getRGB + */ + public int getGreen() { + return (getRGB() >> 8) & 0xff; + } + + /** + * @return the blue component in the range 0-255. + * @see #getRGB + */ + public int getBlue() { + return (getRGB() >> 0) & 0xff; + } + + /** + * @return the alpha component in the range 0-255. + * @see #getRGB + */ + public int getAlpha() { + return (getRGB() >> 24) & 0xff; + } + + /** + * @return the red component in the range 0-1f. + */ + public float getRedAsFloat() { + return getRed() / 255f; + } + + /** + * @return the green component in the range 0-1f. + */ + public float getGreenAsFloat() { + return getGreen() / 255f; + } + + /** + * @return the blue component in the range 0-1f. + */ + public float getBlueAsFloat() { + return getBlue() / 255f; + } + + /** + * @return the alpha component in the range 0-1f. + */ + public float getAlphaAsFloat() { + return getAlpha() / 255f; + } + + /** + * Returns the RGB value representing this color + * (Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are blue). + * @return the RGB value of the color + */ + public int getRGB() { + return value; + } + + public Vec3 asVector() { + return new Vec3(getRedAsFloat(), getGreenAsFloat(), getBlueAsFloat()); + } + + public Vector3f asVectorF() { + return new Vector3f(getRedAsFloat(), getGreenAsFloat(), getBlueAsFloat()); + } + + public Color setRed(int r) { + return ensureMutable().setRedUnchecked(r); + } + + public Color setGreen(int g) { + return ensureMutable().setGreenUnchecked(g); + } + + public Color setBlue(int b) { + return ensureMutable().setBlueUnchecked(b); + } + + public Color setAlpha(int a) { + return ensureMutable().setAlphaUnchecked(a); + } + + public Color setRed(float r) { + return ensureMutable().setRedUnchecked((int) (0xff * Mth.clamp(r, 0, 1))); + } + + public Color setGreen(float g) { + return ensureMutable().setGreenUnchecked((int) (0xff * Mth.clamp(g, 0, 1))); + } + + public Color setBlue(float b) { + return ensureMutable().setBlueUnchecked((int) (0xff * Mth.clamp(b, 0, 1))); + } + + public Color setAlpha(float a) { + return ensureMutable().setAlphaUnchecked((int) (0xff * Mth.clamp(a, 0, 1))); + } + + public Color scaleAlpha(float factor) { + return ensureMutable().setAlphaUnchecked((int) (getAlpha() * Mth.clamp(factor, 0, 1))); + } + + public Color mixWith(Color other, float weight) { + return ensureMutable() + .setRedUnchecked((int) (getRed() + (other.getRed() - getRed()) * weight)) + .setGreenUnchecked((int) (getGreen() + (other.getGreen() - getGreen()) * weight)) + .setBlueUnchecked((int) (getBlue() + (other.getBlue() - getBlue()) * weight)) + .setAlphaUnchecked((int) (getAlpha() + (other.getAlpha() - getAlpha()) * weight)); + } + + public Color darker() { + int a = getAlpha(); + return ensureMutable().mixWith(BLACK, .25f).setAlphaUnchecked(a); + } + + public Color brighter() { + int a = getAlpha(); + return ensureMutable().mixWith(WHITE, .25f).setAlphaUnchecked(a); + } + + public Color setValue(int value) { + return ensureMutable().setValueUnchecked(value); + } + + public Color modifyValue(UnaryOperator function) { + int newValue = function.apply(value); + if (newValue == value) + return this; + + return ensureMutable().setValueUnchecked(newValue); + } + + // ********* // + + protected Color ensureMutable() { + if (this.mutable) + return this; + + return new Color(this.value); + } + + protected Color setRedUnchecked(int r) { + this.value = (this.value & 0xff_00ffff) | ((r & 0xff) << 16); + return this; + } + + protected Color setGreenUnchecked(int g) { + this.value = (this.value & 0xff_ff00ff) | ((g & 0xff) << 8); + return this; + } + + protected Color setBlueUnchecked(int b) { + this.value = (this.value & 0xff_ffff00) | ((b & 0xff) << 0); + return this; + } + + protected Color setAlphaUnchecked(int a) { + this.value = (this.value & 0x00_ffffff) | ((a & 0xff) << 24); + return this; + } + + protected Color setValueUnchecked(int value) { + this.value = value; + return this; + } + + // ********* // + + public static Color mixColors(@Nonnull Color c1, @Nonnull Color c2, float w) { + return new Color( + (int) (c1.getRed() + (c2.getRed() - c1.getRed()) * w), + (int) (c1.getGreen() + (c2.getGreen() - c1.getGreen()) * w), + (int) (c1.getBlue() + (c2.getBlue() - c1.getBlue()) * w), + (int) (c1.getAlpha() + (c2.getAlpha() - c1.getAlpha()) * w) + ); + } + + public static Color mixColors(@Nonnull Couple colors, float w) { + return mixColors(colors.getFirst(), colors.getSecond(), w); + } + + public static int mixColors(int color1, int color2, float w) { + int a1 = (color1 >> 24); + int r1 = (color1 >> 16) & 0xFF; + int g1 = (color1 >> 8) & 0xFF; + int b1 = color1 & 0xFF; + int a2 = (color2 >> 24); + int r2 = (color2 >> 16) & 0xFF; + int g2 = (color2 >> 8) & 0xFF; + int b2 = color2 & 0xFF; + + return + ((int) (a1 + (a2 - a1) * w) << 24) + + ((int) (r1 + (r2 - r1) * w) << 16) + + ((int) (g1 + (g2 - g1) * w) << 8) + + ((int) (b1 + (b2 - b1) * w) << 0); + } + + public static Color rainbowColor(int timeStep) { + int localTimeStep = Math.abs(timeStep) % 1536; + int timeStepInPhase = localTimeStep % 256; + int phaseBlue = localTimeStep / 256; + int red = colorInPhase(phaseBlue + 4, timeStepInPhase); + int green = colorInPhase(phaseBlue + 2, timeStepInPhase); + int blue = colorInPhase(phaseBlue, timeStepInPhase); + return new Color(red, green, blue); + } + + private static int colorInPhase(int phase, int progress) { + phase = phase % 6; + if (phase <= 1) + return 0; + if (phase == 2) + return progress; + if (phase <= 4) + return 255; + else + return 255 - progress; + } + + public static Color generateFromLong(long l) { + return rainbowColor(Hashing.crc32().hashLong(l).asInt()) + .mixWith(WHITE, 0.5f); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ColorHandlers.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ColorHandlers.java new file mode 100644 index 0000000..cec8e18 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ColorHandlers.java @@ -0,0 +1,26 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.client.color.block.BlockColor; +import net.minecraft.client.color.item.ItemColor; +import net.minecraft.client.renderer.BiomeColors; +import net.minecraft.world.level.GrassColor; +import net.minecraft.world.level.block.RedStoneWireBlock; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; + +public class ColorHandlers { + + public static BlockColor getGrassyBlock() { + return (state, world, pos, layer) -> pos != null && world != null ? BiomeColors.getAverageGrassColor(world, pos) + : GrassColor.get(0.5D, 1.0D); + } + + public static ItemColor getGrassyItem() { + return (stack, layer) -> GrassColor.get(0.5D, 1.0D); + } + + public static BlockColor getRedstonePower() { + return (state, world, pos, layer) -> RedStoneWireBlock + .getColorForPower(pos != null && world != null ? state.getValue(BlockStateProperties.POWER) : 0); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Components.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Components.java new file mode 100644 index 0000000..1f83124 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Components.java @@ -0,0 +1,33 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; + +public final class Components { + private static final Component IMMUTABLE_EMPTY = Component.empty(); + + public static Component immutableEmpty() { + return IMMUTABLE_EMPTY; + } + + /** Use {@link #immutableEmpty()} when possible to prevent creating an extra object. */ + public static MutableComponent empty() { + return Component.empty(); + } + + public static MutableComponent literal(String str) { + return Component.literal(str); + } + + public static MutableComponent translatable(String key) { + return Component.translatable(key); + } + + public static MutableComponent translatable(String key, Object... args) { + return Component.translatable(key, args); + } + + public static MutableComponent keybind(String name) { + return Component.keybind(name); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Couple.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Couple.java new file mode 100644 index 0000000..d04238b --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Couple.java @@ -0,0 +1,151 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import com.google.common.collect.ImmutableList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; + +import java.util.Iterator; +import java.util.List; +import java.util.function.*; +import java.util.stream.Stream; + +public class Couple extends Pair implements Iterable { + + private static final Couple TRUE_AND_FALSE = Couple.create(true, false); + + protected Couple(T first, T second) { + super(first, second); + } + + public static Couple create(T first, T second) { + return new Couple<>(first, second); + } + + public static Couple create(Supplier factory) { + return new Couple<>(factory.get(), factory.get()); + } + + public static Couple createWithContext(Function factory) { + return new Couple<>(factory.apply(true), factory.apply(false)); + } + + public T get(boolean first) { + return first ? getFirst() : getSecond(); + } + + public void set(boolean first, T value) { + if (first) + setFirst(value); + else + setSecond(value); + } + + @Override + public Couple copy() { + return create(first, second); + } + + public Couple map(Function function) { + return Couple.create(function.apply(first), function.apply(second)); + } + + public Couple mapWithContext(BiFunction function) { + return Couple.create(function.apply(first, true), function.apply(second, false)); + } + + public Couple mapWithParams(BiFunction function, Couple values) { + return Couple.create(function.apply(first, values.first), function.apply(second, values.second)); + } + + public Couple mapNotNullWithParam(BiFunction function, R value) { + return Couple.create(first != null ? function.apply(first, value) : null, + second != null ? function.apply(second, value) : null); + } + + public boolean both(Predicate test) { + return test.test(getFirst()) && test.test(getSecond()); + } + + public boolean either(Predicate test) { + return test.test(getFirst()) || test.test(getSecond()); + } + + public void replace(Function function) { + setFirst(function.apply(getFirst())); + setSecond(function.apply(getSecond())); + } + + public void replaceWithContext(BiFunction function) { + replaceWithParams(function, TRUE_AND_FALSE); + } + + public void replaceWithParams(BiFunction function, Couple values) { + setFirst(function.apply(getFirst(), values.getFirst())); + setSecond(function.apply(getSecond(), values.getSecond())); + } + + @Override + public void forEach(Consumer consumer) { + consumer.accept(getFirst()); + consumer.accept(getSecond()); + } + + public void forEachWithContext(BiConsumer consumer) { + forEachWithParams(consumer, TRUE_AND_FALSE); + } + + public void forEachWithParams(BiConsumer function, Couple values) { + function.accept(getFirst(), values.getFirst()); + function.accept(getSecond(), values.getSecond()); + } + + public Couple swap() { + return Couple.create(second, first); + } + + public ListTag serializeEach(Function serializer) { + return NBTHelper.writeCompoundList(ImmutableList.of(first, second), serializer); + } + + public static Couple deserializeEach(ListTag list, Function deserializer) { + List readCompoundList = NBTHelper.readCompoundList(list, deserializer); + return new Couple<>(readCompoundList.get(0), readCompoundList.get(1)); + } + + @Override + public Iterator iterator() { + return new Couplerator<>(this); + } + + public Stream stream() { + return Stream.of(first, second); + } + + private static class Couplerator implements Iterator { + + int state; + private final Couple couple; + + public Couplerator(Couple couple) { + this.couple = couple; + state = 0; + } + + @Override + public boolean hasNext() { + return state != 2; + } + + @Override + public T next() { + state++; + if (state == 1) + return couple.first; + if (state == 2) + return couple.second; + return null; + } + + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/CreateRegistry.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/CreateRegistry.java new file mode 100644 index 0000000..d9545fb --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/CreateRegistry.java @@ -0,0 +1,102 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.registries.IForgeRegistry; +import nl.requios.effortlessbuilding.EffortlessBuilding; +import nl.requios.effortlessbuilding.create.Create; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class CreateRegistry { + private static final List> ALL = new ArrayList<>(); + + protected final IForgeRegistry objectRegistry; + protected final Map locationMap = new HashMap<>(); + protected final Map objectMap = new IdentityHashMap<>(); + protected boolean unwrapped = false; + + public CreateRegistry(IForgeRegistry objectRegistry) { + this.objectRegistry = objectRegistry; + ALL.add(this); + } + + public void register(ResourceLocation location, V value) { + if (!unwrapped) { + locationMap.put(location, value); + } else { + K object = objectRegistry.getValue(location); + if (object != null) { + objectMap.put(object, value); + } else { + Create.LOGGER.warn("Could not get object for location '" + location + "' in CreateRegistry after unwrapping!"); + } + } + } + + public void register(K object, V value) { + if (unwrapped) { + objectMap.put(object, value); + } else { + ResourceLocation location = objectRegistry.getKey(object); + if (location != null) { + locationMap.put(location, value); + } else { + Create.LOGGER.warn("Could not get location of object '" + object + "' in CreateRegistry before unwrapping!"); + } + } + } + + @Nullable + public V get(ResourceLocation location) { + if (!unwrapped) { + return locationMap.get(location); + } else { + K object = objectRegistry.getValue(location); + if (object != null) { + return objectMap.get(object); + } else { + Create.LOGGER.warn("Could not get object for location '" + location + "' in CreateRegistry after unwrapping!"); + return null; + } + } + } + + @Nullable + public V get(K object) { + if (unwrapped) { + return objectMap.get(object); + } else { + ResourceLocation location = objectRegistry.getKey(object); + if (location != null) { + return locationMap.get(location); + } else { + Create.LOGGER.warn("Could not get location of object '" + object + "' in CreateRegistry before unwrapping!"); + return null; + } + } + } + + public boolean isUnwrapped() { + return unwrapped; + } + + protected void unwrap() { + for (Map.Entry entry : locationMap.entrySet()) { + ResourceLocation location = entry.getKey(); + K object = objectRegistry.getValue(location); + if (object != null) { + objectMap.put(object, entry.getValue()); + } else { + Create.LOGGER.warn("Could not get object for location '" + location + "' in CreateRegistry during unwrapping!"); + } + } + unwrapped = true; + } + + public static void unwrapAll() { + for (CreateRegistry registry : ALL) { + registry.unwrap(); + } + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Debug.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Debug.java new file mode 100644 index 0000000..ba00b87 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Debug.java @@ -0,0 +1,67 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import nl.requios.effortlessbuilding.create.Create; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.util.thread.EffectiveSide; + +/** Deprecated so simi doensn't forget to remove debug calls **/ +@OnlyIn(value = Dist.CLIENT) +public class Debug { + + @Deprecated + public static void debugChat(String message) { + if (Minecraft.getInstance().player != null) + Minecraft.getInstance().player.displayClientMessage(Components.literal(message), false); + } + + @Deprecated + public static void debugChatAndShowStack(String message, int depth) { + if (Minecraft.getInstance().player != null) + Minecraft.getInstance().player.displayClientMessage(Components.literal(message).append("@") + .append(debugStack(depth)), false); + } + + @Deprecated + public static void debugMessage(String message) { + if (Minecraft.getInstance().player != null) + Minecraft.getInstance().player.displayClientMessage(Components.literal(message), true); + } + + @Deprecated + public static void log(String message) { + Create.LOGGER.info(message); + } + + @Deprecated + public static String getLogicalSide() { + return EffectiveSide.get() + .isClient() ? "CL" : "SV"; + } + + @Deprecated + public static Component debugStack(int depth) { + StackTraceElement[] stackTraceElements = Thread.currentThread() + .getStackTrace(); + MutableComponent text = Components.literal("[") + .append(Components.literal(getLogicalSide()).withStyle(ChatFormatting.GOLD)) + .append("] "); + for (int i = 1; i < depth + 2 && i < stackTraceElements.length; i++) { + StackTraceElement e = stackTraceElements[i]; + if (e.getClassName() + .equals(Debug.class.getName())) + continue; + text.append(Components.literal(e.getMethodName()).withStyle(ChatFormatting.YELLOW)) + .append(", "); + } + return text.append(Components.literal(" ...").withStyle(ChatFormatting.GRAY)); + } + + @Deprecated + public static void markTemporary() {} + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/DyeHelper.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/DyeHelper.java new file mode 100644 index 0000000..b53b435 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/DyeHelper.java @@ -0,0 +1,75 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import com.google.common.collect.ImmutableMap; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.block.Blocks; + +import java.util.Map; + +public class DyeHelper { + + public static ItemLike getWoolOfDye(DyeColor color) { + switch (color) { + case BLACK: + return Blocks.BLACK_WOOL; + case BLUE: + return Blocks.BLUE_WOOL; + case BROWN: + return Blocks.BROWN_WOOL; + case CYAN: + return Blocks.CYAN_WOOL; + case GRAY: + return Blocks.GRAY_WOOL; + case GREEN: + return Blocks.GREEN_WOOL; + case LIGHT_BLUE: + return Blocks.LIGHT_BLUE_WOOL; + case LIGHT_GRAY: + return Blocks.LIGHT_GRAY_WOOL; + case LIME: + return Blocks.LIME_WOOL; + case MAGENTA: + return Blocks.MAGENTA_WOOL; + case ORANGE: + return Blocks.ORANGE_WOOL; + case PINK: + return Blocks.PINK_WOOL; + case PURPLE: + return Blocks.PURPLE_WOOL; + case RED: + return Blocks.RED_WOOL; + case YELLOW: + return Blocks.YELLOW_WOOL; + case WHITE: + default: + return Blocks.WHITE_WOOL; + } + } + + public static final Map> DYE_TABLE = new ImmutableMap.Builder>() + + // DyeColor, ( Front RGB, Back RGB ) + .put(DyeColor.BLACK, Couple.create(0x45403B, 0x21201F)) + .put(DyeColor.RED, Couple.create(0xB13937, 0x632737)) + .put(DyeColor.GREEN, Couple.create(0x208A46, 0x1D6045)) + .put(DyeColor.BROWN, Couple.create(0xAC855C, 0x68533E)) + + .put(DyeColor.BLUE, Couple.create(0x5391E1, 0x504B90)) + .put(DyeColor.GRAY, Couple.create(0x5D666F, 0x313538)) + .put(DyeColor.LIGHT_GRAY, Couple.create(0x95969B, 0x707070)) + .put(DyeColor.PURPLE, Couple.create(0x9F54AE, 0x63366C)) + + .put(DyeColor.CYAN, Couple.create(0x3EABB4, 0x3C7872)) + .put(DyeColor.PINK, Couple.create(0xD5A8CB, 0xB86B95)) + .put(DyeColor.LIME, Couple.create(0xA3DF55, 0x4FB16F)) + .put(DyeColor.YELLOW, Couple.create(0xE6D756, 0xE9AC29)) + + .put(DyeColor.LIGHT_BLUE, Couple.create(0x69CED2, 0x508AA5)) + .put(DyeColor.ORANGE, Couple.create(0xEE9246, 0xD94927)) + .put(DyeColor.MAGENTA, Couple.create(0xF062B0, 0xC04488)) + .put(DyeColor.WHITE, Couple.create(0xEDEAE5, 0xBBB6B0)) + + .build(); + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/DynamicComponent.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/DynamicComponent.java new file mode 100644 index 0000000..6fc46da --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/DynamicComponent.java @@ -0,0 +1,94 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import nl.requios.effortlessbuilding.create.Create; +import net.minecraft.commands.CommandSource; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentUtils; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; + +public class DynamicComponent { + + private JsonElement rawCustomText; + private Component parsedCustomText; + + public DynamicComponent() {} + + public void displayCustomText(Level level, BlockPos pos, String tagElement) { + if (tagElement == null) + return; + + rawCustomText = getJsonFromString(tagElement); + parsedCustomText = parseCustomText(level, pos, rawCustomText); + } + + public boolean sameAs(String tagElement) { + return isValid() && rawCustomText.equals(getJsonFromString(tagElement)); + } + + public boolean isValid() { + return parsedCustomText != null && rawCustomText != null; + } + + public String resolve() { + return parsedCustomText.getString(); + } + + public MutableComponent get() { + return parsedCustomText == null ? Components.empty() : parsedCustomText.copy(); + } + + public void read(Level level, BlockPos pos, CompoundTag nbt) { + rawCustomText = getJsonFromString(nbt.getString("RawCustomText")); + try { + parsedCustomText = Component.Serializer.fromJson(nbt.getString("CustomText")); + } catch (JsonParseException e) { + parsedCustomText = null; + } + } + + public void write(CompoundTag nbt) { + if (!isValid()) + return; + + nbt.putString("RawCustomText", rawCustomText.toString()); + nbt.putString("CustomText", Component.Serializer.toJson(parsedCustomText)); + } + + public static JsonElement getJsonFromString(String string) { + try { + return JsonParser.parseString(string); + } catch (JsonParseException e) { + return null; + } + } + + public static Component parseCustomText(Level level, BlockPos pos, JsonElement customText) { + if (!(level instanceof ServerLevel serverLevel)) + return null; + try { + return ComponentUtils.updateForEntity(getCommandSource(serverLevel, pos), + Component.Serializer.fromJson(customText), null, 0); + } catch (JsonParseException e) { + return null; + } catch (CommandSyntaxException e) { + return null; + } + } + + public static CommandSourceStack getCommandSource(ServerLevel level, BlockPos pos) { + return new CommandSourceStack(CommandSource.NULL, Vec3.atCenterOf(pos), Vec2.ZERO, level, 2, Create.ID, + Components.literal(Create.ID), level.getServer(), null); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/FilesHelper.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/FilesHelper.java new file mode 100644 index 0000000..8fba34d --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/FilesHelper.java @@ -0,0 +1,101 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import nl.requios.effortlessbuilding.create.Create; +import net.minecraft.nbt.CompoundTag; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; + +public class FilesHelper { + + public static void createFolderIfMissing(String name) { + try { + Files.createDirectories(Paths.get(name)); + } catch (IOException e) { + Create.LOGGER.warn("Could not create Folder: {}", name); + } + } + + public static String findFirstValidFilename(String name, String folderPath, String extension) { + int index = 0; + String filename; + String filepath; + do { + filename = slug(name) + ((index == 0) ? "" : "_" + index) + "." + extension; + index++; + filepath = folderPath + "/" + filename; + } while (Files.exists(Paths.get(filepath))); + return filename; + } + + public static String slug(String name) { + return Lang.asId(name) + .replaceAll("\\W+", "_"); + } + + public static boolean saveTagCompoundAsJson(CompoundTag compound, String path) { + try { + Files.deleteIfExists(Paths.get(path)); + JsonWriter writer = new JsonWriter(Files.newBufferedWriter(Paths.get(path), StandardOpenOption.CREATE)); + writer.setIndent(" "); + Streams.write(JsonParser.parseString(compound.toString()), writer); + writer.close(); + return true; + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + public static boolean saveTagCompoundAsJsonCompact(CompoundTag compound, String path) { + try { + Files.deleteIfExists(Paths.get(path)); + JsonWriter writer = new JsonWriter(Files.newBufferedWriter(Paths.get(path), StandardOpenOption.CREATE)); + Streams.write(JsonParser.parseString(compound.toString()), writer); + writer.close(); + return true; + } catch (IOException e) { + e.printStackTrace(); + } + return false; + + } + + private static JsonElement loadJson(InputStream inputStream) { + try { + JsonReader reader = new JsonReader(new BufferedReader(new InputStreamReader(inputStream))); + reader.setLenient(true); + JsonElement element = Streams.parse(reader); + reader.close(); + inputStream.close(); + return element; + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static JsonElement loadJsonResource(String filepath) { + return loadJson(ClassLoader.getSystemResourceAsStream(filepath)); + } + + public static JsonElement loadJson(String filepath) { + try { + return loadJson(Files.newInputStream(Paths.get(filepath), StandardOpenOption.READ)); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/FluidFormatter.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/FluidFormatter.java new file mode 100644 index 0000000..ec19ea9 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/FluidFormatter.java @@ -0,0 +1,26 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.network.chat.MutableComponent; + +public class FluidFormatter { + + public static String asString(long amount, boolean shorten) { + Couple couple = asComponents(amount, shorten); + return couple.getFirst().getString() + " " + couple.getSecond().getString(); + } + + public static Couple asComponents(long amount, boolean shorten) { + if (shorten && amount >= 1000) { + return Couple.create( + Components.literal(String.format("%.1f" , amount / 1000d)), + Lang.translateDirect("generic.unit.buckets") + ); + } + + return Couple.create( + Components.literal(String.valueOf(amount)), + Lang.translateDirect("generic.unit.millibuckets") + ); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/FontHelper.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/FontHelper.java new file mode 100644 index 0000000..092c995 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/FontHelper.java @@ -0,0 +1,87 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.Tesselator; +import com.mojang.math.Matrix4f; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; + +import java.text.BreakIterator; +import java.util.LinkedList; +import java.util.List; + +public final class FontHelper { + + private FontHelper() {} + + public static List cutString(Font font, String text, int maxWidthPerLine) { + // Split words + List words = new LinkedList<>(); + BreakIterator iterator = BreakIterator.getLineInstance(Minecraft.getInstance().getLocale()); + iterator.setText(text); + int start = iterator.first(); + for (int end = iterator.next(); end != BreakIterator.DONE; start = end, end = iterator.next()) { + String word = text.substring(start, end); + words.add(word); + } + // Apply hard wrap + List lines = new LinkedList<>(); + StringBuilder currentLine = new StringBuilder(); + int width = 0; + for (String word : words) { + int newWidth = font.width(word); + if (width + newWidth > maxWidthPerLine) { + if (width > 0) { + String line = currentLine.toString(); + lines.add(line); + currentLine = new StringBuilder(); + width = 0; + } else { + lines.add(word); + continue; + } + } + currentLine.append(word); + width += newWidth; + } + if (width > 0) { + lines.add(currentLine.toString()); + } + return lines; + } + + public static void drawSplitString(PoseStack ms, Font font, String text, int x, int y, int width, + int color) { + List list = cutString(font, text, width); + Matrix4f matrix4f = ms.last() + .pose(); + + for (String s : list) { + float f = (float) x; + if (font.isBidirectional()) { + int i = font.width(font.bidirectionalShaping(s)); + f += (float) (width - i); + } + + draw(font, s, f, (float) y, color, matrix4f, false); + y += 9; + } + } + + private static int draw(Font font, String p_228078_1_, float p_228078_2_, float p_228078_3_, + int p_228078_4_, Matrix4f p_228078_5_, boolean p_228078_6_) { + if (p_228078_1_ == null) { + return 0; + } else { + MultiBufferSource.BufferSource irendertypebuffer$impl = MultiBufferSource.immediate(Tesselator.getInstance() + .getBuilder()); + int i = font.drawInBatch(p_228078_1_, p_228078_2_, p_228078_3_, p_228078_4_, p_228078_6_, p_228078_5_, + irendertypebuffer$impl, false, 0, LightTexture.FULL_BRIGHT); + irendertypebuffer$impl.endBatch(); + return i; + } + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ICoordinate.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ICoordinate.java new file mode 100644 index 0000000..1f466d6 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ICoordinate.java @@ -0,0 +1,8 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.core.BlockPos; + +@FunctionalInterface +public interface ICoordinate { + float get(BlockPos from); +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/IInteractionChecker.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/IInteractionChecker.java new file mode 100644 index 0000000..0dc13c9 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/IInteractionChecker.java @@ -0,0 +1,7 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.world.entity.player.Player; + +public interface IInteractionChecker { + boolean canPlayerUse(Player player); +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/IPartialSafeNBT.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/IPartialSafeNBT.java new file mode 100644 index 0000000..dfe8a01 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/IPartialSafeNBT.java @@ -0,0 +1,8 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.nbt.CompoundTag; + +public interface IPartialSafeNBT { + /** This method always runs on the logical server. */ + public void writeSafe(CompoundTag compound); +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/IntAttached.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/IntAttached.java new file mode 100644 index 0000000..3d9e28e --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/IntAttached.java @@ -0,0 +1,61 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.nbt.CompoundTag; + +import java.util.Comparator; +import java.util.function.Function; + +public class IntAttached extends Pair { + + protected IntAttached(Integer first, V second) { + super(first, second); + } + + public static IntAttached with(int number, V value) { + return new IntAttached<>(number, value); + } + + public static IntAttached withZero(V value) { + return new IntAttached<>(0, value); + } + + public boolean isZero() { + return first.intValue() == 0; + } + + public boolean exceeds(int value) { + return first.intValue() > value; + } + + public boolean isOrBelowZero() { + return first.intValue() <= 0; + } + + public void increment() { + first++; + } + + public void decrement() { + first--; + } + + public V getValue() { + return getSecond(); + } + + public CompoundTag serializeNBT(Function serializer) { + CompoundTag nbt = new CompoundTag(); + nbt.put("Item", serializer.apply(getValue())); + nbt.putInt("Location", getFirst()); + return nbt; + } + + public static Comparator> comparator() { + return (i1, i2) -> Integer.compare(i2.getFirst(), i1.getFirst()); + } + + public static IntAttached read(CompoundTag nbt, Function deserializer) { + return IntAttached.with(nbt.getInt("Location"), deserializer.apply(nbt.getCompound("Item"))); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Iterate.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Iterate.java new file mode 100644 index 0000000..6382596 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Iterate.java @@ -0,0 +1,48 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Direction.Axis; + +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; + +public class Iterate { + + public static final boolean[] trueAndFalse = { true, false }; + public static final boolean[] falseAndTrue = { false, true }; + public static final int[] zeroAndOne = { 0, 1 }; + public static final int[] positiveAndNegative = { 1, -1 }; + public static final Direction[] directions = Direction.values(); + public static final Direction[] horizontalDirections = getHorizontals(); + public static final Axis[] axes = Axis.values(); + public static final EnumSet axisSet = EnumSet.allOf(Axis.class); + + private static Direction[] getHorizontals() { + Direction[] directions = new Direction[4]; + for (int i = 0; i < 4; i++) + directions[i] = Direction.from2DDataValue(i); + return directions; + } + + public static Direction[] directionsInAxis(Axis axis) { + switch (axis) { + case X: + return new Direction[] { Direction.EAST, Direction.WEST }; + case Y: + return new Direction[] { Direction.UP, Direction.DOWN }; + default: + case Z: + return new Direction[] { Direction.SOUTH, Direction.NORTH }; + } + } + + public static List hereAndBelow(BlockPos pos) { + return Arrays.asList(pos, pos.below()); + } + + public static List hereBelowAndAbove(BlockPos pos) { + return Arrays.asList(pos, pos.below(), pos.above()); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Lang.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Lang.java new file mode 100644 index 0000000..e14385c --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Lang.java @@ -0,0 +1,91 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import nl.requios.effortlessbuilding.create.Create; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.fluids.FluidStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public class Lang { + + /** + * legacy-ish. Use Lang.translate and other builder methods where possible + * + * @param key + * @param args + * @return + */ + public static MutableComponent translateDirect(String key, Object... args) { + return Components.translatable(Create.ID + "." + key, resolveBuilders(args)); + } + + public static String asId(String name) { + return name.toLowerCase(Locale.ROOT); + } + + public static String nonPluralId(String name) { + String asId = asId(name); + return asId.endsWith("s") ? asId.substring(0, asId.length() - 1) : asId; + } + + public static List translatedOptions(String prefix, String... keys) { + List result = new ArrayList<>(keys.length); + for (String key : keys) + result.add(translate((prefix != null ? prefix + "." : "") + key).component()); + return result; + } + + // + + public static LangBuilder builder() { + return new LangBuilder(Create.ID); + } + + public static LangBuilder builder(String namespace) { + return new LangBuilder(namespace); + } + + // + + public static LangBuilder blockName(BlockState state) { + return builder().add(state.getBlock() + .getName()); + } + + public static LangBuilder itemName(ItemStack stack) { + return builder().add(stack.getHoverName() + .copy()); + } + + public static LangBuilder fluidName(FluidStack stack) { + return builder().add(stack.getDisplayName() + .copy()); + } + + public static LangBuilder number(double d) { + return builder().text(LangNumberFormat.format(d)); + } + + public static LangBuilder translate(String langKey, Object... args) { + return builder().translate(langKey, args); + } + + public static LangBuilder text(String text) { + return builder().text(text); + } + + // + + public static Object[] resolveBuilders(Object[] args) { + for (int i = 0; i < args.length; i++) + if (args[i]instanceof LangBuilder cb) + args[i] = cb.component(); + return args; + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/LangBuilder.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/LangBuilder.java new file mode 100644 index 0000000..b143a0b --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/LangBuilder.java @@ -0,0 +1,165 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import joptsimple.internal.Strings; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.entity.player.Player; + +import java.util.List; + +public class LangBuilder { + + String namespace; + MutableComponent component; + + public LangBuilder(String namespace) { + this.namespace = namespace; + } + + public LangBuilder space() { + return text(" "); + } + + public LangBuilder newLine() { + return text("\n"); + } + + /** + * Appends a localised component
+ * To add an independently formatted localised component, use add() and a nested + * builder + * + * @param langKey + * @param args + * @return + */ + public LangBuilder translate(String langKey, Object... args) { + return add(Components.translatable(namespace + "." + langKey, Lang.resolveBuilders(args))); + } + + /** + * Appends a text component + * + * @param literalText + * @return + */ + public LangBuilder text(String literalText) { + return add(Components.literal(literalText)); + } + + /** + * Appends a colored text component + * + * @param format + * @param literalText + * @return + */ + public LangBuilder text(ChatFormatting format, String literalText) { + return add(Components.literal(literalText).withStyle(format)); + } + + /** + * Appends a colored text component + * + * @param color + * @param literalText + * @return + */ + public LangBuilder text(int color, String literalText) { + return add(Components.literal(literalText).withStyle(s -> s.withColor(color))); + } + + /** + * Appends the contents of another builder + * + * @param otherBuilder + * @return + */ + public LangBuilder add(LangBuilder otherBuilder) { + return add(otherBuilder.component()); + } + + /** + * Appends a component + * + * @param customComponent + * @return + */ + public LangBuilder add(MutableComponent customComponent) { + component = component == null ? customComponent : component.append(customComponent); + return this; + } + + // + + /** + * Applies the format to all added components + * + * @param format + * @return + */ + public LangBuilder style(ChatFormatting format) { + assertComponent(); + component = component.withStyle(format); + return this; + } + + /** + * Applies the color to all added components + * + * @param color + * @return + */ + public LangBuilder color(int color) { + assertComponent(); + component = component.withStyle(s -> s.withColor(color)); + return this; + } + + // + + public MutableComponent component() { + assertComponent(); + return component; + } + + public String string() { + return component().getString(); + } + + public String json() { + return Component.Serializer.toJson(component()); + } + + public void sendStatus(Player player) { + player.displayClientMessage(component(), true); + } + + public void sendChat(Player player) { + player.displayClientMessage(component(), false); + } + + public void addTo(List tooltip) { + tooltip.add(component()); + } + + public void forGoggles(List tooltip) { + forGoggles(tooltip, 0); + } + + public void forGoggles(List tooltip, int indents) { + tooltip.add(Lang.builder() + .text(Strings.repeat(' ', 4 + indents)) + .add(this) + .component()); + } + + // + + private void assertComponent() { + if (component == null) + throw new IllegalStateException("No components were added to builder"); + } + +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/LangNumberFormat.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/LangNumberFormat.java new file mode 100644 index 0000000..bcf9d41 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/LangNumberFormat.java @@ -0,0 +1,33 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.client.Minecraft; + +import java.text.NumberFormat; +import java.util.Locale; + +public class LangNumberFormat { + + private NumberFormat format = NumberFormat.getNumberInstance(Locale.ROOT); + public static LangNumberFormat numberFormat = new LangNumberFormat(); + + public NumberFormat get() { + return format; + } + + public void update() { + format = NumberFormat.getInstance(Minecraft.getInstance() + .getLanguageManager() + .getSelected() + .getJavaLocale()); + format.setMaximumFractionDigits(2); + format.setMinimumFractionDigits(0); + format.setGroupingUsed(true); + } + + public static String format(double d) { + return numberFormat.get() + .format(d) + .replace("\u00A0", " "); + } + +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/NBTHelper.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/NBTHelper.java new file mode 100644 index 0000000..d409073 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/NBTHelper.java @@ -0,0 +1,106 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.core.Vec3i; +import net.minecraft.nbt.*; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.AABB; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +public class NBTHelper { + + public static void putMarker(CompoundTag nbt, String marker) { + nbt.putBoolean(marker, true); + } + + public static > T readEnum(CompoundTag nbt, String key, Class enumClass) { + T[] enumConstants = enumClass.getEnumConstants(); + if (enumConstants == null) + throw new IllegalArgumentException("Non-Enum class passed to readEnum: " + enumClass.getName()); + if (nbt.contains(key, Tag.TAG_STRING)) { + String name = nbt.getString(key); + for (T t : enumConstants) { + if (t.name() + .equals(name)) + return t; + } + } + return enumConstants[0]; + } + + public static > void writeEnum(CompoundTag nbt, String key, T enumConstant) { + nbt.putString(key, enumConstant.name()); + } + + public static ListTag writeCompoundList(Iterable list, Function serializer) { + ListTag listNBT = new ListTag(); + list.forEach(t -> { + CompoundTag apply = serializer.apply(t); + if (apply == null) + return; + listNBT.add(apply); + }); + return listNBT; + } + + public static List readCompoundList(ListTag listNBT, Function deserializer) { + List list = new ArrayList<>(listNBT.size()); + listNBT.forEach(inbt -> list.add(deserializer.apply((CompoundTag) inbt))); + return list; + } + + public static void iterateCompoundList(ListTag listNBT, Consumer consumer) { + listNBT.forEach(inbt -> consumer.accept((CompoundTag) inbt)); + } + + public static ListTag writeItemList(Iterable stacks) { + return writeCompoundList(stacks, ItemStack::serializeNBT); + } + + public static List readItemList(ListTag stacks) { + return readCompoundList(stacks, ItemStack::of); + } + + public static ListTag writeAABB(AABB bb) { + ListTag bbtag = new ListTag(); + bbtag.add(FloatTag.valueOf((float) bb.minX)); + bbtag.add(FloatTag.valueOf((float) bb.minY)); + bbtag.add(FloatTag.valueOf((float) bb.minZ)); + bbtag.add(FloatTag.valueOf((float) bb.maxX)); + bbtag.add(FloatTag.valueOf((float) bb.maxY)); + bbtag.add(FloatTag.valueOf((float) bb.maxZ)); + return bbtag; + } + + public static AABB readAABB(ListTag bbtag) { + if (bbtag == null || bbtag.isEmpty()) + return null; + return new AABB(bbtag.getFloat(0), bbtag.getFloat(1), bbtag.getFloat(2), bbtag.getFloat(3), + bbtag.getFloat(4), bbtag.getFloat(5)); + } + + public static ListTag writeVec3i(Vec3i vec) { + ListTag tag = new ListTag(); + tag.add(IntTag.valueOf(vec.getX())); + tag.add(IntTag.valueOf(vec.getY())); + tag.add(IntTag.valueOf(vec.getZ())); + return tag; + } + + public static Vec3i readVec3i(ListTag tag) { + return new Vec3i(tag.getInt(0), tag.getInt(1), tag.getInt(2)); + } + + @Nonnull + public static Tag getINBT(CompoundTag nbt, String id) { + Tag inbt = nbt.get(id); + if (inbt != null) + return inbt; + return new CompoundTag(); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/NBTProcessors.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/NBTProcessors.java new file mode 100644 index 0000000..218024e --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/NBTProcessors.java @@ -0,0 +1,84 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.SpawnerBlockEntity; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; +import java.util.function.UnaryOperator; + +public final class NBTProcessors { + + private static final Map, UnaryOperator> processors = new HashMap<>(); + private static final Map, UnaryOperator> survivalProcessors = new HashMap<>(); + + public static synchronized void addProcessor(BlockEntityType type, UnaryOperator processor) { + processors.put(type, processor); + } + + public static synchronized void addSurvivalProcessor(BlockEntityType type, UnaryOperator processor) { + survivalProcessors.put(type, processor); + } + + static { + addProcessor(BlockEntityType.SIGN, data -> { + for (int i = 0; i < 4; ++i) { + if (textComponentHasClickEvent(data.getString("Text" + (i + 1)))) + return null; + } + return data; + }); + addProcessor(BlockEntityType.LECTERN, data -> { + if (!data.contains("Book", Tag.TAG_COMPOUND)) + return data; + CompoundTag book = data.getCompound("Book"); + + if (!book.contains("tag", Tag.TAG_COMPOUND)) + return data; + CompoundTag tag = book.getCompound("tag"); + + if (!tag.contains("pages", Tag.TAG_LIST)) + return data; + ListTag pages = tag.getList("pages", Tag.TAG_STRING); + + for (Tag inbt : pages) { + if (textComponentHasClickEvent(inbt.getAsString())) + return null; + } + return data; + }); + } + + public static boolean textComponentHasClickEvent(String json) { + Component component = Component.Serializer.fromJson(json.isEmpty() ? "\"\"" : json); + return component != null && component.getStyle() != null && component.getStyle().getClickEvent() != null; + } + + private NBTProcessors() { + } + + @Nullable + public static CompoundTag process(BlockEntity tileEntity, CompoundTag compound, boolean survival) { + if (compound == null) + return null; + BlockEntityType type = tileEntity.getType(); + if (survival && survivalProcessors.containsKey(type)) + compound = survivalProcessors.get(type) + .apply(compound); + if (compound != null && processors.containsKey(type)) + return processors.get(type) + .apply(compound); + if (tileEntity instanceof SpawnerBlockEntity) + return compound; + if (tileEntity.onlyOpCanSetNbt()) + return null; + return compound; + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Pair.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Pair.java new file mode 100644 index 0000000..569b1e4 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Pair.java @@ -0,0 +1,68 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import java.util.Objects; + +public class Pair { + + F first; + S second; + + protected Pair(F first, S second) { + this.first = first; + this.second = second; + } + + public static Pair of(F first, S second) { + return new Pair<>(first, second); + } + + public F getFirst() { + return first; + } + + public S getSecond() { + return second; + } + + public void setFirst(F first) { + this.first = first; + } + + public void setSecond(S second) { + this.second = second; + } + + public Pair copy() { + return Pair.of(first, second); + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) + return true; + if (obj instanceof Pair) { + final Pair other = (Pair) obj; + return Objects.equals(first, other.first) && Objects.equals(second, other.second); + } + return false; + } + + @Override + public int hashCode() { + return (nullHash(first) * 31) ^ nullHash(second); + } + + int nullHash(Object o) { + return o == null ? 0 : o.hashCode(); + } + + @Override + public String toString() { + return "(" + first + ", " + second + ")"; + } + + public Pair swap() { + return Pair.of(second, first); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Pointing.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Pointing.java new file mode 100644 index 0000000..18a1716 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/Pointing.java @@ -0,0 +1,35 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.core.Direction; +import net.minecraft.core.Direction.Axis; +import net.minecraft.core.Direction.AxisDirection; +import net.minecraft.util.StringRepresentable; + +public enum Pointing implements StringRepresentable { + UP(0), LEFT(270), DOWN(180), RIGHT(90); + + private int xRotation; + + private Pointing(int xRotation) { + this.xRotation = xRotation; + } + + @Override + public String getSerializedName() { + return Lang.asId(name()); + } + + public int getXRotation() { + return xRotation; + } + + public Direction getCombinedDirection(Direction direction) { + Axis axis = direction.getAxis(); + Direction top = axis == Axis.Y ? Direction.SOUTH : Direction.UP; + int rotations = direction.getAxisDirection() == AxisDirection.NEGATIVE ? 4 - ordinal() : ordinal(); + for (int i = 0; i < rotations; i++) + top = top.getClockWise(axis); + return top; + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/RaycastHelper.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/RaycastHelper.java new file mode 100644 index 0000000..b11259e --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/RaycastHelper.java @@ -0,0 +1,197 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.ClipContext.Block; +import net.minecraft.world.level.ClipContext.Fluid; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; + +import java.util.function.Predicate; + +public class RaycastHelper { + + public static BlockHitResult rayTraceRange(Level worldIn, Player playerIn, double range) { + Vec3 origin = getTraceOrigin(playerIn); + Vec3 target = getTraceTarget(playerIn, range, origin); + ClipContext context = new ClipContext(origin, target, Block.COLLIDER, Fluid.NONE, playerIn); + return worldIn.clip(context); + } + + public static PredicateTraceResult rayTraceUntil(Player playerIn, double range, + Predicate predicate) { + Vec3 origin = getTraceOrigin(playerIn); + Vec3 target = getTraceTarget(playerIn, range, origin); + return rayTraceUntil(origin, target, predicate); + } + + public static Vec3 getTraceTarget(Player playerIn, double range, Vec3 origin) { + float f = playerIn.getXRot(); + float f1 = playerIn.getYRot(); + float f2 = Mth.cos(-f1 * 0.017453292F - (float) Math.PI); + float f3 = Mth.sin(-f1 * 0.017453292F - (float) Math.PI); + float f4 = -Mth.cos(-f * 0.017453292F); + float f5 = Mth.sin(-f * 0.017453292F); + float f6 = f3 * f4; + float f7 = f2 * f4; + double d3 = range; + Vec3 Vector3d1 = origin.add((double) f6 * d3, (double) f5 * d3, (double) f7 * d3); + return Vector3d1; + } + + public static Vec3 getTraceOrigin(Player playerIn) { + double d0 = playerIn.getX(); + double d1 = playerIn.getY() + (double) playerIn.getEyeHeight(); + double d2 = playerIn.getZ(); + Vec3 Vector3d = new Vec3(d0, d1, d2); + return Vector3d; + } + + public static PredicateTraceResult rayTraceUntil(Vec3 start, Vec3 end, Predicate predicate) { + if (Double.isNaN(start.x) || Double.isNaN(start.y) || Double.isNaN(start.z)) + return null; + if (Double.isNaN(end.x) || Double.isNaN(end.y) || Double.isNaN(end.z)) + return null; + + int dx = Mth.floor(end.x); + int dy = Mth.floor(end.y); + int dz = Mth.floor(end.z); + int x = Mth.floor(start.x); + int y = Mth.floor(start.y); + int z = Mth.floor(start.z); + + MutableBlockPos currentPos = new BlockPos(x, y, z).mutable(); + + if (predicate.test(currentPos)) + return new PredicateTraceResult(currentPos.immutable(), Direction.getNearest(dx - x, dy - y, dz - z)); + + int remainingDistance = 200; + + while (remainingDistance-- >= 0) { + if (Double.isNaN(start.x) || Double.isNaN(start.y) || Double.isNaN(start.z)) { + return null; + } + + if (x == dx && y == dy && z == dz) { + return new PredicateTraceResult(); + } + + boolean flag2 = true; + boolean flag = true; + boolean flag1 = true; + double d0 = 999.0D; + double d1 = 999.0D; + double d2 = 999.0D; + + if (dx > x) { + d0 = (double) x + 1.0D; + } else if (dx < x) { + d0 = (double) x + 0.0D; + } else { + flag2 = false; + } + + if (dy > y) { + d1 = (double) y + 1.0D; + } else if (dy < y) { + d1 = (double) y + 0.0D; + } else { + flag = false; + } + + if (dz > z) { + d2 = (double) z + 1.0D; + } else if (dz < z) { + d2 = (double) z + 0.0D; + } else { + flag1 = false; + } + + double d3 = 999.0D; + double d4 = 999.0D; + double d5 = 999.0D; + double d6 = end.x - start.x; + double d7 = end.y - start.y; + double d8 = end.z - start.z; + + if (flag2) { + d3 = (d0 - start.x) / d6; + } + + if (flag) { + d4 = (d1 - start.y) / d7; + } + + if (flag1) { + d5 = (d2 - start.z) / d8; + } + + if (d3 == -0.0D) { + d3 = -1.0E-4D; + } + + if (d4 == -0.0D) { + d4 = -1.0E-4D; + } + + if (d5 == -0.0D) { + d5 = -1.0E-4D; + } + + Direction enumfacing; + + if (d3 < d4 && d3 < d5) { + enumfacing = dx > x ? Direction.WEST : Direction.EAST; + start = new Vec3(d0, start.y + d7 * d3, start.z + d8 * d3); + } else if (d4 < d5) { + enumfacing = dy > y ? Direction.DOWN : Direction.UP; + start = new Vec3(start.x + d6 * d4, d1, start.z + d8 * d4); + } else { + enumfacing = dz > z ? Direction.NORTH : Direction.SOUTH; + start = new Vec3(start.x + d6 * d5, start.y + d7 * d5, d2); + } + + x = Mth.floor(start.x) - (enumfacing == Direction.EAST ? 1 : 0); + y = Mth.floor(start.y) - (enumfacing == Direction.UP ? 1 : 0); + z = Mth.floor(start.z) - (enumfacing == Direction.SOUTH ? 1 : 0); + currentPos.set(x, y, z); + + if (predicate.test(currentPos)) + return new PredicateTraceResult(currentPos.immutable(), enumfacing); + } + + return new PredicateTraceResult(); + } + + public static class PredicateTraceResult { + private BlockPos pos; + private Direction facing; + + public PredicateTraceResult(BlockPos pos, Direction facing) { + this.pos = pos; + this.facing = facing; + } + + public PredicateTraceResult() { + // missed, no result + } + + public Direction getFacing() { + return facing; + } + + public BlockPos getPos() { + return pos; + } + + public boolean missed() { + return this.pos == null; + } + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/RegisteredObjects.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/RegisteredObjects.java new file mode 100644 index 0000000..68e3108 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/RegisteredObjects.java @@ -0,0 +1,66 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.core.particles.ParticleType; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.alchemy.Potion; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.material.Fluid; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.IForgeRegistry; +import org.jetbrains.annotations.NotNull; + +public final class RegisteredObjects { + // registry argument for easier porting to 1.19 + @NotNull + public static ResourceLocation getKeyOrThrow(IForgeRegistry registry, V value) { + ResourceLocation key = registry.getKey(value); + if (key == null) { + throw new IllegalArgumentException("Could not get key for value " + value + "!"); + } + return key; + } + + @NotNull + public static ResourceLocation getKeyOrThrow(Block value) { + return getKeyOrThrow(ForgeRegistries.BLOCKS, value); + } + + @NotNull + public static ResourceLocation getKeyOrThrow(Item value) { + return getKeyOrThrow(ForgeRegistries.ITEMS, value); + } + + @NotNull + public static ResourceLocation getKeyOrThrow(Fluid value) { + return getKeyOrThrow(ForgeRegistries.FLUIDS, value); + } + + @NotNull + public static ResourceLocation getKeyOrThrow(EntityType value) { + return getKeyOrThrow(ForgeRegistries.ENTITY_TYPES, value); + } + + @NotNull + public static ResourceLocation getKeyOrThrow(BlockEntityType value) { + return getKeyOrThrow(ForgeRegistries.BLOCK_ENTITY_TYPES, value); + } + + @NotNull + public static ResourceLocation getKeyOrThrow(Potion value) { + return getKeyOrThrow(ForgeRegistries.POTIONS, value); + } + + @NotNull + public static ResourceLocation getKeyOrThrow(ParticleType value) { + return getKeyOrThrow(ForgeRegistries.PARTICLE_TYPES, value); + } + + @NotNull + public static ResourceLocation getKeyOrThrow(RecipeSerializer value) { + return getKeyOrThrow(ForgeRegistries.RECIPE_SERIALIZERS, value); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ResetableLazy.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ResetableLazy.java new file mode 100644 index 0000000..b18f34f --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ResetableLazy.java @@ -0,0 +1,32 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraftforge.common.util.NonNullSupplier; + +import java.util.function.Supplier; + +public class ResetableLazy implements Supplier { + + private final NonNullSupplier supplier; + private T value; + + public ResetableLazy(NonNullSupplier supplier) { + this.supplier = supplier; + } + + @Override + public T get() { + if (value == null) { + value = supplier.get(); + } + return value; + } + + public void reset() { + value = null; + } + + public static ResetableLazy of(NonNullSupplier supplier) { + return new ResetableLazy<>(supplier); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/UniqueLinkedList.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/UniqueLinkedList.java new file mode 100644 index 0000000..d5d148c --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/UniqueLinkedList.java @@ -0,0 +1,102 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +public class UniqueLinkedList extends LinkedList { + /** + * + */ + private static final long serialVersionUID = 1L; + private final HashSet contained = new HashSet<>(); + + @Override + public boolean contains(Object o) { + return contained.contains(o); + } + + @Override + public E poll() { + E e = super.poll(); + contained.remove(e); + return e; + } + + @Override + public boolean add(E e) { + if (contained.add(e)) + return super.add(e); + else + return false; + } + + @Override + public void add(int index, E element) { + if (contained.add(element)) + super.add(index, element); + } + + @Override + public void addFirst(E e) { + if (contained.add(e)) + super.addFirst(e); + } + + @Override + public void addLast(E e) { + if (contained.add(e)) + super.addLast(e); + } + + @Override + public boolean addAll(Collection c) { + List filtered = c.stream() + .filter(it -> !contained.contains(it)) + .collect(Collectors.toList()); + return super.addAll(filtered); + } + + @Override + public boolean addAll(int index, Collection c) { + List filtered = c.stream() + .filter(it -> !contained.contains(it)) + .collect(Collectors.toList()); + return super.addAll(index, filtered); + } + + @Override + public boolean remove(Object o) { + contained.remove(o); + return super.remove(o); + } + + @Override + public E remove(int index) { + E e = super.remove(index); + contained.remove(e); + return e; + } + + @Override + public E removeFirst() { + E e = super.removeFirst(); + contained.remove(e); + return e; + } + + @Override + public E removeLast() { + E e = super.removeLast(); + contained.remove(e); + return e; + } + + @Override + public void clear() { + super.clear(); + contained.clear(); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/VecHelper.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/VecHelper.java new file mode 100644 index 0000000..e9ea1d0 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/VecHelper.java @@ -0,0 +1,343 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import com.mojang.math.Quaternion; +import com.mojang.math.Vector3f; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Direction.Axis; +import net.minecraft.core.Vec3i; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.DoubleTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.phys.Vec3; + +import javax.annotation.Nullable; + +public class VecHelper { + + public static final Vec3 CENTER_OF_ORIGIN = new Vec3(.5, .5, .5); + + public static Vec3 rotate(Vec3 vec, Vec3 rotationVec) { + return rotate(vec, rotationVec.x, rotationVec.y, rotationVec.z); + } + + public static Vec3 rotate(Vec3 vec, double xRot, double yRot, double zRot) { + return rotate(rotate(rotate(vec, xRot, Axis.X), yRot, Axis.Y), zRot, Axis.Z); + } + + public static Vec3 rotateCentered(Vec3 vec, double deg, Axis axis) { + Vec3 shift = getCenterOf(BlockPos.ZERO); + return VecHelper.rotate(vec.subtract(shift), deg, axis) + .add(shift); + } + + public static Vec3 rotate(Vec3 vec, double deg, Axis axis) { + if (deg == 0) + return vec; + if (vec == Vec3.ZERO) + return vec; + + float angle = (float) (deg / 180f * Math.PI); + double sin = Mth.sin(angle); + double cos = Mth.cos(angle); + double x = vec.x; + double y = vec.y; + double z = vec.z; + + if (axis == Axis.X) + return new Vec3(x, y * cos - z * sin, z * cos + y * sin); + if (axis == Axis.Y) + return new Vec3(x * cos + z * sin, y, z * cos - x * sin); + if (axis == Axis.Z) + return new Vec3(x * cos - y * sin, y * cos + x * sin, z); + return vec; + } + + public static Vec3 mirrorCentered(Vec3 vec, Mirror mirror) { + Vec3 shift = getCenterOf(BlockPos.ZERO); + return VecHelper.mirror(vec.subtract(shift), mirror) + .add(shift); + } + + public static Vec3 mirror(Vec3 vec, Mirror mirror) { + if (mirror == null || mirror == Mirror.NONE) + return vec; + if (vec == Vec3.ZERO) + return vec; + + double x = vec.x; + double y = vec.y; + double z = vec.z; + + if (mirror == Mirror.LEFT_RIGHT) + return new Vec3(x, y, -z); + if (mirror == Mirror.FRONT_BACK) + return new Vec3(-x, y, z); + return vec; + } + + public static Vec3 lookAt(Vec3 vec, Vec3 fwd) { + fwd = fwd.normalize(); + Vec3 up = new Vec3(0, 1, 0); + double dot = fwd.dot(up); + if (Math.abs(dot) > 1 - 1.0E-3) + up = new Vec3(0, 0, dot > 0 ? 1 : -1); + Vec3 right = fwd.cross(up) + .normalize(); + up = right.cross(fwd) + .normalize(); + double x = vec.x * right.x + vec.y * up.x + vec.z * fwd.x; + double y = vec.x * right.y + vec.y * up.y + vec.z * fwd.y; + double z = vec.x * right.z + vec.y * up.z + vec.z * fwd.z; + return new Vec3(x, y, z); + } + + public static boolean isVecPointingTowards(Vec3 vec, Direction direction) { + return Vec3.atLowerCornerOf(direction.getNormal()) + .dot(vec.normalize()) > 0.125; // slight tolerance to activate perpendicular movement actors + } + + public static Vec3 getCenterOf(Vec3i pos) { + if (pos.equals(Vec3i.ZERO)) + return CENTER_OF_ORIGIN; + return Vec3.atLowerCornerOf(pos) + .add(.5f, .5f, .5f); + } + + public static Vec3 offsetRandomly(Vec3 vec, RandomSource r, float radius) { + return new Vec3(vec.x + (r.nextFloat() - .5f) * 2 * radius, vec.y + (r.nextFloat() - .5f) * 2 * radius, + vec.z + (r.nextFloat() - .5f) * 2 * radius); + } + + public static Vec3 axisAlingedPlaneOf(Vec3 vec) { + vec = vec.normalize(); + return new Vec3(1, 1, 1).subtract(Math.abs(vec.x), Math.abs(vec.y), Math.abs(vec.z)); + } + + public static Vec3 axisAlingedPlaneOf(Direction face) { + return axisAlingedPlaneOf(Vec3.atLowerCornerOf(face.getNormal())); + } + + public static ListTag writeNBT(Vec3 vec) { + ListTag listnbt = new ListTag(); + listnbt.add(DoubleTag.valueOf(vec.x)); + listnbt.add(DoubleTag.valueOf(vec.y)); + listnbt.add(DoubleTag.valueOf(vec.z)); + return listnbt; + } + + public static CompoundTag writeNBTCompound(Vec3 vec) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.put("V", writeNBT(vec)); + return compoundTag; + } + + public static Vec3 readNBT(ListTag list) { + if (list.isEmpty()) + return Vec3.ZERO; + return new Vec3(list.getDouble(0), list.getDouble(1), list.getDouble(2)); + } + + public static Vec3 readNBTCompound(CompoundTag nbt) { + return readNBT(nbt.getList("V", Tag.TAG_DOUBLE)); + } + + public static void write(Vec3 vec, FriendlyByteBuf buffer) { + buffer.writeDouble(vec.x); + buffer.writeDouble(vec.y); + buffer.writeDouble(vec.z); + } + + public static Vec3 read(FriendlyByteBuf buffer) { + return new Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()); + } + + public static Vec3 voxelSpace(double x, double y, double z) { + return new Vec3(x, y, z).scale(1 / 16f); + } + + public static int getCoordinate(Vec3i pos, Axis axis) { + return axis.choose(pos.getX(), pos.getY(), pos.getZ()); + } + + public static float getCoordinate(Vec3 vec, Axis axis) { + return (float) axis.choose(vec.x, vec.y, vec.z); + } + + public static boolean onSameAxis(BlockPos pos1, BlockPos pos2, Axis axis) { + if (pos1.equals(pos2)) + return true; + for (Axis otherAxis : Axis.values()) + if (axis != otherAxis) + if (getCoordinate(pos1, otherAxis) != getCoordinate(pos2, otherAxis)) + return false; + return true; + } + + public static Vec3 clamp(Vec3 vec, float maxLength) { + return vec.length() > maxLength ? vec.normalize() + .scale(maxLength) : vec; + } + + public static Vec3 lerp(float p, Vec3 from, Vec3 to) { + return from.add(to.subtract(from) + .scale(p)); + } + + public static Vec3 slerp(float p, Vec3 from, Vec3 to) { + double theta = Math.acos(from.dot(to)); + return from.scale(Mth.sin(1 - p) * theta) + .add(to.scale(Mth.sin((float) (theta * p)))) + .scale(1 / Mth.sin((float) theta)); + } + + public static Vec3 clampComponentWise(Vec3 vec, float maxLength) { + return new Vec3(Mth.clamp(vec.x, -maxLength, maxLength), Mth.clamp(vec.y, -maxLength, maxLength), + Mth.clamp(vec.z, -maxLength, maxLength)); + } + + public static Vec3 project(Vec3 vec, Vec3 ontoVec) { + if (ontoVec.equals(Vec3.ZERO)) + return Vec3.ZERO; + return ontoVec.scale(vec.dot(ontoVec) / ontoVec.lengthSqr()); + } + + @Nullable + public static Vec3 intersectSphere(Vec3 origin, Vec3 lineDirection, Vec3 sphereCenter, double radius) { + if (lineDirection.equals(Vec3.ZERO)) + return null; + if (lineDirection.length() != 1) + lineDirection = lineDirection.normalize(); + + Vec3 diff = origin.subtract(sphereCenter); + double lineDotDiff = lineDirection.dot(diff); + double delta = lineDotDiff * lineDotDiff - (diff.lengthSqr() - radius * radius); + if (delta < 0) + return null; + double t = -lineDotDiff + Math.sqrt(delta); + return origin.add(lineDirection.scale(t)); + } + + // https://forums.minecraftforge.net/topic/88562-116solved-3d-to-2d-conversion/?do=findComment&comment=413573 + // slightly modified + public static Vec3 projectToPlayerView(Vec3 target, float partialTicks) { + /* + * The (centered) location on the screen of the given 3d point in the world. + * Result is (dist right of center screen, dist up from center screen, if < 0, + * then in front of view plane) + */ + Camera ari = Minecraft.getInstance().gameRenderer.getMainCamera(); + Vec3 camera_pos = ari.getPosition(); + Quaternion camera_rotation_conj = ari.rotation() + .copy(); + camera_rotation_conj.conj(); + + Vector3f result3f = new Vector3f((float) (camera_pos.x - target.x), (float) (camera_pos.y - target.y), + (float) (camera_pos.z - target.z)); + result3f.transform(camera_rotation_conj); + + // ----- compensate for view bobbing (if active) ----- + // the following code adapted from GameRenderer::applyBobbing (to invert it) + Minecraft mc = Minecraft.getInstance(); + if (mc.options.bobView().get()) { + Entity renderViewEntity = mc.getCameraEntity(); + if (renderViewEntity instanceof Player) { + Player playerentity = (Player) renderViewEntity; + float distwalked_modified = playerentity.walkDist; + + float f = distwalked_modified - playerentity.walkDistO; + float f1 = -(distwalked_modified + f * partialTicks); + float f2 = Mth.lerp(partialTicks, playerentity.oBob, playerentity.bob); + Quaternion q2 = + new Quaternion(Vector3f.XP, Math.abs(Mth.cos(f1 * (float) Math.PI - 0.2F) * f2) * 5.0F, true); + q2.conj(); + result3f.transform(q2); + + Quaternion q1 = new Quaternion(Vector3f.ZP, Mth.sin(f1 * (float) Math.PI) * f2 * 3.0F, true); + q1.conj(); + result3f.transform(q1); + + Vector3f bob_translation = new Vector3f((Mth.sin(f1 * (float) Math.PI) * f2 * 0.5F), + (-Math.abs(Mth.cos(f1 * (float) Math.PI) * f2)), 0.0f); + bob_translation.setY(-bob_translation.y()); // this is weird but hey, if it works + result3f.add(bob_translation); + } + } + + // ----- adjust for fov ----- + float fov = 70;//(float) ((GameRendererAccessor) mc.gameRenderer).create$callGetFov(ari, partialTicks, true); + + float half_height = (float) mc.getWindow() + .getGuiScaledHeight() / 2; + float scale_factor = half_height / (result3f.z() * (float) Math.tan(Math.toRadians(fov / 2))); + return new Vec3(-result3f.x() * scale_factor, result3f.y() * scale_factor, result3f.z()); + } + + public static Vec3 bezier(Vec3 p1, Vec3 p2, Vec3 q1, Vec3 q2, float t) { + Vec3 v1 = lerp(t, p1, q1); + Vec3 v2 = lerp(t, q1, q2); + Vec3 v3 = lerp(t, q2, p2); + Vec3 inner1 = lerp(t, v1, v2); + Vec3 inner2 = lerp(t, v2, v3); + Vec3 result = lerp(t, inner1, inner2); + return result; + } + + public static Vec3 bezierDerivative(Vec3 p1, Vec3 p2, Vec3 q1, Vec3 q2, float t) { + return p1.scale(-3 * t * t + 6 * t - 3) + .add(q1.scale(9 * t * t - 12 * t + 3)) + .add(q2.scale(-9 * t * t + 6 * t)) + .add(p2.scale(3 * t * t)); + } + + @Nullable + public static double[] intersectRanged(Vec3 p1, Vec3 q1, Vec3 p2, Vec3 q2, Axis plane) { + Vec3 pDiff = p2.subtract(p1); + Vec3 qDiff = q2.subtract(q1); + double[] intersect = intersect(p1, q1, pDiff.normalize(), qDiff.normalize(), plane); + if (intersect == null) + return null; + if (intersect[0] < 0 || intersect[1] < 0) + return null; + if (intersect[0] > pDiff.length() || intersect[1] > qDiff.length()) + return null; + return intersect; + } + + @Nullable + public static double[] intersect(Vec3 p1, Vec3 p2, Vec3 r, Vec3 s, Axis plane) { + if (plane == Axis.X) { + p1 = new Vec3(p1.y, 0, p1.z); + p2 = new Vec3(p2.y, 0, p2.z); + r = new Vec3(r.y, 0, r.z); + s = new Vec3(s.y, 0, s.z); + } + + if (plane == Axis.Z) { + p1 = new Vec3(p1.x, 0, p1.y); + p2 = new Vec3(p2.x, 0, p2.y); + r = new Vec3(r.x, 0, r.y); + s = new Vec3(s.x, 0, s.y); + } + + Vec3 qminusp = p2.subtract(p1); + double rcs = r.x * s.z - r.z * s.x; + if (Mth.equal(rcs, 0)) + return null; + Vec3 rdivrcs = r.scale(1 / rcs); + Vec3 sdivrcs = s.scale(1 / rcs); + double t = qminusp.x * sdivrcs.z - qminusp.z * sdivrcs.x; + double u = qminusp.x * rdivrcs.z - qminusp.z * rdivrcs.x; + return new double[] { t, u }; + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/VoxelShaper.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/VoxelShaper.java new file mode 100644 index 0000000..c18807b --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/VoxelShaper.java @@ -0,0 +1,142 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.core.Direction; +import net.minecraft.core.Direction.Axis; +import net.minecraft.core.Direction.AxisDirection; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.apache.commons.lang3.mutable.MutableObject; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +public class VoxelShaper { + + private Map shapes = new HashMap<>(); + + public VoxelShape get(Direction direction) { + return shapes.get(direction); + } + + public VoxelShape get(Axis axis) { + return shapes.get(axisAsFace(axis)); + } + + public static VoxelShaper forHorizontal(VoxelShape shape, Direction facing) { + return forDirectionsWithRotation(shape, facing, Direction.Plane.HORIZONTAL, new HorizontalRotationValues()); + } + + public static VoxelShaper forHorizontalAxis(VoxelShape shape, Axis along) { + return forDirectionsWithRotation(shape, axisAsFace(along), Arrays.asList(Direction.SOUTH, Direction.EAST), + new HorizontalRotationValues()); + } + + public static VoxelShaper forDirectional(VoxelShape shape, Direction facing) { + return forDirectionsWithRotation(shape, facing, Arrays.asList(Iterate.directions), new DefaultRotationValues()); + } + + public static VoxelShaper forAxis(VoxelShape shape, Axis along) { + return forDirectionsWithRotation(shape, axisAsFace(along), + Arrays.asList(Direction.SOUTH, Direction.EAST, Direction.UP), new DefaultRotationValues()); + } + + public VoxelShaper withVerticalShapes(VoxelShape upShape) { + shapes.put(Direction.UP, upShape); + shapes.put(Direction.DOWN, rotatedCopy(upShape, new Vec3(180, 0, 0))); + return this; + } + + public VoxelShaper withShape(VoxelShape shape, Direction facing) { + shapes.put(facing, shape); + return this; + } + + public static Direction axisAsFace(Axis axis) { + return Direction.get(AxisDirection.POSITIVE, axis); + } + + protected static float horizontalAngleFromDirection(Direction direction) { + return (float) ((Math.max(direction.get2DDataValue(), 0) & 3) * 90); + } + + protected static VoxelShaper forDirectionsWithRotation(VoxelShape shape, Direction facing, + Iterable directions, Function rotationValues) { + VoxelShaper voxelShaper = new VoxelShaper(); + for (Direction dir : directions) { + voxelShaper.shapes.put(dir, rotate(shape, facing, dir, rotationValues)); + } + return voxelShaper; + } + + protected static VoxelShape rotate(VoxelShape shape, Direction from, Direction to, + Function usingValues) { + if (from == to) + return shape; + + return rotatedCopy(shape, usingValues.apply(from) + .reverse() + .add(usingValues.apply(to))); + } + + protected static VoxelShape rotatedCopy(VoxelShape shape, Vec3 rotation) { + if (rotation.equals(Vec3.ZERO)) + return shape; + + MutableObject result = new MutableObject<>(Shapes.empty()); + Vec3 center = new Vec3(8, 8, 8); + + shape.forAllBoxes((x1, y1, z1, x2, y2, z2) -> { + Vec3 v1 = new Vec3(x1, y1, z1).scale(16) + .subtract(center); + Vec3 v2 = new Vec3(x2, y2, z2).scale(16) + .subtract(center); + + v1 = VecHelper.rotate(v1, (float) rotation.x, Axis.X); + v1 = VecHelper.rotate(v1, (float) rotation.y, Axis.Y); + v1 = VecHelper.rotate(v1, (float) rotation.z, Axis.Z) + .add(center); + + v2 = VecHelper.rotate(v2, (float) rotation.x, Axis.X); + v2 = VecHelper.rotate(v2, (float) rotation.y, Axis.Y); + v2 = VecHelper.rotate(v2, (float) rotation.z, Axis.Z) + .add(center); + + VoxelShape rotated = blockBox(v1, v2); + result.setValue(Shapes.or(result.getValue(), rotated)); + }); + + return result.getValue(); + } + + protected static VoxelShape blockBox(Vec3 v1, Vec3 v2) { + return Block.box( + Math.min(v1.x, v2.x), + Math.min(v1.y, v2.y), + Math.min(v1.z, v2.z), + Math.max(v1.x, v2.x), + Math.max(v1.y, v2.y), + Math.max(v1.z, v2.z) + ); + } + + protected static class DefaultRotationValues implements Function { + // assume facing up as the default rotation + @Override + public Vec3 apply(Direction direction) { + return new Vec3(direction == Direction.UP ? 0 : (Direction.Plane.VERTICAL.test(direction) ? 180 : 90), + -horizontalAngleFromDirection(direction), 0); + } + } + + protected static class HorizontalRotationValues implements Function { + @Override + public Vec3 apply(Direction direction) { + return new Vec3(0, -horizontalAngleFromDirection(direction), 0); + } + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/WorldAttached.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/WorldAttached.java new file mode 100644 index 0000000..9c9c081 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/WorldAttached.java @@ -0,0 +1,101 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.world.level.LevelAccessor; + +import javax.annotation.Nonnull; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +public class WorldAttached { + + // weak references to prevent leaking hashmaps when a WorldAttached is GC'd during runtime + static List>> allMaps = new ArrayList<>(); + private final Map attached; + private final Function factory; + + public WorldAttached(Function factory) { + this.factory = factory; + // Weak key hashmaps prevent worlds not existing anywhere else from leaking memory. + // This is only a fallback in the event that unload events fail to fire for any reason. + attached = new WeakHashMap<>(); + allMaps.add(new WeakReference<>(attached)); + } + + public static void invalidateWorld(LevelAccessor world) { + var i = allMaps.iterator(); + while (i.hasNext()) { + Map map = i.next() + .get(); + if (map == null) { + // If the map has been GC'd, remove the weak reference + i.remove(); + } else { + // Prevent leaks + map.remove(world); + } + } + } + + @Nonnull + public T get(LevelAccessor world) { + T t = attached.get(world); + if (t != null) return t; + T entry = factory.apply(world); + put(world, entry); + return entry; + } + + public void put(LevelAccessor world, T entry) { + attached.put(world, entry); + } + + /** + * Replaces the entry with a new one from the factory and returns the new entry. + */ + @Nonnull + public T replace(LevelAccessor world) { + attached.remove(world); + + return get(world); + } + + /** + * Replaces the entry with a new one from the factory and returns the new entry. + */ + @Nonnull + public T replace(LevelAccessor world, Consumer finalizer) { + T remove = attached.remove(world); + + if (remove != null) + finalizer.accept(remove); + + return get(world); + } + + /** + * Deletes all entries after calling a function on them. + * + * @param finalizer Do something with all of the world-value pairs + */ + public void empty(BiConsumer finalizer) { + attached.forEach(finalizer); + attached.clear(); + } + + /** + * Deletes all entries after calling a function on them. + * + * @param finalizer Do something with all of the values + */ + public void empty(Consumer finalizer) { + attached.values() + .forEach(finalizer); + attached.clear(); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/WorldHelper.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/WorldHelper.java new file mode 100644 index 0000000..fa26d0e --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/WorldHelper.java @@ -0,0 +1,13 @@ +package nl.requios.effortlessbuilding.create.foundation.utility; + +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.LevelAccessor; + +public class WorldHelper { + public static ResourceLocation getDimensionID(LevelAccessor world) { + return world.registryAccess() + .registryOrThrow(Registry.DIMENSION_TYPE_REGISTRY) + .getKey(world.dimensionType()); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/animation/Force.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/animation/Force.java new file mode 100644 index 0000000..493a4cf --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/animation/Force.java @@ -0,0 +1,102 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.animation; + +public interface Force { + + float get(float mass, float value, float speed); + + boolean finished(); + + class Drag implements Force { + final float dragFactor; + + public Drag(float dragFactor) { + this.dragFactor = dragFactor; + } + + @Override + public float get(float mass, float value, float speed) { + return -speed * dragFactor; + } + + @Override + public boolean finished() { + return false; + } + } + + class Zeroing implements Force { + final float g; + + public Zeroing(float g) { + this.g = g / 20; + } + + @Override + public float get(float mass, float value, float speed) { + return -Math.signum(value) * g * mass; + } + + @Override + public boolean finished() { + return false; + } + } + + class Impulse implements Force { + + float force; + + public Impulse(float force) { + this.force = force; + } + + @Override + public float get(float mass, float value, float speed) { + return force; + } + + @Override + public boolean finished() { + return true; + } + } + + class OverTime implements Force { + int timeRemaining; + float f; + + public OverTime(int time, float totalAcceleration) { + this.timeRemaining = time; + this.f = totalAcceleration / (float) time; + } + + @Override + public float get(float mass, float value, float speed) { + timeRemaining--; + return f; + } + + @Override + public boolean finished() { + return timeRemaining <= 0; + } + } + + class Static implements Force { + float force; + + public Static(float force) { + this.force = force; + } + + @Override + public float get(float mass, float value, float speed) { + return force; + } + + @Override + public boolean finished() { + return false; + } + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/animation/LerpedFloat.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/animation/LerpedFloat.java new file mode 100644 index 0000000..1043202 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/animation/LerpedFloat.java @@ -0,0 +1,149 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.animation; + +import nl.requios.effortlessbuilding.create.foundation.utility.AngleHelper; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.Mth; + +public class LerpedFloat { + + protected Interpolator interpolator; + protected float previousValue; + protected float value; + + protected Chaser chaseFunction; + protected float chaseTarget; + protected float chaseSpeed; + protected boolean angularChase; + + protected boolean forcedSync; + + public LerpedFloat(Interpolator interpolator) { + this.interpolator = interpolator; + startWithValue(0); + forcedSync = true; + } + + public static LerpedFloat linear() { + return new LerpedFloat((p, c, t) -> (float) Mth.lerp(p, c, t)); + } + + public static LerpedFloat angular() { + LerpedFloat lerpedFloat = new LerpedFloat(AngleHelper::angleLerp); + lerpedFloat.angularChase = true; + return lerpedFloat; + } + + public LerpedFloat startWithValue(double value) { + float f = (float) value; + this.previousValue = f; + this.chaseTarget = f; + this.value = f; + return this; + } + + public LerpedFloat chase(double value, double speed, Chaser chaseFunction) { + updateChaseTarget((float) value); + this.chaseSpeed = (float) speed; + this.chaseFunction = chaseFunction; + return this; + } + + public LerpedFloat disableSmartAngleChasing() { + angularChase = false; + return this; + } + + public void updateChaseTarget(float target) { + if (angularChase) + target = value + AngleHelper.getShortestAngleDiff(value, target); + this.chaseTarget = target; + } + + public boolean updateChaseSpeed(double speed) { + float prevSpeed = this.chaseSpeed; + this.chaseSpeed = (float) speed; + return !Mth.equal(prevSpeed, speed); + } + + public void tickChaser() { + previousValue = value; + if (chaseFunction == null) + return; + if (Mth.equal((double) value, chaseTarget)) { + value = chaseTarget; + return; + } + value = chaseFunction.chase(value, chaseSpeed, chaseTarget); + } + + public void setValueNoUpdate(double value) { + this.value = (float) value; + } + + public void setValue(double value) { + this.previousValue = this.value; + this.value = (float) value; + } + + public float getValue() { + return getValue(1); + } + + public float getValue(float partialTicks) { + return interpolator.interpolate(partialTicks, previousValue, value); + } + + public boolean settled() { + return Mth.equal((double) previousValue, value); + } + + public float getChaseTarget() { + return chaseTarget; + } + + public void forceNextSync() { + forcedSync = true; + } + + public CompoundTag writeNBT() { + CompoundTag compoundNBT = new CompoundTag(); + compoundNBT.putFloat("Speed", chaseSpeed); + compoundNBT.putFloat("Target", chaseTarget); + compoundNBT.putFloat("Value", value); + if (forcedSync) + compoundNBT.putBoolean("Force", true); + forcedSync = false; + return compoundNBT; + } + + public void readNBT(CompoundTag compoundNBT, boolean clientPacket) { + if (!clientPacket || compoundNBT.contains("Force")) + startWithValue(compoundNBT.getFloat("Value")); + readChaser(compoundNBT); + } + + protected void readChaser(CompoundTag compoundNBT) { + chaseSpeed = compoundNBT.getFloat("Speed"); + chaseTarget = compoundNBT.getFloat("Target"); + } + + @FunctionalInterface + public interface Interpolator { + float interpolate(double progress, double current, double target); + } + + @FunctionalInterface + public interface Chaser { + + Chaser IDLE = (c, s, t) -> (float) c; + Chaser EXP = exp(Double.MAX_VALUE); + Chaser LINEAR = (c, s, t) -> (float) (c + Mth.clamp(t - c, -s, s)); + + static Chaser exp(double maxEffectiveSpeed) { + return (c, s, t) -> (float) (c + Mth.clamp((t - c) * s, -maxEffectiveSpeed, maxEffectiveSpeed)); + } + + float chase(double current, double speed, double target); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/animation/PhysicalFloat.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/animation/PhysicalFloat.java new file mode 100644 index 0000000..c4eb9d4 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/animation/PhysicalFloat.java @@ -0,0 +1,90 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.animation; + +import net.minecraft.util.Mth; + +import java.util.ArrayList; + +public class PhysicalFloat { + + float previousValue; + float value; + + float previousSpeed; + float speed; + float limit = Float.NaN; + + float mass; + + private final ArrayList forces = new ArrayList<>(); + + public static PhysicalFloat create() { + return new PhysicalFloat(1); + } + + public static PhysicalFloat create(float mass) { + return new PhysicalFloat(mass); + } + + public PhysicalFloat(float mass) { + this.mass = mass; + } + + public PhysicalFloat startAt(double value) { + previousValue = this.value = (float) value; + return this; + } + + public PhysicalFloat withDrag(double drag) { + return addForce(new Force.Drag((float) drag)); + } + + public PhysicalFloat zeroing(double g) { + return addForce(new Force.Zeroing((float) g)); + } + + public PhysicalFloat withLimit(float limit) { + this.limit = limit; + return this; + } + + public void tick() { + previousSpeed = speed; + previousValue = value; + + float totalImpulse = 0; + for (Force force : forces) + totalImpulse += force.get(mass, value, speed) / mass; + + speed += totalImpulse; + + forces.removeIf(Force::finished); + + if (Float.isFinite(limit)) { + speed = Mth.clamp(speed, -limit, limit); + } + + value += speed; + } + + public PhysicalFloat addForce(Force f) { + forces.add(f); + return this; + } + + public PhysicalFloat bump(double force) { + return addForce(new Force.Impulse((float) force)); + } + + public PhysicalFloat bump(int time, double force) { + return addForce(new Force.OverTime(time, (float) force)); + } + + public float getValue() { + return getValue(1); + } + + public float getValue(float partialTicks) { + return Mth.lerp(partialTicks, previousValue, value); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockParams.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockParams.java new file mode 100644 index 0000000..ecb3e1a --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockParams.java @@ -0,0 +1,50 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.ghost; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.function.Supplier; + +public class GhostBlockParams { + + protected final BlockState state; + protected BlockPos pos; + protected Supplier alphaSupplier; + + private GhostBlockParams(BlockState state) { + this.state = state; + this.pos = BlockPos.ZERO; + this.alphaSupplier = () -> 1f; + } + + public static GhostBlockParams of(BlockState state) { + return new GhostBlockParams(state); + } + + public static GhostBlockParams of(Block block) { + return of(block.defaultBlockState()); + } + + public GhostBlockParams at(BlockPos pos) { + this.pos = pos; + return this; + } + + public GhostBlockParams at(int x, int y, int z) { + return this.at(new BlockPos(x, y, z)); + } + + public GhostBlockParams alpha(Supplier alphaSupplier) { + this.alphaSupplier = alphaSupplier; + return this; + } + + public GhostBlockParams alpha(float alpha) { + return this.alpha(() -> alpha); + } + + public GhostBlockParams breathingAlpha() { + return this.alpha(() -> (float) GhostBlocks.getBreathingAlpha()); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockRenderer.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockRenderer.java new file mode 100644 index 0000000..a27b869 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockRenderer.java @@ -0,0 +1,140 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.ghost; + +import com.jozufozu.flywheel.core.model.ModelUtil; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import nl.requios.effortlessbuilding.create.foundation.render.SuperRenderTypeBuffer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.BlockRenderDispatcher; +import net.minecraft.client.renderer.block.ModelBlockRenderer; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.client.model.data.ModelData; + +import javax.annotation.Nullable; +import java.util.List; + +public abstract class GhostBlockRenderer { + + private static final GhostBlockRenderer STANDARD = new DefaultGhostBlockRenderer(); + + public static GhostBlockRenderer standard() { + return STANDARD; + } + + private static final GhostBlockRenderer TRANSPARENT = new TransparentGhostBlockRenderer(); + + public static GhostBlockRenderer transparent() { + return TRANSPARENT; + } + + public abstract void render(PoseStack ms, SuperRenderTypeBuffer buffer, GhostBlockParams params); + + private static class DefaultGhostBlockRenderer extends GhostBlockRenderer { + + @Override + public void render(PoseStack ms, SuperRenderTypeBuffer buffer, GhostBlockParams params) { + BlockRenderDispatcher dispatcher = Minecraft.getInstance() + .getBlockRenderer(); + ModelBlockRenderer renderer = dispatcher.getModelRenderer(); + + BlockState state = params.state; + BlockPos pos = params.pos; + + BakedModel model = dispatcher.getBlockModel(state); + + ms.pushPose(); + ms.translate(pos.getX(), pos.getY(), pos.getZ()); + + for (RenderType layer : model.getRenderTypes(state, RandomSource.create(42L), ModelUtil.VIRTUAL_DATA)) { + VertexConsumer vb = buffer.getEarlyBuffer(layer); + renderer.renderModel(ms.last(), vb, state, model, 1f, 1f, 1f, LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, + ModelUtil.VIRTUAL_DATA, layer); + } + + ms.popPose(); + } + + } + + private static class TransparentGhostBlockRenderer extends GhostBlockRenderer { + + @Override + public void render(PoseStack ms, SuperRenderTypeBuffer buffer, GhostBlockParams params) { + Minecraft mc = Minecraft.getInstance(); + BlockRenderDispatcher dispatcher = mc.getBlockRenderer(); + + BlockState state = params.state; + BlockPos pos = params.pos; + float alpha = params.alphaSupplier.get() * .75f/* * PlacementHelpers.getCurrentAlpha()*/; + + BakedModel model = dispatcher.getBlockModel(state); + RenderType layer = RenderType.translucent(); + VertexConsumer vb = buffer.getEarlyBuffer(layer); + + ms.pushPose(); + ms.translate(pos.getX(), pos.getY(), pos.getZ()); + + ms.translate(.5, .5, .5); + ms.scale(.85f, .85f, .85f); + ms.translate(-.5, -.5, -.5); + + renderModel(ms.last(), vb, state, model, 1f, 1f, 1f, alpha, + LevelRenderer.getLightColor(mc.level, pos), OverlayTexture.NO_OVERLAY, + ModelUtil.VIRTUAL_DATA, layer); + + ms.popPose(); + } + + // ModelBlockRenderer + public void renderModel(PoseStack.Pose pose, VertexConsumer consumer, + @Nullable BlockState state, BakedModel model, float red, float green, float blue, + float alpha, int packedLight, int packedOverlay, ModelData modelData, RenderType renderType) { + RandomSource random = RandomSource.create(); + + for (Direction direction : Direction.values()) { + random.setSeed(42L); + renderQuadList(pose, consumer, red, green, blue, alpha, + model.getQuads(state, direction, random, modelData, renderType), packedLight, packedOverlay); + } + + random.setSeed(42L); + renderQuadList(pose, consumer, red, green, blue, alpha, + model.getQuads(state, null, random, modelData, renderType), packedLight, packedOverlay); + } + + // ModelBlockRenderer + private static void renderQuadList(PoseStack.Pose pose, VertexConsumer consumer, + float red, float green, float blue, float alpha, List quads, + int packedLight, int packedOverlay) { + for (BakedQuad quad : quads) { + float f; + float f1; + float f2; + if (quad.isTinted()) { + f = Mth.clamp(red, 0.0F, 1.0F); + f1 = Mth.clamp(green, 0.0F, 1.0F); + f2 = Mth.clamp(blue, 0.0F, 1.0F); + } else { + f = 1.0F; + f1 = 1.0F; + f2 = 1.0F; + } + + consumer.putBulkData(pose, quad, f, f1, f2, alpha, packedLight, packedOverlay, true); + } + + } + + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlocks.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlocks.java new file mode 100644 index 0000000..eb38081 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlocks.java @@ -0,0 +1,83 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.ghost; + +import com.mojang.blaze3d.vertex.PoseStack; +import nl.requios.effortlessbuilding.create.foundation.render.SuperRenderTypeBuffer; +import net.minecraft.util.Mth; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.HashMap; +import java.util.Map; + +public class GhostBlocks { + + public static double getBreathingAlpha() { + double period = 2500; + double timer = System.currentTimeMillis() % period; + double offset = Mth.cos((float) ((2d/period) * Math.PI * timer)); + return 0.55d - 0.2d * offset; + } + + final Map ghosts; + + public GhostBlockParams showGhostState(Object slot, BlockState state) { + return showGhostState(slot, state, 1); + } + + public GhostBlockParams showGhostState(Object slot, BlockState state, int ttl) { + Entry e = refresh(slot, GhostBlockRenderer.transparent(), GhostBlockParams.of(state), ttl); + return e.params; + } + + public GhostBlockParams showGhost(Object slot, GhostBlockRenderer ghost, GhostBlockParams params, int ttl) { + Entry e = refresh(slot, ghost, params, ttl); + return e.params; + } + + private Entry refresh(Object slot, GhostBlockRenderer ghost, GhostBlockParams params, int ttl) { + if (!ghosts.containsKey(slot)) + ghosts.put(slot, new Entry(ghost, params, ttl)); + + Entry e = ghosts.get(slot); + e.ticksToLive = ttl; + e.params = params; + e.ghost = ghost; + return e; + } + + public GhostBlocks() { + ghosts = new HashMap<>(); + } + + public void tickGhosts() { + ghosts.forEach((slot, entry) -> entry.ticksToLive--); + ghosts.entrySet().removeIf(e -> !e.getValue().isAlive()); + } + + public void renderAll(PoseStack ms, SuperRenderTypeBuffer buffer) { + ghosts.forEach((slot, entry) -> { + GhostBlockRenderer ghost = entry.ghost; + ghost.render(ms, buffer, entry.params); + }); + } + + static class Entry { + + private GhostBlockRenderer ghost; + private GhostBlockParams params; + private int ticksToLive; + + public Entry(GhostBlockRenderer ghost, GhostBlockParams params) { + this(ghost, params, 1); + } + + public Entry(GhostBlockRenderer ghost, GhostBlockParams params, int ttl) { + this.ghost = ghost; + this.params = params; + this.ticksToLive = ttl; + } + + public boolean isAlive() { + return ticksToLive >= 0; + } + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/AABBOutline.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/AABBOutline.java new file mode 100644 index 0000000..5c85797 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/AABBOutline.java @@ -0,0 +1,100 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.outliner; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import nl.requios.effortlessbuilding.create.foundation.render.RenderTypes; +import nl.requios.effortlessbuilding.create.foundation.render.SuperRenderTypeBuffer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.core.Direction; +import net.minecraft.core.Direction.Axis; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +public class AABBOutline extends Outline { + + protected AABB bb; + + public AABBOutline(AABB bb) { + this.setBounds(bb); + } + + @Override + public void render(PoseStack ms, SuperRenderTypeBuffer buffer, float pt) { + renderBB(ms, buffer, bb); + } + + public void renderBB(PoseStack ms, SuperRenderTypeBuffer buffer, AABB bb) { + Vec3 projectedView = Minecraft.getInstance().gameRenderer.getMainCamera() + .getPosition(); + boolean noCull = bb.contains(projectedView); + bb = bb.inflate(noCull ? -1 / 128d : 1 / 128d); + noCull |= params.disableCull; + + Vec3 xyz = new Vec3(bb.minX, bb.minY, bb.minZ); + Vec3 Xyz = new Vec3(bb.maxX, bb.minY, bb.minZ); + Vec3 xYz = new Vec3(bb.minX, bb.maxY, bb.minZ); + Vec3 XYz = new Vec3(bb.maxX, bb.maxY, bb.minZ); + Vec3 xyZ = new Vec3(bb.minX, bb.minY, bb.maxZ); + Vec3 XyZ = new Vec3(bb.maxX, bb.minY, bb.maxZ); + Vec3 xYZ = new Vec3(bb.minX, bb.maxY, bb.maxZ); + Vec3 XYZ = new Vec3(bb.maxX, bb.maxY, bb.maxZ); + + Vec3 start = xyz; + renderAACuboidLine(ms, buffer, start, Xyz); + renderAACuboidLine(ms, buffer, start, xYz); + renderAACuboidLine(ms, buffer, start, xyZ); + + start = XyZ; + renderAACuboidLine(ms, buffer, start, xyZ); + renderAACuboidLine(ms, buffer, start, XYZ); + renderAACuboidLine(ms, buffer, start, Xyz); + + start = XYz; + renderAACuboidLine(ms, buffer, start, xYz); + renderAACuboidLine(ms, buffer, start, Xyz); + renderAACuboidLine(ms, buffer, start, XYZ); + + start = xYZ; + renderAACuboidLine(ms, buffer, start, XYZ); + renderAACuboidLine(ms, buffer, start, xyZ); + renderAACuboidLine(ms, buffer, start, xYz); + + renderFace(ms, buffer, Direction.NORTH, xYz, XYz, Xyz, xyz, noCull); + renderFace(ms, buffer, Direction.SOUTH, XYZ, xYZ, xyZ, XyZ, noCull); + renderFace(ms, buffer, Direction.EAST, XYz, XYZ, XyZ, Xyz, noCull); + renderFace(ms, buffer, Direction.WEST, xYZ, xYz, xyz, xyZ, noCull); + renderFace(ms, buffer, Direction.UP, xYZ, XYZ, XYz, xYz, noCull); + renderFace(ms, buffer, Direction.DOWN, xyz, Xyz, XyZ, xyZ, noCull); + + } + + protected void renderFace(PoseStack ms, SuperRenderTypeBuffer buffer, Direction direction, Vec3 p1, Vec3 p2, + Vec3 p3, Vec3 p4, boolean noCull) { + if (!params.faceTexture.isPresent()) + return; + + ResourceLocation faceTexture = params.faceTexture.get() + .getLocation(); + float alphaBefore = params.alpha; + params.alpha = + (direction == params.getHighlightedFace() && params.hightlightedFaceTexture.isPresent()) ? 1 : 0.5f; + + RenderType translucentType = RenderTypes.getOutlineTranslucent(faceTexture, !noCull); + VertexConsumer builder = buffer.getLateBuffer(translucentType); + + Axis axis = direction.getAxis(); + Vec3 uDiff = p2.subtract(p1); + Vec3 vDiff = p4.subtract(p1); + float maxU = (float) Math.abs(axis == Axis.X ? uDiff.z : uDiff.x); + float maxV = (float) Math.abs(axis == Axis.Y ? vDiff.z : vDiff.y); + putQuadUV(ms, builder, p1, p2, p3, p4, 0, 0, maxU, maxV, Direction.UP); + params.alpha = alphaBefore; + } + + public void setBounds(AABB bb) { + this.bb = bb; + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/BlockClusterOutline.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/BlockClusterOutline.java new file mode 100644 index 0000000..3ef0a55 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/BlockClusterOutline.java @@ -0,0 +1,178 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.outliner; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import nl.requios.effortlessbuilding.create.AllSpecialTextures; +import nl.requios.effortlessbuilding.create.foundation.render.RenderTypes; +import nl.requios.effortlessbuilding.create.foundation.render.SuperRenderTypeBuffer; +import nl.requios.effortlessbuilding.create.foundation.utility.Iterate; +import nl.requios.effortlessbuilding.create.foundation.utility.VecHelper; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Direction.Axis; +import net.minecraft.core.Direction.AxisDirection; +import net.minecraft.world.phys.Vec3; + +import java.util.*; + +public class BlockClusterOutline extends Outline { + + private Cluster cluster; + + public BlockClusterOutline(Iterable selection) { + cluster = new Cluster(); + selection.forEach(cluster::include); + } + + @Override + public void render(PoseStack ms, SuperRenderTypeBuffer buffer, float pt) { + cluster.visibleEdges.forEach(edge -> { + Vec3 start = Vec3.atLowerCornerOf(edge.pos); + Direction direction = Direction.get(AxisDirection.POSITIVE, edge.axis); + renderAACuboidLine(ms, buffer, start, Vec3.atLowerCornerOf(edge.pos.relative(direction))); + }); + + Optional faceTexture = params.faceTexture; + if (!faceTexture.isPresent()) + return; + + RenderType translucentType = RenderTypes.getOutlineTranslucent(faceTexture.get() + .getLocation(), true); + VertexConsumer builder = buffer.getLateBuffer(translucentType); + + cluster.visibleFaces.forEach((face, axisDirection) -> { + Direction direction = Direction.get(axisDirection, face.axis); + BlockPos pos = face.pos; + if (axisDirection == AxisDirection.POSITIVE) + pos = pos.relative(direction.getOpposite()); + renderBlockFace(ms, builder, pos, direction); + }); + } + + static Vec3 xyz = new Vec3(-.5, -.5, -.5); + static Vec3 Xyz = new Vec3(.5, -.5, -.5); + static Vec3 xYz = new Vec3(-.5, .5, -.5); + static Vec3 XYz = new Vec3(.5, .5, -.5); + static Vec3 xyZ = new Vec3(-.5, -.5, .5); + static Vec3 XyZ = new Vec3(.5, -.5, .5); + static Vec3 xYZ = new Vec3(-.5, .5, .5); + static Vec3 XYZ = new Vec3(.5, .5, .5); + + protected void renderBlockFace(PoseStack ms, VertexConsumer builder, BlockPos pos, Direction face) { + Vec3 center = VecHelper.getCenterOf(pos); + Vec3 offset = Vec3.atLowerCornerOf(face.getNormal()); + offset = offset.scale(1 / 128d); + center = center.add(offset); + + ms.pushPose(); + ms.translate(center.x, center.y, center.z); + + switch (face) { + case DOWN: + putQuad(ms, builder, xyz, Xyz, XyZ, xyZ, face); + break; + case EAST: + putQuad(ms, builder, XYz, XYZ, XyZ, Xyz, face); + break; + case NORTH: + putQuad(ms, builder, xYz, XYz, Xyz, xyz, face); + break; + case SOUTH: + putQuad(ms, builder, XYZ, xYZ, xyZ, XyZ, face); + break; + case UP: + putQuad(ms, builder, xYZ, XYZ, XYz, xYz, face); + break; + case WEST: + putQuad(ms, builder, xYZ, xYz, xyz, xyZ, face); + default: + break; + } + + ms.popPose(); + } + + private static class Cluster { + + private Map visibleFaces; + private Set visibleEdges; + + public Cluster() { + visibleEdges = new HashSet<>(); + visibleFaces = new HashMap<>(); + } + + public void include(BlockPos pos) { + + // 6 FACES + for (Axis axis : Iterate.axes) { + Direction direction = Direction.get(AxisDirection.POSITIVE, axis); + for (int offset : Iterate.zeroAndOne) { + MergeEntry entry = new MergeEntry(axis, pos.relative(direction, offset)); + if (visibleFaces.remove(entry) == null) + visibleFaces.put(entry, offset == 0 ? AxisDirection.NEGATIVE : AxisDirection.POSITIVE); + } + } + + // 12 EDGES + for (Axis axis : Iterate.axes) { + for (Axis axis2 : Iterate.axes) { + if (axis == axis2) + continue; + for (Axis axis3 : Iterate.axes) { + if (axis == axis3) + continue; + if (axis2 == axis3) + continue; + + Direction direction = Direction.get(AxisDirection.POSITIVE, axis2); + Direction direction2 = Direction.get(AxisDirection.POSITIVE, axis3); + + for (int offset : Iterate.zeroAndOne) { + BlockPos entryPos = pos.relative(direction, offset); + for (int offset2 : Iterate.zeroAndOne) { + entryPos = entryPos.relative(direction2, offset2); + MergeEntry entry = new MergeEntry(axis, entryPos); + if (!visibleEdges.remove(entry)) + visibleEdges.add(entry); + } + } + } + + break; + } + } + + } + + } + + private static class MergeEntry { + + private Axis axis; + private BlockPos pos; + + public MergeEntry(Axis axis, BlockPos pos) { + this.axis = axis; + this.pos = pos; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof MergeEntry)) + return false; + + MergeEntry other = (MergeEntry) o; + return this.axis == other.axis && this.pos.equals(other.pos); + } + + @Override + public int hashCode() { + return this.pos.hashCode() * 31 + axis.ordinal(); + } + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/ChasingAABBOutline.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/ChasingAABBOutline.java new file mode 100644 index 0000000..d5639ea --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/ChasingAABBOutline.java @@ -0,0 +1,41 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.outliner; + +import com.mojang.blaze3d.vertex.PoseStack; +import nl.requios.effortlessbuilding.create.foundation.render.SuperRenderTypeBuffer; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.AABB; + +public class ChasingAABBOutline extends AABBOutline { + + AABB targetBB; + AABB prevBB; + + public ChasingAABBOutline(AABB bb) { + super(bb); + prevBB = bb.inflate(0); + targetBB = bb.inflate(0); + } + + public void target(AABB target) { + targetBB = target; + } + + @Override + public void tick() { + prevBB = bb; + setBounds(interpolateBBs(bb, targetBB, .5f)); + } + + @Override + public void render(PoseStack ms, SuperRenderTypeBuffer buffer, float pt) { + renderBB(ms, buffer, interpolateBBs(prevBB, bb, pt)); + } + + private static AABB interpolateBBs(AABB current, AABB target, float pt) { + return new AABB(Mth.lerp(pt, current.minX, target.minX), + Mth.lerp(pt, current.minY, target.minY), Mth.lerp(pt, current.minZ, target.minZ), + Mth.lerp(pt, current.maxX, target.maxX), Mth.lerp(pt, current.maxY, target.maxY), + Mth.lerp(pt, current.maxZ, target.maxZ)); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/LineOutline.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/LineOutline.java new file mode 100644 index 0000000..2829a8d --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/LineOutline.java @@ -0,0 +1,65 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.outliner; + +import com.mojang.blaze3d.vertex.PoseStack; +import nl.requios.effortlessbuilding.create.foundation.render.SuperRenderTypeBuffer; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; + +public class LineOutline extends Outline { + + protected Vec3 start = Vec3.ZERO; + protected Vec3 end = Vec3.ZERO; + + public LineOutline set(Vec3 start, Vec3 end) { + this.start = start; + this.end = end; + return this; + } + + @Override + public void render(PoseStack ms, SuperRenderTypeBuffer buffer, float pt) { + renderCuboidLine(ms, buffer, start, end); + } + + public static class EndChasingLineOutline extends LineOutline { + + float prevProgress = 0; + float progress = 0; + private boolean lockStart; + + public EndChasingLineOutline(boolean lockStart) { + this.lockStart = lockStart; + } + + @Override + public void tick() {} + + public EndChasingLineOutline setProgress(float progress) { + prevProgress = this.progress; + this.progress = progress; + return this; + } + + @Override + public LineOutline set(Vec3 start, Vec3 end) { + if (!end.equals(this.end)) + super.set(start, end); + return this; + } + + @Override + public void render(PoseStack ms, SuperRenderTypeBuffer buffer, float pt) { + float distanceToTarget = Mth.lerp(pt, prevProgress, progress); + if (!lockStart) + distanceToTarget = 1 - distanceToTarget; + Vec3 start = lockStart ? this.end : this.start; + Vec3 end = lockStart ? this.start : this.end; + + start = end.add(this.start.subtract(end) + .scale(distanceToTarget)); + renderCuboidLine(ms, buffer, start, end); + } + + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/Outline.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/Outline.java new file mode 100644 index 0000000..a5d3e8e --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/Outline.java @@ -0,0 +1,246 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.outliner; + +import com.jozufozu.flywheel.util.transform.TransformStack; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Matrix3f; +import nl.requios.effortlessbuilding.create.AllSpecialTextures; +import nl.requios.effortlessbuilding.create.foundation.render.RenderTypes; +import nl.requios.effortlessbuilding.create.foundation.render.SuperRenderTypeBuffer; +import nl.requios.effortlessbuilding.create.foundation.utility.AngleHelper; +import nl.requios.effortlessbuilding.create.foundation.utility.Color; +import nl.requios.effortlessbuilding.create.foundation.utility.VecHelper; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.core.Direction; +import net.minecraft.core.Direction.Axis; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; + +import javax.annotation.Nullable; +import java.util.Optional; + +public abstract class Outline { + + protected OutlineParams params; + protected Matrix3f transformNormals; // TODO: not used? + + public Outline() { + params = new OutlineParams(); + } + + public abstract void render(PoseStack ms, SuperRenderTypeBuffer buffer, float pt); + + public void tick() {} + + public OutlineParams getParams() { + return params; + } + + public void renderCuboidLine(PoseStack ms, SuperRenderTypeBuffer buffer, Vec3 start, Vec3 end) { + Vec3 diff = end.subtract(start); + float hAngle = AngleHelper.deg(Mth.atan2(diff.x, diff.z)); + float hDistance = (float) diff.multiply(1, 0, 1) + .length(); + float vAngle = AngleHelper.deg(Mth.atan2(hDistance, diff.y)) - 90; + ms.pushPose(); + TransformStack.cast(ms) + .translate(start) + .rotateY(hAngle).rotateX(vAngle); + renderAACuboidLine(ms, buffer, Vec3.ZERO, new Vec3(0, 0, diff.length())); + ms.popPose(); + } + + public void renderAACuboidLine(PoseStack ms, SuperRenderTypeBuffer buffer, Vec3 start, Vec3 end) { + float lineWidth = params.getLineWidth(); + if (lineWidth == 0) + return; + + VertexConsumer builder = buffer.getBuffer(RenderTypes.getOutlineSolid()); + + Vec3 diff = end.subtract(start); + if (diff.x + diff.y + diff.z < 0) { + Vec3 temp = start; + start = end; + end = temp; + diff = diff.scale(-1); + } + + Vec3 extension = diff.normalize() + .scale(lineWidth / 2); + Vec3 plane = VecHelper.axisAlingedPlaneOf(diff); + Direction face = Direction.getNearest(diff.x, diff.y, diff.z); + Axis axis = face.getAxis(); + + start = start.subtract(extension); + end = end.add(extension); + plane = plane.scale(lineWidth / 2); + + Vec3 a1 = plane.add(start); + Vec3 b1 = plane.add(end); + plane = VecHelper.rotate(plane, -90, axis); + Vec3 a2 = plane.add(start); + Vec3 b2 = plane.add(end); + plane = VecHelper.rotate(plane, -90, axis); + Vec3 a3 = plane.add(start); + Vec3 b3 = plane.add(end); + plane = VecHelper.rotate(plane, -90, axis); + Vec3 a4 = plane.add(start); + Vec3 b4 = plane.add(end); + + if (params.disableNormals) { + face = Direction.UP; + putQuad(ms, builder, b4, b3, b2, b1, face); + putQuad(ms, builder, a1, a2, a3, a4, face); + putQuad(ms, builder, a1, b1, b2, a2, face); + putQuad(ms, builder, a2, b2, b3, a3, face); + putQuad(ms, builder, a3, b3, b4, a4, face); + putQuad(ms, builder, a4, b4, b1, a1, face); + return; + } + + putQuad(ms, builder, b4, b3, b2, b1, face); + putQuad(ms, builder, a1, a2, a3, a4, face.getOpposite()); + Vec3 vec = a1.subtract(a4); + face = Direction.getNearest(vec.x, vec.y, vec.z); + putQuad(ms, builder, a1, b1, b2, a2, face); + vec = VecHelper.rotate(vec, -90, axis); + face = Direction.getNearest(vec.x, vec.y, vec.z); + putQuad(ms, builder, a2, b2, b3, a3, face); + vec = VecHelper.rotate(vec, -90, axis); + face = Direction.getNearest(vec.x, vec.y, vec.z); + putQuad(ms, builder, a3, b3, b4, a4, face); + vec = VecHelper.rotate(vec, -90, axis); + face = Direction.getNearest(vec.x, vec.y, vec.z); + putQuad(ms, builder, a4, b4, b1, a1, face); + } + + public void putQuad(PoseStack ms, VertexConsumer builder, Vec3 v1, Vec3 v2, Vec3 v3, Vec3 v4, + Direction normal) { + putQuadUV(ms, builder, v1, v2, v3, v4, 0, 0, 1, 1, normal); + } + + public void putQuadUV(PoseStack ms, VertexConsumer builder, Vec3 v1, Vec3 v2, Vec3 v3, Vec3 v4, float minU, + float minV, float maxU, float maxV, Direction normal) { + putVertex(ms, builder, v1, minU, minV, normal); + putVertex(ms, builder, v2, maxU, minV, normal); + putVertex(ms, builder, v3, maxU, maxV, normal); + putVertex(ms, builder, v4, minU, maxV, normal); + } + + protected void putVertex(PoseStack ms, VertexConsumer builder, Vec3 pos, float u, float v, Direction normal) { + putVertex(ms.last(), builder, (float) pos.x, (float) pos.y, (float) pos.z, u, v, normal); + } + + protected void putVertex(PoseStack.Pose pose, VertexConsumer builder, float x, float y, float z, float u, float v, Direction normal) { + Color rgb = params.rgb; + if (transformNormals == null) + transformNormals = pose.normal(); + + int xOffset = 0; + int yOffset = 0; + int zOffset = 0; + + if (normal != null) { + xOffset = normal.getStepX(); + yOffset = normal.getStepY(); + zOffset = normal.getStepZ(); + } + + builder.vertex(pose.pose(), x, y, z) + .color(rgb.getRedAsFloat(), rgb.getGreenAsFloat(), rgb.getBlueAsFloat(), rgb.getAlphaAsFloat() * params.alpha) + .uv(u, v) + .overlayCoords(OverlayTexture.NO_OVERLAY) + .uv2(params.lightMap) + .normal(pose.normal(), xOffset, yOffset, zOffset) + .endVertex(); + + transformNormals = null; + } + + public static class OutlineParams { + protected Optional faceTexture; + protected Optional hightlightedFaceTexture; + protected Direction highlightedFace; + protected boolean fadeLineWidth; + protected boolean disableCull; + protected boolean disableNormals; + protected float alpha; + protected int lightMap; + protected Color rgb; + private float lineWidth; + + public OutlineParams() { + faceTexture = hightlightedFaceTexture = Optional.empty(); + alpha = 1; + lineWidth = 1 / 32f; + fadeLineWidth = true; + rgb = Color.WHITE; + lightMap = LightTexture.FULL_BRIGHT; + } + + // builder + + public OutlineParams colored(int color) { + rgb = new Color(color, false); + return this; + } + + public OutlineParams colored(Color c) { + rgb = c.copy(); + return this; + } + + public OutlineParams lightMap(int light) { + lightMap = light; + return this; + } + + public OutlineParams lineWidth(float width) { + this.lineWidth = width; + return this; + } + + public OutlineParams withFaceTexture(AllSpecialTextures texture) { + this.faceTexture = Optional.ofNullable(texture); + return this; + } + + public OutlineParams clearTextures() { + return this.withFaceTextures(null, null); + } + + public OutlineParams withFaceTextures(AllSpecialTextures texture, AllSpecialTextures highlightTexture) { + this.faceTexture = Optional.ofNullable(texture); + this.hightlightedFaceTexture = Optional.ofNullable(highlightTexture); + return this; + } + + public OutlineParams highlightFace(@Nullable Direction face) { + highlightedFace = face; + return this; + } + + public OutlineParams disableNormals() { + disableNormals = true; + return this; + } + + public OutlineParams disableCull() { + disableCull = true; + return this; + } + + // getter + + public float getLineWidth() { + return fadeLineWidth ? alpha * lineWidth : lineWidth; + } + + public Direction getHighlightedFace() { + return highlightedFace; + } + + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/Outliner.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/Outliner.java new file mode 100644 index 0000000..c2f0660 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/Outliner.java @@ -0,0 +1,179 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.outliner; + +import com.mojang.blaze3d.vertex.PoseStack; +import nl.requios.effortlessbuilding.create.foundation.render.SuperRenderTypeBuffer; +import nl.requios.effortlessbuilding.create.foundation.utility.outliner.LineOutline.EndChasingLineOutline; +import nl.requios.effortlessbuilding.create.foundation.utility.outliner.Outline.OutlineParams; +import net.minecraft.core.BlockPos; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +import java.util.*; + +public class Outliner { + + private final Map outlines = Collections.synchronizedMap(new HashMap<>()); + private final Map outlinesView = Collections.unmodifiableMap(outlines); + + // Facade + + public OutlineParams showLine(Object slot, Vec3 start, Vec3 end) { + if (!outlines.containsKey(slot)) { + LineOutline outline = new LineOutline(); + outlines.put(slot, new OutlineEntry(outline)); + } + OutlineEntry entry = outlines.get(slot); + entry.ticksTillRemoval = 1; + ((LineOutline) entry.outline).set(start, end); + return entry.outline.getParams(); + } + + public OutlineParams endChasingLine(Object slot, Vec3 start, Vec3 end, float chasingProgress, boolean lockStart) { + if (!outlines.containsKey(slot)) { + EndChasingLineOutline outline = new EndChasingLineOutline(lockStart); + outlines.put(slot, new OutlineEntry(outline)); + } + OutlineEntry entry = outlines.get(slot); + entry.ticksTillRemoval = 1; + ((EndChasingLineOutline) entry.outline).setProgress(chasingProgress) + .set(start, end); + return entry.outline.getParams(); + } + + public OutlineParams showAABB(Object slot, AABB bb, int ttl) { + createAABBOutlineIfMissing(slot, bb); + ChasingAABBOutline outline = getAndRefreshAABB(slot, ttl); + outline.prevBB = outline.targetBB = outline.bb = bb; + return outline.getParams(); + } + + public OutlineParams showAABB(Object slot, AABB bb) { + createAABBOutlineIfMissing(slot, bb); + ChasingAABBOutline outline = getAndRefreshAABB(slot); + outline.prevBB = outline.targetBB = outline.bb = bb; + return outline.getParams(); + } + + public OutlineParams chaseAABB(Object slot, AABB bb) { + createAABBOutlineIfMissing(slot, bb); + ChasingAABBOutline outline = getAndRefreshAABB(slot); + outline.targetBB = bb; + return outline.getParams(); + } + + public OutlineParams showCluster(Object slot, Iterable selection) { + BlockClusterOutline outline = new BlockClusterOutline(selection); + OutlineEntry entry = new OutlineEntry(outline); + outlines.put(slot, entry); + return entry.getOutline() + .getParams(); + } + + public void keep(Object slot) { + if (outlines.containsKey(slot)) + outlines.get(slot).ticksTillRemoval = 1; + } + + public void remove(Object slot) { + outlines.remove(slot); + } + + public Optional edit(Object slot) { + keep(slot); + if (outlines.containsKey(slot)) + return Optional.of(outlines.get(slot) + .getOutline() + .getParams()); + return Optional.empty(); + } + + public Map getOutlines() { + return outlinesView; + } + + // Utility + + private void createAABBOutlineIfMissing(Object slot, AABB bb) { + if (!outlines.containsKey(slot) || !(outlines.get(slot).outline instanceof AABBOutline)) { + ChasingAABBOutline outline = new ChasingAABBOutline(bb); + outlines.put(slot, new OutlineEntry(outline)); + } + } + + private ChasingAABBOutline getAndRefreshAABB(Object slot) { + OutlineEntry entry = outlines.get(slot); + entry.ticksTillRemoval = 1; + return (ChasingAABBOutline) entry.getOutline(); + } + + private ChasingAABBOutline getAndRefreshAABB(Object slot, int ttl) { + OutlineEntry entry = outlines.get(slot); + entry.ticksTillRemoval = ttl; + return (ChasingAABBOutline) entry.getOutline(); + } + + // Maintenance + + public void tickOutlines() { + Iterator iterator = outlines.values() + .iterator(); + while (iterator.hasNext()) { + OutlineEntry entry = iterator.next(); + entry.tick(); + if (!entry.isAlive()) + iterator.remove(); + } + } + + public void renderOutlines(PoseStack ms, SuperRenderTypeBuffer buffer, float pt) { + outlines.forEach((key, entry) -> { + Outline outline = entry.getOutline(); + OutlineParams params = outline.getParams(); + params.alpha = 1; + if (entry.isFading()) { + int prevTicks = entry.ticksTillRemoval + 1; + float fadeticks = OutlineEntry.fadeTicks; + float lastAlpha = prevTicks >= 0 ? 1 : 1 + (prevTicks / fadeticks); + float currentAlpha = 1 + (entry.ticksTillRemoval / fadeticks); + float alpha = Mth.lerp(pt, lastAlpha, currentAlpha); + + params.alpha = alpha * alpha * alpha; + if (params.alpha < 1 / 8f) + return; + } + outline.render(ms, buffer, pt); + }); + } + + public static class OutlineEntry { + + static final int fadeTicks = 8; + private Outline outline; + private int ticksTillRemoval; + + public OutlineEntry(Outline outline) { + this.outline = outline; + ticksTillRemoval = 1; + } + + public void tick() { + ticksTillRemoval--; + outline.tick(); + } + + public boolean isAlive() { + return ticksTillRemoval >= -fadeTicks; + } + + public boolean isFading() { + return ticksTillRemoval < 0; + } + + public Outline getOutline() { + return outline; + } + + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/DummyLevelEntityGetter.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/DummyLevelEntityGetter.java new file mode 100644 index 0000000..294fbed --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/DummyLevelEntityGetter.java @@ -0,0 +1,41 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.worldWrappers; + +import net.minecraft.world.level.entity.EntityAccess; +import net.minecraft.world.level.entity.EntityTypeTest; +import net.minecraft.world.level.entity.LevelEntityGetter; +import net.minecraft.world.phys.AABB; + +import java.util.Collections; +import java.util.UUID; +import java.util.function.Consumer; + +public class DummyLevelEntityGetter implements LevelEntityGetter { + + @Override + public T get(int p_156931_) { + return null; + } + + @Override + public T get(UUID pUuid) { + return null; + } + + @Override + public Iterable getAll() { + return Collections.emptyList(); + } + + @Override + public void get(EntityTypeTest p_156935_, Consumer p_156936_) { + } + + @Override + public void get(AABB p_156937_, Consumer p_156938_) { + } + + @Override + public void get(EntityTypeTest p_156932_, AABB p_156933_, Consumer p_156934_) { + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/DummyStatusListener.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/DummyStatusListener.java new file mode 100644 index 0000000..ad9856e --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/DummyStatusListener.java @@ -0,0 +1,23 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.worldWrappers; + +import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.chunk.ChunkStatus; + +import javax.annotation.Nullable; + +public class DummyStatusListener implements ChunkProgressListener { + + @Override + public void updateSpawnPos(ChunkPos pCenter) {} + + @Override + public void onStatusChange(ChunkPos pChunkPosition, @Nullable ChunkStatus pNewStatus) {} + + @Override + public void start() {} + + @Override + public void stop() {} + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/PlacementSimulationServerWorld.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/PlacementSimulationServerWorld.java new file mode 100644 index 0000000..e596b9d --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/PlacementSimulationServerWorld.java @@ -0,0 +1,62 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.worldWrappers; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; + +import java.util.HashMap; +import java.util.function.Predicate; + +public class PlacementSimulationServerWorld extends WrappedServerWorld { + public HashMap blocksAdded; + + public PlacementSimulationServerWorld(ServerLevel wrapped) { + super(wrapped); + blocksAdded = new HashMap<>(); + } + + public void clear() { + blocksAdded.clear(); + } + + @Override + public boolean setBlock(BlockPos pos, BlockState newState, int flags) { + blocksAdded.put(pos.immutable(), newState); + return true; + } + + @Override + public boolean setBlockAndUpdate(BlockPos pos, BlockState state) { + return setBlock(pos, state, 0); + } + + @Override + public boolean isStateAtPosition(BlockPos pos, Predicate condition) { + return condition.test(getBlockState(pos)); + } + + @Override + public boolean isLoaded(BlockPos pos) { + return true; + } + + @Override + public boolean isAreaLoaded(BlockPos center, int range) { + return true; + } + + @Override + public BlockState getBlockState(BlockPos pos) { + if (blocksAdded.containsKey(pos)) + return blocksAdded.get(pos); + return Blocks.AIR.defaultBlockState(); + } + + @Override + public FluidState getFluidState(BlockPos pos) { + return getBlockState(pos).getFluidState(); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/RayTraceWorld.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/RayTraceWorld.java new file mode 100644 index 0000000..74b907b --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/RayTraceWorld.java @@ -0,0 +1,47 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.worldWrappers; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; + +import java.util.function.BiFunction; + +public class RayTraceWorld implements BlockGetter { + + private final LevelAccessor template; + private final BiFunction stateGetter; + + public RayTraceWorld(LevelAccessor template, BiFunction stateGetter) { + this.template = template; + this.stateGetter = stateGetter; + } + + @Override + public BlockEntity getBlockEntity(BlockPos pos) { + return template.getBlockEntity(pos); + } + + @Override + public BlockState getBlockState(BlockPos pos) { + return stateGetter.apply(pos, template.getBlockState(pos)); + } + + @Override + public FluidState getFluidState(BlockPos pos) { + return template.getFluidState(pos); + } + + @Override + public int getHeight() { + return template.getHeight(); + } + + @Override + public int getMinBuildHeight() { + return template.getMinBuildHeight(); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/WrappedClientWorld.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/WrappedClientWorld.java new file mode 100644 index 0000000..31ed743 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/WrappedClientWorld.java @@ -0,0 +1,140 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.worldWrappers; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.targeting.TargetingConditions; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.ColorResolver; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.List; + +@OnlyIn(Dist.CLIENT) +@SuppressWarnings("deprecation") +@ParametersAreNonnullByDefault +public class WrappedClientWorld extends ClientLevel { + private static final Minecraft mc = Minecraft.getInstance(); + protected Level world; + + private WrappedClientWorld(Level world) { + super(mc.getConnection(), mc.level.getLevelData(), world.dimension(), world.dimensionTypeRegistration(), + mc.getConnection().serverChunkRadius, mc.level.getServerSimulationDistance(), world.getProfilerSupplier(), + mc.levelRenderer, world.isDebug(), world.getBiomeManager().biomeZoomSeed); + this.world = world; + } + + public static WrappedClientWorld of(Level world) { + return new WrappedClientWorld(world); + } + + @Override + public boolean hasChunkAt(BlockPos pos) { + return world.hasChunkAt(pos); + } + + @Override + public boolean isLoaded(BlockPos pos) { + return world.isLoaded(pos); + } + + @Override + public BlockState getBlockState(BlockPos pos) { + return world.getBlockState(pos); + } + + // FIXME: blockstate#getCollisionShape with WrappedClientWorld gives unreliable + // data (maybe) + + @Override + public int getBrightness(LightLayer type, BlockPos pos) { + return world.getBrightness(type, pos); + } + + @Override + public int getLightEmission(BlockPos pos) { + return world.getLightEmission(pos); + } + + @Override + public FluidState getFluidState(BlockPos pos) { + return world.getFluidState(pos); + } + + @Nullable + @Override + public T getNearestEntity(List p_217361_1_, TargetingConditions p_217361_2_, + @Nullable LivingEntity p_217361_3_, double p_217361_4_, double p_217361_6_, double p_217361_8_) { + return world.getNearestEntity(p_217361_1_, p_217361_2_, p_217361_3_, p_217361_4_, p_217361_6_, p_217361_8_); + } + + @Override + public int getBlockTint(BlockPos p_225525_1_, ColorResolver p_225525_2_) { + return world.getBlockTint(p_225525_1_, p_225525_2_); + } + + // FIXME: Emissive Lighting might not light stuff properly + + @Override + public void addParticle(ParticleOptions p_195594_1_, double p_195594_2_, double p_195594_4_, double p_195594_6_, + double p_195594_8_, double p_195594_10_, double p_195594_12_) { + world.addParticle(p_195594_1_, p_195594_2_, p_195594_4_, p_195594_6_, p_195594_8_, p_195594_10_, p_195594_12_); + } + + @Override + public void addParticle(ParticleOptions p_195590_1_, boolean p_195590_2_, double p_195590_3_, double p_195590_5_, + double p_195590_7_, double p_195590_9_, double p_195590_11_, double p_195590_13_) { + world.addParticle(p_195590_1_, p_195590_2_, p_195590_3_, p_195590_5_, p_195590_7_, p_195590_9_, p_195590_11_, + p_195590_13_); + } + + @Override + public void addAlwaysVisibleParticle(ParticleOptions p_195589_1_, double p_195589_2_, double p_195589_4_, + double p_195589_6_, double p_195589_8_, double p_195589_10_, double p_195589_12_) { + world.addAlwaysVisibleParticle(p_195589_1_, p_195589_2_, p_195589_4_, p_195589_6_, p_195589_8_, p_195589_10_, + p_195589_12_); + } + + @Override + public void addAlwaysVisibleParticle(ParticleOptions p_217404_1_, boolean p_217404_2_, double p_217404_3_, + double p_217404_5_, double p_217404_7_, double p_217404_9_, double p_217404_11_, double p_217404_13_) { + world.addAlwaysVisibleParticle(p_217404_1_, p_217404_2_, p_217404_3_, p_217404_5_, p_217404_7_, p_217404_9_, + p_217404_11_, p_217404_13_); + } + + @Override + public void playLocalSound(double p_184134_1_, double p_184134_3_, double p_184134_5_, SoundEvent p_184134_7_, + SoundSource p_184134_8_, float p_184134_9_, float p_184134_10_, boolean p_184134_11_) { + world.playLocalSound(p_184134_1_, p_184134_3_, p_184134_5_, p_184134_7_, p_184134_8_, p_184134_9_, p_184134_10_, + p_184134_11_); + } + + @Override + public void playSound(@Nullable Player p_184148_1_, double p_184148_2_, double p_184148_4_, double p_184148_6_, + SoundEvent p_184148_8_, SoundSource p_184148_9_, float p_184148_10_, float p_184148_11_) { + world.playSound(p_184148_1_, p_184148_2_, p_184148_4_, p_184148_6_, p_184148_8_, p_184148_9_, p_184148_10_, + p_184148_11_); + } + + @Nullable + @Override + public BlockEntity getBlockEntity(BlockPos p_175625_1_) { + return world.getBlockEntity(p_175625_1_); + } + + public Level getWrappedWorld() { + return world; + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/WrappedServerWorld.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/WrappedServerWorld.java new file mode 100644 index 0000000..9c92219 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/WrappedServerWorld.java @@ -0,0 +1,120 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.worldWrappers; + +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.saveddata.maps.MapItemSavedData; +import net.minecraft.world.level.storage.ServerLevelData; +import net.minecraft.world.ticks.LevelTicks; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Collections; +import java.util.List; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class WrappedServerWorld extends ServerLevel { + + protected ServerLevel world; + + public WrappedServerWorld(ServerLevel world) { + super(world.getServer(), Util.backgroundExecutor(), world.getServer().storageSource, + (ServerLevelData) world.getLevelData(), world.dimension(), + new LevelStem(world.dimensionTypeRegistration(), world.getChunkSource().getGenerator()), + new DummyStatusListener(), world.isDebug(), world.getBiomeManager().biomeZoomSeed, + Collections.emptyList(), false); + this.world = world; + } + + @Override + public float getSunAngle(float p_72826_1_) { + return 0; + } + + @Override + public int getMaxLocalRawBrightness(BlockPos pos) { + return 15; + } + + @Override + public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) { + world.sendBlockUpdated(pos, oldState, newState, flags); + } + + @Override + public LevelTicks getBlockTicks() { + return super.getBlockTicks(); + } + + @Override + public LevelTicks getFluidTicks() { + return super.getFluidTicks(); + } + + @Override + public void levelEvent(Player player, int type, BlockPos pos, int data) {} + + @Override + public List players() { + return Collections.emptyList(); + } + + @Override + public void playSound(Player player, double x, double y, double z, SoundEvent soundIn, SoundSource category, + float volume, float pitch) {} + + @Override + public void playSound(Player p_217384_1_, Entity p_217384_2_, SoundEvent p_217384_3_, SoundSource p_217384_4_, + float p_217384_5_, float p_217384_6_) {} + + @Override + public Entity getEntity(int id) { + return null; + } + + @Override + public MapItemSavedData getMapData(String mapName) { + return null; + } + + @Override + public boolean addFreshEntity(Entity entityIn) { + entityIn.level = world; + return world.addFreshEntity(entityIn); + } + + @Override + public void setMapData(String mapId, MapItemSavedData mapDataIn) {} + + @Override + public int getFreeMapId() { + return 0; + } + + @Override + public void destroyBlockProgress(int breakerId, BlockPos pos, int progress) {} + + @Override + public RecipeManager getRecipeManager() { + return world.getRecipeManager(); + } + + @Override + public Holder getUncachedNoiseBiome(int p_225604_1_, int p_225604_2_, int p_225604_3_) { + return world.getUncachedNoiseBiome(p_225604_1_, p_225604_2_, p_225604_3_); + } + +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/WrappedWorld.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/WrappedWorld.java new file mode 100644 index 0000000..4040b5e --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/WrappedWorld.java @@ -0,0 +1,250 @@ +package nl.requios.effortlessbuilding.create.foundation.utility.worldWrappers; + +import net.minecraft.core.*; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.entity.LevelEntityGetter; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.gameevent.GameEvent.Context; +import net.minecraft.world.level.lighting.LevelLightEngine; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.saveddata.maps.MapItemSavedData; +import net.minecraft.world.level.storage.WritableLevelData; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.ticks.LevelTickAccess; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; + +public class WrappedWorld extends Level { + + protected Level world; + protected ChunkSource chunkSource; + + protected LevelEntityGetter entityGetter = new DummyLevelEntityGetter<>(); + + public WrappedWorld(Level world) { + super((WritableLevelData) world.getLevelData(), world.dimension(), world.dimensionTypeRegistration(), + world::getProfiler, world.isClientSide, world.isDebug(), 0, 0); + this.world = world; + } + + public void setChunkSource(ChunkSource source) { + this.chunkSource = source; + } + + public Level getLevel() { + return world; + } + + @Override + public LevelLightEngine getLightEngine() { + return world.getLightEngine(); + } + + @Override + public BlockState getBlockState(@Nullable BlockPos pos) { + return world.getBlockState(pos); + } + + @Override + public boolean isStateAtPosition(BlockPos p_217375_1_, Predicate p_217375_2_) { + return world.isStateAtPosition(p_217375_1_, p_217375_2_); + } + + @Override + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return world.getBlockEntity(pos); + } + + @Override + public boolean setBlock(BlockPos pos, BlockState newState, int flags) { + return world.setBlock(pos, newState, flags); + } + + @Override + public int getMaxLocalRawBrightness(BlockPos pos) { + return 15; + } + + @Override + public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) { + world.sendBlockUpdated(pos, oldState, newState, flags); + } + + @Override + public LevelTickAccess getBlockTicks() { + return world.getBlockTicks(); + } + + @Override + public LevelTickAccess getFluidTicks() { + return world.getFluidTicks(); + } + + @Override + public ChunkSource getChunkSource() { + return chunkSource != null ? chunkSource : world.getChunkSource(); + } + + @Override + public void levelEvent(@Nullable Player player, int type, BlockPos pos, int data) {} + + @Override + public List players() { + return Collections.emptyList(); + } + + @Override + public void playSeededSound(Player p_220363_, double p_220364_, double p_220365_, double p_220366_, + SoundEvent p_220367_, SoundSource p_220368_, float p_220369_, float p_220370_, long p_220371_) {} + + @Override + public void playSeededSound(Player p_220372_, Entity p_220373_, SoundEvent p_220374_, SoundSource p_220375_, + float p_220376_, float p_220377_, long p_220378_) {} + + @Override + public void playSound(@Nullable Player player, double x, double y, double z, SoundEvent soundIn, + SoundSource category, float volume, float pitch) {} + + @Override + public void playSound(@Nullable Player p_217384_1_, Entity p_217384_2_, SoundEvent p_217384_3_, + SoundSource p_217384_4_, float p_217384_5_, float p_217384_6_) {} + + @Override + public Entity getEntity(int id) { + return null; + } + + @Override + public MapItemSavedData getMapData(String mapName) { + return null; + } + + @Override + public boolean addFreshEntity(Entity entityIn) { + entityIn.level = world; + return world.addFreshEntity(entityIn); + } + + @Override + public void setMapData(String pMapId, MapItemSavedData pData) {} + + @Override + public int getFreeMapId() { + return world.getFreeMapId(); + } + + @Override + public void destroyBlockProgress(int breakerId, BlockPos pos, int progress) {} + + @Override + public Scoreboard getScoreboard() { + return world.getScoreboard(); + } + + @Override + public RecipeManager getRecipeManager() { + return world.getRecipeManager(); + } + + @Override + public Holder getUncachedNoiseBiome(int p_225604_1_, int p_225604_2_, int p_225604_3_) { + return world.getUncachedNoiseBiome(p_225604_1_, p_225604_2_, p_225604_3_); + } + + @Override + public RegistryAccess registryAccess() { + return world.registryAccess(); + } + + @Override + public float getShade(Direction p_230487_1_, boolean p_230487_2_) { + return world.getShade(p_230487_1_, p_230487_2_); + } + + @Override + public void updateNeighbourForOutputSignal(BlockPos p_175666_1_, Block p_175666_2_) {} + + @Override + public void gameEvent(Entity pEntity, GameEvent pEvent, BlockPos pPos) {} + + @Override + public void gameEvent(GameEvent p_220404_, Vec3 p_220405_, Context p_220406_) {} + + @Override + public String gatherChunkSourceStats() { + return world.gatherChunkSourceStats(); + } + + @Override + protected LevelEntityGetter getEntities() { + return entityGetter; + } + + // Intentionally copied from LevelHeightAccessor. Workaround for issues caused + // when other mods (such as Lithium) + // override the vanilla implementations in ways which cause WrappedWorlds to + // return incorrect, default height info. + // WrappedWorld subclasses should implement their own getMinBuildHeight and + // getHeight overrides where they deviate + // from the defaults for their dimension. + + @Override + public int getMaxBuildHeight() { + return this.getMinBuildHeight() + this.getHeight(); + } + + @Override + public int getSectionsCount() { + return this.getMaxSection() - this.getMinSection(); + } + + @Override + public int getMinSection() { + return SectionPos.blockToSectionCoord(this.getMinBuildHeight()); + } + + @Override + public int getMaxSection() { + return SectionPos.blockToSectionCoord(this.getMaxBuildHeight() - 1) + 1; + } + + @Override + public boolean isOutsideBuildHeight(BlockPos pos) { + return this.isOutsideBuildHeight(pos.getY()); + } + + @Override + public boolean isOutsideBuildHeight(int y) { + return y < this.getMinBuildHeight() || y >= this.getMaxBuildHeight(); + } + + @Override + public int getSectionIndex(int y) { + return this.getSectionIndexFromSectionY(SectionPos.blockToSectionCoord(y)); + } + + @Override + public int getSectionIndexFromSectionY(int sectionY) { + return sectionY - this.getMinSection(); + } + + @Override + public int getSectionYFromSectionIndex(int sectionIndex) { + return sectionIndex + this.getMinSection(); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/package-info.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/package-info.java new file mode 100644 index 0000000..bb6a251 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/worldWrappers/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package nl.requios.effortlessbuilding.create.foundation.utility.worldWrappers; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/nl/requios/effortlessbuilding/create/license.txt b/src/main/java/nl/requios/effortlessbuilding/create/license.txt new file mode 100644 index 0000000..86204cf --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/license.txt @@ -0,0 +1,12 @@ +All files within this folder fall under the MIT license. + +The MIT License Copyright (c) Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java b/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java index f3b9d1e..67d3cc3 100644 --- a/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java +++ b/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java @@ -18,6 +18,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.RandomSource; +import nl.requios.effortlessbuilding.ClientEvents; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmode.ModeOptions; import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; @@ -92,7 +93,7 @@ public class RadialMenu extends Screen { public void tick() { super.tick(); - if (!ClientProxy.isKeybindDown(2)) { + if (!ClientEvents.isKeybindDown(2)) { onClose(); } } @@ -397,7 +398,7 @@ public class RadialMenu extends Screen { if (button.action == ActionEnum.OPEN_MODIFIER_SETTINGS) keybindingIndex = 0; if (keybindingIndex != -1) { - KeyMapping keyMap = ClientProxy.keyBindings[keybindingIndex]; + KeyMapping keyMap = ClientEvents.keyBindings[keybindingIndex]; if (!keyMap.getKeyModifier().name().equals("none")) { result = keyMap.getKeyModifier().name() + " "; @@ -409,7 +410,7 @@ public class RadialMenu extends Screen { //Add (ctrl) to first two actions of first option if (button.action == currentBuildMode.options[0].actions[0] || button.action == currentBuildMode.options[0].actions[1]) { - result = I18n.get(ClientProxy.keyBindings[5].getKey().getName()); + result = I18n.get(ClientEvents.keyBindings[5].getKey().getName()); if (result.equals("Left Control")) result = "Ctrl"; } } diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/buildmodifier/ModifierSettingsGui.java b/src/main/java/nl/requios/effortlessbuilding/gui/buildmodifier/ModifierSettingsGui.java index 5a7807c..a0ae3cf 100644 --- a/src/main/java/nl/requios/effortlessbuilding/gui/buildmodifier/ModifierSettingsGui.java +++ b/src/main/java/nl/requios/effortlessbuilding/gui/buildmodifier/ModifierSettingsGui.java @@ -7,6 +7,7 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.network.chat.Component; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; +import nl.requios.effortlessbuilding.ClientEvents; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmodifier.Array; import nl.requios.effortlessbuilding.buildmodifier.Mirror; @@ -87,7 +88,7 @@ public class ModifierSettingsGui extends Screen { @Override public boolean keyPressed(int keyCode, int p_96553_, int p_96554_) { - if (keyCode == ClientProxy.keyBindings[0].getKey().getValue()) { + if (keyCode == ClientEvents.keyBindings[0].getKey().getValue()) { minecraft.player.closeContainer(); return true; } diff --git a/src/main/java/nl/requios/effortlessbuilding/network/RequestLookAtMessage.java b/src/main/java/nl/requios/effortlessbuilding/network/RequestLookAtMessage.java index bd534cd..d7f960b 100644 --- a/src/main/java/nl/requios/effortlessbuilding/network/RequestLookAtMessage.java +++ b/src/main/java/nl/requios/effortlessbuilding/network/RequestLookAtMessage.java @@ -9,6 +9,7 @@ import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.network.NetworkEvent; +import nl.requios.effortlessbuilding.ClientEvents; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.proxy.ClientProxy; import nl.requios.effortlessbuilding.render.BlockPreviewRenderer; @@ -63,8 +64,8 @@ public class RequestLookAtMessage { //Prevent double placing in normal mode with placeStartPos false //Unless QuickReplace is on, then we do need to place start pos. - if (ClientProxy.previousLookAt.getType() == HitResult.Type.BLOCK) { - PacketHandler.INSTANCE.sendToServer(new BlockPlacedMessage((BlockHitResult) ClientProxy.previousLookAt, message.getPlaceStartPos())); + if (ClientEvents.previousLookAt.getType() == HitResult.Type.BLOCK) { + PacketHandler.INSTANCE.sendToServer(new BlockPlacedMessage((BlockHitResult) ClientEvents.previousLookAt, message.getPlaceStartPos())); } else { PacketHandler.INSTANCE.sendToServer(new BlockPlacedMessage()); } diff --git a/src/main/java/nl/requios/effortlessbuilding/proxy/ClientProxy.java b/src/main/java/nl/requios/effortlessbuilding/proxy/ClientProxy.java index 5e5af73..4047301 100644 --- a/src/main/java/nl/requios/effortlessbuilding/proxy/ClientProxy.java +++ b/src/main/java/nl/requios/effortlessbuilding/proxy/ClientProxy.java @@ -1,333 +1,23 @@ package nl.requios.effortlessbuilding.proxy; -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.client.gui.screens.MenuScreens; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.SoundType; import net.minecraft.client.Minecraft; -import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.resources.language.I18n; -import net.minecraft.client.KeyMapping; -import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.InteractionHand; -import net.minecraft.sounds.SoundSource; -import net.minecraft.core.BlockPos; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.level.ClipContext; -import net.minecraft.world.phys.HitResult; -import net.minecraft.world.phys.Vec3; -import net.minecraft.ChatFormatting; -import net.minecraft.world.level.Level; import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.InputEvent; -import net.minecraftforge.client.event.RegisterKeyMappingsEvent; -import net.minecraftforge.client.event.ScreenEvent; -import net.minecraftforge.client.settings.KeyConflictContext; -import net.minecraftforge.client.settings.KeyModifier; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.fml.LogicalSide; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; -import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.network.NetworkEvent; import nl.requios.effortlessbuilding.EffortlessBuilding; -import nl.requios.effortlessbuilding.buildmode.BuildModes; -import nl.requios.effortlessbuilding.buildmode.ModeOptions; -import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; -import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; -import nl.requios.effortlessbuilding.compatibility.CompatHelper; -import nl.requios.effortlessbuilding.gui.DiamondRandomizerBagScreen; -import nl.requios.effortlessbuilding.gui.GoldenRandomizerBagScreen; -import nl.requios.effortlessbuilding.gui.RandomizerBagScreen; -import nl.requios.effortlessbuilding.gui.buildmode.PlayerSettingsGui; -import nl.requios.effortlessbuilding.gui.buildmode.RadialMenu; -import nl.requios.effortlessbuilding.gui.buildmodifier.ModifierSettingsGui; -import nl.requios.effortlessbuilding.helper.ReachHelper; -import nl.requios.effortlessbuilding.network.*; -import org.lwjgl.glfw.GLFW; -import javax.annotation.ParametersAreNonnullByDefault; import java.util.function.Supplier; -@Mod.EventBusSubscriber(value = {Dist.CLIENT}) -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault +@OnlyIn(Dist.CLIENT) public class ClientProxy implements IProxy { - public static KeyMapping[] keyBindings; - public static HitResult previousLookAt; - public static HitResult currentLookAt; - public static int ticksInGame = 0; - private static int placeCooldown = 0; - private static int breakCooldown = 0; - - @Override - public void setup(FMLCommonSetupEvent event) { - } - - @Override - public void clientSetup(FMLClientSetupEvent event) { - //Keybindings are setup and registered in ModClientEventHandler - - MenuScreens.register(EffortlessBuilding.RANDOMIZER_BAG_CONTAINER.get(), RandomizerBagScreen::new); - MenuScreens.register(EffortlessBuilding.GOLDEN_RANDOMIZER_BAG_CONTAINER.get(), GoldenRandomizerBagScreen::new); - MenuScreens.register(EffortlessBuilding.DIAMOND_RANDOMIZER_BAG_CONTAINER.get(), DiamondRandomizerBagScreen::new); - } - - @SubscribeEvent - public static void onClientTick(TickEvent.ClientTickEvent event) { - - if (event.phase == TickEvent.Phase.START) { - onMouseInput(); - - //Update previousLookAt - HitResult objectMouseOver = Minecraft.getInstance().hitResult; - //Checking for null is necessary! Even in vanilla when looking down ladders it is occasionally null (instead of Type MISS) - if (objectMouseOver == null) return; - - if (currentLookAt == null) { - currentLookAt = objectMouseOver; - previousLookAt = objectMouseOver; - return; - } - - if (objectMouseOver.getType() == HitResult.Type.BLOCK) { - if (currentLookAt.getType() != HitResult.Type.BLOCK) { - currentLookAt = objectMouseOver; - previousLookAt = objectMouseOver; - } else { - if (((BlockHitResult) currentLookAt).getBlockPos() != ((BlockHitResult) objectMouseOver).getBlockPos()) { - previousLookAt = currentLookAt; - currentLookAt = objectMouseOver; - } - } - } - } else if (event.phase == TickEvent.Phase.END) { - Screen gui = Minecraft.getInstance().screen; - if (gui == null || !gui.isPauseScreen()) { - ticksInGame++; - } - } - - } - - private static void onMouseInput() { - Minecraft mc = Minecraft.getInstance(); - LocalPlayer player = mc.player; - if (player == null) return; - BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode(); - - if (mc.screen != null || - buildMode == BuildModes.BuildModeEnum.NORMAL || - RadialMenu.instance.isVisible()) { - return; - } - - if (mc.options.keyUse.isDown()) { - - //KeyBinding.setKeyBindState(mc.gameSettings.keyBindUseItem.getKeyCode(), false); - - if (placeCooldown <= 0) { - placeCooldown = 4; - - ItemStack currentItemStack = player.getItemInHand(InteractionHand.MAIN_HAND); - if (currentItemStack.getItem() instanceof BlockItem || - (CompatHelper.isItemBlockProxy(currentItemStack) && !player.isShiftKeyDown())) { - - ItemStack itemStack = CompatHelper.getItemBlockFromStack(currentItemStack); - - //find position in distance - HitResult lookingAt = getLookingAt(player); - if (lookingAt != null && lookingAt.getType() == HitResult.Type.BLOCK) { - BlockHitResult blockLookingAt = (BlockHitResult) lookingAt; - - BuildModes.onBlockPlacedMessage(player, new BlockPlacedMessage(blockLookingAt, true)); - PacketHandler.INSTANCE.sendToServer(new BlockPlacedMessage(blockLookingAt, true)); - - //play sound if further than normal - if ((blockLookingAt.getLocation().subtract(player.getEyePosition(1f))).lengthSqr() > 25f && - itemStack.getItem() instanceof BlockItem) { - - BlockState state = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState(); - BlockPos blockPos = blockLookingAt.getBlockPos(); - SoundType soundType = state.getBlock().getSoundType(state, player.level, blockPos, player); - player.level.playSound(player, player.blockPosition(), soundType.getPlaceSound(), SoundSource.BLOCKS, - 0.4f, soundType.getPitch()); - player.swing(InteractionHand.MAIN_HAND); - } - } else { - BuildModes.onBlockPlacedMessage(player, new BlockPlacedMessage()); - PacketHandler.INSTANCE.sendToServer(new BlockPlacedMessage()); - } - } - } else if (buildMode == BuildModes.BuildModeEnum.NORMAL_PLUS) { - placeCooldown--; - if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) placeCooldown = 0; - } - } else { - placeCooldown = 0; - } - - if (mc.options.keyAttack.isDown()) { - - //Break block in distance in creative (or survival if enabled in config) - if (breakCooldown <= 0) { - breakCooldown = 4; - - HitResult lookingAt = getLookingAt(player); - if (lookingAt != null && lookingAt.getType() == HitResult.Type.BLOCK) { - BlockHitResult blockLookingAt = (BlockHitResult) lookingAt; - - BuildModes.onBlockBrokenMessage(player, new BlockBrokenMessage(blockLookingAt)); - PacketHandler.INSTANCE.sendToServer(new BlockBrokenMessage(blockLookingAt)); - - //play sound if further than normal - if ((blockLookingAt.getLocation().subtract(player.getEyePosition(1f))).lengthSqr() > 25f) { - - BlockPos blockPos = blockLookingAt.getBlockPos(); - BlockState state = player.level.getBlockState(blockPos); - SoundType soundtype = state.getBlock().getSoundType(state, player.level, blockPos, player); - player.level.playSound(player, player.blockPosition(), soundtype.getBreakSound(), SoundSource.BLOCKS, - 0.4f, soundtype.getPitch()); - player.swing(InteractionHand.MAIN_HAND); - } - } else { - BuildModes.onBlockBrokenMessage(player, new BlockBrokenMessage()); - PacketHandler.INSTANCE.sendToServer(new BlockBrokenMessage()); - } - } else if (buildMode == BuildModes.BuildModeEnum.NORMAL_PLUS) { - breakCooldown--; - if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) breakCooldown = 0; - } - - //EffortlessBuilding.packetHandler.sendToServer(new CancelModeMessage()); - - } else { - breakCooldown = 0; - } - } - - @SubscribeEvent(receiveCanceled = true) - public static void onKeyPress(InputEvent.Key event) { - LocalPlayer player = Minecraft.getInstance().player; - if (player == null) - return; - - //Remember to send packet to server if necessary - //Show Modifier Settings GUI - if (keyBindings[0].consumeClick()) { - openModifierSettings(); - } - - //QuickReplace toggle - if (keyBindings[1].consumeClick()) { - ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - modifierSettings.setQuickReplace(!modifierSettings.doQuickReplace()); - EffortlessBuilding.log(player, "Set " + ChatFormatting.GOLD + "Quick Replace " + ChatFormatting.RESET + ( - modifierSettings.doQuickReplace() ? "on" : "off")); - PacketHandler.INSTANCE.sendToServer(new ModifierSettingsMessage(modifierSettings)); - } - - //Radial menu - if (keyBindings[2].isDown()) { - if (ReachHelper.getMaxReach(player) > 0) { - if (!RadialMenu.instance.isVisible()) { - Minecraft.getInstance().setScreen(RadialMenu.instance); - } - } else { - EffortlessBuilding.log(player, "Build modes are disabled until your reach has increased. Increase your reach with craftable reach upgrades."); - } - } - - //Undo (Ctrl+Z) - if (keyBindings[3].consumeClick()) { - ModeOptions.ActionEnum action = ModeOptions.ActionEnum.UNDO; - ModeOptions.performAction(player, action); - PacketHandler.INSTANCE.sendToServer(new ModeActionMessage(action)); - } - - //Redo (Ctrl+Y) - if (keyBindings[4].consumeClick()) { - ModeOptions.ActionEnum action = ModeOptions.ActionEnum.REDO; - ModeOptions.performAction(player, action); - PacketHandler.INSTANCE.sendToServer(new ModeActionMessage(action)); - } - - //Change placement mode - if (keyBindings[5].consumeClick()) { - //Toggle between first two actions of the first option of the current build mode - BuildModes.BuildModeEnum currentBuildMode = ModeSettingsManager.getModeSettings(player).getBuildMode(); - if (currentBuildMode.options.length > 0) { - ModeOptions.OptionEnum option = currentBuildMode.options[0]; - if (option.actions.length >= 2) { - if (ModeOptions.getOptionSetting(option) == option.actions[0]) { - ModeOptions.performAction(player, option.actions[1]); - PacketHandler.INSTANCE.sendToServer(new ModeActionMessage(option.actions[1])); - } else { - ModeOptions.performAction(player, option.actions[0]); - PacketHandler.INSTANCE.sendToServer(new ModeActionMessage(option.actions[0])); - } - } - } - } - - } - - public static void openModifierSettings() { - Minecraft mc = Minecraft.getInstance(); - LocalPlayer player = mc.player; - if (player == null) return; - - //Disabled if max reach is 0, might be set in the config that way. - if (ReachHelper.getMaxReach(player) == 0) { - EffortlessBuilding.log(player, "Build modifiers are disabled until your reach has increased. Increase your reach with craftable reach upgrades."); - } else { - mc.setScreen(new ModifierSettingsGui()); - } - } - - public static void openPlayerSettings() { - Minecraft mc = Minecraft.getInstance(); - mc.setScreen(new PlayerSettingsGui()); - } - - @SubscribeEvent - public static void onGuiOpen(ScreenEvent event) { - Player player = Minecraft.getInstance().player; - if (player != null) { - BuildModes.initializeMode(player); - } - } - - public static boolean isKeybindDown(int keybindIndex) { - return InputConstants.isKeyDown( - Minecraft.getInstance().getWindow().getWindow(), - ClientProxy.keyBindings[2].getKey().getValue()); - } - - public static HitResult getLookingAt(Player player) { - Level world = player.level; - - //base distance off of player ability (config) - float raytraceRange = ReachHelper.getPlacementReach(player); - - Vec3 look = player.getLookAngle(); - Vec3 start = new Vec3(player.getX(), player.getY() + player.getEyeHeight(), player.getZ()); - Vec3 end = new Vec3(player.getX() + look.x * raytraceRange, player.getY() + player.getEyeHeight() + look.y * raytraceRange, player.getZ() + look.z * raytraceRange); -// return player.rayTrace(raytraceRange, 1f, RayTraceFluidMode.NEVER); - //TODO 1.14 check if correct - return world.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player)); - } public Player getPlayerEntityFromContext(Supplier ctx) { return (ctx.get().getDirection().getReceptionSide() == LogicalSide.CLIENT ? Minecraft.getInstance().player : ctx.get().getSender()); } - @Override public void logTranslate(Player player, String prefix, String translationKey, String suffix, boolean actionBar) { EffortlessBuilding.log(Minecraft.getInstance().player, prefix + I18n.get(translationKey) + suffix, actionBar); } diff --git a/src/main/java/nl/requios/effortlessbuilding/proxy/IProxy.java b/src/main/java/nl/requios/effortlessbuilding/proxy/IProxy.java index e898373..189e1ff 100644 --- a/src/main/java/nl/requios/effortlessbuilding/proxy/IProxy.java +++ b/src/main/java/nl/requios/effortlessbuilding/proxy/IProxy.java @@ -1,17 +1,11 @@ package nl.requios.effortlessbuilding.proxy; import net.minecraft.world.entity.player.Player; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; -import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public interface IProxy { - void setup(final FMLCommonSetupEvent event); - - void clientSetup(final FMLClientSetupEvent event); - Player getPlayerEntityFromContext(Supplier ctx); void logTranslate(Player player, String prefix, String translationKey, String suffix, boolean actionBar); diff --git a/src/main/java/nl/requios/effortlessbuilding/proxy/ServerProxy.java b/src/main/java/nl/requios/effortlessbuilding/proxy/ServerProxy.java index 6c47894..71f6024 100644 --- a/src/main/java/nl/requios/effortlessbuilding/proxy/ServerProxy.java +++ b/src/main/java/nl/requios/effortlessbuilding/proxy/ServerProxy.java @@ -1,9 +1,7 @@ package nl.requios.effortlessbuilding.proxy; -import net.minecraft.world.entity.player.Player; import net.minecraft.server.level.ServerPlayer; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; -import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraft.world.entity.player.Player; import net.minecraftforge.network.NetworkEvent; import net.minecraftforge.network.PacketDistributor; import nl.requios.effortlessbuilding.network.PacketHandler; @@ -13,20 +11,11 @@ import java.util.function.Supplier; public class ServerProxy implements IProxy { //Only physical server! Singleplayer server is seen as clientproxy - @Override - public void setup(FMLCommonSetupEvent event) { - - } - - @Override - public void clientSetup(FMLClientSetupEvent event) { - } public Player getPlayerEntityFromContext(Supplier ctx) { return ctx.get().getSender(); } - @Override public void logTranslate(Player player, String prefix, String translationKey, String suffix, boolean actionBar) { PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new TranslatedLogMessage(prefix, translationKey, suffix, actionBar)); } diff --git a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java index 9122648..641cf36 100644 --- a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java +++ b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java @@ -21,7 +21,9 @@ import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import nl.requios.effortlessbuilding.BuildConfig; +import nl.requios.effortlessbuilding.ClientEvents; import nl.requios.effortlessbuilding.EffortlessBuilding; +import nl.requios.effortlessbuilding.EffortlessBuildingClient; import nl.requios.effortlessbuilding.buildmode.BuildModes; import nl.requios.effortlessbuilding.buildmode.IBuildMode; import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; @@ -59,7 +61,7 @@ public class BlockPreviewRenderer { if (placed.coordinates != null && !placed.coordinates.isEmpty()) { double totalTime = Mth.clampedLerp(30, 60, placed.firstPos.distSqr(placed.secondPos) / 100.0) * BuildConfig.visuals.dissolveTimeMultiplier.get(); - float dissolve = (ClientProxy.ticksInGame - placed.time) / (float) totalTime; + float dissolve = (ClientEvents.ticksInGame - placed.time) / (float) totalTime; renderBlockPreviews(matrixStack, renderTypeBuffer, placed.coordinates, placed.blockStates, placed.itemStacks, dissolve, placed.firstPos, placed.secondPos, false, placed.breaking); } } @@ -67,11 +69,11 @@ public class BlockPreviewRenderer { //Expire placedDataList.removeIf(placed -> { double totalTime = Mth.clampedLerp(30, 60, placed.firstPos.distSqr(placed.secondPos) / 100.0) * BuildConfig.visuals.dissolveTimeMultiplier.get(); - return placed.time + totalTime < ClientProxy.ticksInGame; + return placed.time + totalTime < ClientEvents.ticksInGame; }); //Render block previews - HitResult lookingAt = ClientProxy.getLookingAt(player); + HitResult lookingAt = ClientEvents.getLookingAt(player); if (modeSettings.getBuildMode() == BuildModes.BuildModeEnum.NORMAL) lookingAt = Minecraft.getInstance().hitResult; @@ -170,8 +172,8 @@ public class BlockPreviewRenderer { //if so, renew randomness of randomizer bag AbstractRandomizerBagItem.renewRandomness(); //and play sound (max once every tick) - if (newCoordinates.size() > 1 && blockStates.size() > 1 && soundTime < ClientProxy.ticksInGame - 0) { - soundTime = ClientProxy.ticksInGame; + if (newCoordinates.size() > 1 && blockStates.size() > 1 && soundTime < ClientEvents.ticksInGame - 0) { + soundTime = ClientEvents.ticksInGame; if (blockStates.get(0) != null) { SoundType soundType = blockStates.get(0).getBlock().getSoundType(blockStates.get(0), player.level, @@ -317,7 +319,7 @@ public class BlockPreviewRenderer { if (!coordinates.isEmpty() && blockStates.size() == coordinates.size() && coordinates.size() > 1 && coordinates.size() < BuildConfig.visuals.shaderThreshold.get()) { - placedDataList.add(new PlacedData(ClientProxy.ticksInGame, coordinates, blockStates, + placedDataList.add(new PlacedData(ClientEvents.ticksInGame, coordinates, blockStates, itemStacks, firstPos, secondPos, false)); } } @@ -343,7 +345,7 @@ public class BlockPreviewRenderer { sortOnDistanceToPlayer(coordinates, player); - placedDataList.add(new PlacedData(ClientProxy.ticksInGame, coordinates, blockStates, + placedDataList.add(new PlacedData(ClientEvents.ticksInGame, coordinates, blockStates, itemStacks, firstPos, secondPos, true)); } } diff --git a/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java b/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java index 4bbcd7b..ec8d990 100644 --- a/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java +++ b/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java @@ -1,5 +1,6 @@ package nl.requios.effortlessbuilding.render; +import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; @@ -23,44 +24,54 @@ import net.minecraftforge.client.event.RenderLevelStageEvent; import net.minecraftforge.client.model.data.ModelData; import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; import nl.requios.effortlessbuilding.EffortlessBuilding; +import nl.requios.effortlessbuilding.EffortlessBuildingClient; import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; +import nl.requios.effortlessbuilding.create.CreateClient; +import nl.requios.effortlessbuilding.create.foundation.render.SuperRenderTypeBuffer; +import nl.requios.effortlessbuilding.create.foundation.utility.AnimationTickHolder; import static net.minecraftforge.client.event.RenderLevelStageEvent.Stage.AFTER_PARTICLES; /*** * Main render class for Effortless Building */ -@Mod.EventBusSubscriber(value = Dist.CLIENT) +@EventBusSubscriber(Dist.CLIENT) public class RenderHandler { @SubscribeEvent public static void onRender(RenderLevelStageEvent event) { - if (event.getPhase() != EventPriority.NORMAL || event.getStage() != AFTER_PARTICLES) - return; + Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); +// float pt = AnimationTickHolder.getPartialTicks(); - PoseStack matrixStack = event.getPoseStack(); + PoseStack ms = event.getPoseStack(); BufferBuilder bufferBuilder = Tesselator.getInstance().getBuilder(); - MultiBufferSource.BufferSource renderTypeBuffer = MultiBufferSource.immediate(bufferBuilder); + MultiBufferSource.BufferSource buffer = MultiBufferSource.immediate(bufferBuilder); + +// SuperRenderTypeBuffer superBuffer = SuperRenderTypeBuffer.getInstance(); Player player = Minecraft.getInstance().player; ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - Vec3 projectedView = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); - - matrixStack.pushPose(); - matrixStack.translate(-projectedView.x, -projectedView.y, -projectedView.z); + ms.pushPose(); + ms.translate(-cameraPos.x, -cameraPos.y, -cameraPos.z); //Mirror and radial mirror lines and areas - ModifierRenderer.render(matrixStack, renderTypeBuffer, modifierSettings); + ModifierRenderer.render(ms, buffer, modifierSettings); //Render block previews - BlockPreviewRenderer.render(matrixStack, renderTypeBuffer, player, modifierSettings, modeSettings); + BlockPreviewRenderer.render(ms, buffer, player, modifierSettings, modeSettings); - matrixStack.popPose(); + //Create +// CreateClient.GHOST_BLOCKS.renderAll(ms, superBuffer); +// EffortlessBuildingClient.OUTLINER.renderOutlines(ms, superBuffer, pt); +// superBuffer.draw(); +// RenderSystem.enableCull(); + + ms.popPose(); } protected static VertexConsumer beginLines(MultiBufferSource.BufferSource renderTypeBuffer) { diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 6569222..2752a32 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -1,3 +1,49 @@ public net.minecraft.client.gui.components.AbstractSelectionList f_93398_ # renderHeader public net.minecraft.client.gui.Font m_92803_(Ljava/lang/String;FFILcom/mojang/math/Matrix4f;ZZ)I # renderString -public net.minecraft.world.inventory.MenuType m_39988_(Ljava/lang/String;Lnet/minecraft/world/inventory/MenuType$MenuSupplier;)Lnet/minecraft/world/inventory/MenuType; # register \ No newline at end of file +public net.minecraft.world.inventory.MenuType m_39988_(Ljava/lang/String;Lnet/minecraft/world/inventory/MenuType$MenuSupplier;)Lnet/minecraft/world/inventory/MenuType; # register + +#create +public net.minecraft.client.Minecraft f_91013_ # pausePartialTick +public net.minecraft.client.gui.Font m_92863_(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/gui/font/FontSet; # getFontSet +public net.minecraft.client.gui.screens.TitleScreen f_96729_ # panorama +public net.minecraft.client.multiplayer.ClientPacketListener f_104897_ # serverChunkRadius +protected net.minecraft.client.particle.Particle f_107205_ # stoppedByCollision +public net.minecraft.client.renderer.ItemInHandRenderer f_109300_ # mainHandItem +public net.minecraft.client.renderer.ItemInHandRenderer f_109301_ # offHandItem +public net.minecraft.client.renderer.entity.ItemRenderer f_115096_ # textureManager + +public-f net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket f_132663_ # flyingSpeed + +public net.minecraft.server.MinecraftServer f_129744_ # storageSource +public net.minecraft.server.network.ServerGamePacketListenerImpl f_9737_ # aboveGroundTickCount +public net.minecraft.server.network.ServerGamePacketListenerImpl f_9739_ # aboveGroundVehicleTickCount + +public net.minecraft.world.entity.Entity f_146795_ # removalReason +protected net.minecraft.world.entity.Entity m_19956_(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/entity/Entity$MoveFunction;)V # positionRider +public net.minecraft.world.entity.LivingEntity f_20899_ # jumping + +public-f net.minecraft.world.item.HoneycombItem f_150863_ # WAXABLES +public net.minecraft.world.item.alchemy.PotionBrewing f_43494_ # POTION_MIXES +public net.minecraft.world.item.alchemy.PotionBrewing f_43495_ # CONTAINER_MIXES +public net.minecraft.world.item.alchemy.PotionBrewing f_43497_ # ALLOWED_CONTAINER +public net.minecraft.world.item.crafting.Ingredient f_43902_ # values +public net.minecraft.world.item.crafting.RecipeManager f_44007_ # recipes + +public net.minecraft.world.level.BaseSpawner f_45443_ # spawnPotentials +public net.minecraft.world.level.BaseSpawner f_45444_ # nextSpawnData +public net.minecraft.world.level.biome.BiomeManager f_47863_ # biomeZoomSeed +public net.minecraft.world.level.block.entity.BeaconBlockEntity f_58648_ # beamSections +public net.minecraft.world.level.chunk.HashMapPalette f_62658_ # values +public net.minecraft.world.level.chunk.PaletteResize + +public net.minecraft.client.model.geom.ModelPart f_104212_ # cubes +public net.minecraft.client.model.geom.ModelPart f_104213_ # children +public net.minecraft.client.model.AgeableListModel f_102007_ # scaleHead +public net.minecraft.client.model.AgeableListModel f_170338_ # babyYHeadOffset +public net.minecraft.client.model.AgeableListModel f_170339_ # babyZHeadOffset +public net.minecraft.client.model.AgeableListModel f_102010_ # babyHeadScale +public net.minecraft.client.model.AgeableListModel f_102011_ # babyBodyScale +public net.minecraft.client.model.AgeableListModel f_102012_ # bodyYOffset + +public net.minecraft.client.gui.components.CommandSuggestions f_93866_ # suggestions +public net.minecraft.client.gui.components.CommandSuggestions$SuggestionsList (Lnet/minecraft/client/gui/components/CommandSuggestions;IIILjava/util/List;Z)V # \ No newline at end of file From c2d413fad0e6b8e9d29c8a93ea40a779aa966c14 Mon Sep 17 00:00:00 2001 From: Christian Knaapen Date: Sun, 15 Jan 2023 23:16:22 +0100 Subject: [PATCH 2/5] Added missing resources. Modified mods.toml. Fixed crash and double rendering of modifiers. --- build.gradle | 12 +++++- .../EffortlessBuilding.java | 2 +- .../render/RenderHandler.java | 11 +----- src/main/resources/META-INF/mods.toml | 13 +++++-- .../shaders/core/glowing_shader.fsh | 24 ++++++++++++ .../shaders/core/glowing_shader.json | 31 +++++++++++++++ .../shaders/core/glowing_shader.vsh | 36 ++++++++++++++++++ .../textures/special/blank.png | Bin 0 -> 99 bytes .../textures/special/checkerboard.png | Bin 0 -> 120 bytes .../textures/special/cutout_checkerboard.png | Bin 0 -> 121 bytes .../textures/special/glue.png | Bin 0 -> 223 bytes .../special/highlighted_checkerboard.png | Bin 0 -> 120 bytes .../textures/special/selection.png | Bin 0 -> 147 bytes .../textures/special/thin_checkerboard.png | Bin 0 -> 121 bytes 14 files changed, 114 insertions(+), 15 deletions(-) create mode 100644 src/main/resources/assets/effortlessbuilding/shaders/core/glowing_shader.fsh create mode 100644 src/main/resources/assets/effortlessbuilding/shaders/core/glowing_shader.json create mode 100644 src/main/resources/assets/effortlessbuilding/shaders/core/glowing_shader.vsh create mode 100644 src/main/resources/assets/effortlessbuilding/textures/special/blank.png create mode 100644 src/main/resources/assets/effortlessbuilding/textures/special/checkerboard.png create mode 100644 src/main/resources/assets/effortlessbuilding/textures/special/cutout_checkerboard.png create mode 100644 src/main/resources/assets/effortlessbuilding/textures/special/glue.png create mode 100644 src/main/resources/assets/effortlessbuilding/textures/special/highlighted_checkerboard.png create mode 100644 src/main/resources/assets/effortlessbuilding/textures/special/selection.png create mode 100644 src/main/resources/assets/effortlessbuilding/textures/special/thin_checkerboard.png diff --git a/build.gradle b/build.gradle index ce6f92c..043d4d5 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { id 'net.minecraftforge.gradle' version '5.1.+' } -version = '1.19-2.38' +version = '1.19-2.40' group = 'nl.requios.effortlessbuilding' archivesBaseName = 'effortlessbuilding' @@ -36,7 +36,11 @@ minecraft { // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. property 'forge.enabledGameTestNamespaces', 'effortlessbuilding' - + + // Flywheel + property 'mixin.env.remapRefMap', 'true' + property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" + mods { effortlessbuilding { source sourceSets.main @@ -53,6 +57,10 @@ minecraft { property 'forge.enabledGameTestNamespaces', 'effortlessbuilding' + // Flywheel + property 'mixin.env.remapRefMap', 'true' + property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" + mods { effortlessbuilding { source sourceSets.main diff --git a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java index 3dd7e3f..7d3db47 100644 --- a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java +++ b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java @@ -63,7 +63,7 @@ public class EffortlessBuilding { modEventBus.addListener(EffortlessBuilding::setup); - DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> () -> EffortlessBuildingClient.onConstructorClient(modEventBus, forgeEventBus)); + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> EffortlessBuildingClient.onConstructorClient(modEventBus, forgeEventBus)); ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus()); CONTAINERS.register(FMLJavaModLoadingContext.get().getModEventBus()); diff --git a/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java b/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java index ec8d990..d557a00 100644 --- a/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java +++ b/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java @@ -1,6 +1,5 @@ package nl.requios.effortlessbuilding.render; -import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; @@ -20,20 +19,14 @@ import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.RenderLevelLastEvent; import net.minecraftforge.client.event.RenderLevelStageEvent; import net.minecraftforge.client.model.data.ModelData; -import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod.EventBusSubscriber; import nl.requios.effortlessbuilding.EffortlessBuilding; -import nl.requios.effortlessbuilding.EffortlessBuildingClient; import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; -import nl.requios.effortlessbuilding.create.CreateClient; -import nl.requios.effortlessbuilding.create.foundation.render.SuperRenderTypeBuffer; -import nl.requios.effortlessbuilding.create.foundation.utility.AnimationTickHolder; - -import static net.minecraftforge.client.event.RenderLevelStageEvent.Stage.AFTER_PARTICLES; /*** * Main render class for Effortless Building @@ -42,7 +35,7 @@ import static net.minecraftforge.client.event.RenderLevelStageEvent.Stage.AFTER_ public class RenderHandler { @SubscribeEvent - public static void onRender(RenderLevelStageEvent event) { + public static void onRender(RenderLevelLastEvent event) { Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); // float pt = AnimationTickHolder.getPartialTicks(); diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 37db863..d7d8f41 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -20,13 +20,20 @@ Makes building easier by providing tools like mirrors, arrays, build modes and a [[dependencies.effortlessbuilding]] modId="forge" mandatory=true - versionRange="[41,)" + versionRange="[43.0.0,)" ordering="NONE" side="BOTH" [[dependencies.effortlessbuilding]] modId="minecraft" mandatory=true - versionRange="[1.18.1,1.20)" + versionRange="[1.19.1,1.20)" ordering="NONE" - side="BOTH" \ No newline at end of file + side="BOTH" + +[[dependencies.effortlessbuilding]] + modId="flywheel" + mandatory=true + versionRange="[0.6.8,0.6.9)" + ordering="AFTER" + side="CLIENT" \ No newline at end of file diff --git a/src/main/resources/assets/effortlessbuilding/shaders/core/glowing_shader.fsh b/src/main/resources/assets/effortlessbuilding/shaders/core/glowing_shader.fsh new file mode 100644 index 0000000..ff27aab --- /dev/null +++ b/src/main/resources/assets/effortlessbuilding/shaders/core/glowing_shader.fsh @@ -0,0 +1,24 @@ +#version 150 + +#moj_import + +uniform sampler2D Sampler0; + +uniform vec4 ColorModulator; +uniform float FogStart; +uniform float FogEnd; +uniform vec4 FogColor; + +in float vertexDistance; +in vec4 vertexColor; +in vec4 lightMapColor; +in vec2 texCoord0; +in vec4 normal; + +out vec4 fragColor; + +void main() { + vec4 color = texture(Sampler0, texCoord0) * vertexColor * ColorModulator; + color *= lightMapColor; + fragColor = linear_fog(color, vertexDistance, FogStart, FogEnd, FogColor); +} diff --git a/src/main/resources/assets/effortlessbuilding/shaders/core/glowing_shader.json b/src/main/resources/assets/effortlessbuilding/shaders/core/glowing_shader.json new file mode 100644 index 0000000..52cfb6d --- /dev/null +++ b/src/main/resources/assets/effortlessbuilding/shaders/core/glowing_shader.json @@ -0,0 +1,31 @@ +{ + "blend": { + "func": "add", + "srcrgb": "srcalpha", + "dstrgb": "1-srcalpha" + }, + "vertex": "effortlessbuilding:glowing_shader", + "fragment": "effortlessbuilding:glowing_shader", + "attributes": [ + "Position", + "Color", + "UV0", + "UV1", + "UV2", + "Normal" + ], + "samplers": [ + { "name": "Sampler0" }, + { "name": "Sampler2" } + ], + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "IViewRotMat", "type": "matrix3x3", "count": 9, "values": [ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }, + { "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] }, + { "name": "FogShape", "type": "int", "count": 1, "values": [ 0 ] } + ] +} diff --git a/src/main/resources/assets/effortlessbuilding/shaders/core/glowing_shader.vsh b/src/main/resources/assets/effortlessbuilding/shaders/core/glowing_shader.vsh new file mode 100644 index 0000000..b47f08e --- /dev/null +++ b/src/main/resources/assets/effortlessbuilding/shaders/core/glowing_shader.vsh @@ -0,0 +1,36 @@ +#version 150 + +#moj_import + +in vec3 Position; +in vec4 Color; +in vec2 UV0; +in vec2 UV1; +in ivec2 UV2; +in vec3 Normal; + +uniform sampler2D Sampler1; +uniform sampler2D Sampler2; + +uniform mat4 ModelViewMat; +uniform mat4 ProjMat; +uniform mat3 IViewRotMat; +uniform int FogShape; + +out float vertexDistance; +out vec4 vertexColor; +out vec4 lightMapColor; +out vec2 texCoord0; +out vec2 texCoord1; +out vec4 normal; + +void main() { + gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0); + + vertexDistance = fog_distance(ModelViewMat, IViewRotMat * Position, FogShape); + vertexColor = Color; + lightMapColor = texelFetch(Sampler2, UV2 / 16, 0); + texCoord0 = UV0; + texCoord1 = UV1; + normal = ProjMat * ModelViewMat * vec4(Normal, 0.0); +} diff --git a/src/main/resources/assets/effortlessbuilding/textures/special/blank.png b/src/main/resources/assets/effortlessbuilding/textures/special/blank.png new file mode 100644 index 0000000000000000000000000000000000000000..2e084d97d231ce0b73c9fdfbf8e6c811997b0f48 GIT binary patch literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!D3?x-;bCrM;OS+@4BLl<6e(pbstUx|zopr0JkI^tN;K2 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/effortlessbuilding/textures/special/glue.png b/src/main/resources/assets/effortlessbuilding/textures/special/glue.png new file mode 100644 index 0000000000000000000000000000000000000000..f5a28c191b1d21e61d08e004adad49bd6b669f7a GIT binary patch literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPHVEbxddW?^9T5Zxc-NOfB%3?k=3#XfKsd_L4Lsuj521? zQhrJ&fdU?$E{-7;jI0M8MVk#695(ugGtOq_6FtDlSS#><*0d*Uq!xAE_^b9%CZlmR zQ>gInRkx#`uFN{RZrO#8sy4b(57w%^(OU89aA>muzvx3DHio+W{2zBd(O(9XV(@hJ Kb6Mw<&;$VN$0Pax literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/effortlessbuilding/textures/special/selection.png b/src/main/resources/assets/effortlessbuilding/textures/special/selection.png new file mode 100644 index 0000000000000000000000000000000000000000..177a3445d07b3c0ee994f0b5e093e7d257c909f3 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9Ea{HEjtmSN`?>!lvI6-$0X`wF zK>Gjx|DQg6diCnnImPfgAfK@$$S;_|;n|HeAV<~H#WAGfR`QMmC5@el+yX`~m=u|} n10l0;zywCc7YrgQVX_P> Date: Mon, 16 Jan 2023 01:53:09 +0100 Subject: [PATCH 3/5] Using Create outlines and ghost blocks for block previews. Fixed item names. --- .../create/CreateClientTest.java | 31 ++++++ .../create/events/ClientEvents.java | 4 +- .../utility/ghost/GhostBlockRenderer.java | 4 +- .../item/DiamondRandomizerBagItem.java | 2 +- .../item/GoldenRandomizerBagItem.java | 2 +- .../item/RandomizerBagItem.java | 2 +- .../render/BlockPreviewRenderer.java | 104 +++++++++++------- .../render/RenderHandler.java | 78 ++++++++----- .../assets/effortlessbuilding/lang/en_us.json | 12 +- 9 files changed, 158 insertions(+), 81 deletions(-) create mode 100644 src/main/java/nl/requios/effortlessbuilding/create/CreateClientTest.java diff --git a/src/main/java/nl/requios/effortlessbuilding/create/CreateClientTest.java b/src/main/java/nl/requios/effortlessbuilding/create/CreateClientTest.java new file mode 100644 index 0000000..2f4f2c4 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/create/CreateClientTest.java @@ -0,0 +1,31 @@ +package nl.requios.effortlessbuilding.create; + +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.phys.AABB; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import nl.requios.effortlessbuilding.create.foundation.utility.Color; + +@Mod.EventBusSubscriber(Dist.CLIENT) +public class CreateClientTest { + + @SubscribeEvent + public static void onTick(TickEvent.ClientTickEvent event) { + CreateClient.GHOST_BLOCKS.showGhostState(1, Blocks.SPRUCE_LOG.defaultBlockState()) + .at(0, 120, 0) + .breathingAlpha(); + CreateClient.GHOST_BLOCKS.showGhostState(2, Blocks.SPRUCE_LOG.defaultBlockState()) + .at(1, 120, 0) + .breathingAlpha(); + + CreateClient.OUTLINER.showAABB(1, new AABB(0, 0, 0, 10, 2, 6) + .move(10, 120, 0)) + .withFaceTexture(AllSpecialTextures.CHECKERED) + .colored(new Color(0.11f, 0.49f, 0.7f, 1f)) +// .colored(0xbfbfbf) + .disableNormals() + .lineWidth(1 / 32f); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/create/events/ClientEvents.java b/src/main/java/nl/requios/effortlessbuilding/create/events/ClientEvents.java index 0a13b2c..c849e88 100644 --- a/src/main/java/nl/requios/effortlessbuilding/create/events/ClientEvents.java +++ b/src/main/java/nl/requios/effortlessbuilding/create/events/ClientEvents.java @@ -35,8 +35,8 @@ public class ClientEvents { Level world = Minecraft.getInstance().level; AnimationTickHolder.tick(); - CreateClient.OUTLINER.tickOutlines(); CreateClient.GHOST_BLOCKS.tickGhosts(); + CreateClient.OUTLINER.tickOutlines(); CameraAngleAnimationService.tick(); } @@ -89,7 +89,7 @@ public class ClientEvents { event.setPitch(CameraAngleAnimationService.getPitch(partialTicks)); } - protected static boolean isGameActive() { + public static boolean isGameActive() { return !(Minecraft.getInstance().level == null || Minecraft.getInstance().player == null); } diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockRenderer.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockRenderer.java index a27b869..1fa167b 100644 --- a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockRenderer.java +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockRenderer.java @@ -75,7 +75,7 @@ public abstract class GhostBlockRenderer { BlockState state = params.state; BlockPos pos = params.pos; - float alpha = params.alphaSupplier.get() * .75f/* * PlacementHelpers.getCurrentAlpha()*/; + float alpha = params.alphaSupplier.get()/* * .75f* PlacementHelpers.getCurrentAlpha()*/; BakedModel model = dispatcher.getBlockModel(state); RenderType layer = RenderType.translucent(); @@ -88,7 +88,7 @@ public abstract class GhostBlockRenderer { ms.scale(.85f, .85f, .85f); ms.translate(-.5, -.5, -.5); - renderModel(ms.last(), vb, state, model, 1f, 1f, 1f, alpha, + renderModel(ms.last(), vb, state, model, 0f, 1f, 1f, alpha, LevelRenderer.getLightColor(mc.level, pos), OverlayTexture.NO_OVERLAY, ModelUtil.VIRTUAL_DATA, layer); diff --git a/src/main/java/nl/requios/effortlessbuilding/item/DiamondRandomizerBagItem.java b/src/main/java/nl/requios/effortlessbuilding/item/DiamondRandomizerBagItem.java index 069a4ce..b7620b9 100644 --- a/src/main/java/nl/requios/effortlessbuilding/item/DiamondRandomizerBagItem.java +++ b/src/main/java/nl/requios/effortlessbuilding/item/DiamondRandomizerBagItem.java @@ -33,7 +33,7 @@ public class DiamondRandomizerBagItem extends AbstractRandomizerBagItem{ @Override public Component getDisplayName() { - return Component.translatable("effortlessbuilding:diamond_randomizer_bag"); + return Component.translatable("item.effortlessbuilding.diamond_randomizer_bag"); } @Nullable diff --git a/src/main/java/nl/requios/effortlessbuilding/item/GoldenRandomizerBagItem.java b/src/main/java/nl/requios/effortlessbuilding/item/GoldenRandomizerBagItem.java index 1d05c7d..d853f62 100644 --- a/src/main/java/nl/requios/effortlessbuilding/item/GoldenRandomizerBagItem.java +++ b/src/main/java/nl/requios/effortlessbuilding/item/GoldenRandomizerBagItem.java @@ -33,7 +33,7 @@ public class GoldenRandomizerBagItem extends AbstractRandomizerBagItem{ @Override public Component getDisplayName() { - return Component.translatable("effortlessbuilding:golden_randomizer_bag"); + return Component.translatable("item.effortlessbuilding.golden_randomizer_bag"); } @Nullable diff --git a/src/main/java/nl/requios/effortlessbuilding/item/RandomizerBagItem.java b/src/main/java/nl/requios/effortlessbuilding/item/RandomizerBagItem.java index ac41deb..06c7687 100644 --- a/src/main/java/nl/requios/effortlessbuilding/item/RandomizerBagItem.java +++ b/src/main/java/nl/requios/effortlessbuilding/item/RandomizerBagItem.java @@ -33,7 +33,7 @@ public class RandomizerBagItem extends AbstractRandomizerBagItem { @Override public Component getDisplayName() { - return Component.translatable("effortlessbuilding:randomizer_bag"); + return Component.translatable("item.effortlessbuilding.randomizer_bag"); } @Nullable diff --git a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java index 641cf36..99284be 100644 --- a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java +++ b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java @@ -32,6 +32,9 @@ import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager.ModifierSettings; import nl.requios.effortlessbuilding.compatibility.CompatHelper; +import nl.requios.effortlessbuilding.create.AllSpecialTextures; +import nl.requios.effortlessbuilding.create.CreateClient; +import nl.requios.effortlessbuilding.create.foundation.utility.Color; import nl.requios.effortlessbuilding.helper.ReachHelper; import nl.requios.effortlessbuilding.helper.SurvivalHelper; import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem; @@ -51,7 +54,7 @@ public class BlockPreviewRenderer { private static BlockPos previousSecondPos; private static int soundTime = 0; - public static void render(PoseStack matrixStack, MultiBufferSource.BufferSource renderTypeBuffer, Player player, ModifierSettings modifierSettings, ModeSettings modeSettings) { + public static void render(PoseStack ms, MultiBufferSource.BufferSource buffer, Player player, ModifierSettings modifierSettings, ModeSettings modeSettings) { //Render placed blocks with dissolve effect //Use fancy shader if config allows, otherwise no dissolve @@ -62,7 +65,7 @@ public class BlockPreviewRenderer { double totalTime = Mth.clampedLerp(30, 60, placed.firstPos.distSqr(placed.secondPos) / 100.0) * BuildConfig.visuals.dissolveTimeMultiplier.get(); float dissolve = (ClientEvents.ticksInGame - placed.time) / (float) totalTime; - renderBlockPreviews(matrixStack, renderTypeBuffer, placed.coordinates, placed.blockStates, placed.itemStacks, dissolve, placed.firstPos, placed.secondPos, false, placed.breaking); + renderBlockPreviews(ms, buffer, placed.coordinates, placed.blockStates, placed.itemStacks, dissolve, placed.firstPos, placed.secondPos, false, placed.breaking); } } } @@ -188,22 +191,45 @@ public class BlockPreviewRenderer { if (blockStates.size() != 0 && newCoordinates.size() == blockStates.size()) { int blockCount; - //Use fancy shader if config allows, otherwise outlines - if (BuildConfig.visuals.useShaders.get() && newCoordinates.size() < BuildConfig.visuals.shaderThreshold.get()) { - blockCount = renderBlockPreviews(matrixStack, renderTypeBuffer, newCoordinates, blockStates, itemStacks, 0f, firstPos, secondPos, !breaking, breaking); - } else { - VertexConsumer buffer = RenderHandler.beginLines(renderTypeBuffer); + if (!breaking) { + //Use fancy shader if config allows, otherwise outlines + if (BuildConfig.visuals.useShaders.get() && newCoordinates.size() < BuildConfig.visuals.shaderThreshold.get()) { + blockCount = renderBlockPreviews(ms, buffer, newCoordinates, blockStates, itemStacks, 0f, firstPos, secondPos, !breaking, breaking); - Vec3 color = new Vec3(1f, 1f, 1f); - if (breaking) color = new Vec3(1f, 0f, 0f); + CreateClient.OUTLINER.showCluster("blockpreviews", newCoordinates) + .withFaceTexture(AllSpecialTextures.CHECKERED) + .disableNormals() + .lineWidth(1 / 32f) + .colored(new Color(1f, 1f, 1f, 1f)); + } else { +// VertexConsumer vc = RenderHandler.beginLines(buffer); +// +// Vec3 color = new Vec3(1f, 1f, 1f); +// if (breaking) color = new Vec3(1f, 0f, 0f); +// +// for (int i = newCoordinates.size() - 1; i >= 0; i--) { +// VoxelShape collisionShape = blockStates.get(i).getCollisionShape(player.level, newCoordinates.get(i)); +// RenderHandler.renderBlockOutline(ms, vc, newCoordinates.get(i), collisionShape, color); +// } +// +// RenderHandler.endLines(buffer); - for (int i = newCoordinates.size() - 1; i >= 0; i--) { - VoxelShape collisionShape = blockStates.get(i).getCollisionShape(player.level, newCoordinates.get(i)); - RenderHandler.renderBlockOutline(matrixStack, buffer, newCoordinates.get(i), collisionShape, color); + CreateClient.OUTLINER.showCluster("blockpreviews", newCoordinates) + .withFaceTexture(AllSpecialTextures.HIGHLIGHT_CHECKERED) + .disableNormals() + .lineWidth(1 / 16f) + .colored(new Color(1f, 1f, 1f, 1f)); + + blockCount = newCoordinates.size(); } - RenderHandler.endLines(renderTypeBuffer); - + } else { + //Breaking + CreateClient.OUTLINER.showCluster("blockpreviews", newCoordinates) + .withFaceTexture(AllSpecialTextures.THIN_CHECKERED) + .disableNormals() + .lineWidth(1 / 16f) + .colored(new Color(0.8f, 0.1f, 0.1f, 1f)); blockCount = newCoordinates.size(); } @@ -240,29 +266,29 @@ public class BlockPreviewRenderer { //Draw outlines if no block in hand //Find proper raytrace: either normal range or increased range depending on canBreakFar - VertexConsumer buffer = RenderHandler.beginLines(renderTypeBuffer); - HitResult objectMouseOver = Minecraft.getInstance().hitResult; - HitResult breakingRaytrace = ReachHelper.canBreakFar(player) ? lookingAt : objectMouseOver; - - if (player.isCreative() && noBlockInHand && breakingRaytrace != null && breakingRaytrace.getType() == HitResult.Type.BLOCK) { - BlockHitResult blockBreakingRaytrace = (BlockHitResult) breakingRaytrace; - List breakCoordinates = BuildModifiers.findCoordinates(player, blockBreakingRaytrace.getBlockPos()); - - //Only render first outline if further than normal reach - boolean excludeFirst = objectMouseOver != null && objectMouseOver.getType() == HitResult.Type.BLOCK; - for (int i = excludeFirst ? 1 : 0; i < breakCoordinates.size(); i++) { - BlockPos coordinate = breakCoordinates.get(i); - - BlockState blockState = player.level.getBlockState(coordinate); - if (!blockState.isAir()) { - if (SurvivalHelper.canBreak(player.level, player, coordinate) || i == 0) { - VoxelShape collisionShape = blockState.getCollisionShape(player.level, coordinate); - RenderHandler.renderBlockOutline(matrixStack, buffer, coordinate, collisionShape, new Vec3(0f, 0f, 0f)); - } - } - } - } - RenderHandler.endLines(renderTypeBuffer); +// VertexConsumer vc = RenderHandler.beginLines(buffer); +// HitResult objectMouseOver = Minecraft.getInstance().hitResult; +// HitResult breakingRaytrace = ReachHelper.canBreakFar(player) ? lookingAt : objectMouseOver; +// +// if (player.isCreative() && noBlockInHand && breakingRaytrace != null && breakingRaytrace.getType() == HitResult.Type.BLOCK) { +// BlockHitResult blockBreakingRaytrace = (BlockHitResult) breakingRaytrace; +// List breakCoordinates = BuildModifiers.findCoordinates(player, blockBreakingRaytrace.getBlockPos()); +// +// //Only render first outline if further than normal reach +// boolean excludeFirst = objectMouseOver != null && objectMouseOver.getType() == HitResult.Type.BLOCK; +// for (int i = excludeFirst ? 1 : 0; i < breakCoordinates.size(); i++) { +// BlockPos coordinate = breakCoordinates.get(i); +// +// BlockState blockState = player.level.getBlockState(coordinate); +// if (!blockState.isAir()) { +// if (SurvivalHelper.canBreak(player.level, player, coordinate) || i == 0) { +// VoxelShape collisionShape = blockState.getCollisionShape(player.level, coordinate); +// RenderHandler.renderBlockOutline(ms, vc, coordinate, collisionShape, new Vec3(0f, 0f, 0f)); +// } +// } +// } +// } +// RenderHandler.endLines(buffer); } } @@ -290,9 +316,9 @@ public class BlockPreviewRenderer { if (CompatHelper.isItemBlockProxy(itemstack)) itemstack = CompatHelper.getItemBlockByState(itemstack, blockState); - //Check if can place + //Check if we can place //If check is turned off, check if blockstate is the same (for dissolve effect) - if ((!checkCanPlace /*&& player.world.getNewBlockState(blockPos) == blockState*/) || //TODO enable (breaks the breaking shader) + if ((!checkCanPlace && player.level.getBlockState(blockPos) == blockState) || //TODO enable (breaks the breaking shader) SurvivalHelper.canPlace(player.level, player, blockPos, blockState, itemstack, modifierSettings.doQuickReplace(), Direction.UP)) { RenderHandler.renderBlockPreview(matrixStack, renderTypeBuffer, dispatcher, blockPos, blockState, dissolve, firstPos, secondPos, red); diff --git a/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java b/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java index d557a00..0f2e5e6 100644 --- a/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java +++ b/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java @@ -14,6 +14,7 @@ import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; @@ -22,11 +23,14 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.RenderLevelLastEvent; import net.minecraftforge.client.event.RenderLevelStageEvent; import net.minecraftforge.client.model.data.ModelData; +import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod.EventBusSubscriber; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; +import nl.requios.effortlessbuilding.create.CreateClient; +import nl.requios.effortlessbuilding.create.events.ClientEvents; /*** * Main render class for Effortless Building @@ -34,6 +38,17 @@ import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; @EventBusSubscriber(Dist.CLIENT) public class RenderHandler { + @SubscribeEvent + public static void onTick(TickEvent.ClientTickEvent event) { + if (!ClientEvents.isGameActive()) return; + + Player player = Minecraft.getInstance().player; + ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); + ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); + + BlockPreviewRenderer.render(null, null, player, modifierSettings, modeSettings); + } + @SubscribeEvent public static void onRender(RenderLevelLastEvent event) { Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); @@ -83,39 +98,44 @@ public class RenderHandler { renderTypeBuffer.endBatch(); } - protected static void renderBlockPreview(PoseStack matrixStack, MultiBufferSource.BufferSource renderTypeBuffer, BlockRenderDispatcher dispatcher, + protected static void renderBlockPreview(PoseStack ms, MultiBufferSource.BufferSource buffer, BlockRenderDispatcher dispatcher, BlockPos blockPos, BlockState blockState, float dissolve, BlockPos firstPos, BlockPos secondPos, boolean red) { if (blockState == null) return; - matrixStack.pushPose(); - matrixStack.translate(blockPos.getX(), blockPos.getY(), blockPos.getZ()); -// matrixStack.rotate(Vector3f.YP.rotationDegrees(-90f)); - matrixStack.translate(-0.01f, -0.01f, -0.01f); - matrixStack.scale(1.02f, 1.02f, 1.02f); + CreateClient.GHOST_BLOCKS.showGhostState(blockPos.toShortString(), blockState) +// .breathingAlpha() + .alpha(0.7f) + .at(blockPos); - //Begin block preview rendering - RenderType blockPreviewRenderType = BuildRenderTypes.getBlockPreviewRenderType(dissolve, blockPos, firstPos, secondPos, red); - VertexConsumer buffer = renderTypeBuffer.getBuffer(blockPreviewRenderType); - - try { - BakedModel model = dispatcher.getBlockModel(blockState); - dispatcher.getModelRenderer().renderModel(matrixStack.last(), buffer, blockState, model, - 1f, 1f, 1f, 0, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, blockPreviewRenderType); - } catch (NullPointerException e) { - EffortlessBuilding.logger.warn("RenderHandler::renderBlockPreview cannot render " + blockState.getBlock().toString()); - - //Render outline as backup, escape out of the current renderstack - matrixStack.popPose(); - renderTypeBuffer.endBatch(); - VertexConsumer lineBuffer = beginLines(renderTypeBuffer); - renderBlockOutline(matrixStack, lineBuffer, blockPos, new Vec3(1f, 1f, 1f)); - endLines(renderTypeBuffer); - buffer = renderTypeBuffer.getBuffer(Sheets.translucentCullBlockSheet()); //any type will do, as long as we have something on the stack - matrixStack.pushPose(); - } - - renderTypeBuffer.endBatch(); - matrixStack.popPose(); +// ms.pushPose(); +// ms.translate(blockPos.getX(), blockPos.getY(), blockPos.getZ()); +//// ms.rotate(Vector3f.YP.rotationDegrees(-90f)); +// ms.translate(-0.01f, -0.01f, -0.01f); +// ms.scale(1.02f, 1.02f, 1.02f); +// +// //Begin block preview rendering +// RenderType blockPreviewRenderType = BuildRenderTypes.getBlockPreviewRenderType(dissolve, blockPos, firstPos, secondPos, red); +// VertexConsumer vc = buffer.getBuffer(blockPreviewRenderType); +// +// try { +// BakedModel model = dispatcher.getBlockModel(blockState); +// dispatcher.getModelRenderer().renderModel(ms.last(), vc, blockState, model, +// 1f, 1f, 1f, 0, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, blockPreviewRenderType); +// } catch (NullPointerException e) { +// EffortlessBuilding.logger.warn("RenderHandler::renderBlockPreview cannot render " + blockState.getBlock().toString()); +// +// //Render outline as backup, escape out of the current renderstack +// ms.popPose(); +// buffer.endBatch(); +// VertexConsumer lineBuffer = beginLines(buffer); +// renderBlockOutline(ms, lineBuffer, blockPos, new Vec3(1f, 1f, 1f)); +// endLines(buffer); +// vc = buffer.getBuffer(Sheets.translucentCullBlockSheet()); //any type will do, as long as we have something on the stack +// ms.pushPose(); +// } +// +// buffer.endBatch(); +// ms.popPose(); } protected static void renderBlockOutline(PoseStack matrixStack, VertexConsumer buffer, BlockPos pos, Vec3 color) { diff --git a/src/main/resources/assets/effortlessbuilding/lang/en_us.json b/src/main/resources/assets/effortlessbuilding/lang/en_us.json index 3a37363..e5da310 100644 --- a/src/main/resources/assets/effortlessbuilding/lang/en_us.json +++ b/src/main/resources/assets/effortlessbuilding/lang/en_us.json @@ -11,12 +11,12 @@ "key.effortlessbuilding.redo.desc": "Redo", "key.effortlessbuilding.altplacement.desc": "Alternative placement", - "effortlessbuilding:randomizer_bag": "Leather Randomizer Bag", - "effortlessbuilding:golden_randomizer_bag": "Golden Randomizer Bag", - "effortlessbuilding:diamond_randomizer_bag": "Diamond Randomizer Bag", - "effortlessbuilding:reach_upgrade1": "Reach Upgrade 1", - "effortlessbuilding:reach_upgrade2": "Reach Upgrade 2", - "effortlessbuilding:reach_upgrade3": "Reach Upgrade 3", + "item.effortlessbuilding.randomizer_bag": "Leather Randomizer Bag", + "item.effortlessbuilding.golden_randomizer_bag": "Golden Randomizer Bag", + "item.effortlessbuilding.diamond_randomizer_bag": "Diamond Randomizer Bag", + "item.effortlessbuilding.reach_upgrade1": "Reach Upgrade 1", + "item.effortlessbuilding.reach_upgrade2": "Reach Upgrade 2", + "item.effortlessbuilding.reach_upgrade3": "Reach Upgrade 3", "effortlessbuilding.mode.normal": "Disable", "effortlessbuilding.mode.normal_plus": "Single", From cd3afbbb82f09b56dc0be45788970c8180317138 Mon Sep 17 00:00:00 2001 From: Christian Knaapen Date: Tue, 17 Jan 2023 02:30:08 +0100 Subject: [PATCH 4/5] Overhauled block previews to work with ticks. Added place and break animation. Added DelayedBlockPlacer to play appear animation, then place. Split BuildConfig into CommonConfig and ClientConfig. Fixed itemstack not found error in creative. Added scale and color options to ghost blocks. --- .../effortlessbuilding/ClientConfig.java | 36 ++ .../effortlessbuilding/ClientEvents.java | 6 +- .../{BuildConfig.java => CommonConfig.java} | 37 +- .../effortlessbuilding/CommonEvents.java | 14 +- .../EffortlessBuilding.java | 6 +- .../buildmode/BuildModes.java | 9 +- .../buildmode/ModeSettingsManager.java | 2 +- .../{NormalPlus.java => Disabled.java} | 2 +- .../buildmodes/{Normal.java => Single.java} | 2 +- .../buildmodifier/BuildModifiers.java | 57 +-- .../ModifierSettingsManager.java | 10 +- .../buildmodifier/UndoRedo.java | 12 +- .../capability/ModeCapabilityManager.java | 2 +- .../utility/ghost/GhostBlockParams.java | 33 ++ .../utility/ghost/GhostBlockRenderer.java | 19 +- .../foundation/utility/ghost/GhostBlocks.java | 7 + .../helper/DelayedBlockPlacer.java | 108 +++++ .../helper/ReachHelper.java | 16 +- .../helper/SurvivalHelper.java | 36 +- .../item/AbstractRandomizerBagItem.java | 2 +- .../item/ReachUpgrade1Item.java | 4 +- .../item/ReachUpgrade2Item.java | 5 +- .../item/ReachUpgrade3Item.java | 4 +- .../network/AddUndoMessage.java | 1 - .../network/BlockPlacedMessage.java | 4 +- .../network/ClearUndoMessage.java | 1 - .../network/RequestLookAtMessage.java | 2 - .../render/BlockPreviewRenderer.java | 412 ----------------- .../render/BlockPreviews.java | 414 ++++++++++++++++++ .../render/RenderHandler.java | 166 +++---- .../icons/{normal.png => disabled.png} | Bin .../icons/{normal_plus.png => single.png} | Bin 32 files changed, 761 insertions(+), 668 deletions(-) create mode 100644 src/main/java/nl/requios/effortlessbuilding/ClientConfig.java rename src/main/java/nl/requios/effortlessbuilding/{BuildConfig.java => CommonConfig.java} (74%) rename src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/{NormalPlus.java => Disabled.java} (95%) rename src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/{Normal.java => Single.java} (95%) create mode 100644 src/main/java/nl/requios/effortlessbuilding/helper/DelayedBlockPlacer.java delete mode 100644 src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java rename src/main/resources/assets/effortlessbuilding/textures/icons/{normal.png => disabled.png} (100%) rename src/main/resources/assets/effortlessbuilding/textures/icons/{normal_plus.png => single.png} (100%) diff --git a/src/main/java/nl/requios/effortlessbuilding/ClientConfig.java b/src/main/java/nl/requios/effortlessbuilding/ClientConfig.java new file mode 100644 index 0000000..e2021d2 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/ClientConfig.java @@ -0,0 +1,36 @@ +package nl.requios.effortlessbuilding; + +import net.minecraftforge.common.ForgeConfigSpec; + +public class ClientConfig { + + private static final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder(); + public static final Visuals visuals = new Visuals(builder); + public static final ForgeConfigSpec spec = builder.build(); + + public static class Visuals { + public final ForgeConfigSpec.ConfigValue showBlockPreviews; + public final ForgeConfigSpec.ConfigValue alwaysShowBlockPreview; + public final ForgeConfigSpec.ConfigValue maxBlockPreviews; + + public Visuals(ForgeConfigSpec.Builder builder) { + builder.push("Visuals"); + + showBlockPreviews = builder + .comment("Show previews of the blocks while placing them") + .define("useShaders", true); + + alwaysShowBlockPreview = builder + .comment("Show a block preview if you have a block in hand even in the 'Disabled' build mode") + .define("alwaysShowBlockPreview", false); + + maxBlockPreviews = builder + .comment("Don't show block previews when placing more than this many blocks. " + + "The outline will always be rendered.") + .define("shaderTreshold", 500); + + + builder.pop(); + } + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/ClientEvents.java b/src/main/java/nl/requios/effortlessbuilding/ClientEvents.java index 120f6c3..e189f9b 100644 --- a/src/main/java/nl/requios/effortlessbuilding/ClientEvents.java +++ b/src/main/java/nl/requios/effortlessbuilding/ClientEvents.java @@ -133,7 +133,7 @@ public class ClientEvents { BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode(); if (mc.screen != null || - buildMode == BuildModes.BuildModeEnum.NORMAL || + buildMode == BuildModes.BuildModeEnum.DISABLED || RadialMenu.instance.isVisible()) { return; } @@ -175,7 +175,7 @@ public class ClientEvents { PacketHandler.INSTANCE.sendToServer(new BlockPlacedMessage()); } } - } else if (buildMode == BuildModes.BuildModeEnum.NORMAL_PLUS) { + } else if (buildMode == BuildModes.BuildModeEnum.SINGLE) { placeCooldown--; if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) placeCooldown = 0; } @@ -210,7 +210,7 @@ public class ClientEvents { BuildModes.onBlockBrokenMessage(player, new BlockBrokenMessage()); PacketHandler.INSTANCE.sendToServer(new BlockBrokenMessage()); } - } else if (buildMode == BuildModes.BuildModeEnum.NORMAL_PLUS) { + } else if (buildMode == BuildModes.BuildModeEnum.SINGLE) { breakCooldown--; if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) breakCooldown = 0; } diff --git a/src/main/java/nl/requios/effortlessbuilding/BuildConfig.java b/src/main/java/nl/requios/effortlessbuilding/CommonConfig.java similarity index 74% rename from src/main/java/nl/requios/effortlessbuilding/BuildConfig.java rename to src/main/java/nl/requios/effortlessbuilding/CommonConfig.java index 2c84c58..9db7c50 100644 --- a/src/main/java/nl/requios/effortlessbuilding/BuildConfig.java +++ b/src/main/java/nl/requios/effortlessbuilding/CommonConfig.java @@ -1,8 +1,9 @@ package nl.requios.effortlessbuilding; import net.minecraftforge.common.ForgeConfigSpec; +import nl.requios.effortlessbuilding.create.foundation.render.SuperByteBufferCache; -public class BuildConfig { +public class CommonConfig { private static final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder(); public static final Reach reach = new Reach(builder); @@ -80,33 +81,21 @@ public class BuildConfig { } public static class Visuals { - public final ForgeConfigSpec.ConfigValue alwaysShowBlockPreview; - public final ForgeConfigSpec.ConfigValue dissolveTimeMultiplier; - public final ForgeConfigSpec.ConfigValue shaderThreshold; - public final ForgeConfigSpec.ConfigValue useShaders; + public final ForgeConfigSpec.ConfigValue appearAnimationLength; + public final ForgeConfigSpec.ConfigValue breakAnimationLength; - public Visuals(ForgeConfigSpec.Builder builder) { + public Visuals(ForgeConfigSpec.Builder builder) { builder.push("Visuals"); - alwaysShowBlockPreview = builder - .comment("Show a block preview if you have a block in hand even in the 'Normal' build mode") - .define("alwaysShowBlockPreview", false); + appearAnimationLength = builder + .comment("How long it takes for a block to appear when placed in ticks.", + "Set to 0 to disable animation.") + .define("appearAnimationLength", 5); - dissolveTimeMultiplier = builder - .comment("How long the dissolve effect takes when placing blocks.", - "Default between 30 and 60 ticks, you can multiply that here.", - "Recommended values:", - "Snappy: 0.7", - "Relaxing: 1.5") - .define("dissolveTimeMultiplier", 1.0); - - shaderThreshold = builder - .comment("Switch to using the simple performance shader when placing more than this many blocks.") - .define("shaderTreshold", 1500); - - useShaders = builder - .comment("Use fancy shaders while placing blocks") - .define("useShaders", true); + breakAnimationLength = builder + .comment("How long the break animation is in ticks.", + "Set to 0 to disable animation.") + .define("breakAnimationLength", 10); builder.pop(); } diff --git a/src/main/java/nl/requios/effortlessbuilding/CommonEvents.java b/src/main/java/nl/requios/effortlessbuilding/CommonEvents.java index 8c00125..541555e 100644 --- a/src/main/java/nl/requios/effortlessbuilding/CommonEvents.java +++ b/src/main/java/nl/requios/effortlessbuilding/CommonEvents.java @@ -11,6 +11,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.event.AttachCapabilitiesEvent; +import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.level.BlockEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -52,6 +53,13 @@ public class CommonEvents { } } + @SubscribeEvent + public static void onTick(TickEvent.LevelTickEvent event) { + if (event.phase != TickEvent.Phase.START) return; + + EffortlessBuilding.DELAYED_BLOCK_PLACER.tick(); + } + @SubscribeEvent public static void onBlockPlaced(BlockEvent.EntityPlaceEvent event) { if (event.getLevel().isClientSide()) return; @@ -65,7 +73,7 @@ public class CommonEvents { BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode(); ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - if (buildMode != BuildModes.BuildModeEnum.NORMAL) { + if (buildMode != BuildModes.BuildModeEnum.DISABLED) { //Only cancel if itemblock in hand //Fixed issue with e.g. Create Wrench shift-rightclick disassembling being cancelled. @@ -104,7 +112,7 @@ public class CommonEvents { //Cancel event if necessary //If cant break far then dont cancel event ever BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(event.getPlayer()).getBuildMode(); - if (buildMode != BuildModes.BuildModeEnum.NORMAL && ReachHelper.canBreakFar(event.getPlayer())) { + if (buildMode != BuildModes.BuildModeEnum.DISABLED && ReachHelper.canBreakFar(event.getPlayer())) { event.setCanceled(true); } else { //NORMAL mode, let vanilla handle block breaking @@ -155,7 +163,7 @@ public class CommonEvents { //Set build mode to normal ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); - modeSettings.setBuildMode(BuildModes.BuildModeEnum.NORMAL); + modeSettings.setBuildMode(BuildModes.BuildModeEnum.DISABLED); ModeSettingsManager.setModeSettings(player, modeSettings); //Disable modifiers diff --git a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java index 7d3db47..5748382 100644 --- a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java +++ b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java @@ -22,6 +22,7 @@ import nl.requios.effortlessbuilding.compatibility.CompatHelper; import nl.requios.effortlessbuilding.gui.DiamondRandomizerBagContainer; import nl.requios.effortlessbuilding.gui.GoldenRandomizerBagContainer; import nl.requios.effortlessbuilding.gui.RandomizerBagContainer; +import nl.requios.effortlessbuilding.helper.DelayedBlockPlacer; import nl.requios.effortlessbuilding.item.*; import nl.requios.effortlessbuilding.network.PacketHandler; import nl.requios.effortlessbuilding.proxy.ClientProxy; @@ -39,6 +40,8 @@ public class EffortlessBuilding { public static EffortlessBuilding instance; public static IProxy proxy = DistExecutor.safeRunForDist(() -> ClientProxy::new, () -> ServerProxy::new); + public static final DelayedBlockPlacer DELAYED_BLOCK_PLACER = new DelayedBlockPlacer(); + //Registration private static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID); private static final DeferredRegister> CONTAINERS = DeferredRegister.create(ForgeRegistries.MENU_TYPES, EffortlessBuilding.MODID); @@ -69,7 +72,8 @@ public class EffortlessBuilding { CONTAINERS.register(FMLJavaModLoadingContext.get().getModEventBus()); //Register config - ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, BuildConfig.spec); + ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, CommonConfig.spec); + ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ClientConfig.spec); } public static void setup(final FMLCommonSetupEvent event) { diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java index 2812685..469f5cb 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java @@ -66,7 +66,7 @@ public class BuildModes { //Check if player reach does not exceed startpos int maxReach = ReachHelper.getMaxReach(player); - if (buildMode != BuildModeEnum.NORMAL && player.blockPosition().distSqr(startPos) > maxReach * maxReach) { + if (buildMode != BuildModeEnum.DISABLED && player.blockPosition().distSqr(startPos) > maxReach * maxReach) { EffortlessBuilding.log(player, "Placement exceeds your reach."); return; } @@ -186,9 +186,6 @@ public class BuildModes { return new Vec3(x, y, z); } - - //-- Common build mode functionality --// - public static Vec3 findYBound(double y, Vec3 start, Vec3 look) { //then x and z are double x = (y - start.y) / look.y * look.x + start.x; @@ -246,8 +243,8 @@ public class BuildModes { } public enum BuildModeEnum { - NORMAL("normal", new Normal(), BuildModeCategoryEnum.BASIC), - NORMAL_PLUS("normal_plus", new NormalPlus(), BuildModeCategoryEnum.BASIC, OptionEnum.BUILD_SPEED), + DISABLED("normal", new Disabled(), BuildModeCategoryEnum.BASIC), + SINGLE("normal_plus", new Single(), BuildModeCategoryEnum.BASIC, OptionEnum.BUILD_SPEED), LINE("line", new Line(), BuildModeCategoryEnum.BASIC /*, OptionEnum.THICKNESS*/), WALL("wall", new Wall(), BuildModeCategoryEnum.BASIC, OptionEnum.FILL), FLOOR("floor", new Floor(), BuildModeCategoryEnum.BASIC, OptionEnum.FILL), diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeSettingsManager.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeSettingsManager.java index 264939c..e34510c 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeSettingsManager.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeSettingsManager.java @@ -77,7 +77,7 @@ public class ModeSettingsManager { } public static class ModeSettings { - private BuildModes.BuildModeEnum buildMode = BuildModes.BuildModeEnum.NORMAL; + private BuildModes.BuildModeEnum buildMode = BuildModes.BuildModeEnum.DISABLED; public ModeSettings() { } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/NormalPlus.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Disabled.java similarity index 95% rename from src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/NormalPlus.java rename to src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Disabled.java index 07c3403..5ffee68 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/NormalPlus.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Disabled.java @@ -9,7 +9,7 @@ import nl.requios.effortlessbuilding.buildmode.IBuildMode; import java.util.ArrayList; import java.util.List; -public class NormalPlus implements IBuildMode { +public class Disabled implements IBuildMode { @Override public void initialize(Player player) { diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Normal.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Single.java similarity index 95% rename from src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Normal.java rename to src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Single.java index 3f71149..e68b6f7 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Normal.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Single.java @@ -9,7 +9,7 @@ import nl.requios.effortlessbuilding.buildmode.IBuildMode; import java.util.ArrayList; import java.util.List; -public class Normal implements IBuildMode { +public class Single implements IBuildMode { @Override public void initialize(Player player) { diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java index db2363a..1a5d16f 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java @@ -14,11 +14,13 @@ import net.minecraft.core.BlockPos; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.world.level.Level; +import nl.requios.effortlessbuilding.CommonConfig; +import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.compatibility.CompatHelper; -import nl.requios.effortlessbuilding.helper.InventoryHelper; +import nl.requios.effortlessbuilding.helper.DelayedBlockPlacer; import nl.requios.effortlessbuilding.helper.SurvivalHelper; import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem; -import nl.requios.effortlessbuilding.render.BlockPreviewRenderer; +import nl.requios.effortlessbuilding.render.BlockPreviews; import java.util.ArrayList; import java.util.Collections; @@ -42,55 +44,17 @@ public class BuildModifiers { //check if valid blockstates if (blockStates.size() == 0 || coordinates.size() != blockStates.size()) return; - //remember previous blockstates for undo - List previousBlockStates = new ArrayList<>(coordinates.size()); - List newBlockStates = new ArrayList<>(coordinates.size()); - for (BlockPos coordinate : coordinates) { - previousBlockStates.add(world.getBlockState(coordinate)); - } - if (world.isClientSide) { - BlockPreviewRenderer.onBlocksPlaced(); - - newBlockStates = blockStates; + BlockPreviews.onBlocksPlaced(); } else { - //place blocks - for (int i = placeStartPos ? 0 : 1; i < coordinates.size(); i++) { - BlockPos blockPos = coordinates.get(i); - BlockState blockState = blockStates.get(i); - ItemStack itemStack = itemStacks.get(i); + int delay = CommonConfig.visuals.appearAnimationLength.get() * 3; //DelayedBlockPlacer is called 3 times per tick? - if (world.isLoaded(blockPos)) { - //check itemstack empty - if (itemStack.isEmpty()) { - //try to find new stack, otherwise continue - itemStack = InventoryHelper.findItemStackInInventory(player, blockState.getBlock()); - if (itemStack.isEmpty()) continue; - } - SurvivalHelper.placeBlock(world, player, blockPos, blockState, itemStack, Direction.UP, hitVec, false, false, false); - } - } - - //find actual new blockstates for undo - for (BlockPos coordinate : coordinates) { - newBlockStates.add(world.getBlockState(coordinate)); - } - } - - //Set first previousBlockState to empty if in NORMAL mode, to make undo/redo work - //(Block is placed by the time it gets here, and unplaced after this) - if (!placeStartPos) previousBlockStates.set(0, Blocks.AIR.defaultBlockState()); - - //If all new blockstates are air then no use in adding it, no block was actually placed - //Can happen when e.g. placing one block in yourself - if (Collections.frequency(newBlockStates, Blocks.AIR.defaultBlockState()) != newBlockStates.size()) { - //add to undo stack - BlockPos firstPos = startCoordinates.get(0); - BlockPos secondPos = startCoordinates.get(startCoordinates.size() - 1); - UndoRedo.addUndo(player, new BlockSet(coordinates, previousBlockStates, newBlockStates, hitVec, firstPos, secondPos)); + //place blocks after delay + EffortlessBuilding.DELAYED_BLOCK_PLACER.placeBlocksDelayed(new DelayedBlockPlacer.Entry(world, player, coordinates, + blockStates, itemStacks, hitVec, placeStartPos, delay)); } } @@ -109,7 +73,7 @@ public class BuildModifiers { } if (world.isClientSide) { - BlockPreviewRenderer.onBlocksBroken(); + BlockPreviews.onBlocksBroken(); //list of air blockstates for (int i = 0; i < coordinates.size(); i++) { @@ -237,6 +201,7 @@ public class BuildModifiers { } public static boolean isEnabled(ModifierSettingsManager.ModifierSettings modifierSettings, BlockPos startPos) { + //startPos can be null return Mirror.isEnabled(modifierSettings.getMirrorSettings(), startPos) || Array.isEnabled(modifierSettings.getArraySettings()) || RadialMirror.isEnabled(modifierSettings.getRadialMirrorSettings(), startPos) || diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/ModifierSettingsManager.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/ModifierSettingsManager.java index 7583398..87d9e2e 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/ModifierSettingsManager.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/ModifierSettingsManager.java @@ -5,7 +5,7 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.network.PacketDistributor; -import nl.requios.effortlessbuilding.BuildConfig; +import nl.requios.effortlessbuilding.CommonConfig; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.capability.ModifierCapabilityManager; import nl.requios.effortlessbuilding.helper.ReachHelper; @@ -192,16 +192,16 @@ public class ModifierSettingsManager { int reach = 10; switch (reachUpgrade) { case 0: - reach = BuildConfig.reach.maxReachLevel0.get(); + reach = CommonConfig.reach.maxReachLevel0.get(); break; case 1: - reach = BuildConfig.reach.maxReachLevel1.get(); + reach = CommonConfig.reach.maxReachLevel1.get(); break; case 2: - reach = BuildConfig.reach.maxReachLevel2.get(); + reach = CommonConfig.reach.maxReachLevel2.get(); break; case 3: - reach = BuildConfig.reach.maxReachLevel3.get(); + reach = CommonConfig.reach.maxReachLevel3.get(); break; } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java index 8d0152c..20b9a48 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java @@ -10,12 +10,12 @@ import net.minecraft.core.Direction; import net.minecraft.core.BlockPos; import net.minecraft.world.phys.Vec3; import net.minecraft.server.level.ServerLevel; -import nl.requios.effortlessbuilding.BuildConfig; +import nl.requios.effortlessbuilding.CommonConfig; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.helper.FixedStack; import nl.requios.effortlessbuilding.helper.InventoryHelper; import nl.requios.effortlessbuilding.helper.SurvivalHelper; -import nl.requios.effortlessbuilding.render.BlockPreviewRenderer; +import nl.requios.effortlessbuilding.render.BlockPreviews; import java.util.*; @@ -50,7 +50,7 @@ public class UndoRedo { //If no stack exists, make one if (!undoStacks.containsKey(player.getUUID())) { - undoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[BuildConfig.survivalBalancers.undoStackSize.get()])); + undoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[CommonConfig.survivalBalancers.undoStackSize.get()])); } undoStacks.get(player.getUUID()).push(blockSet); @@ -63,7 +63,7 @@ public class UndoRedo { //If no stack exists, make one if (!redoStacks.containsKey(player.getUUID())) { - redoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[BuildConfig.survivalBalancers.undoStackSize.get()])); + redoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[CommonConfig.survivalBalancers.undoStackSize.get()])); } redoStacks.get(player.getUUID()).push(blockSet); @@ -88,7 +88,7 @@ public class UndoRedo { List itemStacks = findItemStacksInInventory(player, previousBlockStates); if (player.level.isClientSide) { - BlockPreviewRenderer.onBlocksBroken(coordinates, itemStacks, newBlockStates, blockSet.getSecondPos(), blockSet.getFirstPos()); + BlockPreviews.onBlocksBroken(coordinates, itemStacks, newBlockStates, blockSet.getSecondPos(), blockSet.getFirstPos()); } else { //break all those blocks, reset to what they were for (int i = 0; i < coordinates.size(); i++) { @@ -148,7 +148,7 @@ public class UndoRedo { List itemStacks = findItemStacksInInventory(player, newBlockStates); if (player.level.isClientSide) { - BlockPreviewRenderer.onBlocksPlaced(coordinates, itemStacks, newBlockStates, blockSet.getFirstPos(), blockSet.getSecondPos()); + BlockPreviews.onBlocksPlaced(coordinates, itemStacks, newBlockStates, blockSet.getFirstPos(), blockSet.getSecondPos()); } else { //place blocks for (int i = 0; i < coordinates.size(); i++) { diff --git a/src/main/java/nl/requios/effortlessbuilding/capability/ModeCapabilityManager.java b/src/main/java/nl/requios/effortlessbuilding/capability/ModeCapabilityManager.java index cc40ab2..6942e87 100644 --- a/src/main/java/nl/requios/effortlessbuilding/capability/ModeCapabilityManager.java +++ b/src/main/java/nl/requios/effortlessbuilding/capability/ModeCapabilityManager.java @@ -100,7 +100,7 @@ public class ModeCapabilityManager { //TODO add mode settings - ModeSettings modeSettings = new ModeSettings(BuildModes.BuildModeEnum.NORMAL); + ModeSettings modeSettings = new ModeSettings(BuildModes.BuildModeEnum.DISABLED); instance.setModeData(modeSettings); } diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockParams.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockParams.java index ecb3e1a..7202e91 100644 --- a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockParams.java +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockParams.java @@ -3,6 +3,8 @@ package nl.requios.effortlessbuilding.create.foundation.utility.ghost; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import nl.requios.effortlessbuilding.create.foundation.utility.Color; +import nl.requios.effortlessbuilding.create.foundation.utility.outliner.Outline; import java.util.function.Supplier; @@ -11,11 +13,15 @@ public class GhostBlockParams { protected final BlockState state; protected BlockPos pos; protected Supplier alphaSupplier; + protected Supplier scaleSupplier; + public Supplier rgbSupplier; private GhostBlockParams(BlockState state) { this.state = state; this.pos = BlockPos.ZERO; this.alphaSupplier = () -> 1f; + this.scaleSupplier = () -> 0.85f; + this.rgbSupplier = () -> Color.WHITE; } public static GhostBlockParams of(BlockState state) { @@ -47,4 +53,31 @@ public class GhostBlockParams { public GhostBlockParams breathingAlpha() { return this.alpha(() -> (float) GhostBlocks.getBreathingAlpha()); } + + public GhostBlockParams scale(Supplier scaleSupplier) { + this.scaleSupplier = scaleSupplier; + return this; + } + + public GhostBlockParams scale(float scale) { + return this.scale(() -> scale); + } + + public GhostBlockParams colored(Supplier colorSupplier) { + this.rgbSupplier = colorSupplier; + return this; + } + + public GhostBlockParams colored(Color color) { + return this.colored(() -> color); + } + + public GhostBlockParams colored(int color) { + var color2 = new Color(color, false); + return this.colored(() -> color2); + } + + public GhostBlockParams breathingCyan() { + return this.colored(() -> new Color((float) GhostBlocks.getBreathingColor(), 1f, 1f, 1f)); + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockRenderer.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockRenderer.java index 1fa167b..fb29bc1 100644 --- a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockRenderer.java +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlockRenderer.java @@ -19,6 +19,7 @@ import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.client.model.data.ModelData; +import nl.requios.effortlessbuilding.create.foundation.utility.Color; import javax.annotation.Nullable; import java.util.List; @@ -76,6 +77,8 @@ public abstract class GhostBlockRenderer { BlockState state = params.state; BlockPos pos = params.pos; float alpha = params.alphaSupplier.get()/* * .75f* PlacementHelpers.getCurrentAlpha()*/; + float scale = params.scaleSupplier.get(); + Color color = params.rgbSupplier.get(); BakedModel model = dispatcher.getBlockModel(state); RenderType layer = RenderType.translucent(); @@ -85,10 +88,10 @@ public abstract class GhostBlockRenderer { ms.translate(pos.getX(), pos.getY(), pos.getZ()); ms.translate(.5, .5, .5); - ms.scale(.85f, .85f, .85f); + ms.scale(scale, scale, scale); ms.translate(-.5, -.5, -.5); - renderModel(ms.last(), vb, state, model, 0f, 1f, 1f, alpha, + renderModel(ms.last(), vb, state, model, color.getRedAsFloat(), color.getGreenAsFloat(), color.getBlueAsFloat(), alpha, LevelRenderer.getLightColor(mc.level, pos), OverlayTexture.NO_OVERLAY, ModelUtil.VIRTUAL_DATA, layer); @@ -120,15 +123,15 @@ public abstract class GhostBlockRenderer { float f; float f1; float f2; - if (quad.isTinted()) { +// if (quad.isTinted()) { f = Mth.clamp(red, 0.0F, 1.0F); f1 = Mth.clamp(green, 0.0F, 1.0F); f2 = Mth.clamp(blue, 0.0F, 1.0F); - } else { - f = 1.0F; - f1 = 1.0F; - f2 = 1.0F; - } +// } else { +// f = 1.0F; +// f1 = 1.0F; +// f2 = 1.0F; +// } consumer.putBulkData(pose, quad, f, f1, f2, alpha, packedLight, packedOverlay, true); } diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlocks.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlocks.java index eb38081..96c2893 100644 --- a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlocks.java +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/ghost/GhostBlocks.java @@ -17,6 +17,13 @@ public class GhostBlocks { return 0.55d - 0.2d * offset; } + public static double getBreathingColor() { + double period = 2500; + double timer = System.currentTimeMillis() % period; + double offset = Mth.cos((float) ((2d/period) * Math.PI * timer)); + return 0.35d + 0.35d * offset; + } + final Map ghosts; public GhostBlockParams showGhostState(Object slot, BlockState state) { diff --git a/src/main/java/nl/requios/effortlessbuilding/helper/DelayedBlockPlacer.java b/src/main/java/nl/requios/effortlessbuilding/helper/DelayedBlockPlacer.java new file mode 100644 index 0000000..bac8f8c --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/helper/DelayedBlockPlacer.java @@ -0,0 +1,108 @@ +package nl.requios.effortlessbuilding.helper; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; +import nl.requios.effortlessbuilding.buildmodifier.BlockSet; +import nl.requios.effortlessbuilding.buildmodifier.UndoRedo; +import nl.requios.effortlessbuilding.create.foundation.utility.AnimationTickHolder; + +import java.util.*; + +public class DelayedBlockPlacer { + + private final Set entries = Collections.synchronizedSet(new HashSet<>()); + private final Set entriesView = Collections.unmodifiableSet(entries); + + public void placeBlocksDelayed(Entry entry) { + if (entry.world.isClientSide) return; + + entries.add(entry); + } + + public void tick() { + for (Entry entry : entries) { + entry.ticksTillPlacement--; + if (entry.ticksTillPlacement <= 0) { + entry.place(); + entries.remove(entry); + } + } + } + + public Set getEntries() { + return entriesView; + } + + public static class Entry { + private Level world; + private Player player; + private List coordinates; + private List blockStates; + private List itemStacks; + private Vec3 hitVec; + private boolean placeStartPos; + private int ticksTillPlacement; + + public Entry(Level world, Player player, List coordinates, List blockStates, + List itemStacks, Vec3 hitVec, boolean placeStartPos, int ticksTillPlacement) { + this.world = world; + this.player = player; + this.coordinates = coordinates; + this.blockStates = blockStates; + this.itemStacks = itemStacks; + this.hitVec = hitVec; + this.placeStartPos = placeStartPos; + this.ticksTillPlacement = ticksTillPlacement; + } + + public void place() { + //remember previous blockstates for undo + List previousBlockStates = new ArrayList<>(coordinates.size()); + for (BlockPos coordinate : coordinates) { + previousBlockStates.add(world.getBlockState(coordinate)); + } + + for (int i = placeStartPos ? 0 : 1; i < coordinates.size(); i++) { + BlockPos blockPos = coordinates.get(i); + BlockState blockState = blockStates.get(i); + ItemStack itemStack = itemStacks.get(i); + + if (world.isLoaded(blockPos)) { + //check itemstack empty + if (itemStack.isEmpty()) { + //try to find new stack, otherwise continue + itemStack = InventoryHelper.findItemStackInInventory(player, blockState.getBlock()); + if (itemStack.isEmpty()) continue; + } + SurvivalHelper.placeBlock(world, player, blockPos, blockState, itemStack, Direction.UP, hitVec, false, false, false); + } + } + + //find actual new blockstates for undo + List newBlockStates = new ArrayList<>(coordinates.size()); + for (BlockPos coordinate : coordinates) { + newBlockStates.add(world.getBlockState(coordinate)); + } + + //Set first previousBlockState to empty if in NORMAL mode, to make undo/redo work + //(Block is placed by the time it gets here, and unplaced after this) + if (!placeStartPos) previousBlockStates.set(0, Blocks.AIR.defaultBlockState()); + + //If all new blockstates are air then no use in adding it, no block was actually placed + //Can happen when e.g. placing one block in yourself + if (Collections.frequency(newBlockStates, Blocks.AIR.defaultBlockState()) != newBlockStates.size()) { + //add to undo stack + BlockPos firstPos = coordinates.get(0); + BlockPos secondPos = coordinates.get(coordinates.size() - 1); + UndoRedo.addUndo(player, new BlockSet(coordinates, previousBlockStates, newBlockStates, hitVec, firstPos, secondPos)); + } + } + + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/helper/ReachHelper.java b/src/main/java/nl/requios/effortlessbuilding/helper/ReachHelper.java index a22b6e7..656259d 100644 --- a/src/main/java/nl/requios/effortlessbuilding/helper/ReachHelper.java +++ b/src/main/java/nl/requios/effortlessbuilding/helper/ReachHelper.java @@ -2,28 +2,28 @@ package nl.requios.effortlessbuilding.helper; import net.minecraft.world.entity.player.Player; import net.minecraft.util.Mth; -import nl.requios.effortlessbuilding.BuildConfig; +import nl.requios.effortlessbuilding.CommonConfig; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; public class ReachHelper { public static int getMaxReach(Player player) { - if (player.isCreative()) return BuildConfig.reach.maxReachCreative.get(); + if (player.isCreative()) return CommonConfig.reach.maxReachCreative.get(); - if (!BuildConfig.reach.enableReachUpgrades.get()) return BuildConfig.reach.maxReachLevel3.get(); + if (!CommonConfig.reach.enableReachUpgrades.get()) return CommonConfig.reach.maxReachLevel3.get(); //Check buildsettings for reachUpgrade int reachUpgrade = ModifierSettingsManager.getModifierSettings(player).getReachUpgrade(); switch (reachUpgrade) { case 0: - return BuildConfig.reach.maxReachLevel0.get(); + return CommonConfig.reach.maxReachLevel0.get(); case 1: - return BuildConfig.reach.maxReachLevel1.get(); + return CommonConfig.reach.maxReachLevel1.get(); case 2: - return BuildConfig.reach.maxReachLevel2.get(); + return CommonConfig.reach.maxReachLevel2.get(); case 3: - return BuildConfig.reach.maxReachLevel3.get(); + return CommonConfig.reach.maxReachLevel3.get(); } - return BuildConfig.reach.maxReachLevel0.get(); + return CommonConfig.reach.maxReachLevel0.get(); } public static int getPlacementReach(Player player) { diff --git a/src/main/java/nl/requios/effortlessbuilding/helper/SurvivalHelper.java b/src/main/java/nl/requios/effortlessbuilding/helper/SurvivalHelper.java index 64434a6..19d9650 100644 --- a/src/main/java/nl/requios/effortlessbuilding/helper/SurvivalHelper.java +++ b/src/main/java/nl/requios/effortlessbuilding/helper/SurvivalHelper.java @@ -23,7 +23,7 @@ import net.minecraft.world.level.material.Material; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.event.ForgeEventFactory; -import nl.requios.effortlessbuilding.BuildConfig; +import nl.requios.effortlessbuilding.CommonConfig; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; import nl.requios.effortlessbuilding.compatibility.CompatHelper; @@ -187,17 +187,21 @@ public class SurvivalHelper { */ public static boolean canPlace(Level world, Player player, BlockPos pos, BlockState newBlockState, ItemStack itemStack, boolean skipCollisionCheck, Direction sidePlacedOn) { - //Check if itemstack is correct - if (!(itemStack.getItem() instanceof BlockItem) || Block.byItem(itemStack.getItem()) != newBlockState.getBlock()) { -// EffortlessBuilding.log(player, "Cannot (re)place block", true); -// EffortlessBuilding.log("SurvivalHelper#canPlace: itemstack " + itemStack.toString() + " does not match blockstate " + newBlockState.toString()); - //Happens when breaking blocks, no need to notify in that case - return false; + if (!player.isCreative()) { + //Check if itemstack is correct + if (itemStack.isEmpty() || !(itemStack.getItem() instanceof BlockItem) || + Block.byItem(itemStack.getItem()) != newBlockState.getBlock()) { + return false; + } } - Block block = ((BlockItem) itemStack.getItem()).getBlock(); + Block block = null; + if (itemStack != null && !itemStack.isEmpty() && itemStack.getItem() instanceof BlockItem) + block = ((BlockItem) itemStack.getItem()).getBlock(); + else //In creative we might not have an itemstack + block = newBlockState.getBlock(); - return !itemStack.isEmpty() && canPlayerEdit(player, world, pos, itemStack) && + return canPlayerEdit(player, world, pos, itemStack) && mayPlace(world, block, newBlockState, pos, skipCollisionCheck, sidePlacedOn, player) && canReplace(world, player, pos); } @@ -208,7 +212,7 @@ public class SurvivalHelper { BlockState state = world.getBlockState(pos); - int miningLevel = BuildConfig.survivalBalancers.quickReplaceMiningLevel.get(); + int miningLevel = CommonConfig.survivalBalancers.quickReplaceMiningLevel.get(); switch (miningLevel) { case -1: return !state.requiresCorrectToolForDrops(); @@ -245,7 +249,7 @@ public class SurvivalHelper { //From World#mayPlace private static boolean mayPlace(Level world, Block blockIn, BlockState newBlockState, BlockPos pos, boolean skipCollisionCheck, Direction sidePlacedOn, @Nullable Entity placer) { - BlockState iblockstate1 = world.getBlockState(pos); + BlockState currentBlockState = world.getBlockState(pos); VoxelShape voxelShape = skipCollisionCheck ? null : blockIn.defaultBlockState().getCollisionShape(world, pos); if (voxelShape != null && !world.isUnobstructed(placer, voxelShape)) { @@ -259,22 +263,20 @@ public class SurvivalHelper { //Check if same block //Necessary otherwise extra items will be dropped - if (iblockstate1 == newBlockState) { + if (currentBlockState == newBlockState) { return false; } - //TODO 1.14 check what Material.CIRCUITS has become - if (iblockstate1.getMaterial() == Material.BUILDABLE_GLASS && blockIn == Blocks.ANVIL) { + if (currentBlockState.getMaterial() == Material.BUILDABLE_GLASS && blockIn == Blocks.ANVIL) { return true; } //Check quickreplace - if (placer instanceof Player && ModifierSettingsManager.getModifierSettings(((Player) placer)).doQuickReplace()) { + if (placer != null && ModifierSettingsManager.getModifierSettings(((Player) placer)).doQuickReplace()) { return true; } - //TODO 1.13 replaceable - return iblockstate1.getMaterial().isReplaceable() /*&& canPlaceBlockOnSide(world, pos, sidePlacedOn)*/; + return currentBlockState.getMaterial().isReplaceable() /*&& canPlaceBlockOnSide(world, pos, sidePlacedOn)*/; } diff --git a/src/main/java/nl/requios/effortlessbuilding/item/AbstractRandomizerBagItem.java b/src/main/java/nl/requios/effortlessbuilding/item/AbstractRandomizerBagItem.java index aa9eb22..0617705 100644 --- a/src/main/java/nl/requios/effortlessbuilding/item/AbstractRandomizerBagItem.java +++ b/src/main/java/nl/requios/effortlessbuilding/item/AbstractRandomizerBagItem.java @@ -136,7 +136,7 @@ public abstract class AbstractRandomizerBagItem extends Item { //Only place manually if in normal vanilla mode BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode(); ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - if (buildMode != BuildModes.BuildModeEnum.NORMAL || modifierSettings.doQuickReplace()) { + if (buildMode != BuildModes.BuildModeEnum.DISABLED || modifierSettings.doQuickReplace()) { return InteractionResult.FAIL; } diff --git a/src/main/java/nl/requios/effortlessbuilding/item/ReachUpgrade1Item.java b/src/main/java/nl/requios/effortlessbuilding/item/ReachUpgrade1Item.java index cd78755..8caef7a 100644 --- a/src/main/java/nl/requios/effortlessbuilding/item/ReachUpgrade1Item.java +++ b/src/main/java/nl/requios/effortlessbuilding/item/ReachUpgrade1Item.java @@ -9,7 +9,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.network.chat.Component; import net.minecraft.ChatFormatting; import net.minecraft.world.level.Level; -import nl.requios.effortlessbuilding.BuildConfig; +import nl.requios.effortlessbuilding.CommonConfig; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; import nl.requios.effortlessbuilding.helper.ReachHelper; @@ -63,7 +63,7 @@ public class ReachUpgrade1Item extends Item { @Override public void appendHoverText(ItemStack stack, @Nullable Level world, List tooltip, TooltipFlag flag) { - tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + BuildConfig.reach.maxReachLevel1.get())); + tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + CommonConfig.reach.maxReachLevel1.get())); } } diff --git a/src/main/java/nl/requios/effortlessbuilding/item/ReachUpgrade2Item.java b/src/main/java/nl/requios/effortlessbuilding/item/ReachUpgrade2Item.java index 17fbdce..5ac62ed 100644 --- a/src/main/java/nl/requios/effortlessbuilding/item/ReachUpgrade2Item.java +++ b/src/main/java/nl/requios/effortlessbuilding/item/ReachUpgrade2Item.java @@ -9,14 +9,13 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.network.chat.Component; import net.minecraft.ChatFormatting; import net.minecraft.world.level.Level; -import nl.requios.effortlessbuilding.BuildConfig; +import nl.requios.effortlessbuilding.CommonConfig; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; import nl.requios.effortlessbuilding.helper.ReachHelper; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; -import java.awt.*; import java.util.List; import net.minecraft.resources.ResourceLocation; @@ -68,7 +67,7 @@ public class ReachUpgrade2Item extends Item { @Override public void appendHoverText(ItemStack stack, @Nullable Level world, List tooltip, TooltipFlag flag) { - tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + BuildConfig.reach.maxReachLevel2.get())); + tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + CommonConfig.reach.maxReachLevel2.get())); tooltip.add(Component.literal(ChatFormatting.GRAY + "Previous upgrades need to be consumed first")); } } diff --git a/src/main/java/nl/requios/effortlessbuilding/item/ReachUpgrade3Item.java b/src/main/java/nl/requios/effortlessbuilding/item/ReachUpgrade3Item.java index 7aedbc6..2ed5a91 100644 --- a/src/main/java/nl/requios/effortlessbuilding/item/ReachUpgrade3Item.java +++ b/src/main/java/nl/requios/effortlessbuilding/item/ReachUpgrade3Item.java @@ -9,7 +9,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.network.chat.Component; import net.minecraft.ChatFormatting; import net.minecraft.world.level.Level; -import nl.requios.effortlessbuilding.BuildConfig; +import nl.requios.effortlessbuilding.CommonConfig; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; import nl.requios.effortlessbuilding.helper.ReachHelper; @@ -70,7 +70,7 @@ public class ReachUpgrade3Item extends Item { @Override public void appendHoverText(ItemStack stack, @Nullable Level world, List tooltip, TooltipFlag flag) { - tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + BuildConfig.reach.maxReachLevel3.get())); + tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + CommonConfig.reach.maxReachLevel3.get())); tooltip.add(Component.literal(ChatFormatting.GRAY + "Previous upgrades need to be consumed first")); } diff --git a/src/main/java/nl/requios/effortlessbuilding/network/AddUndoMessage.java b/src/main/java/nl/requios/effortlessbuilding/network/AddUndoMessage.java index 4a639b2..4a99723 100644 --- a/src/main/java/nl/requios/effortlessbuilding/network/AddUndoMessage.java +++ b/src/main/java/nl/requios/effortlessbuilding/network/AddUndoMessage.java @@ -14,7 +14,6 @@ import net.minecraftforge.network.NetworkEvent; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmodifier.BlockSet; import nl.requios.effortlessbuilding.buildmodifier.UndoRedo; -import nl.requios.effortlessbuilding.render.BlockPreviewRenderer; import java.util.ArrayList; import java.util.function.Supplier; diff --git a/src/main/java/nl/requios/effortlessbuilding/network/BlockPlacedMessage.java b/src/main/java/nl/requios/effortlessbuilding/network/BlockPlacedMessage.java index 2d68d31..f588936 100644 --- a/src/main/java/nl/requios/effortlessbuilding/network/BlockPlacedMessage.java +++ b/src/main/java/nl/requios/effortlessbuilding/network/BlockPlacedMessage.java @@ -12,7 +12,7 @@ import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.network.NetworkEvent; import nl.requios.effortlessbuilding.buildmode.BuildModes; -import nl.requios.effortlessbuilding.render.BlockPreviewRenderer; +import nl.requios.effortlessbuilding.render.BlockPreviews; import java.util.function.Supplier; @@ -112,7 +112,7 @@ public class BlockPlacedMessage { public static class ClientHandler { public static void handle(BlockPlacedMessage message, Supplier ctx) { //Nod RenderHandler to do the dissolve shader effect - BlockPreviewRenderer.onBlocksPlaced(); + BlockPreviews.onBlocksPlaced(); } } } diff --git a/src/main/java/nl/requios/effortlessbuilding/network/ClearUndoMessage.java b/src/main/java/nl/requios/effortlessbuilding/network/ClearUndoMessage.java index 0bd946c..1358242 100644 --- a/src/main/java/nl/requios/effortlessbuilding/network/ClearUndoMessage.java +++ b/src/main/java/nl/requios/effortlessbuilding/network/ClearUndoMessage.java @@ -9,7 +9,6 @@ import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.network.NetworkEvent; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmodifier.UndoRedo; -import nl.requios.effortlessbuilding.render.BlockPreviewRenderer; import java.util.function.Supplier; diff --git a/src/main/java/nl/requios/effortlessbuilding/network/RequestLookAtMessage.java b/src/main/java/nl/requios/effortlessbuilding/network/RequestLookAtMessage.java index d7f960b..093dcce 100644 --- a/src/main/java/nl/requios/effortlessbuilding/network/RequestLookAtMessage.java +++ b/src/main/java/nl/requios/effortlessbuilding/network/RequestLookAtMessage.java @@ -11,8 +11,6 @@ import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.network.NetworkEvent; import nl.requios.effortlessbuilding.ClientEvents; import nl.requios.effortlessbuilding.EffortlessBuilding; -import nl.requios.effortlessbuilding.proxy.ClientProxy; -import nl.requios.effortlessbuilding.render.BlockPreviewRenderer; import java.util.function.Supplier; diff --git a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java deleted file mode 100644 index 99284be..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java +++ /dev/null @@ -1,412 +0,0 @@ -package nl.requios.effortlessbuilding.render; - -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.client.Minecraft; -import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.renderer.block.BlockRenderDispatcher; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.core.Direction; -import net.minecraft.sounds.SoundSource; -import net.minecraft.core.BlockPos; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.util.Mth; -import net.minecraft.world.phys.HitResult; -import net.minecraft.world.phys.shapes.VoxelShape; -import net.minecraft.world.phys.Vec3; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -import nl.requios.effortlessbuilding.BuildConfig; -import nl.requios.effortlessbuilding.ClientEvents; -import nl.requios.effortlessbuilding.EffortlessBuilding; -import nl.requios.effortlessbuilding.EffortlessBuildingClient; -import nl.requios.effortlessbuilding.buildmode.BuildModes; -import nl.requios.effortlessbuilding.buildmode.IBuildMode; -import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; -import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager.ModeSettings; -import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers; -import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; -import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager.ModifierSettings; -import nl.requios.effortlessbuilding.compatibility.CompatHelper; -import nl.requios.effortlessbuilding.create.AllSpecialTextures; -import nl.requios.effortlessbuilding.create.CreateClient; -import nl.requios.effortlessbuilding.create.foundation.utility.Color; -import nl.requios.effortlessbuilding.helper.ReachHelper; -import nl.requios.effortlessbuilding.helper.SurvivalHelper; -import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem; -import nl.requios.effortlessbuilding.proxy.ClientProxy; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -@OnlyIn(Dist.CLIENT) -public class BlockPreviewRenderer { - private static final List placedDataList = new ArrayList<>(); - private static List previousCoordinates; - private static List previousBlockStates; - private static List previousItemStacks; - private static BlockPos previousFirstPos; - private static BlockPos previousSecondPos; - private static int soundTime = 0; - - public static void render(PoseStack ms, MultiBufferSource.BufferSource buffer, Player player, ModifierSettings modifierSettings, ModeSettings modeSettings) { - - //Render placed blocks with dissolve effect - //Use fancy shader if config allows, otherwise no dissolve - if (BuildConfig.visuals.useShaders.get()) { - for (int i = 0; i < placedDataList.size(); i++) { - PlacedData placed = placedDataList.get(i); - if (placed.coordinates != null && !placed.coordinates.isEmpty()) { - - double totalTime = Mth.clampedLerp(30, 60, placed.firstPos.distSqr(placed.secondPos) / 100.0) * BuildConfig.visuals.dissolveTimeMultiplier.get(); - float dissolve = (ClientEvents.ticksInGame - placed.time) / (float) totalTime; - renderBlockPreviews(ms, buffer, placed.coordinates, placed.blockStates, placed.itemStacks, dissolve, placed.firstPos, placed.secondPos, false, placed.breaking); - } - } - } - //Expire - placedDataList.removeIf(placed -> { - double totalTime = Mth.clampedLerp(30, 60, placed.firstPos.distSqr(placed.secondPos) / 100.0) * BuildConfig.visuals.dissolveTimeMultiplier.get(); - return placed.time + totalTime < ClientEvents.ticksInGame; - }); - - //Render block previews - HitResult lookingAt = ClientEvents.getLookingAt(player); - if (modeSettings.getBuildMode() == BuildModes.BuildModeEnum.NORMAL) - lookingAt = Minecraft.getInstance().hitResult; - - ItemStack mainhand = player.getMainHandItem(); - boolean noBlockInHand = !(!mainhand.isEmpty() && CompatHelper.isItemBlockProxy(mainhand)); - - BlockPos startPos = null; - Direction sideHit = null; - Vec3 hitVec = null; - - //Checking for null is necessary! Even in vanilla when looking down ladders it is occasionally null (instead of Type MISS) - if (lookingAt != null && lookingAt.getType() == HitResult.Type.BLOCK) { - BlockHitResult blockLookingAt = (BlockHitResult) lookingAt; - startPos = blockLookingAt.getBlockPos(); - - //Check if tool (or none) in hand - //TODO 1.13 replaceable - boolean replaceable = player.level.getBlockState(startPos).getMaterial().isReplaceable(); - boolean becomesDoubleSlab = SurvivalHelper.doesBecomeDoubleSlab(player, startPos, blockLookingAt.getDirection()); - if (!modifierSettings.doQuickReplace() && !noBlockInHand && !replaceable && !becomesDoubleSlab) { - startPos = startPos.relative(blockLookingAt.getDirection()); - } - - //Get under tall grass and other replaceable blocks - if (modifierSettings.doQuickReplace() && !noBlockInHand && replaceable) { - startPos = startPos.below(); - } - - sideHit = blockLookingAt.getDirection(); - hitVec = blockLookingAt.getLocation(); - } - - //Dont render if in normal mode and modifiers are disabled - //Unless alwaysShowBlockPreview is true in config - if (doRenderBlockPreviews(modifierSettings, modeSettings, startPos)) { - - //Keep blockstate the same for every block in the buildmode - //So dont rotate blocks when in the middle of placing wall etc. - if (BuildModes.isActive(player)) { - IBuildMode buildModeInstance = modeSettings.getBuildMode().instance; - if (buildModeInstance.getSideHit(player) != null) sideHit = buildModeInstance.getSideHit(player); - if (buildModeInstance.getHitVec(player) != null) hitVec = buildModeInstance.getHitVec(player); - } - - if (sideHit != null) { - - //Should be red? - boolean breaking = BuildModes.currentlyBreakingClient.get(player) != null && BuildModes.currentlyBreakingClient.get(player); - - //get coordinates - List startCoordinates = BuildModes.findCoordinates(player, startPos, breaking || modifierSettings.doQuickReplace()); - - //Remember first and last point for the shader - BlockPos firstPos = BlockPos.ZERO, secondPos = BlockPos.ZERO; - if (!startCoordinates.isEmpty()) { - firstPos = startCoordinates.get(0); - secondPos = startCoordinates.get(startCoordinates.size() - 1); - } - - //Limit number of blocks you can place - int limit = ReachHelper.getMaxBlocksPlacedAtOnce(player); - if (startCoordinates.size() > limit) { - startCoordinates = startCoordinates.subList(0, limit); - } - - List newCoordinates = BuildModifiers.findCoordinates(player, startCoordinates); - - sortOnDistanceToPlayer(newCoordinates, player); - - hitVec = new Vec3(Math.abs(hitVec.x - ((int) hitVec.x)), Math.abs(hitVec.y - ((int) hitVec.y)), - Math.abs(hitVec.z - ((int) hitVec.z))); - - //Get blockstates - List itemStacks = new ArrayList<>(); - List blockStates = new ArrayList<>(); - if (breaking) { - //Find blockstate of world - for (BlockPos coordinate : newCoordinates) { - blockStates.add(player.level.getBlockState(coordinate)); - } - } else { - blockStates = BuildModifiers.findBlockStates(player, startCoordinates, hitVec, sideHit, itemStacks); - } - - - //Check if they are different from previous - //TODO fix triggering when moving player - if (!BuildModifiers.compareCoordinates(previousCoordinates, newCoordinates)) { - previousCoordinates = newCoordinates; - //remember the rest for placed blocks - previousBlockStates = blockStates; - previousItemStacks = itemStacks; - previousFirstPos = firstPos; - previousSecondPos = secondPos; - - //if so, renew randomness of randomizer bag - AbstractRandomizerBagItem.renewRandomness(); - //and play sound (max once every tick) - if (newCoordinates.size() > 1 && blockStates.size() > 1 && soundTime < ClientEvents.ticksInGame - 0) { - soundTime = ClientEvents.ticksInGame; - - if (blockStates.get(0) != null) { - SoundType soundType = blockStates.get(0).getBlock().getSoundType(blockStates.get(0), player.level, - newCoordinates.get(0), player); - player.level.playSound(player, player.blockPosition(), breaking ? soundType.getBreakSound() : soundType.getPlaceSound(), - SoundSource.BLOCKS, 0.3f, 0.8f); - } - } - } - - //Render block previews - if (blockStates.size() != 0 && newCoordinates.size() == blockStates.size()) { - int blockCount; - - if (!breaking) { - //Use fancy shader if config allows, otherwise outlines - if (BuildConfig.visuals.useShaders.get() && newCoordinates.size() < BuildConfig.visuals.shaderThreshold.get()) { - blockCount = renderBlockPreviews(ms, buffer, newCoordinates, blockStates, itemStacks, 0f, firstPos, secondPos, !breaking, breaking); - - CreateClient.OUTLINER.showCluster("blockpreviews", newCoordinates) - .withFaceTexture(AllSpecialTextures.CHECKERED) - .disableNormals() - .lineWidth(1 / 32f) - .colored(new Color(1f, 1f, 1f, 1f)); - } else { -// VertexConsumer vc = RenderHandler.beginLines(buffer); -// -// Vec3 color = new Vec3(1f, 1f, 1f); -// if (breaking) color = new Vec3(1f, 0f, 0f); -// -// for (int i = newCoordinates.size() - 1; i >= 0; i--) { -// VoxelShape collisionShape = blockStates.get(i).getCollisionShape(player.level, newCoordinates.get(i)); -// RenderHandler.renderBlockOutline(ms, vc, newCoordinates.get(i), collisionShape, color); -// } -// -// RenderHandler.endLines(buffer); - - CreateClient.OUTLINER.showCluster("blockpreviews", newCoordinates) - .withFaceTexture(AllSpecialTextures.HIGHLIGHT_CHECKERED) - .disableNormals() - .lineWidth(1 / 16f) - .colored(new Color(1f, 1f, 1f, 1f)); - - blockCount = newCoordinates.size(); - } - - } else { - //Breaking - CreateClient.OUTLINER.showCluster("blockpreviews", newCoordinates) - .withFaceTexture(AllSpecialTextures.THIN_CHECKERED) - .disableNormals() - .lineWidth(1 / 16f) - .colored(new Color(0.8f, 0.1f, 0.1f, 1f)); - blockCount = newCoordinates.size(); - } - - //Display block count and dimensions in actionbar - if (BuildModes.isActive(player)) { - - //Find min and max values (not simply firstPos and secondPos because that doesn't work with circles) - int minX = Integer.MAX_VALUE, maxX = Integer.MIN_VALUE; - int minY = Integer.MAX_VALUE, maxY = Integer.MIN_VALUE; - int minZ = Integer.MAX_VALUE, maxZ = Integer.MIN_VALUE; - for (BlockPos pos : startCoordinates) { - if (pos.getX() < minX) minX = pos.getX(); - if (pos.getX() > maxX) maxX = pos.getX(); - if (pos.getY() < minY) minY = pos.getY(); - if (pos.getY() > maxY) maxY = pos.getY(); - if (pos.getZ() < minZ) minZ = pos.getZ(); - if (pos.getZ() > maxZ) maxZ = pos.getZ(); - } - BlockPos dim = new BlockPos(maxX - minX + 1, maxY - minY + 1, maxZ - minZ + 1); - - String dimensions = "("; - if (dim.getX() > 1) dimensions += dim.getX() + "x"; - if (dim.getZ() > 1) dimensions += dim.getZ() + "x"; - if (dim.getY() > 1) dimensions += dim.getY() + "x"; - dimensions = dimensions.substring(0, dimensions.length() - 1); - if (dimensions.length() > 1) dimensions += ")"; - - EffortlessBuilding.log(player, blockCount + " blocks " + dimensions, true); - } - } - - - } - - //Draw outlines if no block in hand - //Find proper raytrace: either normal range or increased range depending on canBreakFar -// VertexConsumer vc = RenderHandler.beginLines(buffer); -// HitResult objectMouseOver = Minecraft.getInstance().hitResult; -// HitResult breakingRaytrace = ReachHelper.canBreakFar(player) ? lookingAt : objectMouseOver; -// -// if (player.isCreative() && noBlockInHand && breakingRaytrace != null && breakingRaytrace.getType() == HitResult.Type.BLOCK) { -// BlockHitResult blockBreakingRaytrace = (BlockHitResult) breakingRaytrace; -// List breakCoordinates = BuildModifiers.findCoordinates(player, blockBreakingRaytrace.getBlockPos()); -// -// //Only render first outline if further than normal reach -// boolean excludeFirst = objectMouseOver != null && objectMouseOver.getType() == HitResult.Type.BLOCK; -// for (int i = excludeFirst ? 1 : 0; i < breakCoordinates.size(); i++) { -// BlockPos coordinate = breakCoordinates.get(i); -// -// BlockState blockState = player.level.getBlockState(coordinate); -// if (!blockState.isAir()) { -// if (SurvivalHelper.canBreak(player.level, player, coordinate) || i == 0) { -// VoxelShape collisionShape = blockState.getCollisionShape(player.level, coordinate); -// RenderHandler.renderBlockOutline(ms, vc, coordinate, collisionShape, new Vec3(0f, 0f, 0f)); -// } -// } -// } -// } -// RenderHandler.endLines(buffer); - } - } - - //Whether to draw any block previews or outlines - public static boolean doRenderBlockPreviews(ModifierSettings modifierSettings, ModeSettings modeSettings, BlockPos startPos) { - return modeSettings.getBuildMode() != BuildModes.BuildModeEnum.NORMAL || - (startPos != null && BuildModifiers.isEnabled(modifierSettings, startPos)) || - BuildConfig.visuals.alwaysShowBlockPreview.get(); - } - - protected static int renderBlockPreviews(PoseStack matrixStack, MultiBufferSource.BufferSource renderTypeBuffer, List coordinates, List blockStates, - List itemStacks, float dissolve, BlockPos firstPos, - BlockPos secondPos, boolean checkCanPlace, boolean red) { - Player player = Minecraft.getInstance().player; - ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - BlockRenderDispatcher dispatcher = Minecraft.getInstance().getBlockRenderer(); - int blocksValid = 0; - - if (coordinates.isEmpty()) return blocksValid; - - for (int i = coordinates.size() - 1; i >= 0; i--) { - BlockPos blockPos = coordinates.get(i); - BlockState blockState = blockStates.get(i); - ItemStack itemstack = itemStacks.isEmpty() ? ItemStack.EMPTY : itemStacks.get(i); - if (CompatHelper.isItemBlockProxy(itemstack)) - itemstack = CompatHelper.getItemBlockByState(itemstack, blockState); - - //Check if we can place - //If check is turned off, check if blockstate is the same (for dissolve effect) - if ((!checkCanPlace && player.level.getBlockState(blockPos) == blockState) || //TODO enable (breaks the breaking shader) - SurvivalHelper.canPlace(player.level, player, blockPos, blockState, itemstack, modifierSettings.doQuickReplace(), Direction.UP)) { - - RenderHandler.renderBlockPreview(matrixStack, renderTypeBuffer, dispatcher, blockPos, blockState, dissolve, firstPos, secondPos, red); - blocksValid++; - } - } - return blocksValid; - } - - public static void onBlocksPlaced() { - onBlocksPlaced(previousCoordinates, previousItemStacks, previousBlockStates, previousFirstPos, previousSecondPos); - } - - public static void onBlocksPlaced(List coordinates, List itemStacks, List blockStates, - BlockPos firstPos, BlockPos secondPos) { - LocalPlayer player = Minecraft.getInstance().player; - ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); - - //Check if block previews are enabled - if (doRenderBlockPreviews(modifierSettings, modeSettings, firstPos)) { - - //Save current coordinates, blockstates and itemstacks - if (!coordinates.isEmpty() && blockStates.size() == coordinates.size() && - coordinates.size() > 1 && coordinates.size() < BuildConfig.visuals.shaderThreshold.get()) { - - placedDataList.add(new PlacedData(ClientEvents.ticksInGame, coordinates, blockStates, - itemStacks, firstPos, secondPos, false)); - } - } - - } - - public static void onBlocksBroken() { - onBlocksBroken(previousCoordinates, previousItemStacks, previousBlockStates, previousFirstPos, previousSecondPos); - } - - public static void onBlocksBroken(List coordinates, List itemStacks, List blockStates, - BlockPos firstPos, BlockPos secondPos) { - LocalPlayer player = Minecraft.getInstance().player; - ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); - - //Check if block previews are enabled - if (doRenderBlockPreviews(modifierSettings, modeSettings, firstPos)) { - - //Save current coordinates, blockstates and itemstacks - if (!coordinates.isEmpty() && blockStates.size() == coordinates.size() && - coordinates.size() > 1 && coordinates.size() < BuildConfig.visuals.shaderThreshold.get()) { - - sortOnDistanceToPlayer(coordinates, player); - - placedDataList.add(new PlacedData(ClientEvents.ticksInGame, coordinates, blockStates, - itemStacks, firstPos, secondPos, true)); - } - } - - } - - private static void sortOnDistanceToPlayer(List coordinates, Player player) { - - Collections.sort(coordinates, (lhs, rhs) -> { - // -1 - less than, 1 - greater than, 0 - equal - double lhsDistanceToPlayer = Vec3.atLowerCornerOf(lhs).subtract(player.getEyePosition(1f)).lengthSqr(); - double rhsDistanceToPlayer = Vec3.atLowerCornerOf(rhs).subtract(player.getEyePosition(1f)).lengthSqr(); - return (int) Math.signum(lhsDistanceToPlayer - rhsDistanceToPlayer); - }); - - } - - static class PlacedData { - float time; - List coordinates; - List blockStates; - List itemStacks; - BlockPos firstPos; - BlockPos secondPos; - boolean breaking; - - public PlacedData(float time, List coordinates, List blockStates, - List itemStacks, BlockPos firstPos, BlockPos secondPos, boolean breaking) { - this.time = time; - this.coordinates = coordinates; - this.blockStates = blockStates; - this.itemStacks = itemStacks; - this.firstPos = firstPos; - this.secondPos = secondPos; - this.breaking = breaking; - } - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java new file mode 100644 index 0000000..1e20aca --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java @@ -0,0 +1,414 @@ +package nl.requios.effortlessbuilding.render; + +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.core.Direction; +import net.minecraft.sounds.SoundSource; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import nl.requios.effortlessbuilding.ClientConfig; +import nl.requios.effortlessbuilding.ClientEvents; +import nl.requios.effortlessbuilding.CommonConfig; +import nl.requios.effortlessbuilding.EffortlessBuilding; +import nl.requios.effortlessbuilding.buildmode.BuildModes; +import nl.requios.effortlessbuilding.buildmode.IBuildMode; +import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; +import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager.ModeSettings; +import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers; +import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; +import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager.ModifierSettings; +import nl.requios.effortlessbuilding.compatibility.CompatHelper; +import nl.requios.effortlessbuilding.create.AllSpecialTextures; +import nl.requios.effortlessbuilding.create.CreateClient; +import nl.requios.effortlessbuilding.create.foundation.utility.Color; +import nl.requios.effortlessbuilding.helper.ReachHelper; +import nl.requios.effortlessbuilding.helper.SurvivalHelper; +import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@OnlyIn(Dist.CLIENT) +public class BlockPreviews { + private static final List placedDataList = new ArrayList<>(); + private static List previousCoordinates; + private static List previousBlockStates; + private static List previousItemStacks; + private static BlockPos previousFirstPos; + private static BlockPos previousSecondPos; + private static int soundTime = 0; + + public static void drawPlacedBlocks(Player player, ModifierSettings modifierSettings) { + //Render placed blocks with appear animation + if (ClientConfig.visuals.showBlockPreviews.get()) { + for (PlacedData placed : placedDataList) { + if (placed.coordinates != null && !placed.coordinates.isEmpty()) { + + int totalTime = placed.breaking ? CommonConfig.visuals.breakAnimationLength.get() : CommonConfig.visuals.appearAnimationLength.get(); + if (totalTime <= 0) continue; + + float dissolve = (ClientEvents.ticksInGame - placed.time) / (float) totalTime; + renderBlockPreviews(player, modifierSettings, placed.coordinates, placed.blockStates, placed.itemStacks, dissolve, placed.firstPos, placed.secondPos, false, placed.breaking); + + CreateClient.OUTLINER.showCluster(placed.time, placed.coordinates); + } + } + } + + //Expire + placedDataList.removeIf(placed -> { + int totalTime = placed.breaking ? CommonConfig.visuals.breakAnimationLength.get() : CommonConfig.visuals.appearAnimationLength.get(); + return placed.time + totalTime < ClientEvents.ticksInGame; + }); + } + + public static void drawLookAtPreview(Player player, ModeSettings modeSettings, ModifierSettings modifierSettings, BlockPos startPos, Direction sideHit, Vec3 hitVec) { + if (!doShowBlockPreviews(modifierSettings, modeSettings, startPos)) return; + + //Keep blockstate the same for every block in the buildmode + //So dont rotate blocks when in the middle of placing wall etc. + if (BuildModes.isActive(player)) { + IBuildMode buildModeInstance = modeSettings.getBuildMode().instance; + if (buildModeInstance.getSideHit(player) != null) sideHit = buildModeInstance.getSideHit(player); + if (buildModeInstance.getHitVec(player) != null) hitVec = buildModeInstance.getHitVec(player); + } + + if (sideHit == null) return; + + //Should be red? + boolean breaking = BuildModes.currentlyBreakingClient.get(player) != null && BuildModes.currentlyBreakingClient.get(player); + + //get coordinates + List startCoordinates = BuildModes.findCoordinates(player, startPos, breaking || modifierSettings.doQuickReplace()); + + //Remember first and last point for the shader + BlockPos firstPos = BlockPos.ZERO, secondPos = BlockPos.ZERO; + if (!startCoordinates.isEmpty()) { + firstPos = startCoordinates.get(0); + secondPos = startCoordinates.get(startCoordinates.size() - 1); + } + + //Limit number of blocks you can place + int limit = ReachHelper.getMaxBlocksPlacedAtOnce(player); + if (startCoordinates.size() > limit) { + startCoordinates = startCoordinates.subList(0, limit); + } + + List newCoordinates = BuildModifiers.findCoordinates(player, startCoordinates); + + sortOnDistanceToPlayer(newCoordinates, player); + + hitVec = new Vec3(Math.abs(hitVec.x - ((int) hitVec.x)), Math.abs(hitVec.y - ((int) hitVec.y)), + Math.abs(hitVec.z - ((int) hitVec.z))); + + //Get blockstates + List itemStacks = new ArrayList<>(); + List blockStates = new ArrayList<>(); + if (breaking) { + //Find blockstate of world + for (BlockPos coordinate : newCoordinates) { + blockStates.add(player.level.getBlockState(coordinate)); + } + } else { + blockStates = BuildModifiers.findBlockStates(player, startCoordinates, hitVec, sideHit, itemStacks); + } + + + //Check if they are different from previous + //TODO fix triggering when moving player + if (!BuildModifiers.compareCoordinates(previousCoordinates, newCoordinates)) { + previousCoordinates = newCoordinates; + //remember the rest for placed blocks + previousBlockStates = blockStates; + previousItemStacks = itemStacks; + previousFirstPos = firstPos; + previousSecondPos = secondPos; + + //if so, renew randomness of randomizer bag + AbstractRandomizerBagItem.renewRandomness(); + //and play sound (max once every tick) + if (newCoordinates.size() > 1 && blockStates.size() > 1 && soundTime < ClientEvents.ticksInGame - 0) { + soundTime = ClientEvents.ticksInGame; + + if (blockStates.get(0) != null) { + SoundType soundType = blockStates.get(0).getBlock().getSoundType(blockStates.get(0), player.level, + newCoordinates.get(0), player); + player.level.playSound(player, player.blockPosition(), breaking ? soundType.getBreakSound() : soundType.getPlaceSound(), + SoundSource.BLOCKS, 0.3f, 0.8f); + } + } + } + + if (blockStates.size() == 0 || newCoordinates.size() != blockStates.size()) return; + + int blockCount; + + Object outlineID = firstPos; + //Dont fade out the outline if we are still determining where to place + //Every outline with same ID will not fade out (because it gets replaced) + if (newCoordinates.size() == 1 || BuildModifiers.isEnabled(modifierSettings, firstPos)) outlineID = "single"; + + if (!breaking) { + //Use fancy shader if config allows, otherwise outlines + if (ClientConfig.visuals.showBlockPreviews.get() && newCoordinates.size() < ClientConfig.visuals.maxBlockPreviews.get()) { + blockCount = renderBlockPreviews(player, modifierSettings, newCoordinates, blockStates, itemStacks, 0f, firstPos, secondPos, !breaking, breaking); + + CreateClient.OUTLINER.showCluster(outlineID, newCoordinates) + .withFaceTexture(AllSpecialTextures.CHECKERED) + .disableNormals() + .lineWidth(1 / 32f) + .colored(new Color(1f, 1f, 1f, 1f)); + } else { + //Thicker outline without block previews + CreateClient.OUTLINER.showCluster(outlineID, newCoordinates) + .withFaceTexture(AllSpecialTextures.HIGHLIGHT_CHECKERED) + .disableNormals() + .lineWidth(1 / 16f) + .colored(new Color(1f, 1f, 1f, 1f)); + + blockCount = newCoordinates.size(); + } + + } else { + //Breaking + CreateClient.OUTLINER.showCluster(outlineID, newCoordinates) + .withFaceTexture(AllSpecialTextures.THIN_CHECKERED) + .disableNormals() + .lineWidth(1 / 16f) + .colored(new Color(0.8f, 0.1f, 0.1f, 1f)); + blockCount = newCoordinates.size(); + } + + //Display block count and dimensions in actionbar + if (BuildModes.isActive(player)) { + + //Find min and max values (not simply firstPos and secondPos because that doesn't work with circles) + int minX = Integer.MAX_VALUE, maxX = Integer.MIN_VALUE; + int minY = Integer.MAX_VALUE, maxY = Integer.MIN_VALUE; + int minZ = Integer.MAX_VALUE, maxZ = Integer.MIN_VALUE; + for (BlockPos pos : startCoordinates) { + if (pos.getX() < minX) minX = pos.getX(); + if (pos.getX() > maxX) maxX = pos.getX(); + if (pos.getY() < minY) minY = pos.getY(); + if (pos.getY() > maxY) maxY = pos.getY(); + if (pos.getZ() < minZ) minZ = pos.getZ(); + if (pos.getZ() > maxZ) maxZ = pos.getZ(); + } + BlockPos dim = new BlockPos(maxX - minX + 1, maxY - minY + 1, maxZ - minZ + 1); + + String dimensions = "("; + if (dim.getX() > 1) dimensions += dim.getX() + "x"; + if (dim.getZ() > 1) dimensions += dim.getZ() + "x"; + if (dim.getY() > 1) dimensions += dim.getY() + "x"; + dimensions = dimensions.substring(0, dimensions.length() - 1); + if (dimensions.length() > 1) dimensions += ")"; + + EffortlessBuilding.log(player, blockCount + " blocks " + dimensions, true); + } + } + + public static void drawOutlinesIfNoBlockInHand(Player player, HitResult lookingAt) { + //Draw outlines if no block in hand + //Find proper raytrace: either normal range or increased range depending on canBreakFar + HitResult objectMouseOver = Minecraft.getInstance().hitResult; + HitResult breakingRaytrace = ReachHelper.canBreakFar(player) ? lookingAt : objectMouseOver; + + if (player.isCreative() && breakingRaytrace != null && breakingRaytrace.getType() == HitResult.Type.BLOCK) { + BlockHitResult blockBreakingRaytrace = (BlockHitResult) breakingRaytrace; + List breakCoordinates = BuildModifiers.findCoordinates(player, blockBreakingRaytrace.getBlockPos()); + + //Only render first outline if further than normal reach + if (objectMouseOver != null && objectMouseOver.getType() == HitResult.Type.BLOCK) + breakCoordinates.remove(0); + + breakCoordinates.removeIf(pos -> { + BlockState blockState = player.level.getBlockState(pos); + if (blockState.isAir() || blockState.getMaterial().isLiquid()) return true; + return !SurvivalHelper.canBreak(player.level, player, pos); + }); + + if (!breakCoordinates.isEmpty()) { + CreateClient.OUTLINER.showCluster("break", breakCoordinates) + .disableNormals() + .lineWidth(1 / 64f) + .colored(0x222222); + } + } + } + + //Whether to draw any block previews or outlines + public static boolean doShowBlockPreviews(ModifierSettings modifierSettings, ModeSettings modeSettings, BlockPos startPos) { + if (!ClientConfig.visuals.showBlockPreviews.get()) return false; + return modeSettings.getBuildMode() != BuildModes.BuildModeEnum.DISABLED || + (startPos != null && BuildModifiers.isEnabled(modifierSettings, startPos)) || + ClientConfig.visuals.alwaysShowBlockPreview.get(); + } + + protected static int renderBlockPreviews(Player player, ModifierSettings modifierSettings, List coordinates, + List blockStates, List itemStacks, float dissolve, + BlockPos firstPos, BlockPos secondPos, boolean checkCanPlace, boolean red) { + int blocksValid = 0; + + if (coordinates.isEmpty()) return blocksValid; + + for (int i = coordinates.size() - 1; i >= 0; i--) { + BlockPos blockPos = coordinates.get(i); + BlockState blockState = blockStates.get(i); + ItemStack itemstack = itemStacks.isEmpty() ? ItemStack.EMPTY : itemStacks.get(i); + if (CompatHelper.isItemBlockProxy(itemstack)) + itemstack = CompatHelper.getItemBlockByState(itemstack, blockState); + + //Check if we can place + boolean canPlace = true; + if (checkCanPlace) { + canPlace = SurvivalHelper.canPlace(player.level, player, blockPos, blockState, itemstack, modifierSettings.doQuickReplace(), Direction.UP); + } else { + //If check is turned off, check if blockstate is the same (for dissolve effect) + canPlace = player.level.getBlockState(blockPos) != blockState; + } + + if (canPlace) { + renderBlockPreview(blockPos, blockState, dissolve, firstPos, secondPos, red); + blocksValid++; + } + } + return blocksValid; + } + + protected static void renderBlockPreview(BlockPos blockPos, BlockState blockState, float dissolve, BlockPos firstPos, BlockPos secondPos, boolean breaking) { + if (blockState == null) return; + + float scale = 0.5f; + float alpha = 0.7f; + if (dissolve > 0f) { + float animationLength = 0.7f; + + double firstToSecond = secondPos.distSqr(firstPos); + double place = 0; + if (firstToSecond > 0.5) { + double placeFromFirst = firstPos.distSqr(blockPos) / firstToSecond; + double placeFromSecond = secondPos.distSqr(blockPos) / firstToSecond; + place = (placeFromFirst + (1.0 - placeFromSecond)) / 2.0; + } //else only one block + + //Scale place so we start first animation at 0 and end last animation at 1 + place *= 1f - animationLength; + float diff = dissolve - (float) place; + float t = diff / animationLength; + t = Mth.clamp(t, 0, 1); + //Now we got a usable t value for this block + + float sine = Mth.sin((t + (1/7f) * t) * Mth.TWO_PI - (3/4f) * Mth.PI); // -1 to 1 + sine = (sine + 1f) / 2f; // 0 to 1 + +// scale = 1f + (sine * 0.5f); + + t = (float) Mth.smoothstep(t); + if (!breaking) { + scale = 0.5f + (t * 0.3f); + alpha = 0.7f + (t * 0.3f); + } else { + t = 1f - t; + scale = 0.5f + (t * 0.5f); + alpha = t; + } + } + + CreateClient.GHOST_BLOCKS.showGhostState(blockPos.toShortString(), blockState) + .at(blockPos) + .alpha(alpha) + .scale(scale); + } + + public static void onBlocksPlaced() { + onBlocksPlaced(previousCoordinates, previousItemStacks, previousBlockStates, previousFirstPos, previousSecondPos); + } + + public static void onBlocksPlaced(List coordinates, List itemStacks, List blockStates, + BlockPos firstPos, BlockPos secondPos) { + LocalPlayer player = Minecraft.getInstance().player; + ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); + ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); + + //Check if block previews are enabled + if (doShowBlockPreviews(modifierSettings, modeSettings, firstPos)) { + + //Save current coordinates, blockstates and itemstacks + if (!coordinates.isEmpty() && blockStates.size() == coordinates.size() && + coordinates.size() > 1 && coordinates.size() < ClientConfig.visuals.maxBlockPreviews.get()) { + + placedDataList.add(new PlacedData(ClientEvents.ticksInGame, coordinates, blockStates, + itemStacks, firstPos, secondPos, false)); + } + } + + } + + public static void onBlocksBroken() { + onBlocksBroken(previousCoordinates, previousItemStacks, previousBlockStates, previousFirstPos, previousSecondPos); + } + + public static void onBlocksBroken(List coordinates, List itemStacks, List blockStates, + BlockPos firstPos, BlockPos secondPos) { + LocalPlayer player = Minecraft.getInstance().player; + ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); + ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); + + //Check if block previews are enabled + if (doShowBlockPreviews(modifierSettings, modeSettings, firstPos)) { + + //Save current coordinates, blockstates and itemstacks + if (!coordinates.isEmpty() && blockStates.size() == coordinates.size() && + coordinates.size() > 1 && coordinates.size() < ClientConfig.visuals.maxBlockPreviews.get()) { + + sortOnDistanceToPlayer(coordinates, player); + + placedDataList.add(new PlacedData(ClientEvents.ticksInGame, coordinates, blockStates, + itemStacks, firstPos, secondPos, true)); + } + } + + } + + private static void sortOnDistanceToPlayer(List coordinates, Player player) { + + Collections.sort(coordinates, (lhs, rhs) -> { + // -1 - less than, 1 - greater than, 0 - equal + double lhsDistanceToPlayer = Vec3.atLowerCornerOf(lhs).subtract(player.getEyePosition(1f)).lengthSqr(); + double rhsDistanceToPlayer = Vec3.atLowerCornerOf(rhs).subtract(player.getEyePosition(1f)).lengthSqr(); + return (int) Math.signum(lhsDistanceToPlayer - rhsDistanceToPlayer); + }); + + } + + static class PlacedData { + float time; + List coordinates; + List blockStates; + List itemStacks; + BlockPos firstPos; + BlockPos secondPos; + boolean breaking; + + public PlacedData(float time, List coordinates, List blockStates, + List itemStacks, BlockPos firstPos, BlockPos secondPos, boolean breaking) { + this.time = time; + this.coordinates = coordinates; + this.blockStates = blockStates; + this.itemStacks = itemStacks; + this.firstPos = firstPos; + this.secondPos = secondPos; + this.breaking = breaking; + } + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java b/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java index 0f2e5e6..65c5668 100644 --- a/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java +++ b/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java @@ -5,32 +5,25 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.Sheets; -import net.minecraft.client.renderer.block.BlockRenderDispatcher; -import net.minecraft.client.renderer.texture.OverlayTexture; -import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.AABB; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; -import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.RenderLevelLastEvent; import net.minecraftforge.client.event.RenderLevelStageEvent; -import net.minecraftforge.client.model.data.ModelData; import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod.EventBusSubscriber; -import nl.requios.effortlessbuilding.EffortlessBuilding; +import nl.requios.effortlessbuilding.ClientEvents; +import nl.requios.effortlessbuilding.buildmode.BuildModes; import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; -import nl.requios.effortlessbuilding.create.CreateClient; -import nl.requios.effortlessbuilding.create.events.ClientEvents; +import nl.requios.effortlessbuilding.compatibility.CompatHelper; +import nl.requios.effortlessbuilding.helper.SurvivalHelper; /*** * Main render class for Effortless Building @@ -40,26 +33,64 @@ public class RenderHandler { @SubscribeEvent public static void onTick(TickEvent.ClientTickEvent event) { - if (!ClientEvents.isGameActive()) return; + if (!nl.requios.effortlessbuilding.create.events.ClientEvents.isGameActive()) return; - Player player = Minecraft.getInstance().player; - ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); - ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); + var player = Minecraft.getInstance().player; + var modeSettings = ModeSettingsManager.getModeSettings(player); + var modifierSettings = ModifierSettingsManager.getModifierSettings(player); - BlockPreviewRenderer.render(null, null, player, modifierSettings, modeSettings); + BlockPreviews.drawPlacedBlocks(player, modifierSettings); + + + HitResult lookingAt = ClientEvents.getLookingAt(player); + if (modeSettings.getBuildMode() == BuildModes.BuildModeEnum.DISABLED) + lookingAt = Minecraft.getInstance().hitResult; + + ItemStack mainhand = player.getMainHandItem(); + boolean noBlockInHand = !(!mainhand.isEmpty() && CompatHelper.isItemBlockProxy(mainhand)); + + //Find start position, side hit and hit vector + BlockPos startPos = null; + Direction sideHit = null; + Vec3 hitVec = null; + + //Checking for null is necessary! Even in vanilla when looking down ladders it is occasionally null (instead of Type MISS) + if (lookingAt != null && lookingAt.getType() == HitResult.Type.BLOCK) { + BlockHitResult blockLookingAt = (BlockHitResult) lookingAt; + startPos = blockLookingAt.getBlockPos(); + + //Check if tool (or none) in hand + boolean replaceable = player.level.getBlockState(startPos).getMaterial().isReplaceable(); + boolean becomesDoubleSlab = SurvivalHelper.doesBecomeDoubleSlab(player, startPos, blockLookingAt.getDirection()); + if (!modifierSettings.doQuickReplace() && !noBlockInHand && !replaceable && !becomesDoubleSlab) { + startPos = startPos.relative(blockLookingAt.getDirection()); + } + + //Get under tall grass and other replaceable blocks + if (modifierSettings.doQuickReplace() && !noBlockInHand && replaceable) { + startPos = startPos.below(); + } + + sideHit = blockLookingAt.getDirection(); + hitVec = blockLookingAt.getLocation(); + } + + + BlockPreviews.drawLookAtPreview(player, modeSettings, modifierSettings, startPos, sideHit, hitVec); + + if (noBlockInHand) BlockPreviews.drawOutlinesIfNoBlockInHand(player, lookingAt); } @SubscribeEvent - public static void onRender(RenderLevelLastEvent event) { + public static void onRender(RenderLevelStageEvent event) { + if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_TRANSLUCENT_BLOCKS) return; + Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); -// float pt = AnimationTickHolder.getPartialTicks(); PoseStack ms = event.getPoseStack(); BufferBuilder bufferBuilder = Tesselator.getInstance().getBuilder(); MultiBufferSource.BufferSource buffer = MultiBufferSource.immediate(bufferBuilder); -// SuperRenderTypeBuffer superBuffer = SuperRenderTypeBuffer.getInstance(); - Player player = Minecraft.getInstance().player; ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); @@ -70,15 +101,6 @@ public class RenderHandler { //Mirror and radial mirror lines and areas ModifierRenderer.render(ms, buffer, modifierSettings); - //Render block previews - BlockPreviewRenderer.render(ms, buffer, player, modifierSettings, modeSettings); - - //Create -// CreateClient.GHOST_BLOCKS.renderAll(ms, superBuffer); -// EffortlessBuildingClient.OUTLINER.renderOutlines(ms, superBuffer, pt); -// superBuffer.draw(); -// RenderSystem.enableCull(); - ms.popPose(); } @@ -98,82 +120,4 @@ public class RenderHandler { renderTypeBuffer.endBatch(); } - protected static void renderBlockPreview(PoseStack ms, MultiBufferSource.BufferSource buffer, BlockRenderDispatcher dispatcher, - BlockPos blockPos, BlockState blockState, float dissolve, BlockPos firstPos, BlockPos secondPos, boolean red) { - if (blockState == null) return; - - CreateClient.GHOST_BLOCKS.showGhostState(blockPos.toShortString(), blockState) -// .breathingAlpha() - .alpha(0.7f) - .at(blockPos); - -// ms.pushPose(); -// ms.translate(blockPos.getX(), blockPos.getY(), blockPos.getZ()); -//// ms.rotate(Vector3f.YP.rotationDegrees(-90f)); -// ms.translate(-0.01f, -0.01f, -0.01f); -// ms.scale(1.02f, 1.02f, 1.02f); -// -// //Begin block preview rendering -// RenderType blockPreviewRenderType = BuildRenderTypes.getBlockPreviewRenderType(dissolve, blockPos, firstPos, secondPos, red); -// VertexConsumer vc = buffer.getBuffer(blockPreviewRenderType); -// -// try { -// BakedModel model = dispatcher.getBlockModel(blockState); -// dispatcher.getModelRenderer().renderModel(ms.last(), vc, blockState, model, -// 1f, 1f, 1f, 0, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, blockPreviewRenderType); -// } catch (NullPointerException e) { -// EffortlessBuilding.logger.warn("RenderHandler::renderBlockPreview cannot render " + blockState.getBlock().toString()); -// -// //Render outline as backup, escape out of the current renderstack -// ms.popPose(); -// buffer.endBatch(); -// VertexConsumer lineBuffer = beginLines(buffer); -// renderBlockOutline(ms, lineBuffer, blockPos, new Vec3(1f, 1f, 1f)); -// endLines(buffer); -// vc = buffer.getBuffer(Sheets.translucentCullBlockSheet()); //any type will do, as long as we have something on the stack -// ms.pushPose(); -// } -// -// buffer.endBatch(); -// ms.popPose(); - } - - protected static void renderBlockOutline(PoseStack matrixStack, VertexConsumer buffer, BlockPos pos, Vec3 color) { - renderBlockOutline(matrixStack, buffer, pos, pos, color); - } - - //Renders outline. Pos1 has to be minimal x,y,z and pos2 maximal x,y,z - protected static void renderBlockOutline(PoseStack matrixStack, VertexConsumer buffer, BlockPos pos1, BlockPos pos2, Vec3 color) { - AABB aabb = new AABB(pos1, pos2.offset(1, 1, 1)).inflate(0.0020000000949949026); - - LevelRenderer.renderLineBox(matrixStack, buffer, aabb, (float) color.x, (float) color.y, (float) color.z, 0.4f); -// WorldRenderer.drawSelectionBoundingBox(aabb, (float) color.x, (float) color.y, (float) color.z, 0.4f); - } - - //Renders outline with given bounding box - protected static void renderBlockOutline(PoseStack matrixStack, VertexConsumer buffer, BlockPos pos, VoxelShape collisionShape, Vec3 color) { -// WorldRenderer.drawShape(collisionShape, pos.getX(), pos.getY(), pos.getZ(), (float) color.x, (float) color.y, (float) color.z, 0.4f); - LevelRenderer.renderVoxelShape(matrixStack, buffer, collisionShape, pos.getX(), pos.getY(), pos.getZ(), (float) color.x, (float) color.y, (float) color.z, 0.4f); - } - - //TODO - //Sends breaking progress for all coordinates to renderglobal, so all blocks get visually broken -// @Override -// public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress) { -// Minecraft mc = Minecraft.getInstance(); -// -// ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(mc.player); -// if (!BuildModifiers.isEnabled(modifierSettings, pos)) return; -// -// List coordinates = BuildModifiers.findCoordinates(mc.player, pos); -// for (int i = 1; i < coordinates.size(); i++) { -// BlockPos coordinate = coordinates.get(i); -// if (SurvivalHelper.canBreak(mc.world, mc.player, coordinate)) { -// //Send i as entity id because only one block can be broken per id -// //Unless i happens to be the player id, then take something else -// int fakeId = mc.player.getEntityId() != i ? i : coordinates.size(); -// mc.renderGlobal.sendBlockBreakProgress(fakeId, coordinate, progress); -// } -// } -// } } diff --git a/src/main/resources/assets/effortlessbuilding/textures/icons/normal.png b/src/main/resources/assets/effortlessbuilding/textures/icons/disabled.png similarity index 100% rename from src/main/resources/assets/effortlessbuilding/textures/icons/normal.png rename to src/main/resources/assets/effortlessbuilding/textures/icons/disabled.png diff --git a/src/main/resources/assets/effortlessbuilding/textures/icons/normal_plus.png b/src/main/resources/assets/effortlessbuilding/textures/icons/single.png similarity index 100% rename from src/main/resources/assets/effortlessbuilding/textures/icons/normal_plus.png rename to src/main/resources/assets/effortlessbuilding/textures/icons/single.png From 5f27eedde246ae0ab225618a446c99173c3b9684 Mon Sep 17 00:00:00 2001 From: Christian Knaapen Date: Tue, 17 Jan 2023 22:41:38 +0100 Subject: [PATCH 5/5] Keeping outlines until animation is done. Fix itemstack not found error when undoing in creative. Disabled create test code. --- .../buildmodifier/BuildModifiers.java | 2 +- .../buildmodifier/UndoRedo.java | 8 ++--- .../create/CreateClientTest.java | 34 +++++++++--------- .../foundation/utility/outliner/Outliner.java | 6 ++++ .../render/BlockPreviews.java | 36 ++++++++++++++----- 5 files changed, 55 insertions(+), 31 deletions(-) diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java index 1a5d16f..1826f37 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java @@ -50,7 +50,7 @@ public class BuildModifiers { } else { - int delay = CommonConfig.visuals.appearAnimationLength.get() * 3; //DelayedBlockPlacer is called 3 times per tick? + int delay = CommonConfig.visuals.appearAnimationLength.get() * 3 - 3; //DelayedBlockPlacer is called 3 times per tick? //place blocks after delay EffortlessBuilding.DELAYED_BLOCK_PLACER.placeBlocksDelayed(new DelayedBlockPlacer.Entry(world, player, coordinates, diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java index 20b9a48..b27b0d5 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java @@ -98,14 +98,14 @@ public class UndoRedo { if (previousBlockStates.get(i).equals(newBlockStates.get(i))) continue; //get blockstate from itemstack - BlockState previousBlockState = Blocks.AIR.defaultBlockState(); + BlockState previousBlockState = previousBlockStates.get(i); if (itemStack.getItem() instanceof BlockItem) { previousBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState(); } if (player.level.isLoaded(coordinate)) { //check itemstack empty - if (itemStack.isEmpty()) { + if (itemStack.isEmpty() && !player.isCreative()) { itemStack = findItemStackInInventory(player, previousBlockStates.get(i)); //get blockstate from new itemstack if (!itemStack.isEmpty() && itemStack.getItem() instanceof BlockItem) { @@ -158,14 +158,14 @@ public class UndoRedo { if (previousBlockStates.get(i).equals(newBlockStates.get(i))) continue; //get blockstate from itemstack - BlockState newBlockState = Blocks.AIR.defaultBlockState(); + BlockState newBlockState = newBlockStates.get(i); if (itemStack.getItem() instanceof BlockItem) { newBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState(); } if (player.level.isLoaded(coordinate)) { //check itemstack empty - if (itemStack.isEmpty()) { + if (itemStack.isEmpty() && !player.isCreative()) { itemStack = findItemStackInInventory(player, newBlockStates.get(i)); //get blockstate from new itemstack if (!itemStack.isEmpty() && itemStack.getItem() instanceof BlockItem) { diff --git a/src/main/java/nl/requios/effortlessbuilding/create/CreateClientTest.java b/src/main/java/nl/requios/effortlessbuilding/create/CreateClientTest.java index 2f4f2c4..c19bd0b 100644 --- a/src/main/java/nl/requios/effortlessbuilding/create/CreateClientTest.java +++ b/src/main/java/nl/requios/effortlessbuilding/create/CreateClientTest.java @@ -11,21 +11,21 @@ import nl.requios.effortlessbuilding.create.foundation.utility.Color; @Mod.EventBusSubscriber(Dist.CLIENT) public class CreateClientTest { - @SubscribeEvent - public static void onTick(TickEvent.ClientTickEvent event) { - CreateClient.GHOST_BLOCKS.showGhostState(1, Blocks.SPRUCE_LOG.defaultBlockState()) - .at(0, 120, 0) - .breathingAlpha(); - CreateClient.GHOST_BLOCKS.showGhostState(2, Blocks.SPRUCE_LOG.defaultBlockState()) - .at(1, 120, 0) - .breathingAlpha(); - - CreateClient.OUTLINER.showAABB(1, new AABB(0, 0, 0, 10, 2, 6) - .move(10, 120, 0)) - .withFaceTexture(AllSpecialTextures.CHECKERED) - .colored(new Color(0.11f, 0.49f, 0.7f, 1f)) -// .colored(0xbfbfbf) - .disableNormals() - .lineWidth(1 / 32f); - } +// @SubscribeEvent +// public static void onTick(TickEvent.ClientTickEvent event) { +// CreateClient.GHOST_BLOCKS.showGhostState(1, Blocks.SPRUCE_LOG.defaultBlockState()) +// .at(0, 120, 0) +// .breathingAlpha(); +// CreateClient.GHOST_BLOCKS.showGhostState(2, Blocks.SPRUCE_LOG.defaultBlockState()) +// .at(1, 120, 0) +// .breathingAlpha(); +// +// CreateClient.OUTLINER.showAABB(1, new AABB(0, 0, 0, 10, 2, 6) +// .move(10, 120, 0)) +// .withFaceTexture(AllSpecialTextures.CHECKERED) +// .colored(new Color(0.11f, 0.49f, 0.7f, 1f)) +//// .colored(0xbfbfbf) +// .disableNormals() +// .lineWidth(1 / 32f); +// } } diff --git a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/Outliner.java b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/Outliner.java index c2f0660..c1df937 100644 --- a/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/Outliner.java +++ b/src/main/java/nl/requios/effortlessbuilding/create/foundation/utility/outliner/Outliner.java @@ -75,6 +75,12 @@ public class Outliner { outlines.get(slot).ticksTillRemoval = 1; } + //ADDED + public void keep(Object slot, int ticks) { + if (outlines.containsKey(slot)) + outlines.get(slot).ticksTillRemoval = ticks; + } + public void remove(Object slot) { outlines.remove(slot); } diff --git a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java index 1e20aca..78ee7dc 100644 --- a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java +++ b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java @@ -30,6 +30,7 @@ import nl.requios.effortlessbuilding.compatibility.CompatHelper; import nl.requios.effortlessbuilding.create.AllSpecialTextures; import nl.requios.effortlessbuilding.create.CreateClient; import nl.requios.effortlessbuilding.create.foundation.utility.Color; +import nl.requios.effortlessbuilding.create.foundation.utility.VecHelper; import nl.requios.effortlessbuilding.helper.ReachHelper; import nl.requios.effortlessbuilding.helper.SurvivalHelper; import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem; @@ -59,8 +60,6 @@ public class BlockPreviews { float dissolve = (ClientEvents.ticksInGame - placed.time) / (float) totalTime; renderBlockPreviews(player, modifierSettings, placed.coordinates, placed.blockStates, placed.itemStacks, dissolve, placed.firstPos, placed.secondPos, false, placed.breaking); - - CreateClient.OUTLINER.showCluster(placed.time, placed.coordinates); } } } @@ -291,7 +290,7 @@ public class BlockPreviews { float scale = 0.5f; float alpha = 0.7f; if (dissolve > 0f) { - float animationLength = 0.7f; + float animationLength = 0.8f; double firstToSecond = secondPos.distSqr(firstPos); double place = 0; @@ -308,20 +307,18 @@ public class BlockPreviews { t = Mth.clamp(t, 0, 1); //Now we got a usable t value for this block - float sine = Mth.sin((t + (1/7f) * t) * Mth.TWO_PI - (3/4f) * Mth.PI); // -1 to 1 - sine = (sine + 1f) / 2f; // 0 to 1 - -// scale = 1f + (sine * 0.5f); - t = (float) Mth.smoothstep(t); +// t = (float) bezier(t, .58,-0.08,.23,1.33); + if (!breaking) { scale = 0.5f + (t * 0.3f); alpha = 0.7f + (t * 0.3f); } else { t = 1f - t; scale = 0.5f + (t * 0.5f); - alpha = t; + alpha = 0.7f + (t * 0.3f); } + alpha = Mth.clamp(alpha, 0, 1); } CreateClient.GHOST_BLOCKS.showGhostState(blockPos.toShortString(), blockState) @@ -330,6 +327,23 @@ public class BlockPreviews { .scale(scale); } + //A bezier easing function where implicit first and last control points are (0,0) and (1,1). + public static double bezier(double t, double x1, double y1, double x2, double y2) { + double t2 = t * t; + double t3 = t2 * t; + + double cx = 3.0 * x1; + double bx = 3.0 * (x2 - x1) - cx; + double ax = 1.0 - cx -bx; + + double cy = 3.0 * y1; + double by = 3.0 * (y2 - y1) - cy; + double ay = 1.0 - cy - by; + + // Calculate the curve point at parameter value t + return (ay * t3) + (by * t2) + (cy * t) + 0; + } + public static void onBlocksPlaced() { onBlocksPlaced(previousCoordinates, previousItemStacks, previousBlockStates, previousFirstPos, previousSecondPos); } @@ -350,6 +364,8 @@ public class BlockPreviews { placedDataList.add(new PlacedData(ClientEvents.ticksInGame, coordinates, blockStates, itemStacks, firstPos, secondPos, false)); } + + CreateClient.OUTLINER.keep(firstPos, CommonConfig.visuals.appearAnimationLength.get()); } } @@ -376,6 +392,8 @@ public class BlockPreviews { placedDataList.add(new PlacedData(ClientEvents.ticksInGame, coordinates, blockStates, itemStacks, firstPos, secondPos, true)); } + + CreateClient.OUTLINER.keep(firstPos, CommonConfig.visuals.breakAnimationLength.get()); } }