News:

Simutrans Wiki Manual
The official on-line manual for Simutrans. Read and contribute.

A tool to execute squirrel script

Started by THLeaderH, May 05, 2020, 03:49:11 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

THLeaderH

Thanks to a tremendous work by Dwachs and other contributors, simutrans squirrel api has a lot of useful features.

So why don't we use this scripting api for the definition of tools? This is like MOD systems which are commonly seen in major games, such as Minecraft and Cities:Skylines.

The attached patch is a proof-of-concept implementation. In my implementation, init() and work() interfaces are provided and players can implement their own tool function in those interfaces. The nut file is put in pakXXX/tool/<scripting_tool_name>/tool.nut. For example, in my environment, tool.nut is put under pak128/tool/test_tool/ . The script loading window is called with dialog_tool[34].

One thing which scripting enables us is to build a station which consists of multiple kinds of add-ons with a single click. A demo video is available here. The attached nut file is the script used for this demo.

Dwachs

Parsley, sage, rosemary, and maggikraut.

prissi

I like this idea too. I just think you are overly optimistic to assume that every too is network save. It is more the other way round, unless the script is on the server I do not see how it could be network save (but then I did not touch the scripting much at all).

Getting an icon (and even an individual tooltip) is not a priory difficult, I think.

This brings me to the actual implementation. I would prefer if the file name to the script is defined as in the menuconf.dat (default_parameter). That way several scripts could be active (as well as icons to be defined). It might even allow for a forth class beyond dialog_tool ... like Script_tool in menuconf.tab.

Yona-TYT

Well something I have always wanted is a special tool for scripts that allows you to select rectangles with multiple tiles (As the climate editing tools do), this can be very useful to do something like delete a specific type of object in that selected area.

It seems to me an excellent idea!. :D

Dwachs

This should be save for network modes. The scripting framework handles this already: calls to tools are send to the server, and the script is suspended as long as the tool is not executed. The script will resume after the tool gets executed and it receives the error message (if there is any).
Parsley, sage, rosemary, and maggikraut.

THLeaderH

Thank you for your feedback. I have a problem in the implementation. Is it possible for script_vm_t::call_function() to pass non-primitive values to the script? For example, my implementation passes the coordinate as three integers in work() function, but passing single coord3d object is more reasonable than passing three integers.

prissi

OK, while technically save for networking, having a script only for oneself could be like a cheat mode, putting someone at advantage.

But I wonder, there are scripts in the tutorial that block certain calls. If they are blocked locally, what would happen, if another player sends them from his client?

Dwachs

Quote from: THLeaderH on May 06, 2020, 12:16:14 PMIs it possible for script_vm_t::call_function() to pass non-primitive values to the script?
Yes. It should be possible (koord3d and also player_t*). Did you try? For instance, scenario_t::is_work_allowed_here passes koord3d directly.

Quote from: prissi on May 06, 2020, 03:05:37 PMBut I wonder, there are scripts in the tutorial that block certain calls. If they are blocked locally, what would happen, if another player sends them from his client?
There are two types of blocks: one that is purely script based. This only works locally. The second also works for network games, but it is more limited. The forbidden calls are caught in the network code. The scenario class has methods like is_work_allowed_here()  to check some conditions.
Parsley, sage, rosemary, and maggikraut.

THLeaderH

Quote from: Dwachs on May 06, 2020, 05:57:24 PM
Yes. It should be possible (koord3d and also player_t*). Did you try? For instance, scenario_t::is_work_allowed_here passes koord3d directly.
I tried and succeeded. That I hadn't able to do this was due to another bug of my implementation. Thank you.

So the interface work() can be defined as

string work (player_x player, coord3d pos)


Although AI and scenario interfaces passes the player argument as a number, I think passing player_x object directly is more reasonable.

THLeaderH

#9
I attached the updated patch. Now scripted tools can be called from toolbars. Arguments of the functions are passes as coord3d and player_x objects. The script loading window, which is assigned to dialog_tool[34], is also available.

This version supports the following interface for scripted tools. The meaning of arguments and return values are same as those of tool_t's functions.

  • bool init (player_x player)
  • bool exit (player_x player)
  • string work (player_x player, coord3d pos)
  • string check_pos (player_x player, coord3d pos)

The move() function is not supported. This is because move_has_effects() needs to be a const function, but squirrel does not seem to be have such a mechanism. Also, move() is not network safe.
There is a plan to support the functions of two_click_tool_t, such as do_work() and mark_tiles().

Please note that simutrans/script/tool_base.nut must be copied into your simutrans executable directory.

You can write description.tab to define additional parameters for the scripted tools, although it is optional. description.tab is placed at the same directory as tool.nut. In other words, description.tab is placed at pakXXX/tool/<tool name> . The definition in description.tab is as follows.


title=tool for test
type=one_click
restart=1
menu=my_tools
icon=test_tool


"title" value is used as the tooltip text in a toolbar. "menu" value represents the group of scripted tools and used in menuconf.tab. For example, when you write menuconf.tab like this,

toolbar[4][10]=scripts(my_tools)

toolbar[4][10] shows all scripted tools whose "menu" value is my_tools.

"icon" value defines the name of menu icon. The definition is same as that in menuconf.tab.

"restart" and "type" value is not implemented yet. If "restart" is 1, the script virtual machine is disposed every time the tool is exited. Otherwise, the virtual machine is not disposed and state of the tool is preserved. "type" defines the scripted tools is based on tool_t or two_click_tool_t.

KNOWN BUG: When you call a scripted tool from the toolbar, simutrans often crashes. The following log is the backtrace of the crash.

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x5ae75993)
    frame #0: 0x000000010041418c sim`::sq_gettop(v=0x000000005ae7594b) at sqapi.cc:829:13
   826
   827 SQInteger sq_gettop(HSQUIRRELVM v)
   828 {
-> 829 return (v->_top) - v->_stackbase;
   830 }
   831
   832 void sq_settop(HSQUIRRELVM v, SQInteger newtop)
Target 0: (sim) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x5ae75993)
  * frame #0: 0x000000010041418c sim`::sq_gettop(v=0x000000005ae7594b) at sqapi.cc:829:13
    frame #1: 0x00000001003213aa sim`script_vm_t::intern_prepare_call(this=0x000000010a372020, job=0x00007ffeefbfbbc8, ct=QUEUE, function="init") at script.cc:221:95
    frame #2: 0x00000001003d4956 sim`char const* script_vm_t::call_function<bool, player_t*>(this=0x000000010a372020, ct=QUEUE, function="init", ret=0x00007ffeefbfbc2f, arg1=0x0000000125a62f60) at script.h:124:3
    frame #3: 0x00000001003d47fd sim`tool_exec_script_t::call_function(this=0x000000010121a400, func_name="init", player=0x0000000125a62f60) at simtool.cc:6550:16
    frame #4: 0x00000001003d42ca sim`tool_exec_script_t::init(this=0x000000010121a400, p=0x0000000125a62f60) at simtool.cc:6483:10
    frame #5: 0x00000001003f1161 sim`karte_t::local_set_tool(this=0x0000000124a2b200, tool_in=0x000000010121a400, player=0x0000000125a62f60) at simworld.cc:3046:30
    frame #6: 0x00000001003e88d0 sim`karte_t::set_tool(this=0x0000000124a2b200, tool_in=0x000000010121a400, player=0x0000000125a62f60) at simworld.cc:3031:3
    frame #7: 0x00000001001ec7fb sim`tool_selector_t::infowin_event(this=0x0000000125a63960, ev=0x00007ffeefbfc028) at tool_selector.cc:119:12

prissi

This looks very good to me. But I have to admid, that I never worked with the sripting part of simutrans. Could you provide files with a testing tool, please?

Also, I do not understand, where you actually define the name for each tool script. Or do they all need to be in a single file?

And, I think the crashing issue needs fixing before going into trunk. If you have a testing file, I could also help also testing it.

THLeaderH

I attached the testing files. The test script just prints very simple messages. I use an image of road as the icon of testing tool.

The name of tool is defined by "title" value in description.tab. If it is not defined, the directory name of tool is used instead.

Yona-TYT

This looks great, I will wait for it to be included in the trunk to do the tests, regards !. :D

Dwachs

Thanks! I will look into this crash. Why put the script-manager into bauer/ ?  script/ would be a more natural choice.
Parsley, sage, rosemary, and maggikraut.

THLeaderH

#14
This is because classes that have fill_menu() functions gather around bauer/ directory. way_builder_t and hausbauer_t are in bauer/, and script_tool_manager_t also has fill_menu() function.

UPDATE: The crash bug was due to not initializing tool_exec_script_t::script to NULL. Initialization solved the problem.

THLeaderH

#15
I attached the updated patch. This version includes the support of "restart" value and cursor image support. Although previous version used menu object for the icon image, this version uses a cursor pak object. image[0] of a cursor object is used as the cursor image, and image[1] is used as the icon image.

The patch contains code preparation for two-click tool , although I have not tested it at all. To enable two-click tool mode, you have to add

type=two_click

in description.tab. In two-click mode, following interfaces are available.

int is_valid_pos (player_x player, coord3d pos, coord3d start)
string do_work (player_x player, coord3d start, coord3d end)
void mark_tiles (player_x, player, coord3d start, coord3d end)

I will provide a script tool for testing.

Yona-TYT

Is it possible to use the selection mode, as seen in the image?.  :P

Dwachs

mark_tiles has to call some c++ routine to put the markers somewhere and assign an image to them. There is nothing in the script interface to support any marking (besides marking tiles).

Why did you delete these in simskin.cc:
static special_obj_tpl<skin_desc_t> const fakultative_objekte ?

@Yona: somthing like this should be possible.
Parsley, sage, rosemary, and maggikraut.

THLeaderH

I attached the updated patch. Two click tool was supported and some bugs were solved. I attached the code patch file and testing scripts. pak128.zip contains the testing scripts, and test_cursor.zip contains the cursor pak file.
I write down here how to use this patch.

Directory Structure
A scripted tool consists of two files, tool.nut and description.tab. tool.nut is required and description.tab is optional, although it is highly recommended. They are put at pakXXX/tool/<script_name>/ . Attached pak128.zip has this directory structure, where script_names are test_tool and test_two_click.

Please note that script/tool_base.nut at the simutrans executable directory must be placed to make tools work properly. The code patch contains this file tool.

description.tab
The code below is a sample of description.tab.

title=tool for test
type=one_click
restart=1
menu=my_script
icon=one_click_test


  • title: The title of this scripted tool. It is used as the tooltip text.
  • type: It defines the tool type. one_click means one click tool, and two_click means two click tool. The default is one_click.
  • restart: restart=1 means the tool always reloads the script when the tool is exited. If restart=0, the tool vm is not disposed and the state of the tool is preserved until simutrans is terminated.
  • menu: The key to call this tool in menuconf.tab. The value does not have to be unique across scripted tool definitions.
  • icon: It defines the cursor and icon image. For this example, cursor image is image[0] of cursor.one_click_test.pak, and icon image is image[1] of that pak.

How to call the scripted tools
There are two ways to call scripted tools. The first one is using the script loading window, which is assigned to dialog_tool[34]. When the tool is called with this dialog, the script is always reloaded regardless of "restart" value which is defined in description.tab.

The second way is using toolbars. The toolbar definition in menuconf.tab is required. For example, adding

toolbar[4][10]=scripts(my_script)

in menuconf.tab shows all scripted tools whose "menu" value is "my_script" at toolbar[4][10]. Please note that "icon" value needs to be properly set to show the designated icon of the scripted tools.

Script interfaces for scripted tools
When "type" value is one_click, the tool is one click mode and following interfaces are available. The meaning of arguments and return values are same as those of the c++ code.

  • bool init (player_x player)
  • bool exit (player_x player)
  • string work (player_x player, coord3d pos)
  • string check_pos (player_x player, coord3d pos)

When "type" value is two_click, the tool is two click mode and following interfaces are available. The meaning of arguments and return values are same as those of the c++ code.

  • int is_valid_pos (player_x player, coord3d pos, coord3d start)
  • string do_work (player_x player, coord3d start, coord3d end)
  • void mark_tiles (player_x, player, coord3d start, coord3d end)

These functions are called when the scripted tool is launched, exited, clicked, and so on. Implementing any of these functions is optional.

Known issues

  • is_valid_pos() cannot return error messages. It can only return state number.
  • Scripted tools cannot be called when simutrans is in network mode. This problem is to be fixed.

@Dwachs
Deleting fakultative_objekte was my mistake and was restored in the attached patch. skinverwaltung_t::register_desc() was slightly modified to support extra cursor pak objects.

Dwachs

Thannk you! It should be possible to make this safe for use in network games.
Parsley, sage, rosemary, and maggikraut.

Yona-TYT

I would like to make a tool to mark the places to build stops / stations in a city.

Currently I use Script text links from the scenario window, and put the coordinates "start" and "end" manually.

In addition to marking, it would also be a good idea for all stops to be built with just one click, since having the coordinates is very easy to do.

I attach the script.

Isaac Eiland-Hall

I wish it showed the coverage area, but still looks nice and useful. :) But with wishes, being able to move the planned stops (i.e. adjust them after clicking) seems hand as well. :)

Yona-TYT

Quote from: Isaac.Eiland-Hall on May 13, 2020, 12:13:05 AMI wish it showed the coverage area, but still looks nice and useful. :) But with wishes, being able to move the planned stops (i.e. adjust them after clicking) seems hand as well
I can do something like a menu of options (improvised), so that the player can choose the name or type of stop he wants to place, and if they can be replaced very easily.

Yona-TYT

I created menus improvised earlier in the script for the regions.  :P



Double message sorry.  ;D

THLeaderH

@Dwatchs
I am seeing around the script behavior in network mode. In network mode, the script itself seems to be executed only on server, even if it is called from another client. Is it intended?

Also, when I called command_x.set_slope() function in the network mode, I received "Cannot call this tool from within ~ " error.

In the latest version of my patch, init(), work(), and exit() is properly called in server when I call one-click script tool. However, when I call two-click tool, only is_valid_pos() and do_work() is called in server. mark_tiles() should be called in client but it isn't, and that would be a problem.

Dwachs

I will look into this. Thanks for your work!
Parsley, sage, rosemary, and maggikraut.

Yona-TYT

#26
This is my idea for a script tool:

- Now I use the coordinates of the city limits as "start" and "end" automatically just by selecting a town hall.
- Now you can select the stop for your construction automatically.
What do you think ?.  :D

I attach the script for the curious hehehe.  :P
Edit.I forgot, it should also work on any pakset.  8)


THLeaderH

I made a fix and attached the patch. I fixed the call type of script functions. work() and do_work() should be script_vm_t::QUEUE and the rest of tool interfaces are script_vm_t::FORCE.

And I noticed the following phenomena.

  • The result of print() function is not shown on the console when simutrans is in network mode and is not a server.
  • When I write multiple command_x instructions in a single work() function and simutrans is in network mode, only the first command_x instruction is executed and the rest are suspended, but they are not called anymore.

Dwachs

I started working on the patch (the one against r9080).

In the current implementation, the script is not executed on the client but on the server, because is_work_network_safe returns false. Changing this to true would not help too much. There is infrastructure missing to wakeup the script after the command gots executed (for AI players this is done automatically since they have step() called frequently).

Also setting work to QUEUE and other functions to FORCE might not work: The queued script waits for response from server. The forced function might overtake the suspended call, which could lead to strange misbehavior.

Edit: I will move the scripted tools to their own files. Editing simtools.h is a pain, because half of the project is recompiled after a change to this file.
Also the check_pos function is not needed: It is there for regular tools to throw (quickly) an error message to the user before sending it over network and doing more comprehensive checks in work().

Also I would like to get the patch to the state, where the script is only executed on the client (and need only be present there), and only game changing calls are sent over network. (In principle, this works for AI players. They are limited to server because of missing possibility to save and reload their state at clients)
Parsley, sage, rosemary, and maggikraut.

THLeaderH

Thank you for working on this. I agree with eliminating check_pos() function and making scripts themselves executed only on the client.

THLeaderH

@Dwatchs
Is there any progress in the implementation?

Dwachs

Yes, I am working on this. Both kind of tools work with network mode. I wnat to have a function that enables mark_tiles to place real markers.
Parsley, sage, rosemary, and maggikraut.

Yona-TYT

Quote from: Dwachs on May 24, 2020, 08:52:40 AMYes, I am working on this. Both kind of tools work with network mode. I wnat to have a function that enables mark_tiles to place real markers.
Very nice  8) .

prissi

THis would be a shame to bot make it into the next release. How is the status of this?

Yona-TYT

and why not include the work until now?.