What @buildthomas said is exactly how my current project makes its terrain so fast for the client to load (and to reduce stress on the network). I have StreamingEnabled on, and I leave the ReplicationFocus untouched and have the camera positioned far away by default before I load the character. That’s a little trick to manually prevent the terrain from streaming until its done generating, and then I just let streaming handle the rest and it does an amazing job.
Generated objects are parented to a holder object every frame as fast as the server can handle, and then each holder object is moved to the workspace, and then they are automatically streamed in as fast as the client can handle when the character is spawned by Roblox. Overall the FPS hit on my different machines is negligible and hardly noticeable even with a lot of terrain.
As for why I place things in holders, this actually significantly helps with performance for a couple reasons (which are just assumptions on my part, but seem to be true), one being simply that parenting things when they aren’t a descendant of the DataModel is significantly faster, and two being it seems to have positive effects on the way network replication is handled even with StreamingEnabled, what seems to happen is that the server will individually send each ancestry change which is super, super slow if you’re trying to set the parent of (literally in my case) 200k things at once, vs sending each ancestry change for a few thousand or so objects in one go, effectively making it a couple thousand times faster.
The reason I don’t group them all into one parent is for a similar reason, except in this case its because it seems like the client dislikes receiving such a large instance at once. I think breaking it up allows the client to process different amounts of objects each frame or something.
I’m highly doubtful my explanations are actually 100% accurate, in fact I wouldn’t be surprised if they’re downright wrong, but, that’s just what I suspect based on my testing, and based on my testing I know that its significantly faster to do the way that I do it.
Most of your problem isn’t actually rendering the terrain (hence why the lag goes away even after the terrain is gone) so the ability to disable rendering simply wouldn’t help you. Most of your lag comes down to replication, sending all of the data for your terrain over the network, you can actually test this by going into Studio Settings > Render and setting your Render Mode to NoRender and testing your place. You’ll expect to see the client Heartbeat drop about the same amount anyway, regardless of whether or not the client is rendering what’s going on.
If you want to see what I mean with a large amount of terrain, you can also try out /flag CRAZY_ISLANDS
in my game, which will do a soft server restart and have the server generate 100x the terrain. There is no loading GUI for soft restarts currently, so, you will be staring at some floating objects for about 20-30 seconds depending on the terrain seed (most of which is due to model optimization where I’m making changes to individual terrain cells to reduce stress on the rendering engine).