work on packets and networking

This commit is contained in:
gamma-delta 2022-04-27 15:56:59 -05:00
parent df1b7f0963
commit dece4d851a
26 changed files with 602 additions and 603 deletions

View file

@ -1,127 +0,0 @@
package at.petrak.hexcasting
import at.petrak.hexcasting.api.PatternRegistry
import at.petrak.hexcasting.api.advancements.HexAdvancementTriggers
import at.petrak.hexcasting.api.mod.HexConfig
import at.petrak.hexcasting.api.mod.HexStatistics
import at.petrak.hexcasting.api.player.HexPlayerDataHelper
import at.petrak.hexcasting.client.*
import at.petrak.hexcasting.common.blocks.HexBlockEntities
import at.petrak.hexcasting.common.blocks.HexBlocks
import at.petrak.hexcasting.common.casting.RegisterPatterns
import at.petrak.hexcasting.common.casting.operators.spells.great.OpFlight
import at.petrak.hexcasting.common.command.HexCommands
import at.petrak.hexcasting.common.command.PatternResLocArgument
import at.petrak.hexcasting.common.entities.HexEntities
import at.petrak.hexcasting.common.items.HexItems
import at.petrak.hexcasting.common.lib.HexCapabilityHandler
import at.petrak.hexcasting.common.lib.HexSounds
import at.petrak.hexcasting.common.misc.Brainsweeping
import at.petrak.hexcasting.common.network.HexMessages
import at.petrak.hexcasting.common.particles.HexParticles
import at.petrak.hexcasting.common.recipe.HexComposting
import at.petrak.hexcasting.common.recipe.HexCustomRecipes
import at.petrak.hexcasting.common.recipe.HexRecipeSerializers
import at.petrak.hexcasting.datagen.HexDataGenerators
import at.petrak.hexcasting.datagen.lootmods.HexLootModifiers
import at.petrak.hexcasting.server.TickScheduler
import net.minecraft.commands.synchronization.ArgumentTypes
import net.minecraft.commands.synchronization.EmptyArgumentSerializer
import net.minecraftforge.api.distmarker.Dist
import net.minecraftforge.common.ForgeConfigSpec
import net.minecraftforge.eventbus.api.SubscribeEvent
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.FMLCommonSetupEvent
import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@Mod(HexMod.MOD_ID)
object HexMod {
// hmm today I will use a popular logging framework :clueless:
val LOGGER: Logger = LoggerFactory.getLogger(HexMod::class.java)
val CONFIG_SPEC: ForgeConfigSpec
val SERVER_CONFIG_SPEC: ForgeConfigSpec
val CLIENT_CONFIG_SPEC: ForgeConfigSpec
// mumblemumble thanks shy mumble mumble
const val MOD_ID = "hexcasting"
init {
CONFIG_SPEC = ForgeConfigSpec.Builder()
.configure { builder: ForgeConfigSpec.Builder? -> HexConfig(builder) }.right
SERVER_CONFIG_SPEC = ForgeConfigSpec.Builder()
.configure { builder: ForgeConfigSpec.Builder? -> HexConfig.Server(builder) }.right
CLIENT_CONFIG_SPEC = ForgeConfigSpec.Builder()
.configure { builder: ForgeConfigSpec.Builder? -> HexConfig.Client(builder) }.right
ArgumentTypes.register("hexcasting:pattern", PatternResLocArgument::class.java, EmptyArgumentSerializer(PatternResLocArgument::id))
// mod lifecycle
val modBus = thedarkcolour.kotlinforforge.forge.MOD_BUS
// game events
val evBus = thedarkcolour.kotlinforforge.forge.FORGE_BUS
modBus.register(this)
// gotta do it at *some* point
modBus.register(RegisterPatterns::class.java)
modBus.register(HexDataGenerators::class.java)
HexItems.ITEMS.register(modBus)
HexBlocks.BLOCKS.register(modBus)
HexBlockEntities.BLOCK_ENTITIES.register(modBus)
HexEntities.ENTITIES.register(modBus)
HexLootModifiers.LOOT_MODS.register(modBus)
HexSounds.SOUNDS.register(modBus)
HexParticles.PARTICLES.register(modBus)
HexCustomRecipes.RECIPES.register(modBus)
HexRecipeSerializers.SERIALIZERS.register(modBus)
modBus.register(HexStatistics::class.java)
modBus.register(HexRecipeSerializers::class.java)
modBus.register(HexComposting::class.java)
evBus.register(HexCommands::class.java)
evBus.register(TickScheduler)
evBus.register(HexCapabilityHandler::class.java)
evBus.register(HexPlayerDataHelper::class.java)
evBus.register(OpFlight)
evBus.register(Brainsweeping::class.java)
DistExecutor.unsafeRunWhenOn(Dist.CLIENT) {
Runnable {
modBus.register(RegisterClientStuff::class.java)
evBus.register(ClientTickCounter::class.java)
evBus.register(HexAdditionalRenderers::class.java)
evBus.register(ShiftScrollListener::class.java)
evBus.register(HexTooltips::class.java)
}
}
// and then things that don't require busses
HexMessages.register()
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, CONFIG_SPEC)
ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, SERVER_CONFIG_SPEC)
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, CLIENT_CONFIG_SPEC)
}
@SubscribeEvent
fun commonSetup(evt: FMLCommonSetupEvent) {
evt.enqueueWork { HexAdvancementTriggers.registerTriggers() }
}
@JvmStatic
fun getLogger() = this.LOGGER
@SubscribeEvent
fun printPatternCount(evt: FMLLoadCompleteEvent) {
getLogger().info(
PatternRegistry.getPatternCountInfo()
)
}
}

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.api;
import com.google.common.base.Suppliers;
import net.minecraft.resources.ResourceLocation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -24,4 +25,8 @@ public interface HexAPI {
static HexAPI instance() {
return INSTANCE.get();
}
static ResourceLocation modLoc(String s) {
return new ResourceLocation(MOD_ID, s);
}
}

View file

@ -1,5 +1,8 @@
package at.petrak.hexcasting.api.mod;
// Don't understand what this does so i commented it all out :gigachad:
/*
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.api.player.Sentinel;
import at.petrak.hexcasting.api.spell.ParticleSpray;
@ -44,3 +47,5 @@ public final class HexApiMessages {
return particleSprayMessage.apply(spray, colorizer);
}
}
*/

View file

@ -0,0 +1,9 @@
package at.petrak.hexcasting.common.lib;
/**
* Tags, capability keys, etc that need to be used in more than one place
* (for example, across platforms, or between an item and block).
*/
public class HexStringKeys {
public static final String BRAINSWEPT = "hexcasting:brainswept";
}

View file

@ -2,64 +2,50 @@ package at.petrak.hexcasting.common.misc;
import at.petrak.hexcasting.common.network.HexMessages;
import at.petrak.hexcasting.common.network.MsgBrainsweepAck;
import at.petrak.hexcasting.mixin.AccessorLivingEntity;
import net.minecraft.server.level.ServerLevel;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.Brain;
import net.minecraft.world.entity.npc.Villager;
import net.minecraft.world.entity.npc.VillagerDataHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraftforge.event.entity.living.LivingConversionEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.network.PacketDistributor;
import org.jetbrains.annotations.Nullable;
public class Brainsweeping {
public static final String TAG_BRAINSWEPT = "hexcasting:brainswept";
public static boolean isBrainswept(LivingEntity entity) {
return entity instanceof VillagerDataHolder && entity.getPersistentData().getBoolean(TAG_BRAINSWEPT);
// Keeping these functions in Brainsweeping just so we have to change less code
public static void brainsweep(LivingEntity entity) {
IXplatAbstractions.INSTANCE.brainsweep(entity);
}
public static void brainsweep(LivingEntity entity) {
if (entity instanceof VillagerDataHolder) {
entity.getPersistentData().putBoolean(TAG_BRAINSWEPT, true);
if (entity instanceof Mob mob)
mob.removeFreeWill();
if (entity instanceof Villager villager) {
Brain<Villager> brain = villager.getBrain();
if (entity.level instanceof ServerLevel slevel) {
brain.stopAll(slevel, villager);
}
((AccessorLivingEntity) entity).hex$SetBrain(brain.copyWithoutBehaviors());
}
if (entity.level instanceof ServerLevel) {
HexMessages.getNetwork().send(PacketDistributor.TRACKING_ENTITY.with(() -> entity), MsgBrainsweepAck.of(entity));
}
}
public static boolean isBrainswept(LivingEntity entity) {
return IXplatAbstractions.INSTANCE.isBrainswept(entity);
}
@SubscribeEvent
public static void startTracking(PlayerEvent.StartTracking evt) {
Entity target = evt.getTarget();
if (evt.getPlayer() instanceof ServerPlayer serverPlayer &&
target instanceof VillagerDataHolder && target instanceof LivingEntity living && isBrainswept(living)) {
HexMessages.getNetwork().send(PacketDistributor.PLAYER.with(() -> serverPlayer), MsgBrainsweepAck.of(living));
target instanceof VillagerDataHolder && target instanceof LivingEntity living && isBrainswept(living)) {
HexMessages.getNetwork()
.send(PacketDistributor.PLAYER.with(() -> serverPlayer), MsgBrainsweepAck.of(living));
}
}
@SubscribeEvent
public static void tradeWithVillager(PlayerInteractEvent.EntityInteract evt) {
if (evt.getTarget() instanceof Villager v && isBrainswept(v)) {
evt.setCanceled(true);
public static InteractionResult tradeWithVillager(Player player, Level world, InteractionHand hand, Entity entity,
@Nullable EntityHitResult hitResult) {
if (entity instanceof Villager v && IXplatAbstractions.INSTANCE.isBrainswept(v)) {
return InteractionResult.FAIL;
}
return InteractionResult.PASS;
}
@SubscribeEvent
@ -67,7 +53,9 @@ public class Brainsweeping {
var outcome = evt.getOutcome();
var original = evt.getEntityLiving();
if (outcome instanceof VillagerDataHolder && original instanceof VillagerDataHolder) {
if (isBrainswept(original)) brainsweep(outcome);
if (isBrainswept(original)) {
brainsweep(outcome);
}
}
}
}

View file

@ -1,166 +0,0 @@
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,50 +1,5 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.HexMod;
import at.petrak.hexcasting.api.mod.HexApiMessages;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.simple.SimpleChannel;
public class HexMessages {
private static final String PROTOCOL_VERSION = "1";
private static final SimpleChannel NETWORK = NetworkRegistry.newSimpleChannel(
new ResourceLocation(HexMod.MOD_ID, "main"),
() -> PROTOCOL_VERSION,
PROTOCOL_VERSION::equals,
PROTOCOL_VERSION::equals
);
public static SimpleChannel getNetwork() {
return NETWORK;
}
public static void register() {
int messageIdx = 0;
NETWORK.registerMessage(messageIdx++, MsgNewSpellPatternSyn.class, MsgNewSpellPatternSyn::serialize,
MsgNewSpellPatternSyn::deserialize, MsgNewSpellPatternSyn::handle);
NETWORK.registerMessage(messageIdx++, MsgNewSpellPatternAck.class, MsgNewSpellPatternAck::serialize,
MsgNewSpellPatternAck::deserialize, MsgNewSpellPatternAck::handle);
NETWORK.registerMessage(messageIdx++, MsgShiftScrollSyn.class, MsgShiftScrollSyn::serialize,
MsgShiftScrollSyn::deserialize, MsgShiftScrollSyn::handle);
NETWORK.registerMessage(messageIdx++, MsgBlinkAck.class, MsgBlinkAck::serialize,
MsgBlinkAck::deserialize, MsgBlinkAck::handle);
NETWORK.registerMessage(messageIdx++, MsgSentinelStatusUpdateAck.class, MsgSentinelStatusUpdateAck::serialize,
MsgSentinelStatusUpdateAck::deserialize, MsgSentinelStatusUpdateAck::handle);
NETWORK.registerMessage(messageIdx++, MsgColorizerUpdateAck.class, MsgColorizerUpdateAck::serialize,
MsgColorizerUpdateAck::deserialize, MsgColorizerUpdateAck::handle);
NETWORK.registerMessage(messageIdx++, MsgCastParticleAck.class, MsgCastParticleAck::serialize,
MsgCastParticleAck::deserialize, MsgCastParticleAck::handle);
NETWORK.registerMessage(messageIdx++, MsgOpenSpellGuiAck.class, MsgOpenSpellGuiAck::serialize,
MsgOpenSpellGuiAck::deserialize, MsgOpenSpellGuiAck::handle);
NETWORK.registerMessage(messageIdx++, MsgBeepAck.class, MsgBeepAck::serialize,
MsgBeepAck::deserialize, MsgBeepAck::handle);
NETWORK.registerMessage(messageIdx++, MsgBrainsweepAck.class, MsgBrainsweepAck::serialize,
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

@ -0,0 +1,24 @@
package at.petrak.hexcasting.common.network;
import io.netty.buffer.Unpooled;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
// https://github.com/VazkiiMods/Botania/blob/1.18.x/Common/src/main/java/vazkii/botania/network/IPacket.java
// yoink
public interface IMessage {
default FriendlyByteBuf toBuf() {
var ret = new FriendlyByteBuf(Unpooled.buffer());
serialize(ret);
return ret;
}
void serialize(FriendlyByteBuf buf);
/**
* Forge auto-assigns incrementing integers, Fabric requires us to declare an ID
* These are sent using vanilla's custom plugin channel system and thus are written to every single packet.
* So this ID tends to be more terse.
*/
ResourceLocation getFabricId();
}

View file

@ -1,19 +1,24 @@
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.resources.ResourceLocation;
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;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
public record MsgBeepAck(Vec3 target, int note, NoteBlockInstrument instrument) implements IMessage {
public static final ResourceLocation ID = modLoc("beep");
@Override
public ResourceLocation getFabricId() {
return ID;
}
/**
* Sent server->client to synchronize OpAddMotion when the target is a player.
*/
public record MsgBeepAck(Vec3 target, int note, NoteBlockInstrument instrument) {
public static MsgBeepAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
var x = buf.readDouble();
@ -24,20 +29,29 @@ public record MsgBeepAck(Vec3 target, int note, NoteBlockInstrument instrument)
return new MsgBeepAck(new Vec3(x, y, z), note, instrument);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@Override
public void serialize(FriendlyByteBuf buf) {
buf.writeDouble(this.target.x);
buf.writeDouble(this.target.y);
buf.writeDouble(this.target.z);
buf.writeInt(this.note);
buf.writeEnum(instrument);
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::beep);
public static void handle(MsgBeepAck msg) {
Minecraft.getInstance().execute(new Runnable() {
@Override
public void run() {
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);
}
}
});
ctx.get().setPacketHandled(true);
}
}

View file

@ -1,18 +1,24 @@
package at.petrak.hexcasting.common.network;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
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.function.Supplier;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
/**
* Sent server->client to synchronize OpBlink when the target is a player.
*/
public record MsgBlinkAck(Vec3 addedPosition) {
public record MsgBlinkAck(Vec3 addedPosition) implements IMessage {
public static final ResourceLocation ID = modLoc("blink");
@Override
public ResourceLocation getFabricId() {
return ID;
}
public static MsgBlinkAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
var x = buf.readDouble();
@ -21,18 +27,20 @@ public record MsgBlinkAck(Vec3 addedPosition) {
return new MsgBlinkAck(new Vec3(x, y, z));
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@Override
public void serialize(FriendlyByteBuf buf) {
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(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::blink);
public static void handle(MsgBlinkAck self) {
Minecraft.getInstance().execute(new Runnable() {
@Override
public void run() {
var player = Minecraft.getInstance().player;
player.setPos(player.position().add(self.addedPosition()));
}
});
ctx.get().setPacketHandled(true);
}
}

View file

@ -1,18 +1,26 @@
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.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import net.minecraft.world.entity.LivingEntity;
import java.util.function.Supplier;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
/**
* Sent server->client to synchronize the status of a brainswept mob.
*/
public record MsgBrainsweepAck(int target) {
public record MsgBrainsweepAck(int target) implements IMessage {
public static final ResourceLocation ID = modLoc("sweep");
@Override
public ResourceLocation getFabricId() {
return ID;
}
public static MsgBrainsweepAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@ -20,8 +28,8 @@ public record MsgBrainsweepAck(int target) {
return new MsgBrainsweepAck(target);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@Override
public void serialize(FriendlyByteBuf buf) {
buf.writeInt(target);
}
@ -29,11 +37,18 @@ public record MsgBrainsweepAck(int target) {
return new MsgBrainsweepAck(target.getId());
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::brainsweep);
public static void handle(MsgBrainsweepAck msg) {
Minecraft.getInstance().execute(new Runnable() {
@Override
public void run() {
var level = Minecraft.getInstance().level;
if (level != null) {
Entity entity = level.getEntity(msg.target());
if (entity instanceof LivingEntity living) {
Brainsweeping.brainsweep(living);
}
}
}
});
ctx.get().setPacketHandled(true);
}
}

View file

@ -2,19 +2,28 @@ package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.api.spell.ParticleSpray;
import at.petrak.hexcasting.common.particles.ConjureParticleOptions;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
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.function.Supplier;
import java.util.Random;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
/**
* Sent server->client to spray particles everywhere.
*/
public record MsgCastParticleAck(ParticleSpray spray, FrozenColorizer colorizer) {
public record MsgCastParticleAck(ParticleSpray spray, FrozenColorizer colorizer) implements IMessage {
public static final ResourceLocation ID = modLoc("cPrtl");
@Override
public ResourceLocation getFabricId() {
return ID;
}
public static MsgCastParticleAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@ -34,8 +43,8 @@ public record MsgCastParticleAck(ParticleSpray spray, FrozenColorizer colorizer)
colorizer);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@Override
public void serialize(FriendlyByteBuf buf) {
buf.writeDouble(this.spray.getPos().x);
buf.writeDouble(this.spray.getPos().y);
buf.writeDouble(this.spray.getPos().z);
@ -48,14 +57,51 @@ public record MsgCastParticleAck(ParticleSpray spray, FrozenColorizer colorizer)
buf.writeNbt(this.colorizer.serialize());
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::particleSpray);
});
ctx.get().setPacketHandled(true);
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);
}
public static void handle(MsgCastParticleAck msg) {
Minecraft.getInstance().execute(new Runnable() {
@Override
public void run() {
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,18 +1,25 @@
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;
import net.minecraftforge.network.NetworkEvent;
import net.minecraft.resources.ResourceLocation;
import java.util.function.Supplier;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
/**
* Sent server->client to synchronize the status of the sentinel.
*/
public record MsgColorizerUpdateAck(FrozenColorizer update) {
public record MsgColorizerUpdateAck(FrozenColorizer update) implements IMessage {
public static final ResourceLocation ID = modLoc("color");
@Override
public ResourceLocation getFabricId() {
return ID;
}
public static MsgColorizerUpdateAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@ -21,16 +28,20 @@ public record MsgColorizerUpdateAck(FrozenColorizer update) {
return new MsgColorizerUpdateAck(colorizer);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@Override
public void serialize(FriendlyByteBuf buf) {
buf.writeNbt(this.update.serialize());
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::updateColorizer);
public static void handle(MsgColorizerUpdateAck self) {
Minecraft.getInstance().execute(new Runnable() {
@Override
public void run() {
var player = Minecraft.getInstance().player;
if (player != null) {
HexPlayerDataHelper.setColorizer(player, self.update());
}
}
});
ctx.get().setPacketHandled(true);
}
}

View file

@ -1,20 +1,28 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.api.spell.casting.ControllerInfo;
import at.petrak.hexcasting.client.gui.GuiSpellcasting;
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;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import net.minecraft.resources.ResourceLocation;
import java.util.ArrayList;
import java.util.function.Supplier;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
/**
* Sent server->client when the player finishes casting a spell.
*/
public record MsgNewSpellPatternAck(ControllerInfo info) {
public record MsgNewSpellPatternAck(ControllerInfo info) implements IMessage {
public static final ResourceLocation ID = modLoc("patSC");
@Override
public ResourceLocation getFabricId() {
return ID;
}
public static MsgNewSpellPatternAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@ -34,9 +42,8 @@ public record MsgNewSpellPatternAck(ControllerInfo info) {
);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@Override
public void serialize(FriendlyByteBuf buf) {
buf.writeBoolean(this.info.getWasSpellCast());
buf.writeBoolean(this.info.getHasCastingSound());
buf.writeBoolean(this.info.isStackClear());
@ -47,12 +54,24 @@ public record MsgNewSpellPatternAck(ControllerInfo info) {
}
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::newPattern);
public static void handle(MsgNewSpellPatternAck self) {
Minecraft.getInstance().execute(new Runnable() {
@Override
public void run() {
var mc = Minecraft.getInstance();
if (self.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 (self.info().isStackClear()) {
mc.setScreen(null);
} else {
spellGui.recvServerUpdate(self.info());
}
}
}
});
ctx.get().setPacketHandled(true);
}
}

View file

@ -1,30 +1,41 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.api.mod.HexItemTags;
import at.petrak.hexcasting.api.player.HexPlayerDataHelper;
import at.petrak.hexcasting.api.spell.casting.ControllerInfo;
import at.petrak.hexcasting.api.spell.casting.ResolvedPattern;
import at.petrak.hexcasting.api.spell.casting.ResolvedPatternValidity;
import at.petrak.hexcasting.api.spell.math.HexCoord;
import at.petrak.hexcasting.api.spell.math.HexPattern;
import at.petrak.hexcasting.api.mod.HexItemTags;
import at.petrak.hexcasting.common.lib.HexSounds;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraftforge.network.NetworkEvent;
import net.minecraftforge.network.PacketDistributor;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
/**
* Sent client->server when the player finishes drawing a pattern.
* Server will send back a MsgNewSpellPatternAck packet
*/
public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern, List<ResolvedPattern> resolvedPatterns) {
public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern,
List<ResolvedPattern> resolvedPatterns)
implements IMessage {
public static final ResourceLocation ID = modLoc("patCS");
@Override
public ResourceLocation getFabricId() {
return ID;
}
public static MsgNewSpellPatternSyn deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
var hand = buf.readEnum(InteractionHand.class);
@ -38,8 +49,8 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
return new MsgNewSpellPatternSyn(hand, pattern, resolvedPatterns);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@Override
public void serialize(FriendlyByteBuf buf) {
buf.writeEnum(handUsed);
buf.writeNbt(this.pattern.serializeToNBT());
buf.writeInt(this.resolvedPatterns.size());
@ -48,58 +59,56 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
}
}
public void handle(Supplier<NetworkEvent.Context> networkCtx) {
networkCtx.get().enqueueWork(() -> {
ServerPlayer sender = networkCtx.get().getSender();
if (sender != null) {
var held = sender.getItemInHand(this.handUsed);
if (held.is(HexItemTags.WANDS)) {
boolean autoFail = false;
public void handle(MinecraftServer server, ServerPlayer sender) {
var held = sender.getItemInHand(this.handUsed);
if (held.is(HexItemTags.WANDS)) {
boolean autoFail = false;
if (!resolvedPatterns.isEmpty()) {
var allPoints = new ArrayList<HexCoord>();
for (int i = 0; i < resolvedPatterns.size() - 1; i++) {
ResolvedPattern pat = resolvedPatterns.get(i);
allPoints.addAll(pat.getPattern().positions(pat.getOrigin()));
}
var currentResolvedPattern = resolvedPatterns.get(resolvedPatterns.size() - 1);
var currentSpellPoints = currentResolvedPattern.getPattern().positions(currentResolvedPattern.getOrigin());
if (currentSpellPoints.stream().anyMatch(allPoints::contains))
autoFail = true;
}
var harness = HexPlayerDataHelper.getHarness(sender, this.handUsed);
ControllerInfo clientInfo;
if (autoFail) {
clientInfo = new ControllerInfo(false, false, harness.getStack().isEmpty(), true, harness.generateDescs());
} else {
clientInfo = harness.executeNewPattern(this.pattern, sender.getLevel());
if (clientInfo.getWasSpellCast() && clientInfo.getHasCastingSound()) {
sender.level.playSound(null, sender.getX(), sender.getY(), sender.getZ(),
HexSounds.ACTUALLY_CAST.get(), SoundSource.PLAYERS, 1f,
1f + ((float) Math.random() - 0.5f) * 0.2f);
}
}
if (clientInfo.isStackClear()) {
HexPlayerDataHelper.setHarness(sender, null);
HexPlayerDataHelper.setPatterns(sender, List.of());
} else {
HexPlayerDataHelper.setHarness(sender, harness);
if (!resolvedPatterns.isEmpty())
resolvedPatterns.get(resolvedPatterns.size() - 1).setValid(clientInfo.getWasPrevPatternInvalid() ?
ResolvedPatternValidity.ERROR : ResolvedPatternValidity.OK);
HexPlayerDataHelper.setPatterns(sender, resolvedPatterns);
}
HexMessages.getNetwork()
.send(PacketDistributor.PLAYER.with(() -> sender), new MsgNewSpellPatternAck(clientInfo));
if (!resolvedPatterns.isEmpty()) {
var allPoints = new ArrayList<HexCoord>();
for (int i = 0; i < resolvedPatterns.size() - 1; i++) {
ResolvedPattern pat = resolvedPatterns.get(i);
allPoints.addAll(pat.getPattern().positions(pat.getOrigin()));
}
var currentResolvedPattern = resolvedPatterns.get(resolvedPatterns.size() - 1);
var currentSpellPoints = currentResolvedPattern.getPattern()
.positions(currentResolvedPattern.getOrigin());
if (currentSpellPoints.stream().anyMatch(allPoints::contains)) {
autoFail = true;
}
}
});
networkCtx.get().setPacketHandled(true);
var harness = HexPlayerDataHelper.getHarness(sender, this.handUsed);
ControllerInfo clientInfo;
if (autoFail) {
clientInfo = new ControllerInfo(false, false, harness.getStack().isEmpty(), true,
harness.generateDescs());
} else {
clientInfo = harness.executeNewPattern(this.pattern, sender.getLevel());
if (clientInfo.getWasSpellCast() && clientInfo.getHasCastingSound()) {
sender.level.playSound(null, sender.getX(), sender.getY(), sender.getZ(),
HexSounds.ACTUALLY_CAST.get(), SoundSource.PLAYERS, 1f,
1f + ((float) Math.random() - 0.5f) * 0.2f);
}
}
if (clientInfo.isStackClear()) {
HexPlayerDataHelper.setHarness(sender, null);
HexPlayerDataHelper.setPatterns(sender, List.of());
} else {
HexPlayerDataHelper.setHarness(sender, harness);
if (!resolvedPatterns.isEmpty()) {
resolvedPatterns.get(resolvedPatterns.size() - 1).setValid(clientInfo.getWasPrevPatternInvalid() ?
ResolvedPatternValidity.ERROR : ResolvedPatternValidity.OK);
}
HexPlayerDataHelper.setPatterns(sender, resolvedPatterns);
}
HexMessages.getNetwork()
.send(PacketDistributor.PLAYER.with(() -> sender), new MsgNewSpellPatternAck(clientInfo));
}
}
}

View file

@ -1,22 +1,30 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.api.spell.casting.ResolvedPattern;
import at.petrak.hexcasting.client.gui.GuiSpellcasting;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionHand;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
/**
* Sent server->client when the player opens the spell gui to request the server provide the current stack.
*/
public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> patterns, List<Component> components) {
public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> patterns, List<Component> components)
implements IMessage {
public static final ResourceLocation ID = modLoc("cGui");
@Override
public ResourceLocation getFabricId() {
return ID;
}
public static MsgOpenSpellGuiAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@ -38,9 +46,7 @@ public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> pat
return new MsgOpenSpellGuiAck(hand, patterns, desc);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
public void serialize(FriendlyByteBuf buf) {
buf.writeEnum(this.hand);
buf.writeInt(this.patterns.size());
@ -54,12 +60,13 @@ public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> pat
}
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::openSpellGui);
public static void handle(MsgOpenSpellGuiAck msg) {
Minecraft.getInstance().execute(new Runnable() {
@Override
public void run() {
var mc = Minecraft.getInstance();
mc.setScreen(new GuiSpellcasting(msg.hand(), msg.patterns(), msg.components()));
}
});
ctx.get().setPacketHandled(true);
}
}

View file

@ -1,21 +1,28 @@
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;
import net.minecraft.resources.ResourceLocation;
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.function.Supplier;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
/**
* Sent server->client to synchronize the status of the sentinel.
*/
public record MsgSentinelStatusUpdateAck(Sentinel update) {
public record MsgSentinelStatusUpdateAck(Sentinel update) implements IMessage {
public static final ResourceLocation ID = modLoc("sntnl");
@Override
public ResourceLocation getFabricId() {
return ID;
}
public static MsgSentinelStatusUpdateAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@ -28,8 +35,7 @@ public record MsgSentinelStatusUpdateAck(Sentinel update) {
return new MsgSentinelStatusUpdateAck(sentinel);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
public void serialize(FriendlyByteBuf buf) {
buf.writeBoolean(update.hasSentinel());
buf.writeBoolean(update.extendsRange());
buf.writeDouble(update.position().x);
@ -38,11 +44,15 @@ public record MsgSentinelStatusUpdateAck(Sentinel update) {
buf.writeResourceLocation(update.dimension().location());
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
ClientPacketHandler handler = new ClientPacketHandler(this);
DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> handler::updateSentinel);
public static void handle(MsgSentinelStatusUpdateAck self) {
Minecraft.getInstance().execute(new Runnable() {
@Override
public void run() {
var player = Minecraft.getInstance().player;
if (player != null) {
HexPlayerDataHelper.setSentinel(player, self.update());
}
}
});
ctx.get().setPacketHandled(true);
}
}

View file

@ -11,19 +11,27 @@ import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
/**
* Sent client->server when the client shift+scrolls with a shift-scrollable item
* or scrolls in the spellcasting UI.
*/
public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta, boolean isCtrl) {
public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta, boolean isCtrl) implements IMessage {
public static final ResourceLocation ID = modLoc("scroll");
@Override
public ResourceLocation getFabricId() {
return ID;
}
public static MsgShiftScrollSyn deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
var hand = buf.readEnum(InteractionHand.class);
@ -32,27 +40,20 @@ public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta, boolea
return new MsgShiftScrollSyn(hand, scrollDelta, isCtrl);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
public void serialize(FriendlyByteBuf buf) {
buf.writeEnum(this.hand);
buf.writeDouble(this.scrollDelta);
buf.writeBoolean(this.isCtrl);
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
ServerPlayer sender = ctx.get().getSender();
if (sender != null) {
var stack = sender.getItemInHand(hand);
public void handle(MinecraftServer server, ServerPlayer sender) {
var stack = sender.getItemInHand(hand);
if (stack.getItem() == HexItems.SPELLBOOK.get()) {
spellbook(sender, stack);
} else if (stack.getItem() == HexItems.ABACUS.get()) {
abacus(sender, stack);
}
}
});
ctx.get().setPacketHandled(true);
if (stack.getItem() == HexItems.SPELLBOOK.get()) {
spellbook(sender, stack);
} else if (stack.getItem() == HexItems.ABACUS.get()) {
abacus(sender, stack);
}
}
private void spellbook(ServerPlayer sender, ItemStack stack) {
@ -66,28 +67,32 @@ public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta, boolea
MutableComponent component;
if (hand == InteractionHand.OFF_HAND && stack.hasCustomHoverName()) {
if (sealed)
if (sealed) {
component = new TranslatableComponent("hexcasting.tooltip.spellbook.page_with_name.sealed",
new TextComponent(String.valueOf(newIdx)).withStyle(ChatFormatting.WHITE),
new TextComponent(String.valueOf(len)).withStyle(ChatFormatting.WHITE),
new TextComponent("").withStyle(stack.getRarity().color, ChatFormatting.ITALIC).append(stack.getHoverName()),
new TextComponent(String.valueOf(newIdx)).withStyle(ChatFormatting.WHITE),
new TextComponent(String.valueOf(len)).withStyle(ChatFormatting.WHITE),
new TextComponent("").withStyle(stack.getRarity().color, ChatFormatting.ITALIC)
.append(stack.getHoverName()),
new TranslatableComponent("hexcasting.tooltip.spellbook.sealed").withStyle(ChatFormatting.GOLD));
else
} else {
component = new TranslatableComponent("hexcasting.tooltip.spellbook.page_with_name",
new TextComponent(String.valueOf(newIdx)).withStyle(ChatFormatting.WHITE),
new TextComponent(String.valueOf(len)).withStyle(ChatFormatting.WHITE),
new TextComponent("").withStyle(stack.getRarity().color, ChatFormatting.ITALIC).append(stack.getHoverName()));
new TextComponent("").withStyle(stack.getRarity().color, ChatFormatting.ITALIC)
.append(stack.getHoverName()));
}
} else {
if (sealed)
if (sealed) {
component = new TranslatableComponent("hexcasting.tooltip.spellbook.page.sealed",
new TextComponent(String.valueOf(newIdx)).withStyle(ChatFormatting.WHITE),
new TextComponent(String.valueOf(len)).withStyle(ChatFormatting.WHITE),
new TranslatableComponent("hexcasting.tooltip.spellbook.sealed").withStyle(ChatFormatting.GOLD));
else
} else {
component = new TranslatableComponent("hexcasting.tooltip.spellbook.page",
new TextComponent(String.valueOf(newIdx)).withStyle(ChatFormatting.WHITE),
new TextComponent(String.valueOf(len)).withStyle(ChatFormatting.WHITE));
}
}
sender.displayClientMessage(component.withStyle(ChatFormatting.GRAY), true);
@ -122,7 +127,8 @@ public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta, boolea
var datumTag = HexItems.ABACUS.get().readDatumTag(stack);
if (datumTag != null) {
var popup = SpellDatum.DisplayFromTag(datumTag);
sender.displayClientMessage(new TranslatableComponent("hexcasting.tooltip.abacus", popup).withStyle(ChatFormatting.GREEN), true);
sender.displayClientMessage(
new TranslatableComponent("hexcasting.tooltip.abacus", popup).withStyle(ChatFormatting.GREEN), true);
}
}
}

View file

@ -1,18 +1,24 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.api.client.ScryingLensOverlayRegistry;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
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 net.minecraft.resources.ResourceLocation;
import java.util.function.Supplier;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
/**
* 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 record MsgUpdateComparatorVisualsAck(BlockPos pos, int value) implements IMessage {
public static final ResourceLocation ID = modLoc("cmp");
@Override
public ResourceLocation getFabricId() {
return ID;
}
public static MsgUpdateComparatorVisualsAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
@ -23,21 +29,20 @@ public record MsgUpdateComparatorVisualsAck(BlockPos pos, int value) {
return new MsgUpdateComparatorVisualsAck(pos, value);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
public void serialize(FriendlyByteBuf buf) {
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);
public static void handle(MsgUpdateComparatorVisualsAck msg) {
Minecraft.getInstance().execute(new Runnable() {
@Override
public void run() {
ScryingLensOverlayRegistry.receiveComparatorValue(msg.pos(), msg.value());
}
});
ctx.get().setPacketHandled(true);
}
}

View file

@ -2,8 +2,11 @@ package at.petrak.hexcasting.xplat;
import at.petrak.hexcasting.api.HexAPI;
import at.petrak.hexcasting.common.command.PatternResLocArgument;
import at.petrak.hexcasting.common.network.IMessage;
import net.minecraft.commands.synchronization.ArgumentTypes;
import net.minecraft.commands.synchronization.EmptyArgumentSerializer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.LivingEntity;
import java.util.ServiceLoader;
import java.util.stream.Collectors;
@ -13,6 +16,14 @@ public interface IXplatAbstractions {
boolean isPhysicalClient();
void brainsweep(LivingEntity e);
boolean isBrainswept(LivingEntity e);
void sendPacketToPlayer(ServerPlayer target, IMessage packet);
void sendPacketToServer(IMessage packet);
default void init() {
HexAPI.LOGGER.info("Hello Hexcasting! This is {}!", this.platform());

View file

@ -0,0 +1,4 @@
package at.petrak.hexcasting.fabric.network;
public class FabricPacketHandler {
}

View file

@ -0,0 +1,10 @@
package at.petrak.hexcasting.fabric.xplat;
import at.petrak.hexcasting.common.misc.Brainsweeping;
import net.fabricmc.fabric.api.event.player.UseEntityCallback;
public class FabricListenersSetup {
public static void init() {
UseEntityCallback.EVENT.register(Brainsweeping::tradeWithVillager);
}
}

View file

@ -4,33 +4,12 @@ import at.petrak.hexcasting.api.HexAPI
import at.petrak.hexcasting.api.PatternRegistry
import at.petrak.hexcasting.api.advancements.HexAdvancementTriggers
import at.petrak.hexcasting.api.mod.HexConfig
import at.petrak.hexcasting.api.mod.HexStatistics
import at.petrak.hexcasting.api.player.HexPlayerDataHelper
import at.petrak.hexcasting.client.*
import at.petrak.hexcasting.common.blocks.HexBlockEntities
import at.petrak.hexcasting.common.blocks.HexBlocks
import at.petrak.hexcasting.common.casting.RegisterPatterns
import at.petrak.hexcasting.common.casting.operators.spells.great.OpFlight
import at.petrak.hexcasting.common.command.HexCommands
import at.petrak.hexcasting.common.entities.HexEntities
import at.petrak.hexcasting.common.items.HexItems
import at.petrak.hexcasting.common.lib.HexCapabilityHandler
import at.petrak.hexcasting.common.lib.HexSounds
import at.petrak.hexcasting.common.misc.Brainsweeping
import at.petrak.hexcasting.common.network.HexMessages
import at.petrak.hexcasting.common.particles.HexParticles
import at.petrak.hexcasting.common.recipe.HexComposting
import at.petrak.hexcasting.common.recipe.HexCustomRecipes
import at.petrak.hexcasting.common.recipe.HexRecipeSerializers
import at.petrak.hexcasting.datagen.HexDataGenerators
import at.petrak.hexcasting.datagen.lootmods.HexLootModifiers
import at.petrak.hexcasting.forge.ForgeHexConfig
import at.petrak.hexcasting.server.TickScheduler
import at.petrak.hexcasting.forge.network.ForgePacketHandler
import at.petrak.hexcasting.forge.xplat.ForgeListenersSetup
import at.petrak.hexcasting.xplat.IXplatAbstractions
import net.minecraftforge.api.distmarker.Dist
import net.minecraftforge.common.ForgeConfigSpec
import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.fml.DistExecutor
import net.minecraftforge.fml.ModLoadingContext
import net.minecraftforge.fml.common.Mod
import net.minecraftforge.fml.config.ModConfig
@ -56,6 +35,15 @@ object HexMod {
HexConfig.setCommon(config.left)
HexConfig.setClient(clientConfig.left)
HexConfig.setServer(serverConfig.left)
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, config.right)
ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, serverConfig.right)
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, clientConfig.right)
ForgeListenersSetup.init()
ForgePacketHandler.init()
/*
// mod lifecycle
val modBus = thedarkcolour.kotlinforforge.forge.MOD_BUS
@ -98,12 +86,7 @@ object HexMod {
}
}
// and then things that don't require busses
HexMessages.register()
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, config.right)
ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, serverConfig.right)
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, clientConfig.right)
*/
}
@SubscribeEvent

View file

@ -0,0 +1,76 @@
package at.petrak.hexcasting.forge.network;
import at.petrak.hexcasting.HexMod;
import at.petrak.hexcasting.common.network.*;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.NetworkEvent;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.simple.SimpleChannel;
import org.apache.logging.log4j.util.TriConsumer;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class ForgePacketHandler {
private static final String PROTOCOL_VERSION = "1";
private static final SimpleChannel NETWORK = NetworkRegistry.newSimpleChannel(
new ResourceLocation(HexMod.MOD_ID, "main"),
() -> PROTOCOL_VERSION,
PROTOCOL_VERSION::equals,
PROTOCOL_VERSION::equals
);
public static SimpleChannel getNetwork() {
return NETWORK;
}
public static void init() {
int messageIdx = 0;
// Client -> server
NETWORK.registerMessage(messageIdx++, MsgNewSpellPatternSyn.class, MsgNewSpellPatternSyn::serialize,
MsgNewSpellPatternSyn::deserialize, makeServerBoundHandler(MsgNewSpellPatternSyn::handle));
NETWORK.registerMessage(messageIdx++, MsgShiftScrollSyn.class, MsgShiftScrollSyn::serialize,
MsgShiftScrollSyn::deserialize, makeServerBoundHandler(MsgShiftScrollSyn::handle));
// Server -> client
NETWORK.registerMessage(messageIdx++, MsgNewSpellPatternAck.class, MsgNewSpellPatternAck::serialize,
MsgNewSpellPatternAck::deserialize, makeClientBoundHandler(MsgNewSpellPatternAck::handle));
NETWORK.registerMessage(messageIdx++, MsgBlinkAck.class, MsgBlinkAck::serialize,
MsgBlinkAck::deserialize, makeClientBoundHandler(MsgBlinkAck::handle));
NETWORK.registerMessage(messageIdx++, MsgSentinelStatusUpdateAck.class, MsgSentinelStatusUpdateAck::serialize,
MsgSentinelStatusUpdateAck::deserialize, makeClientBoundHandler(MsgSentinelStatusUpdateAck::handle));
NETWORK.registerMessage(messageIdx++, MsgColorizerUpdateAck.class, MsgColorizerUpdateAck::serialize,
MsgColorizerUpdateAck::deserialize, makeClientBoundHandler(MsgColorizerUpdateAck::handle));
NETWORK.registerMessage(messageIdx++, MsgCastParticleAck.class, MsgCastParticleAck::serialize,
MsgCastParticleAck::deserialize, makeClientBoundHandler(MsgCastParticleAck::handle));
NETWORK.registerMessage(messageIdx++, MsgOpenSpellGuiAck.class, MsgOpenSpellGuiAck::serialize,
MsgOpenSpellGuiAck::deserialize, makeClientBoundHandler(MsgOpenSpellGuiAck::handle));
NETWORK.registerMessage(messageIdx++, MsgBeepAck.class, MsgBeepAck::serialize,
MsgBeepAck::deserialize, makeClientBoundHandler(MsgBeepAck::handle));
NETWORK.registerMessage(messageIdx++, MsgBrainsweepAck.class, MsgBrainsweepAck::serialize,
MsgBrainsweepAck::deserialize, makeClientBoundHandler(MsgBrainsweepAck::handle));
NETWORK.registerMessage(messageIdx++, MsgUpdateComparatorVisualsAck.class,
MsgUpdateComparatorVisualsAck::serialize,
MsgUpdateComparatorVisualsAck::deserialize,
makeClientBoundHandler(MsgUpdateComparatorVisualsAck::handle));
}
private static <T> BiConsumer<T, Supplier<NetworkEvent.Context>> makeServerBoundHandler(
TriConsumer<T, MinecraftServer, ServerPlayer> handler) {
return (m, ctx) -> {
handler.accept(m, ctx.get().getSender().getServer(), ctx.get().getSender());
ctx.get().setPacketHandled(true);
};
}
private static <T> BiConsumer<T, Supplier<NetworkEvent.Context>> makeClientBoundHandler(Consumer<T> consumer) {
return (m, ctx) -> {
consumer.accept(m);
ctx.get().setPacketHandled(true);
};
}
}

View file

@ -0,0 +1,20 @@
package at.petrak.hexcasting.forge.xplat;
import at.petrak.hexcasting.common.misc.Brainsweeping;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
public class ForgeListenersSetup {
public static void init() {
var evBus = MinecraftForge.EVENT_BUS;
evBus.addListener((PlayerInteractEvent.EntityInteract evt) -> {
var res = Brainsweeping.tradeWithVillager(evt.getPlayer(), evt.getWorld(), evt.getHand(), evt.getTarget(),
null);
if (res.consumesAction()) {
evt.setCanceled(true);
evt.setCancellationResult(res);
}
});
}
}

View file

@ -1,9 +1,22 @@
package at.petrak.hexcasting.forge.xplat;
import at.petrak.hexcasting.common.lib.HexStringKeys;
import at.petrak.hexcasting.common.network.IMessage;
import at.petrak.hexcasting.common.network.MsgBrainsweepAck;
import at.petrak.hexcasting.forge.network.ForgePacketHandler;
import at.petrak.hexcasting.mixin.AccessorLivingEntity;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
import at.petrak.hexcasting.xplat.Platform;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.Brain;
import net.minecraft.world.entity.npc.Villager;
import net.minecraft.world.entity.npc.VillagerDataHolder;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.network.PacketDistributor;
public class ForgeXplatImpl implements IXplatAbstractions {
@Override
@ -15,4 +28,43 @@ public class ForgeXplatImpl implements IXplatAbstractions {
public boolean isPhysicalClient() {
return FMLLoader.getDist() == Dist.CLIENT;
}
@Override
public void brainsweep(LivingEntity entity) {
if (entity instanceof VillagerDataHolder) {
entity.getPersistentData().putBoolean(HexStringKeys.BRAINSWEPT, true);
if (entity instanceof Mob mob) {
mob.removeFreeWill();
}
if (entity instanceof Villager villager) {
Brain<Villager> brain = villager.getBrain();
if (entity.level instanceof ServerLevel slevel) {
brain.stopAll(slevel, villager);
}
((AccessorLivingEntity) entity).hex$SetBrain(brain.copyWithoutBehaviors());
}
if (entity.level instanceof ServerLevel) {
ForgePacketHandler.getNetwork()
.send(PacketDistributor.TRACKING_ENTITY.with(() -> entity), MsgBrainsweepAck.of(entity));
}
}
}
@Override
public boolean isBrainswept(LivingEntity e) {
return e instanceof VillagerDataHolder && e.getPersistentData().getBoolean(HexStringKeys.BRAINSWEPT);
}
@Override
public void sendPacketToPlayer(ServerPlayer target, IMessage packet) {
ForgePacketHandler.getNetwork().send(PacketDistributor.PLAYER.with(() -> target), packet);
}
@Override
public void sendPacketToServer(IMessage packet) {
ForgePacketHandler.getNetwork().sendToServer(packet);
}
}