Help with knit (Data not loading in time)

Hey! I’m having a problem in knit and not sure how to really approach this. So let me break it down as best I can. So I am using ProfileService to save/manage data and with that, it should be relatively easy to send data to the client/server. Now for the past day, I have been trying to figure out how the client and server can access the data. So let me show you the scripts and then explain more on what I have done.

Server Script (DataService):

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

-- Modules
local Knit = require(ReplicatedStorage:WaitForChild("Common"):WaitForChild("Knit"))
local UserData = require(script.Parent.Parent:WaitForChild("Module"):WaitForChild("DataTable"))
local ProfileService = require(ReplicatedStorage:WaitForChild("Common"):WaitForChild("ProfileService"))

local DataService = Knit.CreateService{
    Name = "DataService",
    Client = {},
}

local ProfileStore = ProfileService.GetProfileStore(
    "PlayerDataTest",
    UserData
)

local Profiles = {}

function DataService:GetData(player)
    if not player then return end
    print(Profiles)
    return Profiles[player].Data
end

function CreateProfile(player)
    local profile = ProfileStore:LoadProfileAsync("Player_"..player.UserId)
    if profile then
        profile:Reconcile() -- Fills missing variables from UserData
        profile:ListenToRelease(function() -- If profile is loaded in another Roblox Server
            Profiles[player] = nil
            player:Kick()
        end)

        if player:IsDescendantOf(Players) then
            Profiles[player] = profile -- Profile Loaded
        else
            profile:Release() -- Player Left before their profile loaded
        end

    else
        player:Kick() -- Another server was loading the profile at the same time
    end
end

function RemoveProfile(player)
    local profile = Profiles[player]
    if profile then
        profile:Release()
    end
end

function DataService.Client:GetPetData(player)
    if player then
      return self.Server:GetData(player).Inventory.Pets
    end
end

function DataService:KnitInit()

    -- Player Events
    Players.PlayerAdded:Connect(CreateProfile)
    Players.PlayerRemoving:Connect(RemoveProfile)

    -- Safety 
    for _, player in ipairs(Players:GetPlayers()) do -- Just incase player spawns before script runs
       task.spawn(CreateProfile, player)
    end
    
    game:BindToClose(function() -- If game closes/ Shuts down 
        for _, player in ipairs(Players:GetPlayers()) do 
            task.spawn(CreateProfile, player)
         end
    end)
end

function DataService:KnitStart()

end

return DataService

So this is my DataService script. I have a function called DataService:GetData() which you can see and whenever I call this from another server script it prints out nothing now I am calling this from another server script called WeaponService and I am running this in the :KnitInit() function:

-- Function
function WeaponService:GetCurrentWeapon(player)
    return Knit.Services.DataService:GetData(player)
end

function WeaponService:KnitInit()
    Players.PlayerAdded:Connect(function(player)
       print(self:GetCurrentWeapon(player))
    end)
end

What I’m thinking is the player data doesn’t exist at the time I call it which could be the case, if it is what would be the fix to this?

Now this is just data from Server to Server but when I try to get data from the client to server it goes horribly wrong. Also is it bad for the client to retrieve data instead of the server sending the data?

My client setup, so I was trying to make a pet inventory which is why in my DataService script there is a function called DataService.Client:GetPetData(player) so I could wait for the client to ask the server for data. Now in my head this should work but it doesn’t.

PetInventory Client:

local Knit = require(game:GetService("ReplicatedStorage"):WaitForChild("Common").Knit)
local Janitor = require(Knit.Util.Janitor)
local DataService = Knit.GetService("DataService")

local PetInventory = {}
PetInventory.__index = PetInventory

PetInventory.Tag = "PetInventory"

function PetInventory:SetUp()
end

function PetInventory:Refresh()
end

function PetInventory.new(guiManager, playerGui)
    local self = setmetatable({}, PetInventory)
    self._janitor = Janitor.new()

    guiManager._GetAsset("Weapons"):Clone().Parent = playerGui:WaitForChild("Main")

    -- Need to get pet Data
    print(DataService:GetPetData(guiManager._Player))

    self:SetUp()

    return self
end

function PetInventory:Init()
end

function PetInventory:Deinit()
end

function PetInventory:Destroy()
    self._janitor:Destroy()
end

return PetInventory

As you can see I call it but I still believe the issue is, whilst writing this I believe I realised the only real issue is the data isn’t loading fast enough so there is no Data when the scripts request it.

How could I make it so the data is loaded before any other scripts request it? This is just so I can request it in any script and there shouldn’t be any issues.

TL:DR
How would I fix the data not loading in time?
My scripts work but there is no player data so the output shows an empty table {}
Edit: Just looking through the API should I use Options in this scenario?

Sorry for the long post most things may be unnecessary but I thought I would give as much detail as possible as to why I did what I did. I apologize in advanced and appreciate any help given

I didn’t read the whole thing but the answer to this question is:

local function WaitForData(player)
    while Profiles[player] == nil do
       task.wait()
    end
    return Profiles[player].Data
end

You can use that when you want to access a specific players data from another script. If you want to run code when any player’s Profile is loaded you can have a Signal which you connect an “onProfileAdded” function to.

1 Like

Oh not sure how I didn’t think of that. :grinning_face_with_smiling_eyes: Would this be a hacky solution or should this be functional to work in the full game? Thanks in advanced.

I’m pretty sure this is the way you wait “wait” for things.

I also thought it was a hacky solution because it’s a while loop checking, but I saw this code in ProfileService so I think that’s how it’s done.

I would also like to know if there is any event-based “waiting” technique.

Anyway with your case you can also consider making an “onProfileAdded” signal then any scripts can pass it a function to run when a profile is added (think of it like onPlayerAdded)

1 Like

This is quite early on in the project but I haven’t made any even-based waiting. Although I will look into signals since I want to learn more about and it should be better in theory. I’ll give it a go and see I can make it work. Thanks, I’ll mark your first reply as solution.

Edit: This is quite a dumb question but how to signals work exactly?

Currently looking the Signal API on the knit page, would it work something like this:

local onProfileAdded  = Signal.new() -- Would I put a function in here?

onProfileAdded:Fire(player?) -- Fire everytime a profile is created? 
-- How would you retrieve it.

edit:

local Connection = onProfileAdded:Connect(function(...) -- Does this work?
   -- Disconnect connection
   print(...)
end

Sorry, I don’t quite understand how they work. I have some basic questions which don’t seem to be answered on the Knit signal Document. I’m going to look for the actual signal documentation and see if that helps.