I was practicing Forge, trying to create a modded item (though based heavily on the vanilla bow) - a lever-action repeating rifle. The idea was that it should have an internal "magazine" of 7 rounds, stored in the damage value, loaded one-by-one on a short right click and fired on a longer right click.
While I succeeded in making my repeater hold ammo, fire it, deplete and replenish the magazine, I ran into an unpleasant problem: the damage values were shared somehow between two items in different slots, and right-clicking one of them also reloaded the others.
Could you help me locate the problem? Here is my item code.
public class ItemLeverActionRifle extends Item {
Random RNG = new Random(System.currentTimeMillis());
private byte loadedAmmo;
public ItemLeverActionRifle(){
setUnlocalizedName(Reference.GuncraftItems.LEVERACTIONRIFLE.getUnlocalizedName());
setRegistryName(Reference.GuncraftItems.LEVERACTIONRIFLE.getRegistryName());
this.maxStackSize = 1;
this.setMaxDamage(7);
this.setCreativeTab(GunCraft.tabGuncraft);
}
public EnumAction getItemUseAction(ItemStack stack)
{
return EnumAction.BOW;
}
public boolean getIsRepairable(ItemStack tool, ItemStack material)
{
return false;
}
//Maximum item usage time.
public int getMaxItemUseDuration(ItemStack stack)
{
return 72000;
}
//Shows ammunition type in tooltip
@SideOnly(Side.CLIENT)
public void addInformation(ItemStack stack, EntityPlayer playerIn, List<String> tooltip, boolean advanced)
{
super.addInformation(stack, playerIn, tooltip, advanced);
tooltip.add(I18n.format("leveractionrifle.ammo.type", TextFormatting.BOLD, TextFormatting.RESET));
}
//Returns the first available ItemStack of ammunition.
private ItemStack findAmmo(EntityPlayer player)
{
if (this.isAmmo(player.getHeldItem(EnumHand.OFF_HAND)))
{
return player.getHeldItem(EnumHand.OFF_HAND);
}
else if (this.isAmmo(player.getHeldItem(EnumHand.MAIN_HAND)))
{
return player.getHeldItem(EnumHand.MAIN_HAND);
}
else
{
for (int i = 0; i < player.inventory.getSizeInventory(); ++i)
{
ItemStack itemstack = player.inventory.getStackInSlot(i);
if (this.isAmmo(itemstack))
{
return itemstack;
}
}
//field_190927_a is a null itemstack
return ItemStack.field_190927_a;
}
}
//Is the item usable as ammunition?
private boolean isAmmo(ItemStack stack) {
return stack.getItem() == ModItems.leveractioncartridge;
}
//Sets rifle in use (aiming).
public ActionResult<ItemStack> onItemRightClick(World world, EntityPlayer player, EnumHand hand)
{
ItemStack itemstack = player.getHeldItem(hand);
player.setActiveHand(hand);
return new ActionResult(EnumActionResult.SUCCESS, itemstack);
}
public void onPlayerStoppedUsing(ItemStack stack, World world, EntityLivingBase entityLiving, int timeLeft)
{
if (entityLiving instanceof EntityPlayer)
{
EntityPlayer entityplayer = (EntityPlayer)entityLiving;
boolean creative = entityplayer.capabilities.isCreativeMode;
ItemStack ammostack = this.findAmmo(entityplayer);
int timeAiming = this.getMaxItemUseDuration(stack) - timeLeft;
if (timeAiming < 0) return;
//Long right click -> finished aiming
if (timeAiming > 10)
{
if (this.loadedAmmo > 0)
{
if (!world.isRemote)
{
//Create and spawn the bullet
EntityMusketBullet bullet = new EntityMusketBullet(world, entityplayer);
bullet.setAim(entityplayer, entityplayer.rotationPitch, entityplayer.rotationYaw, 7.0F, 0.0F);
world.spawnEntityInWorld(bullet);
//Expend 1 unit of ammo and update the damage value
this.loadedAmmo--;
stack.setItemDamage(stack.getMaxDamage() - this.loadedAmmo);
}
//Shot sound and recoil
world.playSound(null, entityplayer.posX, entityplayer.posY, entityplayer.posZ, ModSounds.gunshot_reload, SoundCategory.PLAYERS, 2.0F, 1.0F);
entityplayer.setLocationAndAngles(entityplayer.posX, entityplayer.posY, entityplayer.posZ, entityplayer.rotationYaw, entityplayer.rotationPitch - (RNG.nextFloat() * 5.0F + 5.0F));
}
else
{
//Dry-fire empty gun
world.playSound(null, entityplayer.posX, entityplayer.posY, entityplayer.posZ, ModSounds.leveractiondryfire, SoundCategory.PLAYERS, 1.0F, 1.0F);
}
} //Short right click -> reload 1 unit of ammo
else if (timeAiming <= 10 && this.loadedAmmo < 7)
{
if (!ammostack.func_190926_b() || creative)
{
if (!world.isRemote)
{
//Load 1 unit of ammo and update the damage value
this.loadedAmmo++;
stack.setItemDamage(stack.getMaxDamage() - this.loadedAmmo);
}
//Reload sound
world.playSound(null, entityplayer.posX, entityplayer.posY, entityplayer.posZ, ModSounds.leveractionload, SoundCategory.PLAYERS, 1.0F, 1.0F);
if (!creative)
{
//Detracts 1 from stack size, sets a bool value when stack becomes null
ammostack.func_190918_g(1);
//Checks the bool field (true = null stack)
if (ammostack.func_190926_b())
{
entityplayer.inventory.deleteStack(ammostack);
}
}
}
}
}
}
}
You're only removing and adding ammo on the client side. When you right click, the integrated server updates your inventory, making it seem like two guns are reloading, when in actuality, neither gun has even been fired. Also, your bullets are only created on the client side (meaning no bullet is spawned o the server, and nothing can get shot). Both of these things should happen on both the client and server side.
Thanks for the reply! I went and rechecked the OnPLayerStoppedUsing function of my rifle and the vanilla bow, and in both the projectile spawn happens if the !world.isRemote is true - meaning serverside. I also made sure the damage update happens the same way. Isn't it the way to check for side?
UPDATE: I just got rid of the "privatebyteloadedAmmo' variable, instead changing the damage values directly, and it worked - now the ammo count is not shared between two individual rifles. I am partially relieved and mostly confused.
Thanks for the reply! I went and rechecked the OnPLayerStoppedUsing function of my rifle and the vanilla bow, and in both the projectile spawn happens if the !world.isRemote is true - meaning serverside. I also made sure the damage update happens the same way. Isn't it the way to check for side?
UPDATE: I just got rid of the "privatebyteloadedAmmo' variable, instead changing the damage values directly, and it worked - now the ammo count is not shared between two individual rifles. I am partially relieved and mostly confused.
All itemstacks of the same item share in instance of Item. So they would all use the same "private byte loadedAmmo". Basically you were storing data to the Item class instead of the Itemstack object.
Also, world.isRemote is true when it is a server world, so !world.isRemote is checking if we are on the client.
YES! Finally a good explanation for this shared value problem.
But I think Forge docs disagree with you on the isRemote variable: World.java says this
/**
* True if the world is a 'slave' client; changes will not be saved or propagated from this world. For example,
* server worlds have this set to false, client worlds have this set to true.
*/
public final boolean isRemote;
Greetings everyone,
I was practicing Forge, trying to create a modded item (though based heavily on the vanilla bow) - a lever-action repeating rifle. The idea was that it should have an internal "magazine" of 7 rounds, stored in the damage value, loaded one-by-one on a short right click and fired on a longer right click.
While I succeeded in making my repeater hold ammo, fire it, deplete and replenish the magazine, I ran into an unpleasant problem: the damage values were shared somehow between two items in different slots, and right-clicking one of them also reloaded the others.
Could you help me locate the problem? Here is my item code.
You're only removing and adding ammo on the client side. When you right click, the integrated server updates your inventory, making it seem like two guns are reloading, when in actuality, neither gun has even been fired. Also, your bullets are only created on the client side (meaning no bullet is spawned o the server, and nothing can get shot). Both of these things should happen on both the client and server side.My Github ด้้้้้็็็็็้้้้้็็็็็้้้้้็็็็็้้้้้็็็็็้้้้้็็็็็้้้้้็็็็็้้้้้็็็็็้้้้้дด็็็็็้้้้้็็็็้้้้้็็็็็้้้้้็็็็็้้้้้็็็็็้้้้้
Thanks for the reply! I went and rechecked the OnPLayerStoppedUsing function of my rifle and the vanilla bow, and in both the projectile spawn happens if the !world.isRemote is true - meaning serverside. I also made sure the damage update happens the same way. Isn't it the way to check for side?
UPDATE: I just got rid of the "private byte loadedAmmo' variable, instead changing the damage values directly, and it worked - now the ammo count is not shared between two individual rifles. I am partially relieved and mostly confused.
All itemstacks of the same item share in instance of Item. So they would all use the same "private byte loadedAmmo". Basically you were storing data to the Item class instead of the Itemstack object.
Also, world.isRemote is true when it is a server world, so !world.isRemote is checking if we are on the client.My Github ด้้้้้็็็็็้้้้้็็็็็้้้้้็็็็็้้้้้็็็็็้้้้้็็็็็้้้้้็็็็็้้้้้็็็็็้้้้้дด็็็็็้้้้้็็็็้้้้้็็็็็้้้้้็็็็็้้้้้็็็็็้้้้้
YES! Finally a good explanation for this shared value problem.
But I think Forge docs disagree with you on the isRemote variable: World.java says this
So !World.isRemote means we are on the server.
Thanks anyway!
Thanks @Metamorphic_Fish for the explanation! Problem solved.