Converting LocalScript to ServerScript

So I kinda messed up… I was writing my script for a while now and then I realized other players are unable to see the cloned pet (I’m cloning them through the local script)

local marketplaceService = game:GetService("MarketplaceService")
local runService = game:GetService("RunService")
local player = game.Players.LocalPlayer
local blackToothless = game.ReplicatedStorage.DragonCompanion
local button = script.Parent
local blackToothlessClone = nil
local isPetEquipped = false
local gamepassId = 719413770 -- Replace with the ID of your gamepass

-- Function to update button text based on gamepass ownership
local function UpdateButtonText()
	local ownsGamepass = marketplaceService:UserOwnsGamePassAsync(player.UserId, gamepassId)
	button.Text = ownsGamepass and "Equip" or "Purchase"
end

-- Immediately update button text when the script runs
UpdateButtonText()

local function UpdatePetPosition()
	while isPetEquipped do
		if blackToothlessClone and player.Character then
			if (blackToothlessClone.HumanoidRootPart.Position - player.Character.HumanoidRootPart.Position).Magnitude <= math.huge then
				local newPosition = player.Character.HumanoidRootPart.Position + Vector3.new(5, 3, 5)
				blackToothlessClone.HumanoidRootPart.CFrame = blackToothlessClone.HumanoidRootPart.CFrame:Lerp(CFrame.new(newPosition), 0.05)

				local lookAtPosition = player.Character.HumanoidRootPart.Position
				local lookRotation = CFrame.new(blackToothlessClone.HumanoidRootPart.Position, lookAtPosition)
				local initialRotationOffset = CFrame.Angles(0, math.rad(-90), 0)
				lookRotation = lookRotation * initialRotationOffset

				blackToothlessClone.HumanoidRootPart.CFrame = lookRotation * CFrame.new(0, 0, 0)
			end
		end
		runService.RenderStepped:Wait()
	end
end

local function ClonePet()
	local ownsGamepass = marketplaceService:UserOwnsGamePassAsync(player.UserId, gamepassId)
	if not ownsGamepass then
		-- Prompt the purchase of the gamepass
		marketplaceService:PromptGamePassPurchase(player, gamepassId)
		return -- Stop execution if the player doesn't own the gamepass
	end

	if blackToothlessClone then -- Check if the pet has already been cloned
		-- Destroy the pet if it already exists
		blackToothlessClone:Destroy()
		blackToothlessClone = nil -- Reset the variable
		button.Text = "Equip" -- Change button text to "Equip"
		isPetEquipped = false
	else
		-- Check if the player's pet folder already exists
		local playerFolderName = player.UserId .. "_Pet"
		local existingFolder = game.Workspace:FindFirstChild(playerFolderName)

		if existingFolder then
			-- If the folder already exists, use it to parent the pet
			blackToothlessClone = blackToothless:Clone()
			blackToothlessClone.Parent = existingFolder
		else
			-- If the folder doesn't exist, create a new one
			local playerFolder = Instance.new("Folder")
			playerFolder.Name = playerFolderName -- Use the player's UserId to make it unique
			playerFolder.Parent = game.Workspace
			blackToothlessClone = blackToothless:Clone()
			blackToothlessClone.Parent = playerFolder
		end

		-- Set the pet's position close to the player
		if player.Character then
			blackToothlessClone:SetPrimaryPartCFrame(CFrame.new(player.Character.HumanoidRootPart.Position))
		end

		button.Text = "Unequip" -- Change button text to "Unequip"
		isPetEquipped = true
		UpdatePetPosition()
	end
end

script.Parent.MouseButton1Click:Connect(function()
	ClonePet()
end)

marketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, passId, wasPurchased)
	if passId == gamepassId and wasPurchased then
		print(player.Name .. " successfully purchased the gamepass. Granting particles.")
		UpdateButtonText()
	end
end)

If someone could split the code in LocalScript + ServerScript or instead make the pet visible to others, I’d appreciate it.

What I think you’ll need to do is to use Remote Events. Store a Remote Event in ReplicatedStorage and create a server script into ServerScriptService.

  • Server Script
    game:GetService(“ReplicatedStorage”).RemoteEvent.OnServerEvent:Connect(function(player)
    [The ClonePet() Script]
    end)

  • Local Script
    script.Parent.MouseButton1Click:Connect(function()
    game:GetService(“ReplicatedStorage”).RemoteEvent:FireServer()
    end)

You want to also add one for the UpdatePetPosition.
Also, remember to edit the GUI using “Player.PlayerGui” when you transfer the script to server script.

ServerSide

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

local PetReplicator = game.ReplicatedStorage:WaitForChild("PetReplicator")

local blackToothless = ServerStorage.DragonCompanion
local gamepassId = 719413770 -- Replace with the ID of your gamepass

local function CreatePet(player)
	local ownsGamepass = MarketplaceService:UserOwnsGamePassAsync(player.UserId, gamepassId)
	if not ownsGamepass then
		return -- Player doesn't own the gamepass, don't create the pet
	end

	local playerFolderName = player.UserId .. "_Pet"
	local existingFolder = game.Workspace:FindFirstChild(playerFolderName)

	if not existingFolder then
		existingFolder = Instance.new("Folder")
		existingFolder.Name = playerFolderName
		existingFolder.Parent = game.Workspace
	end

	local pet = blackToothless:Clone()
	pet.Parent = existingFolder
	return pet
end

PetReplicator.OnServerEvent:Connect(function(player)
	local pet = CreatePet(player)
	if pet then
		-- Position the pet close to the player
		local character = player.Character
		if character then
			pet:SetPrimaryPartCFrame(CFrame.new(character.HumanoidRootPart.Position))
		end
	end
end)

LocalScript

local MarketplaceService = game:GetService("MarketplaceService")
local PetReplicator = game:GetService("ReplicatedStorage").PetReplicator
local player = game.Players.LocalPlayer
local button = script.Parent
local isPetEquipped = false
local gamepassId = 719413770 -- Replace with the ID of your gamepass

local runService = game:GetService("RunService")
local blackToothlessClone = nil

local function UpdateButtonText()
	local ownsGamepass = MarketplaceService:UserOwnsGamePassAsync(player.UserId, gamepassId)
	button.Text = ownsGamepass and "Equip" or "Purchase"
end

UpdateButtonText()

local function UpdatePetPosition()
	while isPetEquipped do
		if blackToothlessClone and player.Character then
			local targetPosition = player.Character.HumanoidRootPart.Position + Vector3.new(5, 3, 5) -- Offset from the player's position
			local distance = (blackToothlessClone.HumanoidRootPart.Position - targetPosition).Magnitude
			if distance > 1 then -- Adjust this threshold as needed to prevent jittery movement
				-- Interpolate the pet's position towards the target position
				blackToothlessClone.HumanoidRootPart.CFrame = blackToothlessClone.HumanoidRootPart.CFrame:Lerp(CFrame.new(targetPosition), 0.05)

				-- Make the pet face towards the player
				local lookAtPosition = player.Character.HumanoidRootPart.Position
				local lookRotation = CFrame.new(blackToothlessClone.HumanoidRootPart.Position, lookAtPosition)
				local initialRotationOffset = CFrame.Angles(0, math.rad(-90), 0)
				lookRotation = lookRotation * initialRotationOffset
				blackToothlessClone.HumanoidRootPart.CFrame = lookRotation * CFrame.new(0, 0, 0)
			end
		end
		runService.RenderStepped:Wait()
	end
end

local function ClonePet()
	local ownsGamepass = MarketplaceService:UserOwnsGamePassAsync(player.UserId, gamepassId)
	if not ownsGamepass then
		MarketplaceService:PromptGamePassPurchase(player, gamepassId)
		return
	end

	if not isPetEquipped then
		PetReplicator:FireServer() -- Request the server to create the pet
		button.Text = "Unequip"
	else
		button.Text = "Equip"
		-- You might want to remove the pet from the client's view here
		-- Since the pet is managed by the server
	end
	isPetEquipped = not isPetEquipped
end

button.MouseButton1Click:Connect(ClonePet)

MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, passId, wasPurchased)
	if passId == gamepassId and wasPurchased then
		print(player.Name .. " successfully purchased the gamepass. Granting particles.")
		UpdateButtonText()
	end
end)

PetReplicator.OnClientEvent:Connect(function(pet)
	blackToothlessClone = pet
	if isPetEquipped then
		-- Start updating the pet's position if it's equipped
		UpdatePetPosition()
	end
end)

I tried but uh… the pet won’t clone

Well, I think it is cloning but I think the issue is that you would also want to create another remote event for UpdatePetPosition().

Just put it in the same server script and give that a try!

Can I replicate the movement on the client-side with the LocalScript? The server should automatically update the position of the object if it was cloned globally.

I don’t think that would work, you probably want to just do the movement in a server script and get the model by cloning in the server script or else the local player will see the model and movement but other players won’t see it.

ServerSideScript

local MarketplaceService = game:GetService("MarketplaceService")
local PetReplicator = game:GetService("ReplicatedStorage"):WaitForChild("PetReplicator")
local PetPositionUpdater = game:GetService("ReplicatedStorage"):WaitForChild("PetPositionUpdater")

local playerPets = {}

local gamepassId = 719413770 -- Replace with the ID of your gamepass

local function CreatePet(player)
	local ownsGamepass = MarketplaceService:UserOwnsGamePassAsync(player.UserId, gamepassId)
	if not ownsGamepass then
		return -- Player doesn't own the gamepass, don't create the pet
	end

	local playerFolderName = player.UserId .. "_Pet"
	local existingFolder = game.Workspace:FindFirstChild(playerFolderName)

	if not existingFolder then
		existingFolder = Instance.new("Folder")
		existingFolder.Name = playerFolderName
		existingFolder.Parent = game.Workspace
	end

	local pet = game.ServerStorage.DragonCompanion:Clone()
	pet.Parent = existingFolder
	playerPets[player.UserId] = pet
	return pet
end

local function UpdatePetPosition(player, isPetEquipped)
	while isPetEquipped do
		local pet = playerPets[player.UserId]
		if pet and player.Character then
			local targetPosition = player.Character.HumanoidRootPart.Position + Vector3.new(5, 3, 5) -- Offset from the player's position
			local distance = (pet.HumanoidRootPart.Position - targetPosition).Magnitude
			if distance > 1 then -- Adjust this threshold as needed to prevent jittery movement
				-- Interpolate the pet's position towards the target position
				pet.HumanoidRootPart.CFrame = pet.HumanoidRootPart.CFrame:Lerp(CFrame.new(targetPosition), 0.05)

				-- Make the pet face towards the player
				local lookAtPosition = player.Character.HumanoidRootPart.Position
				local lookRotation = CFrame.new(pet.HumanoidRootPart.Position, lookAtPosition)
				local initialRotationOffset = CFrame.Angles(0, math.rad(-90), 0)
				lookRotation = lookRotation * initialRotationOffset
				pet.HumanoidRootPart.CFrame = lookRotation * CFrame.new(0, 0, 0)
			end
		end
		wait() -- Using wait instead of runService.RenderStepped:Wait() on the server
	end
end

PetReplicator.OnServerEvent:Connect(function(player, destroyPet)
	if destroyPet then
		local pet = playerPets[player.UserId]
		if pet then
			pet:Destroy()
			playerPets[player.UserId] = nil
		end
	else
		local pet = CreatePet(player)
		if pet then
			-- Position the pet close to the player
			local character = player.Character
			if character then
				pet:SetPrimaryPartCFrame(CFrame.new(character.HumanoidRootPart.Position))
			end
		end
	end
end)

PetPositionUpdater.OnServerEvent:Connect(UpdatePetPosition)

LocalScript

local MarketplaceService = game:GetService("MarketplaceService")
local PetReplicator = game:GetService("ReplicatedStorage"):WaitForChild("PetReplicator")

local player = game.Players.LocalPlayer
local button = script.Parent
local isPetEquipped = false
local gamepassId = 719413770 -- Replace with the ID of your gamepass

local function UpdateButtonText()
	local ownsGamepass = MarketplaceService:UserOwnsGamePassAsync(player.UserId, gamepassId)
	button.Text = ownsGamepass and "Equip" or "Purchase"
end

UpdateButtonText()

local function ClonePet()
	local ownsGamepass = MarketplaceService:UserOwnsGamePassAsync(player.UserId, gamepassId)
	if not ownsGamepass then
		MarketplaceService:PromptGamePassPurchase(player, gamepassId)
		return
	end

	if not isPetEquipped then
		PetReplicator:FireServer() -- Request the server to create the pet
		button.Text = "Unequip"
	else
		-- Destroy the pet if it's already equipped
		PetReplicator:FireServer(true) -- Request the server to destroy the pet
		button.Text = "Equip"
	end
	isPetEquipped = not isPetEquipped
end

button.MouseButton1Click:Connect(ClonePet)

MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, passId, wasPurchased)
	if passId == gamepassId and wasPurchased then
		print(player.Name .. " successfully purchased the gamepass. Granting particles.")
		UpdateButtonText()
	end
end)

image
image
image
image

The pet clones, but the position won’t update, it stays still.

Ugh, I give up. I’ll have to go sleep now and hopefully someone could help me out more by tomorrow.

Sorry, my mistake. When you clone the pet you would want to update its position after it’s been cloned.

One thing I would recommend is to not use remote events when updating the pet position since I only
see that you are using one pet. If this is also for multiple pets then if you want you can use a bindable function as an event function to put the cloned pet in the function like “Function:Invoke(pet)” and move the UpdatePetPosition in another server script just to keep things simple and tidy.

Just move the UpdatePetPostion() script under the CreatePet() so it won’t have a separate function for movement and make sure it’s under where the pet has been cloned where you create the variable for the cloned pet.

Also put “isPetEquipped” variable in the “PetReplicator:FireServer” brackets.

PetReplicator:FireServer(isPetEquipped)

local function CreatePet(player, isPetEquipped)

Remember to use your output to help and if that doesn’t then you can use print(“test”) to see where the problem occurs.