Home Technical Talk

Maxscript - file setup - general questions

Hi guys,

A couple of general maxscript questions from a novice programmer for anyone that could clarify:

1. Are there standard ways to set up your script to read functions/methods from another file.
For example, I have my main and I want to call functions from another file, ie class file, that I have set up. Is it as simple as telling max to go to a specific directory and read from a file with my functions already in it? (hope that made sense)

I just hate to make one giant file with every single function in it. I'm looking more for a common practice.

2. If I'm sending tools to clients to use, I'd like to do a basic encryption on the code with all my functions/methods.

For example, they receive my zipfile. They can see the open the main file, but the other file with my methods would be encrypted.

The help docs have been useful for some of my maxscript challenges, but more times then not, I find myself confused on how snippets of code are to be set up with out a lot of trial/error.

Thanks for any clarification or common practices you'd like to share.

J

Replies

  • [Deleted User]
    Options
    Offline / Send Message
    [Deleted User] polycounter lvl 3
    1. Maxscript doesn't call functions from other files like other languages. It evaluates the file and keeps the code in memory for later access. You have to be careful and evaluate code from different files in correct order. The biggest pain of this is that you have to place files in different directories in 3dsmax so it can be ready when the next part of the code needs it. To avoid the need of messing with paths in Max I made my own ScriptLoader and now I can load libraries how and from where I want and keep logical plugin structure. Check my vimeo for explanation how I solved problem of plugins that are splited on multiple files and contains elements that need to be evaluated for access in other part of my scripts. Sorry for the sound, I got shity microphone and don't have to many occasions to speak english live.
  • Mark Dygert
    Options
    Offline / Send Message
    Maxscript is kind of weird, it takes whole scripts and includes them, it doesn't reach out and grab specific functions. So you either break up each function to its own script or stuff them all in one and call/include it all, even if you only need to use one function. This also makes debugging a bit problematic because if there is a problem in the external file it doesn't tell you what line, it just tells you there was a problem in "that file", good luck finding it.

    1) You can trigger an external macro script by using
    macros.run "Category" "MacroName"
    
    Example:
    macros.run "MyTools" "MyMacro"
    

    The external macro script would start out like this:
    macroScript MyMacro --name of the macro
    Category:"MyTools" --category as found in Customize UI: Category
    (
    --script goes here
    )
    

    You can also use "filein" to trigger a .ms script
    local LaunchMyScript = pathConfig.GetDir #Scripts+"\\MyTools\\"+"MyScript.ms"
    fileIn LaunchMyScript
    

    You can also use "include" which will include the code from the external script into the current one, before it evaluates and runs the current script. This has some pitfalls but they should be well documented around the web.


    TLDR
    Search the maxscript help file for:
    "Include scripts in scripts"
    "Macro.run"
    "filein"

    2) Look up "encryptScript" maxscript allows you to generate encrypted maxscript files (.mse) files, they work with max but they can't be read by human eyes.
  • theStudent
    Options
    Offline / Send Message
    You guys are awesome. This is very helpful.

    @mantragora - checking your video to get more clarification on your post. Thanks for your feedback!
    @Mark - Thank you. Sometimes I think the most difficult part is trying to find the correct terminology to search for. I'll go through the help docs and check out includes.
    As for the encryption stuff, I found info for this in the help docs.
    I guess my question would be how to insert this snippet into my code properly?
    Here's my example below. Although I'm posting this, I'll try to see if I can get something basic working:

    macroScript sampleScript
    category:"Sample tools"
    (
    scriptfile= "sampleScript.ms"
    encryptScript scriptfile

    (
    for obj in selection do(
    print "test"
    )
    )
    )
  • [Deleted User]
    Options
    Offline / Send Message
    [Deleted User] polycounter lvl 3
    My solution is good especially if you want to build library of classes/functions that you want to use in more than one script. As for filein/include, I found that they don't work to good with network drives (and path specified as system variables if I remember correctly) so that's why I opted for custom solution. Also this gives me one place to access all paths and correct something instead of correcting code in script if I need to modify path.
  • theStudent
    Options
    Offline / Send Message
    I am viewing the video now on my amazingly slow connection. Very advanced, or at least to me it is! =]
  • Pathologist
    Options
    Offline / Send Message
    Pathologist polycounter lvl 4
    For the encrypting (Q 2) I simply open the maxscript Listener (bottem left the pink/white bars -> right click -> open listener) drag the bar down so you can see the pink and enter the following:
    scriptfile="C:\\testDir\\testfile.ms"
    
    encryptScript scriptfile
    

    replace "C: \\testDir\\testfile.ms" with the file path.

    For the 'class calling' (Q 1) I made all my functions into seperate .ms files which I call at the start of every maxscript. Mostly because the tools we use often use the same functions and often need the same functionality regardless of tool.

    simply put:
    function1 = (include "C:\\testDir\\function1.ms")
    function2 = (include "C:\\testDir\\function2.ms")
    

    edit:

    function1.ms would look something like this:
    fn functionName var1 var2 var3 =
    (
    if var1 == var2 then var3 = var2 -- Just an example
    )
    

    edit2:

    Made a quick GUI (Dialog) so you can select a maxscript file and encrypt it in the GUI
    fn existFile fname = 
    (
    	(getfiles fname).count != 0
    )
    
    rollout msEncrypter "msEncrypter"
    (
    	edittext et_filepath "MS File:" width:240 across:2
    	button btn_path "..." height:18 align:#right
    	button btn_encrypt "Encrypt script!"
    	
    	on btn_encrypt pressed do
    	(
    		scriptfile=et_filepath.text
    		if existFile scriptfile do
    		(
    			try
    			(
    				encryptScript scriptfile
    			)
    			catch()
    		)
    	)
    	
    	on btn_path pressed do
    	(
    		thePath = getOpenFileName types:"MaxScript files(*.ms)|*.ms" filename:et_filepath.text caption:"Load a ms file!"
    		if thePath != undefined then et_filepath.text = thePath
    	)
    )
    
    createdialog msEncrypter 300 60
    
  • theStudent
    Options
    Offline / Send Message
    @Pathologist - Thanks for taking the time to post that. I'm testing this out and looking through the docs to make sure I understand this. I'll post with any questions or triumphs =]
  • Pathologist
    Options
    Offline / Send Message
    Pathologist polycounter lvl 4
    Here is a better version, with some description.
    Decided to work it out a bit more for personal use!

    I left the old code in my previous post, below you can see the new code, have fun! Let me know if it works or not!

    Just to clearify this does not overwrite your *.ms file, it will make a new one next to it. (Trust me, I tried and spilled some coffee while checking if it did!)
    fn existFile fname = 
    (
    	(getfiles fname).count != 0
    )
    
    fn encrypt fname state=
    (
    	local version = 1
    	if existFile fname do
    	(
    		if state == false then version = 1 else version = 0
    		try	-- using a try incase you specify a non-excisting file or a non *.ms file, which would result in a script crash.
    		(
    			encryptScript fname version:version
    		)
    		catch -- required with a try, can be used for debugging as done here.
    		(
    			print "Error! This debug should really be more informative!"
    		)
    	)
    )	
    
    fn filePathSelect et =
    (
    	thePath = getOpenFileName types:"MaxScript files(*.ms)|*.ms" filename:et.text caption:"Load a ms file!"
    	if thePath != undefined then et.text = thePath
    )
    
    rollout msEncrypter "msEncrypter"
    (
    	edittext et_filepath "MS File:" width:240 across:2
    	button btn_path "..." height:18 align:#right
    	checkbox cb_old "Use old encryption!" checked:false across:2 tooltip:"When checked, the method uses the old encryption scheme of 3ds Max 2 to 9, for compatibility reasons."
    	button btn_encrypt "Encrypt script!" height:18 align:#right
    	
    	
    	on btn_encrypt pressed do
    	(
    		encrypt et_filepath.text cb_old.state
    	)
    	
    	on btn_path pressed do
    	(
    		filePathSelect et_filepath 
    	)
    )
    
    createdialog msEncrypter 300 60
    
  • monster
    Options
    Offline / Send Message
    monster polycounter
    My latest big project is something like this:

    When installed the MZP defines an "Additional Plugins" folder, and this ms file goes there:
    global MyRiggingFunctions
    
    struct MyRiggingFunctions
    (
    var1,
    var2,
    var3,
    include "$scripts/MyRigStuff/functions/script1.ms" ,
    include "$scripts/MyRigStuff/functions/script2.ms" ,
    include "$scripts/MyRigStuff/functions/script3.ms" ,
    include "$scripts/MyRigStuff/functions/script4.ms" ,
    include "$scripts/MyRigStuff/rollouts/Rollout1.ms" ,
    include "$scripts/MyRigStuff/rollouts/Rollout2.ms" ,
    include "$scripts/MyRigStuff/rollouts/Rollout3.ms" 
    )
    


    This is pseudo code, but the macros look something like this:
    macroScript RiggingTool
    Category:"My Rigging 1"
    (
        if not MyRiggingFunctions.Rollout1.open do MyRiggingFunctions.Rollout1()
    )
    
  • monster
    Options
    Offline / Send Message
    monster polycounter
    fn existFile fname = 
    (
    	(getfiles fname).count != 0
    )
    

    This is a valid function, but Autodesk added doesFileExist function a few versions ago. You're script is an excellent example. I've never used file encryption.
  • Pathologist
    Options
    Offline / Send Message
    Pathologist polycounter lvl 4
    Thanks!
    monster wrote: »
    This is a valid function, but Autodesk added doesFileExist function a few versions ago.

    Mmm, for some reason I totally overlooked this! Time to change several of my scripts haha.

    Guess I used the function since forever and never looked back!
    This could potentionally speed this up a lot :).
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    Thats interesting Monster. Been doing/learning some maxscript for work and this was really confusing especially coming from python haha. Mind explaining how you defined Additional Plugins folder? Is this changing the 3dsmax.ini in Appdata? Right now using fileIn that reads ms files from P4 on startup. Definitely would like to get a common function library to use.
  • monster
    Options
    Offline / Send Message
    monster polycounter
    This is way more info than you wanted, but it's a simplified version of my setup script. (I didn't actually test it after I simplified it.)

    Basically, I like to have all the artists manually run the setup script. (It's more of a setup wizard.) The setup script points all user folders to perforce. Then whenever they get latest, they always have the updated tools.

    Maybe I can hit you up when I have python questions. :)
    (
    	--EVALATE SCRIPTS
    	fn EvalFolder scriptList =
    	(
    		for script in scriptList do
    		(
    			format "Evaluating: %\n" script
    			fileIn script
    		)
    	)
    
    	--ADD A DIRECTORY TO THE MAX PLUGIN INI
    	local pINI = "$maxdata" + "/" + "Plugin.UserSettings.ini"
    	local pluginFolder
    
    	if doesFileExist pINI do
    	(
    		--USE DIFFERENT PLUGINS PER RELEASE
    		--2011
    		if (maxVersion())[1] == 13000 do
    		(
    			pluginFolder = (perforceDir + "\\MAX\\plugins\\MAX2011_64\\")
    		)
    		--2013
    		if (maxVersion())[1] == 15000 do
    		(
    			pluginFolder = (perforceDir + "\\MAX\\plugins\\MAX2013_64\\")
    		)
    
    		if pluginFolder != undefined do
    		(
    			setINISetting pINI "Directories" "CompanyPlugins" pluginFolder
    			EvalFolder (getFiles (pluginFolder + "*.ms"))
    		)
    	)
    
    	--SET THE SCRIPT DIRECTORIES
    	SetDir #userScripts (perforceDir+ "/MAX/Scripts/")
    	SetDir #userMacros (perforceDir + "/MAX/MacroScripts/")
    	SetDir #userIcons (perforceDir + "/MAX/Icons/")
    	SetDir #userStartupScripts (perforceDir + "/MAX/StartupScripts/")
    
    	--EVAL THE MACROS
    	EvalFolder (getFiles (perforceDir + "/MAX/MacroScripts/*.mcr")
    )
    
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    ah Thanks Monster. Will try some of this stuff. Also feel free to bug me with python questions, I dont mind haha.
  • Pathologist
    Options
    Offline / Send Message
    Pathologist polycounter lvl 4
    monster wrote: »
    This is way more info than you wanted, but it's a simplified version of my setup script. (I didn't actually test it after I simplified it.)

    Basically, I like to have all the artists manually run the setup script. (It's more of a setup wizard.) The setup script points all user folders to perforce. Then whenever they get latest, they always have the updated tools.

    This way of doing it will come out handy! I'll be sure to let you know how it works out, thanks. :thumbup:
  • theStudent
    Options
    Offline / Send Message
    @Pathologist - I hadn't had the chance to respond because my school finals have me wrapped up! =] But thank you so much for the post and description.
    I'm making my attempts to get this working on some basic things. Will post any updates hopefully soon!
Sign In or Register to comment.