Merge branch 'create-utilities' into 1.19

This commit is contained in:
Christian Knaapen
2023-01-17 22:42:12 +01:00
131 changed files with 8393 additions and 1062 deletions

View File

@@ -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'
@@ -37,6 +37,10 @@ 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
@@ -109,6 +117,12 @@ repositories {
// flatDir {
// dir 'libs'
// }
//Flywheel
maven {
name "tterrag maven"
url "https://maven.tterrag.com/"
}
}
dependencies {
@@ -128,6 +142,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..

View File

@@ -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<Boolean> showBlockPreviews;
public final ForgeConfigSpec.ConfigValue<Boolean> alwaysShowBlockPreview;
public final ForgeConfigSpec.ConfigValue<Integer> 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();
}
}
}

View File

@@ -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.DISABLED ||
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.SINGLE) {
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.SINGLE) {
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));
}
}

View File

@@ -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<Boolean> alwaysShowBlockPreview;
public final ForgeConfigSpec.ConfigValue<Double> dissolveTimeMultiplier;
public final ForgeConfigSpec.ConfigValue<Integer> shaderThreshold;
public final ForgeConfigSpec.ConfigValue<Boolean> useShaders;
public final ForgeConfigSpec.ConfigValue<Integer> appearAnimationLength;
public final ForgeConfigSpec.ConfigValue<Integer> 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();
}

View File

@@ -8,13 +8,14 @@ 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.TickEvent;
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 +30,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<Entity> event) {
@@ -41,6 +53,13 @@ public class EventHandler {
}
}
@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;
@@ -54,7 +73,7 @@ public class EventHandler {
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.
@@ -93,7 +112,7 @@ public class EventHandler {
//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
@@ -144,7 +163,7 @@ public class EventHandler {
//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
@@ -172,4 +191,6 @@ public class EventHandler {
ModifierSettingsManager.setModifierSettings(newPlayer, ModifierSettingsManager.getModifierSettings(oldPlayer));
ModeSettingsManager.setModeSettings(newPlayer, ModeSettingsManager.getModeSettings(oldPlayer));
}
}

View File

@@ -5,25 +5,24 @@ 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;
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,7 +38,9 @@ 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);
public static final DelayedBlockPlacer DELAYED_BLOCK_PLACER = new DelayedBlockPlacer();
//Registration
private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
@@ -59,14 +60,26 @@ 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.unsafeRunWhenOn(Dist.CLIENT, () -> () -> EffortlessBuildingClient.onConstructorClient(modEventBus, forgeEventBus));
ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus());
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) {
PacketHandler.register();
CompatHelper.setup();
}
public static <T extends AbstractContainerMenu> MenuType<T> registerContainer(IContainerFactory<T> fact){
@@ -74,26 +87,6 @@ public class EffortlessBuilding {
return type;
}
@SubscribeEvent
public 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 void log(String msg) {
logger.info(msg);
}
@@ -110,4 +103,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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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),

View File

@@ -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:

View File

@@ -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() {
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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<BlockState> previousBlockStates = new ArrayList<>(coordinates.size());
List<BlockState> 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 - 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) ||

View File

@@ -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;
}

View File

@@ -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<ItemStack> 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++) {
@@ -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) {
@@ -148,7 +148,7 @@ public class UndoRedo {
List<ItemStack> 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++) {
@@ -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) {

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
// }
}

View File

@@ -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.GHOST_BLOCKS.tickGhosts();
CreateClient.OUTLINER.tickOutlines();
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));
}
public static boolean isGameActive() {
return !(Minecraft.getInstance().level == null || Minecraft.getInstance().player == null);
}
}

View File

@@ -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));
// });
// }
// }
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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<RenderedBuffer, Integer> pair = ModelUtil.getBufferBuilder(model, referenceState, ms);
return new SuperByteBuffer(pair.first(), pair.second());
}
}

View File

@@ -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<BlockState> GENERIC_TILE = new Compartment<>();
public static final Compartment<PartialModel> PARTIAL = new Compartment<>();
public static final Compartment<Pair<Direction, PartialModel>> DIRECTIONAL_PARTIAL = new Compartment<>();
public static SuperByteBuffer block(BlockState toRender) {
return block(GENERIC_TILE, toRender);
}
public static SuperByteBuffer block(Compartment<BlockState> 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<PoseStack> 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<PoseStack> modelTransform) {
return CreateClient.BUFFER_CACHE.get(DIRECTIONAL_PARTIAL, Pair.of(dir, partial),
() -> BakedModelRenderHelper.standardModelRender(partial.get(), referenceState, modelTransform.get()));
}
public static Supplier<PoseStack> 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<PoseStack> rotateToFaceVertical(Direction facing) {
return () -> {
PoseStack stack = new PoseStack();
TransformStack.cast(stack)
.centre()
.rotateY(AngleHelper.horizontalAngle(facing))
.rotateX(AngleHelper.verticalAngle(facing) + 90)
.unCentre();
return stack;
};
}
}

View File

@@ -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<ObjectArrayList<DiffuseLightCalculator>> 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<DiffuseLightCalculator> stack = FORCED_DIFFUSE.get();
if (stack.isEmpty()) {
return null;
}
return stack.top();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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<SuperByteBuffer>, TStack<SuperByteBuffer> {
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);
}
}

View File

@@ -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<Compartment<?>, Cache<Object, SuperByteBuffer>> 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 <T> SuperByteBuffer get(Compartment<T> compartment, T key, Callable<SuperByteBuffer> callable) {
Cache<Object, SuperByteBuffer> cache = caches.get(compartment);
if (cache != null) {
try {
return cache.get(key, callable);
} catch (ExecutionException e) {
e.printStackTrace();
}
}
return null;
}
public <T> void invalidate(Compartment<T> compartment, T key) {
caches.get(compartment).invalidate(key);
}
public <T> void invalidate(Compartment<?> compartment) {
caches.get(compartment).invalidateAll();
}
public void invalidate() {
caches.forEach((compartment, cache) -> cache.invalidateAll());
}
public static class Compartment<T> {
}
}

View File

@@ -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<RenderType, BufferBuilder> 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<RenderType, BufferBuilder> map, RenderType type) {
map.put(type, new BufferBuilder(type.bufferSize()));
}
}
}

View File

@@ -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<BlockEntity> customRenderTEs, PoseStack ms,
MultiBufferSource buffer) {
renderTileEntities(world, null, customRenderTEs, ms, null, buffer);
}
public static void renderTileEntities(Level world, Iterable<BlockEntity> 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<BlockEntity> 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<BlockEntity> customRenderTEs, PoseStack ms, @Nullable Matrix4f lightTransform, MultiBufferSource buffer,
float pt) {
Iterator<BlockEntity> iterator = customRenderTEs.iterator();
while (iterator.hasNext()) {
BlockEntity tileEntity = iterator.next();
if (Backend.getBackendType() == BackendType.INSTANCING && Backend.isFlywheelWorld(renderWorld) && InstancedRenderRegistry.shouldSkipRender(tileEntity))
continue;
BlockEntityRenderer<BlockEntity> 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;
}
}

View File

@@ -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<BlockPos> makeCallbackFor(Level world, float effectChance, ItemStack toDamage,
@Nullable Player playerEntity, BiConsumer<BlockPos, ItemStack> 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<BlockPos, ItemStack> 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<BlockPos, ItemStack> drop);
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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()));
}
}

View File

@@ -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<BlockPos, Direction> {
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));
}
}

View File

@@ -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<ItemStack> 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<ItemStack> 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 <T extends Comparable<T>> BlockState copyProperty(Property<T> property, BlockState fromState,
BlockState toState) {
if (fromState.hasProperty(property) && toState.hasProperty(property)) {
return toState.setValue(property, fromState.getValue(property));
}
return toState;
}
}

View File

@@ -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
}
}

View File

@@ -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<Integer> 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<Color> 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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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<T> extends Pair<T, T> implements Iterable<T> {
private static final Couple<Boolean> TRUE_AND_FALSE = Couple.create(true, false);
protected Couple(T first, T second) {
super(first, second);
}
public static <T> Couple<T> create(T first, T second) {
return new Couple<>(first, second);
}
public static <T> Couple<T> create(Supplier<T> factory) {
return new Couple<>(factory.get(), factory.get());
}
public static <T> Couple<T> createWithContext(Function<Boolean, T> 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<T> copy() {
return create(first, second);
}
public <S> Couple<S> map(Function<T, S> function) {
return Couple.create(function.apply(first), function.apply(second));
}
public <S> Couple<S> mapWithContext(BiFunction<T, Boolean, S> function) {
return Couple.create(function.apply(first, true), function.apply(second, false));
}
public <S, R> Couple<S> mapWithParams(BiFunction<T, R, S> function, Couple<R> values) {
return Couple.create(function.apply(first, values.first), function.apply(second, values.second));
}
public <S, R> Couple<S> mapNotNullWithParam(BiFunction<T, R, S> function, R value) {
return Couple.create(first != null ? function.apply(first, value) : null,
second != null ? function.apply(second, value) : null);
}
public boolean both(Predicate<T> test) {
return test.test(getFirst()) && test.test(getSecond());
}
public boolean either(Predicate<T> test) {
return test.test(getFirst()) || test.test(getSecond());
}
public void replace(Function<T, T> function) {
setFirst(function.apply(getFirst()));
setSecond(function.apply(getSecond()));
}
public void replaceWithContext(BiFunction<T, Boolean, T> function) {
replaceWithParams(function, TRUE_AND_FALSE);
}
public <S> void replaceWithParams(BiFunction<T, S, T> function, Couple<S> values) {
setFirst(function.apply(getFirst(), values.getFirst()));
setSecond(function.apply(getSecond(), values.getSecond()));
}
@Override
public void forEach(Consumer<? super T> consumer) {
consumer.accept(getFirst());
consumer.accept(getSecond());
}
public void forEachWithContext(BiConsumer<T, Boolean> consumer) {
forEachWithParams(consumer, TRUE_AND_FALSE);
}
public <S> void forEachWithParams(BiConsumer<T, S> function, Couple<S> values) {
function.accept(getFirst(), values.getFirst());
function.accept(getSecond(), values.getSecond());
}
public Couple<T> swap() {
return Couple.create(second, first);
}
public ListTag serializeEach(Function<T, CompoundTag> serializer) {
return NBTHelper.writeCompoundList(ImmutableList.of(first, second), serializer);
}
public static <S> Couple<S> deserializeEach(ListTag list, Function<CompoundTag, S> deserializer) {
List<S> readCompoundList = NBTHelper.readCompoundList(list, deserializer);
return new Couple<>(readCompoundList.get(0), readCompoundList.get(1));
}
@Override
public Iterator<T> iterator() {
return new Couplerator<>(this);
}
public Stream<T> stream() {
return Stream.of(first, second);
}
private static class Couplerator<T> implements Iterator<T> {
int state;
private final Couple<T> couple;
public Couplerator(Couple<T> 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;
}
}
}

View File

@@ -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<K, V> {
private static final List<CreateRegistry<?, ?>> ALL = new ArrayList<>();
protected final IForgeRegistry<K> objectRegistry;
protected final Map<ResourceLocation, V> locationMap = new HashMap<>();
protected final Map<K, V> objectMap = new IdentityHashMap<>();
protected boolean unwrapped = false;
public CreateRegistry(IForgeRegistry<K> 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<ResourceLocation, V> 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();
}
}
}

View File

@@ -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() {}
}

View File

@@ -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<DyeColor, Couple<Integer>> DYE_TABLE = new ImmutableMap.Builder<DyeColor, Couple<Integer>>()
// 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();
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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<MutableComponent> couple = asComponents(amount, shorten);
return couple.getFirst().getString() + " " + couple.getSecond().getString();
}
public static Couple<MutableComponent> 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")
);
}
}

View File

@@ -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<String> cutString(Font font, String text, int maxWidthPerLine) {
// Split words
List<String> 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<String> 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<String> 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;
}
}
}

View File

@@ -0,0 +1,8 @@
package nl.requios.effortlessbuilding.create.foundation.utility;
import net.minecraft.core.BlockPos;
@FunctionalInterface
public interface ICoordinate {
float get(BlockPos from);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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<V> extends Pair<Integer, V> {
protected IntAttached(Integer first, V second) {
super(first, second);
}
public static <V> IntAttached<V> with(int number, V value) {
return new IntAttached<>(number, value);
}
public static <V> IntAttached<V> 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<V, CompoundTag> serializer) {
CompoundTag nbt = new CompoundTag();
nbt.put("Item", serializer.apply(getValue()));
nbt.putInt("Location", getFirst());
return nbt;
}
public static Comparator<? super IntAttached<?>> comparator() {
return (i1, i2) -> Integer.compare(i2.getFirst(), i1.getFirst());
}
public static <T> IntAttached<T> read(CompoundTag nbt, Function<CompoundTag, T> deserializer) {
return IntAttached.with(nbt.getInt("Location"), deserializer.apply(nbt.getCompound("Item")));
}
}

View File

@@ -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<Axis> 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<BlockPos> hereAndBelow(BlockPos pos) {
return Arrays.asList(pos, pos.below());
}
public static List<BlockPos> hereBelowAndAbove(BlockPos pos) {
return Arrays.asList(pos, pos.below(), pos.above());
}
}

View File

@@ -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<Component> translatedOptions(String prefix, String... keys) {
List<Component> 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;
}
}

View File

@@ -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<br>
* 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<? super MutableComponent> tooltip) {
tooltip.add(component());
}
public void forGoggles(List<? super MutableComponent> tooltip) {
forGoggles(tooltip, 0);
}
public void forGoggles(List<? super MutableComponent> 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");
}
}

View File

@@ -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", " ");
}
}

View File

@@ -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 extends Enum<?>> T readEnum(CompoundTag nbt, String key, Class<T> 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 <T extends Enum<?>> void writeEnum(CompoundTag nbt, String key, T enumConstant) {
nbt.putString(key, enumConstant.name());
}
public static <T> ListTag writeCompoundList(Iterable<T> list, Function<T, CompoundTag> serializer) {
ListTag listNBT = new ListTag();
list.forEach(t -> {
CompoundTag apply = serializer.apply(t);
if (apply == null)
return;
listNBT.add(apply);
});
return listNBT;
}
public static <T> List<T> readCompoundList(ListTag listNBT, Function<CompoundTag, T> deserializer) {
List<T> list = new ArrayList<>(listNBT.size());
listNBT.forEach(inbt -> list.add(deserializer.apply((CompoundTag) inbt)));
return list;
}
public static <T> void iterateCompoundList(ListTag listNBT, Consumer<CompoundTag> consumer) {
listNBT.forEach(inbt -> consumer.accept((CompoundTag) inbt));
}
public static ListTag writeItemList(Iterable<ItemStack> stacks) {
return writeCompoundList(stacks, ItemStack::serializeNBT);
}
public static List<ItemStack> 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();
}
}

View File

@@ -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<BlockEntityType<?>, UnaryOperator<CompoundTag>> processors = new HashMap<>();
private static final Map<BlockEntityType<?>, UnaryOperator<CompoundTag>> survivalProcessors = new HashMap<>();
public static synchronized void addProcessor(BlockEntityType<?> type, UnaryOperator<CompoundTag> processor) {
processors.put(type, processor);
}
public static synchronized void addSurvivalProcessor(BlockEntityType<?> type, UnaryOperator<CompoundTag> 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;
}
}

View File

@@ -0,0 +1,68 @@
package nl.requios.effortlessbuilding.create.foundation.utility;
import java.util.Objects;
public class Pair<F, S> {
F first;
S second;
protected Pair(F first, S second) {
this.first = first;
this.second = second;
}
public static <F, S> Pair<F, S> 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<F, S> 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<S, F> swap() {
return Pair.of(second, first);
}
}

View File

@@ -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;
}
}

View File

@@ -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<BlockPos> 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<BlockPos> 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;
}
}
}

View File

@@ -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 <V> ResourceLocation getKeyOrThrow(IForgeRegistry<V> 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);
}
}

View File

@@ -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<T> implements Supplier<T> {
private final NonNullSupplier<T> supplier;
private T value;
public ResetableLazy(NonNullSupplier<T> supplier) {
this.supplier = supplier;
}
@Override
public T get() {
if (value == null) {
value = supplier.get();
}
return value;
}
public void reset() {
value = null;
}
public static <T> ResetableLazy<T> of(NonNullSupplier<T> supplier) {
return new ResetableLazy<>(supplier);
}
}

View File

@@ -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<E> extends LinkedList<E> {
/**
*
*/
private static final long serialVersionUID = 1L;
private final HashSet<E> 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<? extends E> c) {
List<? extends E> filtered = c.stream()
.filter(it -> !contained.contains(it))
.collect(Collectors.toList());
return super.addAll(filtered);
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {
List<? extends E> 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();
}
}

View File

@@ -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 };
}
}

View File

@@ -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<Direction, VoxelShape> 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<Direction> directions, Function<Direction, Vec3> 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<Direction, Vec3> 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<VoxelShape> 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<Direction, Vec3> {
// 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<Direction, Vec3> {
@Override
public Vec3 apply(Direction direction) {
return new Vec3(0, -horizontalAngleFromDirection(direction), 0);
}
}
}

View File

@@ -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<T> {
// weak references to prevent leaking hashmaps when a WorldAttached is GC'd during runtime
static List<WeakReference<Map<LevelAccessor, ?>>> allMaps = new ArrayList<>();
private final Map<LevelAccessor, T> attached;
private final Function<LevelAccessor, T> factory;
public WorldAttached(Function<LevelAccessor, T> 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<LevelAccessor, ?> 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<T> 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<LevelAccessor, T> 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<T> finalizer) {
attached.values()
.forEach(finalizer);
attached.clear();
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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<Force> 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);
}
}

View File

@@ -0,0 +1,83 @@
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;
public class GhostBlockParams {
protected final BlockState state;
protected BlockPos pos;
protected Supplier<Float> alphaSupplier;
protected Supplier<Float> scaleSupplier;
public Supplier<Color> 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) {
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<Float> alphaSupplier) {
this.alphaSupplier = alphaSupplier;
return this;
}
public GhostBlockParams alpha(float alpha) {
return this.alpha(() -> alpha);
}
public GhostBlockParams breathingAlpha() {
return this.alpha(() -> (float) GhostBlocks.getBreathingAlpha());
}
public GhostBlockParams scale(Supplier<Float> scaleSupplier) {
this.scaleSupplier = scaleSupplier;
return this;
}
public GhostBlockParams scale(float scale) {
return this.scale(() -> scale);
}
public GhostBlockParams colored(Supplier<Color> 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));
}
}

View File

@@ -0,0 +1,143 @@
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 nl.requios.effortlessbuilding.create.foundation.utility.Color;
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()*/;
float scale = params.scaleSupplier.get();
Color color = params.rgbSupplier.get();
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(scale, scale, scale);
ms.translate(-.5, -.5, -.5);
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);
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<BakedQuad> 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);
}
}
}
}

View File

@@ -0,0 +1,90 @@
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;
}
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<Object, Entry> 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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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<BlockPos> 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<AllSpecialTextures> 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<MergeEntry, AxisDirection> visibleFaces;
private Set<MergeEntry> 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();
}
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}
}

View File

@@ -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<AllSpecialTextures> faceTexture;
protected Optional<AllSpecialTextures> 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;
}
}
}

View File

@@ -0,0 +1,185 @@
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<Object, OutlineEntry> outlines = Collections.synchronizedMap(new HashMap<>());
private final Map<Object, OutlineEntry> 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<BlockPos> 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;
}
//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);
}
public Optional<OutlineParams> edit(Object slot) {
keep(slot);
if (outlines.containsKey(slot))
return Optional.of(outlines.get(slot)
.getOutline()
.getParams());
return Optional.empty();
}
public Map<Object, OutlineEntry> 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<OutlineEntry> 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;
}
}
}

View File

@@ -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<T extends EntityAccess> implements LevelEntityGetter<T> {
@Override
public T get(int p_156931_) {
return null;
}
@Override
public T get(UUID pUuid) {
return null;
}
@Override
public Iterable<T> getAll() {
return Collections.emptyList();
}
@Override
public <U extends T> void get(EntityTypeTest<T, U> p_156935_, Consumer<U> p_156936_) {
}
@Override
public void get(AABB p_156937_, Consumer<T> p_156938_) {
}
@Override
public <U extends T> void get(EntityTypeTest<T, U> p_156932_, AABB p_156933_, Consumer<U> p_156934_) {
}
}

View File

@@ -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() {}
}

View File

@@ -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<BlockPos, BlockState> 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<BlockState> 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();
}
}

View File

@@ -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<BlockPos, BlockState, BlockState> stateGetter;
public RayTraceWorld(LevelAccessor template, BiFunction<BlockPos, BlockState, BlockState> 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();
}
}

View File

@@ -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 extends LivingEntity> T getNearestEntity(List<? extends T> 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;
}
}

View File

@@ -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<Block> getBlockTicks() {
return super.getBlockTicks();
}
@Override
public LevelTicks<Fluid> getFluidTicks() {
return super.getFluidTicks();
}
@Override
public void levelEvent(Player player, int type, BlockPos pos, int data) {}
@Override
public List<ServerPlayer> 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<Biome> 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_);
}
}

View File

@@ -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<Entity> 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<BlockState> 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<Block> getBlockTicks() {
return world.getBlockTicks();
}
@Override
public LevelTickAccess<Fluid> 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<? extends Player> 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<Biome> 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<Entity> 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();
}
}

View File

@@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package nl.requios.effortlessbuilding.create.foundation.utility.worldWrappers;
import net.minecraft.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -0,0 +1,12 @@
All files within this folder fall under the MIT license.
The MIT License Copyright (c) <year> <copyright holders> 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.

View File

@@ -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";
}
}

View File

@@ -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;
}

View File

@@ -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<Entry> entries = Collections.synchronizedSet(new HashSet<>());
private final Set<Entry> 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<Entry> getEntries() {
return entriesView;
}
public static class Entry {
private Level world;
private Player player;
private List<BlockPos> coordinates;
private List<BlockState> blockStates;
private List<ItemStack> itemStacks;
private Vec3 hitVec;
private boolean placeStartPos;
private int ticksTillPlacement;
public Entry(Level world, Player player, List<BlockPos> coordinates, List<BlockState> blockStates,
List<ItemStack> 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<BlockState> 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<BlockState> 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));
}
}
}
}

View File

@@ -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) {

View File

@@ -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)*/;
}

View File

@@ -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;
}

Some files were not shown because too many files have changed in this diff Show More