[1.7.13] yet another getting started with lua thread
Moderator: Forum Moderators
[1.7.13] yet another getting started with lua thread
I have these files (see below) and I expect them to result in:
- on every "new turn" display a message that says "Hello world!"
Instead they result in:
- The scenario loads, I see the map and all the sides (the map-file has starting positions for 9 sides so I don't need any in the scenario-file) and then:
- on every "new turn" display a message that says "Hello world!"
Instead they result in:
- The scenario loads, I see the map and all the sides (the map-file has starting positions for 9 sides so I don't need any in the scenario-file) and then:
stderr wrote: error scripting/lua: /usr/local/share/wesnoth-dev/data/lua/helper.lua:126: bad argument #2 to 'set_variable' (WML table or scalar expected, got table)
stack traceback:
: in function 'set_variable'
/usr/local/share/wesnoth-dev/data/lua/helper.lua:126: in function set_variable_proxy'
/usr/local/share/wesnoth-dev/data/lua/helper.lua:157: in function </usr/local/share/wesnoth-dev/data/lua/helper.lua:151>
[string "wlp_utils = wesnoth.require '~add-ons/Wesno..."]:1: in main chunk
error scripting/lua: .../tsr/.wesnoth-dev/data/add-ons/WCE2/lua/prestart.lua:4: attempt to call field 'message' (a nil value)
stack traceback:
.../tsr/.wesnoth-dev/data/add-ons/WCE2/lua/prestart.lua:4: in main chunk
[C]: in function 'dofile'
[string " wesnoth.dofile "~add-ons/WCE2/lua/prestart..."]:1: in main chunk
[/quote]
Otoh if I reload the preload-lua-file from within the scenario I don't get the first error-message above, doing the same for the prestart.lua-file still gives the second error.
In my trial and error efforts I managed to get it right once, but now I can't remember what I did to make it happen. It is probably something extremely simple. Please consider not only pointing out what I did wrong but why it is wrong and why the correct solution is correct.
/tsr
ps. I will try to write up some beginners guide to lua after coding this, so hopefully the one answering this (and I'm looking at you silene ;)) wont have to anser the same question again and again...
Files:
[quote="~add-ons/WCE2/_main.cfg"]
#ifdef MULTIPLAYER
{~add-ons/WCE2/scenarios}
#endif
[/quote]
[quote="~add-ons/WCE2/scenarios/WCE_main.cfg"]
[multiplayer]
id=WCE_Main
name= _ "Wesnoth Collaborative Editor"
map_data="{~add-ons/WCE2/maps/WCE_Main.map}"
description="Edit Wesnoth maps together with other players in a multiplayer 'game'"
random_start_time="no"
{DEFAULT_SCHEDULE}
{DEFAULT_MUSIC_PLAYLIST}
[event]
name=preload
first_time_only=no
[lua]
code = << wesnoth.dofile "~add-ons/WCE2/lua/preload.lua" >>
[/lua]
[/event]
[event]
name=prestart
[lua]
code = << wesnoth.dofile "~add-ons/WCE2/lua/prestart.lua" >>
[/lua]
[/event]
[/multiplayer]
[/quote]
[quote="~add-ons/WCE2/lua/preload.lua"]
H = wesnoth.require "lua/helper.lua"
W = H.set_wml_action_metatable {}
H.set_wml_var_metatable(_G)
[/quote]
[quote="~add-ons/WCE2/lua/prestart.lua"]
T = H.set_wml_tag_metatable {}
W.event { name = "new turn", T.message { speaker = "narrator", message = "Hello world!" } }
[/quote]
Re: [1.7.13] yet another getting started with lua thread
When you call set_wml_var_metatable on _G, you cause Lua to consider all the accesses to global variables as accesses to WML variables. As a consequence, when you later assign T, you are asking the engine to store into a WML variable called T something that doesn't make sense to it. Hence your issue.
There are three ways of fixing it.
There are three ways of fixing it.
- Don't touch _G, and access WML variables through an explicit variable (e.g. "V.something").
Code: Select all
V = H.set_wml_var_metatable {}
- Move the definition of T in the preload file before touching _G.
- Declare T as a variable local to the prestart file.
Code: Select all
local T = H.set_wml_tag_metatable {}
Re: [1.7.13] yet another getting started with lua thread
Hmm... now that I think about it, writing a complete scenario in Lua will probably cause a lot of troubles. I think your way of structuring things will not scale well. First of all, you should put all your Lua code in the preload event; everything else will probably force you to write Lua code as if it was just WML, which is kind of pointless.
Then your scenario1.lua file will look like the following. (Disclaimer: completely untested.)
Code: Select all
[event]
name=preload
first_time_only=no
[lua]
code = << wesnoth.dofile '~add-ons/WCE2/lua/scenario1.lua' >>
[/lua]
[/event]
[event]
name=prestart
[lua]
code = "init()"
[/lua]
[/event]
Code: Select all
local H = wesnoth.require "lua/helper.lua"
local W = H.set_wml_action_metatable {}
local T = H.set_wml_tag_metatable {}
H.set_wml_var_metatable(_G)
local function register_event(name, fun, filter)
-- this function should be improved to handle other event setups
local cfg = { name = name, first_time_only = false, T.lua { code = fun .. "(...)" } }
if filter then cfg[2] = { "filter", filter } end
W.event(cfg)
end
function hello_world(cfg)
-- the cfg parameter would be useful for accessing things like x1, y1, ... if needed
W.message { speaker = "narrator", message = "Hello world!" }
end
function init(cfg)
register_event("new turn", "hello_world")
-- register all the other events of the scenario...
end
-
- Inactive Developer
- Posts: 2461
- Joined: August 15th, 2008, 8:46 pm
- Location: Germany
Re: [1.7.13] yet another getting started with lua thread
Just in case that the code you've posted (silene) is way too much - for me at least...
@tsr
Do you have a specific reason for using this stuff
?
Since I honestly still don't understand the difference between table, wml table, userdata, __cfg, __literal, __parsed, "dump", "proxy table" and such, I don't care about "metatable". Complex lua scripts/functions seem quite possible without "metatable"s.
If you want a "hello world" program that's as simple as possible only this should be enough (You don't need a preload event!):
...and I'd advise everyone to start with such small manageable things...
@tsr
Do you have a specific reason for using this stuff
Code: Select all
W = H.set_wml_action_metatable {}
H.set_wml_var_metatable(_G)
Since I honestly still don't understand the difference between table, wml table, userdata, __cfg, __literal, __parsed, "dump", "proxy table" and such, I don't care about "metatable". Complex lua scripts/functions seem quite possible without "metatable"s.
If you want a "hello world" program that's as simple as possible only this should be enough (You don't need a preload event!):
Code: Select all
[event]
name=new turn
first_time_only=no
[lua]
code= << wesnoth.message("Hello world!") >>
[/lua]
[/event]
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml starters • Plan Your Advancements: mp mod
The Earth's Gut: sp campaign • Settlers of Wesnoth: mp scenario • Wesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
A Simple Campaign: campaign draft for wml starters • Plan Your Advancements: mp mod
The Earth's Gut: sp campaign • Settlers of Wesnoth: mp scenario • Wesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
Re: [1.7.13] yet another getting started with lua thread
Anonymissimus wrote:Since I honestly still don't understand the difference between table, wml table, userdata, __cfg, __literal, __parsed, "dump", "proxy table" and such,
- A table is a native Lua type, which is a dictionary (aka a map).
- A wml table is just a Lua table with additional restrictions, so that it can represent/describe WML objects.
- A userdata is a native Lua type, which represents a C++ pointer. In other words, whenever you interact with a userdata, you are modifying the internal data structures of the Wesnoth engine.
As you know, wml objects that appear inside event handlers benefit from delayed variable substitution: formulas are evaluated only when the attributes are read, not when the objects are created. In Lua, these read-only objects are represented by userdata, which provide two special fields: __literal and __parsed. Again, these fields produce a copy of the internal data structure as a wml table. The first field does not perform variable substitution (so the attributes in the copy still contain formulas), while the second one does. If you have understood what the delayed_variable_substitution attribute of the [event] tag does, then you can assume the behavior is exactly the same for the __literal and __parsed attributes.
Metatables are a property that can be set on Lua objects, in particular on tables and userdatas. They describe the behavior of these objects. For instance, it's a metatable that says that a unit has a __cfg field and that it dumps the content of the internal data.Anonymissimus wrote:I don't care about "metatable". Complex lua scripts/functions seem quite possible without "metatable"s.
As for the metatables in the helper file, they are just syntactic sugar. (Contrarily to the ones provided for the userdata, the ones in the helper file are written in Lua, so they don't provide any new feature.) They are just here to simplify the usage of a few things. For instance, the following three scripts are strictly equivalent:
Code: Select all
wesnoth.set_variable("counter", 1 + wesnoth.get_variable "counter")
--
local V = helper.set_wml_var_metatable {}
V.counter = 1 + V.counter
--
helper.set_wml_var_metatable(_G)
counter = 1 + counter
Re: [1.7.13] yet another getting started with lua thread
Thanks silene, your code worked, but I want to go the pointless routesilene wrote:... force you to write Lua code as if it was just WML, which is kind of pointless.
(imho it has merit since it accomplishes two things. First I can edit my WML without having to go back to the main menu and F5, second it allows me to write WML-heavy scenarios without slowing down the initial loading of wesnoth since lua files are dealt with at run time - if I understand correctly)
Anyhow, this is the path I've chosen for now, and I need a little bit of help again...
Compare:
Code: Select all
[event]
name=turn 2
[replace_map]
map="{~add-ons/WCE2/user_maps/test.map}"
expand=yes
shrink=yes
[/replace_map]
[/event]
Code: Select all
wesnoth.fire (
"event",
{
name="turn 3",
{"replace_map",
{
map="{~add-ons/WCE2/user_maps/test.map}",
shrink=true,
expand=true
}
}
}
)
What is it I don't understand?stderr wrote: error general: An error due to possibly invalid WML occurred
The error message is :
A map without a header is not supported
When reporting the bug please include the following error message :
Condition '!(header_offset == std::string::npos || comma_offset < header_offset)' failed at src/map.cpp:163 in function 'read'.
(if I paste the contents of 'test.map' directly into the lua file (quoting with [[...]]) it works, but since the idea is to load maps dynamically I can't really do that)
/tsr
Re: [1.7.13] yet another getting started with lua thread
In case it wasn't clear, it means you won't be able to use any feature of Lua. You are just using it as a preprocessor. For instance it will be impossible to write a "if then else".tsr wrote:but I want to go the pointless route
When using [replace_map] on a map file, it is the preprocessor that is responsible for loading the file. In your case, the preprocessor is not run (it wouldn't make sense to run it on a non-WML file). There are many solutions, all of them involve writing some WML. For instance, you could add the following at the beginning of your prestart eventtsr wrote:What is it I don't understand?
Code: Select all
[set_variable]
name = "test_map"
value = "{~add-ons/WCE2/user_maps/test.map}"
[/set_variable]
Code: Select all
{
map = wesnoth.get_variable "test_map",
shrink = true,
expand = true
}
Re: [1.7.13] yet another getting started with lua thread
Thanks for your time silene, things are becoming clearer and clearer for me.
Soon I think I'll have a basic grasp of this.
(For the record I ended up creating an index file for the maps in WML since the only way to get the WML-preprocessor to parse file is by naming them explicitly and using wesnoth.dofile in Lua required users to edit their maps (adding 'lua_map = [[' in the beginning and ']]' in the end since Lua chokes on map-files otherwise)
Other than that it is working pretty well, I'm currently using Lua to write WML that includes Lua that includes WML... fun!
The 'why?' can't really be answered in any other way than "cause I can!".
/tsr
Soon I think I'll have a basic grasp of this.
(For the record I ended up creating an index file for the maps in WML since the only way to get the WML-preprocessor to parse file is by naming them explicitly and using wesnoth.dofile in Lua required users to edit their maps (adding 'lua_map = [[' in the beginning and ']]' in the end since Lua chokes on map-files otherwise)
Other than that it is working pretty well, I'm currently using Lua to write WML that includes Lua that includes WML... fun!
The 'why?' can't really be answered in any other way than "cause I can!".
/tsr