Skip to content

Commit

Permalink
some event stuff for action controller
Browse files Browse the repository at this point in the history
  • Loading branch information
derklaro committed Jan 26, 2025
1 parent 63f3147 commit df33d95
Show file tree
Hide file tree
Showing 6 changed files with 393 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* This file is part of npc-lib, licensed under the MIT License (MIT).
*
* Copyright (c) 2022-2025 Julian M., Pasqual K. and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package com.github.juliarn.npclib.fabric.controller;

public class FabricActionController {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* This file is part of npc-lib, licensed under the MIT License (MIT).
*
* Copyright (c) 2022-2025 Julian M., Pasqual K. and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package com.github.juliarn.npclib.fabric.controller;

import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* Internal event for notifying the action controller about player actions.
*/
@ApiStatus.Internal
public interface FabricActionControllerEvents {

Event<ServerPlayerPreLevelChange> PRE_SERVER_PLAYER_LEVEL_CHANGE = EventFactory.createArrayBacked(
ServerPlayerPreLevelChange.class,
callbacks -> (player, oldLevel, newLevel) -> {
for (var callback : callbacks) {
callback.preLevelChange(player, oldLevel, newLevel);
}
}
);

Event<ServerPlayerToggleSneak> SERVER_PLAYER_TOGGLE_SNEAK = EventFactory.createArrayBacked(
ServerPlayerToggleSneak.class,
callbacks -> (player, sneak) -> {
for (var callback : callbacks) {
callback.toggleSneak(player, sneak);
}
}
);

Event<ServerPlayerHandSwing> SERVER_PLAYER_HAND_SWING = EventFactory.createArrayBacked(
ServerPlayerHandSwing.class,
callbacks -> player -> {
for (var callback : callbacks) {
callback.swingHand(player);
}
}
);

Event<ServerPlayerDisconnect> SERVER_PLAYER_DISCONNECT = EventFactory.createArrayBacked(
ServerPlayerDisconnect.class,
callbacks -> player -> {
for (var callback : callbacks) {
callback.disconnect(player);
}
}
);

Event<ServerPlayerMove> SERVER_PLAYER_MOVE = EventFactory.createArrayBacked(
ServerPlayerMove.class,
callbacks -> (player, posTo, rotTo) -> {
for (var callback : callbacks) {
callback.move(player, posTo, rotTo);
}
}
);

/**
* Called before a server player changes the level.
*/
@FunctionalInterface
interface ServerPlayerPreLevelChange {

void preLevelChange(@NotNull ServerPlayer player, @Nullable ServerLevel oldLevel, @NotNull ServerLevel newLevel);
}

/**
* Called when a server player starts or stops sprinting.
*/
@FunctionalInterface
interface ServerPlayerToggleSneak {

void toggleSneak(@NotNull ServerPlayer player, boolean sneaking);
}

/**
* Called when a server player swings his hand (left click).
*/
@FunctionalInterface
interface ServerPlayerHandSwing {

void swingHand(@NotNull ServerPlayer player);
}

/**
* Called when a server player disconnects.
*/
@FunctionalInterface
interface ServerPlayerDisconnect {

void disconnect(@NotNull ServerPlayer player);
}

/**
* Called when a server player moves. Note that the pos and rot is only given if it was changed by the client.
*/
@FunctionalInterface
interface ServerPlayerMove {

void move(@NotNull ServerPlayer player, @Nullable Vec3 posTo, @Nullable Vec2 rotTo);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* This file is part of npc-lib, licensed under the MIT License (MIT).
*
* Copyright (c) 2022-2025 Julian M., Pasqual K. and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package com.github.juliarn.npclib.fabric.mixins;

import com.github.juliarn.npclib.fabric.controller.FabricActionControllerEvents;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.players.PlayerList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(PlayerList.class)
public abstract class PlayerListMixin {

@Inject(method = "remove", at = @At("TAIL"))
public void npc_lib$remove(ServerPlayer player, CallbackInfo ci) {
var invoker = FabricActionControllerEvents.SERVER_PLAYER_DISCONNECT.invoker();
invoker.disconnect(player);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* This file is part of npc-lib, licensed under the MIT License (MIT).
*
* Copyright (c) 2022-2025 Julian M., Pasqual K. and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package com.github.juliarn.npclib.fabric.mixins;

import com.github.juliarn.npclib.fabric.controller.FabricActionControllerEvents;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.network.protocol.game.ServerboundMoveVehiclePacket;
import net.minecraft.network.protocol.game.ServerboundPlayerCommandPacket;
import net.minecraft.network.protocol.game.ServerboundSwingPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Slice;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(ServerGamePacketListenerImpl.class)
public abstract class ServerGamePacketListenerImplMixin {

@Shadow
public abstract ServerPlayer getPlayer();

@Inject(method = "handlePlayerCommand", at = @At("TAIL"))
public void npc_lib$handlePlayerCommand(ServerboundPlayerCommandPacket packet, CallbackInfo ci) {
var player = this.getPlayer();
var command = packet.getAction();
if (command == ServerboundPlayerCommandPacket.Action.PRESS_SHIFT_KEY
|| command == ServerboundPlayerCommandPacket.Action.RELEASE_SHIFT_KEY) {
var invoker = FabricActionControllerEvents.SERVER_PLAYER_TOGGLE_SNEAK.invoker();
var sprintStarted = command == ServerboundPlayerCommandPacket.Action.PRESS_SHIFT_KEY;
invoker.toggleSneak(player, sprintStarted);
}
}

@Inject(method = "handleAnimate", at = @At("TAIL"))
public void npc_lib$handleAnimate(ServerboundSwingPacket packet, CallbackInfo ci) {
var player = this.getPlayer();
var invoker = FabricActionControllerEvents.SERVER_PLAYER_HAND_SWING.invoker();
invoker.swingHand(player);
}

@Inject(
method = "handleMovePlayer",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/network/protocol/game/ServerboundMovePlayerPacket;getYRot(F)F"
),
slice = @Slice(
from = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;updateAwaitingTeleport()Z"),
to = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;isPassenger()Z")
)
)
public void npc_lib$handleMovePlayer(ServerboundMovePlayerPacket packet, CallbackInfo ci) {
// ensure that the packet contains at least position and rotation
var hasPos = packet.hasPosition();
var hasRot = packet.hasRotation();
if (!hasPos && !hasRot) {
return;
}

// get the new target position, null in case it's not provided by the packet
var player = this.getPlayer();
var posToX = packet.getX(player.getX());
var posToY = packet.getY(player.getY());
var posToZ = packet.getZ(player.getZ());
var posTo = hasPos ? new Vec3(posToX, posToY, posToZ) : null;

// get the new target rotation, null in case it's not provided by the packet
var rotToX = packet.getXRot(player.getXRot());
var rotToY = packet.getYRot(player.getYRot());
var rotTo = hasRot ? new Vec2(rotToX, rotToY) : null;

// invoke the player move event
var invoker = FabricActionControllerEvents.SERVER_PLAYER_MOVE.invoker();
invoker.move(player, posTo, rotTo);
}

@Inject(
method = "handleMoveVehicle",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/Entity;getControllingPassenger()Lnet/minecraft/world/entity/LivingEntity;"
)
)
public void npc_lib$handleMoveVehicle(ServerboundMoveVehiclePacket packet, CallbackInfo ci) {
var player = this.getPlayer();
var rootVehicle = player.getRootVehicle();

// get the target position, set to null in case it didn't change
var posFrom = rootVehicle.position();
var posTo = packet.position();
if (posTo.equals(posFrom)) {
posTo = null;
}

// get the target rotation, set to null in case it didn't change
var rotFrom = rootVehicle.getRotationVector();
var rotTo = new Vec2(packet.xRot(), packet.yRot());
if (rotTo.equals(rotFrom)) {
rotTo = null;
}

// fire player move event in case position or rotation changed
if (posTo != null || rotTo != null) {
var invoker = FabricActionControllerEvents.SERVER_PLAYER_MOVE.invoker();
invoker.move(player, posTo, rotTo);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* This file is part of npc-lib, licensed under the MIT License (MIT).
*
* Copyright (c) 2022-2025 Julian M., Pasqual K. and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package com.github.juliarn.npclib.fabric.mixins;

import com.github.juliarn.npclib.fabric.controller.FabricActionControllerEvents;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(ServerPlayer.class)
public abstract class ServerPlayerMixin {

@Shadow
public abstract ServerLevel serverLevel();

@Inject(method = "setServerLevel", at = @At("HEAD"))
public void npc_lib$setServerLevel(ServerLevel level, CallbackInfo ci) {
var currentLevel = this.serverLevel();
var player = (ServerPlayer) (Object) this;
var eventInvoker = FabricActionControllerEvents.PRE_SERVER_PLAYER_LEVEL_CHANGE.invoker();
eventInvoker.preLevelChange(player, currentLevel, level);
}
}
Loading

0 comments on commit df33d95

Please sign in to comment.