[Feature] Support for Debug Items

This commit is contained in:
Frank 2023-06-07 18:53:34 +02:00
parent a34a15620a
commit fb5f0a933c
6 changed files with 398 additions and 1 deletions

View file

@ -36,6 +36,7 @@ import net.minecraft.network.chat.Style;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.*; import net.minecraft.world.level.block.entity.*;
@ -603,6 +604,31 @@ public class PlaceCommand {
return bb; return bb;
} }
public static BlockState setJigsawOrientation(
boolean rollable,
Player player,
BlockPos pos,
BlockState state
) {
final int deltaY = player.getBlockY() - pos.getY();
if (deltaY < 2 && deltaY > -2 && rollable) {
state = state.setValue(
JigsawBlock.ORIENTATION,
FrontAndTop.fromFrontAndTop(player.getDirection().getOpposite(), Direction.UP)
);
} else if (deltaY < 0) {
state = state.setValue(
JigsawBlock.ORIENTATION,
FrontAndTop.fromFrontAndTop(Direction.DOWN, player.getDirection().getOpposite())
);
} else {
state = state.setValue(
JigsawBlock.ORIENTATION,
FrontAndTop.fromFrontAndTop(Direction.UP, player.getDirection().getOpposite())
);
}
return state;
}
} }
/* /*

View file

@ -17,7 +17,7 @@ import java.util.concurrent.CompletableFuture;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class ConnectorArgument extends ResourceLocationArgument { public class ConnectorArgument extends ResourceLocationArgument {
private static final Collection<String> EXAMPLES = Arrays.asList("-:entrance", "-:decoration", "-:street"); private static final Collection<String> EXAMPLES = Arrays.asList("-:building_entrance", "-:bottom", "-:street");
@Override @Override
public Collection<String> getExamples() { public Collection<String> getExamples() {

View file

@ -0,0 +1,284 @@
package org.betterx.bclib.items;
import org.betterx.bclib.client.models.ModelsHelper;
import org.betterx.bclib.commands.PlaceCommand;
import org.betterx.bclib.interfaces.ItemModelProvider;
import org.betterx.bclib.util.BlocksHelper;
import org.betterx.ui.ColorUtil;
import net.minecraft.client.renderer.block.model.BlockModel;
import net.minecraft.commands.arguments.blocks.BlockStateParser;
import net.minecraft.core.BlockPos;
import net.minecraft.core.FrontAndTop;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.data.worldgen.Pools;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.ClipBlockStateContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.JigsawBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.JigsawBlockEntity;
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
public class DebugDataItem extends Item implements ItemModelProvider {
public static final ResourceLocation DEFAULT_ICON = new ResourceLocation("stick");
interface DebugInteraction {
InteractionResult use(UseOnContext useOnContext);
}
interface DebugEntityInteraction extends DebugInteraction {
@Override
default InteractionResult use(UseOnContext useOnContext) {
var entity = useOnContext.getLevel().getBlockEntity(useOnContext.getClickedPos());
if (entity != null) {
return use(useOnContext.getPlayer(), entity, useOnContext);
}
return InteractionResult.FAIL;
}
InteractionResult use(Player player, BlockEntity entity, UseOnContext useOnContext);
}
protected final DebugInteraction interaction;
protected final ResourceLocation icon;
public final boolean placeInAir;
public DebugDataItem(DebugEntityInteraction interaction, boolean placeInAir, ResourceLocation icon) {
this((DebugInteraction) interaction, placeInAir, icon);
}
public DebugDataItem(DebugInteraction interaction, boolean placeInAir, ResourceLocation icon) {
super(new Item.Properties().fireResistant().stacksTo(1));
this.interaction = interaction;
this.icon = (icon == null ? DEFAULT_ICON : icon);
this.placeInAir = placeInAir;
}
@Override
public boolean isFoil(ItemStack itemStack) {
return true;
}
@Override
@Environment(EnvType.CLIENT)
public BlockModel getItemModel(ResourceLocation resourceLocation) {
return ModelsHelper.createItemModel(icon);
}
@Override
public InteractionResult useOn(UseOnContext useOnContext) {
if (!useOnContext.getPlayer().canUseGameMasterBlocks()) {
return InteractionResult.FAIL;
}
return interaction.use(useOnContext);
}
public static void message(Player player, String text) {
message(player, text, ColorUtil.GRAY);
}
public static void message(Player player, String text, int color) {
message(player, Component.literal(text).withStyle(Style.EMPTY.withColor(color)));
}
public static void message(Player player, Component component) {
if (player instanceof ServerPlayer sp) {
sp.sendSystemMessage(component, true);
}
}
@Override
public boolean canAttackBlock(BlockState blockState, Level level, BlockPos blockPos, Player player) {
return false;
}
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand interactionHand) {
if (placeInAir) {
final var vec = new Vec3(0, 0, 1)
.xRot(-player.getXRot() * Mth.DEG_TO_RAD)
.yRot(-player.getYHeadRot() * Mth.DEG_TO_RAD);
BlockHitResult hit = level.isBlockInLine(new ClipBlockStateContext(
player.getEyePosition(),
player.getEyePosition().add(vec.scale(6.0)),
BlockBehaviour.BlockStateBase::isAir
));
if (hit != null) {
var result = this.useOn(new UseOnContext(player, interactionHand, hit));
if (result == InteractionResult.SUCCESS)
return InteractionResultHolder.success(player.getItemInHand(interactionHand));
else if (result == InteractionResult.FAIL)
return InteractionResultHolder.fail(player.getItemInHand(interactionHand));
else if (result == InteractionResult.PASS)
return InteractionResultHolder.pass(player.getItemInHand(interactionHand));
else if (result == InteractionResult.CONSUME)
return InteractionResultHolder.consume(player.getItemInHand(interactionHand));
}
}
return InteractionResultHolder.pass(player.getItemInHand(interactionHand));
}
public static DebugDataItem forLootTable(ResourceLocation table, Item icon) {
ResourceLocation iconId = BuiltInRegistries.ITEM.getKey(icon);
return new DebugDataItem(
(player, entity, ctx) -> {
CompoundTag tag = entity.saveWithoutMetadata();
tag.remove(RandomizableContainerBlockEntity.LOOT_TABLE_SEED_TAG);
tag.remove("Items");
tag.putString(RandomizableContainerBlockEntity.LOOT_TABLE_TAG, table.toString());
entity.load(tag);
message(player, "Did set Loot Table to " + table.toString());
return InteractionResult.SUCCESS;
},
false,
iconId
);
}
public static DebugDataItem forSteetJigSaw(
String modID,
ResourceKey<StructureTemplatePool> pool,
Item icon
) {
return forJigsaw(
pool == null ? Pools.EMPTY : pool,
new ResourceLocation(modID, "street"),
JigsawBlockEntity.JointType.ALIGNED,
null,
null,
icon
);
}
public static DebugDataItem forHouseEntranceJigSaw(
String modID,
ResourceKey<StructureTemplatePool> pool,
Item icon
) {
return forJigsaw(
pool == null ? Pools.EMPTY : pool,
new ResourceLocation(modID, "building_entrance"),
JigsawBlockEntity.JointType.ALIGNED,
null,
null,
icon
);
}
public static DebugDataItem forDecorationJigSaw(
String modID,
ResourceKey<StructureTemplatePool> pool,
Item icon
) {
return forJigsaw(
pool == null ? Pools.EMPTY : pool,
new ResourceLocation(modID, "bottom"),
pool == null ? new ResourceLocation("empty") : new ResourceLocation(modID, "bottom"),
JigsawBlockEntity.JointType.ROLLABLE,
null,
pool == null ? FrontAndTop.NORTH_UP : null,
icon
);
}
public static DebugDataItem forJigsaw(
ResourceKey<StructureTemplatePool> pool,
ResourceLocation connector,
JigsawBlockEntity.JointType type,
BlockState finalState,
FrontAndTop forceOrientation,
Item icon
) {
return forJigsaw(pool, connector, connector, type, finalState, forceOrientation, icon);
}
public static DebugDataItem forJigsaw(
ResourceKey<StructureTemplatePool> pool,
ResourceLocation name,
ResourceLocation target,
JigsawBlockEntity.JointType type,
BlockState finalState,
FrontAndTop forceOrientation,
Item icon
) {
ResourceLocation iconId = BuiltInRegistries.ITEM.getKey(icon);
return new DebugDataItem(
(ctx) -> {
final var player = ctx.getPlayer();
final var level = ctx.getLevel();
final var pos = ctx.getClickedPos();
var state = level.getBlockState(pos);
var entity = level.getBlockEntity(pos);
var targetState = finalState;
if (!(entity instanceof JigsawBlockEntity)) {
if (targetState == null)
targetState = state;
state = Blocks.JIGSAW.defaultBlockState();
level.setBlock(pos, state, BlocksHelper.SET_SILENT);
entity = level.getBlockEntity(pos);
message(player, "Created JigSaw at " + pos.toString());
}
if (entity instanceof JigsawBlockEntity e) {
if (forceOrientation == null) {
state = PlaceCommand.setJigsawOrientation(
JigsawBlockEntity.JointType.ROLLABLE != type,
player, pos, state
);
} else {
state = state.setValue(JigsawBlock.ORIENTATION, forceOrientation);
}
level.setBlock(pos, state, BlocksHelper.SET_SILENT);
if (pool != null) e.setName(name);
if (pool != null) e.setTarget(target);
if (pool != null) e.setPool(pool);
if (targetState != null) e.setFinalState(BlockStateParser.serialize(targetState));
e.setJoint(type);
message(player, "Did update Jigsaw at " + pos.toString());
return InteractionResult.SUCCESS;
}
return InteractionResult.FAIL;
},
true,
iconId
);
}
}

View file

@ -0,0 +1,85 @@
package org.betterx.bclib.mixin.client;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.items.DebugDataItem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.*;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LevelRenderer.class)
public abstract class LevelRendererMixin {
@Final
@Shadow
private Minecraft minecraft;
@Shadow
@Final
private RenderBuffers renderBuffers;
@Shadow
protected static void renderShape(
PoseStack poseStack,
VertexConsumer vertexConsumer,
VoxelShape voxelShape,
double d,
double e,
double f,
float g,
float h,
float i,
float j
) {
}
@Inject(method = "renderLevel", at = @At(
value = "INVOKE",
target = "Lcom/mojang/blaze3d/systems/RenderSystem;getModelViewStack()Lcom/mojang/blaze3d/vertex/PoseStack;",
shift = At.Shift.BEFORE
))
public void bcl_renderLevel(
PoseStack poseStack,
float f,
long l,
boolean bl,
Camera camera,
GameRenderer gameRenderer,
LightTexture lightTexture,
Matrix4f matrix4f,
CallbackInfo info
) {
if (BCLib.isDevEnvironment() && minecraft.hitResult instanceof BlockHitResult blockHitResult) {
//will render a block outline when empty blocks are targeted
ItemStack item = minecraft.player.getMainHandItem();
if (item != null && (item.getItem() instanceof DebugDataItem ddi) && ddi.placeInAir) {
final var pos = blockHitResult.getBlockPos();
final var state = Blocks.DIRT.defaultBlockState();
MultiBufferSource.BufferSource bufferSource = this.renderBuffers.bufferSource();
VertexConsumer consumer = bufferSource.getBuffer(RenderType.lines());
Vec3 camPos = camera.getPosition();
this.renderShape(
poseStack, consumer,
state.getShape(minecraft.level, pos, CollisionContext.of(camera.getEntity())),
pos.getX() - camPos.x(), pos.getY() - camPos.y(), pos.getZ() - camPos.z(),
246.0f / 0xff, 250.0f / 0xff, 112.0f / 0xff, 0.75F
);
}
}
}
}

View file

@ -34,6 +34,7 @@ accessible method net/minecraft/world/level/block/Blocks ocelotOrParrot (Lnet/mi
accessible method net/minecraft/world/level/block/Blocks never (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/EntityType;)Ljava/lang/Boolean; accessible method net/minecraft/world/level/block/Blocks never (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/EntityType;)Ljava/lang/Boolean;
accessible method net/minecraft/world/level/block/Blocks never (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;)Z accessible method net/minecraft/world/level/block/Blocks never (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;)Z
accessible method net/minecraft/world/level/levelgen/structure/pools/SinglePoolElement <init> (Lcom/mojang/datafixers/util/Either;Lnet/minecraft/core/Holder;Lnet/minecraft/world/level/levelgen/structure/pools/StructureTemplatePool$Projection;)V accessible method net/minecraft/world/level/levelgen/structure/pools/SinglePoolElement <init> (Lcom/mojang/datafixers/util/Either;Lnet/minecraft/core/Holder;Lnet/minecraft/world/level/levelgen/structure/pools/StructureTemplatePool$Projection;)V
accessible method net/minecraft/world/level/levelgen/structure/pools/LegacySinglePoolElement <init> (Lcom/mojang/datafixers/util/Either;Lnet/minecraft/core/Holder;Lnet/minecraft/world/level/levelgen/structure/pools/StructureTemplatePool$Projection;)V
#Fields #Fields
accessible field net/minecraft/world/entity/ai/village/poi/PoiTypes TYPE_BY_STATE Ljava/util/Map; accessible field net/minecraft/world/entity/ai/village/poi/PoiTypes TYPE_BY_STATE Ljava/util/Map;

View file

@ -10,6 +10,7 @@
"ClientPacketListenerMixin", "ClientPacketListenerMixin",
"ClientRecipeBookMixin", "ClientRecipeBookMixin",
"FogRendererMixin", "FogRendererMixin",
"LevelRendererMixin",
"MinecraftMixin", "MinecraftMixin",
"ModelManagerMixin", "ModelManagerMixin",
"PresetEditorMixin", "PresetEditorMixin",