Hello! Part of my game involves custom OOP objects called Fighters. The Fighter object contains information including the player’s current posture, energy, fighting style, etc. Every Fighter created in the game (whether it be player or NPC) is placed into a global lookup table.
I have a getter function called GetFighterFromCharacter that returns the corresponding Fighter from the global lookup table. GetFighterFromCharacter is supposed to be used both client-side and server-side, as the client is supposed to subscribe to when properties of the fighter are changed to update UI (e.g. when the server changes the fighter’s posture, update the posture bar). The problem arises when I use the function client-side, as it’ll return nil.
-- On the client-side, inside a script that involves updating the UI
local fighter = FighterClass.GetFighterFromCharacter(character)
-- Error here. Attempt to index nil with StructureUpdated
fighter.StructureUpdated:Connect(function(newStructure)
structureDisplay.Text = newStructure
end)
I’ve tried networking to get around this issue, but remote events/functions can’t send metatables. Thanks in advance.
There’s not much we can do without knowledge of the internals of GetFighterFromCharacter. As for sending metatables over the network, that was never necessary. The metatable acts a liaison between the class’ members and the object’s properties; no data is stored in this connection. You need only send the raw object, then re-attach the metatable on the client side
I can’t believe I forgot to add the code of the actually bugging part
-- In fighterTable, key is the character model and value is the Fighter object associated with the model
function FighterClass.GetFighterFromCharacter(character : Model): Fighter
for index, fighter : Fighter in fighterTable do -- Find the Fighter and return it
if fighter.Character == character then
return fighter
end
end
end
How can I go about re-attaching the metatable after receiving the object on the client-side?
if ur adding to a table in the module on the server, then trying to get it with the client, i dont think that works. Im pretty sure modules have seperate data for the server vs the clients.
Assuming these function’s are coming from a module, know that the server and client do not share modules. Each gets a copy that is executed separately. It is likely your fighterTable was not properly initialized on the client, so that’s why you are receiving nil.
Re-attaching a metatable is no unique process; it’s done exactly as you did in the class’ constructor
I don’t have much time to write this but.
You could split the functionality of the fighter table.
E.g. You make a module for fighter with all its functionality and then use a table with just data values for fighter which can be passed around server to client etc.
In this setup, you can call functionality from the fighter module on both the server and the client.
This video by @sleitnick encompasses this concept deeper
Revisiting the issue right now with this video in mind. I’ve gotten pretty far with some new code. Firstly I have a new function of FighterClass that initializes the code for listening to the client requests for a fighter.
function FighterClass.InitServer()
if RunService:IsClient() then
error(`Attempt to init FighterClass module clientside`)
end
FighterRequestRemote.OnServerInvoke = function(player : Player)
local character = player.Character
for index, fighter : Fighter in fighterTable do
if fighter.Character == character then
print(`Fighter found: {fighter.Name}`)
return fighter
end
end
end
end
Then in GetFighterFromCharacter() I have a RunService check then a remote function invoke.
function FighterClass.GetFighterFromCharacter(character : Model): Fighter
if RunService:IsServer() then
for index, fighter : Fighter in fighterTable do -- Find the Fighter and return it
if fighter.Character == character then
return fighter
end
end
else -- Invoked client-side. Must use the FighterRequestRemote
local fighter = FighterRequestRemote:InvokeServer() -- tables cannot be cyclic error here
return fighter
end
end
This request system works as intended, as when I run the game, the print statement within InitServer() shows up on the console. However, I get a “tables cannot be cyclic” error, although I can’t find where the cyclic table would be.
Here’s what the lookup table looks like right now. The other indexes are just for dummies.
Update: Fixed. Instead of trying to get the fighter and subscribe to its events client-side, I decided to simply let the client only handle updating the UI and leave listening to when the properties of the fighter change to the server
This also means I’m switching back to metatables for the time being. Might switch back to the table-based approach but we’ll see