DataStoreService usage and robustness - lua

I have the following code in my Roblox game:
local DataStoreService = game:GetService("DataStoreService")
local myDataStore = DataStoreService:GetDataStore("myDataStore")
function saveCurrentStats(player)
print("saveCurrentStats")
local success, errormessage = pcall(function()
myDataStore:SetAsync(player.UserId.."-foundcats", player.FoundCats.Value)
end)
if success then
print("Progress successfully saved")
else
print("Error while saving progress")
warn(errormessage)
end
print("saveCurrentStats done")
end
game.Players.PlayerRemoving:Connect(function(player)
saveCurrentStats(player)
end)
Often this works - the data is saved for the player. But quite often I also only get something like this in the output:
18:18:38.708 saveCurrentStats - Server - Script:66
So I get neither if nor else printout (and the data is also indeed not saved)
Is there something I should do to make this robust? Like wait for something at some point.

Problem started to happen more often when my project grew, and the reason is that when running the game from Roblox Studio the script is just terminated when you hit the stop button. It will not be given time to execute, and with a larger project the chance of running the script decreases.
A work-around is to type this in the command bar instead of pressing the stop button:
game:GetService("Players").LocalPlayer:Kick()
This allows you to run the script properly when testing the game in Roblox Studio. When running the game live the issue is not seen, since then the script execution is not terminated prematurely.

Related

Why Aren't The Sounds in my Sound Group not working?

I was trying to make a music player for my game, however, when I was trying to get my sound to play it refused to work. The games output works before and after the sound, but I can't hear anything. I tried using both a folder and sound group (what I'm using currently) and both did not work. How would I fix this? I presume it has something to do with client-server but I am not sure.
local ss = game:WaitForChild("SoundService")
local rp = game:WaitForChild("ReplicatedStorage")
local list = ss.Music:GetChildren()
rp.SongOn.OnServerEvent:Connect(function(plr)
repeat
local num = math.random(1, #list)
print(num)
local track = list[num]
local name = track.Name
print(name)
plr.PlayerGui.Overhead.Notch.SongTitle.Text = track.Name
local song = ss.Music:WaitForChild(name)
print("played")
wait(track.TimeLength)
print("waited length")
until
rp.SongOff.OnServerEvent
end)
You never play anything. You don't actually reproduce the Sound. To run a Sound, use Sound:Play()
https://create.roblox.com/docs/reference/engine/classes/Sound
Your repeat until condition is also wrong. The music will not stop playing once that event is fired, and it will actually not matter at all - when the loop finishes running once, it will compare if literally rp.SongOff.OnServerEvent evaluates to true. OnServerEvent is the literal event itself, which will be true since it is not false or nil. So the loop will stop running when it runs once.
Instead, you likely want to make a function that plays the music, and run this function whenever:
Playing music is requested with the remote
The playing music naturally ends (https://create.roblox.com/docs/reference/engine/classes/Sound#Ended)
And then, bind to that stop music remote a function that stops the sound.
Also you should have some more shame, you didn't even try to hide you're making a nazi game

My LoadCharacter() Script Isn't Working Properly

I am trying to make a LoadCharacter() script, but when it runs it makes the player load several times before they actually load.
Here is the code:
game.Players.PlayerAdded:Connect(function(player)
game.ReplicatedStorage.Remotes.LoadCharacter.OnServerEvent:Connect(function()
player:LoadCharacter()
end)
player:LoadCharacter()
end)
BTW I disabled CharacterAutoLoad.
Your issue is that your connection is inside the PlayerAdded event. It is causing the event to register callbacks every single time a player joins. So when any player fires the event, it is causing EVERYONE to reload their character.
So just move the connection outside it, and get the player object from the RemoteEvent. The first argument from OnServerEvent is always the player that called FireServer so you don't need to make any changes to your LocalScript.
game.Players.PlayerAdded:Connect(function(player)
player:LoadCharacter()
end)
game.ReplicatedStorage.Remotes.LoadCharacter.OnServerEvent:Connect(function(player)
player:LoadCharacter()
end)

Why only does idle play from my animation script?

So I'm making a custom animation script for a custom character on Roblox. The AI works fine but it will almost always only play idle animations even with humanoid events.
What should normally happen is when the monster is Idle the idle animation should play. When walking the walk animation should play. And when the AI attacks, the attack animation should play.
I've dried commenting out the idle animation part, but then no animations play at all.
Here's the code:
local myHuman = script.Parent.Humanoid
local walkAnim = myHuman:LoadAnimation(script.Parent.Walk)
local idleAnim = myHuman:LoadAnimation(script.Parent.Idle)
local jumpAnim = myHuman:LoadAnimation(script.Parent.Jump)
myHuman.Running:Connect(function(speed)
if speed > 3 then
walkAnim:Play()
else
walkAnim:Stop()
idleAnim:Play()
end
end)
myHuman.Jumping:Connect(function()
jumpAnim:Play()
end)
myHuman.Died:Connect(function()
for i,v in pairs(myHuman:GetPlayingAnimationTracks()) do
v:Stop()
end
end)
Try adding a print instead of the animation and see what happens and tell me if it prints anything.
myHuman.Running:Connect(function(speed)
if speed > 3 then
print("walk")
else
print("start idle")
end
end)
Edit: https://devforum.roblox.com/t/getplayinganimationtracks-is-deprecated/1075650 I believe that you must do
myHuman.Animator:getPlayingAnimationTracks()

Roblox Studio: Print text, when leaderboard kill value reaches 10

Is there a way in Roblox Studio, when a leaderboard kill value reaches 10 print or do anything. (leaderstats model already exists at game.Players.(player))
Say the variable stats is the model in the workspace for the player stats with the relevant player as its parent:
if stats.KO == 10 then
print("A player just hit 10 kills!")
end
Pay attention to the console. Try printing the player's name to check to make sure that you actually have the player as a variable, like this.
print(player.Name)
That will confirm that you actually referenced the player correctly.

wxLua - How do I implement a Cancel button?

I have a wxLua Gui app that has a "Run" button. Depending on selected options, Run can take a long time, so I would like to implement a "Cancel" button/feature. But it looks like everything in wxLua is working on one Gui thread, and once you hit Run, pressing Cancel does nothing, the Run always goes to completion.
Cancel basically sets a variable to true, and the running process regularly checks that variable. But the Cancel button press event never happens while Running.
I have never used co-routines; if the Run process regularly yields to a "Cancel check" process, will the Cancel event happen then?
Or is there another way?
(the following assumes that by "Run" you mean a long running operation in the same process and not running an external process using wxExecute or wxProcess.)
"Cancel" event is not triggered because by executing your Run logic you have not given a chance to the UI to handle the click event.
To avoid blocking the UI you need to do something like this. When you click Run button create a co-routine around the function you want to run:
coro = coroutine.create(myLongRunningFunction)
Your Run event is completed at this point. Then in EVT_IDLE event you will be resuming this coroutine as long as it's not complete. It will look something like this:
if coro then -- only if there is a coroutine to work on
local ok, res = coroutine.resume(coro, additional, parameters)
-- your function either yielded or returned
-- you may check ok to see if there was an error
-- res can tell you how far you are in the process
-- coro can return multiple values (just give them as parameters to yield)
if coroutine.status(coro) == 'dead' then -- finished or stopped with error
coro = nil
-- do whatever you need to do knowing the process is completed
end
end
You will probably need to request more IDLE event for as long as your process is not finished as some operating systems will not trigger IDLE events unless there is some other event triggered. Assuming your handler has event parameter, you can do event:RequestMore(true) to ask for more IDLE events (RequestMore).
Your long-running process will need to call coroutine.yield() at the right time (not too short as you will be wasting time to switch back and forth and not too long for users to notice delays in the UI); you probably need to experiment with this, but something timer-based with 100ms or so between calls may work.
You can check for Cancel values either in your IDLE event handler or in the long-running function as you do now. The logic I described will give your application UI a chance to process Cancel event as you expect.
I don't use WXWidgets, but the way I implement cancel buttons in my lua scripts which use IUP is to have a cancel flag, which is set when the button is pressed and the progress display is checked for during the run.
Usage is like this
ProgressDisplay.Start('This is my progress box',100)
for i=1,100 do
ProgressDisplay.SetMessage(i.." %")
fhSleep(50,40) -- Emulate performing the task
ProgressDisplay.Step(1)
if ProgressDisplay.Cancel() then
break
end
end
ProgressDisplay.Reset()
ProgressDisplay.Close()
If you want to see the definition for the ProgressDisplay see:
http://www.fhug.org.uk/wiki/doku.php?id=plugins:code_snippets:progress_bar

Resources