News:

Want to praise Simutrans?
Your feedback is important for us ;D.

[code example] Script tool with window

Started by Yona-TYT, October 27, 2022, 12:27:50 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Yona-TYT

I'm trying to implement a kind of tool that works in combination with an info window that allows simple data to be sent to be used in the current script tool.

For example look at the image:

 Captura desde 2022-10-27 14-02-41.png

  • This script should automatically build stops in a city.
  • The window displays a list of available bus stops for the player to choose the stop they need in the city.
  • When clicked, the link script sends an index that will be interpreted by the script tool and thus build the stop chosen by the player.

It seems to me that this is a very useful type of tool, unfortunately I have not been able to make it work, I cannot send the information from the window to the tool in use (All my attempts have made simutrans crash). :-[

prissi

You cannot call the scripting from the window directly. Instead the window has to call a tool. So you need to define a tool with a custom parameter, and the window then need to call this tool as action. (In your case the bus stop building tool with a matching string).

welt->get_scenario()->eval_string(func) cannot be used in windows.

Yona-TYT

@Prissi  I really appreciate your reply . :D

Quote from: prissi on October 27, 2022, 12:53:33 AMSo you need to define a tool with a custom parameter, and the window then need to call this tool as action. (In your case the bus stop building tool with a matching string).
You mean to run the tool from the window ?. I was thinking of sending simple data with a link script (script:) tag and then running the tool when the map is clicked.
The window itself will not execute the tool, it will only send the data that will tell the script tool what action to take (in this case the information  to build  stop).

Dwachs

Do I understand correctly: You want to implement a new window dialog that can act as one of the tabs of the scenario info window?

@prissi: I think the code does support calling tools from windows. That should be possible with a-tags in the flowtext with "href=script: ...". The patch will not work if there is no scenario to begin with.
Parsley, sage, rosemary, and maggikraut.

prissi

GUI windows will be handled asynchronous to your script. This was needed, because Android is soo weak that the GUI update must run independent from the main game step() in a sync_step() and thus must not directly change the game state.

Therefore, calls from windows to tools that affect the game state are queued for the next karte_t::step() where all changes to the game state and then processed in the same order (important for client on network games).

Thus, calling script from sync_step() would be only save for game state preserving functions. And I thought calls from sync_step() could even mess up the squirrel state? Execution of a script from a GUI window should rather requires a tool that calls the script at the next step. Not hard to to, but currently not there.

Yona-TYT

Quote from: Dwachs on October 27, 2022, 06:29:01 AMDo I understand correctly: You want to implement a new window dialog that can act as one of the tabs of the scenario info window?
Yes, it acts like a scenario info window tab, but allows decisions to be made that affect the script tool.


Yona-TYT


The classes "class exec_script_base_t", "tool_exec_script_t" and "tool_exec_two_click_script_t" initialize the script tools and call the API functions, the problem is that calling an API function from any of these classes causes a crash in simutrans.

For example:
void exec_script_base_t::step(player_t* player)
{

if (script) {
script->call_function(script_vm_t::QUEUE, "step");

if (tool_exec_two_click_script_t *tt = dynamic_cast<tool_exec_two_click_script_t*>(this)) {
if (tt->needs_call_to_init) {
tt->init(player);
}
}
}
}


Running this from the window will crash simutrans unless you skip "script" and "script->call_function(script_vm_t::QUEUE, "step");".



Dwachs

You get this crash because script == NULL.
Parsley, sage, rosemary, and maggikraut.

prissi

You need a new tool which is not init safe and calls in its init function
welt->get_script("") with the parameter passed as default parameter of that tool when clicked on a hreaf=script object.

That tool needs to be initialized in the window routine and then welt->set_tool() etc.

The most flexible approach would be a href=tool,catg,number,default_string which would be even processed by help files, and would be able to call any tool. Thus the help file on removal tool would be able to call the remover and way remover etc. directly from the text. That would also call directly the bus stop building ...

Yona-TYT

Quote from: Dwachs on October 27, 2022, 11:48:55 AMYou get this crash because script == NULL.
I'm afraid not, I tried checking if script = NULL, but it turns out that just trying to read the script variable simutrans is crashed.

I have delved a bit and I think that in this way simutrans does not crash (although I must test well):
tool_t *tool = welt->get_tool(player->get_player_nr());

if (tool_exec_script_t* ot = dynamic_cast<tool_exec_script_t*>(tool)) {
one_click_script_tools.append(ot);
}
else if (tool_exec_two_click_script_t* tt = dynamic_cast<tool_exec_two_click_script_t*>(tool)) {
two_click_script_tools.append(tt);
}

for(uint32 i=0; i<one_click_script_tools.get_count(); i++) {
tool_exec_script_t* tool = one_click_script_tools[i];
tool->step(welt->get_active_player());
}

for(uint32 i=0; i<two_click_script_tools.get_count(); i++) {
tool_exec_two_click_script_t* tool = two_click_script_tools[i];
tool->step(welt->get_active_player());
}

Yona-TYT

#10
Here is an update: (It works surprisingly well)

Code: Not a valid attachment ID.

Script Test: test_tool_yona.zip

Captura desde 2022-10-27 14-02-41.png

Dwachs

Nice :)

Some suggestions:

1) The tool should just open the window. You do not need these one/two-click tools in the window class, just a pointer to the script.

2) The destroy_win call can be put in the exit-method of the tool.

3) If the window is destroyed then the active tool should be reset to the query tool (in the window destructor).

4) If info->window is true then info->restart has to be false (I think).
Parsley, sage, rosemary, and maggikraut.

Yona-TYT

Thank you very much for your suggestions!.  :D

https://github.com/Yona-TYT/simutrans/commit/702f197eb663d8fbe532d4cc732617a2fd14caa2

Quote from: Dwachs on October 27, 2022, 07:15:16 PM1) The tool should just open the window. You do not need these one/two-click tools in the window class, just a pointer to the script.
I'm not sure how to resolve this, my attempts to call "script" from the window ended in crashes.  :-[

Quote from: Dwachs on October 27, 2022, 07:15:16 PM2) The destroy_win call can be put in the exit-method of the tool.
Certainly now it's fixed!.

Quote from: Dwachs on October 27, 2022, 07:15:16 PM3) If the window is destroyed then the active tool should be reset to the query tool (in the window destructor).
i think i fixed this too.

Quote from: Dwachs on October 27, 2022, 07:15:16 PM4) If info->window is true then info->restart has to be false (I think).
I don't think this is a problem, from what I saw "restart" is not initializing (or at least not where it should) look at the code and you will see that it is nowhere:  ???

line 46, src/simutrans/script/script_tool_manager.cc

if (  file.open(buf)  ) {
tabfileobj_t contents;
file.read( contents );
info->title    = translator::translate(contents.get_string("title", path));
info->menu_arg = contents.get_string("menu", "");
info->tooltip  = translator::translate(contents.get_string("tooltip", ""));
info->cursor_area = contents.get_koord("cursor_area", koord(1,1));
info->cursor_offset = contents.get_koord("cursor_offset", koord(0,0));
if(  info->cursor_area.x<1  ||  info->cursor_area.y<1  ) {
dbg->warning("script_tool_manager_t::get_script_info()", "The cursor area of scripted tool %s must have at least 1 tile length. Sanitized to (1,1).", info->title.c_str());
info->cursor_area = koord(1,1);
}

const char* skin_name = contents.get_string("icon", "");
info->desc   = skinverwaltung_t::get_extra(skin_name, strlen(skin_name), skinverwaltung_t::cursor);
info->is_one_click = !( strcmp(contents.get_string("type", "one_click"), "two_click")==0 );
info->window = contents.get_int("window", false);
}



Yona-TYT

There is also a crash using "tool_t" ,

I suspect this is a bug. I was testing this:
player_t *player = welt->get_active_player();
tool_t *tool = welt->get_tool(player->get_player_nr());
if( tool && tool->exit(player) ){
//destroy_win(this);
NULL;
}

Tracking points to (simtool-scripted.cc:343):
script->prepare_callback("exec_script_base_work_callback", 3, (exec_script_base_t*)this, player, (const char*)"");
gdb:
Thread 1 "simutrans" received signal SIGSEGV, Segmentation fault.
0x00005555558cd627 in script_vm_t::intern_prepare_pending_callback (this=this@entry=0x0, function=function@entry=0x5555559fe150 "exec_script_base_work_callback", nret=nret@entry=3) at /home/casa/github/simutrans/src/simutrans/script/script.cc:578
578 BEGIN_STACK_WATCH(vm);
(gdb) where
#0  0x00005555558cd627 in script_vm_t::intern_prepare_pending_callback(char const*, int)
    (this=this@entry=0x0, function=function@entry=0x5555559fe150 "exec_script_base_work_callback", nret=nret@entry=3)
    at /home/casa/github/simutrans/src/simutrans/script/script.cc:578
#1  0x0000555555914084 in script_vm_t::prepare_callback<exec_script_base_t*, player_t*, char const*>(char const*, int, exec_script_base_t* const&, player_t* const&, char const* const&) (nret=3, function=0x5555559fe150 "exec_script_base_work_callback", this=0x0)
    at /home/casa/github/simutrans/src/simutrans/tool/../tool/../script/script.h:143
#2  tool_exec_two_click_script_t::do_work(player_t*, koord3d const&, koord3d const&)
    (this=0x555556d03cf0, player=<optimized out>, start=..., end=...)
    at /home/casa/github/simutrans/src/simutrans/tool/simtool-scripted.cc:343
#3  0x000055555590d434 in two_click_tool_t::work(player_t*, koord3d) (this=0x555556d03cf0, player=0x55556f392e00, pos=...)
    at /home/casa/github/simutrans/src/simutrans/tool/simmenu.cc:1374
#4  0x000055555597aef6 in karte_t::call_work_api(tool_t*, player_t*, koord3d, bool&, bool)
    (this=0x55556f725d90, tool=tool@entry=0x555556d03cf0, player=player@entry=0x55556f392e00, pos=..., suspended=@0x7fffffffb71f: false, called_from_api=called_from_api@entry=false) at /home/casa/github/simutrans/src/simutrans/world/simworld.cc:5725
#5  0x00005555558f91c2 in interaction_t::interactive_event(event_t const&) (this=this@entry=0x5555705d0720, ev=...)
    at /home/casa/github/simutrans/src/simutrans/network/../world/simworld.h:1213
#6  0x00005555558f930e in interaction_t::process_event(event_t&) (this=this@entry=0x5555705d0720, ev=...)
    at /home/casa/github/simutrans/src/simutrans/siminteraction.cc:360
#7  0x00005555558f9596 in interaction_t::check_events() (this=0x5555705d0720)
    at /home/casa/github/simutrans/src/simutrans/siminteraction.cc:398
#8  0x000055555597322f in karte_t::sync_step(unsigned int) (this=0x0, delta_t=<optimized out>, delta_t@entry=41)
    at /home/casa/github/simutrans/src/simutrans/world/simworld.cc:2682
#9  0x00005555558f987b in interrupt_check(char const*) (caller_info=caller_info@entry=0x5555559eb232 "0")



Dwachs

I suspect this is similar to another bug report (crash in test scenario when loading another scenario). It seems the script got interrupted by sync_check, then the exit-method kills the script, execution returns to the script  (which is now invalid pointer) and boom.

Not sure how to fix/debug it.
Parsley, sage, rosemary, and maggikraut.

Yona-TYT

Quote from: Dwachs on November 02, 2022, 07:10:12 AMI suspect this is similar to another bug report (crash in test scenario when loading another scenario). It seems the script got interrupted by sync_check, then the exit-method kills the script, execution returns to the script  (which is now invalid pointer) and boom.

Not sure how to fix/debug it.
Do you think this could be due to a bug in the Squirrel API?... Maybe we should try older versions of "squirrel" and see if we have any luck.

Dwachs

This has nothing to do with squirrel itself. The crash in the other thread happened because the script-vm was destroyed while running a script, and after vm destruction simutrans attempted to continue to run the script on the destroyed vm -> crash.

What happened in your case might be the same type of crash or something related to the patch itself. What kind of actions did you do to get the crash? (I would to do some debugging, but have not much spare time this week)
Parsley, sage, rosemary, and maggikraut.

prissi

I think these tools still need to be called via world()->set_tool() otherwise they will be not sent to the server and will crash with asynchronous execution.

get the tool (or call new tool_t)
set default parameter accordingly
welt->set?tool(t,...)

Like for any other tool. The oatch still call the tools directly, which is not allowed.

Yona-TYT

Quote from: prissi on November 02, 2022, 11:56:00 AMI think these tools still need to be called via world()->set_tool() otherwise they will be not sent to the server and will crash with asynchronous execution.

I have tried uasr "world()->get_tool..." but it doesn't make any difference I'm afraid.
player_t *player = welt->get_active_player();
tool_t *tool = world()->get_tool(player->get_player_nr());
if( tool && tool->exit(player) ){
//destroy_win(this);
NULL;
}

Thread 1 "simutrans" received signal SIGSEGV, Segmentation fault.
0x00005555558cd627 in script_vm_t::intern_prepare_pending_callback (this=this@entry=0x0, function=function@entry=0x5555559fe150 "exec_script_base_work_callback", nret=nret@entry=3) at /home/casa/github/simutrans/src/simutrans/script/script.cc:578
578 BEGIN_STACK_WATCH(vm);
(gdb) where
#0  0x00005555558cd627 in script_vm_t::intern_prepare_pending_callback(char const*, int)
    (this=this@entry=0x0, function=function@entry=0x5555559fe150 "exec_script_base_work_callback", nret=nret@entry=3)
    at /home/casa/github/simutrans/src/simutrans/script/script.cc:578
#1  0x0000555555914084 in script_vm_t::prepare_callback<exec_script_base_t*, player_t*, char const*>(char const*, int, exec_script_base_t* const&, player_t* const&, char const* const&) (nret=3, function=0x5555559fe150 "exec_script_base_work_callback", this=0x0)
    at /home/casa/github/simutrans/src/simutrans/tool/../tool/../script/script.h:143
#2  tool_exec_two_click_script_t::do_work(player_t*, koord3d const&, koord3d const&)
    (this=0x555556d14f50, player=<optimized out>, start=..., end=...)
    at /home/casa/github/simutrans/src/simutrans/tool/simtool-scripted.cc:343
#3  0x000055555590d434 in two_click_tool_t::work(player_t*, koord3d) (this=0x555556d14f50, player=0x55556f3b5320, pos=...)
    at /home/casa/github/simutrans/src/simutrans/tool/simmenu.cc:1374
#4  0x000055555597aef6 in karte_t::call_work_api(tool_t*, player_t*, koord3d, bool&, bool)
    (this=0x55556f73eda0, tool=tool@entry=0x555556d14f50, player=player@entry=0x55556f3b5320, pos=..., suspended=@0x7fffffffb71f: false, called_from_api=called_from_api@entry=false) at /home/casa/github/simutrans/src/simutrans/world/simworld.cc:5725
#5  0x00005555558f91c2 in interaction_t::interactive_event(event_t const&) (this=this@entry=0x55556f73a140, ev=...)
    at /home/casa/github/simutrans/src/simutrans/network/../world/simworld.h:1213
#6  0x00005555558f930e in interaction_t::process_event(event_t&) (this=this@entry=0x55556f73a140, ev=...)
    at /home/casa/github/simutrans/src/simutrans/siminteraction.cc:360
#7  0x00005555558f9596 in interaction_t::check_events() (this=0x55556f73a140)
    at /home/casa/github/simutrans/src/simutrans/siminteraction.cc:398
#8  0x000055555597322f in karte_t::sync_step(unsigned int) (this=0x0, delta_t=<optimized out>, delta_t@entry=40)
    at /home/casa/github/simutrans/src/simutrans/world/simworld.cc:2682
#9  0x00005555558f987b in interrupt_check(char const*) (caller_info=caller_info@entry=0x5555559eb232 "0")

Yona-TYT

Quote from: prissi on November 02, 2022, 11:56:00 AMI think these tools still need to be called via world()->set_tool() otherwise they will be not sent to the server and will crash with asynchronous execution.

get the tool (or call new tool_t)
set default parameter accordingly
welt->set?tool(t,...)

Like for any other tool. The oatch still call the tools directly, which is not allowed.

How clumsy of my parter, I thought you said "get_tool". 

I suppose that this may be the correct approach to implement this window-like tool, however in order to move forward we still have to solve that possible error with script-vm. ;)

Otherwise I will only get shocks and more shocks in simutrans. :o

prissi

The tools with welt->set_tool() will called later in the karte_t::step() when the script machine is suspended, and it is save. For this the tool needs to be marked as altering the game state.

Yona-TYT

Quote from: Dwachs on November 02, 2022, 11:15:45 AMWhat happened in your case might be the same type of crash or something related to the patch itself. What kind of actions did you do to get the crash? (I would to do some debugging, but have not much spare time this week)

I have done tests to rule out that it is a bug with the patch, so I run "get_tool" from elsewhere, I have noticed that simutrans only fails if it is specifically a "script tool", so this confirms that it is a bug.

Test: player_t *play = welt->get_active_player();
tool_t *tool = world()->get_tool(play->get_player_nr());
if( tool && tool->exit(player) ){
//destroy_win(this);
NULL;
}

When using a two-click tool the crash occurs on the second click.

gdb for one click tool
Thread 1 "simutrans" received signal SIGSEGV, Segmentation fault.
0x00005555558cd627 in script_vm_t::intern_prepare_pending_callback (this=this@entry=0x0, function=function@entry=0x5555559fe150 "exec_script_base_work_callback", nret=nret@entry=3) at /home/casa/github/simutrans/src/simutrans/script/script.cc:578
578 BEGIN_STACK_WATCH(vm);
(gdb) where
#0  0x00005555558cd627 in script_vm_t::intern_prepare_pending_callback(char const*, int)
    (this=this@entry=0x0, function=function@entry=0x5555559fe150 "exec_script_base_work_callback", nret=nret@entry=3)
    at /home/casa/github/simutrans/src/simutrans/script/script.cc:578
#1  0x0000555555913489 in script_vm_t::prepare_callback<exec_script_base_t*, player_t*, char const*>(char const*, int, exec_script_base_t* const&, player_t* const&, char const* const&) (nret=3, function=0x5555559fe150 "exec_script_base_work_callback", this=0x0)
    at /home/casa/github/simutrans/src/simutrans/tool/../tool/../script/script.h:143
#2  tool_exec_script_t::work(player_t*, koord3d) (this=0x5555568d1f50, player=<optimized out>, pos=...)
    at /home/casa/github/simutrans/src/simutrans/tool/simtool-scripted.cc:268
#3  0x000055555597aef6 in karte_t::call_work_api(tool_t*, player_t*, koord3d, bool&, bool)
    (this=0x55556f728bd0, tool=tool@entry=0x5555568d1f50, player=player@entry=0x55556f399400, pos=..., suspended=@0x7fffffffb71f: false, called_from_api=called_from_api@entry=false) at /home/casa/github/simutrans/src/simutrans/world/simworld.cc:5725
#4  0x00005555558f91c2 in interaction_t::interactive_event(event_t const&) (this=this@entry=0x55556f723f50, ev=...)
    at /home/casa/github/simutrans/src/simutrans/network/../world/simworld.h:1213
#5  0x00005555558f930e in interaction_t::process_event(event_t&) (this=this@entry=0x55556f723f50, ev=...)
    at /home/casa/github/simutrans/src/simutrans/siminteraction.cc:360
#6  0x00005555558f9596 in interaction_t::check_events() (this=0x55556f723f50)
    at /home/casa/github/simutrans/src/simutrans/siminteraction.cc:398
#7  0x000055555597322f in karte_t::sync_step(unsigned int) (this=0x0, delta_t=<optimized out>, delta_t@entry=41)
    at /home/casa/github/simutrans/src/simutrans/world/simworld.cc:2682
#8  0x00005555558f987b in interrupt_check(char const*) (caller_info=caller_info@entry=0x5555559eb232 "0")
    at /home/casa/github/simutrans/src/simutrans/simintr.cc:114
#9  0x0000555555982166 in karte_t::interactive(unsigned int) (this=this@entry=0x55556f728bd0, quit_month=quit_month@entry=2147483647)
    at /home/casa/github/simutrans/src/simutrans/world/simworld.cc:6113

gdb for two click tool
Thread 1 "simutrans" received signal SIGSEGV, Segmentation fault.
exec_script_base_t::load_script (this=0x555556d04290, path=0x55556f990dc0 "/home/casa/simutrans/paksets/pak128/tool/test_tool_err", player=0x0) at /home/casa/github/simutrans/src/simutrans/tool/../player/simplay.h:241
241 sint8 get_player_nr() const {return player_nr; }
(gdb) where
#0  exec_script_base_t::load_script(char const*, player_t*)
    (this=0x555556d04290, path=0x55556f990dc0 "/home/casa/simutrans/paksets/pak128/tool/test_tool_err", player=0x0)
    at /home/casa/github/simutrans/src/simutrans/tool/../player/simplay.h:241
#1  0x0000555555914794 in exec_script_base_t::init_vm(player_t*) (this=this@entry=0x555556d04290, player=player@entry=0x0)
    at /home/casa/github/simutrans/src/simutrans/tool/../tool/../script/../utils/plainstring.h:38
#2  0x00005555559149b4 in tool_exec_two_click_script_t::init(player_t*) (this=0x555556d04230, player=0x0)
    at /home/casa/github/simutrans/src/simutrans/tool/simtool-scripted.cc:308
#3  0x0000555555913257 in two_click_tool_t::exit(player_t*) (player=0x0, this=0x555556d04230)
    at /home/casa/github/simutrans/src/simutrans/tool/../tool/../tool/simmenu.h:387
#4  tool_exec_two_click_script_t::exit(player_t*) (this=0x555556d04230, player=0x0)
    at /home/casa/github/simutrans/src/simutrans/tool/simtool-scripted.cc:328
#5  0x000055555582ebe2 in add_scenario_message(player_t*, char const*) (player=0x0, text=0x555572be0ff8 "image_id: 38696 pos 44,45,-1")
    at /home/casa/github/simutrans/src/simutrans/script/api/api_gui.cc:49
#6  0x000055555582ee84 in std::__invoke_impl<script_api::call_tool_work, script_api::call_tool_work (*&)(player_t*, char const*), player_t*, char const*>(std::__invoke_other, script_api::call_tool_work (*&)(player_t*, char const*), player_t*&&, char const*&&)
    (__f=<optimized out>) at /usr/include/c++/12.2.0/bits/invoke.h:61
#7  std::__invoke_r<script_api::call_tool_work, script_api::call_tool_work (*&)(player_t*, char const*), player_t*, char const*>(script_api::call_tool_work (*&)(player_t*, char const*), player_t*&&, char const*&&) (__fn=<optimized out>)
    at /usr/include/c++/12.2.0/bits/invoke.h:143
#8  std::_Function_handler<script_api::call_tool_work (player_t*, char const*), script_api::call_tool_work (*)(player_t*, char const*)>::_M_invoke(std::_Any_data const&, player_t*&&, char const*&&) (__functor=<optimized out>, __args#0=<optimized out>, __args#1=<optimized out>)
    at /usr/include/c++/12.2.0/bits/std_function.h:291
#9  0x000055555582f5ba in std::function<script_api::call_tool_work (player_t*, char const*)>::operator()(player_t*, char const*) const




Dwachs

Putting arbitrary code somewhere that crash the program is not a valid bug report  :o

What did you do to get the crash? It seem you call exit with player == NULL.

How can one reproduce the crash for an unpatched version?
Parsley, sage, rosemary, and maggikraut.

Yona-TYT

Quote from: Dwachs on November 02, 2022, 08:10:19 PMPutting arbitrary code somewhere that crash the program is not a valid bug report  :o
Certainly, but still I think that code shouldn't fail with script tools (with the other tools it works without problems), don't you think?, so this makes me suspect that there is something wrong and it has nothing to do with the previous patch.

Quote from: Dwachs on November 02, 2022, 08:10:19 PMWhat did you do to get the crash? It seem you call exit with player == NULL.

I also made sure that player was different from NULL.
player_t *play = welt->get_active_player();
tool_t *tool = world()->get_tool(play->get_player_nr());
if( play != NULL && tool ){
tool->exit(player);
}

Quote from: Dwachs on November 02, 2022, 08:10:19 PMHow can one reproduce the crash for an unpatched version?

I can't because there is no real condition where this is executed, so for my tests I add it in api_gui.cc and call it with a script function, however it doesn't seem to matter where it is used, it will always fail with a script tool.


This is for testing and debugging only.

call_tool_work add_scenario_message(player_t* player, const char* text)
{
player_t *play = welt->get_active_player();
tool_t *tool = world()->get_tool(play->get_player_nr());
if( play != NULL && tool ){
tool->exit(player);
}

// build param string (see tool_add_message_t::init)
cbuffer_t buf;
buf.printf("%d,%s", message_t::scenario, text);

return call_tool_work(TOOL_ADD_MESSAGE | GENERAL_TOOL, (const char*)buf, 0, player ? player : welt->get_active_player(), koord3d::invalid);
}

prissi

welt->set_tool() also takes care of the exit() calls. Please do not call tools directly from your code. Only karte_t is allowed to call init, work and exit functions (and of course the script VM)!