diff --git a/build.gradle b/build.gradle index ec0e715..030c93d 100644 --- a/build.gradle +++ b/build.gradle @@ -30,9 +30,18 @@ targetCompatibility = 1.8 repositories { mavenCentral() - maven { url 'https://libraries.minecraft.net/' } - maven { url 'https://www.dimdev.org/maven/' } - maven { url = 'https://repo.spongepowered.org/maven/' } + maven { + name = "Minecraft" + url = "https://libraries.minecraft.net/" + } + maven { + name = "Dimensional Development" + url = "https://www.dimdev.org/maven/" + } + maven { + name = "Sponge" + url = "https://repo.spongepowered.org/maven/" + } } dependencies { @@ -59,9 +68,9 @@ mixin { } sourceSets { - main debug { - compileClasspath += main.compileClasspath + compileClasspath += main.compileClasspath + main.output + runtimeClasspath += main.runtimeClasspath } } diff --git a/src/debug/java/org/dimdev/testmod/TestMod.java b/src/debug/java/org/dimdev/testmod/TestMod.java index 343944b..b207637 100644 --- a/src/debug/java/org/dimdev/testmod/TestMod.java +++ b/src/debug/java/org/dimdev/testmod/TestMod.java @@ -7,7 +7,12 @@ import net.minecraft.block.material.Material; import net.minecraft.client.Minecraft; import net.minecraft.client.audio.MusicTicker; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.entity.RenderSprite; import net.minecraft.command.CommandSource; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; import net.minecraft.fluid.FlowingFluid; import net.minecraft.fluid.Fluid; import net.minecraft.item.EnumDyeColor; @@ -23,23 +28,29 @@ import org.dimdev.rift.listener.*; import org.dimdev.rift.listener.client.AmbientMusicTypeProvider; import org.dimdev.rift.listener.client.ClientTickable; +import org.dimdev.rift.listener.client.EntityRendererAdder; import org.dimdev.rift.listener.client.TextureAdder; import org.dimdev.rift.network.Message; +import org.dimdev.testmod.entity.EntityGrenade; +import org.dimdev.testmod.item.ItemGrenade; import java.util.Collection; import java.util.Collections; +import java.util.Map; import java.util.Set; import static net.minecraft.init.SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP; -public class TestMod implements BlockAdder, ItemAdder, FluidAdder, TextureAdder, PacketAdder, CommandAdder, ClientTickable, /*AmbientMusicTypeProvider,*/ DimensionTypeAdder, MessageAdder { +public class TestMod implements BlockAdder, ItemAdder, FluidAdder, TextureAdder, PacketAdder, CommandAdder, ClientTickable, /*AmbientMusicTypeProvider,*/ DimensionTypeAdder, MessageAdder, EntityTypeAdder, EntityRendererAdder, EntityTrackerAdder { + public static EntityType ENTITY_TYPE_GRENADE; private static final Logger LOGGER = LogManager.getLogger(); public static final Block WHITE_BLOCK = new Block(Block.Builder.create(Material.ROCK)); public static final Block TRANSLUCENT_WHITE_BLOCK = new BlockStainedGlass(EnumDyeColor.WHITE, Block.Builder.create(Material.GLASS)); public static final FlowingFluid WHITE_FLUID = new WhiteFluid.Source(); public static final FlowingFluid FLOWING_WHITE_FLUID = new WhiteFluid.Flowing(); public static final BlockFlowingFluid BLOCK_WHITE_FLUID = new BlockFlowingFluid(WHITE_FLUID, Block.Builder.create(Material.WATER).doesNotBlockMovement().hardnessAndResistance(100F, 100F).variableOpacity()); - public static final Item PACKET_TESTER = new ItemPacketTester(new Item.Builder()); + public static final Item PACKET_TESTER = new ItemPacketTester(new Item.Builder().group(ItemGroup.TOOLS)); + public static final Item GRENADE = new ItemGrenade(new Item.Builder().group(ItemGroup.COMBAT).maxStackSize(8)); public static final MusicTicker.MusicType TEST_MUSIC = AmbientMusicTypeProvider.newMusicType("test", ENTITY_EXPERIENCE_ORB_PICKUP, 0, 0); private int clientTickCount = 0; @@ -55,6 +66,7 @@ public void registerItems() { Item.registerItemBlock(WHITE_BLOCK, ItemGroup.BUILDING_BLOCKS); Item.registerItemBlock(TRANSLUCENT_WHITE_BLOCK, ItemGroup.BUILDING_BLOCKS); Item.registerItem(new ResourceLocation("testmod", "packet_tester"), PACKET_TESTER); + Item.register(new ResourceLocation("testmod", "grenade"), GRENADE); } @Override @@ -108,5 +120,20 @@ public Set getDimensionTypes() { @Override public void registerMessages(RegistryNamespaced> registry) { registry.putObject(new ResourceLocation("testmod", "test_message"), TestMessage.class); + + @Override + public void registerEntityTypes() { + ENTITY_TYPE_GRENADE = EntityType.register("testmod:grenade", EntityType.Builder.create(EntityGrenade.class, EntityGrenade::new)); + } + + @Override + public void addEntityRenderers(Map, Render> entityRenderMap, RenderManager renderManager) { + entityRenderMap.put(EntityGrenade.class, new RenderSprite<>(renderManager, GRENADE, Minecraft.getMinecraft().getRenderItem())); + } + + @Override + public void addEntityTrackerInfo(Map entityTrackers) { + entityTrackers.put(ENTITY_TYPE_GRENADE, new EntityTrackerInfo(64, 20, true)); + entityTrackers.put(EntityType.ARROW, new EntityTrackerInfo(128, 20, false)); //test substitution of vanilla trackers } } diff --git a/src/debug/java/org/dimdev/testmod/entity/EntityGrenade.java b/src/debug/java/org/dimdev/testmod/entity/EntityGrenade.java new file mode 100644 index 0000000..97eb7df --- /dev/null +++ b/src/debug/java/org/dimdev/testmod/entity/EntityGrenade.java @@ -0,0 +1,31 @@ +package org.dimdev.testmod.entity; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.projectile.EntityThrowable; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; +import org.dimdev.testmod.TestMod; + +public class EntityGrenade extends EntityThrowable { + + public EntityGrenade(World world) { + super(TestMod.ENTITY_TYPE_GRENADE, world); + } + + public EntityGrenade(World world, EntityLivingBase shooter) { + super(TestMod.ENTITY_TYPE_GRENADE, shooter, world); + } + + public EntityGrenade(World world, double x, double y, double z) { + super(TestMod.ENTITY_TYPE_GRENADE, x, y, z, world); + } + + @Override + protected void onImpact(RayTraceResult result) { + if(this.world.isRemote()) { + this.world.createExplosion(this, this.posX, this.posY, this.posZ, 12.0F, false); + } + this.setDead(); + } + +} diff --git a/src/debug/java/org/dimdev/testmod/item/ItemGrenade.java b/src/debug/java/org/dimdev/testmod/item/ItemGrenade.java new file mode 100644 index 0000000..e35b0cb --- /dev/null +++ b/src/debug/java/org/dimdev/testmod/item/ItemGrenade.java @@ -0,0 +1,38 @@ +package org.dimdev.testmod.item; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.stats.StatList; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; +import net.minecraft.util.SoundCategory; +import net.minecraft.world.World; +import org.dimdev.testmod.entity.EntityGrenade; + +public class ItemGrenade extends Item { + + public ItemGrenade(Builder builder) { + super(builder); + } + + @Override + public ActionResult onItemRightClick(World world, EntityPlayer player, EnumHand hand) { + ItemStack stack = player.getHeldItem(hand); + if (!player.capabilities.isCreativeMode) { + stack.shrink(1); + } + + world.playSound(null, player.posX, player.posY, player.posZ, SoundEvents.ENTITY_SNOWBALL_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); + if (!world.isRemote) { + EntityGrenade grenade = new EntityGrenade(world, player); + grenade.shoot(player, player.rotationPitch, player.rotationYaw, 0.0F, 1.5F, 1.0F); + world.spawnEntity(grenade); + } + + player.addStat(StatList.ITEM_USED.get(this)); + return new ActionResult<>(EnumActionResult.SUCCESS, stack); + } +} diff --git a/src/debug/resources/assets/testmod/lang/en_us.json b/src/debug/resources/assets/testmod/lang/en_us.json index 470b156..02dd717 100644 --- a/src/debug/resources/assets/testmod/lang/en_us.json +++ b/src/debug/resources/assets/testmod/lang/en_us.json @@ -2,5 +2,6 @@ "block.testmod.white_block": "White Block", "block.testmod.translucent_white_block": "Translucent White Block", "item.testmod.white_block": "White Block", - "item.testmod.translucent_white_block": "Translucent White Block" + "item.testmod.translucent_white_block": "Translucent White Block", + "item.testmod.grenade": "Grenade" } diff --git a/src/debug/resources/assets/testmod/models/item/grenade.json b/src/debug/resources/assets/testmod/models/item/grenade.json new file mode 100644 index 0000000..794c83b --- /dev/null +++ b/src/debug/resources/assets/testmod/models/item/grenade.json @@ -0,0 +1,3 @@ +{ + "parent": "block/tnt" +} \ No newline at end of file diff --git a/src/debug/resources/assets/testmod/models/item/packet_tester.json b/src/debug/resources/assets/testmod/models/item/packet_tester.json new file mode 100644 index 0000000..cad9f6e --- /dev/null +++ b/src/debug/resources/assets/testmod/models/item/packet_tester.json @@ -0,0 +1,3 @@ +{ + "parent": "item/bone" +} \ No newline at end of file diff --git a/src/main/java/org/dimdev/rift/entity/EntityTrackerRegistry.java b/src/main/java/org/dimdev/rift/entity/EntityTrackerRegistry.java new file mode 100644 index 0000000..26730b6 --- /dev/null +++ b/src/main/java/org/dimdev/rift/entity/EntityTrackerRegistry.java @@ -0,0 +1,16 @@ +package org.dimdev.rift.entity; + +import net.minecraft.entity.EntityType; +import org.dimdev.rift.listener.EntityTrackerAdder; +import org.dimdev.riftloader.RiftLoader; + +import java.util.HashMap; +import java.util.Map; + +public class EntityTrackerRegistry { + public static final Map TRACKER_INFO = new HashMap<>(); + + public static void buildTrackerInfo() { + RiftLoader.instance.getListeners(EntityTrackerAdder.class).forEach(trackerAdder -> trackerAdder.addEntityTrackerInfo(TRACKER_INFO)); + } +} diff --git a/src/main/java/org/dimdev/rift/entity/IEntityExtraSpawnData.java b/src/main/java/org/dimdev/rift/entity/IEntityExtraSpawnData.java new file mode 100644 index 0000000..42e0acf --- /dev/null +++ b/src/main/java/org/dimdev/rift/entity/IEntityExtraSpawnData.java @@ -0,0 +1,13 @@ +package org.dimdev.rift.entity; + +import net.minecraft.network.PacketBuffer; + +/** + * used to de/serialize custom data on entity spawn + */ +public interface IEntityExtraSpawnData { + + void read(PacketBuffer buffer) throws Exception; + + void write(PacketBuffer buffer) throws Exception; +} diff --git a/src/main/java/org/dimdev/rift/listener/EntityTrackerAdder.java b/src/main/java/org/dimdev/rift/listener/EntityTrackerAdder.java new file mode 100644 index 0000000..8af5d55 --- /dev/null +++ b/src/main/java/org/dimdev/rift/listener/EntityTrackerAdder.java @@ -0,0 +1,37 @@ +package org.dimdev.rift.listener; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; + +import java.util.Map; + +@Deprecated //TODO make this a static register() call that is to be called when adding the type! +public interface EntityTrackerAdder { + + void addEntityTrackerInfo(Map entityTrackers); + + class EntityTrackerInfo { + + private final int trackingRange; + private final int updateFrequency; + private final boolean sendVelocityUpdates; + + public EntityTrackerInfo(int trackingRange, int updateFrequency, boolean sendVelocityUpdates) { + this.trackingRange = trackingRange; + this.updateFrequency = updateFrequency; + this.sendVelocityUpdates = sendVelocityUpdates; + } + + public int getTrackingRange() { + return trackingRange; + } + + public int getUpdateFrequency() { + return updateFrequency; + } + + public boolean shouldSendVelocityUpdates() { + return sendVelocityUpdates; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/dimdev/rift/listener/EntityTypeAdder.java b/src/main/java/org/dimdev/rift/listener/EntityTypeAdder.java index 0546c47..d31efda 100644 --- a/src/main/java/org/dimdev/rift/listener/EntityTypeAdder.java +++ b/src/main/java/org/dimdev/rift/listener/EntityTypeAdder.java @@ -1,5 +1,6 @@ package org.dimdev.rift.listener; +@Deprecated public interface EntityTypeAdder { void registerEntityTypes(); } diff --git a/src/main/java/org/dimdev/rift/mixin/hook/MixinEntityTracker.java b/src/main/java/org/dimdev/rift/mixin/hook/MixinEntityTracker.java new file mode 100644 index 0000000..f8f93d8 --- /dev/null +++ b/src/main/java/org/dimdev/rift/mixin/hook/MixinEntityTracker.java @@ -0,0 +1,24 @@ +package org.dimdev.rift.mixin.hook; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityTracker; +import org.dimdev.rift.entity.EntityTrackerRegistry; +import org.dimdev.rift.listener.EntityTrackerAdder; +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(EntityTracker.class) +public class MixinEntityTracker { + + @SuppressWarnings("ConstantConditions") + @Inject(method = "track", at = @At("HEAD"), cancellable = true) + public void trackEntity(Entity entityIn, CallbackInfo ci) { + if(EntityTrackerRegistry.TRACKER_INFO.containsKey(entityIn.getType())) { + EntityTrackerAdder.EntityTrackerInfo info = EntityTrackerRegistry.TRACKER_INFO.get(entityIn.getType()); + ((EntityTracker) (Object) this).track(entityIn, info.getTrackingRange(), info.getUpdateFrequency(), info.shouldSendVelocityUpdates()); + ci.cancel(); + } + } +} diff --git a/src/main/java/org/dimdev/rift/mixin/hook/MixinEntityTrackerEntry.java b/src/main/java/org/dimdev/rift/mixin/hook/MixinEntityTrackerEntry.java new file mode 100644 index 0000000..8c42ef6 --- /dev/null +++ b/src/main/java/org/dimdev/rift/mixin/hook/MixinEntityTrackerEntry.java @@ -0,0 +1,35 @@ +package org.dimdev.rift.mixin.hook; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityTrackerEntry; +import net.minecraft.entity.EntityType; +import net.minecraft.network.EnumPacketDirection; +import net.minecraft.network.Packet; +import net.minecraft.util.ResourceLocation; +import org.dimdev.rift.network.message.MessageSpawnEntity; +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; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(EntityTrackerEntry.class) +public class MixinEntityTrackerEntry { + + @Shadow + @Final + private Entity trackedEntity; + + @Inject(method = "createSpawnPacket", at = @At("INVOKE"), cancellable = true) + private void createCustomSpawnPacket(CallbackInfoReturnable> info) { + if(!this.trackedEntity.isDead) { + EntityType type = this.trackedEntity.getType(); + ResourceLocation typeName = EntityType.getId(type); + if(!typeName.getNamespace().equals("minecraft")) { + info.setReturnValue(new MessageSpawnEntity(this.trackedEntity).toPacket(EnumPacketDirection.CLIENTBOUND)); + } + } + } + +} diff --git a/src/main/java/org/dimdev/rift/mixin/hook/MixinEntityType.java b/src/main/java/org/dimdev/rift/mixin/hook/MixinEntityType.java index 4528aae..96a4979 100644 --- a/src/main/java/org/dimdev/rift/mixin/hook/MixinEntityType.java +++ b/src/main/java/org/dimdev/rift/mixin/hook/MixinEntityType.java @@ -9,6 +9,7 @@ import net.minecraft.world.World; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dimdev.rift.entity.EntityTrackerRegistry; import org.dimdev.rift.listener.EntityTypeAdder; import org.dimdev.riftloader.RiftLoader; import org.spongepowered.asm.mixin.Final; @@ -24,6 +25,7 @@ public abstract class MixinEntityType { for (EntityTypeAdder entityTypeAdder : RiftLoader.instance.getListeners(EntityTypeAdder.class)) { entityTypeAdder.registerEntityTypes(); } + EntityTrackerRegistry.buildTrackerInfo(); } @Mixin(EntityType.Builder.class) diff --git a/src/main/java/org/dimdev/rift/mixin/hook/client/MixinNetHandlerPlayClient.java b/src/main/java/org/dimdev/rift/mixin/hook/client/MixinNetHandlerPlayClient.java index d185b6c..e3de687 100644 --- a/src/main/java/org/dimdev/rift/mixin/hook/client/MixinNetHandlerPlayClient.java +++ b/src/main/java/org/dimdev/rift/mixin/hook/client/MixinNetHandlerPlayClient.java @@ -31,17 +31,32 @@ @Mixin(NetHandlerPlayClient.class) public class MixinNetHandlerPlayClient { - @Shadow private WorldClient world; - @Shadow @Final private GameProfile profile; - @Shadow private Minecraft client; - @Shadow @Final private RecipeManager recipeManager; - @Shadow @Final private ClientAdvancementManager advancementManager; - @Shadow @Final private NetworkManager netManager; - @Shadow private CommandDispatcher commandDispatcher; - @Shadow @Final private ClientSuggestionProvider clientSuggestionProvider; - @Shadow private NetworkTagManager networkTagManager; - @Shadow private NBTQueryManager nbtQueryManager; + @Shadow + private WorldClient world; + @Shadow + @Final + private GameProfile profile; + @Shadow + private Minecraft client; + @Shadow + @Final + private RecipeManager recipeManager; + @Shadow + @Final + private ClientAdvancementManager advancementManager; + @Shadow + @Final + private NetworkManager netManager; + @Shadow + private CommandDispatcher commandDispatcher; + @Shadow + @Final + private ClientSuggestionProvider clientSuggestionProvider; + @Shadow + private NetworkTagManager networkTagManager; + @Shadow + private NBTQueryManager nbtQueryManager; @SuppressWarnings("deprecation") @Inject(method = "handleCustomPayload", at = @At("HEAD"), cancellable = true) @@ -49,14 +64,14 @@ private void handleModCustomPayload(SPacketCustomPayload packet, CallbackInfo ci ResourceLocation channelName = packet.getChannelName(); PacketBuffer data = packet.getBufferData(); - for (CustomPayloadHandler customPayloadHandler : RiftLoader.instance.getListeners(CustomPayloadHandler.class)) { - if (customPayloadHandler.clientHandlesChannel(channelName)) { + for(CustomPayloadHandler customPayloadHandler : RiftLoader.instance.getListeners(CustomPayloadHandler.class)) { + if(customPayloadHandler.clientHandlesChannel(channelName)) { customPayloadHandler.clientHandleCustomPayload(channelName, data); } } Class messageClass = Message.REGISTRY.get(channelName); - if (messageClass != null) { + if(messageClass != null) { try { Message message = RiftLoader.instance.newInstance(messageClass); message.read(data); @@ -71,7 +86,7 @@ private void handleModCustomPayload(SPacketCustomPayload packet, CallbackInfo ci networkTagManager, nbtQueryManager )); - } catch (ReflectiveOperationException e) { + } catch(ReflectiveOperationException e) { throw new RuntimeException("Error creating " + messageClass, e); } ci.cancel(); @@ -81,13 +96,13 @@ private void handleModCustomPayload(SPacketCustomPayload packet, CallbackInfo ci @Inject(method = "handleUpdateTileEntity", at = @At("RETURN")) private void handleUpdateModTileEntity(SPacketUpdateTileEntity packet, CallbackInfo ci) { TileEntity tileEntity = world.getTileEntity(packet.getPos()); - if (tileEntity == null || packet.getNbtCompound() == null || !packet.getNbtCompound().hasKey("id", 8)) { + if(tileEntity == null || packet.getNbtCompound() == null || !packet.getNbtCompound().hasKey("id", 8)) { return; } ResourceLocation tileEntityId = TileEntityType.getId(tileEntity.getType()); ResourceLocation packetId = new ResourceLocation(packet.getNbtCompound().getString("id")); - if (packetId != null && !packetId.getNamespace().equals("minecraft") && packetId.equals(tileEntityId)) { + if(!packetId.getNamespace().equals("minecraft") && packetId.equals(tileEntityId)) { tileEntity.readFromNBT(packet.getNbtCompound()); } } diff --git a/src/main/java/org/dimdev/rift/network/MessageRegistrator.java b/src/main/java/org/dimdev/rift/network/MessageRegistrator.java index 3c8f741..5be1ead 100644 --- a/src/main/java/org/dimdev/rift/network/MessageRegistrator.java +++ b/src/main/java/org/dimdev/rift/network/MessageRegistrator.java @@ -1,12 +1,15 @@ package org.dimdev.rift.network; +import net.minecraft.util.ResourceLocation; import org.dimdev.rift.listener.BootstrapListener; import org.dimdev.rift.listener.MessageAdder; +import org.dimdev.rift.network.message.MessageSpawnEntity; import org.dimdev.riftloader.RiftLoader; public class MessageRegistrator implements BootstrapListener { @Override public void afterVanillaBootstrap() { + Message.REGISTRY.put(new ResourceLocation("rift:entity/spawn"), MessageSpawnEntity.class); for (MessageAdder messageAdder : RiftLoader.instance.getListeners(MessageAdder.class)) { messageAdder.registerMessages(Message.REGISTRY); } diff --git a/src/main/java/org/dimdev/rift/network/message/MessageSpawnEntity.java b/src/main/java/org/dimdev/rift/network/message/MessageSpawnEntity.java new file mode 100644 index 0000000..43a507e --- /dev/null +++ b/src/main/java/org/dimdev/rift/network/message/MessageSpawnEntity.java @@ -0,0 +1,162 @@ +package org.dimdev.rift.network.message; + +import io.netty.buffer.Unpooled; +import net.minecraft.client.multiplayer.WorldClient; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.EntityTracker; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.projectile.EntityThrowable; +import net.minecraft.network.PacketBuffer; +import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.util.math.MathHelper; +import org.dimdev.rift.network.ClientMessageContext; +import org.dimdev.rift.entity.IEntityExtraSpawnData; +import org.dimdev.rift.network.Message; +import org.dimdev.riftloader.RiftLoader; +import org.dimdev.utils.ReflectionUtils; + +import java.io.IOException; +import java.util.List; +import java.util.UUID; + +public class MessageSpawnEntity extends Message { + + private Entity entity; + private int entityID; + private UUID entityUUID; + private EntityType entityType; + private double x, y, z; + private float yaw, pitch, yawHead; + private double velocityX, velocityY, velocityZ; + private List> dataEntries; + private int throwerID; + private PacketBuffer packetBuffer; + + @SuppressWarnings("WeakerAccess") + public MessageSpawnEntity() { + //NO-OP + } + + public MessageSpawnEntity(Entity entity) { + this(); + this.entity = entity; + } + + @SuppressWarnings("ConstantConditions") + @Override + public void write(PacketBuffer buffer) { + buffer.writeVarInt(entity.getEntityId()); + buffer.writeUniqueId(entity.getUniqueID()); + buffer.writeResourceLocation(EntityType.getId(entity.getType())); + buffer.writeDouble(entity.posX); + buffer.writeDouble(entity.posY); + buffer.writeDouble(entity.posZ); + buffer.writeByte((byte)(entity.rotationYaw * 256.0F / 360.0F)); + buffer.writeByte((byte) (entity.rotationPitch * 256.0F / 360.0F)); + if (entity instanceof EntityLivingBase) buffer.writeByte((byte) (((EntityLivingBase)entity).rotationYawHead * 256.0F / 360.0F)); + else buffer.writeByte(0); + + if(entity instanceof EntityThrowable) { + EntityLivingBase thrower = ((EntityThrowable) entity).getThrower(); + buffer.writeVarInt(thrower != null ? thrower.getEntityId() : entity.getEntityId()); + double maxVelocity = 3.9D; + buffer.writeInt((int) (MathHelper.clamp(entity.motionX, -maxVelocity, maxVelocity) * 8000.0D)); + buffer.writeInt((int) (MathHelper.clamp(entity.motionY, -maxVelocity, maxVelocity) * 8000.0D)); + buffer.writeInt((int) (MathHelper.clamp(entity.motionZ, -maxVelocity, maxVelocity) * 8000.0D)); + } + else buffer.writeVarInt(0); + + //write DataWatcher + { + PacketBuffer tmp = new PacketBuffer(Unpooled.buffer()); + try { + entity.getDataManager().writeEntries(tmp); + } catch(Exception e) { + RiftLoader.getLogger().fatal("unable to write entity spawn data watchers", e); + throw new RuntimeException(e); + } + buffer.writeBytes(tmp); + } + + //write extra data + if(entity instanceof IEntityExtraSpawnData) { + PacketBuffer tmp = new PacketBuffer(Unpooled.buffer()); + try { + ((IEntityExtraSpawnData) entity).write(tmp); + } + catch(Exception e) { + RiftLoader.getLogger().fatal("unable to write entity additional spawn data", e); + throw new RuntimeException(e); + } + buffer.writeBytes(tmp); + } + } + + @Override + public void read(PacketBuffer buffer) { + this.entityID = buffer.readVarInt(); + this.entityUUID = buffer.readUniqueId(); + this.entityType = EntityType.REGISTRY.get(buffer.readResourceLocation()); + x = buffer.readDouble(); + y = buffer.readDouble(); + z = buffer.readDouble(); + yaw = buffer.readByte() * 360F / 256F; + pitch = buffer.readByte() * 360F / 256F; + yawHead = buffer.readByte() * 360F / 256F; + + throwerID = buffer.readVarInt(); + if(throwerID != 0) { + velocityX = buffer.readInt() / 8000.0D; + velocityY = buffer.readInt() / 8000.0D; + velocityZ = buffer.readInt() / 8000.0D; + } + + try { + dataEntries = EntityDataManager.readEntries(buffer); + } catch(IOException e) { + RiftLoader.getLogger().fatal("unable to read entity spawn data watchers", e); + throw new RuntimeException(e); + } + packetBuffer = new PacketBuffer(buffer.retain()); + } + + @Override + public void process(ClientMessageContext context) { + WorldClient world = context.getClient().world; + entity = entityType.create(world); + if(entity != null) { + entity.setPosition(x, y, z); + EntityTracker.updateServerPosition(entity, x, y, z); + Entity[] parts = entity.getParts(); + if(parts != null) { + for(Entity part : parts) { + part.setEntityId(entityID); + } + } + entity.setEntityId(entityID); + entity.setUniqueId(entityUUID); + world.addEntityToWorld(entityID, entity); + entity.rotationYaw = yaw; + entity.rotationPitch = pitch; + entity.setRotationYawHead(yawHead); + if(entity instanceof EntityThrowable) { + Entity thrower = world.getEntityByID(throwerID); + if(thrower instanceof EntityLivingBase) ReflectionUtils.setPrivateValue(EntityThrowable.class, (EntityThrowable) entity, thrower, "c", "field_70192_c", "thrower"); + entity.motionX = velocityX; + entity.motionY = velocityY; + entity.motionZ = velocityZ; + } + entity.getDataManager().setEntryValues(dataEntries); + if(entity instanceof IEntityExtraSpawnData) { + try { + ((IEntityExtraSpawnData) entity).read(packetBuffer); + } catch(Exception e) { + RiftLoader.getLogger().fatal("unable to read entity additional spawn data", e); + throw new RuntimeException(e); + } + } + } + else RiftLoader.getLogger().error("discarding spawn packet for null entity!"); + } +} diff --git a/src/main/java/org/dimdev/riftloader/RiftLoader.java b/src/main/java/org/dimdev/riftloader/RiftLoader.java index 8facf0e..cca2c0c 100644 --- a/src/main/java/org/dimdev/riftloader/RiftLoader.java +++ b/src/main/java/org/dimdev/riftloader/RiftLoader.java @@ -27,6 +27,11 @@ public class RiftLoader { public static final RiftLoader instance = new RiftLoader(); + + public static Logger getLogger() { + return log; + } + private static final Logger log = LogManager.getLogger("RiftLoader"); public final File modsDir = new File(Launch.minecraftHome, "mods"); diff --git a/src/main/java/org/dimdev/riftloader/launch/RiftLoaderTweaker.java b/src/main/java/org/dimdev/riftloader/launch/RiftLoaderTweaker.java index a4f61cb..d03cb00 100644 --- a/src/main/java/org/dimdev/riftloader/launch/RiftLoaderTweaker.java +++ b/src/main/java/org/dimdev/riftloader/launch/RiftLoaderTweaker.java @@ -3,6 +3,7 @@ import net.minecraft.launchwrapper.ITweaker; import net.minecraft.launchwrapper.Launch; import net.minecraft.launchwrapper.LaunchClassLoader; +import org.spongepowered.asm.launch.MixinBootstrap; import org.spongepowered.asm.mixin.MixinEnvironment; import java.util.List; @@ -28,6 +29,7 @@ public void injectIntoClassLoader(LaunchClassLoader classLoader) { throw new RuntimeException(e); } + MixinBootstrap.init(); MixinEnvironment.getDefaultEnvironment().setSide(isClient() ? MixinEnvironment.Side.CLIENT : MixinEnvironment.Side.SERVER); } diff --git a/src/main/java/org/dimdev/utils/ReflectionUtils.java b/src/main/java/org/dimdev/utils/ReflectionUtils.java index c295451..a80a2f3 100644 --- a/src/main/java/org/dimdev/utils/ReflectionUtils.java +++ b/src/main/java/org/dimdev/utils/ReflectionUtils.java @@ -1,8 +1,11 @@ package org.dimdev.utils; +import org.dimdev.riftloader.RiftLoader; + import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; @@ -15,7 +18,7 @@ public class ReflectionUtils { Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); method.setAccessible(true); addURLHandle = MethodHandles.lookup().unreflect(method); - } catch (ReflectiveOperationException e) { + } catch(ReflectiveOperationException e) { throw new AssertionError(e); } } @@ -27,7 +30,7 @@ public static T makeEnumInstance(Class enumClass, Object... constructorAr //noinspection unchecked return (T) MethodHandles.lookup().unreflectConstructor(constructor).invokeWithArguments(constructorArgs); - } catch (Throwable t) { + } catch(Throwable t) { throw t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t); } } @@ -35,8 +38,39 @@ public static T makeEnumInstance(Class enumClass, Object... constructorAr public static void addURLToClasspath(URL url) { try { addURLHandle.invoke(ClassLoader.getSystemClassLoader(), url); - } catch (Throwable t) { + } catch(Throwable t) { throw t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t); } } + + @SuppressWarnings("unchecked") + public static T getPrivateValue(Class type, E instance, String... fieldNames) { + try { + return (T) findField(type, fieldNames).get(instance); + } catch(IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static Field findField(Class clazz, String... fieldNames) { + for(String name : fieldNames) { + try { + Field f = clazz.getDeclaredField(name); + f.setAccessible(true); + return f; + } catch(NoSuchFieldException e) { + //ignore + } + } + RiftLoader.getLogger().error("Reflection error in class {}, cannot find any of those fields: {}", clazz.getCanonicalName(), String.join(", ", fieldNames)); + throw new RuntimeException(); + } + + public static void setPrivateValue(Class type, T instance, E value, String... fieldNames) { + try { + findField(type, fieldNames).set(instance, value); + } catch(IllegalAccessException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/resources/mixins.rift.hooks.json b/src/main/resources/mixins.rift.hooks.json index cc8d25b..42ce678 100644 --- a/src/main/resources/mixins.rift.hooks.json +++ b/src/main/resources/mixins.rift.hooks.json @@ -6,39 +6,41 @@ "package": "org.dimdev.rift.mixin.hook", "refmap": "mixins.rift.refmap.json", "mixins": [ + "MixinAnvilSaveHandler", + "MixinArgumentTypes", + "MixinBiome", "MixinBlock", - "MixinItem", - "MixinFluid", + "MixinBootstrap", + "MixinChunk", + "MixinCommands", + "MixinCPacketCustomPayload", + "MixinDimensionType", + "MixinEnchantment", + "MixinEntityTracker", + "MixinEntityTrackerEntry", + "MixinEntityType", + "MixinEntityType$Builder", "MixinEnumConnectionState", "MixinEnumConnectionState$Handshaking", + "MixinEnumConnectionState$Login", "MixinEnumConnectionState$Play", "MixinEnumConnectionState$Status", - "MixinEnumConnectionState$Login", - "MixinNetHandlerPlayServer", - "MixinCommands", + "MixinFeature", + "MixinFluid", + "MixinFurnace", + "MixinItem", + "MixinItemTool", "MixinMinecraftServer", - "MixinPotion", - "MixinEnchantment", - "MixinParticleType", - "MixinSoundEvent", + "MixinNetHandlerPlayServer", "MixinOverworldBiomeProvider", - "MixinEntityType", - "MixinTileEntityType", - "MixinBootstrap", - "MixinBiome", - "MixinCPacketCustomPayload", - "MixinEntityType$Builder", - "MixinWorldServer", - "MixinFurnace", - "MixinAnvilSaveHandler", - "MixinDimensionType", - "MixinStructureIO", - "MixinFeature", - "MixinChunk", + "MixinParticleType", + "MixinPotion", "MixinRecipeManager", "MixinRecipeSerializers", - "MixinArgumentTypes", - "MixinItemTool" + "MixinSoundEvent", + "MixinStructureIO", + "MixinTileEntityType", + "MixinWorldServer" ], "client": [ "client.MixinMinecraft",