[Solved] Gun stops spawning after dropping/picking up several times

I am working on the gun picking up and dropping system like L4D or Phantom Forces. My plan was to copy the gun model from ReplicatedStorage to ServerScriptService to wait for ammo statistics, then transmit to Workspace. Everything was going well until I spotted a problem 2 days ago.

At first, the gun was from ReplicatedStorage properly transmitted to Workspace when I give it the first try

However, when I pick up/drop the gun several times, the gun stopped appearing as the scripts become immobile to any inputs.

This is my main LocalScript:

gunPickup.OnClientEvent:Connect(function(weapon, value, ammo, storage)
	reloading = false
	reloadAnimTrack:Stop()
	saveData()
	if value == 1 then
		gunDrop:FireServer(primary)
		sendAmmo:FireServer(primary, priAmmo[1], priAmmo[2], character.HumanoidRootPart)
		priAmmo[1] = ammo
		priAmmo[2] = storage
		primary = weapon
		switch(primary)
	elseif value == 2 then
		gunDrop:FireServer(secondary) -- Transmit gun model from ReplicatedStorage to ServerScriptService to receive ammo statistics
		sendAmmo:FireServer(secondary, secAmmo[1], secAmmo[2], character.HumanoidRootPart) -- Send ammo stats to the gun which is in ServerScriptService
		secAmmo[1] = ammo
		secAmmo[2] = storage
		secondary = weapon
		switch(secondary)
	end
end)

Gun dropping from ServerScript:

local replicatedStorage = game:WaitForChild('ReplicatedStorage')
local event = replicatedStorage.Events:WaitForChild('GunDrop')
local models = replicatedStorage:WaitForChild('Collectibles')

local function GunDrop(player, weapon)
	if weapon ~= nil then
		print('Copied weapon to Server')
		local droppedGun = models:WaitForChild(weapon.Name):Clone()
		droppedGun.Parent = game:GetService('ServerScriptService')
		return true
	end
end

event.OnServerEvent:Connect(GunDrop)

ServerScript inside a gun to receive ammo information from main script (All guns have the same structure below, so I copied from a custom weapon):

local RS = game:GetService('ReplicatedStorage')
local GunPickup = RS.Events:WaitForChild('GunPickup')
local sendAmmo = RS.Events:WaitForChild('SendAmmo')

local gun = script.Parent
local prompt = gun.Handle.ProximityPrompt

local gunValue = 1
local currentAmmo = 30
local ammoStorage = 300

prompt.Triggered:Connect(function(player)
	GunPickup:FireClient(player, gun, gunValue, currentAmmo, ammoStorage)
	gun:Destroy()
end)

gun.AncestryChanged:Connect(function(gun, parent)
	if parent == game:GetService('ServerScriptService') then
		sendAmmo.OnServerEvent:Connect(function(player, gunName, ammo, storage, rootPart)
			repeat wait() until gunName
			if gun.Name == gunName.Name then
				print("Weapon sent to Workspace")
				currentAmmo = ammo
				ammoStorage = storage
				gun.Parent = workspace
				gun:WaitForChild('Handle').CFrame = rootPart.CFrame * CFrame.new(0,1,-3) * CFrame.Angles(0, math.rad(90),0)
				wait(1)
				prompt.Enabled = true
			end
		end)
	end
end)

So what have I been doing wrong and how can I fix it?

PS: If these given information is not enough, you can copy the game from here:

1 Like

Something’s probably yielding forever in your script. I’d try adding numbered prints (e.g. 1, 2, 3, 4, 5, etc.) until the error occurs so you can look and see where exactly your script abruptly ended, and narrow it down to the specific problematic line.

The problem is from gun dropping script. After 20-25 times, the script doesn’t seem to receive weapon name

local function GunDrop(player, weapon)
--"weapon" variable returns nil after 20-25 times

I’ve tested another factor, like checking who fired the it from LocalScript, but “player” variable is always received even if there is no gun spawned.

It sounds like the gun doesn’t exist as far as the server can tell when when the gundrop function is fired. Does the gun ever get reparented outside of being initially created?

“weapon” variable from Gundrop function only receive “string” value from LocalScript, then Gundrop will find the weapon inside ReplicatedStorage and clone it. Therefore, it should be fine (theoretically)
Also, when I check the weapon name from LocalScript, it always returns the name of the weapon, but gunDrop:FireServer(primary) does not send the name as the problem occurs.

Right before you do gundrop:FireServer(primary), could you print primary and if it prints text in your output, click on it? If it’s a string it should bring you to the script, if it’s an instance it should select the instance in the explorer

The “primary” variable always return “Instance”

Then it sounds like you’re not passing over a string to the server but rather a direct reference to the gun itself, which may not exist on the server for various reasons.
There are several ways to go about sidestepping issues like this entirely, but my personal favorite is definitively a slot system. Instead of telling the server to drop this one very specific weapon, you’d instead ask it to drop whatever’s in your primary slot. It makes it a lot harder for things to go wrong. You just have to keep track of what a player’s equipped weapons are on the server, but you should be doing that anyways to prevent exploits.

You could also try checking if the variable you’re firing to the server is an instance or not and swap it to a string instead, but if the weapon doesn’t exist on the server then odds are it’ll still break the same way.

So I sent the raw “primary” variable to execute the GunDrop function instead of finding the weapon by its name, but then it yells this error:
image
GunDrop function:

local replicatedStorage = game:WaitForChild('ReplicatedStorage')
local event = replicatedStorage.Events:WaitForChild('GunDrop')
local models = replicatedStorage:WaitForChild('Collectibles')

local function GunDrop(player, weapon)
	if weapon ~= nil then
		weapon.Parent = game:GetService('ServerScriptService')
	end
end

event.OnServerEvent:Connect(GunDrop)

That error usually occurs when an object gets reparented too quickly, or when an object is destroyed.
Is your weapon being reparented multiple times in a row, or being destroyed anywhere?

The weapon is only reparented once, and then prompt will be enabled for players to pick up again. After that, it will self-destruct when a player triggers a prompt.

Seems the reparenting cannot be done by using the Instance itself, but by using another variable and set it to the model from the library indirectly

Nevermind, I found the solution. No longer that messing with gun location that I realized how drunk I was. The “primary” and “secondary” weapon was set up like this right when the player is added:

local primary = collectibles:WaitForChild('SPAS-12')
local secondary = collectibles:WaitForChild('Glock17')

However, when i tried to use those variables to make gun dropping/picking up system, I just called the Instance itself

		gunDrop:FireServer(primary)
		sendAmmo:FireServer(primary, priAmmo[1], priAmmo[2], character.HumanoidRootPart)
		priAmmo[1] = ammo
		priAmmo[2] = storage
		primary = weapon --Problematic line right here
		switch(primary)

Now I changed that line to be consistent with the setup lines above, and now it worked:

gunDrop:FireServer(primary)
		sendAmmo:FireServer(primary, priAmmo[1], priAmmo[2], character.HumanoidRootPart)
		priAmmo[1] = ammo
		priAmmo[2] = storage
		primary = collectibles:WaitForChild(weapon.Name) --Corrected line
		switch(primary)

Anyways, thank you for pointing out my mistake

1 Like