Skip to content

Commit 9580f7c

Browse files
committed
Blocking prevention for MessagingService
There have been reports that suggested that MessagingService methods may yield for long times or throw errors even though there are no such notes in official Roblox API. As a precaution MessagingService calls have been wrapped in coroutines to prevent blocking.
1 parent 0105e46 commit 9580f7c

2 files changed

Lines changed: 36 additions & 25 deletions

File tree

ProfileStore.luau

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ MAD STUDIO (by loleris)
148148
Profile:AddUserId(user_id) -- Associates user_id with profile (GDPR compliance)
149149
user_id [number]
150150
151-
Profile:RemoveUserId(user_id) -- Unassociates user_id with profile (safe function)
151+
Profile:RemoveUserId(user_id) -- Removes user_id association with profile (safe function)
152152
user_id [number]
153153
154154
Profile:MessageHandler(fn) -- Sets a message handler for this profile
@@ -508,7 +508,7 @@ local function UpdateAsync(profile_store, profile_key, transform_params, is_user
508508
--transform_params = {
509509
-- ExistingProfileHandle = function(latest_data),
510510
-- MissingProfileHandle = function(latest_data),
511-
-- EditProfile = function(lastest_data),
511+
-- EditProfile = function(latest_data),
512512
--}
513513

514514
local loaded_data, key_info
@@ -1110,7 +1110,7 @@ function Profile:AddUserId(user_id) -- Associates user_id with profile (GDPR com
11101110

11111111
end
11121112

1113-
function Profile:RemoveUserId(user_id) -- Unassociates user_id with profile (safe function)
1113+
function Profile:RemoveUserId(user_id) -- Removes user_id association with profile (safe function)
11141114

11151115
if type(user_id) ~= "number" or user_id % 1 ~= 0 then
11161116
warn(`[{script.Name}]: Invalid UserId argument for :RemoveUserId() ({tostring(user_id)}); Traceback:\n` .. debug.traceback())
@@ -1333,6 +1333,34 @@ function ProfileStore.New(store_name, template)
13331333

13341334
end
13351335

1336+
local function RobloxMessageSubscription(profile, unique_session_id)
1337+
1338+
local last_roblox_message = 0
1339+
1340+
local roblox_message_subscription = MessagingService:SubscribeAsync("PS_" .. unique_session_id, function(message)
1341+
if type(message.Data) == "table" and message.Data.LoadCount == profile.SessionLoadCount then
1342+
-- High reaction rate, based on numPlayers × 10 DataStore budget as of writing
1343+
if os.clock() - last_roblox_message > 6 then
1344+
last_roblox_message = os.clock()
1345+
if profile:IsActive() == true then
1346+
if message.Data.EndSession == true then
1347+
SaveProfileAsync(profile, true, false, "External")
1348+
else
1349+
profile:Save()
1350+
end
1351+
end
1352+
end
1353+
end
1354+
end)
1355+
1356+
if profile:IsActive() == true then
1357+
profile.roblox_message_subscription = roblox_message_subscription
1358+
else
1359+
roblox_message_subscription:Disconnect()
1360+
end
1361+
1362+
end
1363+
13361364
function ProfileStore:StartSessionAsync(profile_key, params)
13371365

13381366
local is_mock = ReadMockFlag()
@@ -1392,7 +1420,7 @@ function ProfileStore:StartSessionAsync(profile_key, params)
13921420

13931421
-- SPECIAL CASE - If StartSessionAsync is called for the same key again before another StartSessionAsync finishes,
13941422
-- grab the DataStore return for the new call. The early call will return nil. This is supposed to retain
1395-
-- expected and efficient behaviour in cases where a player would quickly rejoin the same server.
1423+
-- expected and efficient behavior in cases where a player would quickly rejoin the same server.
13961424

13971425
LoadIndex += 1
13981426
local load_id = LoadIndex
@@ -1517,24 +1545,7 @@ function ProfileStore:StartSessionAsync(profile_key, params)
15171545
if is_mock ~= true and DataStoreState == "Access" then
15181546

15191547
-- Use MessagingService to quickly detect session conflicts and resolve them quickly:
1520-
1521-
local last_roblox_message = 0
1522-
1523-
profile.roblox_message_subscription = MessagingService:SubscribeAsync("PS_" .. unique_session_id, function(message)
1524-
if type(message.Data) == "table" and message.Data.LoadCount == profile.SessionLoadCount then
1525-
-- High reaction rate, based on numPlayers × 10 DataStore budget as of writing
1526-
if os.clock() - last_roblox_message > 6 then
1527-
last_roblox_message = os.clock()
1528-
if profile:IsActive() == true then
1529-
if message.Data.EndSession == true then
1530-
SaveProfileAsync(profile, true, false, "External")
1531-
else
1532-
profile:Save()
1533-
end
1534-
end
1535-
end
1536-
end
1537-
end)
1548+
task.spawn(RobloxMessageSubscription, profile, unique_session_id) -- Blocking prevention
15381549

15391550
end
15401551

@@ -1571,7 +1582,7 @@ function ProfileStore:StartSessionAsync(profile_key, params)
15711582
-- Request the remote server to end its session:
15721583
if type(active_session[3]) == "string" then
15731584
local session_load_count = loaded_data.MetaData.SessionLoadCount or 0
1574-
MessagingService:PublishAsync("PS_" .. active_session[3], {LoadCount = session_load_count, EndSession = true})
1585+
task.spawn(MessagingService.PublishAsync, MessagingService, "PS_" .. active_session[3], {LoadCount = session_load_count, EndSession = true})
15751586
end
15761587

15771588
-- Attempt to load the profile again after a delay
@@ -1697,7 +1708,7 @@ function ProfileStore:MessageAsync(profile_key, message)
16971708

16981709
if type(active_session) == "table" and type(active_session[3]) == "string" then
16991710
-- Request the remote server to auto-save sooner and receive the message:
1700-
MessagingService:PublishAsync("PS_" .. active_session[3], {LoadCount = session_load_count})
1711+
task.spawn(MessagingService.PublishAsync, MessagingService, "PS_" .. active_session[3], {LoadCount = session_load_count})
17011712
end
17021713

17031714
end

wally.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "lm-loleris/profilestore"
33
description = "Periodic DataStore saving solution with session locking"
4-
version = "1.0.2"
4+
version = "1.0.3"
55
registry = "https://github.com/UpliftGames/wally-index"
66
realm = "server"
77

0 commit comments

Comments
 (0)