I have a Module Script for Melee Weapons named “MeleeController” that uses a OOP system. It currently works perfectly fine on the Client, however I dont really know how I can make it work on the Server.
The reason why is because my OOP script uses remote events (e.g. to fire the attack function).
First of all, why do I wanna make it work on the Server? It’s simply because so I can make NPCs use this “MeleeController” script, because for now it only works for players.
So your first thought may be: “Why not use Bindable Events then?” If I would’ve used that, that means I would have to make all of the remote events to new bindable events with the exact same names and connect each of them on a script aswell… This is very not optimal as you can see. (Btw note that currently all the events are being connected on one and singular server script.
I had an other idea, and it was to directly call a function intended for remote events via a Module but only if it detects that it’s running on the Server, otherwise if it detects the client instead, it would fire the remote event for it. I guess a way to detect it is to check if theres not Players.LocalPlayer or RunService:IsServer().
However I’m not really sure if that’s the best way, so that’s why I created this topic, How do you guys usually do it? Do you guys have a better idea? Is there any additional informations I have to add? Tell me everything that could potentially help me, therefore thank you for reading this topic.
EDIT: For those that are running into the same issue, I suggest making a class that only contains the methods for it, then make controllers dedicated for client (players) and server (AI) by inheriting or “composing” them.
I’d still suggest you to learn more about structures, which, if you use module scripts a lot, will benefit you in a lot of ways. Take a look to this guy’s posts: Writing Clean Code Part 1 || What is Software Architecture and Design, and why do I care?
And this one as well: MVC: A Practical Approach Towards Developing Games (And how to stop confusing yourself with ECS vs OOP)
Oh and I also use networking and signal libraries which has made my workflow significantly better for me, I prefer them than using the original method from Roblox (which is to manually listen to remote/bindable instances.)
OOP’s kryptonite is the client/server model. It’s really unfortunate, and I understand your struggles.
My answer is it really just depends how you’ve structured your objects. Personally, I usually make a class on both the client and the server to represent my object. Then I just make them communicate with each other. Logistically, I haven’t run into any issues while doing this, but it feels off to have two separate instances of classes representing one thing.
I think if you create a system to generate these events for you, the solution will become more clear. I currently have one of these which automatically creates the remote when you attempt to assign to a currently-nil value in a central “Remotes” module. This step can recognize whether its on the server or client, so it can decide which remote to generate. It also saves you from accidentally having remotes that are no longer used or are being used wrong. You also gain an easy central location to add test or profiling code to your events.
Mine also re-sets any metatables, which will have been lost when a table is sent via remote or bindable. You can also avoid needing this at all if you’re more careful than I at keeping things as either a server or client responsability.
Thank you for your response, but I kinda struggle to understand what you actually mean, could you perhaps make it clearer by showing how you do it step by step.
You can use the __index metamethod to detect when an attempt is made to access a missing item in a table. I have a “Remotes” module that uses this to detect the first time a Remote is called or bound (its handler assigned), if it’s missing, it automatically creates the RemoteFunction or BindableFunction required to make that call valid.
You mean that for each scripts that uses MeleeController has their own unique events? I’m kinda dubious on that, wouldn’t that have issues with performance?
It only creates an event when it doesn’t already exist. So each type of event has its own remote, but each publisher shares the same event for that type.
I use attributes for server → client replication. Client just detects when attribute changes and changes its own stats. For Client → Server, I just use remotes like usual.
Sorry for the late response, I usually struggle to understand someone’s idea (not ur fault btw), but in other words if I understood correctly, there would still be as I said previously:
While I was tagged in this discussion, I’d like to offer some perspective on an alternative approach that’s often overlooked.
Rather than using Object-Oriented Programming (OOP), consider Data-Oriented Programming (DOP) for cleaner system architecture. Before languages like C#, Java, and C++ popularized OOP, programming languages such as C and COBOL utilized a structural approach that separated data from behavior.
In DOP, data is organized in simple structures containing only values without methods. This creates clear, unambiguous contracts between components without the complexity of inheritance hierarchies or OOP overhead. Functions then operate on these data structures externally, providing a clean separation of concerns.
This approach offers several advantages:
Decoupled systems with clearly defined boundaries
Reduced interdependencies between components
More predictable data flow
Easier testing and maintenance
By separating what something is (data) from what it does (behavior), DOP allows your functionality to evolve independently of your data structures, resulting in more modular and maintainable systems.
Hi, thanks for your answer, from what I understand, DOP is to seperate the data from the behavior, which is basically how a component work, hence it behaves kind of like ECS but in a more abstract and customizable form?
I tried my best understanding what you’re saying because I’m really bad at understanding abstract ideas.
What I usually do now is I would do something like player.Inventory = Inventory.new() for example. Is it what you mean by that?