Thornack Posted October 2, 2015 Posted October 2, 2015 Hi everyone, I have a method that gives my mob attacks which are basically customized items (it works). But I need to sync the mobs inventory so that the attack items also exist on the client. Im not sure how to do this. (And Dont just say "Packets" I know that it requires packets or use of a datawatcher but I am stumped how to do this) Currently I have tried the following see below). This method is called server side only when my entity gets constructed and then again whenever my entity levels up (this works) public void attacks(EntityCustom customEntity){ if(customEntity instanceof EntityMyMob){ int lvl = customEntity.getStats().level; if(lvl <= 5){ customEntity.attacks[0] = new ItemStack(CommonProxy.itemAttack_Primary); PacketOverlord.sendToAll(new PacketSyncAttacks(customEntity)); <- need to update the attacks when they are given } if(lvl > 5){ customEntity.attacks[1] = new ItemStack(CommonProxy.itemAttack_Secondary); PacketOverlord.sendToAll(new PacketSyncAttacks(customEntity)); <- need to update the attacks when they are given } if (lvl >= 12){ customEntity.attacks[2] = new ItemStack(CommonProxy.itemAttack_Tertiary); PacketOverlord.sendToAll(new PacketSyncAttacks(customEntity)); <- need to update the attacks when they are given } if(lvl >= 18){ customEntity.attacks[3] = new ItemStack(CommonProxy.itemAttack_Quaternary); PacketOverlord.sendToAll(new PacketSyncAttacks(customEntity)); <- need to update the attacks when they are given } } } My packet class - The issue is my entity is null when I send the packet for some reason, So I am unsure how to update the entity on its client side so that it has the attacks on both sides. Currently the attacks are only present on server side public class PacketSyncAttacks extends AbstractMessageToClient<PacketSyncAttacks> { private NBTTagCompound data; EntityCustom custom; public PacketSyncAttacks() {} public PacketSyncAttacks(EntityCustom custom) { this.custom= custom; <- Entity is not null here data = new NBTTagCompound(); custom.writeEntityToNBT(data); } @Override protected void read(PacketBuffer buffer) throws IOException { data = buffer.readNBTTagCompoundFromBuffer(); } @Override protected void write(PacketBuffer buffer) throws IOException { System.out.println("FUCK3"); buffer.writeNBTTagCompoundToBuffer(data); } @Override public void process(EntityPlayer player, Side side) { //But my entity is null here. So I am unsure what to do to update my entities inventory if(data != null && custom!= null){ <- this is never called since entity is null here on client side this.custom.readFromNBT(data); } } } My Packet Handler class public class PacketOverlord { private static byte packetId = 0; private static final SimpleNetworkWrapper dispatcher = NetworkRegistry.INSTANCE .newSimpleChannel(CustomMod.CHANNEL); /** * Register all packets and handlers here - this should be called during * {@link FMLPreInitializationEvent} */ public static final void preInit() { registerMessage(PacketSyncAttacks.class); } /** * Register an {@link AbstractMessageOverlord} to the correct side */ private static final <T extends AbstractMessageOverlord<T> & IMessageHandler<T, IMessage>> void registerMessage( Class<T> clazz) { if (AbstractMessageOverlord.AbstractMessageToClient.class.isAssignableFrom(clazz)) { PacketOverlord.dispatcher.registerMessage(clazz, clazz, packetId++, Side.CLIENT); } else if (AbstractMessageOverlord.AbstractMessageToServer.class.isAssignableFrom(clazz)) { PacketOverlord.dispatcher.registerMessage(clazz, clazz, packetId++, Side.SERVER); } else { PacketOverlord.dispatcher.registerMessage(clazz, clazz, packetId,Side.CLIENT); PacketOverlord.dispatcher.registerMessage(clazz, clazz, packetId++, Side.SERVER); } } /** * This message is sent to the specified player's client-side counterpart. See * {@link SimpleNetworkWrapper#sendTo(IMessage, EntityPlayerMP)} */ public static final void sendTo(IMessage message, EntityPlayerMP player) { PacketOverlord.dispatcher.sendTo(message, player); } public static final void sendToPlayers(IMessage message, Set<EntityPlayer> players) { for(EntityPlayer player : players) { //System.out.println("player"+player); //System.out.println("players"+players); PacketOverlord.dispatcher.sendTo(message, (EntityPlayerMP) player); } } /**This sends a message to everyone. See * {@link SimpleNetworkWrapper#sendToAll(IMessage)} */ public static void sendToAll(IMessage message) { PacketOverlord.dispatcher.sendToAll(message); } /** * This sends a message to everyone within a specified range of a specified point. See * {@link SimpleNetworkWrapper#sendToAllAround(IMessage, NetworkRegistry.TargetPoint)} */ public static final void sendToAllAround(IMessage message, NetworkRegistry.TargetPoint point) { PacketOverlord.dispatcher.sendToAllAround(message, point); } /** * This sends a message to everyone within a specified range of the passed in coordinates in * the same MC dimension. Shortcut to * {@link SimpleNetworkWrapper#sendToAllAround(IMessage, NetworkRegistry.TargetPoint)} */ public static final void sendToAllAroundCoordinates(IMessage message, int dimension, double x, double y, double z, double range) { PacketOverlord.sendToAllAround(message, new NetworkRegistry.TargetPoint(dimension, x, y, z, range)); } /** * This sends a message to everyone within a specified range of the passed in player * Shortcut to * {@link SimpleNetworkWrapper#sendToAllAround(IMessage, NetworkRegistry.TargetPoint)} */ public static final void sendToAllAround(IMessage message, EntityPlayer player, double range) { PacketOverlord.sendToAllAroundCoordinates(message, player.worldObj.provider.dimensionId, player.posX, player.posY, player.posZ, range); } /** * This sends a message to everyone within a specified range of the passed in pkmn * Shortcut to * {@link SimpleNetworkWrapper#sendToAllAround(IMessage, NetworkRegistry.TargetPoint)} */ public static final void sendToAllAround(IMessage message, EntityCustom entityCustom, double range) { PacketOverlord.sendToAllAroundCoordinates(message, entityCustom.worldObj.provider.dimensionId, entityCustom.posX, entityCustom.posY, entityCustom.posZ, range); } /** * this sends a message to everyone within the passed in dimension that is denoted by its id. See * {@link SimpleNetworkWrapper#sendToDimension(IMessage, int)} */ public static final void sendToDimension(IMessage message, int dimensionId) { PacketOverlord.dispatcher.sendToDimension(message, dimensionId); } /** * This sends a message to the server. See * {@link SimpleNetworkWrapper#sendToServer(IMessage)} */ public static final void sendToServer(IMessage message) { PacketOverlord.dispatcher.sendToServer(message); } public static final SimpleNetworkWrapper getInstance() { return dispatcher; } } Anyone know of a good way to do this I am stumped atm Quote
jabelar Posted October 2, 2015 Posted October 2, 2015 I don't think you need custom packets for this. Inventories can be synced by built in methods if you make them part of a Container along with an IGuiHandler. The IGuiHandler is what syncs up the client and server for you. I have a tutorial for inventory GUIs for blocks, but I'm fairly certain it would work same way for any inventory: http://jabelarminecraft.blogspot.com/p/minecraft-modding-blocks-with-guis.html Quote Check out my tutorials here: http://jabelarminecraft.blogspot.com/
coolAlias Posted October 2, 2015 Posted October 2, 2015 Thornack, you are misunderstanding how packets work. A packet is created on one side, populating all the class fields as needed and then writing those to an output stream so it can be sent to the other side; when this data is received on the other side, a COMPLETELY NEW instance of the packet is created, meaning NONE of your class fields have any information in them yet. The data is then parsed by the packet's read method, which is generally where you initialize class fields, and then those fields can be used when the handler processes the packet. That said, Jabelar is correct - you shouldn't need to manually sync inventory information, as that is done by the Container class. If you don't have a Container class, you probably don't have a GUI, and if you don't have a GUI, then why does the client need to know the information anyway? Quote http://i.imgur.com/NdrFdld.png[/img]
Thornack Posted October 3, 2015 Author Posted October 3, 2015 Ok Thanks for clarifying that up, coolAlias I think I understand packets a bit better now, but I still need to send my packet and still have the same problem. I dont know how to sync my attack items client and server side.. I dont have a gui for my entity, I need to make my items exist on client side also so that my mob entity can use them. Currently what I have is the following. Entity is spawned -> Entities level is generated -> Entities attacks are calculated based on level -> This is done server side -> Entity can be added to players party -> Entity can then be spawned by player as an ownable entity -> Player can become this entity by morphing into it -> Player gets all entity stats, inventory and items in inventory -> Items are attacks that the player can then use. All of that works great. Im trying ot now write an AI to use my entities attack items so that the untamed entity can attack other entities using the attack items (just like the player can). my partial entity class (sorry alot of the code is pretty complicated so I omitted the crazy stuff cause this entity can do a lot atm) Hopefully this is sufficient to help grasp the issue. I need this entity to be able to use my attack items with onItem Right Click and on Item Use -> and onUsingTick-> to spawn custom particles and deal custom damage from a custom DamageSource. I left in every line related to my attack items. The attacks only exist on server side however for my entity. Currently My entity spawns the attack item particles but the attack does no damage (for some reason) public class EntityCustom extends EntityAnimal implements IEntityAdditionalSpawnData { public ItemStack[] attacks = new ItemStack[4]; public int currentAttack; private ItemStack attackInUse; private int attackInUseCount; public CustomStats stats; public EntityCustom(World world) { super(world); this.setSize(0.9F, 0.9F); this.setupAI(); this.stats = new CustomStats(this); if(!world.isRemote){ stats.calculateNewStats(); stats.recalculateStats(this); } } public CustomStats getStats() { return this.stats; } public ItemStack getCurrentAttack() { return this.currentAttack < 4 && this.currentAttack >= 0 ? this.attacks[this.currentAttack] : null; } public void setAttackItemInUse(ItemStack itemStack, int maxItemUseDuration) { if (itemStack != this.attackInUse) { if (maxItemUseDuration <= 0){ return; } this.attackInUse = itemStack; this.attackInUseCount = maxItemUseDuration; if (!this.worldObj.isRemote) { this.setEating(true); } } } public void clearAttackInUse() { this.attackInUse = null; this.attackInUseCount = 0; if (!this.worldObj.isRemote) { this.setEating(false); } } protected void onAttackUseFinish() { if (this.attackInUse != null) { int i = this.attackInUse.stackSize; /**may cause problems*/ ItemStack itemstack = this.attackInUse.onFoodEaten(this.worldObj, null); if (itemstack != this.attackInUse || itemstack != null && itemstack.stackSize != i) { this.attacks[this.currentAttack] = itemstack; if (itemstack != null && itemstack.stackSize == 0) { this.attacks[this.currentAttack] = null; } } this.clearAttackInUse(); } } @Override public void onLivingUpdate(){ super.onLivingUpdate(); /////////////////////////////////// Attack att = (Attack) attacks[0].getItem(); att.onItemRightClick( attacks[0], this.worldObj, this); if (this.attackInUse != null) { ItemStack itemstack = this.getCurrentAttack(); if (itemstack == this.attackInUse) { if (attackInUseCount <= 0) { this.onAttackUseFinish(); } else { if(attackInUse.getItem() instanceof PWItem){ PWItem attack = (PWItem) attackInUse.getItem(); attack.onUsingTick(attackInUse, this, attackInUseCount); } if (--this.attackInUseCount == 0 && !this.worldObj.isRemote) { this.onAttackUseFinish(); } } } else { this.clearAttackInUse(); } } /////////////////////////////////////+ } @Override protected void entityInit() { super.entityInit(); } @Override public boolean attackEntityFrom(DamageSource damageSource, float damageAmount) { if (!this.worldObj.isRemote) { if(this.stats.currentHp > 0) { this.stats.currentHp = (int) this.getHealth() - (int) damageAmount; } else { this.stats.currentHp = 0; } MessageUpdateCustomStats msg = new MessageUpdateCustomStats(this); msg.sendToAll(); } return super.attackEntityFrom(damageSource, damageAmount); } @Override public boolean isAIEnabled() { return true; } @Override protected void updateAITasks() { super.updateAITasks(); } private void setupAI() { this.getNavigator().setAvoidsWater(true); clearAITasks(); double speed = 0.5F; //this.tasks.addTask(0, new EntityAISwimming(this)); this.tasks.addTask(1, new EntityAIWander(this, speed)); this.tasks.addTask(2, new EntityAIWatchClosest(this, EntityPlayer.class, 12.0F)); this.tasks.addTask(3, new EntityAIPanic(this, speed)); // speed is the second parameter this.tasks.addTask(4, new EntityAILookIdle(this)); this.tasks.addTask(5, new EntityAIWatchClosest(this, EntityCustom.class, 6.0F)); } private void clearAITasks() { tasks.taskEntries.clear(); targetTasks.taskEntries.clear(); } @Override public void writeSpawnData(ByteBuf buffer) { this.stats.toBytes(buffer); } @Override public void readSpawnData(ByteBuf additionalData) { this.stats.fromBytes(additionalData); } /** * Returns the item that this EntityLiving is holding, if any. */ public ItemStack getFirstAttack() { return this.attacks[0]; } public ItemStack getAttackFromSlot(int slot) { return this.attacks[slot]; } public ItemStack deleteAttackInSlot(int slot) { return this.attacks[slot] = null; } @Override public void writeEntityToNBT(NBTTagCompound nbt) { super.writeEntityToNBT(nbt); NBTTagList nbttaglist = new NBTTagList(); NBTTagCompound attackNBT; for (int i = 0; i < this.attacks.length; ++i) { attackNBT = new NBTTagCompound(); if (this.attacks[i] != null) { this.attacks[i].writeToNBT(attackNBT); } nbttaglist.appendTag(attackNBT); } nbt.setTag("Attacks", nbttaglist); this.stats.saveNBT(nbt); } @Override public void readEntityFromNBT(NBTTagCompound nbt) { super.readEntityFromNBT(nbt); NBTTagList nbttaglist; if (nbt.hasKey("Attacks", 9)){ nbttaglist = nbt.getTagList("Attacks", 10); for (int i = 0; i < this.attacks.length; ++i) { this.attacks[i] = ItemStack.loadItemStackFromNBT(nbttaglist.getCompoundTagAt(i)); } } this.stats.loadNBT(nbt); } } When I get the entities that the attack is supposed to target (they return correctly Sorry I also removed a lot of logic here cause its kinda huge atm) BUT for some reason List<EntityLivingBase> targets = TargetingHelper.acquireAllLookTargets(customEntity, Math.round(damageRadius), 1.0F); for (EntityLivingBase target : targets) { System.out.println("Damage Done: " + appliedDmg + " target " + target); target.attackEntityFrom(getDamageSource(customEntity), appliedDmg); } The damage isnt done since when I call target.attackEntityFrom(getDamageSource(customEntity), appliedDmg); the appliedDmg variable is there and calculated correctly, the entity is there, the damage source is there, but the attackEntityFrom bit is never called (I checked with a break point)!! But for the player (when player is morphed into the entity and gets the entities attack) everything is there and damage does get dealt...So I checked whether or not my item exists client side for my entity and it does not, I suspect this is the problem. The onLivingUpdate method crashes the game when I allow it to call the following on client side because the item is null when I call. if(this.worldObj.isRemote){ System.out.println(attacks[0]); <- this is null on client side but not null on server side Attack att = (Attack) attacks[0].getItem(); att.onItemRightClick( attacks[0], this.worldObj, this); } So my item is only there on server side and this is probably why I am having issues. Its basically driving me crazy how hard this "get mobs to use my items" is to implement ugh. The good thing though if I get this one class implemented then all of my attacks will work because they are all set up the same. Any ideas anybody? I am basically trying to take all of the onItemRightClick stuff and onItemUse, and onUsingTick etc etc and implement it for my entity so that it can use my item. I believe the important classes/methods have already been posted Quote
coolAlias Posted October 3, 2015 Posted October 3, 2015 Why do you care if the code runs on the client side, though? Only the server side should care about whether an attack or action is performed - the only exception is if you need some animation or something to happen on the client, and then you can just send a packet or health update flag saying 'this action was done on the server, so do your thing now client'. So the solution is to only run the code on the server side - there is no reason to run it on both sides for a non-player entity. Quote http://i.imgur.com/NdrFdld.png[/img]
Thornack Posted October 3, 2015 Author Posted October 3, 2015 My customized onItemRightClick method from my attack item class that I changed to work for my custom entity (it works for the player). Currently just trying to get the else if(ball ==false){} attacks done and working as they represent the majority of my attacks (the last else statement in this method). The ball type attacks are kind of like a snowball projectile but they do damage and have a trailing particle behind them and use a custom model. For now the non ball attacks is what I am looking at. public ItemStack onItemRightClick(ItemStack itemStack, World world, EntityCustom entityCustom) { int fatigueMultiplier = 1; if(ball == true && (getCooldown(itemStack) == 0)){ fatigueMultiplier = 2; EntityRangedAttackBase distanceAttack = new EntityRangedAttack(world, entityCustom).setType(attackType).setArea(damageRadius); distanceAttack.setBaseDamageForProjectileAttack(baseDmg); setCooldown(itemStack, 30); if (!world.isRemote) {//spawn the bomb entity if your on the server WorldHelper.playSoundAtEntity(entityCustom,BattleSoundsList.WHOOSH, 0.4F, 0.5F); world.spawnEntityInWorld(distanceAttack); } } else if(ball ==false){ //This is the bit that I care about atm as these are the main attacks entityCustom.setAttackItemInUse(itemStack,getMaxItemUseDuration(itemStack)); } return itemStack; } My onUsingTickMethod inside my attack item class (atm I only care about the non stationary attacks (again since they represent the majority) (last else statement in this method public void onUsingTick(ItemStack itemStack, EntityCustom entityCustom, int count) { if(entityCustom.worldObj.isRemote){ System.out.println("NEVER CALLED"); //This is never called because my item is serverside only so the aiming logic for the attack doesnt work properly atm. MovingObjectPosition mop = entityCustom.rayTrace(15, 1F); if (mop != null && !entityCustom.worldObj.isAirBlock(mop.blockX, mop.blockY, mop.blockZ) && (mop.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK || mop.typeOfHit == MovingObjectPosition.MovingObjectType.ENTITY)) { PacketOverlord.sendToServer(new PacketUpdateOnUsingTickTargetCoord(mop.blockX, mop.blockY, mop.blockZ)); } } if (!entityCustom.worldObj.isRemote) { int ticksInUse = getMaxItemUseDuration(itemStack) - count; // Spawn stationary particle attack on right click if (this == CommonProxy.itemAttack_Stationary) { if (ticksInUse % 10 == 0) { EntityStationary entityStationary = new EntityStationary(entityCustom.worldObj, xTargetCoord + 0.5, yTargetCoord + 1, zTargetCoord + 0.5).disableGriefing(); entityStationary .thrower = entityCustom; entityStationary .setBaseDamageForProjectileAttack(baseDmg); entityCustom.worldObj.spawnEntityInWorld(entityStationary); } } else { //This is what I care about here because I want to deal with the non stationary attacks handleUpdateTick(itemStack, entityCustom.worldObj, entityCustom, ticksInUse); } } } The handleUpdateTick method inside my attack item class that I use for my main attacks. The non ball non stationary attacks (these are the majority of my particle attacks and they behave like a flamethrower essentially but all have different particles that are set elsewhere. All particles spawn normally but damage isnt done ever private void handleUpdateTick(ItemStack itemStack, World world, EntityCustom entityCustom, int ticksInUse) { PacketOverlord.sendToAllAround(new PacketISpawnParticles(entityCustom, this, (float) (damageRadius)), entityCustom, 64.0D); <- works to spawn my particles (and they do actually show up) if (ticksInUse % 4 == 3) { float radius = 1; if(entityCustom.entID != -1){ radius = 10; } List<EntityLivingBase> targets = TargetingHelper.acquireAllLookTargets(entityCustom, Math.round(1*damageRadius), 1.0F); for (EntityLivingBase target : targets) { //////////DAMAGE LOGIC FOR NON BALL TYPE ATTACKS//////////// int defense = 1; if(target instanceof EntityCustom){ defense = ((EntityCustom) target).stats.currentDef; } else if (target instanceof EntityPlayer){ defense = 1; } if(entityCustom.entID != -1){ float calcPartTwo = ((float)entityCustom.stats.currentAtt/defense); float modifier = 1.0F; float appliedDmg = ((calcPartTwo*baseDmg)+2.0F)*modifier; System.out.println("Damage Done: " + appliedDmg); <-This prints correctly target.attackEntityFrom(getDamageSource(entityCustom), appliedDmg); //BUT DAMAGE ISNT ACTUALLY DONE!!!!!!!!!! System.out.println("damage source "+getDamageSource(entityCustom)); <-This prints correctly System.out.println(entityCustom);<-This prints correctly } //////////////////////////////////////////////////////////////////// } } } Quote
Thornack Posted October 3, 2015 Author Posted October 3, 2015 I think I need it to happen on both sides for my code to work properly Quote
Thornack Posted October 3, 2015 Author Posted October 3, 2015 Ok I got my damage working but now I still need the targeting to work. It was a simple fix ish where I passed in the the target as an entity player so that the player into the target.attackEntityFrom(getDamageSource((EntityPlayer) target), appliedDmg); can take damage from the wild entities. Im not sure if the damage is the correct damage but the player can now be hurt by the entity so that seems to work good. Quote
Thornack Posted October 3, 2015 Author Posted October 3, 2015 I still need to figure out how to get the aiming code working (that would require the invnetory to be on client side also so that the onItemRightClick method can be called on client and server just like the player one does Quote
coolAlias Posted October 3, 2015 Posted October 3, 2015 I still need to figure out how to get the aiming code working (that would require the invnetory to be on client side also so that the onItemRightClick method can be called on client and server just like the player one does Is the aiming code the reason you think you need the code to run client side? If so, you don't. You can write aiming code that works just fine on the server - the only reason the player has code running on the client side is because that's where player input (i.e. mouse and keyboard) comes from, so it makes sense to say 'hey client, where is the player's mouse pointing right now?'. In the context of a non-player entity, however, that doesn't make much sense. Much better to use the server side values for the entity to determine if it can attack the player or not, using the entity's look vector to determine what it is looking at, geometry based on the entity's rotation vs. the player's position, or even just proximity as most vanilla mobs do. Quote http://i.imgur.com/NdrFdld.png[/img]
Thornack Posted October 3, 2015 Author Posted October 3, 2015 Its not just the aiming however but ok ill try youre suggestion and maybe see if i can only use server side values Quote
coolAlias Posted October 3, 2015 Posted October 3, 2015 Its not just the aiming however but ok ill try youre suggestion and maybe see if i can only use server side values Okay, so what other reasons do you have for wanting your code to run on the client side? Keep in mind that the client is ONLY responsible for things like rendering the world, displaying GUIs on the screen, and taking input from the player and sending it to the server. Some methods run on both sides to make the interaction smoother, but the server is the final arbiter of the outcome. E.g. when you (a player) attack an entity, the client also runs the code so that the player can immediately see the visible result of their action (the attacked entity appearing to take damage, the sword losing durability, etc.) without waiting for a response from the server, but that is only for visuals - the server is the one that actually determines if the entity takes damage, how much, etc., and the sword's new durability, etc. The client side values are irrelevant except for that immediate feedback to the player. Look at every other mob's AI / attack code and you will see it only runs on the server. At most, the entity will send a packet to notify the client that it is attacking so that it can perform an animation or spawn particles, nothing more. LivingAttackEvent, LivingHurtEvent, etc. all only fire on the server side, with the sole exception of when the aggressor is an EntityPlayer, in which case some of those events fire on both sides. My point is that even if you allow the code to run on both sides, it sounds like you are using it wrong. 'Aiming' only occurs due to player input on the client - every other entity determines what it can strike on the server. If you have animations or particles that you want to happen, use #setEntityState on the server at the appropriate time to send a custom flag to #handleHealthUpdate - open up the call hierarchy of both methods to see how they are used in many entities, e.g. EntityIronGolem, EntityVillager, etc. Quote http://i.imgur.com/NdrFdld.png[/img]
Thornack Posted October 3, 2015 Author Posted October 3, 2015 One of the main reasons really is so I dont have to rewrite a ton of logic that works currently if it is called client and server side for the player. My attacks recalculate an effective distance based on a ray trace (that bit isnt shown but ray trace needs to be client side only), they can "affect blocks" ie set blocks on fire, or spawn ice blocks to freeze things... etc etc and they also have a few visual effects. The aiming code works for me atm I like the ray trace since it lets me control the max distance, etc etc... But out of curiosity in case I do feel like rewriting a bunch of stuff how would you suggest I do the aiming bit Quote
coolAlias Posted October 3, 2015 Posted October 3, 2015 Ray tracing is available on the server side as well... MovingObjectPosition mop = world.rayTraceBlocks(vec3OriginPosition, vec3TargetPosition); Where both arguments are a Vec3, the first being the point of origin, and the second being some location off in the distance you are trying to reach - if the mop is null, there is nothing in the way; otherwise it will have information about the first block encountered between the two positions. Like I mentioned above, you don't want any of your logic that is responsible for affecting the world to run on the client - this includes setting blocks on fire, spawning blocks, etc. Do ALL of that on the server. For your visuals, if you don't need them to be super precise, you can do what the Explosion class does and allow the code to run on the client, too, for the sole purpose of spawning particles. The affected block positions will not necessarily be identical to what the server decides was affected, so there can/will be some mismatch. If you need the positions to be exact, then you will probably need to send a packet with each or all of the affected positions to notify the client where to spawn the particles. Note how the above solution is 'server affects blocks, sends notification to client' rather than 'client determines affected blocks, sends list to server'. That is always the flow you should aim for. As for targeting code, what exactly are you trying to do? What I mean is, non-player entities don't aim, they target whatever you tell them to target, so I'm not sure what problem you are trying to solve here. Do you only want your entity to be able to attack players in front of it? Are you having issues finding a player to attack? I need more information. Quote http://i.imgur.com/NdrFdld.png[/img]
Thornack Posted October 3, 2015 Author Posted October 3, 2015 Currently i got my entity to "use the attack item" this works, it spawns a stream of particles infront of it and anything within the calculated radius of effect (directly in front of my entity) will take damage. that is working. But when my mob rotates the "aiming" for the attack is incorrect. currently the particles are spawned along one set direction and the attack happens in that direction only. Im looking for a way to get my attack to follow the entities rotation. Just like for my player, if my player rotates the attack follows his look vector and the particles get spawned along that vector. Currently this is what I am trying to fix. I am going to implement a targeting AI later that will determine the ttack frequency and all of that. but for now I just made it so the attack pulses as so. @Override public void onLivingUpdate(){ super.onLivingUpdate(); ////////////////////////The pulsing code/////////////////////////////////////////////////// if(!this.worldObj.isRemote){ if(attackInUse == null && this.ticksExisted % 20 == 0){ Attack att = (Attack) attacks[0].getItem(); att.onItemRightClick( attacks[0], this.worldObj, this); } else { if(this.ticksExisted % 20 == 0){ attackInUse = null; } } } /////////////////////////////////////////////////////////////////////////// if (this.attackInUse != null) { ItemStack itemstack = this.getCurrentAttack(); if (itemstack == this.attackInUse) { if (attackInUseCount <= 0) { this.onAttackUseFinish(); } else { if(attackInUse.getItem() instanceof PWItem){ PWItem attack = (PWItem) attackInUse.getItem(); attack.onUsingTick(attackInUse, this, attackInUseCount); } if (--this.attackInUseCount == 0 && !this.worldObj.isRemote) { this.onAttackUseFinish(); } } } else { this.clearAttackInUse(); } } /////////////////////////////////////+ This bit of code is the part I need to change since my method never gets called on client if(customEntity.worldObj.isRemote){ System.out.println("NEVER CALLED"); MovingObjectPosition mop = customEntity.rayTrace(15, 1F); if (mop != null && !customEntity.worldObj.isAirBlock(mop.blockX, mop.blockY, mop.blockZ) && (mop.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK || mop.typeOfHit == MovingObjectPosition.MovingObjectType.ENTITY)) { PacketOverlord.sendToServer(new PacketUpdateOnUsingTickTargetCoord(mop.blockX, mop.blockY, mop.blockZ)); } } it is located inside my onUsingTick method. Basically I need the look vector of the entities model so that when the model rotates inside its collision box the attack also rotates accordingly. Quote
Thornack Posted October 3, 2015 Author Posted October 3, 2015 I cant seem to just be able to use the entities look vector coordinates directly to determine my attack coordinates since the look vector coordinates are all really really small values like X is -1.2246468525851679E-16 Y is 0.0 Z is 1.0 That is the output I get from inside my onUsingTick method System.out.println(" X is "+customEntity.getLookVec().xCoord + " Y is "+ customEntity.getLookVec().yCoord + " Z is "+customEntity.getLookVec().zCoord) Quote
coolAlias Posted October 3, 2015 Posted October 3, 2015 Entity#getLookVec returns a normalized vector, which means the 'length' of the vector should equal 1. X, Y, and Z should all be values between -1.0F and 1.0F, i.e. values on the unit circle. The look vector is determined using the entity's head rotation, not their body rotation - these are separate values. If you want to use the entity's body rotation to determine the path of the attack, you will need to copy the Entity#getLook method and swap in this.rotationYaw in place of this.rotationYawHead: // from Entity#getLook(float) when the parameter = 1.0F, which is what is called from #getLookVec return this.getVectorForRotation(this.rotationPitch, this.rotationYawHead); // note it uses HEAD yaw Using #getVectorForRotation with the entity's body rotation should work, I would imagine. Quote http://i.imgur.com/NdrFdld.png[/img]
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.