Efficiency tips for MMORPGs?

I’m currently working on an MMORPG game which would certainly contains ALOT of lines of codes, which means, it is supposed to be highly optimized.
My current setup is the following:


Frameework contains all data in form of tables, here is the code in it: (tables setup is hidden to save space)

And Modules inside MainFramework are as follows:

local Main = require(game:GetService("ReplicatedStorage").MainFramework)
local Modules = Main.Modules

return
	function(Parameters)
		--Code Here
	end;

And since this is my first game so far, I have got a few questions I need to address:

  1. Are there ways in which I could maintain modularization of my code such as in the image whilst making in more efficient?

  2. What efficiency tips would you be giving out as for players and NPCs specially?

  3. Should I use Network Ownership in anything? Is it even useful in RPGs?

  4. What should be handled locally and what should be handled globally?

3 Likes

One thing you could try doing is something I’ve started applying recently:
A module that contains paths to all other modules and directories

Example module (totally not my actual module..)

Usage

image

setupPaths() is called in a script named "LocalSetup" which resides in ReplicatedFirst

image

Haven’t had any problems with this so far.
You could make fallbacks and debug errors using this,
plus, you don’t have to go through multiple scripts to change the name of a module if you’ve changed the original!

I’m actually already doing that, but I named it main framework, I’ve attached a picture of its code in the post as well.
It’s a pretty efficient in my opinion and makes the scripts easier to add on or debug.

1 Like

Gosh there are a lot of questions here!!

My only feedback is that modularisation of code doesn’t make it more efficient, and being so focused on efficiency in terms of how your actual code base is structured is pretty weird. Right now, you don’t need to optimise, but for when you do

Modularisation of your code is basically done to help organise it, and you’ll develop a good way of doing that for yourself overtime. Let me tell you lol, in terms of what you’re doing for structure right now, its pretty much bound to be bad. This will only be fixed by either A. making lots and lots of games or B. working with people more experienced than yourself. B is faster and preferable.

The biggest thing I did to improve my modularisation is have a rule that if a module is over 200 lines, to further modularise it. This isn’t hard and fast, some stuff will be longer, but 95% of the time, 200 lines is a good upper limit.

4 Likes

Intro


When you’re structuring or organizing a larger project, the key factors are maintainability, readability and efficiency. You should primarily be focused on the maintainability and readability parts, they are essential for you to be able to continue developing your game. Efficiency is also important, and you should make sure that your scripts are not poorly written, but maintainability > efficiency. (Personal opinion).

You should create a README file with how you’re structuring your scripts, where you’re placing them and why you’re doing that. Some other info is also great, remember that anything you work on now, literally describes how you’re going to be working with it later. You are setting everything up for your future self, unless you’re not going to work on it anymore :stuck_out_tongue: That’s why it’s important to follow a set of rules that you have to follow strictly.

Thinking “this is so easy I don’t have to follow my rules” will bite you in the future, trust me. If you have properly set up a system, you know what works where, and you don’t have to worry if that function exists or not. I have several times

Some tips when it comes to this:
  • Self-explanatory variable naming
  • Rules when to use camelCase and PascalCase, don’t mix! For example constants with PascalCase and other logic with camelCase.
  • Create independent modules, modules are made to be extensible, so utilize this as much as possible
  • Document your code, no over-commenting or other redundant stuff

If you’re more interested please read my comment and not the original post as I don’t agree with what it states. (No offense to the author) A Scripture's Guide To Scripting - #14 by Visualizememe

And if you’re interested in a commonly accepted scripting style Roblox Lua Style guide

Other things to consider


Don’t send clients things they don’t need!

This is probably an obvious point, but how to overcome this issue is probably not that obvious. We all know “ReplicatedStorage” and its usecases, but did you know that anything you put in there automatically replicates to the client? Maybe, maybe not. Don’t replicate stuff that is not needed. Have all the items on the server (more specifically, in ServerStorage). That way, when clients need something, then you can move it to your liking, but otherwise, hands off!

Optimize code, or not…

This is a commonly discussed matter and we’ll never know who wins. Some say “Rules of optimizing: don’t do it.”, others say do it anywhere you can. I think that the second you start optimizing your code, you want to optimize more and more for “nothing”. Yes, maybe you get a millisecond or two faster load times, but who notices? Not me, not the players, nobody else but you. Optimize with a grain of salt. I’m staying on the “don’t do it unless there is a significant improvement if you do” side.

Optimizing code might, or will most likely result in less maintainability and readability, which breaks my predefined rules stated above. Efficiency is not important if you can’t update your game because of it.

Some threads discussing this:

Security

Another interesting topic. Don’t let your clients command your server, let the clients politely ask the server. Have you ever encountered exploiters? I have, most, if not all have experienced some kind of an exploiter in a game. Do you know what’s guaranteed to be common with exploiters? Frustration, annoyance and less players.

If you let exploiters thrive you let your players dive, is that what you want? No, and especially not for a larger game where players might spend more time. Players get a feeling that somebody else can cheat their way to faster stats and that will make them uncomfortable and unpleased. Get rid of common exploits. Speed, fly and those kind of basic exploits break the natural feel of the game. Get rid of that before it becomes a problem.

When you’re coding your game you must always think of what the player can, and can’t do. If you understand that, you will gradually understand what not to do. For example, if you are to let players buy something in your game, you want them to be able to buy the sword, but you don’t want them to buy it for free. Think of all attack surfaces, all the ways an exploiter can harm your game, and fix it.

Remotes

Don’t have one remote, don’t have 1000. Proceed with caution. I don’t remember which topic it was, but there was a discussion where many people argued whether one remote and several remotes were faster. Utilizing several remotes results in better performance. That doesn’t mean you should have one remote for one thing.

Keep them general so they’re easy to maintain.

When it comes to communication between your server and player, this is the point where you actually should be worried about efficiency and bandwidth. The more data you transmit the longer time it will take for input and output to be processed and replicated. Now, don’t go completely bananas, but utilize several methods for compressing data so that your game feels responsive.

Separate client & server stuff, please

I have talked about this so many times, and people who don’t get it, outright called me a noob. So I guess you’ll have to trust me on this one :wink:

The server is the messenger, the client is the computer. Don’t overload the messenger with things that the client can do.

The server exists for the following things:

  • Securing your game and filtering/validating input from the client
  • Replicating information to all clients
  • General things that clients should not be trusted to run (or can’t :p)

The server does not, and again not, did I say “not” twice? Well, I mean it. It does not exist for the following:

  • Animating characters
  • Rendering stuff in the game (visuals like gun bullets, effects etc…)
  • Things that could be done on the client with, aka all things that don’t affect other players.

Yes, you can have the server replicate the animation data, but you can’t have it do the animations.
Yes, you can have the server replicate the visuals (gun bullets) to the other clients, but you can’t have the server do it on its own.

_G, or not

Ever heard about _G? That’s Lua’s version of global variables that Roblox has made accessible for either the client or the server. If you use _G on the client, it’s only accessible on the client. Use it on the server, and it’s only accessible on the server. You can also use it on both sides :stuck_out_tongue:

Some people say it’s bad to use global variables, and they’re right. It’s actually bad in other programming languages, but on Roblox? Not so much. _G is super easy to use, and it functions just like a regular table with values. If you want to use it in your larger game, please be sure not to make it a complete mess. Some of the reasons why _G is bad on Roblox are the following:
image

I personally use modules because that’s the way I like it. But don’t worry if you’re using _G on Roblox, just don’t use them in other languages unless they’re declared safe to use.

Summary


Now, I know that I haven’t answered all of your questions, but I hope you at least learned something new from the post. I’m only trying to help you out :stuck_out_tongue:
If you have any questions and I’ll be happy to answer them. :slight_smile:

16 Likes

Thanks for the feedback! I’m still developing my organization skills, hopefully will get some use out of your tips!

Have read it all, word by word. Probably one of the most useful and helpful posts I have seen yet! Thanks!

I have a few questions though:

  1. For the usage of _G, would it be functional/correct to use it to store players’ variables?
    Such that _G.PlayerID is a table containing the variables for the humanoid, humanroidrootpart, player’s animations, basically stuff that are used by the modules for the player.
  2. You mentioned using _G locally or globally, can I have one for each sides? :thinking:
  3. And finally, I’m currently using server side for anything that edits data, runs the sword to see which attack combo will run, and basically stuff that will be seen for all players and that affect gameplay. Is that correct? Since I didn’t really understand that part
1 Like
  1. Sure! You can use _G however you’d like, it’s pretty much a regular table, but it’s accessible to whole environment (client/server). Just be sure to have a system in place that you understand so you don’t randomly do _G.playerId = {} and all of that.
  2. Yes, both the server and client have _G but they’re separated, meaning that if you set _G.isNoob = true on the client, it will not be defined on the server (nil). However, within the client “environment”, in any other script you can access _G.isNoob
  3. Yup, those are perfectly valid use cases for the server and I’m happy you’re doing that already. You don’t want the server to be slow by filling it up with things that are not vital to the game, or if it could be done safely on the client.

Let’s imagine a PVP game. If you enter a zone you get your swords, and if you’re not in the zone, you don’t have your tools. Now there are multiple ways to implement this that I can think of, but I’ll stick with this one.

Instead of having the server have a while do loop, or detecting when the client has entered the zone, it will instead let the client determine it. Why shouldn’t the server do it? Simply because you can now have the client do it for you.

The client now has a while loop or touch event checker, and as soon as it notices it’s in the zone, it will tell this to the server. On the server, it will make sure the player is actually in the zone on its own, and if the player is in the zone it gives them the tools. If not, nothing happens.

Now, you could also have the server detect when the player is in/out of the zone, but this was simply an example to show how much actually can be done on the client instead. It keeps the server more responsive and faster.

2 Likes

Just thought I’d point this out since I didn’t see anyone else point this out.
It’s not a very good idea to keep all your code in Replicated Storage.
Clients have access to Replicated Storage so this would make it a lot easier for hackers to steal your game’s code.

EDIT:
Nvm, looks like 1TheNoobestNoob already said that.

In my experience OOP, React, ECS, and other frameworks have a large amount of development overhead. In addition, by focusing on performance first you may spend more time optimizing than required and not develop as many features.

My recommendation is to analyse your problem as if you were storing all of its data in a database. The technical term is to “normalize” your data. After that, define how each bit of data needs to be manipulated and create functions to handle those transformations. As needed, create additional structures for caching and what not. What you’ll find is that this method - data oriented development with functional influences - will efficiently mold into your problem without the complicated issues that arise with OO or component oriented systems.

If you focus on the data and transformations on that data, performance will naturally follow.

1 Like

I would suggest using a module loader, like Quenty’s Nevermore.

This lets you keep all all your modules in the same place, and you also don’t have to memorize module paths then.

This is less about efficiency, but it’s important to note that more modularization is not always the answer.

From my own experience, I have often spent way too much time on increasing the modularity of my game, sacrificing development speed of new features. All of these abstractions begin to crumble when I need to make an inherent design change that my modular system was not built for – you have to adjust your solution to fit the boundaries of your design and you get lost in the complexity of the overall system, or you have to rewrite the system (which is a gigantic waste of time).

I’m not a software engineer or anything, so I don’t think I described this idea with much technicality, but my general point is that simplicity is sometimes better than mass-scale modularisation and abstractions of your code, especially when you’re in the early stages of development. Over-engineering clogs up the simplicity of your framework and makes it take much longer to release a functioning game, from which you can make design changes.

3 Likes