remote.call only returns one value

Place to get help with not working mods / modding interface.
Post Reply
User avatar
cpeosphoros
Inserter
Inserter
Posts: 40
Joined: Fri Dec 23, 2016 10:57 pm
Contact:

remote.call only returns one value

Post by cpeosphoros »

Still working on Busy Bots.

On the client side I have this:

Code: Select all

remote.add_interface("Tree Cutter",
    {
        doJob = function(worker, amount)
            return TreeCutter.doJob(worker, amount)
        end
    }
)
and on TreeCutter.doJob I have:

Code: Select all

return count, "Job done."
Then, on Busy Bots I have this call:

Code: Select all

local jobCount, report =  remote.call(job.name, "doJob", worker, amount)
jobCount works all right, with the value returned by doJob. report, though, is always nil, no matter what doJob returned on the second value.

remote.call() is probably treating the return values so they can be sent between the separate interpreters, but is only treating the first value returned.

User avatar
aubergine18
Smart Inserter
Smart Inserter
Posts: 1264
Joined: Fri Jul 22, 2016 8:51 pm
Contact:

Re: remote.call only returns one value

Post by aubergine18 »

Currently you have to use a table if you want multiple return values (put them in a table as named or numerically indexed values).

Code: Select all

-- in remote function
return { count, "Job done." }

-- in calling function:

local ret = remote.call( ..... )

local jobCount, report = ret[0], ret[1]
Better forum search for modders: Enclose your search term in quotes, eg. "font_color" or "custom-input" - it prevents the forum search from splitting on hypens and underscores, resulting in much more accurate results.

Nebelwolfi
Inserter
Inserter
Posts: 23
Joined: Tue Nov 15, 2016 4:07 pm
Contact:

Re: remote.call only returns one value

Post by Nebelwolfi »

aubergine18 wrote:

Code: Select all

-- in remote function
return { count, "Job done." }

-- in calling function:

local ret = remote.call( ..... )

local jobCount, report = ret[0], ret[1]
if you index them numerically you can do

Code: Select all

local jobCount, report = unpack(ret)
or even

Code: Select all

local jobCount, report = unpack(remote.call(...))

User avatar
cpeosphoros
Inserter
Inserter
Posts: 40
Joined: Fri Dec 23, 2016 10:57 pm
Contact:

Re: remote.call only returns one value

Post by cpeosphoros »

Nebelwolfi wrote:

Code: Select all

local jobCount, report = unpack(remote.call(...))
Very elegant. I will adopt it, thank you. I was using an interface callback, which though not performance relevant (real tail calls, yay Lua!!!), were quite ugly hacks.

User avatar
Optera
Smart Inserter
Smart Inserter
Posts: 2919
Joined: Sat Jun 11, 2016 6:41 am
Contact:

Re: remote.call only returns one value

Post by Optera »

According to this

Code: Select all

local jobCount, report = ret[0], ret[1]
should be several times faster than using unpack

User avatar
cpeosphoros
Inserter
Inserter
Posts: 40
Joined: Fri Dec 23, 2016 10:57 pm
Contact:

Re: remote.call only returns one value

Post by cpeosphoros »

Optera wrote:According to this

Code: Select all

local jobCount, report = ret[0], ret[1]
should be several times faster than using unpack
Well, your statement made me curious, about if all those performance tests would hold the same result inside Factorio's customized Lua interpreter. So I've made a test harness and time profiled them inside a real mod.

This is the harness (Sorry, it's quite long):

Code: Select all

require "LOGGER"

----------------------
-- TEST 1: Localize --
----------------------

local min = math.min

local function localMin()
	local a = min(1, 10)
end

local function mathMin()
	local a = math.min(1, 10)
end

-------------------------------------
-- TEST 2: Localized Class-Methods --
-------------------------------------

local class = {}

function class.test()
	return 10
end

local function localTest()
    local test = class.test
    local x = test()
    local y = test()
    local z = test()
end

local function classTest()
    local x = class.test()
    local y = class.test()
    local z = class.test()
end

----------------------------
-- TEST 3: Unpack a Table --
----------------------------

local a = {10, 20, 30, 40}

local function directIndex()
	local x = min(a[1], a[2], a[3], a[4])
end

local myUnpack = unpack

local function localUnpack()
	local x = min(myUnpack(a))
end

local function nativeUnpack()
	local x = min(unpack(a))
end

local function unpack4(a)
	return a[1], a[2], a[3], a[4]
end

local function rewrittenUnpack()
	local x = min(unpack4(a))
end

-------------------------------
-- TEST 4: Determine Maximun --
-------------------------------

local max = math.max
local random = math.random
local x = 0

local function useMax()
	x = max(random(), x)
end

local function useIf()
	local r = random()
	if r > x then x = r end
end

------------------------
-- TEST 5: Nil Checks --
------------------------

local function nilIf()
	local y,x
	if (random()>0.5) then y=1 end 
	if (y==nil) then x=1 else x=y end
end

local function nilOr()
	local y,x
	if (random()>0.5) then y=1 end 
	x = y or 1
end

----------------------------
-- TEST 6: Exponentiation --
----------------------------

local function exp(v)
	local x = v^2
end

local function mult(v)
	local x = v*v
end

-------------------------------
-- TEST 7: Modulus - skipped --
-------------------------------

---------------------------------
-- TEST 8: Function parameters --
--------------------------------

local func1 = function(a,b,func) 
	return func(a+b) 
end

local function inline()
	local x = func1(1,2,function(a) return a*2 end)
end

local func2 = function(a) 
	return a*2 
end

local function extracted()
	local x = func1(1,2,func2)
end

---------------------------------
-- TEST 9: For loops - skipped --
---------------------------------

-------------------------------------------
-- TEST 10: table["name"] vs. table.name --
-------------------------------------------

local function useArray()
	local x = global["dummy"]
end

local function useMethod()
	local x = global.dummy
end

------------------------------------
-- TEST 11: Buffered Table Access --
------------------------------------

local function nonBuffered()
	for n=1,100 do
		global.a[n].x = global.a[n].x + 1
	end
end

local function buffered()
	for n=1,100 do
		local y = global.a[n]
		y.x = y.x + 1
	end
end

-----------------------------------------
-- TEST 12: Table Population - Skipped --
-----------------------------------------

-----------------------------
-- TEST 13: Table Creation --
-----------------------------

local function createDirect()
	local a = {1, 2, 3}
end

local function createNonAlloc()
	local a = {}
	a[1] = 1
	a[2] = 2
	a[3] = 3
end

local function createAlloc()
	local a = {true, true, true}
	a[1] = 1
	a[2] = 2
	a[3] = 3
end

----------
-- Work --
----------

local function doAssorted(f, limit, message)
	for tick = 1, limit do
		f(tick)
	end
	LOGGER.log(message)
end

local function setGlobals(v)
	global.dummy = 0
	global.a = {}
	for n = 1, 100 do
		global.a[n] = {x = n}
	end
end

local function nilGlobals()
	global.dummy = nil
end

function runAssorted()

	for _, v in ipairs({100, 10000, 100000}) do

		LOGGER.log("Testing for v=" .. v)

		setGlobals(v)

		doAssorted(localMin, v, "Local Min")
		doAssorted(mathMin, v, "Math Min")

		doAssorted(localTest, v, "Local Test")
		doAssorted(classTest, v, "Class Test")

		doAssorted(directIndex, v, "Direct Index")
		doAssorted(localUnpack, v, "Local Unpack")
		doAssorted(nativeUnpack, v, "Native Unpack")
		doAssorted(rewrittenUnpack, v, "Rewritten Unpack")

		doAssorted(useMax, v, "Find Maximun with max")
		doAssorted(useIf, v, "Find Maximun with if")

		doAssorted(nilIf, v, "Nil if")
		doAssorted(nilOr, v, "Nil or")

		doAssorted(exp, v, "Exponentiation")
		doAssorted(mult, v, "Multiplication")

		doAssorted(inline, v, "Inline function parameters")
		doAssorted(extracted, v, "Localized function parameters")

		doAssorted(useArray, v, "Array Access")
		doAssorted(useMethod, v, "Method Access")

		doAssorted(buffered, v, "Buffered Access")
		doAssorted(nonBuffered, v, "Non Buffered Access")

		doAssorted(createDirect, v, "Direct Table Creation")
		doAssorted(createAlloc, v, "Pre Allocated Table Creation")
		doAssorted(createNonAlloc, v, "Non Pre Allocated Table Creation")
	end
	nilGlobals()
end

local done
local function onTick(event)
	if not done then
		runAssorted()
	end
end
script.on_event(defines.events.on_tick, onTick)
These are the results, for v=100k (the lesser values hold the same relation):

Code: Select all

00007.207: 102:43:33.28: Local Min
00011.906: 102:43:33.28: Math Min

00016.782: 102:43:33.28: Local Test
00017.506: 102:43:33.28: Class Test

00014.686: 102:43:33.28: Direct Index
00016.787: 102:43:33.28: Local Unpack
00014.928: 102:43:33.28: Native Unpack
00022.710: 102:43:33.28: Rewritten Unpack

00015.227: 102:43:33.28: Find Maximun with max
00008.113: 102:43:33.28: Find Maximun with if

00009.640: 102:43:33.28: Nil if
00008.982: 102:43:33.28: Nil or

00005.543: 102:43:33.28: Exponentiation
00004.153: 102:43:33.28: Multiplication

00012.036: 102:43:33.28: Inline function parameters
00011.767: 102:43:33.28: Localized function parameters

00008.703: 102:43:33.28: Array Access
00008.734: 102:43:33.28: Method Access

00832.507: 102:43:33.28: Buffered Access
01372.638: 102:43:33.28: Non Buffered Access

00027.763: 102:43:33.28: Direct Table Creation
00031.491: 102:43:33.28: Pre Allocated Table Creation
00059.412: 102:43:33.28: Non Pre Allocated Table Creation
Specifically as to your statement, using native unpack will perform almost exactly the same as using "local jobCount, report = ret[0], ret[1]", while being more readable and maintainable.

Results related to the tests I've skipped here can be found on these threads:
viewtopic.php?f=25&t=39500
viewtopic.php?f=25&t=39025

User avatar
Optera
Smart Inserter
Smart Inserter
Posts: 2919
Joined: Sat Jun 11, 2016 6:41 am
Contact:

Re: remote.call only returns one value

Post by Optera »

cpeosphoros wrote: Well, your statement made me curious, about if all those performance tests would hold the same result inside Factorio's customized Lua interpreter. So I've made a test harness and time profiled them inside a real mod.

Specifically as to your statement, using native unpack will perform almost exactly the same as using "local jobCount, report = ret[0], ret[1]", while being more readable and maintainable.

Results related to the tests I've skipped here can be found on these threads:
viewtopic.php?f=25&t=39500
viewtopic.php?f=25&t=39025
Thanks for the testing. The unpack results are a surprise. I'll bookmark this post for reference.

Post Reply

Return to “Modding help”