Jump to content

Recommended Posts

Posted

Hey there. My mod summons out tamable entities for the player and the entities act as a companion much like a wolf. It works flawlessly on Single Player. However in trying it out on my server setting I am running into some issues. Well not issues, more like oddities.

 

The entities get summoned just fine and they are "owned" by the player. Each entity is unqiue yadayada and it works great. However on a server another player can only see the entity for like a second or two and then it disappears to the other player. However to the original summoner it remains visible and functioning. And it does still function it is just that another player cannot see it. But the player who summoned it can see what it is doing and everything. I don't know why it is happening. Any ideas on how I can "fix" it so that once the player summons the entity another player will see it summoned and it will remain visible to them (the other player) and not just the owner/summoner of the entity? Here is an example class:

 

EntityClass:

http://pastebin.com/HKGndwnZ

 

RenderClass:

http://pastebin.com/Mc6zMeKc 

**Note I tried this with and without the commenting of the SideOnly(Side.CLIENT) same effect no matter which way I do it. Although if I recall the server doesn't do any rendering so proper function would be to have it uncommented.

 

ClientProxy

http://pastebin.com/PWAVer6u

 

ServerProxy

http://pastebin.com/LtxRrr7y

 

I'm at a total loss because it works perfectly on Single Player and I am sure it is such a simple oversight on my part but any help is appreciated.

Posted

SNIP

 

No dice. Gave it a shot and enabled the override methods for writeNBT and readNBT and the exact same error is happening.

 

The code is the same as this: http://pastebin.com/HKGndwnZ

However now lines read:

        @Override
        public void writeEntityToNBT(NBTTagCompound p_70014_1_)
        {
                super.writeEntityToNBT(p_70014_1_);
                p_70014_1_.setBoolean("Hostile", this.isAngry());
                //p_70014_1_.setByte("ColorCollar", (byte)this.getCollarColor());
        }

        @Override
        public void readEntityFromNBT(NBTTagCompound p_70037_1_)
        {
                super.readEntityFromNBT(p_70037_1_);
                this.setAngry(p_70037_1_.getBoolean("Hostile"));

                //if (p_70037_1_.hasKey("ColorCollar", 99))
                //{
                //      this.setCollarColor(p_70037_1_.getByte("ColorCollar"));
                //}
        }

 

So they are no longer written to do nothing.

Posted

Bumping this, since I made the edit and did the testing and still had the same result. Any ideas from anyone? What makes this so difficult is it works great on Single Player. Not a single bug/glitch.

Posted

Can you show the code that spawns the entity, then? I don't see anything in the render or entity class that should make it behave differently in multiplayer.

 

However, on a side note: looking at your renderer registrations in the ClientProxy and then at your RenderCapricorn class, I think you'd be a lot better off with a little OOP action, e.g.:

@SideOnly(Side.CLIENT)
public class RenderGenericEntity extends RenderBiped
{
    private final ResourceLocation texture;

    public RenderCapricorn(ModelBiped model, float shadowSize, String textureName)
    {
        super(model, shadowSize);
        this.texture = new ResourceLocation(RefStrings.MODID + ":textures/entity/" + textureName + ".png");
    }

    @Override
    protected ResourceLocation getEntityTexture(Entity entity) {
        return this.texture;
    }
}

Then, every single one of your registrations can use the same class:

RenderingRegistry.registerEntityRenderingHandler(EntityCapricorn.class, new RenderGenericEntity(new ModelBiped(), 0.3F, "capricorn"));

Just a suggestion.

Posted

Sure thing. And that is not a bad idea to have the generic rendering. Thanks for the tip on that.

 

Here is the class that spawns the entity in the world:

http://pastebin.com/6FnQs8G4

 

Specifically it is this. Line 95 - 185

 

       public boolean onItemUse(ItemStack itemstack, EntityPlayer player, World world, int x, int y, int z, int meta, float p_77648_8_, float p_77648_9_, float p_77648_10_)
        {
                if (player.canPlayerEdit(x, y, z, meta, itemstack) && !world.isRemote)
                {
                        try {
                                Entity e = entityClasses[itemstack.getItemDamage()].getConstructor(World.class).newInstance(world);
                                Entity gem2 = EntityGemini2.class.getConstructor(World.class).newInstance(world);
                                Entity pis2 = EntityPisces2.class.getConstructor(World.class).newInstance(world);
                                Random randCordY = new Random();
                                Random randCordZ = new Random();
                                int sF = SpiritCostFormula[itemstack.getItemDamage()];
                                int sB = SpiritCostBase[itemstack.getItemDamage()];
                                int sC = SpiritSummonCost = (int)(Math.ceil((player.experienceLevel) / sF));
                                ExtendedPlayer instance = ExtendedPlayer.get(player);
                                if (!itemstack.hasTagCompound())
                                {
                                        itemstack.setTagCompound(new NBTTagCompound());
                                        itemstack.stackTagCompound.setString("Contract With: ", player.getDisplayName());
                                        itemstack.stackTagCompound.setInteger("Summoned Amount: ", summonedAmount);
                                }
                                else if (itemstack.hasTagCompound())
                                {
                                        String owner = itemstack.stackTagCompound.getString("Contract With: ");
                                        if (!owner.equals(player.getDisplayName()))
                                        {
                                                player.addChatComponentMessage(new ChatComponentTranslation ("\u00A74You are not the owner of this contract!"));
                                                return false;
                                        }
                                }
                                if (!instance.summonedState[itemstack.getItemDamage()])
                                {
                                        if ( (player.experienceTotal > sC) && (player.experienceLevel >= sB) )
                                        {
                                                int rY = randCordY.nextInt(3);
                                                int rZ = randCordZ.nextInt(3) + 1;
                                                e.setPosition(player.posX, player.posY + rY, player.posZ - rZ);
                                                ((EntityTameable) e).func_152115_b(player.getUniqueID().toString());
                                                if (itemstack.getItemDamage() == 
                                                {
                                                        gem2.setPosition(player.posX + 2, player.posY + rY, player.posZ - rZ);
                                                        ((EntityTameable) gem2).func_152115_b(player.getUniqueID().toString());
                                                        world.spawnEntityInWorld(e); //Gemini 1 Master
                                                        world.spawnEntityInWorld(gem2); //Gemini 2 Mirror
                                                }
                                                else if (itemstack.getItemDamage() == 10)
                                                {
                                                        pis2.setPosition(player.posX + 2, player.posY + rY, player.posZ - rZ);
                                                        ((EntityTameable) pis2).func_152115_b(player.getUniqueID().toString());
                                                        world.spawnEntityInWorld(e); //Pisces 1 Master
                                                        world.spawnEntityInWorld(pis2); //Pisces 2 Mirror
                                                }
                                                else
                                                {
                                                        world.spawnEntityInWorld(e);
                                                }
                                                world.playSoundAtEntity(player, RefStrings.MODID + ":summon-bell", 0.7F, 1.0F);
                                                player.addChatComponentMessage(new ChatComponentTranslation ("Come forth "+celKeys2[itemstack.getItemDamage()] +"! I summon you!"));
                                                instance.setSummoned(itemstack.getItemDamage(), true);
                                                int newAmount = itemstack.stackTagCompound.getInteger("Summoned Amount: ") + 1;
                                                itemstack.stackTagCompound.setInteger("Summoned Amount: ", newAmount);
                                                if(sC > sB){ player.addExperienceLevel((int)-sC); return true; }else{ player.addExperienceLevel((int)-sB); return true; }
                                        }
                                        else
                                        {
                                                player.addChatComponentMessage(new ChatComponentTranslation ("\u00A74You do not have enough Celestial Power!"));
                                                return false;
                                        }
                                }
                                else
                                {
                                        player.addChatComponentMessage(new ChatComponentTranslation (celKeys2[itemstack.getItemDamage()]+" is already summoned!"));
                                        return false;
                                }

                        } catch (InstantiationException e) {
                                e.printStackTrace();
                        } catch (IllegalAccessException e) {
                                e.printStackTrace();
                        } catch (IllegalArgumentException e) {
                                e.printStackTrace();
                        } catch (InvocationTargetException e) {
                                e.printStackTrace();
                        } catch (NoSuchMethodException e) {
                                e.printStackTrace();
                        } catch (SecurityException e) {
                                e.printStackTrace();
                        }
                }
                else if (itemstack.getItemDamage() > 11){};
                return false;
        }

 

Posted

Sure here is the function I use to register my summonable entities:

 

	public static void registerSummon(Class entityClass, String name){
	int entityId = EntityRegistry.findGlobalUniqueEntityId();
	long x = name.hashCode();
	Random random = new Random(x);
	int mainColor = random.nextInt() * 16777215;
	int subColor = random.nextInt() * 16777215;		
	EntityRegistry.registerGlobalEntityID(entityClass, name, entityId);
	EntityRegistry.registerModEntity(entityClass, name, entityId, MainRegistry.modInstance, 64, 1, true);
	EntityList.entityEggs.put(Integer.valueOf(entityId), new EntityList.EntityEggInfo(entityId, mainColor, subColor));
}

Full Handler: http://pastebin.com/KZQAZdRc

 

They are all registered here:

		EntityHandler.registerSummon(EntityAries.class, "Aries");
	EntityHandler.registerSummon(EntityAquarius.class, "Aquarius");
	EntityHandler.registerSummon(EntityTaurus.class, "Taurus");
	EntityHandler.registerSummon(EntityCapricorn.class, "Capricorn");
	EntityHandler.registerSummon(EntityLeo.class, "Leo");
	EntityHandler.registerSummon(EntityVirgo.class, "Virgo");
	EntityHandler.registerSummon(EntityCancer.class, "Cancer");
	EntityHandler.registerSummon(EntityLibra.class, "Libra");
	EntityHandler.registerSummon(EntityGemini.class, "GeminiMaster");
	EntityHandler.registerSummon(EntityGemini2.class, "GeminiMirror");
	EntityHandler.registerSummon(EntityPisces.class, "PiscesMaster");
	EntityHandler.registerSummon(EntityPisces2.class, "PiscesMirror");
	EntityHandler.registerSummon(EntityScorpio.class, "Scorpio");
	EntityHandler.registerSummon(EntitySagittarius.class, "Sagittarius");

Full Main: http://pastebin.com/jSue2jxb

 

 

EDIT: In doing some thinking. Is it possible that I need some type of packet handler? I mean I really don't understand why I would since my normal hostile mobs don't need one and work just fine. But because the issue is not that the Tameable entites aren't working its simply that they disappear after about 5 seconds to other players. They still function, they still work, and the original summoner still sees them in action. It really is just they disappear to other players.

 

To be quite honest I read a number of tutorials on packets (and all of them say they are really easy) and still am lost with them so try my best to stay away from them. But could it be possible that because this seems to be a visual glitch that only occurs on a server that it is a packet issue?

 

 

 

 

Posted

The most I can recommend is going through your github history and seeing how far along your entity is working, then you can see which commit broke it

"you seem to be THE best modder I've seen imo."

~spynathan

 

ლ(́◉◞౪◟◉‵ლ

Posted

The most I can recommend is going through your github history and seeing how far along your entity is working, then you can see which commit broke it

 

I'm going to take a guess and say you responded to the wrong post lol? There is no github here and nothing is broken or was broken. They work just fine. The problem is after a few seconds they disappear to other players but their owners can still see them. They never stop functioning. This is visual.

Posted

I think you have to go about old-fashioned debug. Basically confirm that each part of your code is working as expected. I find this is easiest to do by adding console statements (System.out.println()) at judicious points in the code.

 

For example, I would put statements in the constructor and the setDead() method for the entity and see how the statements compare on the server and client. You might also want to put a statement in the onLivingUpdate() method as well -- this will spam a lot of statements but it would be interesting if they stopped.

 

It is also good to have some possible theories to guide where to look. You say that the entity still "works" but can't be seen by the other player after some point. That means that either the entity is dead on that client, that the rendering is doing something funny, or that the location of the entity has changed somehow to get out of sync and it is truly out of view.

 

For example, to test idea that it is the renderer that is screwing up you might want to put console statements in the render() call to confirm that it continues to get called on the client even after the entity disappears.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted

Thank you Jabelar. I will for sure do some checking however atm, I dont not have access to my computer now as I am at work however this just occurred to me and I want to jot it down before I forget and collect some thoughts.

 

Is it possible that because in my on-update function of the entity I check to see if the owner is null. And if the owner is null we kill the entity (as these creatures are not supposed to persist).

 

Is it possible that the entity is being spawned server side however is not being set up with the owner on the server but only client side. Which would explain why the server is "killing the entity" but the client is not because as far as the client is concerned it has an owner so there is no reason to kill it.

 

In other words the item is spawning the entity correctly in the server. However, because nothing in my entity class writes the owner of the entity to the NBT server side, as far as the server is concerned, there is no owner so it kills it. Yet on the client side of things we set the owner in the item class right here:

((EntityTameable) e).func_152115_b(player.getUniqueID().toString());

 

So the client thinks that there is an owner so it doesn't kill it hence why the client still sees it. So in other words perhaps I need to try and establish somewhere in the EntityClass using this.getOwner() that this should be written to NBT so the server knows there is an owner and not to kill it. Because on the server side the this.getOwner() check in the entity class means nothing.

 

Once again I don't have access to editing my code atm as I am at work. This is all speculation. Granted I still have to do the debugging when I get home with the System.out.println() to be sure. But this just all occurred to me now and I wanted to present this theory. Is it plausible?

Posted

You're absolutely right on, but not :P

 

If the server side version is killed, however, the client side version is as well, so that's not the problem.

 

The problem is (very probably) that the CLIENT-side version doesn't know about the owner, and is setting the entity to dead only on the client side. To solve that, in your #onUpdate check only set the entity to dead on the server side (i.e. when the world is NOT remote), which is usually how you should do it anyway.

 

That would explain the entity 'disappearing' on clients while still functioning on the server, but the mystery remains why it would still render for the player that summoned it. Very strange.

 

Anyway, let us know if that fixes it. Btw, you should not use global entity IDs or the vanilla spawn egg system when registering your entity, only EntityRegistry#registerModEntity. If you need a spawn egg, you should implement it yourself or, if you can update to 1.8, take advantage of Forge's implementation.

Posted

That would explain the entity 'disappearing' on clients while still functioning on the server, but the mystery remains why it would still render for the player that summoned it. Very strange.

 

I agree that not knowing what the owner is on the client is likely theory. I think you can also explain the above if the ownership is set in the interact() method (which I believe runs on both sides) then the code would run on both the summoning player's client and on the server, so both of those would get the owner, but you would need to explicitly sync the other clients to also know the owner.  Now it seems that's he's using datawatcher like wolf does so it should sync, but I'm just saying that it is quite possible for something that happens in the interact() method to be synced on the interacting player's client and server but not necessarily other clients.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted

I think you can also explain the above if the ownership is set in the interact() method (which I believe runs on both sides)

That would be true if that was the method used to spawn the entity, but it is not - the entity is spawned from an Item using #onItemUse, with the whole block of code nested in if (!world.isRemote). The entity's #interact code that is responsible for taming is also only run on the server (due to nesting within if (!world.isRemote), not because the method is only called on the server [it's not]). You can see the code a few posts back.

 

But you raise a good point about DataWatcher... the owner's UUID should be available to all clients, so calling #getOwner should, in theory, return the same value for everyone... hell, I'm stumped. There's got to be some other interaction going on somewhere that is clearing the owner for other players, or something...

Posted

Well here is an update I wrote in some debugging lines and I can confirm (assuming I did it correctly) that both the client (who summoned) and the server are recognizing that there is an owner and are syncing properly. I simply wrote the debug line to output who the owner is and I wrote it in the onUpdate method. So naturally my logs got spammed to hell with message however I can confirm that when running eclipse's built in server everything loaded fine, then I started up the client and connected to my localhost. Upon summoning the entity both my client logs AND the server logs were outputting that the ower is EntityPlayerMP(Player556(or whatever name was as assigned at that time) as well as giving the X, Y, Z of the entity and it was accurately tracking the coords and was synced between both client and server.

 

So I then proceeded to add two debug lines to my code where the entity is setDead() a point 1 elimination and a point 2 elimination. Neither of the lines got output until the specific conditions were met. Point 1 was only triggered if the player ran out of EXP levels which is intended. And point 2 was only triggered if the owner logged out and became null. So both of these triggered correctly and the messages output both the server logs and the client logs. So I don't know what both of your determinations might be on this as you guys are the experts lol (I am merely learning this as I go) but it seems to me they are synced properly.

 

coolAlias - I will also try your method to check if it is dead on the server-side just to try it as right now after the above test I am grasping at straws here. Your method does make sense (if I understood it correctly) because if the other clients never summoned the entity the code is likely reading to them (the other clients), because there is no owner, it is "dead" hence a null is returned too them basically saying "Hey this entity in front of me has no owner therefore this is a dead entity." But to the server and to the client who did summon it, it is very much alive so they see it. As they are the owners.

 

Now for the kicker (which could just be a fluke as it hasn't happened on my live server). I did about 8 tests of running the client and the server. 2 or 3 of those tests had an odd result. The summoned entity disappeared to me (the owner). HOWEVER, the server still recognized me as the owner and was synced correctly with the client and was functioning as it should, I just could no longer see it. However, my EXP levels were being diminished properly and the logs were spamming out that I was still the original owner and was still keeping the co-ords of the entity and the server in sync. So now it appears we may have come full circle and the issue is not that it is not syncing properly but perhaps it is not rendering properly OR more likely there is some disconnect with the rendering client side?

 

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.