matter icon indicating copy to clipboard operation
matter copied to clipboard

queryChanged does not work for multiple worlds

Open TheyCallMeRyan opened this issue 11 months ago • 2 comments

Description

Loop starts topoRuntime for all worlds. queryChanged calls useHookState expecting a pristine result for each world. Instead, all worlds receive the same result from topoRuntime, and since the first world to call queryChanged there sets hookState.storage, all future worlds will both:

  1. Fail to iterate over any pre-existing entities
  2. Fail to mark _changedStorage[componentToTrack] for that specific world With the result being that no other world will receive the associated data

Steps to reproduce

Minimum Reproduction:

--!strict

-- Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- Libraries
local Matter = require(ReplicatedStorage.Matter)

local World1 = Matter.World.new()
local World2 = Matter.World.new()
local World3 = Matter.World.new()

local Worlds = {
	World1 = World1,
	World2 = World2,
}

local Component = Matter.component("TestComponent")

local Loop = Matter.Loop.new(Worlds)
Loop:setWorlds(Worlds)

Loop:scheduleSystem(function(worlds: {[string]: Matter.World})
	for worldId, world in worlds do
		print("queryChanged: ", worldId)
		for id, record in world:queryChanged(Component) do
			print("Component Changed: ", worldId, id, record.new.text)
		end
	end
end)

local Bindable = Instance.new("BindableEvent")

Loop:begin({
	default = Bindable.Event
})

World1:spawn(Component({text = "Test 1"}))
World2:spawn(Component({text = "Test 2"}))
World3:spawn(Component({text = "Test 3"}))

Bindable:Fire()

Worlds.World3 = World3

Loop:setWorlds(Worlds)

Bindable:Fire()

Output:

  18:56:13.548  queryChanged:  World2  -  Server - TestSingleLoopMultipleWorlds:25
  18:56:13.548  Component Changed:  World2 1 Test 2  -  Server - TestSingleLoopMultipleWorlds:27
  18:56:13.548  queryChanged:  World1  -  Server - TestSingleLoopMultipleWorlds:25
  18:56:13.549  queryChanged:  World1  -  Server - TestSingleLoopMultipleWorlds:25
  18:56:13.549  queryChanged:  World2  -  Server - TestSingleLoopMultipleWorlds:25
  18:56:13.549  queryChanged:  World3  -  Server - TestSingleLoopMultipleWorlds:25

queryChanged failed to receive either the Component in World1, or the one in World3 because it processed the one in World2

Expected behavior

I expect queryChanged to work for each world assigned to the Loop and not just the first one called.

TheyCallMeRyan avatar Jan 11 '25 00:01 TheyCallMeRyan

It might be possible to do something related to setting unique topoRuntimes for each world in Loop; however, I think a better solution might be to have each world store the storage table as: world._queryChangedHooks[hookState] = storage instead of hookState.storage = storage This would also allow for easy cleanup in queryChangedCleanup.

TheyCallMeRyan avatar Jan 11 '25 00:01 TheyCallMeRyan

The potentially larger issue going on here is that using systems within Loop with topoRuntime.start called for all the worlds will make it so any calls to topoRuntime within that system will get the same result for all worlds.

So if you wanted unique values for useEvent/useThrottle/useDeltaTime/other hooks, they'll be the same. I think in general, other hooks getting the same result in a system with multiple worlds is fine, but I think specifically world:queryChanged is world specific and needs world-specific hook results.

TheyCallMeRyan avatar Jan 11 '25 00:01 TheyCallMeRyan