ZBrushCentral

Loop with plugins/scripts ( Challenge of the month)

haha, i am sure MArcus, that you read the title and you know that is not possible, but you probably remember about that old thread :

so here is the idea : bring that full concept to life , it’s just that !?

so i use two script one is a plugin with an ui, which is the entry point in the loop. ( see Plugin Code A)

so the very first thing which happen, is that the sleep mode on startup event will run the init_main routine, and that routine check two differents memblocks exist and then read the data(MemReadString).
this init_main is use to check some data which was edited from the second script.
. well at first startup the memblock do not exists, until you press the ZPlugin:Plugin sample:Get Version.
so basically it when you press getversion, if the memblocks do not exists it assumes this is the first iteration in the loop, and if the memblocks exist then it’s a new iteration in the loop.
on the side, i have three RoutineDef to write in mem block for :

  1. The register_process_id , which is the button path on the plugin so it’s Zplugin:Plugin Sample:Get Version
  2. The register_routine write the routine name I want to run from the secondary script, here it’s get_version.
  3. there is a routine to unregister a process so it just delete the meme block, and should happen at to end the loop.

Note :
fm_shared_data_block (memblock) is create on the plugin side and the secondary script just need to read that memblock.
for fm_id_data_block its a memblock only use by the main plugin, to store what button was pressed initially. so at the second step it gonna try to IPress the button ( get version), but it here that problem occurs *

Script Code B (see script code below):
So you press the get version button on the plugin, and it’s the first iteration, then it load the secondary script the way we all do it.
the second script also has a init routine that routine is use to read the process_id and routine name in the two memblocks.
so it run the proper routine, and eveything is fine the end_script routine bring you back the the first plugin using the ZScript:previous button.
and now the plugin at startup run the init_main routine, and read the into the memblock, so if routine variable (data) which at beginning stores the the routine to run i use the input var in the routine to store the String that contain the version of ZBrush.

Part C : the issue with this method .

so the main problem here is that when we get back the the initial plugin, the memblock are properly read the data is valid. THe I_press routine run properly because adding a [note, “hello”] will be displayed, but the button ( zplugin:plugin sample:get version)
do not behave has expected ( so it doesn’t press the button again and the loop is broken till i press manually on the button. so the memblock exists and the process continue but stop here, i always got to press it manually again and again, and honestly i got not a damn idea of what is going wrong as the code is fine a swiss watch .

So that a challenge, it jsut that i want to share my challenge and the code for anyone to reuse it or to help to fix it.
@marcus_civis if you want me to pack all that, i will !
I just hope it wasn’t too long but it a project that woudl be super powerfull if we can go back and forth with plugin that know about the memblocks names and data structure.
so anykind of help will be greatly appreciated.

Plugin Code A . here is the code of the main plugin :

[VarDef, DEBUG, 1]

// STARTUP PROCESS (HEADER)
// manage routine/buttons
[RoutineDef, init_main,
    // keep it on top ( before the next if statement)
    // manage data 
    [If, [MemGetSize, fm_shared_data_block],
            [VarDef, data, ""]
            [MemReadString, fm_shared_data_block, data, 0, 1,,255]
            [If, ([StrLength, data] == 0),
                ,//else
                [If, DEBUG, [Note, [StrMerge, "data : ", data]]]
            ]
            ,//else
            // NOTE :  create a memblock only from routine or button definition
    ]

    [If, [MemGetSize, fm_id_data_block],
        [VarDef, process_id, ""]
        //then we restart the process
        [MemReadString, fm_id_data_block, process_id, 0, 1,,255]
        // [Note, [StrMerge, "process id : " , process_id]]
        [If, ([StrLength, process_id] == 0),
            ,//else            
            [If, [IExists, process_id],
                [RoutineCall, i_press, process_id]
                [If, DEBUG, [Note, [StrMerge, "terminate process id exists: ", process_id]]]
            ]
        ]
        ,//else
        // NOTE : create a memblock only from routine or button definition
    ]

]
// END STARTUP

[RoutineDef, i_press,
    [VarSet, process_id, [StrExtract, process_id, 0, [StrLength, process_id] - 1]]
    [If, [IExists, process_id],
        [VarSet, h, [IHPos, process_id, 1]]
        [VarSet, v, [IVPos, process_id, 1]]
        

        [ISet, process_id, 1]
        [IClick, process_id, 1,1]
        [IPress, process_id]

        ,//else
        [Note, "The process do not exists."]
    ]
,process_id]

[RoutineDef, register_process_id,

    [If, [MemGetSize, fm_id_data_block],
        // sanitize it
        [MemDelete, fm_id_data_block]

        [MemCreate, fm_id_data_block, 255, 0]
        [MemWriteString, fm_id_data_block, process_id, 0, [StrLength, process_id]]
        ,//else
        [MemCreate, fm_id_data_block, 255, 0]
        [MemWriteString, fm_id_data_block, process_id, 0, [StrLength, process_id]]
    ]

,process_id]


[RoutineDef, register_routine,

    [If, [MemGetSize, fm_shared_data_block],
        // sanitize it
        [MemDelete, fm_shared_data_block]
        
        [MemCreate, fm_shared_data_block, 255, 0]
        [MemWriteString, fm_shared_data_block, routine, 0,[StrLength, routine]]
        ,//else
        [MemCreate, fm_shared_data_block, 255, 0]
        [MemWriteString, fm_shared_data_block, routine, 0,[StrLength, routine]]
    ]

,routine]

[RoutineDef, unregister_process,
    [If, [MemGetSize, fm_id_data_block], 
        [MemDelete, fm_id_data_block]
    ]
    [If, [MemGetSize, fm_shared_data_block], 
        [MemDelete, fm_shared_data_block]
    ]
]

// Init the plugin

// Register the UI
[ISubPalette, "Zplugin:Plugin Sample"]

[IButton, "Zplugin:Plugin Sample:Get Version", "get plugin version.",
    // should we only check the id_data block ?
    [If, DEBUG, [Note, [StrMerge, "mem block exists ?", [MemGetSize, fm_shared_data_block]]]]
    [If, [MemGetSize, fm_shared_data_block],
        // if mem block exists , then it supposed to complete the process
        [If, DEBUG, [Note, "Then we can now complete the task."]]
        // if the memblock exists then we just read It
        [VarSet, data, ""]
		[MemReadString, fm_shared_data_block, data, 0, 255]
        [Note, [StrMerge, "returned data :\n", data]]
        // if the task is complete, then we just clean up all memblock
        [RoutineCall, unregister_process]

        ,//else
        // setup the very first iteration
        [If, DEBUG, [Note, "First Iteration !" ]]

        [VarSet, process_id, "Zplugin:Plugin Sample:Get Version"]
        //[VarSet, process_id, [StrExtract, process_id, 0, [StrLength, process_id] - 1 ]]

        [VarSet, routine, "get_version"]

        // register process_id and function_name
        [RoutineCall, register_process_id, process_id]
        [RoutineCall, register_routine, routine]

        [FileNameSetNext, "plugin-sample/get_version.txt"]
        [IPress, "Zscript:Load"]

    ]

,0,1]

[Sleep,0.01,

	[If,(result & 256 == 256)
		,//cmds	
		// [Note, "startup !"]
        // run init after ui loading
        [RoutineCall, init_main]
        
	]

	[SleepAgain]
,256 | 512, result,winId]

Script Part B .
here is the secondary script :

// so only run a routine if the block exists so make sure we delete it when we won't run that script
// for another purpose.

[VarDef, DEBUG, 1]

[RoutineDef, init,

	[VarDef, routine, ""]
	[VarDef, optional, ""]
	
	[If, [MemGetSize, fm_shared_data_block],
		// if the memblock exists then we just read It
		[MemReadString, fm_shared_data_block, routine, 0, 255]

		[If, DEBUG, [Note, [StrMerge, "The routine name is : ", routine]]]

		// if true then we can call the correct routinecall
		[If, [StrLength, routine],[RoutineCall, routine, optional]]
	]
]


[RoutineDef, get_version,

	[VarSet, version, "2021.5"]

	// write the in the mem block
	[If, [MemGetSize, fm_shared_data_block],
		//if the memblock exists then we just write on top ? or after ?
		[MemWriteString, fm_shared_data_block, version, 0, [StrLength, version]]
		[MemResize, fm_shared_data_block, [StrLength, version]]
		,//else if it does exists then create it, we still can access it from the previous script
		[MemCreate, fm_shared_data_block, 255, 0]
		[MemWriteString, fm_shared_data_block, version, 0, [StrLength, version]]
		[MemResize, fm_shared_data_block, [StrLength, version]]
	]
	[If, DEBUG, [IPress, "Color:SwitchColor"]]

,optional]

// [RoutineDef, init,
// 	[VarDef, version, ""]
// 	[RoutineCall, get_version, version]
// ]

[RoutineDef, end_script,
	[IPress, "Zscript:Previous"]
]

[RoutineCall, init]
[RoutineCall, end_script]

Hi Nicolas,

This is interesting. As far as I can tell, this is failing at the Routine i_press button press. The routine is called OK but doesn’t succeed in pressing the button, or if the button is pressed no code is run. I’m not sure why. I tried various methods, as you have done, but couldn’t get it to work.

However, if you make a routine and call that instead then it will work without problems. I simply made a new routine called “Update” which duplicated the button code, registered that as the “process_id” and called it in the i_press routine (after removing the [IExists] bits of code!).

HTH,
Marcus

2 Likes

Thank for the time you took to test that out Marcus.
well I am sorry as I forget to explain the main goal of the challenge :smiley:
So the objective is to create an infinite loop using multiple scripts, because [Loop] do not support infinite loops.
so yes it plays well when looping with routines,(still haven’t tested the solution you 'd suggested) but it doesn’t with ui interfaces.

The point here is by pressing the button, it gonna defines the “rule” on the shared memblock, the routine won’t know which button was pressed.
Well I’d liked to keep that code in the button and not in routine that matches the name of the button, cause i afraid of having border effect using that method, and i want to keep the number of registered routine as low as possible, because a script with 255 routines will fails with a error message about the exceed of variables a script can contains.

Those kind of script will have to be written from python and I got a python framework for that job and that I have coded myself.
It can build plugin, using python and even create all the user interface using Json format.

Nicolas

Hmm, well have fun with that!
-Marcus