From 21f8e9e49380e3e898e91aa661a3915755d1eb59 Mon Sep 17 00:00:00 2001 From: fill1890 Date: Sun, 19 Jun 2022 19:31:26 +1000 Subject: [PATCH] spin implementation Initial implementation for /spin --- src/main/java/net/fill1890/fabsit/FabSit.java | 2 + .../fill1890/fabsit/command/SpinCommand.java | 71 +++++++++++++++++++ .../java/net/fill1890/fabsit/entity/Pose.java | 3 +- .../fabsit/entity/PoseManagerEntity.java | 13 ++-- .../fabsit/entity/SpinningEntity.java | 38 ++++++++++ .../fabsit/mixin/LivingEntityAccessor.java | 3 + 6 files changed, 123 insertions(+), 7 deletions(-) create mode 100644 src/main/java/net/fill1890/fabsit/command/SpinCommand.java create mode 100644 src/main/java/net/fill1890/fabsit/entity/SpinningEntity.java diff --git a/src/main/java/net/fill1890/fabsit/FabSit.java b/src/main/java/net/fill1890/fabsit/FabSit.java index 0cad5d4..641eae5 100644 --- a/src/main/java/net/fill1890/fabsit/FabSit.java +++ b/src/main/java/net/fill1890/fabsit/FabSit.java @@ -4,6 +4,7 @@ import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fill1890.fabsit.command.LayCommand; import net.fill1890.fabsit.command.SitCommand; +import net.fill1890.fabsit.command.SpinCommand; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,6 +17,7 @@ public class FabSit implements ModInitializer { public void onInitialize() { CommandRegistrationCallback.EVENT.register(SitCommand::register); CommandRegistrationCallback.EVENT.register(LayCommand::register); + CommandRegistrationCallback.EVENT.register(SpinCommand::register); LOGGER.info("FabSit finished loading"); } diff --git a/src/main/java/net/fill1890/fabsit/command/SpinCommand.java b/src/main/java/net/fill1890/fabsit/command/SpinCommand.java new file mode 100644 index 0000000..948eae6 --- /dev/null +++ b/src/main/java/net/fill1890/fabsit/command/SpinCommand.java @@ -0,0 +1,71 @@ +package net.fill1890.fabsit.command; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import me.lucko.fabric.api.permissions.v0.Permissions; +import net.fill1890.fabsit.entity.Pose; +import net.fill1890.fabsit.entity.PoseManagerEntity; +import net.minecraft.block.BlockState; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import net.minecraft.util.math.BlockPos; + +import static net.minecraft.server.command.CommandManager.literal; + +/** + * /spin command implementation + *
+ * Requires fabsit.commands.spin permission node, granted to all players by default + *
+ * Implementation details taken from GSit + */ +public class SpinCommand { + public static void register(CommandDispatcher dispatcher, CommandRegistryAccess commandRegistryAccess, CommandManager.RegistrationEnvironment registrationEnvironment) { + dispatcher.register(literal("spin") + .requires(Permissions.require("fabsit.commands.spin", true)) + .executes(SpinCommand::run)); + } + + public static int run(CommandContext context) { + final ServerCommandSource source = context.getSource(); + ServerPlayerEntity player; + + try { + player = source.getPlayerOrThrow(); + } catch (CommandSyntaxException e) { + source.sendError(Text.of("You must be a player to run this command!")); + return -1; + } + + // toggle sitting if the player was sat down + if(player.hasVehicle()) { + player.dismountVehicle(); + player.teleport(player.getX(), player.getY() + 0.6, player.getZ()); + return 1; + } + + // get the block below to check it isn't air + BlockState standingBlock = player.getEntityWorld().getBlockState(new BlockPos(player.getPos()).down()); + // check cancel conditions + if( + player.isFallFlying() + || player.isSleeping() + || player.isSwimming() + || player.isSpectator() + || standingBlock.isAir() + ) { return -1; } + + // create a new pose manager for spinning and sit the player down + // (player is then invisible and an npc spins) + PoseManagerEntity chair = new PoseManagerEntity(player.getEntityWorld(), player.getPos(), Pose.SPINNING, player); + player.getEntityWorld().spawnEntity(chair); + player.startRiding(chair, true); + + return 1; + + } +} diff --git a/src/main/java/net/fill1890/fabsit/entity/Pose.java b/src/main/java/net/fill1890/fabsit/entity/Pose.java index 2e92cd0..7d9cdaf 100644 --- a/src/main/java/net/fill1890/fabsit/entity/Pose.java +++ b/src/main/java/net/fill1890/fabsit/entity/Pose.java @@ -2,5 +2,6 @@ public enum Pose { SITTING, - LAYING; + LAYING, + SPINNING; } diff --git a/src/main/java/net/fill1890/fabsit/entity/PoseManagerEntity.java b/src/main/java/net/fill1890/fabsit/entity/PoseManagerEntity.java index a217df5..3974dde 100644 --- a/src/main/java/net/fill1890/fabsit/entity/PoseManagerEntity.java +++ b/src/main/java/net/fill1890/fabsit/entity/PoseManagerEntity.java @@ -44,12 +44,13 @@ public PoseManagerEntity(World world, Vec3d pos, Pose pose, ServerPlayerEntity p this.setYaw(player.getYaw()); // TODO: test this properly // if the pose is more complex than sitting, create a posing npc - if(pose == Pose.LAYING) { + if(pose == Pose.LAYING || pose == Pose.SPINNING) { // copy player game profile with a random uuid GameProfile gameProfile = new GameProfile(UUID.randomUUID(), player.getEntityName()); gameProfile.getProperties().putAll(player.getGameProfile().getProperties()); - this.poser = new LayingEntity(player, gameProfile); + if(pose == Pose.LAYING) this.poser = new LayingEntity(player, gameProfile); + if(pose == Pose.SPINNING) this.poser = new SpinningEntity(player, gameProfile); } this.pose = pose; @@ -60,7 +61,7 @@ protected void addPassenger(Entity passenger) { super.addPassenger(passenger); // if the pose is npc-based, hide the player when initiated - if(this.pose == Pose.LAYING) { + if(this.pose == Pose.LAYING || this.pose == Pose.SPINNING) { passenger.setInvisible(true); // update shoulder entities @@ -80,7 +81,7 @@ protected void removePassenger(Entity passenger) { super.removePassenger(passenger); // if the pose was npc-based, show the player again when exited - if(this.pose == Pose.LAYING) { + if(this.pose == Pose.LAYING || this.pose == Pose.SPINNING) { passenger.setInvisible(false); // replace shoulder entities @@ -92,7 +93,7 @@ protected void removePassenger(Entity passenger) { } public void animate(int id) { - if(this.pose == Pose.LAYING) { + if(this.pose == Pose.LAYING || this.pose == Pose.SPINNING) { poser.animate(id); } } @@ -133,7 +134,7 @@ public void tick() { if(sittingBlock.isAir()) { kill(); } // if pose is npc-based, update players with npc info - if(this.pose == Pose.LAYING) { + if(this.pose == Pose.LAYING || this.pose == Pose.SPINNING) { poser.sendUpdates(); } diff --git a/src/main/java/net/fill1890/fabsit/entity/SpinningEntity.java b/src/main/java/net/fill1890/fabsit/entity/SpinningEntity.java new file mode 100644 index 0000000..70a544d --- /dev/null +++ b/src/main/java/net/fill1890/fabsit/entity/SpinningEntity.java @@ -0,0 +1,38 @@ +package net.fill1890.fabsit.entity; + +import com.mojang.authlib.GameProfile; +import net.minecraft.network.packet.s2c.play.EntityS2CPacket; +import net.minecraft.network.packet.s2c.play.EntityTrackerUpdateS2CPacket; +import net.minecraft.server.network.ServerPlayerEntity; + +import static net.fill1890.fabsit.mixin.LivingEntityAccessor.getLIVING_FLAGS; + +public class SpinningEntity extends PosingEntity { + // pivot the poser to face vertically + EntityS2CPacket pivotPacket; + + public SpinningEntity(ServerPlayerEntity player, GameProfile gameProfile) { + super(player, gameProfile); + + // set spinning state + this.getDataTracker().set(getLIVING_FLAGS(), (byte) 0x04); + // refresh data packet + this.trackerPoserPacket = new EntityTrackerUpdateS2CPacket(this.getId(), this.getDataTracker(), true); + + this.pivotPacket = new EntityS2CPacket.RotateAndMoveRelative(this.getId(), (short) 0, (short) 0, (short) 0, (byte) 0, (byte) (-90.0f * 256.0f / 360.0f), true); + } + + @Override + public void sendUpdates() { + super.sendUpdates(); + + // rotate the poser to be spinning vertically + this.addingPlayers.forEach(p -> p.networkHandler.sendPacket(this.pivotPacket)); + } + + @Override + protected void syncHeadYaw() { + // do nothing; no point since already rotating + // also might mess up angle + } +} diff --git a/src/main/java/net/fill1890/fabsit/mixin/LivingEntityAccessor.java b/src/main/java/net/fill1890/fabsit/mixin/LivingEntityAccessor.java index 32f98c1..1806854 100644 --- a/src/main/java/net/fill1890/fabsit/mixin/LivingEntityAccessor.java +++ b/src/main/java/net/fill1890/fabsit/mixin/LivingEntityAccessor.java @@ -14,4 +14,7 @@ public interface LivingEntityAccessor { static TrackedData> getSLEEPING_POSITION() { throw new AssertionError(); } + + @Accessor("LIVING_FLAGS") + static TrackedData getLIVING_FLAGS() { throw new AssertionError(); } }