Jump to content

[1.10.2] Give equipment (armor + hands) to a custom Entity through a GUI ?


Major Squirrel

Recommended Posts

Good evening,

 

I am trying to set up a GUI to equip my custom Entity, meaning giving it some armor pieces or items in hands.

 

Here is how it looks like right now :

 

xGjrCzY.png

 

ContainerNPCInventory:

public class                ContainerNPCInventory extends Container {

    private static final EntityEquipmentSlot[]  VALID_EQUIPMENT_SLOTS = new EntityEquipmentSlot[] {
            EntityEquipmentSlot.HEAD,
            EntityEquipmentSlot.CHEST,
            EntityEquipmentSlot.LEGS,
            EntityEquipmentSlot.FEET
    };

    private final EntityNPCDialog   entityNPC;
    private Iterable<ItemStack>     inventoryArmor;
    private Iterable<ItemStack>     heldEquipment;

    private final InventoryPlayer   playerInventory;
    private InventoryBasic          entityInventory;

    public                  ContainerNPCInventory(EntityPlayer playerIn, EntityNPCDialog entityNPC) {
        this.entityNPC = entityNPC;
        this.inventoryArmor = entityNPC.getArmorInventoryList();
        this.heldEquipment = entityNPC.getHeldEquipment();

        this.playerInventory = playerIn.inventory;
        this.entityInventory = new InventoryBasic("NPC Inventory", true,
                Iterables.size(this.inventoryArmor) + Iterables.size(this.heldEquipment));
        this.entityInventory.addInventoryChangeListener(entityNPC);

        for (int index = 0; index < 4; ++index) {
            final EntityEquipmentSlot   equipmentSlot = ContainerNPCInventory.VALID_EQUIPMENT_SLOTS[index];

            this.addSlotToContainer(new Slot(this.entityInventory, index, 8, 8 + index * 18) {

                @Override
                public boolean  isItemValid(@Nullable ItemStack stack) {
                    return (stack != null && stack.getItem().isValidArmor(stack, equipmentSlot, entityNPC));
                }

                @Override
                public int      getSlotStackLimit() {
                    return (1);
                }

                @Nullable
                @Override
                public String   getSlotTexture() {
                    return (ItemArmor.EMPTY_SLOT_NAMES[equipmentSlot.getIndex()]);
                }
            });
        }

        this.addSlotToContainer(new Slot(this.entityInventory, 4, 77, );

        this.addSlotToContainer(new Slot(this.entityInventory, 5, 95,  {

            @Nullable
            @Override
            public String   getSlotTexture() {
                return ("minecraft:items/empty_armor_slot_shield");
            }
        });

        for (int index = 0; index < 9; ++index) {
            this.addSlotToContainer(new Slot(this.playerInventory, index, 9 + index * 18, 112));
        }
    }

    @Override
    public boolean          canInteractWith(EntityPlayer playerIn) {
        return (true);
    }
}

 

EntityNPCDialog:

public class                EntityNPCDialog extends EntityLiving implements IInventoryChangedListener {

//    private static final DataParameter<Integer> TEXTURE_INDEX = EntityDataManager.<Integer>createKey(EntityNPCDialog.class, DataSerializers.VARINT);

    public                  EntityNPCDialog(World worldIn) {
        super(worldIn);
    }

    @Override
    protected void          initEntityAI() {
        super.initEntityAI();

        this.tasks.addTask(0, new EntityAISwimming(this));
        this.tasks.addTask(1, new EntityAIWatchClosest(this, EntityPlayer.class, 16.0F, 42.0F));
        this.tasks.addTask(2, new EntityAILookIdle(this));
    }

    @Override
    public boolean          canBePushed() {
        return (false);
    }

    @Override
    public boolean          isPushedByWater() {
        return (false);
    }

    @Override
    protected boolean       canDespawn() {
        return(false);
    }

    @Override
    public void             addVelocity(double x, double y, double z) {}

    @Override
    public boolean          canRiderInteract() {
        return (false);
    }

    @Override
    public boolean          isEntityInvulnerable(DamageSource source) {
        return (false);
    }

    @Override
    public boolean          isPotionApplicable(PotionEffect potioneffectIn) {
        return (false);
    }

    @Override
    public boolean          isImmuneToExplosions() {
        return (false);
    }

    @Nullable
    @Override
    protected SoundEvent    getAmbientSound() {
        return (CraftAndConquerSounds.NPCDialogSoundEvent);
    }

    @Nullable
    @Override
    protected SoundEvent    getHurtSound() {
        return (CraftAndConquerSounds.NPCDialogSoundEvent);
    }

    @Nullable
    @Override
    protected SoundEvent    getDeathSound() {
        return (CraftAndConquerSounds.NPCDialogSoundEvent);
    }

    @Override
    protected SoundEvent    getFallSound(int heightIn) {
        return (CraftAndConquerSounds.NPCDialogSoundEvent);
    }

    @Override
    protected SoundEvent    getSwimSound() {
        return (CraftAndConquerSounds.NPCDialogSoundEvent);
    }

    @Override
    protected SoundEvent    getSplashSound() {
        return (CraftAndConquerSounds.NPCDialogSoundEvent);
    }

    public static ThreadDownloadImageData   getDownloadImageSkin(ResourceLocation resourceLocationIn) {
        TextureManager          textureManager = Minecraft.getMinecraft().getTextureManager();
        ThreadDownloadImageData threadDownloadImageData;

        threadDownloadImageData = new ThreadDownloadImageData(null, "http://imgur.com/Z0pbA3P.png", DefaultNPCSkin.getDefaultSkin(), new ImageBufferDownload() {

            @Override
            public BufferedImage    parseUserSkin(BufferedImage image) {
                return (image);
            }

        });

        textureManager.loadTexture(resourceLocationIn, threadDownloadImageData);

        return (threadDownloadImageData);
    }

    private void            updateNPCSlots(InventoryBasic inventoryBasic) {
        if (!this.worldObj.isRemote) {
            this.setItemStackToSlot(EntityEquipmentSlot.HEAD, inventoryBasic.getStackInSlot(0));
            this.setItemStackToSlot(EntityEquipmentSlot.CHEST, inventoryBasic.getStackInSlot(1));
            this.setItemStackToSlot(EntityEquipmentSlot.LEGS, inventoryBasic.getStackInSlot(2));
            this.setItemStackToSlot(EntityEquipmentSlot.FEET, inventoryBasic.getStackInSlot(3));
            this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, inventoryBasic.getStackInSlot(4));
            this.setItemStackToSlot(EntityEquipmentSlot.OFFHAND, inventoryBasic.getStackInSlot(5));
        }
    }

    @Override
    public void             onInventoryChanged(InventoryBasic inventoryBasic) {
        this.updateNPCSlots(inventoryBasic);
    }
}

 

I must admit I am really confused. I got inspired by the GuiContainerCreative, ContainerHorseInventory (the container for horses) and EntityZombie, but it seems that I am going in the wrong way. What I would like to achieve is that, when I place an item in the Slots of my container, it updates the equipment and hands ItemStack arrays of my Entity (+ rendering the items on the entity, like Zombies or Skeletons but this should work since my Entity model extends from ModelBiped).

 

I think I am complicating the task for no reason. Could you guys highlight me on what should I achieve this ?

 

Thank you for your time. :)

Squirrel ! Squirrel ! Squirrel !

Link to comment
Share on other sites

Good evening,

 

After checking my code again, I don't really understand why it does not work at all. The EntityLivingBase#setItemStackToSlot(EntityEquipmentSlot slotIn, @Nullable ItemStack stack) method should work, but in my case I think I'm not using it in the right way.

 

Basically, I've made some simple tests to check if my entity could currently hold those items. One of this test is very simplistic: when I proceed a right-click on my entity with an equipment item, I call the method above from the server.

 

NPCInteractEvent:

    @SubscribeEvent
    public void     onEntityNPCInteract(PlayerInteractEvent.EntityInteract event) {
        ItemStack   heldItem = event.getEntityPlayer().getHeldItem(EnumHand.MAIN_HAND);
        ItemBase    npcManager = CraftAndConquerItems.npcManager;

        if (event.getTarget() instanceof EntityNPCDialog && event.getHand().equals(EnumHand.MAIN_HAND) && heldItem != null) {
            if (!event.getWorld().isRemote) {
                if (heldItem.getItem().equals(npcManager))
                    event.getEntityPlayer().openGui(CraftAndConquer.instance, GuiHandler.CAC_NPC_GUI, event.getWorld(), event.getTarget().getEntityId(), 0, 0);
                else if (heldItem.getItem().equals(Items.DIAMOND_HELMET))
                    event.getTarget().setItemStackToSlot(EntityEquipmentSlot.HEAD, heldItem);
                else if (heldItem.getItem().equals(Items.DIAMOND_CHESTPLATE))
                    event.getTarget().setItemStackToSlot(EntityEquipmentSlot.CHEST, heldItem);
                else if (heldItem.getItem().equals(Items.DIAMOND_LEGGINGS))
                    event.getTarget().setItemStackToSlot(EntityEquipmentSlot.LEGS, heldItem);
                else if (heldItem.getItem().equals(Items.DIAMOND_BOOTS))
                    event.getTarget().setItemStackToSlot(EntityEquipmentSlot.FEET, heldItem);
                else if (heldItem.getItem().equals(Items.DIAMOND_SWORD))
                    event.getTarget().setItemStackToSlot(EntityEquipmentSlot.MAINHAND, heldItem);
                else if (heldItem.getItem().equals(Items.SHIELD))
                    event.getTarget().setItemStackToSlot(EntityEquipmentSlot.OFFHAND, heldItem);
            }
        }
    }

 

The event does actually work, even when I disconnect/reconnect, the equipment has been successfully saved in NBTTags, here is the result:

 

width=800 height=433https://images.discordapp.net/.eJwFwVEOgyAMANC7cABqO4vM2xAkaNSWQM0-lt19733d0y-3ut2sjRVgO0bWvvlh2lMtvqrWq6R2DJ_1hmSW8n4XsQEUkAnDRMSMFJjeQAu9GJc4R8LIEyLBI6foR3yT6n5_BBkixQ.vHGe_Qq0t2Fo_YJzemzOELu6gjU?width=1250&height=677[/img]

 

But still, through a GUI, my code does not work. What is strange is that, when those entities are wearing the equipment in the event andler way, it does not appear in the GUI:

 

NhUKOBC.png

 

I don't really want to add an Inventory in my Entity class, since I only want to access to the existing inventoryHands and inventoryArmor fields of the EntityLiving class. I don't see the point to add an Inventory that includes what already exists in their own way. I feel that this is something stupid that I am missing, but I don't see what.

Squirrel ! Squirrel ! Squirrel !

Link to comment
Share on other sites

What do you think this does,

this.entityInventory = new InventoryBasic("NPC Inventory", true,
                Iterables.size(this.inventoryArmor) + Iterables.size(this.heldEquipment));

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

What do you think this does,

this.entityInventory = new InventoryBasic("NPC Inventory", true,
                Iterables.size(this.inventoryArmor) + Iterables.size(this.heldEquipment));

 

It does create an empty InventoryBasic, which I use it to make the link between the already-existing inventoryHands and inventoryArmor fields from LivingEntity and my Container, but it does not seem to work at all.  ;D I use them because Slots don't support Iterable<ItemStack> to be constructed.

Squirrel ! Squirrel ! Squirrel !

Link to comment
Share on other sites

Create a custom Inventory that does then.

 

What do you mean by creating a "custom Inventory" ? Is the InventoryBasic not sufficient for this kind of things ?

 

Also, I am surprised that nobody tried to develop this in Forge. I searched few minutes on google and different forge forums and I have not been able to find a single topic talking about this.  :-[

Squirrel ! Squirrel ! Squirrel !

Link to comment
Share on other sites

Create a custom Inventory that does then.

 

What do you mean by creating a "custom Inventory" ? Is the InventoryBasic not sufficient for this kind of things ?

 

Also, I am surprised that nobody tried to develop this in Forge. I searched few minutes on google and different forge forums and I have not been able to find a single topic talking about this.  :-[

Create a class that extends InventoryBasic. Then in the constructor have it pass ItemStack Arrays and loop through them and add them to the already existing array with setInventorySlotContents or addItem.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

Good evening,

 

So I tried the suggestion of Animefan8888:

 

Create a class that extends InventoryBasic. Then in the constructor have it pass ItemStack Arrays and loop through them and add them to the already existing array with setInventorySlotContents or addItem.

 

Here is the code.

 

NPCInventoryBasic:

public class    NPCInventoryBasic extends InventoryBasic {

    public      NPCInventoryBasic(Iterable<ItemStack> inventoryArmor, Iterable<ItemStack> inventoryHands) {
        super("NPC Inventory Basic", true, Iterables.size(inventoryArmor) + Iterables.size(inventoryHands));

        Iterable<ItemStack> equipment = Iterables.concat(inventoryArmor, inventoryHands);

        for (int index = 0; index < Iterables.size(inventoryArmor) + Iterables.size(inventoryHands); ++index) {
            this.setInventorySlotContents(index, Iterables.get(equipment, index));
        }
    }
}

 

ContainerNPCInventory:

public class                ContainerNPCInventory extends Container {

    private static final EntityEquipmentSlot[]  VALID_EQUIPMENT_SLOTS = new EntityEquipmentSlot[] {
            EntityEquipmentSlot.HEAD,
            EntityEquipmentSlot.CHEST,
            EntityEquipmentSlot.LEGS,
            EntityEquipmentSlot.FEET
    };

    private EntityNPCDialog         entityNPC;

    private final InventoryPlayer   playerInventory;
    private NPCInventoryBasic       entityInventory;

    public                  ContainerNPCInventory(EntityPlayer playerIn, EntityNPCDialog entityNPC) {
        Iterable<ItemStack> inventoryArmor;
        Iterable<ItemStack> heldEquipment;

        this.entityNPC = entityNPC;
        inventoryArmor = entityNPC.getArmorInventoryList();
        heldEquipment = entityNPC.getHeldEquipment();

        if (!playerIn.worldObj.isRemote) {
            inventoryArmor.forEach(armor -> CraftAndConquer.logger.log(Level.INFO, "[ARMOR] I am:\t" + (armor != null ? armor.getDisplayName() : "null")));
            heldEquipment.forEach(hand -> CraftAndConquer.logger.log(Level.INFO, "[HANDS] I am:\t" + (hand != null ? hand.getDisplayName() : "null")));
        }


        this.playerInventory = playerIn.inventory;
        this.entityInventory = new NPCInventoryBasic(inventoryArmor, heldEquipment);
        //this.entityInventory.addInventoryChangeListener(this.entityNPC);

        for (int index = 0; index < 4; ++index) {
            final EntityEquipmentSlot   equipmentSlot = ContainerNPCInventory.VALID_EQUIPMENT_SLOTS[index];

            this.addSlotToContainer(new Slot(this.entityInventory, index, 8, 8 + index * 18) {

                @Override
                public void onSlotChanged() {
                    entityNPC.setItemStackToSlot(equipmentSlot, this.getStack());
                    super.onSlotChanged();
                }

                @Override
                public boolean  isItemValid(@Nullable ItemStack stack) {
                    return (stack != null && stack.getItem().isValidArmor(stack, equipmentSlot, entityNPC));
                }

                @Override
                public int      getSlotStackLimit() {
                    return (1);
                }

                @Nullable
                @Override
                public String   getSlotTexture() {
                    return (ItemArmor.EMPTY_SLOT_NAMES[equipmentSlot.getIndex()]);
                }
            });
        }

        this.addSlotToContainer(new Slot(this.entityInventory, 4, 77,  {

            @Override
            public void onSlotChanged() {
                entityNPC.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, this.getStack());
                super.onSlotChanged();
            }
        });

        this.addSlotToContainer(new Slot(this.entityInventory, 5, 95,  {

            @Override
            public void onSlotChanged() {
                entityNPC.setItemStackToSlot(EntityEquipmentSlot.OFFHAND, this.getStack());
                super.onSlotChanged();
            }

            @Nullable
            @Override
            public String   getSlotTexture() {
                return ("minecraft:items/empty_armor_slot_shield");
            }
        });

        for (int index = 0; index < 9; ++index) {
            this.addSlotToContainer(new Slot(this.playerInventory, index, 9 + index * 18, 112));
        }
    }

    @Override
    public boolean          canInteractWith(EntityPlayer playerIn) {
        return (true);
    }
}

 

I think it works.

 

tPGMmUl.png

 

Well, it "half" works.

 

bIdQzbF.png

 

Wtf haha. When I place the equipment in the different slots, it updates the entity correctly as it equips and renders all the stuff nicely. But when I leave the GUI and open it again, this kind of thing happens as above (meaning, all the armor pieces are reversed, but not the hands). I just don't understand why, the indexes of the EntityEquipmentSlot enums are correct, the slots in my container didn't change, so what could happen ??

Squirrel ! Squirrel ! Squirrel !

Link to comment
Share on other sites

Minecraft started it's IDs for armor at zero = to boots, and so on upwards. I know a little strange right.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

Minecraft started it's IDs for armor at zero = to boots, and so on upwards. I know a little strange right.

 

g1352129205367343592.jpg

 

I figured it out by myself and your comment confirmed what I noticed, thanks !

 

I managed to make it work without a custom inventory and by using EntityLiving#getItemStackFromSlot(EntityEquipmentSlot slotIn) and EntityLiving#setItemStackToSlot(EntityEquipmentSlot slotIn, @Nullable ItemStack stack) instead of going through the Iterable<ItemStack>.

 

EntityNPCDialog:

public class                EntityNPCDialog extends EntityLiving {

//    private static final DataParameter<Integer> TEXTURE_INDEX = EntityDataManager.<Integer>createKey(EntityNPCDialog.class, DataSerializers.VARINT);

    public                  EntityNPCDialog(World worldIn) {
        super(worldIn);
    }

    @Override
    protected void          initEntityAI() {
        super.initEntityAI();

        this.tasks.addTask(0, new EntityAISwimming(this));
        this.tasks.addTask(1, new EntityAIWatchClosest(this, EntityPlayer.class, 16.0F, 42.0F));
        this.tasks.addTask(2, new EntityAILookIdle(this));
    }

    @Override
    public boolean          canBePushed() {
        return (false);
    }

    @Override
    public boolean          isPushedByWater() {
        return (false);
    }

    @Override
    protected boolean       canDespawn() {
        return(false);
    }

    @Override
    public void             addVelocity(double x, double y, double z) {}

    @Override
    public boolean          canRiderInteract() {
        return (false);
    }

    @Override
    public boolean          isEntityInvulnerable(DamageSource source) {
        return (false);
    }

    @Override
    public boolean          isPotionApplicable(PotionEffect potioneffectIn) {
        return (false);
    }

    @Override
    public boolean          isImmuneToExplosions() {
        return (false);
    }

    @Nullable
    @Override
    protected SoundEvent    getAmbientSound() {
        return (CraftAndConquerSounds.NPCDialogSoundEvent);
    }

    @Nullable
    @Override
    protected SoundEvent    getHurtSound() {
        return (CraftAndConquerSounds.NPCDialogSoundEvent);
    }

    @Nullable
    @Override
    protected SoundEvent    getDeathSound() {
        return (CraftAndConquerSounds.NPCDialogSoundEvent);
    }

    @Override
    protected SoundEvent    getFallSound(int heightIn) {
        return (CraftAndConquerSounds.NPCDialogSoundEvent);
    }

    @Override
    protected SoundEvent    getSwimSound() {
        return (CraftAndConquerSounds.NPCDialogSoundEvent);
    }

    @Override
    protected SoundEvent    getSplashSound() {
        return (CraftAndConquerSounds.NPCDialogSoundEvent);
    }

//    public static ThreadDownloadImageData   getDownloadImageSkin(ResourceLocation resourceLocationIn) {
//        TextureManager          textureManager = Minecraft.getMinecraft().getTextureManager();
//        ThreadDownloadImageData threadDownloadImageData;
//
//        threadDownloadImageData = new ThreadDownloadImageData(null, "http://imgur.com/Z0pbA3P.png", DefaultNPCSkin.getDefaultSkin(), new ImageBufferDownload() {
//
//            @Override
//            public BufferedImage    parseUserSkin(BufferedImage image) {
//                return (image);
//            }
//
//        });
//
//        textureManager.loadTexture(resourceLocationIn, threadDownloadImageData);
//
//        return (threadDownloadImageData);
//    }
}

 

ContainerNPCInventory:

public class                ContainerNPCInventory extends Container {

    public static final EntityEquipmentSlot[]   VALID_EQUIPMENT_SLOTS = new EntityEquipmentSlot[] {
            EntityEquipmentSlot.HEAD,
            EntityEquipmentSlot.CHEST,
            EntityEquipmentSlot.LEGS,
            EntityEquipmentSlot.FEET,
            EntityEquipmentSlot.MAINHAND,
            EntityEquipmentSlot.OFFHAND
    };

    private EntityNPCDialog         entityNPC;

    private final InventoryPlayer   playerInventory;
    private InventoryBasic          entityInventory;

    public                  ContainerNPCInventory(EntityPlayer playerIn, EntityNPCDialog entityNPC) {
        this.entityNPC = entityNPC;


        this.playerInventory = playerIn.inventory;
        this.entityInventory = new InventoryBasic("NPC Inventory", true, ContainerNPCInventory.VALID_EQUIPMENT_SLOTS.length);
        for (int index = 0; index < ContainerNPCInventory.VALID_EQUIPMENT_SLOTS.length; ++index) {
            this.entityInventory.setInventorySlotContents(index, entityNPC.getItemStackFromSlot(ContainerNPCInventory.VALID_EQUIPMENT_SLOTS[index]));
        }

        for (int index = 0; index < 4; ++index) {
            final EntityEquipmentSlot   equipmentSlot = ContainerNPCInventory.VALID_EQUIPMENT_SLOTS[index];

            this.addSlotToContainer(new Slot(this.entityInventory, index, 8, 8 + index * 18) {

                @Override
                public void onSlotChanged() {
                    entityNPC.setItemStackToSlot(equipmentSlot, this.getStack());
                    super.onSlotChanged();
                }

                @Override
                public boolean  isItemValid(@Nullable ItemStack stack) {
                    return (stack != null && stack.getItem().isValidArmor(stack, equipmentSlot, entityNPC));
                }

                @Override
                public int      getSlotStackLimit() {
                    return (1);
                }

                @Nullable
                @Override
                public String   getSlotTexture() {
                    return (ItemArmor.EMPTY_SLOT_NAMES[equipmentSlot.getIndex()]);
                }
            });
        }

        this.addSlotToContainer(new Slot(this.entityInventory, 4, 77,  {

            @Override
            public void onSlotChanged() {
                entityNPC.setItemStackToSlot(ContainerNPCInventory.VALID_EQUIPMENT_SLOTS[4], this.getStack());
                super.onSlotChanged();
            }
        });

        this.addSlotToContainer(new Slot(this.entityInventory, 5, 95,  {

            @Override
            public void onSlotChanged() {
                entityNPC.setItemStackToSlot(ContainerNPCInventory.VALID_EQUIPMENT_SLOTS[5], this.getStack());
                super.onSlotChanged();
            }

            @SideOnly(Side.CLIENT)
            @Nullable
            @Override
            public String   getSlotTexture() {
                return ("minecraft:items/empty_armor_slot_shield");
            }
        });

        for (int index = 0; index < 9; ++index) {
            this.addSlotToContainer(new Slot(this.playerInventory, index, 9 + index * 18, 112));
        }
    }

    @Override
    public boolean          canInteractWith(EntityPlayer playerIn) {
        return (true);
    }
}

 

It seems to work on multiplayer, I tested and I didn't have any crash. Also, the server stops correctly and restarts correctly with the correct equipement on the Entity.

 

One last question though: when putting a debug message in Slot#onSlotChanged() method, I noticed that this method was called multiple times, either client side or server side. Why is that ? And does it influence the Entity stats when I put the equipment once ? (like summing the armor stats because the method is called multiple times)

Squirrel ! Squirrel ! Squirrel !

Link to comment
Share on other sites

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.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.