Help with plot saving/loading system

What it does in my game is that it saves the plot cframe (x, y, z and y rotation) and ToObjectSpace and ToWorldSpace aren’t use during saving. When the game loads a save it creates the cframe from the plot values that were saved. Then the game takes the plot cframe that was saved (not the current one) and does CurrentSurface = SavedPlot:ToObjectSpace(ItemCFrame) after this it uses ToWorldSpace and does FinalCFrame = CurrentPlot:ToWorldSpace(CurrentSurface) (CurrentPlot is the on the player selected after loading not the one that was saved)

Here is an image of the loading code (the cam variables come from the saved data for the previous plot position):

How do you save your plot? Make sure you’re converting it into object space when saving (if you don’t convert it to object space while saving, you’d get something similar to how your plot loaded). This is because you’re saving the plot relative to world space, and you’re loading the saved Vectors relative to object space, which produces a different result.

It does get converted to object space if you look near the bottom of the script in the function called game.ReplicatedStorage.Remotes.PlaceObjectServer.OnServerEvent:Connect(function(player)
in the first script i ever posted on this thread

Also what does ItemCFrameRepresent?

It’s quite unclear as to what that snippet of code even does. “PlaceObjectServer” doesn’t sound like something you would be saving, rather and event to place different objects down in the plot.

The cframe of the item that it’s loading in. ItemCframe is not in object space. My saving and loading doesn’t convert the cframe to object space during saving as it’s not necessary if the cframe of the plot was saved too.

Thats because It places the object and saves the data after being placed. If I try to save my data when the player leaves the game it doesnt work

So would I use the CFrame of the model in ReplicatedStorage?

No you need to save the world position of the item.

This is my code so far:

for index, plotObj in pairs(profile.Data.PlotData) do
		local model = game.ReplicatedStorage.Models:FindFirstChild(plotObj.Object):Clone()
		
		local BaseCFrame = CFrame.new(plotObj.Location.x, plotObj.Location.y, plotObj.Location.z) * CFrame.Angles(math.rad(0), math.rad(plotObj.Location.r), math.rad(0))
		local CurrentSurface = BaseCFrame:ToObjectSpace()
		model:SetPrimaryPartCFrame(locpos)
		model.Parent = plot.ItemHolder
		model:SetAttribute("Identifier", index)
	end

I am a little confused on what I would set CurrentSurface to

You’re not providing what status means. This is why I asked the question to show how you saved your code and if you converted it to object space. I believe you haven’t provided any of that information anywhere in this thread. If you can show us the snippet of code that sends status over to the server, it might be a bit easier to debug. Currently, status doesn’t give us any information.

Set it to the locpos variable that you had in the original script. BaseCFrame:ToObjectSpace(locpos)

1 Like
game.ReplicatedStorage.Remotes.SetPlayerStatus.OnServerEvent:Connect(function(plr, status, ...)
	local args = {...}
	PlayerData:AddOrSetNumber(plr, status, "PlayerStatus")
	if args[1] ~= nil then
		plr.playerdata.ObjectBeingPlaced.Value = args[1]
	end
end)
local PlayerData = {}

local function GetOrCreateDataHolder(plr)
	if not plr:FindFirstChild("playerdata") then
		local pdata = Instance.new("Folder")
		pdata.Name = "playerdata"
		pdata.Parent = plr
	end
	return plr:FindFirstChild("playerdata")
end

local function Add(datatype, plr, value, name)
	if not GetOrCreateDataHolder(plr):FindFirstChild(name) then
		local i = Instance.new(datatype)
		i.Value = value
		i.Name = name
		i.Parent = GetOrCreateDataHolder(plr)
	else
		local d = GetOrCreateDataHolder(plr):FindFirstChild(name)
		d.Value = value
	end
end

function PlayerData:AddOrSetString(plr, value, name)
	Add("StringValue", plr, value, name)
end

function PlayerData:AddOrSetNumber(plr, value, name)
	Add("NumberValue", plr, value, name)
end

function PlayerData:AddOrSetBool(plr, value, name)
	Add("BoolValue", plr, value, name)
end

function PlayerData:GetData(plr, name)
	if not GetOrCreateDataHolder(plr):FindFirstChild(name) then
		return false
	end
	return GetOrCreateDataHolder(plr):FindFirstChild(name)
end

function PlayerData:RemoveData(plr, name)
	if not GetOrCreateDataHolder(plr):FindFirstChild(name) then
		return false
	end
	GetOrCreateDataHolder(plr):FindFirstChild(name):Destroy()
	return true
end

return PlayerData
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local player = Players.LocalPlayer
local mouse = player:GetMouse()

local remote = ReplicatedStorage.Remotes:WaitForChild("RequestPlacement")
local button = script.Parent

local PlacementModule = require(ReplicatedStorage.Modules:WaitForChild("PlacementModuleV3"))

local isDeleting = false

local deleteHL = Instance.new("SelectionBox")
deleteHL.Color3 = Color3.fromRGB(250, 46, 46)
deleteHL.Transparency = 0.5
deleteHL.LineThickness = 0.1

coroutine.wrap(function()
	while true do
		if isDeleting then
			if mouse.Target ~= nil then
				if mouse.Target.Parent.Parent.Parent ~= nil then
					if mouse.Target.Name == "Hitbox" and mouse.Target.Parent.Parent.Parent == workspace.Plots:FindFirstChild(player.playerdata.Plot.Value) then
						deleteHL.Adornee = mouse.Target
						deleteHL.Parent = mouse.Target
						deleteHL.Visible = true
					else
						deleteHL.Adornee = nil
						deleteHL.Parent = nil
						deleteHL.Visible = false
					end
				end
			end
		end
		task.wait()
	end
end)()


local placementInfo = PlacementModule.new(
	2,
	ReplicatedStorage.Models,
	Enum.KeyCode.R, Enum.KeyCode.X, Enum.KeyCode.U, Enum.KeyCode.L,
	Enum.KeyCode.ButtonR1, Enum.KeyCode.ButtonX, Enum.KeyCode.DPadUp, Enum.KeyCode.DPadDown
)

local function requestDeletion()
	if isDeleting then
		local obj = mouse.Target

		if obj then
			if obj.Parent.Parent.Parent == workspace.Plots:FindFirstChild(player.playerdata.Plot.Value) then
				game.ReplicatedStorage.Remotes.DeleteItem:FireServer(obj)
			end
		end
	end
end

button.MouseButton1Click:Connect(function()
	if player.playerdata.ObjectBeingPlaced.Value == "None" then
		if player.playerdata.PlayerStatus.Value == 1 then
			if player.playerdata:FindFirstChild(script.Parent.TextLabel.Text).Value > 0 then
				local plot = workspace.Plots:FindFirstChild(player.playerdata.Plot.Value)
				placementInfo:activate(script.Parent.TextLabel.Text, plot.ItemHolder, plot, false, false, false)
				game.ReplicatedStorage.Remotes.SetPlayerStatus:FireServer(2, script.Parent.TextLabel.Text)
			end
		end
	end
end)

mouse.Button1Down:Connect(function()
	if player.playerdata.ObjectBeingPlaced.Value == script.Parent.TextLabel.Text then
		if player.playerdata.PlayerStatus ~= nil then
			if player.playerdata.PlayerStatus.Value == 2 and not isDeleting then
				if player.playerdata:FindFirstChild(script.Parent.TextLabel.Text) then
					if player.playerdata:FindFirstChild(script.Parent.TextLabel.Text).Value > 0 then
						placementInfo:requestPlacement(remote)
						game.ReplicatedStorage.Remotes.PlaceObjectServer:FireServer()
					end
				end
			end
			if player.playerdata.PlayerStatus.Value == 3 and isDeleting then
				requestDeletion()
			end
		end
	end
end)

game.ReplicatedStorage.Bindables.SyncBulldoze.Event:Connect(function(bl)
	if game.Players.LocalPlayer.playerdata.ObjectBeingPlaced.Value == script.Parent.TextLabel.Text then
		isDeleting = bl
	end
end)

status Lets me know if the player is not placing or destroying anything (if the value == 1), placing an object (if the value == 2), or destroying an object (if the value == 3)


Everything just gets set over there

for index, plotObj in pairs(profile.Data.PlotData) do
		local model = game.ReplicatedStorage.Models:FindFirstChild(plotObj.Object):Clone()
		
		local locpos = CFrame.new(plotObj.Location.x, plotObj.Location.y, plotObj.Location.z) * CFrame.Angles(0, math.rad(plotObj.Location.r), 0):ToObjectSpace(workspace.Plots:FindFirstChild(plr.playerdata.Plot.Value).CFrame)
		local BaseCFrame = CFrame.new(plotObj.Location.x, plotObj.Location.y, plotObj.Location.z) * CFrame.Angles(math.rad(0), math.rad(plotObj.Location.r), math.rad(0))
		local CurrentSurface = BaseCFrame:ToObjectSpace(locpos)
		model:SetPrimaryPartCFrame(workspace.Plots:FindFirstChild(plr.playerdata.Plot.Value).CFrame:ToWorldSpace(CurrentSurface))
		model.Parent = plot.ItemHolder
		model:SetAttribute("Identifier", index)
	end

I probably made a mistake in this part of the script

locpos should not be in object space.

image
also locpos is basically identical to BaseCFrame

1 Like

BaseCFrame should be the cframe that the plot was at during saving. locpos should be the cframe the item was at during saving. Don’t use ToObjectSpace during saving if you are.

I’m still confused about where you converted anything into object space in both the snippets provided here. I don’t think you provided the third argument for PlaceObjectServer:FireServer (...) anywhere in the code. I couldn’t even find keywords such as Position, CFrame, or ToObjectSpace in the snippet provided. If you can elaborate on what you’re doing, it would make a lot more sense.


If they have multiple plots in their game, then this would be necessary as you’d have to transform each location of each object relative to each plot, not relative to the world.

game.ReplicatedStorage.Remotes.PlaceObjectServer.OnServerEvent:Connect(function(player)
	local profile = Profiles[player]
	if profile ~= nil then
		profile.Data.PlotData = {}
		for index, p in pairs(workspace.Plots:FindFirstChild(player.playerdata.Plot.Value).ItemHolder:GetChildren()) do
			profile.Data.PlotData[#profile.Data.PlotData + 1] = {
				Object = p.Name,
				Location = {
					x = p.PrimaryPart.CFrame:ToObjectSpace(workspace.Plots:FindFirstChild(player.playerdata.Plot.Value).CFrame).X,
					y = p.PrimaryPart.CFrame:ToObjectSpace(workspace.Plots:FindFirstChild(player.playerdata.Plot.Value).CFrame).Y,
					z = p.PrimaryPart.CFrame:ToObjectSpace(workspace.Plots:FindFirstChild(player.playerdata.Plot.Value).CFrame).Z,
					r = p.PrimaryPart.Orientation.Y
				}
			}
			p:SetAttribute("Identifier", index)
			if game.ReplicatedStorage.Models:FindFirstChild(p.Name) then
				profile.Data.Items[p.Name] = {
					ObjectName = p.Name,
					Count = player.playerdata:FindFirstChild(p.Name).Value
				}
			end
		end
		print(profile.Data)
	end
end)

This is the code from the very first script I posted on this which converted to object space originally.
The third argument you are referring to in SetPlayerStatus represents the value if you optionally want to change the current object the player is placing. The reason I didn’t elaborate on this is because it doesn’t have remotely anything to do with the data loading and saving. It doesn’t affect it at all, its just used for some GUI elements and such to tell you to Press X to stop building and to make sure you don’t place multiple objects and glitch out the placement

In my game I don’t use ToObjectSpace during saving and I have multiple plots with different rotations.