Notifications
Clear all

[Closed] Broken Globals

I’m totally confused on why this doesn’t work. What I’m trying to do is ‘filein’ a ms file containing a global struct filled with functions. However no matter what i do it always returns ‘undefined’. Below are my simple example scripts.

This script goes in the 3ds Max Scripts Startup folder
C:\Users\jmartini\AppData\Local\Autodesk\3dsMax\2016 – 64bit\ENU\scripts\startup\startupBugs.ms


/* Option 1 */
(
	fn run =
	(
		sharedDir = @"$userscripts\StudioTools\Shared"
		files = getFiles (pathconfig.appendpath sharedDir "JM*.ms")
		for f in files do fileIn f
		print JM_Functions
		JM_Functions.DeleteEmptyLayers()
	)
	run()
)

/* Option 2 */
-- (
-- 		sharedDir = @"$userscripts\StudioTools\Shared"
-- 		files = getFiles (pathconfig.appendpath sharedDir "JM*.ms")
-- 		for f in files do fileIn f
-- 		print JM_Functions
-- 		JM_Functions.DeleteEmptyLayers()
-- )

The file containing the global struct of functions goes in a folder i created in the scripts folder:
C:\Users\jmartini\AppData\Local\Autodesk\3dsMax\2016 – 64bit\ENU\scripts\StudioTools\Shared\JM_Functions.ms


global JM_Functions
struct _JM_Functions
(
	fn DeleteEmptyLayers =
	(
		local defaultLayer = layermanager.getlayer 0
		defaultLayer.current = true
		for i = layerManager.count - 1 to 1 by -1 do
		(
			local layer = layerManager.getLayer i
			LayerManager.deleteLayerByName layer.name
		)
	)
)
JM_Functions = _JM_Functions()
print "+ JM_Functions Loaded successfully"

What’s most interesting is even after 3ds Max loads and throws an error, if you type in the listener JM_Functions and hit enter, it will show it’s a global struct. It doesn’t print undefined.

It only works if i put it exactly like this…


sharedDir = @"$userscripts\StudioTools\Shared"
files = getFiles (pathconfig.appendpath sharedDir "JM*.ms")
for f in files do fileIn f

6 Replies

Your first version of the startup script (with the braces arround all) creates the sharedDir variable in the local scope of the block created by those outer braces. That local variable will be deleted as soon the scope is left ( with the closing bracket ). As result, as soon as this startup script is done, sharedDir will be no more.

On contrary, the second version of the startup script, creates the sharedDir variable in the global scope ( as it’s not surrounded by (…) ). Thus the global sharedDir variable still exists after the script has run .

You could circumvent this by explicitly making sharedDir global by using

(  
   global sharedDir = @"$userscripts\StudioTools\Shared"
.
.
.

BTW:
there is some strange thing with global vars in Maxscript and the “globalVars” struct, i supsect the latter being somehow borked
[B]

globalVars.isGlobal #sharedDir

[/B] will reporttrue in both cases of your script
Looks like the globalVars struct stores the names in this case and report them as existing, even if the variable value is “undefined”
will report undefined:[B]

globalVars.get #sharedDir

[/B]

On the other hand, if you query a really non-existing variable, it will report false to isGlobal and throw an exception if you query the value
will report false:[B]

globalVars.isGlobal #neverUsedName

[/B]
throws a does not exist runtime error:[B]

globalVars.get #neverUsedName

[/B]

 lo1

@Joseph I don’t follow, why would sharedDir need to be global at all? It’s only ever used inside the enclosing block.

1 Reply
(@spacefrog)
Joined: 10 months ago

Posts: 0

I don’t follow myself now – but seems the original post was changed, so i can’t recheck why i came to my conclusion. But it might as well be that i should’nt simply post right after crawling out of bed after less then 6 hours of sleep

This does not work, because when the function run() is parsed, the JM_Functions variable does not exist yet, and the call to print JM_Function causes an implicitly local variable to be created in the scope of the function.

So when the function is actually called and performs the fileIn(), the other script is evaluated and a new global JM_Functions variable is created (as you can see after the failure if you ask about it in the Listener), but the run function cannot see it because the local variable is masking it.

There are two possible solutions
The classical one is to pre-declare the JM_Functions as global before it is ever mentioned in the run() function.

(
	fn run =
	(
		global JM_Functions
		sharedDir = @"$userscripts\StudioTools\Shared"
		files = getFiles (pathconfig.appendpath sharedDir "JM*.ms")
		for f in files do fileIn f
		print JM_Functions
		JM_Functions.DeleteEmptyLayers()
	)
	run()
)

The more obscure one is to prefix any call to JM_Functions inside the run() function with double-colon :: – this will force the global scope access and will create a global variable when first encountered instead of making a local one. You can read more about this in the MAXScript Help, or in my ancient blog here: http://lotsofparticles.blogspot.bg/2009/09/lost-gems-in-maxscript-forcing-global.html

(
	fn run =
	(
		sharedDir = @"$userscripts\StudioTools\Shared"
		files = getFiles (pathconfig.appendpath sharedDir "JM*.ms")
		for f in files do fileIn f
		print ::JM_Functions
		::JM_Functions.DeleteEmptyLayers()
	)
	run()
)

This was super helpful and helps me understand what is going on. thanks for the details explanation of things.

I updated the original post to simplify the code. Not much changed in terms of how your suggestions would be applied.