In game user id system?

Hello. How could I make a user id system? This is what I mean: When a player joins, a id gets added to their data. The first player to join has a user id of 1. The second, 2, the third, 3, etc. And their user id never changes. How could I make something like that?
Current code (UserId prints nil):

local DSS = game:GetService("DataStoreService")
local DS = DSS:GetDataStore("ArksieData")

local LOADED_PLAYERS = {}

local function GenerateDataKey(Player)
	local Key = "UID_" .. Player.UserId
	return Key
end 

game.Players.PlayerAdded:Connect(function(player)
	
	local ls = Instance.new("Folder")
	ls.Name = "leaderstats"
	ls.Parent = player

	local bits = Instance.new("IntValue")
	bits.Name = "Bits"
	bits.Value = 10 --starter amt
	bits.Parent = ls
	
	local UserId = Instance.new("IntValue")
	UserId.Name = "UserId"
	UserId.Parent = ls

	local key = GenerateDataKey(player)
	local data = nil
	local Id = 0
	
	local success,err = pcall(function()
		data = DS:GetAsync(key)
		Id = DS:GetAsync("Id")
	end)
	if not success then
		player:Kick("Couldn't fetch data!\n\nError:\n"..err)
	else
		if data.UserId ~= nil then
			UserId.Value = data.UserId
		else
			if Id ~= nil then
				Id += 1
				DS:SetAsync("Id", Id)
				UserId.Value = Id
			else
				Id = 1
				DS:SetAsync("Id", Id)
				UserId.Value = Id
			end
		end
	end

	if data then
		print(data.Bits, data.UserId)
		bits.Value = data.Bits
		UserId.Value = data.UserId
	else
		print('no data')
	end
	LOADED_PLAYERS[player] = true
end)

game.Players.PlayerRemoving:Connect(function(player)
	local Id = player.leaderstats.UserId.Value
	
	if not LOADED_PLAYERS[player] then return end;
	LOADED_PLAYERS[player] = nil

	local key = GenerateDataKey(player)
	local data = {
		Bits = player.leaderstats.Bits.Value,
		UserId = player.leaderstats.UserId.Value,
	}
	local success, err = pcall(function()
		DS:SetAsync(key, data)
	end)
	if not success then
		warn(err)
	end
end)

game:BindToClose(function()
	local leftToSave = 0
	for player in pairs(LOADED_PLAYERS) do
		LOADED_PLAYERS[player] = nil
		leftToSave += 1

		coroutine.wrap(function()
			local data = {
				Bits = player.leaderstats.Bits.Value,
				UserId = player.leaderstats.UserId.Value,
			}

			local success, errorMessage = pcall(function()
				DS:SetAsync(GenerateDataKey(player), data)
			end)
			leftToSave -= 1
		end)()
	end
	local RunService = game:GetService("RunService")
	while leftToSave > 0 do
		RunService.Heartbeat:Wait()
	end
end)

This is for Arksie :eyes:, here

You could use UserId, all players have their own identification number.

But I don’t to use userid. I want to make my own userid system.

Oh, you can use OrderedDataStore, first see if they have a number assigned and, if not, you give it one and save it with the player id, but this can be inconvenient in games with many players and / or servers.

2 Likes

Do you want the Id to be unique to the server or all servers of the game?

I’d like for the id to be unique for all servers, and I want it to save. I think OrderedDataStore is my answer, but I’m still reading through it.

I don’t even think you need an ordered Data store

Just keep a "NextId” key around.

When a new player joins,

  • check if they have an Id
  • if not, use UpdateAsync to get the next ID and use SetAsync to map the userId to the NextId, then increment the NextId

How could I do that? Sorry, I don’t normally use UpdateAsync.

Hey there! Do you mind telling me if this would save even if they leave? so they’d be the first ever player to join, and that would save?

Yes, I want it to save when they leave.

Try this:

local DS = game:GetService("DataStoreService")
local UserIdSystem = DS:GetDataStore("UserId")

game.Players.PlayerAdded:Connect(function(Player)
	local CustomId = Instance.new("IntValue")
	CustomId.Name = "CustomId"
	CustomId.Parent = Player

	local Id

	local success, errormessage = pcall(function()
		Id = UserIdSystem:GetAsync("Id")
		PlayerId = UserIdSystem:GetAsync(Player.UserId)
	end)

	if success then
		if PlayerId ~= nil then
			CustomId.Value = PlayerId
		else
			if Id ~= nil then
				Id += 1
				UserIdSystem:SetAsync("Id", Id)
				CustomId.Value = Id
			else
				Id = 1
				UserIdSystem:SetAsync("Id", Id)
				CustomId.Value = Id
			end
		end
	else
		print("There was an error loading")
	end
end)

game.Players.PlayerRemoving:Connect(function(Player)
	local Id = Player.CustomId.Value

	local success, errormessage = pcall(function()
		UserIdSystem:SetAsync(Player.UserId, Id)
	end)

	if success then
		print("Saved id for "..Player.DisplayName)
	else
		print("There was an error loading")
	end
end)

To access the Id, use Player.CustomId.Value.

EDIT: had to fix some things

1 Like

Pseudo code without error handling or anything but just the general idea:

local function Joined(player)
    local hasId = IDs:GetAsync(player.UserId)
    
    if not hasId then
        local setTo
        IDs:UpdateAsync("NextId",
            function(nextId)
                setTo = nextId
                return nextId + 1
            end)
        -- fine to split up Get/Set
        -- like this bc the same
        -- player won't be in two
        -- servers at once
        IDs:SetAsync(player.UserId, setTo)
    end
end

This is nice, I would just be careful because two servers could call GetAsync at the same time, get the same value, and assign two players the same ID. That’s what UpdateAsync is useful for :slight_smile:

I don’t even know how to use UpdateAsync.

So I should use OrderedDataStore and UpdateAsync? Could you provide code? I don’t use either of those. :laughing:

I provided code: In game user id system? - #13 by nicemike40

I used your code and edited it to fit my code, but it isn’t working. UserId is nil. Here’s the full code:

local DSS = game:GetService("DataStoreService")
local DS = DSS:GetDataStore("ArksieData")

local LOADED_PLAYERS = {}

local function GenerateDataKey(Player)
	local Key = "UID_" .. Player.UserId
	return Key
end 

game.Players.PlayerAdded:Connect(function(player)
	
	local ls = Instance.new("Folder")
	ls.Name = "leaderstats"
	ls.Parent = player

	local bits = Instance.new("IntValue")
	bits.Name = "Bits"
	bits.Value = 10 --starter amt
	bits.Parent = ls
	
	local UserId = Instance.new("IntValue")
	UserId.Name = "UserId"
	UserId.Parent = ls

	local key = GenerateDataKey(player)
	local data = nil
	local Id
	
	local success,err = pcall(function()
		data = DS:GetAsync(key)
		Id = DS:GetAsync("Id")
	end)
	if not success then
		player:Kick("Couldn't fetch data!\n\nError:\n"..err)
	else
		if data.UserId ~= nil then
			UserId.Value = data.UserId
		else
			if Id ~= nil then
				Id += 1
				DS:SetAsync("Id", Id)
				UserId.Value = Id
			else
				Id = 1
				DS:SetAsync("Id", Id)
				UserId.Value = Id
			end
		end
	end

	if data then
		print(data.Bits, data.UserId)
		bits.Value = data.Bits
		UserId.Value = data.UserId
	else
		print('no data')
	end
	LOADED_PLAYERS[player] = true
end)

game.Players.PlayerRemoving:Connect(function(player)
	local Id = player.leaderstats.UserId.Value
	
	if not LOADED_PLAYERS[player] then return end;
	LOADED_PLAYERS[player] = nil

	local key = GenerateDataKey(player)
	local data = {
		Bits = player.leaderstats.Bits.Value,
		UserId = player.leaderstats.UserId.Value,
	}
	local success, err = pcall(function()
		DS:SetAsync(key, data)
	end)
	if not success then
		warn(err)
	end
end)

game:BindToClose(function()
	local leftToSave = 0
	for player in pairs(LOADED_PLAYERS) do
		LOADED_PLAYERS[player] = nil
		leftToSave += 1

		coroutine.wrap(function()
			local data = {
				Bits = player.leaderstats.Bits.Value,
				UserId = player.leaderstats.UserId.Value,
			}

			local success, errorMessage = pcall(function()
				DS:SetAsync(GenerateDataKey(player), data)
			end)
			leftToSave -= 1
		end)()
	end
	local RunService = game:GetService("RunService")
	while leftToSave > 0 do
		RunService.Heartbeat:Wait()
	end
end)