Page 1 of 1

LUA help

Posted: Thu Jan 21, 2021 8:37 pm
by cbadal
I'm working on an addon system for SWBFII consoles and have become stumped on an issue.
I have a function called 'ProcessAddons()'
Which checks if a addon file exists and then tries to read and run it.

I expected that each time I call 'ReadDataFile(addonFile)', that the script referred to as 'addon' would be replaced internally by the one in the latest lvl file.
But I am only seeing the code from the first addon be executed. I don't know why.
A solution is to name the scripts 'addon000', 'addon001' ... [Which is kinda lame, but it works.]
Anyone know of a 'less lame' way to make this work?

Code: Select all

function ProcessAddons()
	print("ProcessAddons: START ")
	local addonFile = ""
	for i=1,999, 1 do 
		addonFile = string.format("addon\\%03d\\addon.lvl", i)
		if( ScriptCB_IsFileExist(addonFile) == 1) then 
			print("adding: " .. addonFile )
			ReadDataFile(addonFile)
			ScriptCB_DoFile("addon")
			print("did file: " .. addonFile )
		end 
    end 
    print("ProcessAddons: DONE ")
end
There are 3 addons.lvl files at:
addon\001\addon.lvl > contents 1 script named 'addon' content => print("001")
addon\002\addon.lvl > contents 1 script named 'addon' content => print("002")
addon\003\addon.lvl > contents 1 script named 'addon' content => print("003")

------------- output --------------
ProcessAddons: START
adding: addon\001\addon.lvl
001
did file: addon\001\addon.lvl
adding: addon\002\addon.lvl
did file: addon\002\addon.lvl
adding: addon\003\addon.lvl
did file: addon\003\addon.lvl
ProcessAddons: DONE
-----------------------------------

Re: LUA help

Posted: Fri Jan 22, 2021 1:26 am
by MileHighGuy
I cant really think of anything... unless you have the user input a string to check for instead of some incremented number. But maybe you don't want the user to have to worry about that.

Re: LUA help

Posted: Fri Jan 22, 2021 7:05 am
by Anakin
The Problem is not the ReadDataFile but at ScriptCB_DoFile("addon"). Lua is lazy and so tracks the names of scripts it already executed. And if you ask for the same script twice it just ignores it, because it already handled that.

There are some possible soluions i can think of:
1) give unique names to the script inside addon.lvl
Like ...\addon\XXX\addon.lvl contains a script named addon_XXX.lua

2) The scripts executed need to be tracked somewhere. Like in _G or something like that. Findout about it and set it to nil every time you run the script with ScribtCB_DoFile("addon")

3) As far as i remember there are two lua functions to execute code DoFile and something else. The other one does always execute no matter if the file was handled prior. But i'm not sure about the name or if it was already part of lua 5.0.2 or if it was removed from the swbf2 lua 5.0.2 binary.


==EDIT==

the different functions are
require: keep track of what already was loaded
dofile: just loads a file always you call this

Looks like ScribtCB_DoFile() is actually a lua require. So try using dofile() instead of ScribtCB_DoFile()

==EDIT2==

If you have any further questions about lua stuff, feel free to ping me on the discord server. i'm more active there and i know a lot about lua stuff. I already tried to recompile the new and old binaries to replace the limit one in modtools (no success :cry: but anyway i leared a lot about this stuff)

Re: LUA help

Posted: Fri Jan 22, 2021 6:52 pm
by cbadal
When the Game processes the addons for the PC, this is the exact scenario though.
Each addon has an 'addme.script' file with 1 file in it named 'addme' (although we've found that the addme.script can actually be a renamed from a 'addme.lvl' file and contain multiple scripts and textures, which is a handy way to add icons for the menus to use).

Do you know how would the game get away with doing this for the 'addme'?

It does not appear that script references make their way to '_G'.
Here is an example of a printout of the contents of '_G' during the 'addme' execution:
https://github.com/BAD-AL/SWBF2_Xbox_mo ... _addme.txt

In this case there are only functions, tables, strings and booleans.
If scripts were attached to '_G', then we should see 'addme' and for sure 'shell_interface' present.

Re: LUA help

Posted: Sat Jan 23, 2021 4:58 am
by Sporadia
Most of this is too complicated for me, but if you're looking for a list of loaded file names, what about the _LOADED table? Saw it in _G

Re: LUA help

Posted: Sat Jan 23, 2021 7:07 am
by Ronald79
Sporadia wrote:
Sat Jan 23, 2021 4:58 am
Most of this is too complicated for me, but if you're looking for a list of loaded file names, what about the _LOADED table? Saw it in _G
Also for me

Re: LUA help

Posted: Sat Jan 23, 2021 8:19 am
by Anakin
cbadal wrote:
Fri Jan 22, 2021 6:52 pm
When the Game processes the addons for the PC, this is the exact scenario though.
Each addon has an 'addme.script' file with 1 file in it named 'addme' (although we've found that the addme.script can actually be a renamed from a 'addme.lvl' file and contain multiple scripts and textures, which is a handy way to add icons for the menus to use).

Do you know how would the game get away with doing this for the 'addme'?

It does not appear that script references make their way to '_G'.
Here is an example of a printout of the contents of '_G' during the 'addme' execution:
https://github.com/BAD-AL/SWBF2_Xbox_mo ... _addme.txt

In this case there are only functions, tables, strings and booleans.
If scripts were attached to '_G', then we should see 'addme' and for sure 'shell_interface' present.
Have you tried using the function dofile (https://github.com/BAD-AL/SWBF2_Xbox_mo ... .txt#L1059) instead of ScriptCB_DoFile (https://github.com/BAD-AL/SWBF2_Xbox_mo ... e.txt#L394) ?
Sporadia wrote:
Sat Jan 23, 2021 4:58 am
Most of this is too complicated for me, but if you're looking for a list of loaded file names, what about the _LOADED table? Saw it in _G
Very good point (https://github.com/BAD-AL/SWBF2_Xbox_mo ... .txt#L1918)


io table may be interesting too if it allows you to read/write stuff (https://github.com/BAD-AL/SWBF2_Xbox_mo ... .txt#L2144)
os can help too (https://github.com/BAD-AL/SWBF2_Xbox_mo ... .txt#L2188)

The PC version does not support those but maybe xbox/PS do?

Re: LUA help

Posted: Sat Jan 23, 2021 10:52 pm
by cbadal
Sporadia wrote:
Sat Jan 23, 2021 4:58 am
Most of this is too complicated for me, but if you're looking for a list of loaded file names, what about the _LOADED table? Saw it in _G
Ran the tests ( hooked up 'print statements' to go to the credits ) and found the following:

Code: Select all

_LOADED: {  },
os: { },
io: {stdin:userdata, stdout:userdata, stderr:userdata }
Looks like 'dofile()' crashes the calling script on PSP, as the script doesn't finish (on both valid and invalid filenames).

Re: LUA help

Posted: Sun Jan 24, 2021 9:01 am
by Sporadia
I have another suggestion. What happens if you use dofile() or even ScriptCB_DoFile() on an addon.script instead of having addon.lvl? (Unless you're using .lvl because you need to on console). My idea is it could behave the same as addme.script based on the file extension. I don't see what else would distinguish your addon from an addme.

Edit: Although if you can do that, presumably you can repurpose your code to make addme files work? I don't know what the limits are on what consoles can do. Is .script off the table?

Edit 2 (this edit was based on a misunderstanding):
Hidden/Spoiler:
Also, has anyone tested _LOADED on PC, to see what it's supposed to contain? If that's empty too then there might not even be a table of script references, because the majority of tables in _G can be ruled out, and there'd only be a few left to check like coroutine (at this point picking out tables with names that I don't understand). The only other weird thing is that _G lists the table _G, which I assume is a reference to itself but if you're already reading _G you know it exists so there's no reason to list it. It looks like _G contains every important table without missing any, so if a table of script references isn't in there then it probably doesn't exist.

Re: LUA help

Posted: Sun Jan 24, 2021 9:32 am
by Anakin
_loaded is empty on PC, too.

==EDIT==

Code: Select all

tprint(_G._LOADED)

ReadDataFile("..\\..\\addon\\XXX\\printLVL.lvl")

tprint(_G._LOADED)

print("marker 1")
ScriptCB_DoFile("printLVL")

tprint(_G._LOADED)


print("marker 2")
ScriptCB_DoFile("printLVL")

tprint(_G._LOADED)

print("marker 3")
dofile("..\\..\\addon\\XXX\\printLVL.lvl")

tprint(_G._LOADED)

print("marker 4")
dofile("..\\..\\addon\\XXX\\printLVL.lvl")

tprint(_G._LOADED)

Code: Select all

XXX_interface_script: entered
table: 07B5730C {
}
table: 07B5730C {
}
marker 1
Hello There World
table: 07B5730C {
}
marker 2
table: 07B5730C {
}
marker 3
cannot read ..\..\addon\XXX\printLVL.lvl: No such file or directory
looks like dofile points somewhere else then the default ScriptCB_DoFile


==EDIT2==

Ok, found out how it works. you need uncompiled lua files for dofile:

Code: Select all

tprint(_G._LOADED)

ReadDataFile("..\\..\\addon\\XXX\\printLVL.lvl")

tprint(_G._LOADED)

print("marker 1")
ScriptCB_DoFile("printLVL")

tprint(_G._LOADED)


print("marker 2")
ScriptCB_DoFile("printLVL")

tprint(_G._LOADED)

print("marker 3")
dofile("addon\\XXX\\printLVL.lua")

tprint(_G._LOADED)

print("marker 4")
dofile("addon\\XXX\\printLVL.lua")

tprint(_G._LOADED)

Code: Select all

XXX_interface_script: entered
table: 07A4130C {
}
table: 07A4130C {
}
marker 1
Hello There World
table: 07A4130C {
}
marker 2
table: 07A4130C {
}
marker 3
Hello There World
table: 07A4130C {
}
marker 4
Hello There World
table: 07A4130C {
}

Re: LUA help

Posted: Sun Jan 24, 2021 9:49 am
by Sporadia
Oh I've just understood how the _G was printed in the doc. I thought it was a table that contained strings but I've just understood that it's an object with all it's operations printed out. So if _LOADED is an operation of the object _G (along with all those other tables), it means there is a second _G table contained inside the object _G. What's in _G._G?
Edit: Phrased this more properly.

Edit 2: Also I don't understand tprint exactly but those hex numbers look like addresses, like what you get when you try to print a nested table. What happens if you replace each of your print tests with this:

Code: Select all

for index, table in ipairs(_G._LOADED) do
    print(index)
    tprint(table)
end

Re: LUA help

Posted: Sun Jan 24, 2021 11:16 pm
by cbadal
Forgot to post my 'dumpToString' function (used to print table data):

Code: Select all

function dumpToString(obj, count)
    if( count == nil ) then count = 0 end 
    if type(obj) == 'table' then
        local retVal =  string.rep('  ', count) .. '{ '
        for k,v in pairs(obj) do
            retVal =  retVal ..  tostring(k) .. ':' .. dumpToString(v, count+1) .. ', '
        end
        return retVal .. ' },'
    elseif type(obj) == 'string' then 
        return '"' ..  tostring(obj) .. '"'
    else 
        return tostring(obj) 
    end
end
Also, I think this is the code is what I used to print the Global environment.

Code: Select all

function dump(obj)
	if type(obj) == 'table' then
		for k,v in pairs(obj) do
			print( type(v)..':'..tostring(k)) 
		end
	else
		print( type(v) ..': ' .. tostring(obj))
	end
end
dump(_G)
And yes 'dofile()' references files from the program's filesystem location (GameData) and 'ReadDataFile()' reference root is '_LVL_PC' (or _LVL_XBOX, _LVL_PSP, _LVL_PS2).
Reading the file in as as a '.script' does not change the behavior.

Re: LUA help

Posted: Mon Jan 25, 2021 5:33 am
by Anakin
Sporadia wrote:
Sun Jan 24, 2021 9:49 am
Oh I've just understood how the _G was printed in the doc. I thought it was a table that contained strings but I've just understood that it's an object with all it's operations printed out. So if _LOADED is an operation of the object _G (along with all those other tables), it means there is a second _G table contained inside the object _G. What's in _G._G?
Edit: Phrased this more properly.

Edit 2: Also I don't understand tprint exactly but those hex numbers look like addresses, like what you get when you try to print a nested table. What happens if you replace each of your print tests with this:

Code: Select all

for index, table in ipairs(_G._LOADED) do
    print(index)
    tprint(table)
end
_G is a lua specific global variable that contains EVERYTHING. Every function, every table, every variable. In case you are in a scope, but you want to access a variable from a different scope you can do it through _G. So since _G is a valid global variable it's of course part of _G, too. You could do _G._G._G and it's the same as _G.
tprint is a help function to print out table content with all names, values and their datatype. It's pretty much the same as uf_print() from Zerted but adjusted by marth, so i can collapse the content in my debug log.

Yes the number is a memory address since every element is printed out by it's type, name and value. The value of a table is a memory pointer. The following {} block contains everything that is in there. Just in case you wanna test this on _G it won't work and crash the game. Use uf_print() without deep search activated instead.

if you're interested in the code, here it is:

Code: Select all

function getn(v)
    local v_type = type(v);
    if v_type == "table" then
        return table.getn(v);
    elseif v_type == "string" then
        return string.len(v);
    else
        return;
    end
end

function string.starts(str, Start)
    return string.sub(str, 1, getn(Start)) == Start;
end

function tprint(t, indent)
    if not indent then indent = 1, print(tostring(t) .. " {") end
    if t then
        for key,value in pairs(t) do
            if not string.starts(tostring(key), "__") then
                local formatting = string.rep("    ", indent) .. tostring(key) .. ": ";
                if value and type(value) == "table" then
					print(formatting .. tostring(value) .. " {")
                    tprint(value, indent+1);
				else
					print(formatting .. tostring(value))
                end
            end
        end
		print(string.rep("    ", indent - 1) .. "}")
    end
end


-- helper function from Zerted to print all contents of a table
function uf_print( data, nested, depth )
	if (not data) then return end	--must have something to print
	if (not type) then return end	--must have something to print
	if depth == 0 then
		print(depth..": uf_print(): Starting: ", data, type, nested, depth)
	end

	--for each pair in the given table, 
	for key,value in pairs(data) do

		--check for nils
		if key == nil and value == nil then
			print(depth..": uf_print(): Both the key and value are nil")
		elseif key == nil then
			print(depth..": uf_print(): Nil key, but value is:", value)
		elseif value == nil then
			print(depth..": uf_print(): Nil value, but key is:", key)
		else
			--have no nils (but a continue keyword would have been nice...)
			
			--display the key, value pair if possible
			if key ~= "mapluafile" then
				--normal display
				print(depth..": Key, Value: ", key, value)
			else
				--have to format map lua file values to prevent crash when outputting the value
				local map = string.format(value, "<A>", "<B>")
				print(depth..": Key, Formated Value: ", key, map)
			end
	
			--if nested, search deeper, but don't recurse into the global table or our starting table
			if nested and key ~= "_G" and key ~= data then
			
				--the developers didn't include type(), so have to use this hack to determine if the value represents a table
				local result = pcall(function(array)
					table.getn(array)
				end, value)
				
				--can only process tables
				if result then
					uf_print(value, nested, depth+1)
				end
			end
		end
	end
	
	if depth == 0 then
		print(depth..": uf_print(): Finished: ", data, nested, depth)
	else
		print()
	end
end

@cbadal: does it work if you use a lua file instead of a lvl with dofile?

Re: LUA help

Posted: Wed Jan 27, 2021 3:54 am
by cbadal
I was trying with a .lua file.
I would be pretty surprised if dofile worked on a .script file.