Урок 12 из 15

DataStore: сохранить прогресс и не потерять его

Загружать и сохранять данные игрока так, чтобы сбой сети ничего не стирал.

Теория

DataStore — облачное хранилище Roblox. Работает только на сервере и только в опубликованной игре с включённым доступом к API-сервисам. В Studio без этой галочки запрос упадёт.

Любой запрос идёт по сети и может не ответить. Поэтому его оборачивают в pcall: функция вернёт признак успеха и результат, и код сам решит, что делать с неудачей. Без pcall упавший запрос обрывает весь скрипт.

Загружают данные при входе, сохраняют при выходе и при закрытии сервера. Последнее важно: без BindToClose сервер выключится раньше, чем сохранение успеет уйти.

  • UpdateAsync безопаснее SetAsync: он видит текущее значение перед записью
  • Имя хранилища с версией — PlayerData_v1 — спасает при смене формата
  • Не сохраняй по запросу клиента: что и когда писать, решает сервер
  • Пустой результат при первом входе — не ошибка, а новый игрок

Пример

Загрузка при входе и сохранение при выходе.

ServerScriptService/PlayerData.lua
  1. local DataStoreService = game:GetService("DataStoreService")
  2. local Players = game:GetService("Players")
  3.  
  4. local store = DataStoreService:GetDataStore("PlayerData_v1")
  5. local cache = {}
  6.  
  7. Players.PlayerAdded:Connect(function(player)
  8. local ok, data = pcall(function()
  9. return store:GetAsync("user_" .. player.UserId)
  10. end)
  11.  
  12. cache[player.UserId] = ok and data or { coins = 0 }
  13.  
  14. if not ok then
  15. warn("Данные не загрузились, играем без сохранения")
  16. end
  17. end)
  18.  
  19. Players.PlayerRemoving:Connect(function(player)
  20. local data = cache[player.UserId]
  21.  
  22. if not data then
  23. return
  24. end
  25.  
  26. pcall(function()
  27. store:UpdateAsync("user_" .. player.UserId, function()
  28. return data
  29. end)
  30. end)
  31.  
  32. cache[player.UserId] = nil
  33. end)

Задача

Добавь к этому коду сохранение при закрытии сервера, чтобы прогресс последних игроков не пропал при рестарте.

  • game:BindToClose(function() … end) вызывается перед выключением
  • Пройдись по всем оставшимся записям кэша
Показать решение
ServerScriptService/Solution.lua
  1. game:BindToClose(function()
  2. for userId, data in pairs(cache) do
  3. pcall(function()
  4. store:UpdateAsync("user_" .. userId, function()
  5. return data
  6. end)
  7. end)
  8. end
  9.  
  10. task.wait(2)
  11. end)

Небольшое ожидание в конце даёт запросам уйти до выключения сервера.

Проверь себя

Зачем запросы к DataStore оборачивают в pcall?

Показать ответ

pcall перехватывает ошибку и даёт коду решить, что делать дальше.

Почему UpdateAsync предпочтительнее SetAsync?

Показать ответ

UpdateAsync принимает решение на основе того, что уже лежит в хранилище.