Spawn system issue

Hey everyone! So I am trying to make a custom spawn system, that sends players to clan location’s or exploration when players wondering around, and I have the rest of the code working, the only thing I cannot figure out is how to make it so that players can spawn on the spawn parts I have (Not spawn locations).

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local Modules = ServerStorage.Modules
local Managers = Modules.Managers
local DataManager = require(Managers.DataManager)
local PlayerData = ReplicatedStorage.PlayerData

-- Configuration
local SPAWN_BOX_SIZE = Vector3.new(30, 20, 30) -- Box dimensions (width, height, depth)
local SPAWN_FLOOR_SIZE = Vector3.new(32, 2, 32) -- Floor platform size

-- Reference to spawn points folder (must exist in workspace)
local SPAWN_POINTS_FOLDER = workspace:WaitForChild("ClanSpawn", math.huge) -- Adjust folder name as needed

-- Backup spawn position if clan spawn not found
local DEFAULT_SPAWN = Vector3.new(0, 50, 0)

-- CFrame serialization functions
local function serializeCFrame(cf)
	if cf and typeof(cf) == "CFrame" then
		return {cf:GetComponents()}
	end
	return nil
end

local function deserializeCFrame(data)
	if data and type(data) == "table" and #data == 12 then
		return CFrame.new(unpack(data))
	end
	return nil
end

-- Helper function to check if a CFrame is the default/empty CFrame
local function isDefaultCFrame(cf)
	if not cf or typeof(cf) ~= "CFrame" then
		return true
	end
	-- Check if it's the default CFrame (0, 0, 0) with no rotation
	local x, y, z = cf.Position.X, cf.Position.Y, cf.Position.Z
	-- Only consider it default if ALL coordinates are 0, not just X and Z
	return x == 0 and y == 0 and z == 0
end

-- Function to spawn player at custom box location
function spawnAtCustomBox(player)
	local character = player.Character or player.CharacterAdded:Wait()
	local humanoidRootPart = character:WaitForChild("HumanoidRootPart")

	-- Position player inside the box, slightly above the floor
	local spawnFloor = workspace:WaitForChild("ClanSpawn", math.huge):WaitForChild("SpawnFloor")
	local spawnPosition = spawnFloor.Position + Vector3.new(0, SPAWN_BOX_SIZE.Y/2 - 5, 0)
	humanoidRootPart.CFrame = CFrame.new(spawnPosition)

	print(player.Name .. " spawned at custom spawn box")
end

-- Function to get clan spawn position from parts in workspace
function getClanSpawnPosition(clanName)
	-- Try to find the spawn point part by clan name
	local spawnPart = SPAWN_POINTS_FOLDER:FindFirstChild(clanName)

	if spawnPart and spawnPart:IsA("BasePart") then
		-- Return position slightly above the part to prevent clipping
		return spawnPart.Position + Vector3.new(0, 5, 0)
	else
		-- Fallback to default spawn if clan part not found
		warn("Spawn point for clan '" .. clanName .. "' not found. Using default spawn.")
		return DEFAULT_SPAWN
	end
end

-- Function to spawn player at clan location
function spawnAtClanLocation(player)
	local character = player.Character
	if not character then return end

	local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
	if not humanoidRootPart then return end

	local PlayerFolder = PlayerData:WaitForChild(player.Name, math.huge)
	local SelectedSlot = PlayerFolder:WaitForChild("SelectedSlot", math.huge)
	local DataSlot = PlayerFolder:WaitForChild("Slot"..SelectedSlot.Value, math.huge)
	local Clan = DataSlot:WaitForChild("Clan", math.huge)

	local clan = "Lee" -- Default clan

	if Clan and Clan.Value ~= "" then
		clan = Clan.Value
	end

	-- Get spawn position for clan
	local spawnPosition = getClanSpawnPosition(clan)
	humanoidRootPart.CFrame = CFrame.new(spawnPosition)

	print(player.Name .. " spawned at " .. clan .. " clan location")
end

-- Function to check if player has a valid saved position
function hasValidSavedPosition(player)
	local PlayerFolder = PlayerData:WaitForChild(player.Name, math.huge)
	local SelectedSlot = PlayerFolder:WaitForChild("SelectedSlot", math.huge)
	local DataSlot = PlayerFolder:WaitForChild("Slot"..SelectedSlot.Value, math.huge)
	local LastCF = DataSlot:WaitForChild("LastCF", math.huge)

	-- Check if LastCF has a valid non-default value
	if LastCF and LastCF.Value and not isDefaultCFrame(LastCF.Value) then
		return true
	end

	-- Also check persistent data
	local profile = DataManager:RequestFullProfile(player)
	if profile and profile.Data and profile.Data.LastCF then
		local savedCFrame = deserializeCFrame(profile.Data.LastCF)
		if savedCFrame and not isDefaultCFrame(savedCFrame) then
			return true
		end
	end

	return false
end

-- Function to handle player spawning
function onPlayerSpawned(player)
	local PlayerFolder = PlayerData:WaitForChild(player.Name, math.huge)
	local SelectedSlot = PlayerFolder:WaitForChild("SelectedSlot", math.huge)
	local DataSlot = PlayerFolder:WaitForChild("Slot"..SelectedSlot.Value, math.huge)
	local CreatedCharacter = DataSlot:WaitForChild("CreatedCharacter", math.huge)

	if CreatedCharacter and CreatedCharacter.Value == false then
		-- First time spawn - send to custom spawn box
		spawnAtCustomBox(player)
	else
		-- Check if player has saved position data
		if hasValidSavedPosition(player) then
			-- Player has a saved position - spawn there
			spawnAtLastPosition(player)
		else
			-- Character already created but no saved position - send to clan spawn
			spawnAtClanLocation(player)
		end
	end
end

-- Function to spawn player at their last saved position
function spawnAtLastPosition(player)
	local PlayerFolder = PlayerData:WaitForChild(player.Name, math.huge)
	local SelectedSlot = PlayerFolder:WaitForChild("SelectedSlot", math.huge)
	local DataSlot = PlayerFolder:WaitForChild("Slot"..SelectedSlot.Value, math.huge)
	local LastCF = DataSlot:WaitForChild("LastCF", math.huge)

	local character = player.Character
	if not character then return end

	local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
	if not humanoidRootPart then return end

	local spawnCFrame = nil

	-- First try to get from persistent data
	local profile = DataManager:RequestFullProfile(player)
	if profile and profile.Data and profile.Data.LastCF then
		local savedCFrame = deserializeCFrame(profile.Data.LastCF)
		if savedCFrame and not isDefaultCFrame(savedCFrame) then
			spawnCFrame = savedCFrame
		end
	end

	-- Fall back to LastCF value if persistent data not available
	if not spawnCFrame and LastCF and LastCF.Value and not isDefaultCFrame(LastCF.Value) then
		spawnCFrame = LastCF.Value
	end

	if spawnCFrame then
		humanoidRootPart.CFrame = spawnCFrame
		print(player.Name .. " spawned at last saved position")
	else
		-- No valid saved position, fallback to clan spawn
		print(player.Name .. " has no valid saved position, spawning at clan location")
		spawnAtClanLocation(player)
	end
end

-- Function to save player's current position
function savePlayerPosition(player)
	local profile = DataManager:RequestFullProfile(player)

	local PlayerFolder = PlayerData:WaitForChild(player.Name, math.huge)
	if not PlayerFolder then return end

	local SelectedSlot = PlayerFolder:WaitForChild("SelectedSlot", math.huge)
	if not SelectedSlot then return end

	local DataSlot = PlayerFolder:WaitForChild("Slot"..SelectedSlot.Value, math.huge)
	if not DataSlot then return end

	local LastCF = DataSlot:WaitForChild("LastCF", math.huge)
	if not LastCF then return end

	local character = player.Character
	if not character then return end

	local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
	if not humanoidRootPart then return end

	-- Store the CFrame directly in the replicated storage for immediate access
	LastCF.Value = humanoidRootPart.CFrame

	-- Serialize the CFrame for persistent storage
	local serializedCFrame = serializeCFrame(humanoidRootPart.CFrame)
	if serializedCFrame then
		profile.Data["LastCF" .. SelectedSlot.Value] = serializedCFrame
		print("Saved exploration position for " .. player.Name .. " at " .. tostring(humanoidRootPart.Position))
	else
		warn("Failed to serialize CFrame for " .. player.Name)
	end
end

-- Main player connection
function onPlayerAdded(player)
	local PlayerFolder = PlayerData:WaitForChild(player.Name, math.huge)
	local SelectedSlot = PlayerFolder:WaitForChild("SelectedSlot", math.huge)
	local DataSlot = PlayerFolder:WaitForChild("Slot"..SelectedSlot.Value, math.huge)
	local CreatedCharacter = DataSlot:WaitForChild("CreatedCharacter", math.huge)

	CreatedCharacter.Changed:Connect(function()
		if CreatedCharacter.Value == true then
			task.wait(0.25)
			if player.Character then
				spawnAtClanLocation(player)
			end
		end
	end)

	-- Handle initial spawn
	player.CharacterAdded:Connect(function()
		task.wait(1) -- Small delay to ensure character is fully loaded
		onPlayerSpawned(player)
	end)
end

-- Connect events
Players.PlayerAdded:Connect(onPlayerAdded)

-- Handle players already in game
for _, player in pairs(Players:GetPlayers()) do
	onPlayerAdded(player)
end

-- Auto-save position every 10 seconds for all players
task.spawn(function()
	while true do
		task.wait(10) -- Reduced from 30 to 10 seconds for more frequent saves
		for _, player in pairs(Players:GetPlayers()) do
			if player.Character then
				savePlayerPosition(player)
			end
		end
	end
end)

-- Save player position when they leave the game
Players.PlayerRemoving:Connect(function(player)
	if player.Character then
		savePlayerPosition(player)
		print("Saved " .. player.Name .. "'s position on disconnect")
	end
end)

-- Save positions when server is shutting down
game:BindToClose(function()
	for _, player in pairs(Players:GetPlayers()) do
		if player.Character then
			savePlayerPosition(player)
		end
	end
	task.wait(10) -- Give time for data to save
end)

If anyone can help fix this issue let me know because its required for my game.

2 Likes

try player:LoadCharacter() then teleporting the player to the clan spawn with CFrame.

What you can do is listen for a CharacterAddded event, and connect it to a function that gets the proper spawn “location” and Teleports the player’s character there.

That will make it so whenever they spawn/respawn, they will get teleported there.

I had to redo the code to get it to work, and I forgot to respond that I found out.

1 Like

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