In this multiplayer RPG, each enemy is handled entirely on the server from its movement to its attack hitboxes. If there are 16 players on the server, and each player is in a battle with 3 enemies each, that would equal 48 enemies that the server needs to handle. Would it be less intensive to create each enemy on the client and then simply update the server with its status occasionally? Players will be fighting their own battles, so server-side enemies aren’t necessary.
Put the enemies on the client. On the server just have a part (or a table) which represents the npc. That will store data such as NPC Health, position, walkspeed ect.
If you put the enemies fully on client the exploiters would just be able to kill them instantly
That’s why I’d be sending info occasionally to the server to check
you could make the system that the person above me suggested and have a part represent the enemy on server (that is then deleted on client)
and have fake rigs on client that are basically relying on the information the server is sending to the client
This is exactly what you want.
If you’re using Roblox’s built-in humanoids, stick to an npc model that only consists of minimum objects: The root as well as extra collision (e.g. hitboxes).
Identify the npc variants using tags or attributes, read those on the client, and build their visual model on top of the base humanoid that’s already replicated on the client.
Going with an approach where you track positions and other data solely through tables requires a fully custom replication system which is unnecessary.
If you want to lessen the load of the primary thread on the server, use actors.
One actor would be in charge of processing one npc’s logic.
You could go even further and create an “actor pool” module that automatically assigns npc processors to an actor or creates a new one based on a pre-defined quota, but it’s not required for small games.
I would link you to a video that clearly showcases the massive performance gains when utilizing actors for npcs, but I can’t find it.
If you really don’t want to handle them on the server, I’d weigh it against how cheaters would stand to profit from local processing instead.
In my opinion, if it were simply for something like a training area before fighting alongside players, I’d make a hybrid model that could both be processed on the client or server.
How do you suggest doing this? Should I simply update the model’s position each frame to match the server enemy’s?
No, you can just use constraints.
If you want to attach your visual model (e.g. the R6 character) to the existing server model while on the client, you could opt for a simple Weld, or if you want to do transformations on its orientation/position, use a Motor6D between the root part of your visual model and server model.
Any other physics-related constraint are possible too, but that’s up to your use cases.
On the client, you should just treat the server npc model as a physics object you can dress up and attach to.
Thanks, I’ll try using this approach.
Hey! I tried welding the visual model to the actual model on the client, and encountered this strange bug.
I noticed the enemies were moving incorrectly. I found the problem: the weld is somehow causing a discrepancy between the enemy’s position on the client and on the server. Attached are two images showing the difference between client and server (Note the disembodied faces on the right of the second image, showing the enemies’ true positions).
I am confused because, as I stated before, the code that moves the enemies is on the server. I don’t know why the weld causes the enemies on the server to not move, or why they ARE being shown moving on the client but aren’t actually moving on the server. Additionally, I tried using WeldConstraints and Motor6Ds and achieved the same result.
Do you have any idea how to fix this?
I can’t tell what could be the issue here because there’s too many possible factors at play.
This approach was already successful in a game I worked on prior, so it’s definitely possible.
Would it be possible for you to provide a place file with the minimum code that reproduces this?
Make sure you create the constraints for your visuals solely on the client, and that all related objects are unanchored.
Of course. Here is the place file and an additional video recording of the error. As you can see, the enemy only starts to move once the Motor6D is destroyed.
ConstraintError.rbxl (638.5 KB)
This is the code that creates the constraint, it’s on a module script that’s accessed on the client via a module loader.
I appear to have attached the wrong video, sorry. Here’s the correct one
Sorry for the delayed responses; time has been flying recently and I haven’t had a chance to check back. I’ll be taking a look first thing in the morning.
No problem, thanks for taking the time to do so. ![]()
Seems like the problem was caused by two issues:
- The root of the visual model remains anchored after binding with the server model. This results in the visual model not moving at all.
- The baseparts of the visual model have mass, causing a desync in physics. This is something I didn’t expect and I can’t really seem to make sense of why this happens, but setting
Massless = trueon all visual parts completely resolves the issue.
As best practice, you should always be mindful of when something should have physical properties or not. Personally, I like to micro-manage this by preconfiguring all of my instances with the exact properties I do and don’t need, which is also why I never ran into this desync issue before. Strange!
These are the lines I modified
Other things I noticed (not problematic; take it as feedback):
- Excessive use of
ReplicatedFirst. You’re practically loading all of your game logic and assets from there which entirely negates the benefits of using this service. You should only be utilizing it for very lightweight logic that absolutely needs to be loaded ASAP, for something like a loading graphic to mask 3D pop-ins as the player joins. Traditionally your core scripts should go intoStarterPlayerScripts. - Opinionated, but when setting
Motor6D.Part0, it should refer to the part to which theMotor6Dinstance will be parented. Has no technical impact, but simplifies the thought process of the constraint connection (e.g. this connection connects from part0 (this part) to the other part). If you adhere to this order, it makes it easier to find the constraint instance if you ever need to during playtest. - If you ever want to apply transformations to the visual model using the
Motor6D, use the.Transformproperty instead of.C0. The former internally utilizes multithreading for the best performance. If visual speed is of the importance (e.g. when setting the offset initially during spawn), that’s where you want to use.C0.
I’m liking the feel of your game so far. Best of luck and hope this gets you rolling again!
Oh wow, I never thought to tick Massless. Odd. Thank you so much for taking the time to look over this issue, this has certainly motivated me to continue. I also greatly appreciate your feedback! I’ll work on that.
This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.



