Functions does not carry over from server to client

  1. What do you want to achieve?
    I want to be able to clear out the metatable from both server and client of a projectile class

  2. What is the issue?
    Upon trying to “destroy” the projectile, the destroy function does not exists.

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I was not able to find a specific solution to this problem. I did some bug testing to see what I have found. This is what I know:

  • The function still retains when attempting to call the functions from server side
  • When calling from client side, the function no longer exists.
  • What I got in return was the table as shown in Projectile.new

Below is a local script that creates the projectile from gun class:

function Gun:Shoot(startPosition: Vector3, endPosition: Vector3)
	self.FireAnimation:Play()
	Global_Function.PlaySound(self.FireSound, self.gunModel.WeaponModel.bulletExit, true)
	
	for i = 1, self.gunModel:GetAttribute("pellet") do
		projectile.new(ReplicatedStorage.InstanceReplication.Bullet, startPosition, endPosition, self.gunModel:GetAttribute("velocity"), self.gunModel:GetAttribute("spread"), self.gunModel:GetAttribute("bulletWeight"), 15, self.gunModel.Parent.Name)
	end
	
	self.gunModel.Ammo.Value -= 1
end

Gun:Shoot(startPosition, endPosition)

Here is my projectile module script:

local Projectile = {}
Projectile.__index = Projectile

function Projectile.new(projectilePart: Part, startPosition: Vector3, endPosition: Vector3, speed: number, spread: number, weight: number, damage: number, owner: string)
	local self = setmetatable({}, Projectile)

	self.projectilePart = projectilePart:Clone()
	self.startPosition = startPosition
	self.endPosition = endPosition
	self.speed = speed or 0
	self.spread = spread or 0
	self.weight = weight or 0
	self.damage = damage or 0
	self.owner = owner or ""
	
	task.spawn(function()
		if RunService:IsClient() then
			-- Creates a visual projectile from server
			print("server bullet created")
			self.serverBullet = ReplicatedStorage.RemoteEvents.replicateProjectile:InvokeServer(projectilePart, startPosition, endPosition, speed, spread, weight, damage, owner)
		end
	end)

	self.characterHit = {}
	self:_Init()

	return self
end

function Projectile:_Init()
	task.spawn(function()
		if RunService:IsClient() then
			if not self.serverBullet then
				repeat task.wait() until self.serverBullet

				if self.projectilePart.Parent and self.serverBullet.projectilePart.Parent then
					-- Hides server projectile from client
					self.serverBullet.projectilePart.Parent = ReplicatedStorage
				end
			end
		end
	end)

	if projectile touch something then
		destroy projectile
	end

	self.projectilePart.Destroying:Connect(function()
		self:Destroy()
	end)

	task.spawn(function()
		task.wait(10)
		self:Destroy()
	end)
end

function Projectile:Destroy()
	if self.projectilePart.Parent then
		self.projectilePart:Destroy()
		
		if RunService:IsClient() then
			if self.serverBullet.projectilePart then
				print(self.serverBullet)
				
				-- THE PROBLEM IS HERE, "attempt to call missing method 'Destroy' of table"
				self.serverBullet:Destroy()
				

				print("server bullet destroy")
			end
		end
		
		return setmetatable(Projectile, nil)
	end
end

return Projectile

Below is the remote function handling the replication:

ReplicatedStorage.RemoteEvents.replicateProjectile.OnServerInvoke = function(player, projectilePart: Part, startPosition: Vector3, endPosition: Vector3, speed: number, spread: number, weight: number, damage: number, owner: string)
	print("bullet clone called")
	return Projectile_Module.new(projectilePart, startPosition, endPosition, speed, spread, weight, damage, owner)
end
1 Like

I made some additional testing, and it is confirmed that I was not able to transfer the functions from the server to the client

task.spawn(function()
		if RunService:IsClient() then
			print("server bullet created")

			-- Creates server bullet with Projectile.new in the server

			self.serverBullet = ReplicatedStorage.RemoteEvents.replicateProjectile:InvokeServer(projectilePart, startPosition, endPosition, speed, spread, weight, damage, owner)
			self.serverBullet:Destroy() -- Cannot find function "destroy" in client
		end
	end)
1 Like

Metatables are not able to be sent over RemoteEvents/Functions, usually done as a way to trim down on data sent. This is a limitation that you will just have to deal with, but thankfully there’s a really easy way to get around this.

While the metatable data is lost, anything inside of self that is able to be sent over to the client (i.e. numbers, instances, vectors, etc.) will pass right through.

You could then just use the . operator in the module as a way to specify what self is when you run the function, as its not implicit when you do that unlike :.

local ServerBullet = MyRemoteEvent:InvokeServer(...)

-- using '.' changes the arguments from (any...) -> (self, any...), and allows us to define self before running it
-- also, instead of Object:Destroy(), you have to refrence the module directly and call .Destroy() on it.
Projectile.Destroy(ServerBullet)
2 Likes

Thank you for your input, your solution works! I’m still getting used to metatable, but this is a great addition to learning about this topic :+1:

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.