Feedback on "Actor"

Actors need a section on their limitations, because otherwise engineering around them feels like a game of wackamole. I’m still trying to design a system right now, but the limitations I’ve hit are:

Messages cannot send functions, as those functions will be originally made in a different Lua VM (but you can send a ModuleScript that returns a function as a workaround if you just want the modularity of code. Keep in mind that ModuleScripts won’t be cached/shared, you will be running a new module per actor, and it will not share data with the main game thread)

Actors really don’t like being sent big data in Messages (hundreds of thousands of tables). I suspect they use pass-by-copy rather than table references, but I didn’t verify that was happening, I just figured out I can’t use it to communicate the large data set I’m using.

ModuleScripts do not have access to a properly functioning script:GetActor(), it will always return nil- so if you intend to put shared functionality inside Modules, keep in mind to do anything that requires knowing the environment’s actor inside the individual scripts.

I am sure there are more limitations, and I will suggest them to be added to the list as I (or others) find them.

Affected URL: https://create.roblox.com/docs/reference/engine/classes/Actor

1 Like

Additionally, SharedTables are super slow. Both in their editing (which I expected), but also their conversion process is absolute ass for some reason. SharedTable.new(LargeDataSet) takes 31 seconds to run, when the time to make the LargeDataSet is 2s (and runs a bunch of calculations to do so too, so certainly the table functions themselves are a fraction of that 2s)

I don’t know why roblox needs 31s to convert a regular table to a SharedTable, and I was not expecting that, but that’s a good caveat to add to limitations- there is no way to have a large data set (which almost certainly many other people who want to multithread will have) be in a SharedTable without taking exponentially more time

Additionally, I found out that SharedTables cannot be cyclic, which made me suspicious that the conversion process to a SharedTable doesn’t maintain table references- and it’s true, testing with this code:

local Time = os.clock()
local SharedT = {["Hi"] = {}} -- Compare to time of SharedT just being a number, like how data costs should be for just a reference
local T = {}
for i=1,1000000 do
	T[i] = SharedT
end
local X = SharedTable.new(T)
print("Done:",os.clock()-Time)
-- Test 2:
local SharedT = {}
local T = {SharedT,SharedT}
local X = SharedTable.new(T)
print(X[1] == X[2])--> false
print(X == X)--> true

In test1, it takes significantly longer if SharedT is a table, than if it’s any other kind of value, implying that a new SharedTable is being created for each reference of it in T. I believe this theory is confirmed in Test2, as the two SharedTables do not share the same reference anymore, and it’s not just broken == behavior, which the last line tests for.

The implications of this is that if your data references any external tables/classes/etc, you must prune your data set to only contain unique data, and remove all those references, or it absolutely will dramatically slow down the copy process, as Roblox will make millions of tables doing a deep copy of it all.

Looks like you can get SharedTable to respect references if your references are already SharedTables- Roblox skips over them, instead of making new copies.

Proof:

local SharedT = SharedTable.new({})
local T1 = {SharedT,SharedT}
local T2 = {SharedT,SharedT}
local X = SharedTable.new(T1)
local Y = SharedTable.new(T2)
print(X[1] == X[2])--> true
print(X[1] == Y[1])--> true