make everything work

This commit is contained in:
Octol1ttle 2024-01-17 22:51:32 +05:00
parent 38939750e6
commit 3a93c3beae
Signed by: Octol1ttle
GPG key ID: B77C34313AEE1FFF
19 changed files with 346 additions and 391 deletions

View file

@ -41,7 +41,7 @@ allprojects {
// for more information about repositories. // for more information about repositories.
} }
tasks.withType(JavaCompile) { tasks.withType(JavaCompile).configureEach {
options.encoding = "UTF-8" options.encoding = "UTF-8"
options.release = 17 options.release = 17
} }

View file

@ -1,12 +1,25 @@
package ru.octol1ttle.knockdowns.common; package ru.octol1ttle.knockdowns.common;
import dev.architectury.event.EventResult;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import org.jetbrains.annotations.Nullable;
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
import ru.octol1ttle.knockdowns.common.events.KnockdownsClientEvents; import ru.octol1ttle.knockdowns.common.events.KnockdownsClientEvents;
import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork;
import ru.octol1ttle.knockdowns.common.network.packets.RequestStartRevivingC2SPacket;
import ru.octol1ttle.knockdowns.common.network.packets.StopRevivingC2SPacket;
import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents; import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents;
import ru.octol1ttle.knockdowns.common.registries.KnockedDownSoundInstance; import ru.octol1ttle.knockdowns.common.registries.KnockedDownSoundInstance;
public class KnockdownsClient { public class KnockdownsClient {
@Nullable
public static Entity reviving;
public static void init() { public static void init() {
KnockdownsClientEvents.registerCallbacks(); KnockdownsClientEvents.registerCallbacks();
} }
@ -16,4 +29,35 @@ public class KnockdownsClient {
new KnockedDownSoundInstance(KnockdownsSoundEvents.KNOCKED_DOWN.get(), pos) new KnockedDownSoundInstance(KnockdownsSoundEvents.KNOCKED_DOWN.get(), pos)
); );
} }
public static EventResult onEntityUse(PlayerEntity player, Entity entity) {
if (KnockdownsCommon.isKnockedOrReviving(player) || !(entity instanceof IKnockableDown knockable) || !knockable.is_KnockedDown()) {
return EventResult.pass();
}
KnockdownsNetwork.sendToServer(new RequestStartRevivingC2SPacket(entity.getUuid()));
reviving = entity;
return EventResult.interruptTrue();
}
public static void onPlayerTick(PlayerEntity player) {
MinecraftClient client = MinecraftClient.getInstance();
if (!player.equals(client.player) || reviving == null) {
return;
}
boolean playerKnocked = ((IKnockableDown) player).is_KnockedDown();
boolean revivingTargeted = client.crosshairTarget != null && client.crosshairTarget.getType() == HitResult.Type.ENTITY
&& ((EntityHitResult) client.crosshairTarget).getEntity().getUuid().equals(reviving.getUuid());
if (!(reviving instanceof IKnockableDown knockable)) {
return;
}
if (!knockable.is_KnockedDown() || playerKnocked || !revivingTargeted) {
KnockdownsNetwork.sendToServer(new StopRevivingC2SPacket(reviving.getUuid()));
reviving = null;
}
}
} }

View file

@ -1,15 +1,23 @@
package ru.octol1ttle.knockdowns.common; package ru.octol1ttle.knockdowns.common;
import net.minecraft.SharedConstants;
import net.minecraft.entity.player.PlayerEntity;
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
import ru.octol1ttle.knockdowns.common.events.KnockdownsEvents; import ru.octol1ttle.knockdowns.common.events.KnockdownsEvents;
import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork;
import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents; import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents;
public class KnockdownsCommon { public class KnockdownsCommon {
public static final String MOD_ID = "knockdowns"; public static final String MOD_ID = "knockdowns";
public static final int REVIVE_WAIT_TIME = 10 * SharedConstants.TICKS_PER_SECOND;
public static void init() { public static void init() {
KnockdownsSoundEvents.register(); KnockdownsSoundEvents.register();
KnockdownsNetwork.registerPackets(); KnockdownsNetwork.registerPackets();
KnockdownsEvents.registerCallbacks(); KnockdownsEvents.registerCallbacks();
} }
public static boolean isKnockedOrReviving(PlayerEntity player) {
return player instanceof IKnockableDown knockable && (knockable.is_KnockedDown() || knockable.is_Reviving());
}
} }

View file

@ -1,15 +1,19 @@
package ru.octol1ttle.knockdowns.common.api; package ru.octol1ttle.knockdowns.common.api;
import java.util.UUID;
public interface IKnockableDown { public interface IKnockableDown {
boolean knockdowns$isKnockedDown(); boolean is_KnockedDown();
void knockdowns$setKnockedDown(boolean knockedDown); void set_KnockedDown(boolean knockedDown);
boolean knockdowns$isBeingRevived(); int get_ReviverCount();
void knockdowns$setBeingRevived(boolean beingRevived); void set_ReviverCount(int reviverCount);
UUID knockdowns$getUuid(); boolean is_Reviving();
void set_Reviving(boolean reviving);
int get_ReviveTimer();
void set_ReviveTimer(int reviveTimer);
} }

View file

@ -1,105 +1,44 @@
package ru.octol1ttle.knockdowns.common.events; package ru.octol1ttle.knockdowns.common.events;
import dev.architectury.event.EventResult;
import dev.architectury.event.events.client.ClientGuiEvent; import dev.architectury.event.events.client.ClientGuiEvent;
import dev.architectury.event.events.client.ClientPlayerEvent;
import dev.architectury.event.events.client.ClientTickEvent;
import dev.architectury.event.events.common.InteractionEvent;
import java.util.UUID;
import net.minecraft.SharedConstants; import net.minecraft.SharedConstants;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer; import net.minecraft.client.font.TextRenderer;
import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.Formatting;
import net.minecraft.util.hit.HitResult; import ru.octol1ttle.knockdowns.common.KnockdownsClient;
import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork;
import ru.octol1ttle.knockdowns.common.api.IKnockableDown; import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
import ru.octol1ttle.knockdowns.common.network.packets.KnockedDownStatusPacket;
import ru.octol1ttle.knockdowns.common.network.packets.ReviveStatusPacket;
public class KnockdownsClientEvents { public class KnockdownsClientEvents {
private static final int REVIVAL_WAIT_TIME = 10 * SharedConstants.TICKS_PER_SECOND;
private static IKnockableDown reviving = null;
private static int revivalTimer = -1;
public static void registerCallbacks() { public static void registerCallbacks() {
registerOnClientPlayerJoin();
registerOnEntityUse();
registerOnWorldTick();
registerOnHudRender(); registerOnHudRender();
} }
private static void registerOnClientPlayerJoin() {
ClientPlayerEvent.CLIENT_PLAYER_JOIN.register(player -> {
UUID playerUuid = player.getUuid();
KnockdownsNetwork.sendToServer(new KnockedDownStatusPacket.RequestC2S(playerUuid));
KnockdownsNetwork.sendToServer(new ReviveStatusPacket.RequestC2S(playerUuid));
});
}
private static void registerOnEntityUse() {
InteractionEvent.INTERACT_ENTITY.register((player, entity, hand) -> {
if (!player.getWorld().isClient() || !(entity instanceof IKnockableDown knockableEntity) || !knockableEntity.knockdowns$isKnockedDown()
|| knockableEntity.knockdowns$isBeingRevived()) {
return EventResult.pass();
}
IKnockableDown self = (IKnockableDown) player;
if (self.knockdowns$isKnockedDown()) {
return EventResult.interruptFalse();
}
knockableEntity.knockdowns$setBeingRevived(true);
KnockdownsNetwork.sendToServer(new ReviveStatusPacket.SendC2S(entity.getUuid(), true));
reviving = knockableEntity;
revivalTimer = REVIVAL_WAIT_TIME;
return EventResult.interruptTrue();
});
}
private static void registerOnWorldTick() {
ClientTickEvent.ClientLevel.CLIENT_LEVEL_POST.register(world -> {
boolean revived = false;
revivalTimer--;
if (revivalTimer <= 0) {
revivalTimer = -1;
revived = true;
}
if (reviving == null) {
return;
}
HitResult crosshairTarget = MinecraftClient.getInstance().crosshairTarget;
if (revived || crosshairTarget == null || crosshairTarget.getType() != HitResult.Type.ENTITY
|| !((EntityHitResult) crosshairTarget).getEntity().getUuid().equals(reviving.knockdowns$getUuid())) {
reviving.knockdowns$setBeingRevived(false);
KnockdownsNetwork.sendToServer(new ReviveStatusPacket.SendC2S(reviving.knockdowns$getUuid(), false));
if (revived) {
reviving.knockdowns$setKnockedDown(false);
KnockdownsNetwork.sendToServer(new ReviveStatusPacket.RevivedC2S(reviving.knockdowns$getUuid()));
}
reviving = null;
revivalTimer = -1;
}
});
}
private static void registerOnHudRender() { private static void registerOnHudRender() {
ClientGuiEvent.RENDER_HUD.register((drawContext, tickDelta) -> { ClientGuiEvent.RENDER_HUD.register((drawContext, tickDelta) -> {
if (revivalTimer == -1) { IKnockableDown reviving = (IKnockableDown) KnockdownsClient.reviving;
return; MinecraftClient client = MinecraftClient.getInstance();
if (reviving == null) {
reviving = (IKnockableDown) client.player;
if (reviving == null || reviving.get_ReviverCount() == 0) {
return;
}
} }
TextRenderer renderer = MinecraftClient.getInstance().textRenderer; TextRenderer renderer = client.textRenderer;
String text = String.format("%.1f", revivalTimer / (float) SharedConstants.TICKS_PER_SECOND);
int x = (drawContext.getScaledWindowWidth() - renderer.getWidth(text)) / 2;
drawContext.drawTextWithShadow(renderer, text, x, drawContext.getScaledWindowHeight() / 2 + 15, 0xFFFFFF); String timerText = String.format("%.1f", reviving.get_ReviveTimer() / (float) SharedConstants.TICKS_PER_SECOND);
int timerX = (drawContext.getScaledWindowWidth() - renderer.getWidth(timerText)) / 2;
int reviverCount = reviving.get_ReviverCount();
Integer color = reviverCount > 1 ? Formatting.GREEN.getColorValue() : Formatting.WHITE.getColorValue();
String reviverCountText = "x" + reviverCount;
int reviveCountX = (drawContext.getScaledWindowWidth() - renderer.getWidth(reviverCountText)) / 2;
if (color != null) {
drawContext.drawTextWithShadow(renderer, timerText, timerX, drawContext.getScaledWindowHeight() / 2 + 5, color);
drawContext.drawTextWithShadow(renderer, reviverCountText, reviveCountX, drawContext.getScaledWindowHeight() / 2 + 14, color);
}
}); });
} }
} }

View file

@ -5,46 +5,50 @@ import dev.architectury.event.EventResult;
import dev.architectury.event.events.common.EntityEvent; import dev.architectury.event.events.common.EntityEvent;
import dev.architectury.event.events.common.InteractionEvent; import dev.architectury.event.events.common.InteractionEvent;
import dev.architectury.event.events.common.PlayerEvent; import dev.architectury.event.events.common.PlayerEvent;
import dev.architectury.event.events.common.TickEvent;
import java.util.Objects;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.text.TranslatableTextContent; import net.minecraft.text.TranslatableTextContent;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.world.GameRules; import ru.octol1ttle.knockdowns.common.KnockdownsClient;
import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; import ru.octol1ttle.knockdowns.common.KnockdownsCommon;
import ru.octol1ttle.knockdowns.common.api.IKnockableDown; import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
import ru.octol1ttle.knockdowns.common.network.packets.KnockedDownStatusPacket; import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork;
import ru.octol1ttle.knockdowns.common.network.packets.PlayKnockedDownSoundS2CPacket; import ru.octol1ttle.knockdowns.common.network.packets.PlayKnockedDownSoundS2CPacket;
public class KnockdownsEvents { public class KnockdownsEvents {
private static final float KNOCKED_DOWN_TIMER = 50.0f;
public static void registerCallbacks() { public static void registerCallbacks() {
registerOnLivingDeath(); registerOnLivingDeath();
registerOnPlayerTick();
registerOnPlayerInteractions(); registerOnPlayerInteractions();
registerOnEntityUse();
} }
private static void registerOnLivingDeath() { private static void registerOnLivingDeath() {
EntityEvent.LIVING_DEATH.register((entity, source) -> { EntityEvent.LIVING_DEATH.register((entity, source) -> {
if (!(entity instanceof IKnockableDown knockableDown) || knockableDown.knockdowns$isKnockedDown()) { if (entity.getWorld().isClient() || !(entity instanceof IKnockableDown knockable) || knockable.is_KnockedDown()) {
return EventResult.pass(); return EventResult.pass();
} }
ServerPlayerEntity serverPlayer = (ServerPlayerEntity) entity; ServerPlayerEntity serverPlayer = (ServerPlayerEntity) entity;
// TODO: timer
if (!serverPlayer.getWorld().getGameRules().getBoolean(GameRules.KEEP_INVENTORY)) { entity.clearStatusEffects();
serverPlayer.getInventory().dropAll();
}
entity.setHealth(1.0f);
entity.setInvulnerable(true); entity.setInvulnerable(true);
entity.setGlowing(true); entity.setGlowing(true);
entity.setAir(entity.getMaxAir()); entity.setHealth(entity.getMaxHealth());
entity.extinguish(); entity.extinguish();
entity.setAir(entity.getMaxAir());
entity.setFrozenTicks(0); entity.setFrozenTicks(0);
entity.setOnFire(false); serverPlayer.stopFallFlying();
entity.clearStatusEffects();
knockableDown.knockdowns$setKnockedDown(true); knockable.set_KnockedDown(true);
knockable.set_ReviveTimer(KnockdownsCommon.REVIVE_WAIT_TIME);
KnockdownsNetwork.sendToListenersAndSelf(serverPlayer, new KnockedDownStatusPacket.SendS2C(serverPlayer.getUuid(), true));
KnockdownsNetwork.sendToWorld(serverPlayer.getServerWorld(), new PlayKnockedDownSoundS2CPacket(serverPlayer.getX(), serverPlayer.getY(), serverPlayer.getZ())); KnockdownsNetwork.sendToWorld(serverPlayer.getServerWorld(), new PlayKnockedDownSoundS2CPacket(serverPlayer.getX(), serverPlayer.getY(), serverPlayer.getZ()));
TranslatableTextContent content = (TranslatableTextContent) entity.getDamageTracker().getDeathMessage().getContent(); TranslatableTextContent content = (TranslatableTextContent) entity.getDamageTracker().getDeathMessage().getContent();
@ -58,30 +62,69 @@ public class KnockdownsEvents {
}); });
} }
private static void registerOnPlayerTick() {
TickEvent.PLAYER_POST.register(player -> {
if (player.getWorld().isClient()) {
KnockdownsClient.onPlayerTick(player);
return;
}
if (!(player instanceof IKnockableDown knockable) || !knockable.is_KnockedDown()) {
return;
}
if (knockable.get_ReviverCount() > 0) {
knockable.set_ReviveTimer(knockable.get_ReviveTimer() - knockable.get_ReviverCount());
if (knockable.get_ReviveTimer() <= 0) {
knockable.set_KnockedDown(false);
knockable.set_ReviverCount(0);
knockable.set_ReviveTimer(KnockdownsCommon.REVIVE_WAIT_TIME);
player.setInvulnerable(false);
player.setGlowing(false);
player.setHealth(6.0f);
}
return;
}
knockable.set_ReviveTimer(Math.min(KnockdownsCommon.REVIVE_WAIT_TIME, knockable.get_ReviveTimer() + 2));
if (player.age % 20 == 0) {
player.setInvulnerable(false);
DamageSource recent = player.getRecentDamageSource();
player.damage(Objects.requireNonNullElse(recent, player.getDamageSources().generic()), player.getMaxHealth() / KNOCKED_DOWN_TIMER);
player.velocityModified = false;
}
});
}
private static void registerOnPlayerInteractions() { private static void registerOnPlayerInteractions() {
InteractionEvent.LEFT_CLICK_BLOCK.register((player, hand, pos, direction) -> { InteractionEvent.LEFT_CLICK_BLOCK.register((player, hand, pos, direction) -> {
if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { if (KnockdownsCommon.isKnockedOrReviving(player)) {
return EventResult.interruptFalse(); return EventResult.interruptFalse();
} }
return EventResult.pass(); return EventResult.pass();
}); });
PlayerEvent.ATTACK_ENTITY.register((player, world, hand, entity, hitResult) -> { PlayerEvent.ATTACK_ENTITY.register((player, world, hand, entity, hitResult) -> {
if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { if (KnockdownsCommon.isKnockedOrReviving(player)) {
return EventResult.interruptFalse(); return EventResult.interruptFalse();
} }
return EventResult.pass(); return EventResult.pass();
}); });
InteractionEvent.RIGHT_CLICK_ITEM.register((player, hand) -> { InteractionEvent.RIGHT_CLICK_ITEM.register((player, hand) -> {
if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { if (KnockdownsCommon.isKnockedOrReviving(player)) {
return CompoundEventResult.interruptFalse(hand == Hand.MAIN_HAND ? player.getMainHandStack() : player.getOffHandStack()); return CompoundEventResult.interruptFalse(hand == Hand.MAIN_HAND ? player.getMainHandStack() : player.getOffHandStack());
} }
return CompoundEventResult.pass(); return CompoundEventResult.pass();
}); });
InteractionEvent.RIGHT_CLICK_BLOCK.register((player, hand, pos, direction) -> { InteractionEvent.RIGHT_CLICK_BLOCK.register((player, hand, pos, direction) -> {
if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { if (KnockdownsCommon.isKnockedOrReviving(player)) {
return EventResult.interruptFalse(); return EventResult.interruptFalse();
} }
return EventResult.pass(); return EventResult.pass();
}); });
} }
private static void registerOnEntityUse() {
InteractionEvent.INTERACT_ENTITY.register((player, entity, hand)
-> player.getWorld().isClient() ? KnockdownsClient.onEntityUse(player, entity) : EventResult.pass());
}
} }

View file

@ -0,0 +1,15 @@
package ru.octol1ttle.knockdowns.common.mixin;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import net.minecraft.entity.LivingEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
@Mixin(LivingEntity.class)
public abstract class LivingEntityMixin {
@ModifyReturnValue(method = "canTarget(Lnet/minecraft/entity/LivingEntity;)Z", at = @At("RETURN"))
private boolean dontTargetKnockedPlayers(boolean original, LivingEntity target) {
return original && !(target instanceof IKnockableDown knockable && knockable.is_KnockedDown());
}
}

View file

@ -0,0 +1,19 @@
package ru.octol1ttle.knockdowns.common.mixin;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.MobEntity;
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;
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
@Mixin(MobEntity.class)
public abstract class MobEntityMixin {
@Inject(method = "setTarget", at = @At("HEAD"), cancellable = true)
private void setTarget(LivingEntity target, CallbackInfo ci) {
if (target instanceof IKnockableDown knockable && knockable.is_KnockedDown()) {
ci.cancel();
}
}
}

View file

@ -2,70 +2,114 @@ package ru.octol1ttle.knockdowns.common.mixin;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import java.util.UUID; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import ru.octol1ttle.knockdowns.common.KnockdownsCommon;
import ru.octol1ttle.knockdowns.common.api.IKnockableDown; import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
@SuppressWarnings("WrongEntityDataParameterClass")
@Mixin(PlayerEntity.class) @Mixin(PlayerEntity.class)
public abstract class PlayerEntityMixin implements IKnockableDown { public abstract class PlayerEntityMixin extends Entity implements IKnockableDown {
@Unique @Unique
private boolean knockdowns$knockedDown; private static final TrackedData<Boolean> KNOCKED_DOWN = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
@Unique @Unique
private boolean knockdowns$beingRevived; private static final TrackedData<Boolean> IS_REVIVING = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
@Unique
private static final TrackedData<Integer> REVIVER_COUNT = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.INTEGER);
@Unique
private static final TrackedData<Integer> REVIVE_TIMER = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.INTEGER);
private PlayerEntityMixin(EntityType<?> type, World world) {
super(type, world);
throw new AssertionError();
}
@ModifyExpressionValue(method = "updatePose", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;isSwimming()Z")) @ModifyExpressionValue(method = "updatePose", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;isSwimming()Z"))
private boolean enterSwimmingIfKnockedDown(boolean original) { private boolean enterSwimmingIfKnockedDown(boolean original) {
PlayerEntity player = (PlayerEntity)(Object)this; return original || this.is_KnockedDown();
if (!(player instanceof IKnockableDown knockableDown)) {
throw new IllegalStateException();
}
return original || knockableDown.knockdowns$isKnockedDown();
} }
@ModifyReturnValue(method = "canFoodHeal", at = @At("RETURN")) @ModifyReturnValue(method = "canFoodHeal", at = @At("RETURN"))
private boolean dontHealIfKnockedDown(boolean original) { private boolean dontHealIfKnockedDown(boolean original) {
return original && !this.knockdowns$isKnockedDown(); return original && !this.is_KnockedDown();
}
@Inject(method = "checkFallFlying", at = @At("HEAD"), cancellable = true)
private void dontOpenElytraIfKnockedDown(CallbackInfoReturnable<Boolean> cir) {
if (this.is_KnockedDown()) {
cir.setReturnValue(false);
}
}
@Inject(method = "initDataTracker", at = @At("TAIL"))
private void initCustomDataTracker(CallbackInfo ci) {
this.dataTracker.startTracking(KNOCKED_DOWN, false);
this.dataTracker.startTracking(IS_REVIVING, false);
this.dataTracker.startTracking(REVIVER_COUNT, 0);
this.dataTracker.startTracking(REVIVE_TIMER, KnockdownsCommon.REVIVE_WAIT_TIME);
} }
@Inject(method = "readCustomDataFromNbt", at = @At("TAIL")) @Inject(method = "readCustomDataFromNbt", at = @At("TAIL"))
public void readKnockedDownFromNbt(NbtCompound nbt, CallbackInfo ci) { private void readKnockedDownFromNbt(NbtCompound nbt, CallbackInfo ci) {
this.knockdowns$knockedDown = nbt.getBoolean("KnockedDown"); this.set_KnockedDown(nbt.getBoolean("KnockedDown"));
this.set_ReviveTimer(nbt.getInt("ReviveTimer"));
} }
@Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) @Inject(method = "writeCustomDataToNbt", at = @At("TAIL"))
public void writeKnockedDownToNbt(NbtCompound nbt, CallbackInfo ci) { private void writeKnockedDownToNbt(NbtCompound nbt, CallbackInfo ci) {
nbt.putBoolean("KnockedDown", this.knockdowns$knockedDown); nbt.putBoolean("KnockedDown", this.is_KnockedDown());
nbt.putInt("ReviveTimer", this.get_ReviveTimer());
} }
@Override @Override
public boolean knockdowns$isKnockedDown() { public boolean is_KnockedDown() {
return knockdowns$knockedDown; return this.dataTracker.get(KNOCKED_DOWN);
} }
@Override @Override
public void knockdowns$setKnockedDown(boolean knockedDown) { public void set_KnockedDown(boolean knockedDown) {
this.knockdowns$knockedDown = knockedDown; this.dataTracker.set(KNOCKED_DOWN, knockedDown);
} }
@Override @Override
public boolean knockdowns$isBeingRevived() { public boolean is_Reviving() {
return knockdowns$beingRevived; return this.dataTracker.get(IS_REVIVING);
} }
@Override @Override
public void knockdowns$setBeingRevived(boolean beingRevived) { public void set_Reviving(boolean reviving) {
this.knockdowns$beingRevived = beingRevived; this.dataTracker.set(IS_REVIVING, reviving);
} }
@Override @Override
public UUID knockdowns$getUuid() { public int get_ReviverCount() {
return ((PlayerEntity)(Object)this).getUuid(); return this.dataTracker.get(REVIVER_COUNT);
}
@Override
public void set_ReviverCount(int reviverCount) {
this.dataTracker.set(REVIVER_COUNT, reviverCount);
}
@Override
public int get_ReviveTimer() {
return this.dataTracker.get(REVIVE_TIMER);
}
@Override
public void set_ReviveTimer(int reviveTimer) {
this.dataTracker.set(REVIVE_TIMER, reviveTimer);
} }
} }

View file

@ -7,10 +7,9 @@ import org.spongepowered.asm.mixin.injection.At;
import ru.octol1ttle.knockdowns.common.api.IKnockableDown; import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
@Mixin(ClientPlayerEntity.class) @Mixin(ClientPlayerEntity.class)
public abstract class ClientPlayerEntityMixin { public abstract class ClientPlayerEntityMixin implements IKnockableDown {
@ModifyReturnValue(method = "shouldSlowDown", at = @At("RETURN")) @ModifyReturnValue(method = "shouldSlowDown", at = @At("RETURN"))
private boolean shouldSlowDown(boolean original) { private boolean shouldSlowDown(boolean original) {
IKnockableDown self = (IKnockableDown) this; return original || this.is_KnockedDown();
return original || self.knockdowns$isKnockedDown();
} }
} }

View file

@ -2,32 +2,22 @@ package ru.octol1ttle.knockdowns.common.network;
import dev.architectury.networking.NetworkChannel; import dev.architectury.networking.NetworkChannel;
import dev.architectury.networking.NetworkManager; import dev.architectury.networking.NetworkManager;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.packet.Packet; import net.minecraft.network.packet.Packet;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.EntityTrackingListener;
import net.minecraft.server.world.ServerChunkManager;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
import net.minecraft.server.world.ThreadedAnvilChunkStorage;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import ru.octol1ttle.knockdowns.common.KnockdownsCommon; import ru.octol1ttle.knockdowns.common.KnockdownsCommon;
import ru.octol1ttle.knockdowns.common.network.packets.KnockedDownStatusPacket;
import ru.octol1ttle.knockdowns.common.network.packets.PlayKnockedDownSoundS2CPacket; import ru.octol1ttle.knockdowns.common.network.packets.PlayKnockedDownSoundS2CPacket;
import ru.octol1ttle.knockdowns.common.network.packets.ReviveStatusPacket; import ru.octol1ttle.knockdowns.common.network.packets.RequestStartRevivingC2SPacket;
import ru.octol1ttle.knockdowns.common.network.packets.StopRevivingC2SPacket;
public class KnockdownsNetwork { public class KnockdownsNetwork {
private static final NetworkChannel CHANNEL = NetworkChannel.create(new Identifier(KnockdownsCommon.MOD_ID, "main")); private static final NetworkChannel CHANNEL = NetworkChannel.create(new Identifier(KnockdownsCommon.MOD_ID, "main"));
public static void registerPackets() { public static void registerPackets() {
CHANNEL.register(KnockedDownStatusPacket.SendS2C.class, KnockedDownStatusPacket.SendS2C::encode, KnockedDownStatusPacket.SendS2C::new, KnockedDownStatusPacket.SendS2C::apply);
CHANNEL.register(KnockedDownStatusPacket.RequestC2S.class, KnockedDownStatusPacket.RequestC2S::encode, KnockedDownStatusPacket.RequestC2S::new, KnockedDownStatusPacket.RequestC2S::apply);
CHANNEL.register(PlayKnockedDownSoundS2CPacket.class, PlayKnockedDownSoundS2CPacket::encode, PlayKnockedDownSoundS2CPacket::new, PlayKnockedDownSoundS2CPacket::apply); CHANNEL.register(PlayKnockedDownSoundS2CPacket.class, PlayKnockedDownSoundS2CPacket::encode, PlayKnockedDownSoundS2CPacket::new, PlayKnockedDownSoundS2CPacket::apply);
CHANNEL.register(RequestStartRevivingC2SPacket.class, RequestStartRevivingC2SPacket::encode, RequestStartRevivingC2SPacket::new, RequestStartRevivingC2SPacket::apply);
CHANNEL.register(ReviveStatusPacket.SendS2C.class, ReviveStatusPacket.SendS2C::encode, ReviveStatusPacket.SendS2C::new, ReviveStatusPacket.SendS2C::apply); CHANNEL.register(StopRevivingC2SPacket.class, StopRevivingC2SPacket::encode, StopRevivingC2SPacket::new, StopRevivingC2SPacket::apply);
CHANNEL.register(ReviveStatusPacket.SendC2S.class, ReviveStatusPacket.SendC2S::encode, ReviveStatusPacket.SendC2S::new, ReviveStatusPacket.SendC2S::apply);
CHANNEL.register(ReviveStatusPacket.RequestC2S.class, ReviveStatusPacket.RequestC2S::encode, ReviveStatusPacket.RequestC2S::new, ReviveStatusPacket.RequestC2S::apply);
CHANNEL.register(ReviveStatusPacket.RevivedC2S.class, ReviveStatusPacket.RevivedC2S::encode, ReviveStatusPacket.RevivedC2S::new, ReviveStatusPacket.RevivedC2S::apply);
} }
public static <T> void sendToServer(T message) { public static <T> void sendToServer(T message) {
@ -52,30 +42,6 @@ public class KnockdownsNetwork {
} }
} }
// TODO: PR to Architectury API
public static <T> void sendToListeners(Entity entity, T message) {
Packet<?> packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message);
Class<?> messageClass = message.getClass();
sendToListeners(entity, packet, messageClass);
}
private static void sendToListeners(Entity entity, Packet<?> packet, Class<?> messageClass) {
ServerChunkManager chunkManager = (ServerChunkManager) entity.getWorld().getChunkManager();
ThreadedAnvilChunkStorage.EntityTracker entityTracker = chunkManager.threadedAnvilChunkStorage.entityTrackers.get(entity.getId());
for (EntityTrackingListener listener : entityTracker.listeners) {
sendToPlayer(listener.getPlayer(), packet, messageClass);
}
}
public static <T> void sendToListenersAndSelf(PlayerEntity player, T message) {
Packet<?> packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message);
Class<?> messageClass = message.getClass();
sendToPlayer(player, packet, messageClass);
sendToListeners(player, packet, messageClass);
}
public static <T> void sendToWorld(ServerWorld world, T message) { public static <T> void sendToWorld(ServerWorld world, T message) {
Packet<?> packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message); Packet<?> packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message);
Class<?> messageClass = message.getClass(); Class<?> messageClass = message.getClass();

View file

@ -1,69 +0,0 @@
package ru.octol1ttle.knockdowns.common.network.packets;
import dev.architectury.networking.NetworkManager;
import java.util.UUID;
import java.util.function.Supplier;
import net.minecraft.network.PacketByteBuf;
import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork;
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
public class KnockedDownStatusPacket {
public static class SendS2C extends KnockdownsPacket {
private final UUID playerUuid;
private final boolean knockedDown;
public SendS2C(PacketByteBuf buf) {
this(buf.readUuid(), buf.readBoolean());
}
public SendS2C(UUID playerUuid, boolean knockedDown) {
this.playerUuid = playerUuid;
this.knockedDown = knockedDown;
}
@Override
public void encode(PacketByteBuf buf) {
buf.writeUuid(this.playerUuid);
buf.writeBoolean(this.knockedDown);
}
@Override
public void apply(Supplier<NetworkManager.PacketContext> contextSupplier) {
NetworkManager.PacketContext context = contextSupplier.get();
context.queue(() -> {
IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid);
if (knockableDown != null) {
knockableDown.knockdowns$setKnockedDown(this.knockedDown);
}
});
}
}
public static class RequestC2S extends KnockdownsPacket {
private final UUID playerUuid;
public RequestC2S(PacketByteBuf buf) {
this(buf.readUuid());
}
public RequestC2S(UUID playerUuid) {
this.playerUuid = playerUuid;
}
@Override
public void encode(PacketByteBuf buf) {
buf.writeUuid(this.playerUuid);
}
@Override
public void apply(Supplier<NetworkManager.PacketContext> contextSupplier) {
NetworkManager.PacketContext context = contextSupplier.get();
context.queue(() -> {
IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid);
if (knockableDown != null) {
KnockdownsNetwork.sendToPlayer(context.getPlayer(), new SendS2C(this.playerUuid, knockableDown.knockdowns$isKnockedDown()));
}
});
}
}
}

View file

@ -0,0 +1,39 @@
package ru.octol1ttle.knockdowns.common.network.packets;
import dev.architectury.networking.NetworkManager;
import java.util.UUID;
import java.util.function.Supplier;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.PacketByteBuf;
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
public class RequestStartRevivingC2SPacket extends KnockdownsPacket {
private final UUID targetUuid;
public RequestStartRevivingC2SPacket(PacketByteBuf buf) {
this(buf.readUuid());
}
public RequestStartRevivingC2SPacket(UUID targetUuid) {
this.targetUuid = targetUuid;
}
@Override
public void encode(PacketByteBuf buf) {
buf.writeUuid(this.targetUuid);
}
@Override
public void apply(Supplier<NetworkManager.PacketContext> contextSupplier) {
NetworkManager.PacketContext context = contextSupplier.get();
context.queue(() -> {
PlayerEntity player = context.getPlayer();
IKnockableDown playerKnockable = (IKnockableDown) player;
IKnockableDown targetKnockable = (IKnockableDown) player.getWorld().getPlayerByUuid(this.targetUuid);
if (!playerKnockable.is_Reviving() && targetKnockable != null && targetKnockable.is_KnockedDown()) {
playerKnockable.set_Reviving(true);
targetKnockable.set_ReviverCount(targetKnockable.get_ReviverCount() + 1);
}
});
}
}

View file

@ -1,138 +0,0 @@
package ru.octol1ttle.knockdowns.common.network.packets;
import dev.architectury.networking.NetworkManager;
import java.util.UUID;
import java.util.function.Supplier;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.PacketByteBuf;
import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork;
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
public class ReviveStatusPacket {
public static class SendS2C extends KnockdownsPacket {
private final UUID playerUuid;
private final boolean beingRevived;
public SendS2C(PacketByteBuf buf) {
this(buf.readUuid(), buf.readBoolean());
}
public SendS2C(UUID playerUuid, boolean beingRevived) {
this.playerUuid = playerUuid;
this.beingRevived = beingRevived;
}
@Override
public void encode(PacketByteBuf buf) {
buf.writeUuid(this.playerUuid);
buf.writeBoolean(this.beingRevived);
}
@Override
public void apply(Supplier<NetworkManager.PacketContext> contextSupplier) {
NetworkManager.PacketContext context = contextSupplier.get();
context.queue(() -> {
IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid);
if (knockableDown != null) {
knockableDown.knockdowns$setBeingRevived(this.beingRevived);
}
});
}
}
public static class SendC2S extends KnockdownsPacket {
private final UUID playerUuid;
private final boolean beingRevived;
public SendC2S(PacketByteBuf buf) {
this(buf.readUuid(), buf.readBoolean());
}
public SendC2S(UUID playerUuid, boolean beingRevived) {
this.playerUuid = playerUuid;
this.beingRevived = beingRevived;
}
@Override
public void encode(PacketByteBuf buf) {
buf.writeUuid(this.playerUuid);
buf.writeBoolean(this.beingRevived);
}
@Override
public void apply(Supplier<NetworkManager.PacketContext> contextSupplier) {
NetworkManager.PacketContext context = contextSupplier.get();
context.queue(() -> {
IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid);
if (knockableDown != null) {
knockableDown.knockdowns$setBeingRevived(this.beingRevived);
KnockdownsNetwork.sendToListenersAndSelf(context.getPlayer(), new ReviveStatusPacket.SendS2C(this.playerUuid, this.beingRevived));
}
});
}
}
public static class RequestC2S extends KnockdownsPacket {
private final UUID playerUuid;
public RequestC2S(PacketByteBuf buf) {
this(buf.readUuid());
}
public RequestC2S(UUID playerUuid) {
this.playerUuid = playerUuid;
}
@Override
public void encode(PacketByteBuf buf) {
buf.writeUuid(this.playerUuid);
}
@Override
public void apply(Supplier<NetworkManager.PacketContext> contextSupplier) {
NetworkManager.PacketContext context = contextSupplier.get();
context.queue(() -> {
IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid);
if (knockableDown != null) {
KnockdownsNetwork.sendToPlayer(context.getPlayer(), new ReviveStatusPacket.SendS2C(this.playerUuid, knockableDown.knockdowns$isBeingRevived()));
}
});
}
}
public static class RevivedC2S extends KnockdownsPacket {
private final UUID playerUuid;
public RevivedC2S(PacketByteBuf buf) {
this(buf.readUuid());
}
public RevivedC2S(UUID playerUuid) {
this.playerUuid = playerUuid;
}
@Override
public void encode(PacketByteBuf buf) {
buf.writeUuid(this.playerUuid);
}
@Override
public void apply(Supplier<NetworkManager.PacketContext> contextSupplier) {
NetworkManager.PacketContext context = contextSupplier.get();
context.queue(() -> {
PlayerEntity reviving = context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid);
IKnockableDown knockableDown = (IKnockableDown) reviving;
if (knockableDown == null || !knockableDown.knockdowns$isKnockedDown()) {
return;
}
reviving.setInvulnerable(false);
reviving.setGlowing(false);
reviving.setHealth(6.0f);
knockableDown.knockdowns$setKnockedDown(false);
KnockdownsNetwork.sendToListenersAndSelf(reviving, new KnockedDownStatusPacket.SendS2C(reviving.getUuid(), false));
});
}
}
}

View file

@ -0,0 +1,41 @@
package ru.octol1ttle.knockdowns.common.network.packets;
import dev.architectury.networking.NetworkManager;
import java.util.UUID;
import java.util.function.Supplier;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.PacketByteBuf;
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
public class StopRevivingC2SPacket extends KnockdownsPacket {
private final UUID targetUuid;
public StopRevivingC2SPacket(PacketByteBuf buf) {
this(buf.readUuid());
}
public StopRevivingC2SPacket(UUID targetUuid) {
this.targetUuid = targetUuid;
}
@Override
public void encode(PacketByteBuf buf) {
buf.writeUuid(this.targetUuid);
}
@Override
public void apply(Supplier<NetworkManager.PacketContext> contextSupplier) {
NetworkManager.PacketContext context = contextSupplier.get();
context.queue(() -> {
PlayerEntity player = context.getPlayer();
IKnockableDown playerKnockable = (IKnockableDown) player;
IKnockableDown targetKnockable = (IKnockableDown) player.getWorld().getPlayerByUuid(this.targetUuid);
if (playerKnockable.is_Reviving() && targetKnockable != null) {
playerKnockable.set_Reviving(false);
if (targetKnockable.is_KnockedDown()) {
targetKnockable.set_ReviverCount(targetKnockable.get_ReviverCount() - 1);
}
}
});
}
}

View file

@ -1,4 +1,6 @@
{ {
"subtitles.knockdowns.knocked_down": "Player knocked down",
"knockdown.attack.anvil": "%1$s was knocked down by a falling anvil", "knockdown.attack.anvil": "%1$s was knocked down by a falling anvil",
"knockdown.attack.anvil.player": "%1$s was knocked down by a falling anvil while fighting %2$s", "knockdown.attack.anvil.player": "%1$s was knocked down by a falling anvil while fighting %2$s",
"knockdown.attack.arrow": "%1$s was knocked down due to an arrow fired by %2$s", "knockdown.attack.arrow": "%1$s was knocked down due to an arrow fired by %2$s",
@ -97,6 +99,5 @@
"knockdown.fell.assist.item": "%1$s was doomed to get knocked down by %2$s using %3$s", "knockdown.fell.assist.item": "%1$s was doomed to get knocked down by %2$s using %3$s",
"knockdown.fell.finish": "%1$s fell too far and was knocked down by %2$s", "knockdown.fell.finish": "%1$s fell too far and was knocked down by %2$s",
"knockdown.fell.finish.item": "%1$s fell too far and was knocked down by %2$s using %3$s", "knockdown.fell.finish.item": "%1$s fell too far and was knocked down by %2$s using %3$s",
"knockdown.fell.killer": "%1$s was doomed to get knocked down by a fall", "knockdown.fell.killer": "%1$s was doomed to get knocked down by a fall"
"subtitles.knockdowns.knocked_down": "Player knocked down"
} }

View file

@ -1,4 +1,6 @@
{ {
"subtitles.knockdowns.knocked_down": "Игрок тяжело ранен",
"knockdown.attack.anvil": "%1$s был тяжело ранен упавшей наковальней", "knockdown.attack.anvil": "%1$s был тяжело ранен упавшей наковальней",
"knockdown.attack.anvil.player": "%1$s был тяжело ранен упавшей наковальней, пока сражался с %2$s", "knockdown.attack.anvil.player": "%1$s был тяжело ранен упавшей наковальней, пока сражался с %2$s",
"knockdown.attack.arrow": "%1$s тяжело ранен стрелой %2$s", "knockdown.attack.arrow": "%1$s тяжело ранен стрелой %2$s",
@ -97,6 +99,5 @@
"knockdown.fell.assist.item": "%1$s был тяжело ранен падением благодаря %2$s с помощью %3$s", "knockdown.fell.assist.item": "%1$s был тяжело ранен падением благодаря %2$s с помощью %3$s",
"knockdown.fell.finish": "%1$s упал с высоты и был тяжело ранен %2$s", "knockdown.fell.finish": "%1$s упал с высоты и был тяжело ранен %2$s",
"knockdown.fell.finish.item": "%1$s упал с высоты и был тяжело ранен %2$s с помощью %3$s", "knockdown.fell.finish.item": "%1$s упал с высоты и был тяжело ранен %2$s с помощью %3$s",
"knockdown.fell.killer": "%1$s был тяжело ранен падением", "knockdown.fell.killer": "%1$s был тяжело ранен падением"
"subtitles.knockdowns.knocked_down": "Игрок тяжело ранен"
} }

View file

@ -7,6 +7,8 @@
"client.ClientPlayerEntityMixin" "client.ClientPlayerEntityMixin"
], ],
"mixins": [ "mixins": [
"LivingEntityMixin",
"MobEntityMixin",
"PlayerEntityMixin" "PlayerEntityMixin"
], ],
"injectors": { "injectors": {

View file

@ -1,4 +1 @@
accessWidener v2 named accessWidener v2 named
accessible field net/minecraft/server/world/ThreadedAnvilChunkStorage entityTrackers Lit/unimi/dsi/fastutil/ints/Int2ObjectMap;
accessible field net/minecraft/server/world/ThreadedAnvilChunkStorage$EntityTracker listeners Ljava/util/Set;