How can I use the self method on a table/Object created from the server on the client? (or work arround)

Hello I was scripting a Custom Tool system since I do not like roblox’s tools as they are outdated, messy, and glitchy

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")

local ToolID = 0
local ClientReplicator = ReplicatedStorage.CToolRS.CToolClientReplicatorRE

-- Loads all classes
local Classes = {}
for i,v in pairs(script:GetChildren()) do
	Classes[v.name] = require(v)
end

local CTool = {}
CTool.__index = CTool

function CTool.new(Name, IconId)
	local NewCTool = {}
	setmetatable(NewCTool, CTool)
	
	NewCTool.ID = ToolID
	NewCTool.Owner = nil
	NewCTool.IsEquipped = false
	NewCTool.CanEquip = true
	
	if Name then
		NewCTool.Name = Name
	else
		NewCTool.Name = "CTool"
	end
	
	if IconId then
		NewCTool.Icon = IconId
	else
		NewCTool.Icon = nil
	end

	ToolID = ToolID + 1
	return NewCTool
end

-- ONLY CALL FROM SERVER SIDE
function CTool:SetOwner(Player)
	local PlayerInventory = require(Player:FindFirstChild("Inventory"))
	
	if PlayerInventory then
		self.Owner = Player
		PlayerInventory.Backpack[self.ID] = self
		ClientReplicator:FireClient(Player, self, "Add")
	else
		print("Could not find player's inventory")
	end
end

-- ONLY CALL FROM SERVER SIDE
function CTool:Destroy()
	
	if self.Owner then
		
		local Inventory = require(self.Owner.Inventory)
		
		ClientReplicator:FireClient(self.Owner,self.ID,"Destroy")
		if self.IsEquipped == true then
			Inventory.Hand = nil
		end
	end
	
	self = nil
end

-- ONLY CALL FROM CLIENT, Due to networking behavior unequipping a tool from the server is not possible as we also need to run it's local code first
-- And exploiters can make it not run, to add this feature I will add custom support to make functions to handle these things
function CTool:Equip()
	
end

function CTool:Unequip()
	
end

return CTool

Here I wanna call these functions (Equip and unequip) from the client but I cannot do this as once I pass them to the client they obviously will not work since for the client it looks like some random table i passed it, So how can I make it work on the client or work arround it?

Also heres the Inventory handler although this isnt that relevant to the question

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ContextActionService = game:GetService("ContextActionService")
local Players = game:GetService("Players")
local StarterGui = game:GetService("StarterGui")

StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, false)

local MyPlayer = Players.LocalPlayer
local Inventory = require(MyPlayer:WaitForChild("Inventory"))

local IsInventoryOpen = false

local Settings = require(script.HandlerSettings)
local Data = require(script.HandlerData)
local GUI = Settings.GUI
local Values = Settings.Values

local HotBarSlotsInUse = 0

local CToolRS = ReplicatedStorage.CToolRS
local ClientReplicator = CToolRS:WaitForChild("CToolClientReplicatorRE")
local CToolEquipRE = CToolRS.CToolEquipRE

local CTool1 = require(ReplicatedStorage.CToolLibrary)

local ToolExperiment = nil

local Keybinds = {
	[1] = Enum.KeyCode.One,
	[2] = Enum.KeyCode.Two,
	[3] = Enum.KeyCode.Three,
	[4] = Enum.KeyCode.Four,
	[5] = Enum.KeyCode.Five,
	[6] = Enum.KeyCode.Six,
	[7] = Enum.KeyCode.Seven,
	[8] = Enum.KeyCode.Eight,
	[9] = Enum.KeyCode.Nine,
	[10] = Enum.KeyCode.Zero
}

local function ActionHandler(Action,InputState,InputObject)
	if InputState == Enum.UserInputState.Begin then
		local ID = tonumber(Action)
		local CTool = Inventory.Backpack[ID]
		
		print(ID)
		
		if Inventory.Hand then
			if Inventory.Hand == CTool then
				Inventory.Hand = nil
				CTool.IsEquipped = false
				CTool:Unequip()
				--CToolEquipRE:FireServer(false, ID)
				GUI.HotBar[ID].Outline.Visible = false
			else
				Inventory.Hand.IsEquipped = false
				Inventory.Hand:Unequip()
				--CToolEquipRE:FireServer(false, Inventory.Hand.ID)
				GUI.HotBar[Inventory.Hand.ID].Outline.Visible = false
				Inventory.Hand = nil
				CTool.IsEquipped = true
				Inventory.Hand = CTool
				CTool:Equip()
				--CToolEquipRE:FireServer(true, ID)
				GUI.HotBar[ID].Outline.Visible = true
			end
		else
			if CTool.IsEquipped == false then
				CTool.IsEquipped = true
				Inventory.Hand = CTool
				CTool:Equip()
				--CToolEquipRE:FireServer(true, ID)
				GUI.HotBar[ID].Outline.Visible = true
			else
				CTool.IsEquipped = false
				CTool:Unequip()
				--CToolEquipRE:FireServer(false, ID)
				GUI.HotBar[ID].Outline.Visible = false
			end
		end
		
	end
end

local Functions = {
	["Add"] = function(CTool)
		Inventory.Backpack[CTool.ID] = CTool
		ToolExperiment = CTool
		
		local NewToolFrame = GUI.ToolFrame:Clone()
		NewToolFrame.Name = CTool.ID
		NewToolFrame.NameLabel.Text = CTool.Name
		NewToolFrame.Visible = true
		
		if HotBarSlotsInUse <= Values.MaxHotBarSlots then
			HotBarSlotsInUse = HotBarSlotsInUse + 1
			NewToolFrame.Parent = GUI.HotBar
			
			for Slot = 1,Settings.Values.MaxHotBarSlots,1 do
				if not Data.HotBar[Slot] then
					Data.HotBar[Slot] = CTool
					NewToolFrame.NumberLabel.Text = Slot
					ContextActionService:BindAction(CTool.ID,ActionHandler,false,Keybinds[Slot])
					break
				end
			end
		else
			Data.InventoryMenu[CTool.ID] = Inventory.Backpack[CTool.ID]
			NewToolFrame.Parent = GUI.InventoryMenu
		end
	end,
	["Destroy"] = function(CTool)
		local String = tostring(CTool)
		GUI.HotBar[String]:Destroy()
		
		if Inventory.Backpack[CTool].IsEquipped == true then
			Inventory.Hand = nil
		end
		
		Inventory.Backpack[CTool] = nil
		
		for Slot = 1,Settings.Values.MaxHotBarSlots,1 do
			if Data.HotBar[Slot] then
				Data.HotBar[Slot] = nil
			end
		end
	end,
	["Connect"] = function(CTool)
		
	end,
}

local function ReplicateToolToClient(CTool, Instruction)
	Functions[Instruction](CTool)
end

ClientReplicator.OnClientEvent:Connect(ReplicateToolToClient)

After some research I discovered that the self method can only be used on the module the object was created, if you pass it to the client it looks like a copy of said modules so it will not work,

The work arround was just make similar functions without the self method for the client.

Or make a local constructor function and create it with the same properties as the object created on the server and fire a remote to create the object locally.

1 Like