MPlug: Can not get a non-networked version of a networked plug

MPlug: Can not get a non-networked version of a networked plug

kramer-bastian
Explorer Explorer
905 Views
1 Reply
Message 1 of 2

MPlug: Can not get a non-networked version of a networked plug

kramer-bastian
Explorer
Explorer

Hi,

I'm currently developing a plugin with the C++ API  for Maya 2019 on Windows 10, where I have to rearrange connections on an array attribute.

 

Therefore I have to list all connections already exist, disconnect them, sort the list and connect them in a new order.

For each array element plug I query the input plug by calling its "MPlug::source()"  function, getting a networked plug. I store this input plug via "MPlug::operator=" into a member variable, expecting to get, like the documentation says, a non-networked version of the input plug.

But, when calling the "MPlug::isNetworked()" function for the member variable, it returns "true", telling me it's still a networked plug. And after disconnecting the input plug from the array element plug, the member variable plug is corrupted. Trying to use it for later connection fails or ends up in a fatal error.

 

So, why do I not get a non-networked plug from the "MPlug::operator=" function, although the documentation implies it. Or is there another way to achieve it? Maybe by using the copy constructor?

 

Here a little example, showing in rough steps, what I'm trying to achieve and where it fails.

 

 

MDGModifier dgMod;
MPlug sourcePlug;// variable for storing the non-networked input plug
if (destinationPlug.isDestination()):
    sourcePlug = destinationPlug.source();// Getting the input plug
if (!sourcePlug.isNull()) {
    dgMod.disconnect(destinationPlug.source(), destinationPlug);
    dgMod.doIt();// works fine and disconnects
    dgMod.connect(sourcePlug, destinationPlug);// eventually throws a memory leak exception
    dgMod.doIt();// returns MStatus::kFailure
}

 

Thanks in advance for any suggestions.

906 Views
1 Reply
Reply (1)
Message 2 of 2

kramer-bastian
Explorer
Explorer

I wrote a little command plugin for reproducing the error.

 

//
// Copyright (C) Bastian Kramer
// 
// File: reconnectPlugs.cpp
//
// MEL Command: reconnectPlugs
//
// Author: Maya Plug-in Wizard 2.0
//

#include <maya/MSimple.h>
#include <maya\MArgDatabase.h>
#include <maya\MGlobal.h>
#include <maya\MSelectionList.h>
#include <maya\MFnDependencyNode.h>
#include <maya\MPlugArray.h>
#include <maya\MStreamUtils.h>
#include <maya\MDGModifier.h>

DeclareSimpleCommand(reconnectPlugs, "Bastian Kramer", "1.0");

MStatus reconnectPlugs::doIt( const MArgList& args )
//
//	Description:
//		Implements the MEL reconnectPlugs command for debugging MPlug copy and "="operator issues.
//		
//		This command is an example of how a reconnection between plugs fails when getting a non-networked plug from a networked plug is not possible.
//		The command works on all input connections to passed in node, queries their source plug, disconnects or removes multi instance of the destination plug and tries to connect them again.
//
//		As the C++ API documentation implies, networked plugs like the ones received by "MPlug::source()" will be invalid after disconnecting them.
//		So, the "MPlug::operator=" is used, like the documentation says, to create a non-networked version of the networked plug, which should be still valid after disconnection.
//		But this command shows, that the operator does not return a non-networked version and that the attempt to use those versions for connecting will fail or raise fatal errors.
//
//	Arguments:
//		args - the argument list that was passes to the command from MEL.
//		Must contain at least one dependency graph node.
//
//	Return Value:
//		MS::kSuccess - command succeeded
//		MS::kFailure - command failed (returning this value will cause the 
//                     MEL script that is being run to terminate unless the
//                     error is caught using a "catch" statement.
//
{
	MStatus status;
	std::ostream &cout = MStreamUtils::stdOutStream();

	// Getting the node from args without using a syntax.
	MString nodeName;
	status = args.get(0, nodeName);
	CHECK_MSTATUS_AND_RETURN_IT(status);
	MObject node;
	MSelectionList nodeList;
	status = MGlobal::getSelectionListByName(nodeName, nodeList);
	CHECK_MSTATUS_AND_RETURN_IT(status);
	if (nodeList.length() > 0)
		status = nodeList.getDependNode(0, node);
	CHECK_MSTATUS_AND_RETURN_IT(status);

	if (!node.isNull() && node.hasFn(MFn::kDependencyNode)) {
		MDGModifier dgMod;
		MFnDependencyNode nodeFn(node);

		// List all connections made with the node.
		MPlugArray connections;
		status = nodeFn.getConnections(connections);
		CHECK_MSTATUS_AND_RETURN_IT(status);

		MPlug overridden, sourceOverridden;
		for (unsigned int i = 0; i < connections.length(); i++) {
			// Use "MPlug::operator=" to create a non-networked version of the connection plug.
			overridden = connections[i];
			// Use the copy constructor to proof if even this way does not create a non-networked version of the connection plug.
			MPlug copy(connections[i]);

			// Printing destination plug's name and if its versions are networked. Expected: 1 | 0 | 0	Got: 1 | 1 | 1
			cout << connections[i].name() << " -> " << connections[i].isNetworked() << " | " << overridden.isNetworked() << " | " << copy.isNetworked() << std::endl;

			// Working only on destination plugs
			if (connections[i].isDestination()) {
				// Use "MPlug::operator=" to create a non-networked version of the connection plug's source.
				sourceOverridden = connections[i].source();
				// Use the copy constructor to proof if even this way does not create a non-networked version of the connection plug's source.
				MPlug sourceCopy(connections[i].source());

				// Printing source plug's name and if its versions are networked. Expected: 1 | 0 | 0	Got: 1 | 1 | 1
				cout << "\t" << sourceOverridden.name() << " -> " << connections[i].source().isNetworked() << " | " << sourceOverridden.isNetworked() << " | " << sourceCopy.isNetworked() << std::endl;

				// Following "if" statement can get modified to always disconnect plugs no matter if the destination plug is an array element.
				if (connections[i].isElement()) {
					// Removes multi instance if destination plug is an array element. This operation will make the networked versions of plugs invalid.
					status = dgMod.removeMultiInstance(connections[i], true);
					CHECK_MSTATUS_AND_RETURN_IT(status);
				}
				else {
					// Disconnect source plug from destination plug. This operation will make the networked versions of plugs invalid.
					status = dgMod.disconnect(connections[i].source(), connections[i]);
					CHECK_MSTATUS_AND_RETURN_IT(status);
				}
				// Apply the disconnection or removal.
				status = dgMod.doIt();
				CHECK_MSTATUS_AND_RETURN_IT(status);

				// Reconnect source and destination plugs by using the overriden versions. Command eventually fails at this point or raises a fatal error by access violation.
				status = dgMod.connect(sourceOverridden, overridden);
				CHECK_MSTATUS_AND_RETURN_IT(status);
				// Apply the connection. Command eventually fails at this point or raises a fatal error by access violation.
				status = dgMod.doIt();
				CHECK_MSTATUS_AND_RETURN_IT(status);
			}
		}
	}
	else {
		// Notify user to use the command correct.
		displayError("Failed to get a dependency node by name. Must pass a node as argument.");
	}

	return MStatus::kSuccess;
}