I have been experimenting with OOP classes in order to try to create a framework for turn-based RPG games I could make, but recently I have been stuck on a rather annoying problem relating to client input to module scripts. So far a server script uses a few module scripts to create a series of classes including a Battle class which contains tables that contain other classes. The problem resides within the Hero class, which serves as an entity class used by players to fight other enemies. The code for that specific module looks like this:
GetArena = game:GetService("ReplicatedStorage"):WaitForChild("GetArena")
MakeChoice = game:GetService("ReplicatedStorage"):WaitForChild("MakeChoice")
Entity = require(game.ServerScriptService.Classes.Entity)
Hero = {}
Hero.__index = Hero
setmetatable(Hero, Entity)
function Hero.new(maxhp, maxmp, maxsp, bp, hp, mp, sp, character, currentarena, player)
local newplayer = Entity.new(maxhp, maxmp, maxsp, bp, hp, mp, sp, character, currentarena)
setmetatable(newplayer, Entity)
newplayer.Player = player
newplayer.Choice = nil
return newplayer
end
function Hero:FetchArena()
return self.Arena
end
function Hero:ListenForRequest()
GetArena.OnServerInvoke = self:FetchArena()
end
return Hero
Whenever the Battle class tries to call ListenForRequest() from a hero class, it returns this error:
[ServerScriptService.Classes.Battle:40: attempt to call a nil value]
If I were to modify the code so the .new function contains this line
GetArena.OnServerInvoke = function() return newplayer.Arena end
instead of the extra functions, it instead returns
[tables cannot be cyclic]
None of the documentation I have found has helped me with this specific problem and I am unsure what other way I could do so that a Local script can call a function from an OOP class module script run by a server script. The remote function is meant to allow the local script in the PlayerGui to âreadâ the arena class to be able to pull off functions like selecting all enemies in an arena.
Another thing to be known is that all Entity classes have the Arena property set to nil when they are first created and are set by the Battle class later due to how OOP works.
Are there any solutions for this or any ideas to work around it?
Update 1:
The initial problem has been resolved, but it has changed to something else as shown at the post below:
function Hero:ListenForRequest()
GetArena.OnServerInvoke = self.FetchArena
end
Reason for this is youâll be hooking to the reference of the function, rather than calling the function.
On the wiki here you can see that you set the RemoteFunction.OnServerInvoke event with an = to a function.
Unfortunately even with those changes it still returns the same error:
[ ServerScriptService.Classes.Battle:40: attempt to call a nil value]
The problem might be somewhere else.
Classes.Battle line 40 resides inside a for in pairs loop:
for i,v in pairs(self.Players) do
v.Arena = self
v.ListenForRequest() --Line 40
if self.Arena.Origin["Player"..i] ~= nil then
coroutine.wrap(function()
local Entity = v.Character
local Slot = self.Arena.Origin["Player"..i]
Entity.Humanoid:MoveTo(Slot.WorldPosition)
Entity.Humanoid.MoveToFinished:Wait()
Entity.HumanoidRootPart.Facing.CFrame = CFrame.new(0,0,0) * CFrame.fromEulerAnglesXYZ(math.rad(Slot.WorldOrientation.X),math.rad(Slot.WorldOrientation.Y),math.rad(Slot.WorldOrientation.Z))
Entity.HumanoidRootPart.Facing.MaxTorque = Vector3.new(0,math.huge,0)
end)()
else
table.remove(self.Players,i)
end
end
It initializes the Hero class, setting the Arena Property which contains a reference to the Battle class to allow the Player to read data from the Battle through the remote function. I tried to make each player listen for the remote function individually using this initialization but it doesnât seem to work regardless of what I try.
Yep, it always seems to think that function is nil for some strange reason though it was clearly defined in the Hero module. The alternate way only returns a cyclic table which results in an error.
Since youâre trying to reference the self in the ListenForRequest() you should be using the : to make it a method that passes in the self variable to it.
Iâm trying to think of why your code still errors though when it gets to it.
In Hero.new, you set the metatable to Entity instead of Hero. Is this intended? This might explain why ListenForRequest isnât there even if it should.
Surprisingly I missed that, it now returns a new error when I attempt to click a button from the player GUI which calls that remote function.
[1:44.130 - Arena is not a valid member of Player âPlayers.Glitchy_Robotâ]
Then this might be where it goes into what Cody said above where it strips metatables.
I donât know all of your code so Iâm not 100% sure where that error is starting.
But the error looks like youâre trying to reference Arena from the player object itself and not one of your objects.