This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Joeld98's profile picture

dofile() in MC1.7.10/CC1.74

Started by Joeld98, 02 October 2016 - 12:40 AM
Joeld98 #1
Posted 02 October 2016 - 02:40 AM
At the risk of being a duplicate of the other recent dofile() question...

I've spent the better part of the afternoon trying to really understand os.loadAPI() vs. dofile() vs. loadfile()

If you want the back story:
SpoilerI wanted to learn how to make a decent UI so I started looking on the forums at some prior art the other day.

I found Bedrock and Cobalt-UI on the forums. Cobalt had some recent posts so I decided to check it out but could not even get
it to do "cobalt version" after install. I narrowed it down to the use of dofile() to load the API which was throwing an attempt
to call nil error. Chatted with the author on Discord briefly and concluded it was my version / config so I decided to hunt for the root cause.

I'm working in MC1.7.10/CC1.7.4 mainly but setup a MC1.8.9/CC1.7.9 client/server to test some of these scenarios.

I worked through the API tutorial again to ensure I understood os.loadAPI and that I could make it work.

I went back to the PIL docs and reviewed chunks and the dofile() and loadfile().

I dug through the forums for dofile() issues and found a couple posts describing the differences and a known issue in CC 1.7.8

http://www.computerc...__fromsearch__1
http://www.computerc...__fromsearch__1

Tested each example on 1.7.9 and they all seemed to work.

I tried to test the examples in LDT but could not figure out environment / setup. Kept getting file not found errors in the console.

I tried various tests with 1.7.4 and could not get get dofile to work directly but could override it with a definition I found on the PIL

function dofile (filename)
	  local f = assert(loadfile(filename))
	  return f()
end

The issue reported in CC1.7.9 on github says dofile should be "fnFile, e = loadfile( _sFile, _G )"

So my questions to the Pro's are:

1) I came across a statement from exxero who authored sheets who says:
There are two ways you can load Sheets into your environment, either as an API (via os.loadAPI) or as a local library (using dofile). We recommend the latter, as using dofile means that you can use any file name for the library itself, you don't pollute the global environment and accessing the local table is slightly more efficient.

Is there any truth to the environment and speed comments? It looks to me like the PIL version and CC version all end up in _G

2) Can someone confirm what version of CC dofile() was introduced in and/or fixed in? I did not see it anywhere in the release notes.

3) Any downside to defining dofile() as stated above to allow the API's that prefer that method to run without modification?

Bonus questions:

4) Can someone point me to a LDT environment setup tutorial / primer? I tried the dofile() examples on files in the same project but got
"com.naef.jnlua.LuaRuntimeException: cannot open func.lua: No such file or directory" where func.lua was the api file in the same project.

5) Being relatively new to CC, Lua and coding, is this par for the course in terms of running down issues and work arounds in various versions?
Bomb Bloke #2
Posted 02 October 2016 - 07:10 AM
I came across a statement from exxero who authored sheets who says:
There are two ways you can load Sheets into your environment, either as an API (via os.loadAPI) or as a local library (using dofile). We recommend the latter, as using dofile means that you can use any file name for the library itself, you don't pollute the global environment and accessing the local table is slightly more efficient.
Is there any truth to the environment and speed comments? It looks to me like the PIL version and CC version all end up in _G

The way Exerro intends you to use Sheets is to call dofile() on it, and then assign the table he coded it to return when called, to a local variable. You could instead assign it to an element in _G, if you really wanted, but dofile() doesn't dump anything in there unless the code you get it to execute tells it to. It compiles source into a Lua function and then executes it, that's all. You can run code that returns stuff, you can run code that doesn't return stuff, it's a fairly low-level function with a lot of different applications.

The ComputerCraft-specific os.loadAPI(), on the other hand, expects scripts written according to a specific layout - it executes them, as dofile() does, but uses a specific environment table and ignores any return values. Instead, when those scripts complete it takes anything they left in their global environment (usually a bunch of declared functions), bundles them up into a new table which it names after the script file, and then sticks that in _G. It's a relatively high-level function intended for a specific purpose.

(If you're not familiar, "low-level" functions tend to perform simple tasks, whereas "high-level" functions perform complex ones. Low-level coding gives you a lot more flexibility (and often efficiency), but you tend to need to combine a lot of them to get a job done! High-level functions tend to be inflexible and inefficient, but using them allows you to keep your own scripts short and simple.)

In terms of whether accessing stuff from _G is any faster or not: it's true that you'll get faster access times from local variables. When you refer to a variable in your code, the interpreter has to figure out which scope it's in, starting with the local and ending in _G; the less places it has to look before finding it, the better.

In practise this tends not to matter. But if you intend to call certain functions very frequently (eg within loops lasting hundreds of iterations), it does pay to copy values from _G to a local variable. Don't just copy pointers to API tables, though! If there's a specific function you want, get the specific function pointer! This way, you furthermore save on time that'd otherwise be spent indexing into your table over and over. Eg:

local bitBand = bit.band

for i = 1, 100000 do
  -- call bitBand over and over, instead of bit.band
end

But again, for general usage this really doesn't matter - it's a micro-optimisation.

Can someone confirm what version of CC dofile() was introduced in and/or fixed in? I did not see it anywhere in the release notes.

dofile() has been available within CC since long before any MC 1.7.10 builds were available. It'd surprise me if it wasn't there since day one.

The only build I'm aware of which was missing it was a pre-release version of 1.79 (which no one's got any good reason to be using). Difficult to comment on your "attempt to call nil" error without seeing the code involved in context. You didn't set (or use a modpack which pre-sets) "disable_lua51_features" to true within ComputerCraft.cfg, did you? That setting blocks access to dofile() (and a bunch of additional functions), and should only be used if you're wanting to develop code that'd (hopefully) be forwards-compatible with a hypothetical future version of ComputerCraft based on Lua 5.2 (which may or may not ever actually come into existence)… If memory serves, some Tekkit pack or other does this for no good reason.

The function is defined within bios.lua. There are a few githubs around which track CC's Lua file history - eg. Unfortunately github web interface is pretty rubbish for this purpose (even wikipedia offers a better diff tool, and that's not intended for hosting code!), but hey, the data's there. You want the <> buttons.

The definition in 1.79 goes:

dofile = function( _sFile )
    local fnFile, e = loadfile( _sFile, _G )
    if fnFile then
        return fnFile()
    else
        error( e, 2 )
    end
end

Any downside to defining dofile() as stated above to allow the API's that prefer that method to run without modification?

Frankly I'd say that any coder who expects you to use dofile() to load a given API should be making sure it works with the version of dofile() included in the version of ComputerCraft they expect you to use.

Again, difficult to say without seeing the code in context.

Can someone point me to a LDT environment setup tutorial / primer?

Apparently one's built-in within the included help files, but it's also online.

Being relatively new to CC, Lua and coding, is this par for the course in terms of running down issues and work arounds in various versions?

os.loadAPI(), as well as various other functions, are included with CC more or less so that novice coders don't have to know about correct usage of the likes of dofile(), and can instead jump straight to making their turtles build houses or whatever.
MKlegoman357 #3
Posted 02 October 2016 - 07:40 PM
You didn't set (or use a modpack which pre-sets) "disable_lua51_features" to true within ComputerCraft.cfg, did you? That setting blocks access to dofile() (and a bunch of additional functions), and should only be used if you're wanting to develop code that'd (hopefully) be forwards-compatible with a hypothetical future version of ComputerCraft based on Lua 5.2 (which may or may not ever actually come into existence)… If memory serves, some Tekkit pack or other does this for no good reason.

dofile() is still a feature of Lua 5.2 and 5.3, and that ComputerCraft.cfg setting doesn't remove it.




dofile() is better if the API will have an internal state of some sort. For example, the CC's settings API stores all the settings in one place, thus if you edit a setting in one program, that setting will be immediately changed for another program. That's how some "os.loadAPI()" APIs work. When using dofile(), each program loads the API for itself, and thus gets a new API table, together with it's state, created just for it. If settings API was made to be loaded with it, each program would get it's own settings and wouldn't need to worry about other programs changing those settings.

Of course, you could simply make a "os.loadAPI()" API which would work like the vector API, which requires the program to use the vector.new() function to get a vector object with it's own state (the x, y and z coordinates).
Edited on 02 October 2016 - 05:52 PM
apemanzilla #4
Posted 02 October 2016 - 07:50 PM
There was a beta build a while ago that had a broken version of dofile but IIRC it was fixed before release, are you sure you're using 1.74 and not an old prerelease?
KingofGamesYami #5
Posted 02 October 2016 - 08:38 PM
Of course, you can always have the best of both worlds by checking for the shell when your file is being loaded. I do that in my Stitch API.


local stitch = shell and {} or getfenv() --#either set things in a table (for dofile) or the environment (for os.loadAPI)
stitch.func = function()
 ...
end
return stitch --#if it's dofile this is necessary, if it's loadAPI this does nothing.
Joeld98 #6
Posted 03 October 2016 - 01:05 AM
It was definitely the configuration (Good call Bomb Bloke!). The Tekkit Legends server pack has it set to true by default. Once I set that line to false, dofile() worked like a champ.
I discounted that at first because I found dofile() in the PIL from 5.0 on like MKlegoman357 stated but that setting prevented it from working.

B:disable_lua51_features=false

Good to know there is a work around for when you don't have control of the config.

Thanks for the detailed replies. Hopefully this will help others that get tripped up on this.
Bomb Bloke #7
Posted 03 October 2016 - 01:40 AM
dofile() is still a feature of Lua 5.2 and 5.3, and that ComputerCraft.cfg setting doesn't remove it.

*headdesk*

That'll teach me to believe anything anybody says ever - I read this baloney and didn't even think to check it. My bad.