Currently facing some extreme challenges. Any input would be very helpful.

Options
For a while now I've been developing a game that has a non-static world, that is to say the world is:

1: Tile-based and generated at random, but limited
2: Dynamic, meaning we have trees that can be cut, tiles representing holes dug in the ground, rocks that can be broken, etc.

As you can imagine, loading the entire map at one time isn't feasible. Because of this, we stream in whatever is closest to a player, and discard things that are too far away from everyone.

We used to be doing instantiation/destruction of the tiles, which was rock solid in terms of Bolt networking "what I expected was what I got", but for performance reasons we decided we had no choice but to create a pooling system whereby we instantiate a great deal of possibly needed tiles and deactivate them. Then as they're needed we reactivate them, move them into place, etc.

I've programmed numerous pooling systems before successfully, but having Bolt in the mix is... complicating the issue, to put it mildly. Currently the pooling system stores tiles in a deactivated, unattached state. The tiles are then attached in their OnEnable, and our despawn routine deactivates the tile (and on top of that we have all of our tiles set to "Detach when disabled" in their entity settings).

We have the strangest unexplained issues, such as tiles that will vanish right off the screen despite the fact that it's literally impossible for a tile to be placed back into the pool in those circumstances, and impossible for something else to "take" a tile whose GameObject is active. It leads me to believe the only explanation is that Bolt is doing something I don't want it to do with my objects, but I cannot explain what's going on.

At first I thought Bolt was just "self-destructing" and blowing up random objects because there are so many, maybe something to do with the scoping? Admittedly we have a ridiculous number of entities that Bolt has to take care of; we saw no way around that, since as far as I can tell, each tile needs to be its own entity. I just don't know what's happening.

I'm open to any suggestions on better ways to handle this and so forth, insight on what in the Bolt side of things could be going wrong, and so forth.

It's worth mentioning that these tiles never need to move after their initial placement, and most have no continuously moving parts (for instance, a rock need only sit there and be a rock until it's hit with a hammer). However, the entity does say it's being considered for updates "every packet". I don't know if things like this need to be dealt with or worried about.

If anyone has any input about this situation either directly, tangentially, or just has a way of accomplishing what I'm setting out to do that I haven't thought of, I'm all ears. I've been working on this for weeks and am becoming extremely discouraged.

Comments

  • stanchion
    Options
    Using tons of entities will not work in this scenario. You should definitely read up on how Minecraft is networked http://wiki.vg/Protocol#Chunk_Data and our discussion in chat about this kind of thing http://boltchat.aursand.no/?q=voxel
  • stanchion
    stanchion mod
    edited March 2016
    Options
    ignore this post
  • stanchion
    stanchion mod
    edited March 2016
    Options
    ignore this post
  • So I've done a lot of reading, and been playing a bit of Factorio as well (a game that has thousands of moving parts potentially yet seems to run beautifully in multiplayer).

    It seems I have to send the save data over to each client when they connect and let them sort of control their own destiny in terms of instantiating objects and so forth from data, and I'm guessing I just use sort of a "blind" networking method to make it all work?

    In other words, rather than have a tree be a bolt entity, the tree is instantiated "offline" for each player and when they swing at it, they're sending a notification to the server that they're trying to cut a tree at those tile coordinates, and the server would then just check and see if the "story" checks out (that a tree indeed exists there, etc)?

    Am I getting warmer?
  • stanchion
    Options
    If you want to have thousands of trees server auth then you pool rigidbodies that follow players around and when they hit a tree the server verifies it. Here's a package that does that and has Bolt integration https://www.assetstore.unity3d.com/en/#!/content/43129
  • DirtyHippy
    Options
    Are you using IPrefabPool to implement pooling entities? Or do you have a single entity type for blocks, and pull off the visual representation off a pool and parent it to the entity?

    Just off the top of my head, depending on if this is a top down, 2d game or a 3d game, I would probably bail on using bolt entities. For something like minecraft, and I am just spitballing here, I would divide the world into arbitrary 3d chunks of some fairly small size. And then I when I load a chunk to a user based on his AoI, I could compress the living crap out of that chunk. Say I was using a 16x16 chunk (256 blocks). You take advantage of the fact it is a sparse 3d matrix and only send blocks that exist, and probably a single byte at most for the block type (i.e. giving you 256 block types which is probably plenty). Of course, blocks are the easiest. Syncing state over for less trivial blocks like crafting stations, etc, would be more effort. But in any case, you could really optimize what you need to send in order to stream that part of the world in.

    I would probably also implement a caching system on the client, so the server knows "I already streamed this chunk to the client, and nothing has changed since he last visited it, so I will let him stream it in from his client cache (which allows you to avoid having to send any updates at all). The reason I am thinking this is simply that in these games there are a lot of chunks, and probably the vast majority of the time, the chunks remain unchanged between visits (except if things are growing I suppose). Perhaps in that case you just send a delta until the delta grows too large (in which case you do a full sync)? Even growth could probably be predicted by the client, as long as the growth algorithms are deterministic.

    if this is a 2d top down game, you could probably get away with just doing this with bolt entities.

    Just some random thoughts. Depending on the game type this isn't a super trivial thing to do in the most performant fashion. It would definitely require some plumbing.