Jump to content

Saving "entity rendering" data on the client side


jabelar

Recommended Posts

Sorry but this needs a long explanation...

 

Okay, I've run into an interesting problem. I've made an entity animation system for a mod and it works well -- the server decides to initiate an animation based on the entity and sends a packet with the animation id to all clients, then the clients step through an animation sequence consisting of poses and tweens (intermediate steps between poses). That works well, except if you try to save the game in the middle of an animation it doesn't remember it.

 

Since the server knows the last animation it sent, i'm able to save the animation ID as NBT and then load it and send it as spawn data. However, that isn't good enough because that starts the animation over at the beginning. I really want to remember where it is in the animation.

 

Only the client really knows where it is in the animation. I could have the client send packets back to server but that would really spam the network since all the clients would be doing it every tick.

 

So I think what I need to do is to save the animation point on the client side. But what is recommended way to store entity data on the client side? I know how to do file access in Java so I know I could create my own save system, but it would be complicated as it would need to be mapped to the world and such.

 

Is there already some way to store per-entity data on client side?

 

The only other thing I could think of is whether there is a way that before the server saves the game that I could have it send a packet to query the clients, but that also seems a bit complicated.

 

Any ideas?

 

EDIT #1: Configurations don't seem a good way to go because this isn't fixed set of data, but variable depending on number of saves and entities per world.

 

EDIT #2: I'm thinking maybe the best way is to estimate it by having the server guess at where it is in the animation. The problem is that the animations are defined client side only so I guess I'll have to have the server query the client to give the number of poses and tweens for each animation, then count ticks on the server.

 

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

Link to comment
Share on other sites

Save the animation state server side and then make your entity implement IAdditionalSpawnData so you can send information about what the entity is doing when it spawns. There you can send how far between animations and which animation is playing

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

~spynathan

 

ლ(́◉◞౪◟◉‵ლ

Link to comment
Share on other sites

Save the animation state server side and then make your entity implement IAdditionalSpawnData so you can send information about what the entity is doing when it spawns. There you can send how far between animations and which animation is playing

 

The problem is that the server doesn't know the animation state (it knows the id of the last animation but doesn't know anything about the animation itself -- these are contained in client-side only classes), and keeping it up to date would require every client to send a packet with information on every entity every tick. That would kinda spam the network, which is what I'm hoping to avoid.

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

Link to comment
Share on other sites

Save the animation state server side and then make your entity implement IAdditionalSpawnData so you can send information about what the entity is doing when it spawns. There you can send how far between animations and which animation is playing

 

The problem is that the server doesn't know the animation state (it knows the id of the last animation but doesn't know anything about the animation itself -- these are contained in client-side only classes), and keeping it up to date would require every client to send a packet with information on every entity every tick. That would kinda spam the network, which is what I'm hoping to avoid.

 

If this is a looping animation or something, then maybe you could use the current time as the current state? Because if it loops, and has, say, 5 states, then something like 0==time%5 would always get the first state, 1==time%5 would get the second, etc. Maybe you could try something with that?

 

Then again, my animation system works in an entirely different manner from yours, so I have no idea if this will work. Thought I'd suggest it though.

Currently working on a mod to provide support for the Clojure programming language in Minecraft, check it out here.

Link to comment
Share on other sites

Why can't the server also run the animation counter or whatever you are using to control the stage? Similar to how Minecraft runs many methods on both sides - it's not usually application-critical that the animation is correctly timed, so you can allow a little leeway if the server and client get out of sync; if it is critical, then the only way is to send packets, but keep in mind that rendering can happen multiple times per tick, if you want to get to that level of precision.

Link to comment
Share on other sites

Create client NBT cache.

 

Use Java IO (File) - make some cache.dat in your client/mod directory.

Use CompressedStreamTools (CST) to write NBT into given file.

You can use update events of entites to copy their UUID and animation states to some Storage.class with Map<UUID, AnimationState>.

You can call CST's write method from ClientTickEvent every some time to dump data from Storage's Map to HDD.

 

Performance note:

Dumping AnimationState from entity to Storage.class can be done even per-tick, but CST's writing-to-file shouldn't be used more than on per-sec basis.

 

Storage note: Without clearing, after some time of new entities (along with new UUIDs) your cache can be big. Be sure to nullify all of it once in a while OR use some smart system (which for client can be hard to write since client only knows about entities that he sees so it doesn't know which entries to remove, eg. entities that were killed when you were offline). BUT it is possible.

 

Dev. note: I WOULD do like above. Also: To gain more precision when you quit game - you can also call CST's write with client's disconnection event or since you would use ClientTickEvent - simply check if game has been deconstructed (by reading Minecraft.class fields).

 

1.7.10 is no longer supported by forge, you are on your own.

Link to comment
Share on other sites

If this is a looping animation or something, then maybe you could use the current time as the current state? Because if it loops, and has, say, 5 states, then something like 0==time%5 would always get the first state, 1==time%5 would get the second, etc. Maybe you could try something with that?

 

Then again, my animation system works in an entirely different manner from yours, so I have no idea if this will work. Thought I'd suggest it though.

 

The problem is that the animation is done in client-side only classes. So the server actually doesn't even load the classes that have the information about the loops. Yes the animations are predictable, but only on the client. If the server had the information then it could probably predict the animation point. It would still create a lot of network traffic because the information on the animations would still need to be sent to server, but I think it is probably the way I have to do it.

 

I was just hoping for a cleaner way to save directly on the client. Configurations are actually saved on client but since the information I have to save is dynamic and has to be associated with specific world saves, it doesn't make much sense to use a Configuration file for this.

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

Link to comment
Share on other sites

Create client NBT cache.

 

Use Java IO (File) - make some cache.dat in your client/mod directory.

Use CompressedStreamTools (CST) to write NBT into given file.

You can use update events of entites to copy their UUID and animation states to some Storage.class with Map<UUID, AnimationState>.

You can call CST's write method from ClientTickEvent every some time to dump data from Storage's Map to HDD.

 

Performance note:

Dumping AnimationState from entity to Storage.class can be done even per-tick, but CST's writing-to-file shouldn't be used more often than on per-sec basis.

 

Storage note: Without clearing, after some time of new entities (along with new UUIDs) your cache can be big. Be sure to nullify all of it once in a while OR use some smart system (which for client can be hard to write since client only knows about entities that he sees so it doesn't know which entries to remove, eg. entities that were killed when you were offline). BUT it is possible.

 

That was similar to what I was thinking of doing, basically make my own file/cache. I was just hoping to avoid the trouble.

 

I only need to save it when world is saved or client disconnected and similar, so file write performance shouldn't be a problem.

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

Link to comment
Share on other sites

Yes the animations are predictable, but only on the client.

Not entirely true. You say the server initiates the animation, and the animation is predictable i.e. on a schedule, so why can't the server have the same timer counting down? Look at EntityIronGolem's attack timer - it is used on both server and client and is basically exactly what you want. Just save it to the regular entity NBT and implement IEntityAdditionalSpawnData to send the current animation point upon loading like someone else suggested.

Link to comment
Share on other sites

Why can't the server also run the animation counter or whatever you are using to control the stage? Similar to how Minecraft runs many methods on both sides - it's not usually application-critical that the animation is correctly timed, so you can allow a little leeway if the server and client get out of sync; if it is critical, then the only way is to send packets, but keep in mind that rendering can happen multiple times per tick, if you want to get to that level of precision.

 

For the same reason I just responded to yoshiguest -- the server actually knows nothing about the animation, except the id. The animation (i.e. the sequence of pose models and the number of ticks between each pose) is loaded from custom JSON files in a client-side only class.  The animations are basically a resource and could presumably have resource pack to replace them if someone wanted to go through the trouble.

 

I do think that I will have to have the server estimate the animation progress so the question is how the server gets the information about the animations. I could have the client send all the animation info when first connecting but I'm thinking maybe at the start of each pose I can send the number of ticks for the next transition. That might not be too much spam on the network and would allow the server to estimate.

 

There is still the issue of multiple clients each sending this info, but I guess I can just accept all packets as they come in. It doesn't need to be perfectly exact per client, just close so it doesn't look noticeable.

 

EDIT: I suppose another way is to recode the animation loading so server can load it too. Just little tricky because the code is mixed up in models and renderers which are client side only. But I could probably make a server side loader for the animation information.

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

Link to comment
Share on other sites

Question tho: Are you aware of the fact that anything not steered from server will cause all clients to see differently?

 

Making any type of client cache and not saving directly on server will make different clients go out of sync after one e.g relogs.

Since you are not storing animation states on server - you cannot even send them in spawn packets.

 

Next thing is that even if you wanted to send packets client->server you cannot, without making per-player animation server storage (otherwise one client would override other's data).

Then again - making per-player server animation cache would again cause different players to see different things.

 

Solution: Make server perform tasks (goddamit!). :D

 

EDIT: And yes - that is what you should do - config for server which sends data to clients how they should act on PlayerLoggedInEvent.

 

1.7.10 is no longer supported by forge, you are on your own.

Link to comment
Share on other sites

Question tho: Are you aware of the fact that anything not steered from server will cause all clients to see differently?

 

Making any type of client cache and not saving directly on server will make different clients go out of sync after one e.g relogs.

Since you are not storing animation states on server - you cannot even send them in spawn packets.

 

Next thing is that even if you wanted to send packets client->server you cannot, without making per-player animation server storage (otherwise one client would override other's data).

Then again - making per-player server animation cache would again cause different players to see different things.

 

Solution: Make server perform tasks (goddamit!). :D

The reason I took the approach is that I based my animation system off of vanilla. In the vanilla code, the server doesn't know anything about model rotations and arm swinging and so on. However the vanilla animations aren't complex, long or persistent so I guess the problem isn't very noticeable. So even vanilla offloads some stuff to client!

 

Anyway, my point is I DO want the server to know the state and manage this (i.e. sending out spawn data and such) but most animation code I worte is intertwined with classes like models and renderers which are client-side only.

 

I also can't have the server actually control the animation by sending model and rotation information every tick to every client, so best case is to try to have the code run in parallel on both server and client.

 

So I think this is leading to me trying to recreate some of the animation loading and execution to also happen on server. It's just also going to be a bit of a pain. Unless there is some other mechanism for client-side saving readily available, I think I'll consider this problem solved and do it this way.

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

Link to comment
Share on other sites

The thing is, animations in Minecraft are NOT saved. The server typically doesn't care about what the renderer is doing, nor should it, and what is rendering shouldn't have any effect on anything else, even though it may appear that way to the player ("oh, the boss attacks each time after it finishes this funky dance - I'd better watch carefully so I can dodge!"). In some cases, the animation may even send an 'animation complete' packet to trigger things on the server, so it is somewhat linked, but that's about the extent of it.

 

The server says  "golem is attacking now" or "mob is moving" and lets the client know, and the client then does whatever animation it wants. When the game reloads, though, the entity is neither attacking nor moving, so the animation state starts at zero (although that zero may be for a certain 'state', e.g. sitting, standing, prone, etc., sent from the server upon loading).

 

I get what you are trying to do, but I guess I don't understand why you need the animation state to save - why not just reset it like vanilla? Will your animations really be stopping mid-way through when the game saves, and need to start at exactly the right spot when it loads? This seems unlikely, and I doubt there are many or possibly even any games that have coded animations that way.

Link to comment
Share on other sites

The thing is, animations in Minecraft are NOT saved. The server typically doesn't care about what the renderer is doing, nor should it, and what is rendering shouldn't have any effect on anything else, even though it may appear that way to the player ("oh, the boss attacks each time after it finishes this funky dance - I'd better watch carefully so I can dodge!"). In some cases, the animation may even send an 'animation complete' packet to trigger things on the server, so it is somewhat linked, but that's about the extent of it.

 

The server says  "golem is attacking now" or "mob is moving" and lets the client know, and the client then does whatever animation it wants. When the game reloads, though, the entity is neither attacking nor moving, so the animation state starts at zero (although that zero may be for a certain 'state', e.g. sitting, standing, prone, etc., sent from the server upon loading).

 

I get what you are trying to do, but I guess I don't understand why you need the animation state to save - why not just reset it like vanilla? Will your animations really be stopping mid-way through when the game saves, and need to start at exactly the right spot when it loads? This seems unlikely, and I doubt there are many or possibly even any games that have coded animations that way.

 

You got it exactly. However, when you say in Minecraft the animation state isn't saved, I think it works for vanilla because the animations are so short and not very distinct. So the disruption isn't noticeable.

 

In my case however, I have things like a dying animation that goes on for about 10 seconds (entity sort of rears up, staggers back and forth, falls then flops on the ground) and actually persists longer than that to create a "carcass". So if you save the game while looking at a dying entity (or carcass), when you load the game it will look like it is alive and then die all over again. It is just extremely noticeable.

 

I think generally if someone has a long, distinctive animation then ideally when you load the animation would be is same point.

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

Link to comment
Share on other sites

Maybe, but in all the games I've played, if something has a long dying animation, to use your example, and I quit while it is in progress, when I reload the mob is just dead and in the carcass position, rather than replaying the animation.

 

Wouldn't that make more sense, anyway? Since the entity was dying, you quit and come back, so now it should be dead, not still dying.

 

Even for other animations, the animation is typically just eye candy, no matter how long it is, and no one will even notice that it isn't starting in the same place when they log back in, nor would they expect it to. Just my 2 cents.

Link to comment
Share on other sites

Maybe, but in all the games I've played, if something has a long dying animation, to use your example, and I quit while it is in progress, when I reload the mob is just dead and in the carcass position, rather than replaying the animation.

 

Wouldn't that make more sense, anyway? Since the entity was dying, you quit and come back, so now it should be dead, not still dying.

 

Even for other animations, the animation is typically just eye candy, no matter how long it is, and no one will even notice that it isn't starting in the same place when they log back in, nor would they expect it to. Just my 2 cents.

 

Yeah I guess I could just consider it something that people would tolerate. I definitely noticed it in testing though which is why I tackled the problem. I guess I can handle the dying as a separate case, like you said, so that if it loads and is already dying then it would go straight to carcass pose.

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

Link to comment
Share on other sites

Yeah I guess I could just consider it something that people would tolerate. I definitely noticed it in testing though which is why I tackled the problem. I guess I can handle the dying as a separate case, like you said, so that if it loads and is already dying then it would go straight to carcass pose.

Us developers notice a lot of things during testing - I highly doubt anyone else is going to be logging in and out as much as we do ;)

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.