Skip to content

Unlock Password Protect Clusters

Grasshopper clusters can be password protected to "prevent" others from reading the logic inside.
BUT This protection is an illusion, as this password can easily be unlocked or replaced thru code.

Password is saved in the private fields of the cluster, the private fields will contain these:

  1. m_password
  2. m_file
  3. m_internalDocument
  4. m_author
  5. m_mapping
  6. m_component_cache
  7. m_previewContent
  8. m_attributes
  9. m_icon_index
  10. m_icon_index_locked
  11. m_icon_override
  12. m_icon_mode

We can simply select the password protected clusters and overwrite the value of m_password with an empty password.

Basic component setup:

  • x : item, bool - Enable Toggle

Code

using System.Reflection;
using Grasshopper.Kernel.Special;
// Automatically adds and connects a button and panel
Ini();

// Descriptions
this.Component.Name = "Unlock Cluster";
this.Component.NickName = "Unlock";
this.Component.Description = "Unlocks clusters by removing the password";

// Have the user explicity select the cluster to unlock
var obj = Grasshopper.Instances.ActiveCanvas.Document.SelectedObjects();
if(x)
{
  log = new List<string>(); // Clear log
  foreach(var cluster in obj){ // Check all selected objects
    if(!(cluster is GH_Cluster)){ // If selected obj isn't a cluster, log it
      log.Add("\"" + cluster.Name + "\" is not a cluster");
      continue; // Move onto next object since this one isn't a cluster
    }
    // Get all private fields of a GH_Cluster
    Type type = typeof(Grasshopper.Kernel.Special.GH_Cluster);
    FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
    // Shortcut - Set first field of the cluster to an empty string
    fields[0].SetValue(cluster, Convert.FromBase64String(" "));
    log.Add("\"" + cluster.NickName + "\" => (unlocked)"); // Log results
    // Fallback method - if m_password isn't the first field anymore
    // for(int i =0;i<fields.Length;i++){
    //   if(fields[i].Name == "m_password"){
    //     fields[i].SetValue(cluster, Convert.FromBase64String(" "));
    //     log.Add("\"" + cluster.NickName + "\" => (unlocked)");
    //     break;
    //   }
    // }
  }
}
foreach(var str in log) Print(str);
// <Custom additional code> 
// Nice to haves, but not required

List<string> log = new List<string>();

private void Ini(){
  if(Component.Params.Input[0].SourceCount == 0){
  var button = new Grasshopper.Kernel.Special.GH_ButtonObject(); // Instantiate new button
    button.CreateAttributes(); // Sets up default values to prevent crashing rhino
    button.Attributes.Pivot = new System.Drawing.PointF(
      this.Component.Attributes.Pivot.X - this.Component.Attributes.Bounds.Width - button.Attributes.Bounds.Width,
      this.Component.Attributes.Pivot.Y - button.Attributes.Bounds.Height / 2);
    GrasshopperDocument.AddObject(button, false);
    this.Component.Params.Input[0].AddSource(button);
    button.CollectData();
    this.GrasshopperDocument.ScheduleSolution(1);
  }
  if(Component.Params.Output[0].Recipients.Count == 0){
    var panel = new Grasshopper.Kernel.Special.GH_Panel();
    panel.CreateAttributes();
    panel.Attributes.Pivot = new System.Drawing.PointF(
      Component.Attributes.Pivot.X + Component.Attributes.Bounds.Width,
      Component.Attributes.Pivot.Y - panel.Attributes.Bounds.Height / 2);
    panel.AddSource(this.Component.Params.Output[0]);
    GrasshopperDocument.AddObject(panel, false);
    this.GrasshopperDocument.ScheduleSolution(1);
  }
  this.Component.ExpireSolution(true);
  Grasshopper.Instances.ActiveCanvas.Document.ScheduleSolution(5);
}

GitHub Gist