Skip to content

Commit

Permalink
test for server only support
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexandra-Myers committed Aug 29, 2023
1 parent f578083 commit 8a822ce
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 3 deletions.
4 changes: 4 additions & 0 deletions src/main/java/net/atlas/combatify/Combatify.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
import net.minecraft.world.level.block.DispenserBlock;
import org.jetbrains.annotations.NotNull;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import static net.minecraft.world.item.Items.NETHERITE_SWORD;
Expand All @@ -36,6 +38,8 @@ public class Combatify implements ModInitializer {
public static ResourceLocation modDetectionNetworkChannel = id("networking");
public NetworkingHandler networkingHandler;
public static final List<UUID> unmoddedPlayers = new ArrayListExtensions<>();
public static Map<UUID, Boolean> isPlayerAttacking = new HashMap<>();
public static Map<UUID, Boolean> finalizingAttack = new HashMap<>();

@Override
public void onInitialize() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package net.atlas.combatify.extensions;

import net.minecraft.server.level.ServerPlayer;

public interface IUpdateAttributesPacket {
void changeAttributes(ServerPlayer reciever);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package net.atlas.combatify.mixin;

import net.atlas.combatify.Combatify;
import net.atlas.combatify.extensions.IUpdateAttributesPacket;
import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.ai.attributes.Attributes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;

import java.util.List;

@Mixin(ClientboundUpdateAttributesPacket.class)
public class ClientboundUpdateAttributesPacketMixin implements IUpdateAttributesPacket {
@Shadow
@Final
private List<ClientboundUpdateAttributesPacket.AttributeSnapshot> attributes;

@Override
public void changeAttributes(ServerPlayer reciever) {
attributes.forEach(attributeSnapshot -> {
if(attributeSnapshot.getAttribute() == Attributes.ATTACK_SPEED && Combatify.unmoddedPlayers.contains(reciever.getUUID())) {
int index = attributes.indexOf(attributeSnapshot);
attributes.remove(attributeSnapshot);
attributes.add(index, new ClientboundUpdateAttributesPacket.AttributeSnapshot(attributeSnapshot.getAttribute(), attributeSnapshot.getBase() - 1.5, attributeSnapshot.getModifiers()));
}
});
}
}
9 changes: 9 additions & 0 deletions src/main/java/net/atlas/combatify/mixin/PlayerMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
import net.atlas.combatify.util.CustomEnchantmentHelper;
import net.atlas.combatify.util.UtilClass;
import net.atlas.combatify.extensions.*;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
Expand All @@ -23,6 +25,7 @@
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.*;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
Expand Down Expand Up @@ -93,6 +96,12 @@ private static double changeAttack(double constant) {
private static AttributeSupplier.Builder createAttributes(AttributeSupplier.Builder original) {
return original.add(NewAttributes.ATTACK_REACH);
}
@Inject(method = "drop(Lnet/minecraft/world/item/ItemStack;ZZ)Lnet/minecraft/world/entity/item/ItemEntity;", at = @At(value = "HEAD"))
public void addServerOnlyCheck(ItemStack itemStack, boolean bl, boolean bl2, CallbackInfoReturnable<ItemEntity> cir) {
if(Combatify.unmoddedPlayers.contains(player.getUUID())) {
Combatify.isPlayerAttacking.put(player.getUUID(), false);
}
}
@Redirect(method = "tick", at = @At(value = "FIELD",target = "Lnet/minecraft/world/entity/player/Player;attackStrengthTicker:I",opcode = Opcodes.PUTFIELD))
public void redirectAttackStrengthTicker(Player instance, int value) {
if(player.getUseItem().getItem() instanceof SwordItem swordItem) {
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/net/atlas/combatify/mixin/ServerEntityMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package net.atlas.combatify.mixin;

import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
import net.atlas.combatify.extensions.IUpdateAttributesPacket;
import net.minecraft.network.protocol.game.ClientboundBundlePacket;
import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket;
import net.minecraft.server.level.ServerEntity;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(ServerEntity.class)
public class ServerEntityMixin {
@Shadow
@Final
private Entity entity;

@ModifyExpressionValue(method = "addPairing", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/game/ClientboundBundlePacket;<init>(Ljava/lang/Iterable;)V"))
public ClientboundBundlePacket modifyAttributes(ClientboundBundlePacket original, @Local(ordinal = 0) ServerPlayer serverPlayer) {
original.subPackets().forEach(clientGamePacketListenerPacket -> {
if(clientGamePacketListenerPacket instanceof ClientboundUpdateAttributesPacket clientboundUpdateAttributesPacket) {
((IUpdateAttributesPacket) clientboundUpdateAttributesPacket).changeAttributes(serverPlayer);
}
});
return original;
}
@ModifyExpressionValue(method = "sendDirtyEntityData", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket;<init>(ILjava/util/Collection;)V"))
public ClientboundUpdateAttributesPacket modifyAttributes1(ClientboundUpdateAttributesPacket original) {
if(entity instanceof ServerPlayer serverPlayer)
((IUpdateAttributesPacket) original).changeAttributes(serverPlayer);
return original;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public double redirectCheck(AABB instance, Vec3 old) {
@ModifyExpressionValue(method = "handleInteract",
at = @At(value = "FIELD", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;MAX_INTERACTION_DISTANCE:D",opcode = Opcodes.GETSTATIC))
public double getActualAttackRange(double original, @Local(ordinal = 0) Entity entity) {
double d = ((PlayerExtensions)player).getAttackRange(1.0F) + 0.25;
double d = ((PlayerExtensions)player).getAttackRange(1.0F) + 1;
d *= d;
if(!player.hasLineOfSight(entity)) {
d = 6.25;
Expand Down
145 changes: 145 additions & 0 deletions src/main/java/net/atlas/combatify/mixin/ServerPlayerMixin.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,167 @@
package net.atlas.combatify.mixin;

import net.atlas.combatify.Combatify;
import net.atlas.combatify.extensions.AABBExtensions;
import net.atlas.combatify.extensions.PlayerExtensions;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.PacketUtils;
import net.minecraft.network.protocol.game.ServerboundInteractPacket;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.*;
import net.minecraft.world.entity.decoration.ItemFrame;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.entity.projectile.ProjectileUtil;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.*;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
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 extends PlayerMixin {

@Shadow
public abstract Entity getCamera();

@Unique
public final ServerPlayer player = ServerPlayer.class.cast(this);

public ServerPlayerMixin(EntityType<? extends LivingEntity> entityType, Level level) {
super(entityType, level);
}
@Inject(method = "swing", at = @At(value = "HEAD"), cancellable = true)
public void removeReset(InteractionHand hand, CallbackInfo ci) {
super.swing(hand);
if(Combatify.unmoddedPlayers.contains(getUUID())) {
if (Combatify.isPlayerAttacking.get(getUUID())){
Entity camera = getCamera();
if (camera != null) {
double d = ((PlayerExtensions) player).getAttackRange(0.0F) + 2;
HitResult hitResult = camera.pick(d, 1, false);
Vec3 vec3 = camera.getEyePosition(1);
boolean bl = false;
double e = d;
if (d > ((PlayerExtensions) player).getAttackRange(0.0F)) {
bl = true;
}

e *= e;
if (hitResult != null) {
e = hitResult.getLocation().distanceToSqr(vec3);
}

Vec3 vec32 = camera.getViewVector(1.0F);
Vec3 vec33 = vec3.add(vec32.x * d, vec32.y * d, vec32.z * d);
AABB aABB = camera.getBoundingBox().expandTowards(vec32.scale(d)).inflate(1.0, 1.0, 1.0);
EntityHitResult entityHitResult = ProjectileUtil.getEntityHitResult(camera, vec3, vec33, aABB, (entityx) ->
!entityx.isSpectator() && entityx.isPickable(), e);
if (entityHitResult != null) {
Vec3 vec34 = entityHitResult.getLocation();
double h = vec3.distanceToSqr(vec34);
if (bl && h > ((PlayerExtensions) player).getSquaredAttackRange(0.0F)) {
hitResult = BlockHitResult.miss(vec34, Direction.getNearest(vec32.x, vec32.y, vec32.z), BlockPos.containing(vec34));
} else if (h < e || hitResult == null) {
hitResult = entityHitResult;
}
}
redirectResult(hitResult);
Combatify.finalizingAttack.put(getUUID(), false);
switch (hitResult.getType()) {
case BLOCK:
this.player.gameMode.handleBlockBreakAction(((BlockHitResult) hitResult).getBlockPos(), ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK, ((BlockHitResult) hitResult).getDirection(), this.player.level().getMaxBuildHeight(), 0);
this.player.connection.ackBlockChangesUpTo(0);
case ENTITY:
handleInteract(((EntityHitResult) hitResult).getEntity(), true);
case MISS:
handleInteract(player, false);
}
}
}
Combatify.isPlayerAttacking.put(getUUID(), true);
Combatify.finalizingAttack.put(getUUID(), true);
}
ci.cancel();
}
public void handleInteract(Entity entity, boolean hit) {
if(!isAttackAvailable(1.0F))
return;
final ServerLevel serverLevel = this.player.serverLevel();
this.player.resetLastActionTime();
if (entity != null) {
if (!serverLevel.getWorldBorder().isWithinBounds(entity.blockPosition())) {
return;
}
double d = ((PlayerExtensions)player).getAttackRange(1.0F) + 1;
d *= d;
if(!player.hasLineOfSight(entity)) {
d = 6.25;
}

AABB aABB = entity.getBoundingBox();
Vec3 vec3 = player.getEyePosition(0.0F);
if (vec3.distanceToSqr(((AABBExtensions)aABB).getNearestPointTo(vec3)) < d) {
if(hit) {
if (!(entity instanceof ItemEntity) && !(entity instanceof ExperienceOrb) && !(entity instanceof AbstractArrow) && entity != player) {
ItemStack itemStack = player.getItemInHand(InteractionHand.MAIN_HAND);
if (itemStack.isItemEnabled(serverLevel.enabledFeatures())) {
player.attack(entity);
}
} else {
player.connection.disconnect(Component.translatable("multiplayer.disconnect.invalid_entity_attacked"));
ServerGamePacketListenerImpl.LOGGER.warn("Player {} tried to attack an invalid entity", player.getName().getString());
}
} else {
attackAir();
}
}
}

}
public final HitResult redirectResult(HitResult instance) {
if(instance.getType() == HitResult.Type.BLOCK) {
BlockHitResult blockHitResult = (BlockHitResult)instance;
BlockPos blockPos = blockHitResult.getBlockPos();
boolean bl = !level().getBlockState(blockPos).canOcclude() && !level().getBlockState(blockPos).getBlock().hasCollision;
assert player != null;
EntityHitResult rayTraceResult = rayTraceEntity(player, 1.0F, ((PlayerExtensions)player).getAttackRange(0.0F));
if (rayTraceResult != null && bl) {
return rayTraceResult;
} else {
return instance;
}
}
return instance;
}
@Nullable
public EntityHitResult rayTraceEntity(Player player, float partialTicks, double blockReachDistance) {
Vec3 from = player.getEyePosition(partialTicks);
Vec3 look = player.getViewVector(partialTicks);
Vec3 to = from.add(look.x * blockReachDistance, look.y * blockReachDistance, look.z * blockReachDistance);

return ProjectileUtil.getEntityHitResult(
player.level(),
player,
from,
to,
new AABB(from, to),
EntitySelector.NO_CREATIVE_OR_SPECTATOR.and(e -> e != null
&& e.isPickable()
&& e instanceof LivingEntity)
);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package net.atlas.combatify.networking;

import net.atlas.combatify.Combatify;
import net.fabricmc.fabric.api.event.player.*;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.network.chat.Component;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder;

import static net.atlas.combatify.Combatify.CONFIG;
import static net.atlas.combatify.Combatify.modDetectionNetworkChannel;
import static net.atlas.combatify.Combatify.*;

public class NetworkingHandler {

Expand All @@ -20,7 +22,34 @@ public NetworkingHandler() {
if(bl)
handler.player.connection.disconnect(Component.literal("Combatify needs to be installed on the client to join this server!"));
Combatify.unmoddedPlayers.add(handler.player.getUUID());
Combatify.isPlayerAttacking.put(handler.player.getUUID(), true);
Combatify.finalizingAttack.put(handler.player.getUUID(), true);
}
});
AttackBlockCallback.EVENT.register(modDetectionNetworkChannel, (player, world, hand, pos, direction) -> {
if (Combatify.unmoddedPlayers.contains(player.getUUID()) && finalizingAttack.get(player.getUUID()))
return InteractionResult.FAIL;
return InteractionResult.PASS;
});
AttackEntityCallback.EVENT.register(modDetectionNetworkChannel, (player, world, hand, entity, hitResult) -> {
if (Combatify.unmoddedPlayers.contains(player.getUUID()) && finalizingAttack.get(player.getUUID()))
return InteractionResult.FAIL;
return InteractionResult.PASS;
});
UseBlockCallback.EVENT.register(modDetectionNetworkChannel, (player, world, hand, hitResult) -> {
if(Combatify.unmoddedPlayers.contains(player.getUUID()))
Combatify.isPlayerAttacking.put(player.getUUID(), false);
return InteractionResult.PASS;
});
UseEntityCallback.EVENT.register(modDetectionNetworkChannel, (player, world, hand, entity, hitResult) -> {
if(Combatify.unmoddedPlayers.contains(player.getUUID()))
Combatify.isPlayerAttacking.put(player.getUUID(), false);
return InteractionResult.PASS;
});
UseItemCallback.EVENT.register(modDetectionNetworkChannel, (player, world, hand) -> {
if(Combatify.unmoddedPlayers.contains(player.getUUID()))
Combatify.isPlayerAttacking.put(player.getUUID(), false);
return InteractionResultHolder.pass(player.getItemInHand(hand));
});
}
}
1 change: 1 addition & 0 deletions src/main/resources/combatify.accesswidener
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ accessible class net/minecraft/network/protocol/game/ServerboundInteractPacket$A
accessible class net/minecraft/network/protocol/game/ServerboundInteractPacket$ActionType
accessible method net/minecraft/network/protocol/game/ServerboundInteractPacket <init> (IZLnet/minecraft/network/protocol/game/ServerboundInteractPacket$Action;)V
accessible class net/minecraft/resources/ResourceLocation$Dummy
accessible field net/minecraft/server/network/ServerGamePacketListenerImpl LOGGER Lorg/slf4j/Logger;
2 changes: 2 additions & 0 deletions src/main/resources/combatify.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"AttributeModifierMixin",
"AxeItemMixin",
"BowItemMixin",
"ClientboundUpdateAttributesPacketMixin",
"DamageEnchantmentMixin",
"DiggerItemMixin",
"EffectsMixin",
Expand All @@ -38,6 +39,7 @@
"ProjectileMixin",
"RamTargetMixin",
"ResourceLocationMixin",
"ServerEntityMixin",
"ServerGameInteractPacketMixin",
"ServerGamePacketMixin",
"ServerPlayerMixin",
Expand Down

1 comment on commit 8a822ce

@not-coded
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤯

Please sign in to comment.