尝试自行实现客户端实体动量同步

This commit is contained in:
17146 2025-03-19 18:21:14 +08:00
parent e35c7ff530
commit 867f7a6bde
9 changed files with 89 additions and 82 deletions

View file

@ -178,6 +178,7 @@ public class ModUtils {
addNetworkMessage(SwitchVehicleWeaponMessage.class, SwitchVehicleWeaponMessage::encode, SwitchVehicleWeaponMessage::decode, SwitchVehicleWeaponMessage::handler); addNetworkMessage(SwitchVehicleWeaponMessage.class, SwitchVehicleWeaponMessage::encode, SwitchVehicleWeaponMessage::decode, SwitchVehicleWeaponMessage::handler);
addNetworkMessage(RadarSetTargetMessage.class, RadarSetTargetMessage::encode, RadarSetTargetMessage::decode, RadarSetTargetMessage::handler); addNetworkMessage(RadarSetTargetMessage.class, RadarSetTargetMessage::encode, RadarSetTargetMessage::decode, RadarSetTargetMessage::handler);
addNetworkMessage(ChangeVehicleSeatMessage.class, ChangeVehicleSeatMessage::encode, ChangeVehicleSeatMessage::decode, ChangeVehicleSeatMessage::handler); addNetworkMessage(ChangeVehicleSeatMessage.class, ChangeVehicleSeatMessage::encode, ChangeVehicleSeatMessage::decode, ChangeVehicleSeatMessage::handler);
addNetworkMessage(ClientMotionSyncMessage.class, ClientMotionSyncMessage::encode, ClientMotionSyncMessage::decode, ClientMotionSyncMessage::handler, Optional.of(NetworkDirection.PLAY_TO_CLIENT));
event.enqueueWork(() -> BrewingRecipeRegistry.addRecipe(Ingredient.of(PotionUtils.setPotion(new ItemStack(Items.POTION), Potions.WATER)), event.enqueueWork(() -> BrewingRecipeRegistry.addRecipe(Ingredient.of(PotionUtils.setPotion(new ItemStack(Items.POTION), Potions.WATER)),
Ingredient.of(Items.LIGHTNING_ROD), PotionUtils.setPotion(new ItemStack(Items.POTION), ModPotion.SHOCK.get()))); Ingredient.of(Items.LIGHTNING_ROD), PotionUtils.setPotion(new ItemStack(Items.POTION), ModPotion.SHOCK.get())));

View file

@ -5,6 +5,7 @@ import com.atsuishio.superbwarfare.config.server.ExplosionConfig;
import com.atsuishio.superbwarfare.entity.vehicle.base.VehicleEntity; import com.atsuishio.superbwarfare.entity.vehicle.base.VehicleEntity;
import com.atsuishio.superbwarfare.init.*; import com.atsuishio.superbwarfare.init.*;
import com.atsuishio.superbwarfare.network.message.ClientIndicatorMessage; import com.atsuishio.superbwarfare.network.message.ClientIndicatorMessage;
import com.atsuishio.superbwarfare.network.message.ClientMotionSyncMessage;
import com.atsuishio.superbwarfare.tools.ChunkLoadTool; import com.atsuishio.superbwarfare.tools.ChunkLoadTool;
import com.atsuishio.superbwarfare.tools.CustomExplosion; import com.atsuishio.superbwarfare.tools.CustomExplosion;
import com.atsuishio.superbwarfare.tools.ParticleTool; import com.atsuishio.superbwarfare.tools.ParticleTool;
@ -50,7 +51,7 @@ import software.bernie.geckolib.util.GeckoLibUtil;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
public class CannonShellEntity extends ThrowableItemProjectile implements GeoEntity { public class CannonShellEntity extends ThrowableItemProjectile implements GeoEntity, FastProjectile {
public static final EntityDataAccessor<String> ANIMATION = SynchedEntityData.defineId(CannonShellEntity.class, EntityDataSerializers.STRING); public static final EntityDataAccessor<String> ANIMATION = SynchedEntityData.defineId(CannonShellEntity.class, EntityDataSerializers.STRING);
private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this); private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
@ -255,6 +256,15 @@ public class CannonShellEntity extends ThrowableItemProjectile implements GeoEnt
} }
this.discard(); this.discard();
} }
this.syncMotion();
}
@Override
public void syncMotion() {
if (!this.level().isClientSide) {
ModUtils.PACKET_HANDLER.send(PacketDistributor.ALL.noArg(), new ClientMotionSyncMessage(this));
}
} }
private void causeExplode(Entity entity) { private void causeExplode(Entity entity) {

View file

@ -1,4 +1,6 @@
package com.atsuishio.superbwarfare.entity.projectile; package com.atsuishio.superbwarfare.entity.projectile;
public interface FastProjectile { public interface FastProjectile {
void syncMotion();
} }

View file

@ -9,6 +9,7 @@ import com.atsuishio.superbwarfare.entity.vehicle.base.VehicleEntity;
import com.atsuishio.superbwarfare.init.*; import com.atsuishio.superbwarfare.init.*;
import com.atsuishio.superbwarfare.item.Transcript; import com.atsuishio.superbwarfare.item.Transcript;
import com.atsuishio.superbwarfare.network.message.ClientIndicatorMessage; import com.atsuishio.superbwarfare.network.message.ClientIndicatorMessage;
import com.atsuishio.superbwarfare.network.message.ClientMotionSyncMessage;
import com.atsuishio.superbwarfare.network.message.PlayerGunKillMessage; import com.atsuishio.superbwarfare.network.message.PlayerGunKillMessage;
import com.atsuishio.superbwarfare.tools.*; import com.atsuishio.superbwarfare.tools.*;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -300,6 +301,15 @@ public class ProjectileEntity extends Projectile implements IEntityAdditionalSpa
Math.max(this.getDeltaMovement().length() - 1.1 * this.tickCount, 0.2), true Math.max(this.getDeltaMovement().length() - 1.1 * this.tickCount, 0.2), true
); );
} }
this.syncMotion();
}
@Override
public void syncMotion() {
if (!this.level().isClientSide) {
ModUtils.PACKET_HANDLER.send(PacketDistributor.ALL.noArg(), new ClientMotionSyncMessage(this));
}
} }
@Override @Override

View file

@ -55,9 +55,9 @@ public class ModEntities {
public static final RegistryObject<EntityType<MortarShellEntity>> MORTAR_SHELL = register("projectile_mortar_shell", public static final RegistryObject<EntityType<MortarShellEntity>> MORTAR_SHELL = register("projectile_mortar_shell",
EntityType.Builder.<MortarShellEntity>of(MortarShellEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(true).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(MortarShellEntity::new).sized(0.5f, 0.5f)); EntityType.Builder.<MortarShellEntity>of(MortarShellEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(true).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(MortarShellEntity::new).sized(0.5f, 0.5f));
public static final RegistryObject<EntityType<ProjectileEntity>> PROJECTILE = register("projectile", public static final RegistryObject<EntityType<ProjectileEntity>> PROJECTILE = register("projectile",
EntityType.Builder.<ProjectileEntity>of(ProjectileEntity::new, MobCategory.MISC).setCustomClientFactory(ProjectileEntity::new).setTrackingRange(64).noSave().noSummon().sized(0.25f, 0.25f)); EntityType.Builder.<ProjectileEntity>of(ProjectileEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).setCustomClientFactory(ProjectileEntity::new).setTrackingRange(64).noSave().noSummon().sized(0.25f, 0.25f));
public static final RegistryObject<EntityType<CannonShellEntity>> CANNON_SHELL = register("projectile_cannon_shell", public static final RegistryObject<EntityType<CannonShellEntity>> CANNON_SHELL = register("projectile_cannon_shell",
EntityType.Builder.<CannonShellEntity>of(CannonShellEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(true).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(CannonShellEntity::new).sized(0.5f, 0.5f)); EntityType.Builder.<CannonShellEntity>of(CannonShellEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(CannonShellEntity::new).sized(0.5f, 0.5f));
public static final RegistryObject<EntityType<HandGrenadeEntity>> HAND_GRENADE_ENTITY = register("projectile_hand_grenade_entity", public static final RegistryObject<EntityType<HandGrenadeEntity>> HAND_GRENADE_ENTITY = register("projectile_hand_grenade_entity",
EntityType.Builder.<HandGrenadeEntity>of(HandGrenadeEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(true).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(HandGrenadeEntity::new).sized(0.3f, 0.3f)); EntityType.Builder.<HandGrenadeEntity>of(HandGrenadeEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(true).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(HandGrenadeEntity::new).sized(0.3f, 0.3f));
public static final RegistryObject<EntityType<RgoGrenadeEntity>> RGO_GRENADE = register("projectile_rgo_grenade", public static final RegistryObject<EntityType<RgoGrenadeEntity>> RGO_GRENADE = register("projectile_rgo_grenade",

View file

@ -1,74 +0,0 @@
package com.atsuishio.superbwarfare.mixins;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* From Fast Projectile Fix
* <br>
* Author: Roki27
*/
@Mixin(ClientboundSetEntityMotionPacket.class)
public class ClientboundSetEntityMotionPacketMixin {
@Mutable
@Final
@Shadow
private int id;
@Mutable
@Final
@Shadow
private int xa;
@Mutable
@Final
@Shadow
private int ya;
@Mutable
@Final
@Shadow
private int za;
// TODO 实现仅对 FastProjectile 对象的动量修改
@Inject(method = "<init>(ILnet/minecraft/world/phys/Vec3;)V", at = @At(value = "RETURN"))
public void init(int entityId, Vec3 motion, CallbackInfo ci) {
this.xa = (int) (motion.x * 8000.0D);
this.ya = (int) (motion.y * 8000.0D);
this.za = (int) (motion.z * 8000.0D);
}
@Redirect(method = "<init>(Lnet/minecraft/network/FriendlyByteBuf;)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/network/FriendlyByteBuf;readShort()S"))
public short readShort(FriendlyByteBuf instance) {
return 0;
}
@Inject(method = "<init>(Lnet/minecraft/network/FriendlyByteBuf;)V", at = @At(value = "RETURN"), cancellable = true)
public void read(FriendlyByteBuf buf, CallbackInfo ci) {
ci.cancel();
this.xa = buf.readInt();
this.ya = buf.readInt();
this.za = buf.readInt();
}
@Inject(method = "Lnet/minecraft/network/protocol/game/ClientboundSetEntityMotionPacket;" +
"write(Lnet/minecraft/network/FriendlyByteBuf;)V", at = @At(value = "HEAD"), cancellable = true)
public void write(FriendlyByteBuf buf, CallbackInfo ci) {
ci.cancel();
buf.writeVarInt(this.id);
buf.writeInt(this.xa);
buf.writeInt(this.ya);
buf.writeInt(this.za);
}
}

View file

@ -7,10 +7,7 @@ import com.atsuishio.superbwarfare.config.client.KillMessageConfig;
import com.atsuishio.superbwarfare.event.ClientEventHandler; import com.atsuishio.superbwarfare.event.ClientEventHandler;
import com.atsuishio.superbwarfare.event.KillMessageHandler; import com.atsuishio.superbwarfare.event.KillMessageHandler;
import com.atsuishio.superbwarfare.menu.EnergyMenu; import com.atsuishio.superbwarfare.menu.EnergyMenu;
import com.atsuishio.superbwarfare.network.message.ClientIndicatorMessage; import com.atsuishio.superbwarfare.network.message.*;
import com.atsuishio.superbwarfare.network.message.ContainerDataMessage;
import com.atsuishio.superbwarfare.network.message.GunsDataMessage;
import com.atsuishio.superbwarfare.network.message.RadarMenuOpenMessage;
import com.atsuishio.superbwarfare.tools.GunsTool; import com.atsuishio.superbwarfare.tools.GunsTool;
import com.atsuishio.superbwarfare.tools.PlayerKillRecord; import com.atsuishio.superbwarfare.tools.PlayerKillRecord;
import net.minecraft.client.CameraType; import net.minecraft.client.CameraType;
@ -88,4 +85,15 @@ public class ClientPacketHandler {
Minecraft.getInstance().options.setCameraType(Objects.requireNonNullElse(ClientEventHandler.lastCameraType, CameraType.FIRST_PERSON)); Minecraft.getInstance().options.setCameraType(Objects.requireNonNullElse(ClientEventHandler.lastCameraType, CameraType.FIRST_PERSON));
} }
} }
public static void handleClientSyncMotion(ClientMotionSyncMessage message, Supplier<NetworkEvent.Context> ctx) {
if (ctx.get().getDirection().getReceptionSide() == LogicalSide.CLIENT) {
var level = Minecraft.getInstance().level;
if (level == null) return;
Entity entity = level.getEntity(message.id);
if (entity != null) {
entity.lerpMotion(message.x, message.y, message.z);
}
}
}
} }

View file

@ -0,0 +1,51 @@
package com.atsuishio.superbwarfare.network.message;
import com.atsuishio.superbwarfare.network.ClientPacketHandler;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
public class ClientMotionSyncMessage {
public final int id;
public final double x;
public final double y;
public final double z;
public ClientMotionSyncMessage(Entity entity) {
this(entity.getId(), entity.getDeltaMovement());
}
public ClientMotionSyncMessage(int id, Vec3 motion) {
this.id = id;
this.x = motion.x;
this.y = motion.y;
this.z = motion.z;
}
public static void encode(ClientMotionSyncMessage message, FriendlyByteBuf buffer) {
buffer.writeVarInt(message.id);
buffer.writeDouble(message.x);
buffer.writeDouble(message.y);
buffer.writeDouble(message.z);
}
public static ClientMotionSyncMessage decode(FriendlyByteBuf buffer) {
int id = buffer.readVarInt();
double x = buffer.readDouble();
double y = buffer.readDouble();
double z = buffer.readDouble();
return new ClientMotionSyncMessage(id, new Vec3(x, y, z));
}
public static void handler(ClientMotionSyncMessage message, Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> DistExecutor.unsafeRunWhenOn(Dist.CLIENT,
() -> () -> ClientPacketHandler.handleClientSyncMotion(message, ctx)));
ctx.get().setPacketHandled(true);
}
}

View file

@ -4,7 +4,6 @@
"compatibilityLevel": "JAVA_17", "compatibilityLevel": "JAVA_17",
"refmap": "mixins.superbwarfare.refmap.json", "refmap": "mixins.superbwarfare.refmap.json",
"mixins": [ "mixins": [
"ClientboundSetEntityMotionPacketMixin",
"ClientboundSetPassengersPacketMixin", "ClientboundSetPassengersPacketMixin",
"ExplosionMixin", "ExplosionMixin",
"LivingEntityMixin", "LivingEntityMixin",