Crossing server-client boundary with custom OOP?

Hello everyone,

I am trying to develop an OOP-based game using modules based on this tutorial. The issue I am finding is that I do not know how to properly cross the client-server boundary with objects that only exist on the server. For example: I have a backpack class that can hold other classes, and some of these backpacks the client is expected to be able to view the inventory of. The issue I am facing is I do not know how to communicate the existence of this backpack to the client so the client can request the info about its contents. I am considering either refactoring the OOP system to use cloned modules so the client can point to an object and a server script can look up the backpack via the object, or give the client the table’s memory address as a string via tostring() which the server can then look up via a dictionary.

EDIT: the memory address would be the server’s memory address, which the client would recieve as a string. Every class as a Serialize and Deserialize method to allow integration with stravant’s DataStore module, which can be used with RE/RF.

What have you guys done to handle this problem?

2 Likes

What? The client and server don’t share memory, unless you’re referring to something else.

And I suggest if you’re going to go for OOP in Lua you should make some intermediate form of the class which would be easier to transfer via RE and reformed on the receiver.

4 Likes

You need to have a function to “serialize” your class. In roblox I find this easiest to do by have a :serialize() method which condenses the class into a json string. Then having another method to deserialize that json string back into an object.

The object can now be passed across the client-server boundary via the string and can also be used in a data store.

An example of my use of this can be found inside my everest framework here: Everest2/src/Objects/BaseObject.lua at master · magnalite/Everest2 · GitHub

And the classes which extend it.

11 Likes

The real solution to this problem is not easy, it never is. Roblox tries to make networking as easy as possible for you, but when you take the cover off and try to do something like you’re doing now you find out how hard it really is. The tradeoff for it being harder is that once you do it the hard way you get a lot more control, and can get a more polished feeling result in the end.

One of the really important keys is to write shared classes that can be used by both the client and the server. For instance, take your backpack class. An instance of this class can exist on both the client and the server at the same time, but it won’t be used in exactly the same way on both. It contains the “backpacking” logic that is useful to both the client and server.

Your usage pattern might look like this:

  • User clicks on an item to pick up on the client

  • The client calls a remote PickUpItem(itemId / instance)

  • The server receives the call and checks that nobody else has picked up that item first

  • The server now calls playerWhoPickedItUp.Backpack:AddItem( … )

  • The server then sends a notification back to the client via remote: ItemSuccessfullyPickedUp or whatever

  • The client knows that the item was successfully picked up and calls myCurrentBackpack:AddItem( … )

You’ll also obviously need some code to send all of the current items in the backpack to the client to add to the backpack when they first enter the game. To do that you need to transform the packpack’s state into some table or string that can be sent via remote to the client, and then the client will “unpack” the table / string back into the representation that the Backpack class uses internally.

At the end of the day you’re going to have:

  • Some shared “utility” classes like Backpack that exist on both the client and server

  • Some client only classes, like those that implement your GUI

  • Some server only classes, like those that handle the DataStoreService

In addition to some “glue” code that takes your set of RemoteFunctions/Events and hooks them up to all of the classes in the appropriate way.

18 Likes

So I’m currently in the process of making an inventory, how would I check if no one else has picked it up first? Currently I have a value in the item, if the player initiates the item pick up and the value is false it allows him to pick it up and sets the value to true. Am I doing it right or is there a better way?

I mean, if you’re going to go full custom OOP, then the server should probably also have a “stuff that’s sitting on the ground” class to track what there is to pick up. The hardest part with Roblox coding is deciding what to put in custom OOP vs what to just leave in the object hierarchy for convenience. If you want to leave it in the hierarchy then presumably you would either delete it entirely once it is picked up, or if it’s a one-time pickup by everyone, record it in the player’s data (Which would also be an object, like ServerPlayerData) with a flag of “have they picked up X yet”… that would be harder though, because you also have to create the object locally on the client in that case since they players who have already picked it up shouldn’t be able to see it.

2 Likes