actual comparator visuals!

This commit is contained in:
yrsegal@gmail.com 2022-04-23 11:50:48 -04:00
parent 6c30117cf2
commit 6547047d11
19 changed files with 452 additions and 245 deletions

View file

@ -115,21 +115,20 @@ public abstract class BlockEntityAbstractImpetus extends PaucalBlockEntity imple
this.stepCircle();
}
public List<Pair<ItemStack, Component>> getScryingLensOverlay(BlockState state, BlockPos pos,
LocalPlayer observer, ClientLevel world, InteractionHand lensHand) {
var out = new ArrayList<Pair<ItemStack, Component>>();
public void applyScryingLensOverlay(List<Pair<ItemStack, Component>> lines,
BlockState state, BlockPos pos,
LocalPlayer observer, ClientLevel world, InteractionHand lensHand) {
if (world.getBlockEntity(pos) instanceof BlockEntityAbstractImpetus beai) {
var dustCount = (float) beai.getMana() / (float) HexConfig.dustManaAmount.get();
var dustCmp = new TranslatableComponent("hexcasting.tooltip.lens.impetus.mana",
String.format("%.2f", dustCount));
out.add(new Pair<>(new ItemStack(HexApiItems.AMETHYST_DUST), dustCmp));
lines.add(new Pair<>(new ItemStack(HexApiItems.AMETHYST_DUST), dustCmp));
var mishap = this.getLastMishap();
if (mishap != null) {
out.add(new Pair<>(new ItemStack(Items.MUSIC_DISC_11), mishap));
lines.add(new Pair<>(new ItemStack(Items.MUSIC_DISC_11), mishap));
}
}
return out;
}
@NotNull

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.api.client;
import com.mojang.datafixers.util.Pair;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
@ -9,13 +10,20 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.apache.commons.compress.utils.Lists;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@ -26,16 +34,53 @@ import java.util.concurrent.ConcurrentMap;
*/
@OnlyIn(Dist.CLIENT)
public final class ScryingLensOverlayRegistry {
private static final ConcurrentMap<ResourceLocation, Displayer> ID_LOOKUP = new ConcurrentHashMap<>();
private static final ConcurrentMap<ResourceLocation, OverlayBuilder> ID_LOOKUP = new ConcurrentHashMap<>();
// vectors are thread-safe!
private static final List<Pair<Predicate, Displayer>> PREDICATE_LOOKUP = new Vector<>();
private static final List<Pair<OverlayPredicate, OverlayBuilder>> PREDICATE_LOOKUP = new Vector<>();
// implemented as a map to allow for weak dereferencing
private static final Map<LocalPlayer, Pair<BlockPos, Integer>> comparatorData = new WeakHashMap<>();
public static void receiveComparatorValue(BlockPos pos, int value) {
LocalPlayer player = Minecraft.getInstance().player;
if (player != null) {
if (pos == null || value == -1)
comparatorData.remove(player);
else
comparatorData.put(player, new Pair<>(pos, value));
}
}
public static int getComparatorValue(boolean onlyRealComparators) {
var mc = Minecraft.getInstance();
var player = mc.player;
var level = mc.level;
var result = mc.hitResult;
if (player == null || level == null || result == null || result.getType() != HitResult.Type.BLOCK)
return -1;
var comparatorValue = comparatorData.get(player);
if (comparatorValue == null)
return -1;
var pos = ((BlockHitResult)result).getBlockPos();
if (!pos.equals(comparatorValue.getFirst()))
return -1;
var state = mc.level.getBlockState(pos);
if ((onlyRealComparators && !state.is(Blocks.COMPARATOR)) || (!onlyRealComparators && !state.hasAnalogOutputSignal()))
return -1;
return comparatorValue.getSecond();
}
/**
* Add the block to display things when the player is holding a lens and looking at it.
*
* @throws IllegalArgumentException if the block is already registered.
*/
public static void addDisplayer(Block block, Displayer displayer) {
public static void addDisplayer(Block block, OverlayBuilder displayer) {
addDisplayer(block.getRegistryName(), displayer);
}
@ -44,7 +89,7 @@ public final class ScryingLensOverlayRegistry {
*
* @throws IllegalArgumentException if the block ID is already registered.
*/
public static void addDisplayer(ResourceLocation blockID, Displayer displayer) {
public static void addDisplayer(ResourceLocation blockID, OverlayBuilder displayer) {
if (ID_LOOKUP.containsKey(blockID)) {
throw new IllegalArgumentException("Already have a displayer for " + blockID);
}
@ -57,47 +102,49 @@ public final class ScryingLensOverlayRegistry {
* These have a lower priority than the standard ID-based displays, so if an ID and predicate both match,
* this won't be displayed.
*/
public static void addPredicateDisplayer(Predicate predicate, Displayer displayer) {
public static void addPredicateDisplayer(OverlayPredicate predicate, OverlayBuilder displayer) {
PREDICATE_LOOKUP.add(new Pair<>(predicate, displayer));
}
/**
* Internal use only.
*/
public static @Nullable List<Pair<ItemStack, Component>> getLines(BlockState state, BlockPos pos,
LocalPlayer observer, ClientLevel world,
@Nullable InteractionHand lensHand) {
public static @NotNull List<Pair<ItemStack, Component>> getLines(BlockState state, BlockPos pos,
LocalPlayer observer, ClientLevel world,
@Nullable InteractionHand lensHand) {
List<Pair<ItemStack, Component>> lines = Lists.newArrayList();
var idLookedup = ID_LOOKUP.get(state.getBlock().getRegistryName());
if (idLookedup != null) {
return idLookedup.getLines(state, pos, observer, world, lensHand);
idLookedup.addLines(lines, state, pos, observer, world, lensHand);
}
for (var pair : PREDICATE_LOOKUP) {
if (pair.getFirst().test(state, pos, observer, world, lensHand)) {
return pair.getSecond().getLines(state, pos, observer, world, lensHand);
pair.getSecond().addLines(lines, state, pos, observer, world, lensHand);
}
}
return null;
return lines;
}
/**
* Return the lines displayed by the cursor: an item and some text.
* <p>
* The ItemStack can be null; if it is, the text isn't shifted over for it.
* The ItemStack can be empty; if it is, the text isn't shifted over for it.
*/
@FunctionalInterface
public interface Displayer {
List<Pair<ItemStack, Component>> getLines(BlockState state, BlockPos pos, LocalPlayer observer,
ClientLevel world,
@Nullable InteractionHand lensHand);
public interface OverlayBuilder {
void addLines(List<Pair<ItemStack, Component>> lines,
BlockState state, BlockPos pos, LocalPlayer observer,
ClientLevel world,
@Nullable InteractionHand lensHand);
}
/**
* Predicate for matching on a block state.
*/
@FunctionalInterface
public interface Predicate {
public interface OverlayPredicate {
boolean test(BlockState state, BlockPos pos, LocalPlayer observer, ClientLevel world, @Nullable InteractionHand lensHand);
}
}

View file

@ -174,7 +174,7 @@ public class HexAdditionalRenderers {
var bs = mc.level.getBlockState(pos);
var lines = ScryingLensOverlayRegistry.getLines(bs, pos, mc.player, mc.level, lensHand);
if (lines != null) {
if (!lines.isEmpty()) {
var window = mc.getWindow();
var x = window.getGuiScaledWidth() / 2f + 8f;
var y = window.getGuiScaledHeight() / 2f;
@ -184,13 +184,12 @@ public class HexAdditionalRenderers {
var maxWidth = (int) (window.getGuiScaledWidth() / 2f * 0.8f);
for (var pair : lines) {
var stack = pair.getFirst();
if (stack != null) {
if (!stack.isEmpty()) {
// this draws centered in the Y ...
RenderLib.renderItemStackInGui(ps, pair.getFirst(), 0, 0);
}
float tx = stack == null ? 0 : 18;
float tx = stack.isEmpty() ? 0 : 18;
float ty = 5;
// but this draws where y=0 is the baseline
var text = pair.getSecond();

View file

@ -28,7 +28,6 @@ import net.minecraft.client.renderer.ItemBlockRenderTypes;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRenderers;
import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.item.Item;
@ -45,8 +44,6 @@ import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class RegisterClientStuff {
@ -110,52 +107,43 @@ public class RegisterClientStuff {
private static void addScryingLensStuff() {
ScryingLensOverlayRegistry.addPredicateDisplayer(
(state, pos, observer, world, lensHand) -> state.getBlock() instanceof BlockAbstractImpetus,
(state, pos, observer, world, lensHand) -> {
(lines, state, pos, observer, world, lensHand) -> {
if (world.getBlockEntity(pos) instanceof BlockEntityAbstractImpetus beai) {
return beai.getScryingLensOverlay(state, pos, observer, world, lensHand);
} else {
return List.of();
beai.applyScryingLensOverlay(lines, state, pos, observer, world, lensHand);
}
});
ScryingLensOverlayRegistry.addDisplayer(HexBlocks.AKASHIC_BOOKSHELF.get(),
(state, pos, observer, world, lensHand) -> {
if (!(world.getBlockEntity(pos) instanceof BlockEntityAkashicBookshelf tile)) {
return List.of();
}
var out = new ArrayList<Pair<ItemStack, Component>>();
var recordPos = tile.getRecordPos();
var pattern = tile.getPattern();
if (recordPos != null && pattern != null) {
out.add(new Pair<>(new ItemStack(HexBlocks.AKASHIC_RECORD.get()), new TranslatableComponent(
"hexcasting.tooltip.lens.akashic.bookshelf.location",
recordPos.toShortString()
)));
if (world.getBlockEntity(recordPos) instanceof BlockEntityAkashicRecord record) {
out.add(new Pair<>(new ItemStack(Items.BOOK), record.getDisplayAt(pattern)));
(lines, state, pos, observer, world, lensHand) -> {
if (world.getBlockEntity(pos) instanceof BlockEntityAkashicBookshelf tile) {
var recordPos = tile.getRecordPos();
var pattern = tile.getPattern();
if (recordPos != null && pattern != null) {
lines.add(new Pair<>(new ItemStack(HexBlocks.AKASHIC_RECORD.get()), new TranslatableComponent(
"hexcasting.tooltip.lens.akashic.bookshelf.location",
recordPos.toShortString()
)));
if (world.getBlockEntity(recordPos) instanceof BlockEntityAkashicRecord record) {
lines.add(new Pair<>(new ItemStack(Items.BOOK), record.getDisplayAt(pattern)));
}
}
}
return out;
});
ScryingLensOverlayRegistry.addDisplayer(HexBlocks.AKASHIC_RECORD.get(),
((state, pos, observer, world, lensHand) -> {
if (!(world.getBlockEntity(pos) instanceof BlockEntityAkashicRecord tile)) {
return List.of();
(lines, state, pos, observer, world, lensHand) -> {
if (world.getBlockEntity(pos) instanceof BlockEntityAkashicRecord tile) {
int count = tile.getCount();
lines.add(new Pair<>(new ItemStack(HexBlocks.AKASHIC_BOOKSHELF.get()), new TranslatableComponent(
"hexcasting.tooltip.lens.akashic.record.count" + (count == 1 ? ".single" : ""),
count
)));
}
int count = tile.getCount();
return List.of(new Pair<>(new ItemStack(HexBlocks.AKASHIC_BOOKSHELF.get()), new TranslatableComponent(
"hexcasting.tooltip.lens.akashic.record.count" + (count == 1 ? ".single" : ""),
count
)));
}));
});
ScryingLensOverlayRegistry.addDisplayer(Blocks.REDSTONE_WIRE,
(state, pos, observer, world, lensHand) -> List.of(
(lines, state, pos, observer, world, lensHand) -> lines.add(
new Pair<>(
new ItemStack(Items.REDSTONE),
new TextComponent(String.valueOf(state.getValue(BlockStateProperties.POWER)))
@ -163,27 +151,41 @@ public class RegisterClientStuff {
));
ScryingLensOverlayRegistry.addDisplayer(Blocks.COMPARATOR,
(state, pos, observer, world, lensHand) -> List.of(
new Pair<>(
(lines, state, pos, observer, world, lensHand) -> {
int comparatorValue = ScryingLensOverlayRegistry.getComparatorValue(true);
lines.add(new Pair<>(
new ItemStack(Items.REDSTONE),
new TextComponent(String.valueOf(state.getDirectSignal(world, pos, state.getValue(BlockStateProperties.HORIZONTAL_FACING))))
.withStyle(ChatFormatting.RED)),
new Pair<>(
new TextComponent(comparatorValue == -1 ? "?" : String.valueOf(comparatorValue))
.withStyle(ChatFormatting.RED)));
lines.add(new Pair<>(
new ItemStack(Items.REDSTONE_TORCH),
new TextComponent(
state.getValue(ComparatorBlock.MODE) == ComparatorMode.COMPARE ? ">" : "-")
.withStyle(ChatFormatting.RED))));
.withStyle(ChatFormatting.RED)));
});
ScryingLensOverlayRegistry.addDisplayer(Blocks.REPEATER,
(state, pos, observer, world, lensHand) -> List.of(
new Pair<>(
(lines, state, pos, observer, world, lensHand) -> {
lines.add(new Pair<>(
new ItemStack(Items.REDSTONE),
new TextComponent(String.valueOf(state.getValue(RepeaterBlock.POWERED) ? 15 : 0))
.withStyle(ChatFormatting.RED)),
new Pair<>(
.withStyle(ChatFormatting.RED)));
lines.add(new Pair<>(
new ItemStack(Items.CLOCK),
new TextComponent(String.valueOf(state.getValue(RepeaterBlock.DELAY)))
.withStyle(ChatFormatting.YELLOW))));
.withStyle(ChatFormatting.YELLOW)));
});
ScryingLensOverlayRegistry.addPredicateDisplayer(
(state, pos, observer, world, lensHand) -> state.hasAnalogOutputSignal(),
(lines, state, pos, observer, world, lensHand) -> {
int comparatorValue = ScryingLensOverlayRegistry.getComparatorValue(false);
lines.add(
new Pair<>(
new ItemStack(Items.COMPARATOR),
new TextComponent(comparatorValue == -1 ? "?" : String.valueOf(comparatorValue))
.withStyle(ChatFormatting.RED)));
});
}
private static void registerDataHolderOverrides(DataHolderItem item) {

View file

@ -109,6 +109,16 @@ public class BlockAkashicBookshelf extends BlockAkashicFloodfiller implements En
return this.defaultBlockState().setValue(FACING, ctx.getHorizontalDirection().getOpposite());
}
@Override
public boolean hasAnalogOutputSignal(BlockState pState) {
return true;
}
@Override
public int getAnalogOutputSignal(BlockState pState, Level pLevel, BlockPos pPos) {
return pState.getValue(DATUM_TYPE).ordinal();
}
@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) {

View file

@ -50,9 +50,10 @@ public class BlockEntityStoredPlayerImpetus extends BlockEntityAbstractImpetus {
}
@Override
public List<Pair<ItemStack, Component>> getScryingLensOverlay(BlockState state, BlockPos pos, LocalPlayer observer,
ClientLevel world, InteractionHand lensHand) {
var list = super.getScryingLensOverlay(state, pos, observer, world, lensHand);
public void applyScryingLensOverlay(List<Pair<ItemStack, Component>> lines,
BlockState state, BlockPos pos, LocalPlayer observer,
ClientLevel world, InteractionHand lensHand) {
super.applyScryingLensOverlay(lines, state, pos, observer, world, lensHand);
var bound = this.getPlayer();
if (bound != null) {
@ -60,13 +61,11 @@ public class BlockEntityStoredPlayerImpetus extends BlockEntityAbstractImpetus {
String name = bound.getScoreboardName();
tag.putString("SkullOwner", name);
var head = new ItemStack(Items.PLAYER_HEAD, 1, tag);
list.add(new Pair<>(head, new TranslatableComponent("hexcasting.tooltip.lens.impetus.storedplayer", name)));
lines.add(new Pair<>(head, new TranslatableComponent("hexcasting.tooltip.lens.impetus.storedplayer", name)));
} else {
list.add(new Pair<>(new ItemStack(Items.BARRIER),
lines.add(new Pair<>(new ItemStack(Items.BARRIER),
new TranslatableComponent("hexcasting.tooltip.lens.impetus.storedplayer.none")));
}
return list;
}
@Override

View file

@ -1,12 +1,18 @@
package at.petrak.hexcasting.common.items;
import at.petrak.hexcasting.common.network.HexMessages;
import at.petrak.hexcasting.common.network.MsgUpdateComparatorVisualsAck;
import com.mojang.datafixers.util.Pair;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockSource;
import net.minecraft.core.dispenser.OptionalDispenseItemBehavior;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.stats.Stats;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.player.Player;
@ -15,10 +21,19 @@ import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Wearable;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.DispenserBlock;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraftforge.common.ForgeMod;
import net.minecraftforge.network.PacketDistributor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.WeakHashMap;
public class ItemLens extends Item implements Wearable {
public ItemLens(Properties pProperties) {
@ -55,7 +70,55 @@ public class ItemLens extends Item implements Wearable {
}
}
@Override
public void inventoryTick(ItemStack pStack, Level pLevel, Entity pEntity, int pSlotId, boolean pIsSelected) {
if (!pLevel.isClientSide() && pEntity instanceof ServerPlayer player && pIsSelected) {
sendComparatorDataToClient(player);
}
}
@Override
public void onArmorTick(ItemStack stack, Level level, Player player) {
if (!level.isClientSide() && player instanceof ServerPlayer serverPlayer) {
sendComparatorDataToClient(serverPlayer);
}
}
private static final Map<ServerPlayer, Pair<BlockPos, Integer>> comparatorDataMap = new WeakHashMap<>();
private void sendComparatorDataToClient(ServerPlayer player) {
double reachAttribute = player.getAttribute(ForgeMod.REACH_DISTANCE.get()).getValue();
double distance = player.isCreative() ? reachAttribute : reachAttribute - 0.5;
var hitResult = player.pick(distance, 0, false);
if (hitResult.getType() == HitResult.Type.BLOCK) {
var pos = ((BlockHitResult)hitResult).getBlockPos();
var state = player.level.getBlockState(pos);
if (state.is(Blocks.COMPARATOR)) {
syncComparatorValue(player, pos, state.getDirectSignal(player.level, pos, state.getValue(BlockStateProperties.HORIZONTAL_FACING)));
} else if (state.hasAnalogOutputSignal()) {
syncComparatorValue(player, pos, state.getAnalogOutputSignal(player.level, pos));
} else {
syncComparatorValue(player, null, -1);
}
} else
syncComparatorValue(player, null, -1);
}
private void syncComparatorValue(ServerPlayer player, BlockPos pos, int value) {
var previous = comparatorDataMap.get(player);
if (value == -1) {
if (previous != null) {
comparatorDataMap.remove(player);
HexMessages.getNetwork().send(PacketDistributor.PLAYER.with(() -> player), new MsgUpdateComparatorVisualsAck(null, -1));
}
} else if (previous == null || (!pos.equals(previous.getFirst()) || value != previous.getSecond())) {
comparatorDataMap.put(player, new Pair<>(pos, value));
HexMessages.getNetwork().send(PacketDistributor.PLAYER.with(() -> player), new MsgUpdateComparatorVisualsAck(pos, value));
}
}
@Nullable
@Override
public SoundEvent getEquipSound() {
return SoundEvents.AMETHYST_BLOCK_CHIME;
}

View file

@ -0,0 +1,166 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.api.client.ScryingLensOverlayRegistry;
import at.petrak.hexcasting.api.player.HexPlayerDataHelper;
import at.petrak.hexcasting.client.gui.GuiSpellcasting;
import at.petrak.hexcasting.common.lib.HexSounds;
import at.petrak.hexcasting.common.misc.Brainsweeping;
import at.petrak.hexcasting.common.particles.ConjureParticleOptions;
import net.minecraft.client.Minecraft;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import java.util.Random;
// Ah, java classloading, how I hate thee
public record ClientPacketHandler(Object rawMsg) {
@OnlyIn(Dist.CLIENT)
public void beep() {
if (!(rawMsg instanceof MsgBeepAck msg))
return;
var minecraft = Minecraft.getInstance();
var world = minecraft.level;
if (world != null){
float pitch = (float) Math.pow(2, (msg.note() - 12) / 12.0);
world.playLocalSound(msg.target().x, msg.target().y, msg.target().z, msg.instrument().getSoundEvent(), SoundSource.PLAYERS, 3, pitch, false);
world.addParticle(ParticleTypes.NOTE, msg.target().x, msg.target().y + 0.2, msg.target().z, msg.note() / 24.0, 0, 0);
}
}
@OnlyIn(Dist.CLIENT)
public void blink() {
if (!(rawMsg instanceof MsgBlinkAck msg))
return;
var player = Minecraft.getInstance().player;
player.setPos(player.position().add(msg.addedPosition()));
}
@OnlyIn(Dist.CLIENT)
public void updateComparator() {
if (!(rawMsg instanceof MsgUpdateComparatorVisualsAck msg))
return;
ScryingLensOverlayRegistry.receiveComparatorValue(msg.pos(), msg.value());
}
@OnlyIn(Dist.CLIENT)
public void openSpellGui() {
if (!(rawMsg instanceof MsgOpenSpellGuiAck msg))
return;
var mc = Minecraft.getInstance();
mc.setScreen(new GuiSpellcasting(msg.hand(), msg.patterns(), msg.components()));
}
@OnlyIn(Dist.CLIENT)
public void brainsweep() {
if (!(rawMsg instanceof MsgBrainsweepAck msg))
return;
var level = Minecraft.getInstance().level;
if (level != null) {
Entity entity = level.getEntity(msg.target());
if (entity instanceof LivingEntity living) {
Brainsweeping.brainsweep(living);
}
}
}
@OnlyIn(Dist.CLIENT)
public void newPattern() {
if (!(rawMsg instanceof MsgNewSpellPatternAck msg))
return;
var mc = Minecraft.getInstance();
if (msg.info().isStackClear()) {
// don't pay attention to the screen, so it also stops when we die
mc.getSoundManager().stop(HexSounds.CASTING_AMBIANCE.getId(), null);
}
var screen = Minecraft.getInstance().screen;
if (screen instanceof GuiSpellcasting spellGui) {
if (msg.info().isStackClear()) {
mc.setScreen(null);
} else {
spellGui.recvServerUpdate(msg.info());
}
}
}
@OnlyIn(Dist.CLIENT)
public void updateColorizer() {
if (!(rawMsg instanceof MsgColorizerUpdateAck msg))
return;
var player = Minecraft.getInstance().player;
if (player != null) {
HexPlayerDataHelper.setColorizer(player, msg.update());
}
}
@OnlyIn(Dist.CLIENT)
public void updateSentinel() {
if (!(rawMsg instanceof MsgSentinelStatusUpdateAck msg))
return;
var player = Minecraft.getInstance().player;
if (player != null) {
HexPlayerDataHelper.setSentinel(player, msg.update());
}
}
private static final Random RANDOM = new Random();
// https://math.stackexchange.com/questions/44689/how-to-find-a-random-axis-or-unit-vector-in-3d
private static Vec3 randomInCircle(double maxTh) {
var th = RANDOM.nextDouble(0.0, maxTh + 0.001);
var z = RANDOM.nextDouble(-1.0, 1.0);
return new Vec3(Math.sqrt(1.0 - z * z) * Math.cos(th), Math.sqrt(1.0 - z * z) * Math.sin(th), z);
}
@OnlyIn(Dist.CLIENT)
public void particleSpray() {
if (!(rawMsg instanceof MsgCastParticleAck msg))
return;
for (int i = 0; i < msg.spray().getCount(); i++) {
// For the colors, pick any random time to get a mix of colors
var color = msg.colorizer().getColor(RANDOM.nextFloat() * 256f, Vec3.ZERO);
var offset = randomInCircle(Mth.TWO_PI).normalize()
.scale(RANDOM.nextFloat() * msg.spray().getSpread() / 2);
var pos = msg.spray().getPos().add(offset);
var phi = Math.acos(1.0 - RANDOM.nextDouble() * (1.0 - Math.cos(msg.spray().getSpread())));
var theta = Math.PI * 2.0 * RANDOM.nextDouble();
var v = msg.spray().getVel().normalize();
// pick any old vector to get a vector normal to v with
Vec3 k;
if (v.x == 0.0 && v.y == 0.0) {
// oops, pick a *different* normal
k = new Vec3(1.0, 0.0, 0.0);
} else {
k = v.cross(new Vec3(0.0, 0.0, 1.0));
}
var velUnlen = v.scale(Math.cos(phi))
.add(k.scale(Math.sin(phi) * Math.cos(theta)))
.add(v.cross(k).scale(Math.sin(phi) * Math.sin(theta)));
var vel = velUnlen.scale(msg.spray().getVel().length() / 20);
Minecraft.getInstance().level.addParticle(
new ConjureParticleOptions(color, false),
pos.x, pos.y, pos.z,
vel.x, vel.y, vel.z
);
}
}
}

View file

@ -1,14 +0,0 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.client.gui.GuiSpellcasting;
import net.minecraft.client.Minecraft;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public record GuiOpener(MsgOpenSpellGuiAck msg) {
@OnlyIn(Dist.CLIENT)
public void openGui() {
var mc = Minecraft.getInstance();
mc.setScreen(new GuiSpellcasting(msg.hand(), msg.patterns(), msg.components()));
}
}

View file

@ -37,11 +37,13 @@ public class HexMessages {
NETWORK.registerMessage(messageIdx++, MsgCastParticleAck.class, MsgCastParticleAck::serialize,
MsgCastParticleAck::deserialize, MsgCastParticleAck::handle);
NETWORK.registerMessage(messageIdx++, MsgOpenSpellGuiAck.class, MsgOpenSpellGuiAck::serialize,
MsgOpenSpellGuiAck::deserialize, MsgOpenSpellGuiAck::handle);
MsgOpenSpellGuiAck::deserialize, MsgOpenSpellGuiAck::handle);
NETWORK.registerMessage(messageIdx++, MsgBeepAck.class, MsgBeepAck::serialize,
MsgBeepAck::deserialize, MsgBeepAck::handle);
MsgBeepAck::deserialize, MsgBeepAck::handle);
NETWORK.registerMessage(messageIdx++, MsgBrainsweepAck.class, MsgBrainsweepAck::serialize,
MsgBrainsweepAck::deserialize, MsgBrainsweepAck::handle);
MsgBrainsweepAck::deserialize, MsgBrainsweepAck::handle);
NETWORK.registerMessage(messageIdx++, MsgUpdateComparatorVisualsAck.class, MsgUpdateComparatorVisualsAck::serialize,
MsgUpdateComparatorVisualsAck::deserialize, MsgUpdateComparatorVisualsAck::handle);
HexApiMessages.setSyncChannel(NETWORK, MsgSentinelStatusUpdateAck::new, MsgColorizerUpdateAck::new, MsgCastParticleAck::new);
}

View file

@ -1,10 +1,7 @@
package at.petrak.hexcasting.common.network;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
@ -37,17 +34,10 @@ public record MsgBeepAck(Vec3 target, int note, NoteBlockInstrument instrument)
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() ->
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
var minecraft = Minecraft.getInstance();
var world = minecraft.level;
if (world != null){
float pitch = (float) Math.pow(2, (note - 12) / 12.0);
world.playLocalSound(target.x, target.y, target.z, instrument.getSoundEvent(), SoundSource.PLAYERS, 3, pitch, false);
world.addParticle(ParticleTypes.NOTE, target.x, target.y + 0.2, target.z, note / 24.0, 0, 0);
}
})
);
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::beep);
});
ctx.get().setPacketHandled(true);
}
}

View file

@ -1,7 +1,6 @@
package at.petrak.hexcasting.common.network;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
@ -11,9 +10,9 @@ import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
/**
* Sent server->client to synchronize OpAddMotion when the target is a player.
* Sent server->client to synchronize OpBlink when the target is a player.
*/
public record MsgBlinkAck(Vec3 addedMotion) {
public record MsgBlinkAck(Vec3 addedPosition) {
public static MsgBlinkAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
var x = buf.readDouble();
@ -24,18 +23,16 @@ public record MsgBlinkAck(Vec3 addedMotion) {
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
buf.writeDouble(this.addedMotion.x);
buf.writeDouble(this.addedMotion.y);
buf.writeDouble(this.addedMotion.z);
buf.writeDouble(this.addedPosition.x);
buf.writeDouble(this.addedPosition.y);
buf.writeDouble(this.addedPosition.z);
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() ->
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
var player = Minecraft.getInstance().player;
player.setPos(player.position().add(this.addedMotion));
})
);
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::blink);
});
ctx.get().setPacketHandled(true);
}
}

View file

@ -1,11 +1,8 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.common.misc.Brainsweeping;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
@ -33,23 +30,10 @@ public record MsgBrainsweepAck(int target) {
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() ->
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
IHateJava.handle(target);
})
);
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::brainsweep);
});
ctx.get().setPacketHandled(true);
}
private static class IHateJava {
public static void handle(int target) {
var level = Minecraft.getInstance().level;
if (level != null) {
Entity entity = level.getEntity(target);
if (entity instanceof LivingEntity living) {
Brainsweeping.brainsweep(living);
}
}
}
}
}

View file

@ -1,25 +1,20 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.api.spell.ParticleSpray;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.common.particles.ConjureParticleOptions;
import at.petrak.hexcasting.api.spell.ParticleSpray;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import java.util.Random;
import java.util.function.Supplier;
/**
* Sent server->client to spray particles everywhere.
*/
public record MsgCastParticleAck(ParticleSpray spray, FrozenColorizer colorizer) {
private static final Random RANDOM = new Random();
public static MsgCastParticleAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@ -54,48 +49,13 @@ public record MsgCastParticleAck(ParticleSpray spray, FrozenColorizer colorizer)
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() ->
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
for (int i = 0; i < spray.getCount(); i++) {
// For the colors, pick any random time to get a mix of colors
var color = colorizer.getColor(RANDOM.nextFloat() * 256f, Vec3.ZERO);
var offset = randomInCircle(Mth.TWO_PI).normalize()
.scale(RANDOM.nextFloat() * spray.getSpread() / 2);
var pos = spray.getPos().add(offset);
var phi = Math.acos(1.0 - RANDOM.nextDouble() * (1.0 - Math.cos(spray.getSpread())));
var theta = Math.PI * 2.0 * RANDOM.nextDouble();
var v = spray.getVel().normalize();
// pick any old vector to get a vector normal to v with
Vec3 k;
if (v.x == 0.0 && v.y == 0.0) {
// oops, pick a *different* normal
k = new Vec3(1.0, 0.0, 0.0);
} else {
k = v.cross(new Vec3(0.0, 0.0, 1.0));
}
var velUnlen = v.scale(Math.cos(phi))
.add(k.scale(Math.sin(phi) * Math.cos(theta)))
.add(v.cross(k).scale(Math.sin(phi) * Math.sin(theta)));
var vel = velUnlen.scale(spray.getVel().length() / 20);
Minecraft.getInstance().level.addParticle(
new ConjureParticleOptions(color, false),
pos.x, pos.y, pos.z,
vel.x, vel.y, vel.z
);
}
})
);
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::particleSpray);
});
ctx.get().setPacketHandled(true);
}
// https://math.stackexchange.com/questions/44689/how-to-find-a-random-axis-or-unit-vector-in-3d
private static Vec3 randomInCircle(double maxTh) {
var th = RANDOM.nextDouble(0.0, maxTh + 0.001);
var z = RANDOM.nextDouble(-1.0, 1.0);
return new Vec3(Math.sqrt(1.0 - z * z) * Math.cos(th), Math.sqrt(1.0 - z * z) * Math.sin(th), z);
}
}

View file

@ -1,9 +1,7 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.api.player.HexPlayerDataHelper;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
@ -29,20 +27,10 @@ public record MsgColorizerUpdateAck(FrozenColorizer update) {
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() ->
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
IHateJava.handle(this.update);
})
);
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::updateColorizer);
});
ctx.get().setPacketHandled(true);
}
private static class IHateJava {
public static void handle(FrozenColorizer update) {
var player = Minecraft.getInstance().player;
if (player != null) {
HexPlayerDataHelper.setColorizer(player, update);
}
}
}
}

View file

@ -1,10 +1,7 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.client.gui.GuiSpellcasting;
import at.petrak.hexcasting.api.spell.casting.ControllerInfo;
import at.petrak.hexcasting.common.lib.HexSounds;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraftforge.api.distmarker.Dist;
@ -51,23 +48,10 @@ public record MsgNewSpellPatternAck(ControllerInfo info) {
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() ->
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
var mc = Minecraft.getInstance();
if (this.info.isStackClear()) {
// don't pay attention to the screen, so it also stops when we die
mc.getSoundManager().stop(HexSounds.CASTING_AMBIANCE.getId(), null);
}
var screen = Minecraft.getInstance().screen;
if (screen instanceof GuiSpellcasting spellGui) {
if (this.info.isStackClear()) {
mc.setScreen(null);
} else {
spellGui.recvServerUpdate(this.info);
}
}
})
);
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::newPattern);
});
ctx.get().setPacketHandled(true);
}

View file

@ -56,8 +56,8 @@ public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> pat
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
GuiOpener opener = new GuiOpener(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> opener::openGui);
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::openSpellGui);
});
ctx.get().setPacketHandled(true);
}

View file

@ -1,9 +1,7 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.api.player.HexPlayerDataHelper;
import at.petrak.hexcasting.api.player.Sentinel;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.core.Registry;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey;
@ -41,20 +39,10 @@ public record MsgSentinelStatusUpdateAck(Sentinel update) {
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() ->
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
IHateJava.handle(this.update);
})
);
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::updateSentinel);
});
ctx.get().setPacketHandled(true);
}
private static class IHateJava {
public static void handle(Sentinel update) {
var player = Minecraft.getInstance().player;
if (player != null) {
HexPlayerDataHelper.setSentinel(player, update);
}
}
}
}

View file

@ -0,0 +1,43 @@
package at.petrak.hexcasting.common.network;
import io.netty.buffer.ByteBuf;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
/**
* Sent server->client when a player is looking at a block through a lens whose comparator value is not the same as what they last saw.
*/
public record MsgUpdateComparatorVisualsAck(BlockPos pos, int value) {
public static MsgUpdateComparatorVisualsAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
int value = buf.readInt();
BlockPos pos = value == -1 ? null : buf.readBlockPos();
return new MsgUpdateComparatorVisualsAck(pos, value);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
buf.writeInt(this.value);
if (this.value != -1) {
buf.writeBlockPos(this.pos);
}
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::updateComparator);
});
ctx.get().setPacketHandled(true);
}
}