I have thought of a roundabout way to transfer data from a MainModule to your game securely using teleport data. The idea is to require
the MainModule in a separate universe to prevent any manipulation of datastores or the like.
First, have the plugin save the data in a MainModule somehow and have the player upload this as a model.
Next, instruct the player to join a separate game in a different universe (it MUST be in a different universe, otherwise you could inject code and modify datastores or things like that). This game would have solo servers so that any injected code would not affect others.
In this game, tell them to insert the id of the model and call require(assetId)
on the server and get the serialized data. Send this data to the client via something like a RemoteEvent (note: IIRC RemoteEvents have data limits, so you may need to separate large data into several calls and recombine it on the client, but that shouldn’t be too hard to do). When the client has this data, call TeleportService:Teleport and teleport them to the starting place of the actual game, putting the serialized data inside of the teleport data. It is important that you use TeleportService:Teleport
on the client, as if you do it on the server then it tries to send the teleport data to the client, but if it is too large then the teleport fails. Doing the teleport on the client removes this need, allowing you to send seemingly unlimited data.
Finally, inside of the starting place of the actual game, when a player joins have the client check TeleportService:GetLocalPlayerTeleportData and see if it is a serialized map. If it is, then you could either directly send the data directly to the server they are in or teleport them to a sub-place if needed (such as to prevent DDoS attacks by sending massive maps to the server to crash it), passing the teleport data with the second teleport as well. When sending the data with a RemoteEvent, again be aware of data size limits per request.
Note that you MUST use TeleportService:GetLocalPlayerTeleportData
and not Player:GetJoinData, as Player:GetJoinData
will not include the teleport data if the teleport originated from outside of the universe or if it was set on the client.
Since this data is being passed through the client, it could be spoofed by exploiters (or even just teleporting from a place the person owns), so do any sanity checks after receiving the data on the server in the real game.
This method seems to work. I tested it with the following code in two LocalScripts
in two separate universes.
Place 1 (substitute PLACE_ID with the place id of Place 2):
local TeleportService = game:GetService("TeleportService")
-- testString is approaching the maximum size a string can be, so I didn't test any larger
local testString = string.rep("abcdefghijklmnopqrstuvwxyz", 40000000) -- Length of 1040000000
-- Several variants are stored to test possible data size limits
local testTeleportData = {
Test1 = testString,
Test2 = string.upper(testString),
Test3 = string.reverse(testString),
Test4 = testString,
}
TeleportService:Teleport(PLACE_ID, nil, testTeleportData)
Place 2:
local TeleportService = game:GetService("TeleportService")
local data = TeleportService:GetLocalPlayerTeleportData()
if data then
print("data exists")
print("Test1 Length:", #data.Test1)
print("Test2 Length:", #data.Test2)
print("Test3 Length:", #data.Test3)
print("Test4 Length:", #data.Test4)
else
print("no data found")
end
Although working with strings these large caused massive lag, when it finally teleported to the second place, the output was, as expected:
data exists
Test1 Length: 1040000000
Test2 Length: 1040000000
Test3 Length: 1040000000
Test4 Length: 1040000000
This method, while inconvenient, is probably the best way to do what you want without some sort of proxy to download the model.