Saving Beams Created By Player

Hello,
So, I basically know how to serialize objects, but this time, I wanted to save an object which is not found in the replicated storage, it is just created using Ray.new and other stuff.

I tried looking for tutorials and I found nothing, so any help?

Local Script:

local UIS = game:GetService("UserInputService")
local RunService = game:GetService("RunService")

local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Mouse = Player:GetMouse()

local Counter = 1
local Grid = 4
local Toggle = false

local PointA = script.Point:Clone()
local PointB = script.Point:Clone()

local PosA
local PosB
local LRA

function Round(Num, To)
	return math.floor(Num / To + 0.5) * To
end

RunService.RenderStepped:Connect(function()
	if Mouse.Target then
		if Mouse.Target.Name == Mouse.Target.Name ~= "Baseplate" and Mouse.Target.Name ~= "LargePlotBanBox" then
			if Toggle then
				if Counter == 1 then
					PointA.Parent = workspace
					PosA = Vector3.new(Round(Mouse.Hit.p.X, Grid), 0, Round(Mouse.Hit.p.Z, Grid))
					PointA.CFrame = CFrame.new(PosA) + Vector3.new(0,4,0)
					PointA.Orientation = Vector3.new(0, 90, -90)
				elseif Counter == 2 then
					PointB.Parent = workspace
					PosB = Vector3.new(Round(Mouse.Hit.p.X, Grid), 0, Round(Mouse.Hit.p.Z, Grid))
					PointB.CFrame = CFrame.new(PosB) + Vector3.new(0,4,0)
					PointB.Orientation = Vector3.new(0, 90, -90)

					if LRA then
						LRA:Destroy()
						LRA = nil
					end

					local Mag = (PosA - PosB).magnitude

					local ray = Ray.new(PosA, (PosA - PosB).unit * Mag)
					local part, position = workspace:FindPartOnRay(ray, workspace.BuildingPlace, false, false)

					local beam = Instance.new("Part", workspace)
					beam.Name = "WallPreview"
					beam.BrickColor = BrickColor.new("Shamrock")
					beam.FormFactor = "Custom"
					beam.Material = "Neon"
					beam.Transparency = 0.25
					beam.Anchored = true
					beam.Locked = true
					beam.CanCollide = false

					local distance = (PosA - position).magnitude
					beam.Size = Vector3.new(0.3, 10, distance)
					beam.CFrame = CFrame.new(PosA, position) * CFrame.new(0, 0, distance / 2) + Vector3.new(0,4,0)
					LRA = beam
				end
			end
		end
	end
end)

Mouse.Button1Down:Connect(function()
	if Toggle then
		if Counter == 1 then
			Counter = 2
		elseif Counter == 2 then
			Counter = 1
			game.ReplicatedStorage.Events.GenerateObjectsEvents.generateWall:FireServer(PosA, PosB, game.ReplicatedStorage.PlotName.Value)		
			LRA:Destroy()
			PointA:Destroy()
			PointB:Destroy()
			PointA = script.Point:Clone()
			PointB = script.Point:Clone()
		end
	end
end)

script.Parent.MouseButton1Click:Connect(function()
	Toggle = true
	script.Parent.Parent.Parent.Parent.BuildButtons:TweenPosition(UDim2.new(0, 0,1, 0), "In", "Sine", 0.3)
	script.Parent.Parent.Parent.Parent.OBJButtons:TweenPosition(UDim2.new(0, 0,0.838, 0), "In", "Sine", 0.3)
	script.Parent.Parent.Parent.Parent.ObjHelp.Visible = true
	script.Parent.Parent.Parent.Parent.Collisions.Visible = true
	script.Parent.Parent.Parent.Parent.PaintButton.Visible = false
	script.Parent.Parent.Parent.Parent.DeleteButton.Visible = false
	script.Parent.Parent.Parent.Parent.ShowOBJMoney.Visible = true
end)

UIS.InputBegan:Connect(function(Input)
	if Input.KeyCode == Enum.KeyCode.X and Toggle then
		script.Parent.Parent.Parent.Parent.BuildButtons:TweenPosition(UDim2.new(0, 0,0.838, 0), "In", "Sine", 0.3)
		script.Parent.Parent.Parent.Parent.OBJButtons:TweenPosition(UDim2.new(0, 0,1, 0), "In", "Sine", 0.3)
		script.Parent.Parent.Parent.Parent.ObjHelp.Visible = false
		script.Parent.Parent.Parent.Parent.Collisions.Visible = false
		script.Parent.Parent.Parent.Parent.PaintButton.Visible = true
		script.Parent.Parent.Parent.Parent.DeleteButton.Visible = true
		script.Parent.Parent.Parent.Parent.ShowOBJMoney.Visible = false
		if LRA then
			LRA:Destroy()
		end
		PointA:Destroy()
		PointB:Destroy()
		PointA = script.Point:Clone()
		PointB = script.Point:Clone()
		Toggle = false
		Counter = 1

	end
end)

Script:

game.ReplicatedStorage.Events.GenerateObjectsEvents.generateWall.OnServerEvent:Connect(function(player, PosA, PosB, plot)
	local Mag = (PosA - PosB).magnitude

	local ray = Ray.new(PosA, (PosA - PosB).unit * Mag)
	local part, position = workspace:FindPartOnRay(ray, workspace.BuildingPlace, false, false)

	local beam = Instance.new("Part", plot.placedObjects)
	beam.Name = "RegularWall"
	beam.BrickColor = BrickColor.new("Medium stone grey")
	beam.FormFactor = "Custom"
	beam.Material = Enum.Material.SmoothPlastic
	beam.Transparency = 0
	beam.Anchored = true
	beam.Locked = false
	beam.CanCollide = true

	local distance = (PosA - position).magnitude
	beam.Size = Vector3.new(0.3, 10, distance) + Vector3.new(0,0,.25)
	beam.CFrame = CFrame.new(PosA, position) * CFrame.new(0, 0, distance / 2) + Vector3.new(0,4,0)
end)

Serializing script:

-- 1. Data Save
-- 1.1 Get Plot

local players = game:GetService("Players")
local replicatedStorage = game:GetService("ReplicatedStorage")
local argument = "Data"
local argument2 = "ReadData"

local plotHandler = require(script.Parent.Parent.ServerModules.PlotHandler)
local remoteRF = replicatedStorage.Events.requestPlot

local function assignPlot(plr)
	plotHandler.assignPlot(workspace.Plots, plr)
end

local function requestPlot(plr)
	return plotHandler.returnPlot(workspace.Plots, plr)
end

remoteRF.OnServerInvoke = requestPlot
players.PlayerAdded:Connect(assignPlot)

-- 1.2 DataStore
local dataStoreService = game:GetService("DataStoreService")
local dataStore = dataStoreService:GetDataStore("DataSave1")

local tries = 3
local dataloaded = nil

local function serialize(plr)
	if dataloaded then
		local plot = plotHandler.returnPlot(workspace.Plots, plr)

		local key = plr.UserId
		local count = 0

		local data = {}

		for i, obj in ipairs(plot.placedObjects:GetChildren()) do
			table.insert(data, {
				["name"] = obj.Name,
				["transform"] = {
					["x"] = obj.PrimaryPart.CFrame.X;
					["y"] = obj.PrimaryPart.CFrame.Y;
					["z"] = obj.PrimaryPart.CFrame.Z;
					["r"] = obj.PrimaryPart.Orientation.Y;
					["r2"] = obj.PrimaryPart.Orientation.X;
					["paintableR"] = obj:WaitForChild("Paintable1").Color.R;
					["paintableG"] = obj:WaitForChild("Paintable1").Color.G;
					["paintableB"] = obj:WaitForChild("Paintable1").Color.B
				}
			})
		end

		local success, err

		repeat
			success, err = pcall(function()
				dataStore:SetAsync(key, data)
			end)

			count = count + 1
		until count >= tries or success

		if not success then
			warn("Data could not be set." .. tostring(err))

			return
		end
	else
		game.ReplicatedStorage.Events.DataGUI:FireClient(plr, argument)

		return
	end
end

local function deserialize(plr)
	local plot = plotHandler.returnPlot(workspace.Plots, plr)

	local key = plr.UserId
	local count = 0

	local data

	local success, err

	repeat
		success, err = pcall(function()
			data = dataStore:GetAsync(key)
		end)

		count = count + 1
	until count >= tries or success

	if not success then
		warn("Failed to read data." .. tostring(err))
		game.ReplicatedStorage.Events.DataGUI:FireClient(plr, argument2)
		wait(17)
		plr:Kick("Failed to read data. Please rejoin the game.")

		return
	end

	if data then
		local plot = plotHandler.returnPlot(workspace.Plots, plr)

		for i, saved in ipairs(data) do
			local loadedModel = replicatedStorage.Objects:FindFirstChild(saved.name):Clone()
			
			if loadedModel then
				loadedModel:SetPrimaryPartCFrame(CFrame.new(saved.transform.x, saved.transform.y, saved.transform.z)*CFrame.Angles(0, math.rad(saved.transform.r), 0))
				loadedModel.Paintable1.BrickColor = BrickColor.new(saved.transform.paintableR, saved.transform.paintableG, saved.transform.paintableB)
				loadedModel.Parent = plot.placedObjects
			else
				return
			end
		end

		dataloaded = true
	else
		dataloaded = true
	end
end

-- 1.3 Remove objects and set values to nil

local function unloadTycoonData(plr)
	local plot = plotHandler.returnPlot(workspace.Plots, plr)

	serialize(plr)

	for _, obj in ipairs(plot.placedObjects:GetChildren()) do
		obj:Destroy()
	end
	
	plot.Owner.Value = nil
	plot.OwnerName.Value = ""
	plot.Plot.Size = Vector3.new(450,0.1,450)
	for i, parts in pairs(plot.Banbox:GetChildren()) do
		if parts:IsA("BasePart") then
			parts.Transparency = 0
			parts.CanCollide = true
		end
	end
	
	-- Remove banboxes
	
	for i, parts in pairs(plot.LargePlotBanbox:GetChildren()) do
		if parts:IsA("BasePart") then
			parts.Transparency = 1
			parts.CanCollide = false
		end
	end
	
	for i, parts in pairs(plot.Banbox:GetChildren()) do
		if parts:IsA("BasePart") then
			parts.Transparency = 1
			parts.CanCollide = false
		end
	end
end

-- 1.4 Connect functions

-- On player added, load data and assign player's plot.
players.PlayerAdded:Connect(deserialize)

-- Player removing, save data.
players.PlayerRemoving:Connect(unloadTycoonData)

-- When servers are shut down.
game:BindToClose(function()
	for i, plr in ipairs(players:GetPlayers()) do
		serialize(plr)
	end
end)

Thank you.

1 Like

My approach would be to store the data of each beam inside a folder inside the player, let’s say called ‘Beams’, and keep the CFrame components and other properties that you would like you be saved under some values, and once the player leaves, just add each beam with it’s data individually to a table that contains all of the beams, and save that table. And lastly just load the beams inside the table when the player joins.

1 Like

Hm. What should I edit on my serializing script?

1 Like

@vyexon Instead of having tons of instances I would say use attributes

1 Like

I know. But I like using instances.

(Gonna take your advice anyways)

1 Like

Well if you are going to have tons of instances that may impact performances

2 Likes

@ComplexMetatable How can I do that?

1 Like