How can i save a map and folder called "Money" on vip server, and when the owner rejoins it reloads, for my incremental game

so, i was making a incremental game called “Slap Battles Incremental” and i need to save the current state of the map and the current state of workspace.Money (the map is workspace.Map)

local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local HttpService = game:GetService("HttpService")

local mapStore = DataStoreService:GetDataStore("MapSaveStore")
local moneyStore = DataStoreService:GetDataStore("MoneySaveStore")

local function serializeInstance(instance)
	local cloned = instance:Clone()
	cloned.Archivable = true
	return HttpService:JSONEncode(cloned:Clone():GetChildren())
end

local function deserializeInstance(jsonData, parent)
	local success, data = pcall(function()
		return HttpService:JSONDecode(jsonData)
	end)
	if success and typeof(data) == "table" then
		for _, childData in pairs(data) do
			local newObj = Instance.new(childData.ClassName)
			for prop, value in pairs(childData) do
				if prop ~= "ClassName" then
					pcall(function()
						newObj[prop] = value
					end)
				end
			end
			newObj.Parent = parent
		end
	end
end

local function saveData(userId)
	local success, err = pcall(function()
		local mapClone = workspace:FindFirstChild("Map"):Clone()
		local moneyClone = workspace:FindFirstChild("Money"):Clone()

		mapClone.Archivable = true
		moneyClone.Archivable = true

		local mapData = HttpService:JSONEncode(mapClone:Clone():GetChildren())
		local moneyData = HttpService:JSONEncode(moneyClone:Clone():GetChildren())

		mapStore:SetAsync("Map_" .. userId, mapData)
		moneyStore:SetAsync("Money_" .. userId, moneyData)
	end)

	if not success then
		warn("Failed to save data for user " .. userId .. ": " .. tostring(err))
	end
end

local function loadData(userId)
	local success, mapData = pcall(function()
		return mapStore:GetAsync("Map_" .. userId)
	end)

	if success and mapData then
		workspace.Map:ClearAllChildren()
		deserializeInstance(mapData, workspace.Map)
	end

	local success2, moneyData = pcall(function()
		return moneyStore:GetAsync("Money_" .. userId)
	end)

	if success2 and moneyData then
		workspace.Money:ClearAllChildren()
		deserializeInstance(moneyData, workspace.Money)
	end
end

-- Save when any player leaves
Players.PlayerRemoving:Connect(function(player)
	saveData(player.UserId)
end)

-- Load when any player joins
Players.PlayerAdded:Connect(function(player)
	wait(1) -- Give time for workspace to initialize
	loadData(player.UserId)
end)

if you want to take a look at the game here is a Link

1 Like

make a model and insert it through InsertService/Make a MainModule require.
MainModule is probably better cuz it will initiate everything only once

1 Like
local MainModule = {}

-- Services
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local HttpService = game:GetService("HttpService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- Constants
local SAVE_INTERVAL = 60 * 5 -- Auto-save every 5 minutes
local RETRY_DELAY = 5 -- Seconds to wait between retries
local MAX_RETRIES = 3 -- Maximum number of retry attempts

-- Data stores
local mapStore = DataStoreService:GetDataStore("MapSaveStore")
local moneyStore = DataStoreService:GetDataStore("MoneySaveStore")

-- Cache for player data to prevent frequent saves of unchanged data
local playerDataCache = {}

-- Serialization functions
function MainModule.serializeInstance(instance)
	local serializedChildren = {}

	for _, child in ipairs(instance:GetChildren()) do
		if child.Archivable then
			local childData = {
				ClassName = child.ClassName
			}

			-- Get all properties
			local success, properties = pcall(function()
				return child:GetAttributes()
			end)

			if success then
				for attrName, attrValue in pairs(properties) do
					childData[attrName] = attrValue
				end
			end

			-- Add specific important properties
			for _, prop in ipairs({"Name", "Position", "Size", "Color", "Value", "Transparency"}) do
				if pcall(function() return child[prop] end) then
					childData[prop] = child[prop]
				end
			end

			-- Handle children recursively if needed
			if #child:GetChildren() > 0 then
				childData.Children = MainModule.serializeInstance(child)
			end

			table.insert(serializedChildren, childData)
		end
	end

	return serializedChildren
end

function MainModule.deserializeInstance(data, parent)
	if not data then return end

	for _, childData in ipairs(data) do
		local newObj = Instance.new(childData.ClassName)
		newObj.Name = childData.Name or newObj.ClassName

		-- Set properties
		for prop, value in pairs(childData) do
			if prop ~= "ClassName" and prop ~= "Children" and prop ~= "Name" then
				pcall(function()
					newObj[prop] = value
				end)
			end
		end

		-- Set attributes
		for attr, value in pairs(childData) do
			if not newObj[attr] then -- Only set if not a built-in property
				pcall(function()
					newObj:SetAttribute(attr, value)
				end)
			end
		end

		newObj.Parent = parent

		-- Recursively create children
		if childData.Children then
			MainModule.deserializeInstance(childData.Children, newObj)
		end
	end
end

-- Data handling functions with retry logic
function MainModule.saveData(userId)
	if not workspace:FindFirstChild("Map") or not workspace:FindFirstChild("Money") then
		warn("Map or Money folder not found in workspace")
		return false
	end

	local currentData = {
		map = MainModule.serializeInstance(workspace.Map),
		money = MainModule.serializeInstance(workspace.Money)
	}

	-- Check if data has changed before saving
	if playerDataCache[userId] and HttpService:JSONEncode(playerDataCache[userId]) == HttpService:JSONEncode(currentData) then
		return true -- Data hasn't changed, no need to save
	end

	-- Data has changed, proceed with save
	for attempt = 1, MAX_RETRIES do
		local success, err = pcall(function()
			local mapData = HttpService:JSONEncode(currentData.map)
			local moneyData = HttpService:JSONEncode(currentData.money)

			mapStore:SetAsync("Map_" .. userId, mapData)
			moneyStore:SetAsync("Money_" .. userId, moneyData)

			-- Update cache on successful save
			playerDataCache[userId] = currentData
		end)

		if success then
			return true
		else
			warn(string.format("Save attempt %d/%d failed for user %d: %s", attempt, MAX_RETRIES, userId, tostring(err)))
			if attempt < MAX_RETRIES then
				wait(RETRY_DELAY)
			end
		end
	end

	return false
end

function MainModule.loadData(userId)
	if not workspace:FindFirstChild("Map") or not workspace:FindFirstChild("Money") then
		warn("Map or Money folder not found in workspace")
		return false
	end

	local loadedSuccessfully = false

	-- Load map data
	for attempt = 1, MAX_RETRIES do
		local success, mapData = pcall(function()
			return mapStore:GetAsync("Map_" .. userId)
		end)

		if success and mapData then
			workspace.Map:ClearAllChildren()
			local decodedData = HttpService:JSONDecode(mapData)
			MainModule.deserializeInstance(decodedData, workspace.Map)
			loadedSuccessfully = true
			break
		elseif attempt == MAX_RETRIES then
			warn("Failed to load map data for user " .. userId)
		else
			wait(RETRY_DELAY)
		end
	end

	-- Load money data
	for attempt = 1, MAX_RETRIES do
		local success, moneyData = pcall(function()
			return moneyStore:GetAsync("Money_" .. userId)
		end)

		if success and moneyData then
			workspace.Money:ClearAllChildren()
			local decodedData = HttpService:JSONDecode(moneyData)
			MainModule.deserializeInstance(decodedData, workspace.Money)
			loadedSuccessfully = true
			break
		elseif attempt == MAX_RETRIES then
			warn("Failed to load money data for user " .. userId)
		else
			wait(RETRY_DELAY)
		end
	end

	-- Cache the loaded data
	if loadedSuccessfully then
		playerDataCache[userId] = {
			map = MainModule.serializeInstance(workspace.Map),
			money = MainModule.serializeInstance(workspace.Money)
		}
	end

	return loadedSuccessfully
end

-- Player event handlers
function MainModule.setupPlayer(player)
	-- Wait for workspace to initialize
	local startTime = os.time()
	while not workspace:FindFirstChild("Map") or not workspace:FindFirstChild("Money") do
		if os.time() - startTime > 10 then
			warn("Timed out waiting for Map/Money folders to appear for player " .. player.Name)
			return
		end
		wait(1)
	end

	-- Load player data
	MainModule.loadData(player.UserId)

	-- Set up auto-save
	local autoSaveConnection
	autoSaveConnection = game:GetService("RunService").Heartbeat:Connect(function()
		if os.time() % SAVE_INTERVAL == 0 then
			MainModule.saveData(player.UserId)
		end
	end)

	-- Clean up on player leaving
	player:GetPropertyChangedSignal("Parent"):Connect(function()
		if not player.Parent then
			autoSaveConnection:Disconnect()
			MainModule.saveData(player.UserId)
			playerDataCache[userId] = nil
		end
	end)
end

-- Initialize the module
function MainModule.init()
	-- Set up existing players
	for _, player in ipairs(Players:GetPlayers()) do
		coroutine.wrap(MainModule.setupPlayer)(player)
	end

	-- Set up future players
	Players.PlayerAdded:Connect(function(player)
		MainModule.setupPlayer(player)
	end)

	-- Global save function that can be called from elsewhere
	ReplicatedStorage:WaitForChild("SaveAllData", 10).OnServerEvent:Connect(function()
		for _, player in ipairs(Players:GetPlayers()) do
			MainModule.saveData(player.UserId)
		end
	end)

	-- Handle game closing
	game:BindToClose(function()
		for _, player in ipairs(Players:GetPlayers()) do
			MainModule.saveData(player.UserId)
		end
	end)
end

return MainModule

this is what i did, but the map dissapeared