Train moving on Server with CFrame Lagging

Hey there devs.

I Am working on a train system which i have made a couple of threads about in the past couple of days. I am aiming to have a large rail network. I want the train to run smoothly on the tracks.

The issue is, The CFrame is choppy and lagging because i am setting it at a high rate on the server-side. I haven’t been able to find any solutions that work for my situation, because I don’t believe I can do what i need to do on the client, because if there are 20 trains running, i would have to keep sending the position to the client. I also am unable to use TweenService because for the train to turn, i am using a special interpolation function using Bezier Curves.

Here is what it currently looks like (Using a free model train rn because i dont have one built):

Thank you for any support

  • Mezza
3 Likes

How do you move with it? Pls give code only of the line setting pos.

This is the line where it is being set.
It uses Bezier Curves, so that gets the Interpolation for the point the front set of wheels are at, and the back wheels are at, and averages it to get the center and proper rotation.

workspace["TrainPart"].CFrame = (CFrame.new((front+back)/2, front))

Then the train is moved to the TrainPart using SetPrimaryPartCFrame.

that is problem, use welds and move just trainpart

1 Like

Instead of CFraming the whole model and having it all anchored, have you tried the solution here:

You can still use your trainpart CFrame but instead of moving the whole model to that part, you instead use AlignOrientation and AlignPosition. Works well for single carriage trains.

3 Likes

Have you tried using :SetNetworkOwner(nil) on all the the train’s parts?

At default, its nil…

It’s not nil by default… nil means the part’s owner is the server; otherwise, it is dynamically set to the closest player by default. Using :SetNetworkOwner disables the dynamic ownership.

1 Like

Every part in workspace is owned by server

As said in the Network Ownership Article:

When a Roblox game runs, several computers/devices are involved. To divide the work of calculating physics, Roblox automatically assigns parts to either the server or to a client. In general, if a part is near an in-game character, its physics will be calculated by that player’s device; otherwise it will be calculated by the server. In either case, the server or client that calculates a part’s physics is called its owner.

2 Likes

The client owns the part, which means that they own its physics. In other words, they can manipulate its CFrame to their heart’s content. They send the data to the server, which replicates it to other clients. The server can override those properties though, as it is the one in charge of replication. The server doesn’t own the part, it owns the network.

so it is exploitable, when i dont set network owner on parts, that i need to secure?

It depends on what you want to achieve. If the part is just a piece of rubble, then you just wait for the velocity to become 0, set the owner to be the server and anchor it. Stuff like vehicles are much harder to secure. The way I do this is by setting the network owner to be the driver, after which I do additional checks on the server. About the same as one would with characters, by checking for speed and fly hacks.

You have to move the train individually on each client using a .RenderStepped to ensure it’s smooth. Generally it will require a system where the server will periodically tell the clients which section of track the train is going on to next, and changes in speed. You can update the train server-side at a much lower rate without interfering with the client-side behaviour, so that the server position of the train is still roughly accurate.

The method with body movers and nodes posted above is very good, but depends on what you prioritise. There are a few issue with the body movers method:

  1. It only really works smoothly for one person at a time, the network owner. It will still be smooth-ish because Roblox’s physics networking has gotten much better, but it won’t be buttery smooth like a correctly built client-side solution.
  2. The train can still glitch out, particularly if the owner is laggy, and having other people standing on the train is a big no.
  3. The network owner has full control of the train and will be able to make it do whatever they want with basic exploit tools.

The main issue with sticking with CFrame is that you will need to also update the velocity property of any parts you want players to be able to stand upon, and as mentioned the train itself won’t respond to other physics objects (e.g. it would just phase through another train), and physics objects that collide with the train wont always respond as you expect.

2 Likes

For starting a project from scratch, what is the general or high level approach to a correctly built client-side solution?

I have a system in place that generates paths when the game begins for each section of track. When the train completes that section, it moved to the next one, which is given by that path itself. Each path has an Id as well. Could it be possible to have the server store the id of the track the train is on, and then the client changes the cframe with any of these given methods?

First remember this solution is good when:

  1. You need high reliability, no chance of it glitching out or whatever
  2. You want it as smooth as possible for multiple players simultaneously
  3. Physics interactions with other objects are not important - cars and players will still collide with the train and be pushed out the way, but they won’t necessarily respond realistically.
  4. Quite important This method will be more complicated than the minecart thread linked above.

The general idea is that the client deals with the fine details - it knows the path the train is going to take, and it updates the trains position from a local script for each client. So something like this:

local Speed = 1
local DistanceAlongPath = 0
game:GetService("RunService").Stepped:Connect(function(dt)
   DistanceAlongPath = DistanceAlongPath + Speed * dt
   Train.PrimaryPart.CFrame = CalculateCFrame(DistanceAlongPath)--Moving a part that the rest of the train is welded to is more efficient than SetPrimaryPartCFrame I believe
  --Where CalculateCFrame is some function you have to figure out where the train should actually be
end)

Some of the details depend on your game, for example if the train switched tracks at the last minute, the other clients will have a delay before they can know this from the server, and so will have to tween the train from the old path onto the new one.

Generally you should still be updating the position of the train on the server too, otherwise it can get confusing as the server will always see the train in the same spot. The stepped connection ensures that this does not effect the clients, how often the trains position is updated server side is up to you.

4 Likes

Any of the above methods should work fine for you - effectively you just take whatever code you currently have to determine the trains movement and put it inside a local script, or you take it and instead of CFraming the train, you repeatedly put the next node into the trains bodymovers.

Ok thank you for the help everyone. I will try out these methods as soon as possible. :+1:

1 Like

What i’d do is render the cframe position updating on server, and fireallclientevent to move train to lerp old position to new position.