In other languages:

Tutorial:Script interfaces: Difference between revisions

From Official Factorio Wiki
Jump to navigation Jump to search
(→‎Calling interface functions: note on remote call limitations - particularly trying to "share" a table)
 
(18 intermediate revisions by 6 users not shown)
Line 1: Line 1:
Script interfaces allow direct communication between simultaneously running scripts. This is done in form of defining a public interface with given functions. All the code regarding the interfaces communication is in the <code>remote</code> namespace.  
{{Languages}}
== Script interfaces (LuaRemote) ==
 
Script interfaces allow direct communication between simultaneously running scripts. This is done in form of defining a public interface with given functions. All the code regarding the interfaces communication is in the <code>remote</code> namespace. More info can be found in the offical api documentation, in this case [http://lua-api.factorio.com/latest/LuaRemote.html LuaRemote].


=== Defining interfaces ===
=== Defining interfaces ===
Line 5: Line 8:
The interface is defined as follows:
The interface is defined as follows:


  -- assuming name and table_of_functions are defined elsewhere
<syntaxhighlight lang="lua">-- assuming interface_name and table_of_functions are defined elsewhere
  remote.addinterface(interface_name, table_of_functions)
remote.add_interface(interface_name, table_of_functions)


  -- It is possible to define the name and table inside the call
-- It is possible to define the name and table inside the call
  remote.addinterface("myinterface", {
remote.add_interface("my_interface", {
     mygetter = function()
     my_getter = function()
      -- you can return 1 or more variables from the script
        -- you can return 1 or more variables from the script
      return "foo"
        return "foo"
     end,
     end,
    
    
     -- the values can be only primitive type or (nested) tables
     -- the values can be only primitive type or (nested) tables
     mysetter = function(foo, bar)
     my_setter = function(foo, bar)
      glob.foo = foo
        global.foo = foo
      glob.bar = bar
        global.bar = bar
     end
     end
  })
})</syntaxhighlight>
 
The interface functions can take only primitive types and tables as variables. The same holds for return values. The reason for this is that the scripts are running in different lua states and there is no easy way of passing more elaborate data (functions, rich Factorio objects - Lua Entity, etc.) around. Passing simple function callbacks is very powerful mechanism and might be implemented in some way in the future.
 
The interfaces are not serialised. Therefore it is a good idea to put them into the global scope of the script. In that way they are run everytime the script file is loaded (no matter whether it is the first run or game being loaded).


At the moment the interface is identified solely by its name. There is no vesion mechanism yet. It is a good idea to prefix the name of the interface with the name of the mod / map where it is defined.
The interface functions cannot take function pointers or function closures. Primitive types, LuaObjects and tables work just fine.


=== Calling interface functions ===
=== Calling interface functions ===


The interface can be used for calling functions from a different script.  
The interface can be used for calling functions from a different script. '''Limitations''': A table with a metatable will not retain that metatable when received by the remote mod (however, game objects passed will remain intact). A received table is a '''copy''' of the original. It is '''not possible''' for two mods to "share" the same table across a remote call.


Example (in the different script than the one above):
Example (in the different script than the one above):


  -- calls the myinterface.mygetter from the script above and prints the returning value
<syntaxhighlight lang="lua">-- calls the my_interface.my_getter from the script above and prints the returning value
  print(remote.call("myinterface", "mygetter"))
print(remote.call("my_interface", "my_getter"))
 
-- remote call takes the name of the interface, name of the function and then variable amount of parameters
This remote.call can be used in the global scope as well. However then the script in which it is called has to be invoked after the script which defined the interface. The ordering of the scripts is: first the map script and then the mod scripts in order defined by the mod ordering.
remote.call("my_interface", "my_setter", 5, {bar=baz})</syntaxhighlight>
 
  -- set values in the other script
  -- remote call takes the name of the interface, name of the function and then variable amount of parameters
  remote.call("myinterface', "mysetter", 5, {bar=baz})


=== Discovering interfaces ===
=== Discovering interfaces ===
Line 48: Line 43:


Example:
Example:
<syntaxhighlight lang="lua">
-- check whether there is the "my_interface" interface and whether it contains the "my_getter" function
if remote.interfaces.my_interface and remote.interfaces.my_interface.my_getter then
    -- the remote call for the function is safe to use
end</syntaxhighlight>


  -- check whether there is the "myinterface" interface and whether it contains the "mygetter" function
== Custom input ==
  if remote.interfaces.myinterface and remote.interfaces.myinterface.mygetter then
 
    -- the remote call for the function is safe to use
Keybindings can also be created. First the keybinding has to be defined in the data stage, see [[Prototype/CustomInput]]:
  end
 
<syntaxhighlight lang="lua">local button={
    type = "custom-input",
    name = "my-custom-input",
    key_sequence = "SHIFT + G",
    consuming = "none"
}
data:extend{button}</syntaxhighlight>
 
Available options for "consuming" are:
* none: default if not defined
* game-only: Blocks game inputs using the same key sequence but lets other custom inputs using the same key sequence fire.
 
:''See also [[Types/ConsumingType]]''
 
Locale definition:
 
<syntaxhighlight lang="lua">[controls] --Text for "game menu -> controls -> mods"
my-custom-input=Potato controls</syntaxhighlight>
 
Use <code>__CONTROL__my-custom-input__</code> to get the bound key in other locale, for example
 
<syntaxhighlight lang="lua">this-is-some-locale=Potato controls are bound to "__CONTROL__my-custom-input__"</syntaxhighlight>
 
shows <code>Potato controls are bound to "SHIFT + G"</code> in-game.
 
 
And then it can be used runtime by subscribing to the event of the name of the custom input:
 
<syntaxhighlight lang="lua">script.on_event("my-custom-input", function(event)
    game.print("Ran on tick: " .. tostring(event.tick))
end)</syntaxhighlight>
 
The [https://lua-api.factorio.com/latest/events.html#CustomInputEvent event] contains the following:
 
* player_index :: [[Types/uint32]]
* tick :: [[Types/uint32]]
* input_name :: [[Types/string]]: The name of the custom input.
* cursor_position :: [[Types/Position]]:  The mouse cursor position when the input was activated.
* selected_prototype :: [[Types/table]] (optional): Provided if [[Prototype/CustomInput#include_selected_prototype|include_selected_prototype]] is true. This table has the following members:
** base_type :: [[Types/string]]: E.g. "entity".
** derived_type :: [[Types/string]]: E.g. "tree".
** name :: [[Types/string]]: E.g. "tree-05".

Latest revision as of 10:01, 19 September 2022

Script interfaces (LuaRemote)

Script interfaces allow direct communication between simultaneously running scripts. This is done in form of defining a public interface with given functions. All the code regarding the interfaces communication is in the remote namespace. More info can be found in the offical api documentation, in this case LuaRemote.

Defining interfaces

The interface is defined as follows:

-- assuming interface_name and table_of_functions are defined elsewhere
remote.add_interface(interface_name, table_of_functions)

-- It is possible to define the name and table inside the call
remote.add_interface("my_interface", {
    my_getter = function()
        -- you can return 1 or more variables from the script
        return "foo"
    end,
  
    -- the values can be only primitive type or (nested) tables
    my_setter = function(foo, bar)
        global.foo = foo
        global.bar = bar
    end
})

The interface functions cannot take function pointers or function closures. Primitive types, LuaObjects and tables work just fine.

Calling interface functions

The interface can be used for calling functions from a different script. Limitations: A table with a metatable will not retain that metatable when received by the remote mod (however, game objects passed will remain intact). A received table is a copy of the original. It is not possible for two mods to "share" the same table across a remote call.

Example (in the different script than the one above):

-- calls the my_interface.my_getter from the script above and prints the returning value
print(remote.call("my_interface", "my_getter"))
-- remote call takes the name of the interface, name of the function and then variable amount of parameters
remote.call("my_interface", "my_setter", 5, {bar=baz})

Discovering interfaces

The script can check for expected interfaces and its functions via the remote.interfaces table. This is a table indexed by interface names where the values are set of functions for particular interfaces.

Example:

-- check whether there is the "my_interface" interface and whether it contains the "my_getter" function
if remote.interfaces.my_interface and remote.interfaces.my_interface.my_getter then
    -- the remote call for the function is safe to use
end

Custom input

Keybindings can also be created. First the keybinding has to be defined in the data stage, see Prototype/CustomInput:

local button={
    type = "custom-input",
    name = "my-custom-input",
    key_sequence = "SHIFT + G",
    consuming = "none"
}
data:extend{button}

Available options for "consuming" are:

  • none: default if not defined
  • game-only: Blocks game inputs using the same key sequence but lets other custom inputs using the same key sequence fire.
See also Types/ConsumingType

Locale definition:

[controls] --Text for "game menu -> controls -> mods"
my-custom-input=Potato controls

Use __CONTROL__my-custom-input__ to get the bound key in other locale, for example

this-is-some-locale=Potato controls are bound to "__CONTROL__my-custom-input__"

shows Potato controls are bound to "SHIFT + G" in-game.


And then it can be used runtime by subscribing to the event of the name of the custom input:

script.on_event("my-custom-input", function(event)
    game.print("Ran on tick: " .. tostring(event.tick))
end)

The event contains the following: