diff --git a/build.gradle b/build.gradle index 1097e2e..d825bba 100644 --- a/build.gradle +++ b/build.gradle @@ -147,6 +147,10 @@ repositories { includeGroup("maven.modrinth") } } + maven { + name = "KosmX" + url = "https://maven.kosmx.dev/" + } maven { name = "Fuzs Mod Resources" url = "https://raw.githubusercontent.com/Fuzss/modresources/main/maven/" @@ -176,6 +180,7 @@ dependencies { implementation jarJar(fg.deobf("com.leisuretimedock:jsonem-forge-${minecraft_version}:${jsonem_version}")) { jarJar.ranged(it, "[${jsonem_version},)") } + implementation fg.deobf ("dev.kosmx.player-anim:player-animation-lib-forge:${player_anim_version}") // Example mod dependency with JEI - using fg.deobf() ensures the dependency is remapped to your development mappings diff --git a/gradle.properties b/gradle.properties index 632f6ca..59a52c0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -41,6 +41,7 @@ mod_license=MIT mod_version=1.0.7 jsonem_version=0.2.4 lib39_version=1.20.1-0.0.17 +player_anim_version=1.0.2-rc1+1.20 mod_source=https://github.com/LeisureTimeDock/BlastTravel_Neo_Forge # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. diff --git a/src/main/java/com/leisuretimedock/blasttravelreborn/content/entity/CannonEntity.java b/src/main/java/com/leisuretimedock/blasttravelreborn/content/entity/CannonEntity.java index f3f2fac..180bc49 100644 --- a/src/main/java/com/leisuretimedock/blasttravelreborn/content/entity/CannonEntity.java +++ b/src/main/java/com/leisuretimedock/blasttravelreborn/content/entity/CannonEntity.java @@ -39,6 +39,7 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.javafmlmod.FMLModContainer; import net.minecraftforge.fml.loading.FMLEnvironment; import net.minecraftforge.network.NetworkDirection; import org.jetbrains.annotations.NotNull; @@ -207,9 +208,12 @@ public class CannonEntity extends Entity { } else if (stack.is(Items.CHAIN)) { handlerChain(player, stack); return true; - } else if (CannonBehavior.isValidBehaviorStack(stack)) { + } else if (CannonBehavior.isValidBehaviorStack(stack) && this.getPassengers().isEmpty()) { handlerBehaviorItem(player, stack); return true; + } else if (stack.is(Items.FLINT_AND_STEEL)) { + fireServer(); + return true; } return false; } @@ -217,7 +221,6 @@ public class CannonEntity extends Entity { private void handlerGunPower(Player player, ItemStack stack) { if (!level().isClientSide()) { ItemStack currentGunpowder = inventory.getItem(0); - // 如果炮内已有火药,先还给玩家 if (currentGunpowder.getCount() >= currentGunpowder.getMaxStackSize()) { if (!player.isCreative() && !player.getInventory().add(currentGunpowder)) { @@ -225,11 +228,10 @@ public class CannonEntity extends Entity { player.drop(currentGunpowder, false); } } - // 设置新的火药到炮中 ItemStack newGunpowder = stack.copy(); newGunpowder.setCount(currentGunpowder.getCount() + 1); // 只放一个火药 - ((ServerPlayer) player).sendSystemMessage(Component.literal(String.format("%d / %d", currentGunpowder.getCount(), currentGunpowder.getMaxStackSize())), true); + ((ServerPlayer) player).sendSystemMessage(Component.literal(String.format("%d / %d", currentGunpowder.getCount() + 1, currentGunpowder.getMaxStackSize())), true); inventory.setItem(0, newGunpowder); if (!player.isCreative()) { @@ -242,17 +244,17 @@ public class CannonEntity extends Entity { private void handlerChain(Player player, ItemStack stack) { if (!level().isClientSide()) { - ItemStack currentChain = inventory.getItem(2); - if (currentChain.isEmpty()) { // 添加锁链 + ItemStack currentChain = inventory.getItem(1); + if (currentChain.isEmpty()) { // 添加锁链Pl ItemStack item = stack.copy(); item.setCount(1); - inventory.setItem(2, item); + inventory.setItem(1, item); if (!player.isCreative()) { stack.shrink(1); } level().playSound(null, this.blockPosition(), SoundEvents.CHAIN_PLACE, SoundSource.BLOCKS, 1.0f, 1.0f); } else { - inventory.setItem(2, ItemStack.EMPTY); + inventory.setItem(1, ItemStack.EMPTY); if (!player.isCreative()) { // 移除锁链 if (!player.getInventory().add(currentChain)) { player.drop(currentChain, false); @@ -267,18 +269,41 @@ public class CannonEntity extends Entity { if (!level().isClientSide()) { ItemStack currentBehaviorItem = inventory.getItem(2); if (currentBehaviorItem.isEmpty()) { // 添加物品 - ItemStack item = stack.copy(); - item.setCount(1); - inventory.setItem(2, item); + ItemStack copied = stack.copy(); + copied.setCount(1); + inventory.setItem(2, copied); if (!player.isCreative()) { stack.shrink(1); } level().playSound(null, this.blockPosition(), getBehavior().getPlaceInSound(), SoundSource.BLOCKS, 1.0f, 1.0f); } else { if (stack.is(currentBehaviorItem.getItem())) { //同类物品 +1 + if (stack.getCount() >= currentBehaviorItem.getMaxStackSize()) { + if (!player.isCreative() && !player.getInventory().add(currentBehaviorItem)) { + player.drop(currentBehaviorItem, false); + } + } + ItemStack stack1 = stack.copy(); + stack1.setCount(currentBehaviorItem.getCount() + 1); + ((ServerPlayer) player).sendSystemMessage(Component.literal(String.format("%d / %d", currentBehaviorItem.getCount() + 1, currentBehaviorItem.getMaxStackSize())), true); + inventory.setItem(2, stack1); + if (!player.isCreative()) { + stack.shrink(1); + } + + level().playSound(null, this.blockPosition(), getBehavior().getPlaceInSound(), SoundSource.BLOCKS, 1.0f, 1.0f); } else { //不同类替换 - + if (!player.isCreative() && !player.getInventory().add(currentBehaviorItem)) { + player.drop(currentBehaviorItem, false); + } + ItemStack copied = stack.copy(); + copied.setCount(1); + inventory.setItem(2, copied); + if (!player.isCreative()) { + stack.shrink(1); + } + level().playSound(null, this.blockPosition(), getBehavior().getPlaceInSound(), SoundSource.BLOCKS, 1.0f, 1.0f); } } } @@ -545,6 +570,7 @@ public class CannonEntity extends Entity { return super.getPassengersRidingOffset(); } + @Override protected void defineSynchedData() { this.entityData.define(BEHAVIOR, 0); diff --git a/src/main/java/com/leisuretimedock/blasttravelreborn/mixin/PlayerEntityMixin.java b/src/main/java/com/leisuretimedock/blasttravelreborn/mixin/PlayerEntityMixin.java index 01cec89..06a1369 100644 --- a/src/main/java/com/leisuretimedock/blasttravelreborn/mixin/PlayerEntityMixin.java +++ b/src/main/java/com/leisuretimedock/blasttravelreborn/mixin/PlayerEntityMixin.java @@ -2,10 +2,12 @@ package com.leisuretimedock.blasttravelreborn.mixin; import com.leisuretimedock.blasttravelreborn.BlastTravelReborn; import com.leisuretimedock.blasttravelreborn.network.BTRNetwork; +import com.leisuretimedock.blasttravelreborn.network.toClient.SyncFlightStatePayload; import com.leisuretimedock.blasttravelreborn.network.toServer.StopCannonFlightServerPayload; import com.leisuretimedock.blasttravelreborn.util.PlayerEntityDuck; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.Mth; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.EntityDimensions; @@ -17,6 +19,8 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.entity.EntityTypeTest; import net.minecraft.world.phys.Vec3; +import net.minecraftforge.fml.loading.FMLEnvironment; +import net.minecraftforge.network.NetworkDirection; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -59,6 +63,30 @@ public abstract class PlayerEntityMixin extends LivingEntity implements PlayerEn @Inject(method = "tick", at = @At("HEAD")) private void blasttravel$beginTick(CallbackInfo ci) { this.blasttravel$prevVel = this.isLocalPlayer() ? blasttravel$vel : blasttravel$trackingVel; + if(!FMLEnvironment.production) { + if (blasttravel$inCannonFlight) { + // 获取当前速度 + Vec3 currentVelocity = this.getDeltaMovement(); + double speed = currentVelocity.length(); + + // 记录到对应的速度列表 + if (this.isLocalPlayer()) { + // 客户端本地玩家 + blasttravel$vel = currentVelocity; + System.out.println("[Client-Local] Tick: " + this.level().getGameTime() + + ", Velocity: " + currentVelocity + + ", Speed: " + String.format("%.3f", speed) + + ", Position: " + this.position()); + } else { + System.out.println("[Server] Tick: " + this.level().getGameTime() + + ", Velocity: " + currentVelocity + + ", Speed: " + String.format("%.3f", speed) + + ", Position: " + String.format("(%.1f, %.1f, %.1f)", this.getX(), this.getY(), this.getZ()) + + ", OnGround: " + this.onGround() + + ", noPhysics: " + this.noPhysics); + } + } + } } @Inject(method = "tick", at = @At("TAIL")) @@ -79,6 +107,10 @@ public abstract class PlayerEntityMixin extends LivingEntity implements PlayerEn entity.hurt(source, (float)(vel.length() * 4)); } } + try { + ServerPlayer cast = ServerPlayer.class.cast(self); + BTRNetwork.CHANNEL.sendTo(new SyncFlightStatePayload(self.getId(), vel, self.position(), true, noPhysics), cast.connection.connection, NetworkDirection.PLAY_TO_CLIENT); + } catch(Exception ignored){} } if (self.isLocalPlayer() && diff --git a/src/main/java/com/leisuretimedock/blasttravelreborn/network/BTRNetwork.java b/src/main/java/com/leisuretimedock/blasttravelreborn/network/BTRNetwork.java index 19fd6d2..f5f38d6 100644 --- a/src/main/java/com/leisuretimedock/blasttravelreborn/network/BTRNetwork.java +++ b/src/main/java/com/leisuretimedock/blasttravelreborn/network/BTRNetwork.java @@ -3,6 +3,7 @@ package com.leisuretimedock.blasttravelreborn.network; import com.leisuretimedock.blasttravelreborn.BlastTravelReborn; import com.leisuretimedock.blasttravelreborn.network.toClient.FireCannonPayload; import com.leisuretimedock.blasttravelreborn.network.toClient.StopCannonFlightClientPayload; +import com.leisuretimedock.blasttravelreborn.network.toClient.SyncFlightStatePayload; import com.leisuretimedock.blasttravelreborn.network.toServer.RequestFirePayload; import com.leisuretimedock.blasttravelreborn.network.toServer.StopCannonFlightServerPayload; import net.minecraftforge.network.NetworkDirection; @@ -43,6 +44,13 @@ public class BTRNetwork { .decoder(RequestFirePayload::read) .consumerNetworkThread(RequestFirePayload::handle) .add(); + CHANNEL + .messageBuilder(SyncFlightStatePayload.class, getIDAndIncrease(), NetworkDirection.PLAY_TO_CLIENT) + .encoder(SyncFlightStatePayload::write) + .decoder(SyncFlightStatePayload::read) + .consumerNetworkThread(SyncFlightStatePayload::handle) + .add(); + } private static int getIDAndIncrease() { return ++ID; diff --git a/src/main/java/com/leisuretimedock/blasttravelreborn/network/toClient/FireCannonPayload.java b/src/main/java/com/leisuretimedock/blasttravelreborn/network/toClient/FireCannonPayload.java index 3555f26..d73b46a 100644 --- a/src/main/java/com/leisuretimedock/blasttravelreborn/network/toClient/FireCannonPayload.java +++ b/src/main/java/com/leisuretimedock/blasttravelreborn/network/toClient/FireCannonPayload.java @@ -42,6 +42,7 @@ public record FireCannonPayload(int cannonId, Optional launchedId, doub if (context.getNetworkManager().getPacketListener() instanceof ClientPacketListener clientPacketListener) { context.enqueueWork(() -> { if (hasPlayer()) { + //noinspection OptionalGetWithoutIsPresent if (clientPacketListener.getLevel().getEntity(launchedId.get()) instanceof Player launchedPlayer) { // 记录发射前状态 if (!FMLEnvironment.production) { @@ -56,7 +57,6 @@ public record FireCannonPayload(int cannonId, Optional launchedId, doub // 直接设置速度,不要延迟 launchedPlayer.setDeltaMovement(velocityX, velocityY, velocityZ); - launchedPlayer.hurtMarked = true; // 强制同步运动 ((PlayerEntityDuck) launchedPlayer).blasttravel$setCannonFlight(true); // 记录发射后状态 diff --git a/src/main/java/com/leisuretimedock/blasttravelreborn/network/toClient/SyncFlightStatePayload.java b/src/main/java/com/leisuretimedock/blasttravelreborn/network/toClient/SyncFlightStatePayload.java new file mode 100644 index 0000000..04c1a40 --- /dev/null +++ b/src/main/java/com/leisuretimedock/blasttravelreborn/network/toClient/SyncFlightStatePayload.java @@ -0,0 +1,80 @@ +package com.leisuretimedock.blasttravelreborn.network.toClient; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.fml.loading.FMLEnvironment; +import net.minecraftforge.network.NetworkEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Supplier; + +public record SyncFlightStatePayload( + int playerId, + Vec3 velocity, + Vec3 position, + boolean inCannonFlight, + boolean noPhysics +) { + + public SyncFlightStatePayload(@NotNull FriendlyByteBuf buf) { + this( + buf.readInt(), + new Vec3(buf.readDouble(), buf.readDouble(), buf.readDouble()), + new Vec3(buf.readDouble(), buf.readDouble(), buf.readDouble()), + buf.readBoolean(), + buf.readBoolean() + ); + } + + public void write(@NotNull FriendlyByteBuf buf) { + buf.writeInt(playerId); + buf.writeDouble(velocity.x); + buf.writeDouble(velocity.y); + buf.writeDouble(velocity.z); + buf.writeDouble(position.x); + buf.writeDouble(position.y); + buf.writeDouble(position.z); + buf.writeBoolean(inCannonFlight); + buf.writeBoolean(noPhysics); + } + public static SyncFlightStatePayload read(FriendlyByteBuf buf) { + return new SyncFlightStatePayload(buf); + } + + public void handle(Supplier context) { + context.get().enqueueWork(() -> { + // 客户端处理逻辑 + net.minecraft.client.Minecraft client = net.minecraft.client.Minecraft.getInstance(); + if (client.level != null) { + net.minecraft.world.entity.Entity entity = client.level.getEntity(this.playerId); + if (entity instanceof net.minecraft.world.entity.player.Player player) { + // 同步速度 + player.setDeltaMovement(this.velocity); + + // 同步位置(如果差异较大) + if (player.position().distanceTo(this.position) > 0.1) { + player.setPos(this.position); + } + + // 同步飞行状态 + if (player instanceof com.leisuretimedock.blasttravelreborn.util.PlayerEntityDuck duck) { + duck.blasttravel$setCannonFlight(this.inCannonFlight); + } + + player.noPhysics = this.noPhysics; + player.hurtMarked = true; + + // 调试输出 + if (!FMLEnvironment.production) { + System.out.println("[Client] Received flight sync: " + + "Vel=" + this.velocity + ", " + + "Pos=" + this.position + ", " + + "Flight=" + this.inCannonFlight + ", " + + "NoPhysics=" + this.noPhysics); + } + } + } + }); + context.get().setPacketHandled(true); + } +} \ No newline at end of file