Gdevelop: Internal method gd::Project::GetUsedExtension should be deleted or reworked

Created on 14 May 2020  路  6Comments  路  Source: 4ian/GDevelop

Describe the bug

It seems like GetUsedExtension doesn't output the actual used extensions.
image
As you can see there is not the AdMob extension in the list, even tho it is clearly used. I also moved some actions arround and added other from admob to see if libGD would then register the extension but it didn't work.

To Reproduce

Steps to reproduce the behavior:

  1. Open GDevelop and create a project that uses an Extension (Here I used AdMob)
  2. Open the dev tools and go on the components tab of the react debugger tools
  3. Find MainFrame and then the Electron menu component.
  4. Click on that component and scroll to the state to the project object.
  5. Expand it and find the ptr attribute
  6. Go to the console and type
let project = new gd.Project();
project.ptr = <copiedPointer>;
project.getUsedExtensions().toJSArray();
  1. See that the non built-in extensions are missing.

Other details

馃挩 Technical debt removal

Most helpful comment

See my answer here: https://github.com/4ian/GDevelop/pull/1717/files#r425121945

For now use gd::Platform::GetAllPlatformExtensions as a workaround. We need to identify a smart way to recognise all the extensions used by a project to fix the method you reported.

All 6 comments

Sorry for the confusion, it's a semi-dead code. I say semi dead because even if it's used in some part of the code, the array is never really changed/repopulated. It should be removed - and you should not rely on it.
Though I have an underlying question: why would you need this?

I wanted to use it to iterate over all used extensions in the PR for adding cordova or npm dependencies when exporting, but if I cannot use that how do I iterate over all used extensions?

The exact logic I am applying is
For each included extension: Get the extension, and if it has dependency metadata add that metadata to the appropriate file:

  gd::String str =
      fs.ReadFile(gdjsRoot + "/Runtime/Cordova/config.xml");
  /* ... */

 gd::String plugins = "";

 for(gd::String const extensionName : project.GetUsedExtensions()) {
    std::shared_ptr<gd::PlatformExtension> extension = project.GetCurrentPlatform().GetExtension(extensionName);

    for (gd::DependencyMetadata dependency: extension->GetAllDependencies()) {
      //if (dependency.GetDependencyType() == gd::DependencyTypes::cordova) {

        plugins += "<plugin name=\"" + dependency.GetExportName();
        if(dependency.GetVersion() != "-1") {
          plugins += "\" spec=\"" + dependency.GetVersion();
        }
        plugins += "\">\n"; 

        // In cordova all settings are considered a plugin variable
        for (std::pair<gd::String, gd::PropertyDescriptor>& variable : dependency.GetAllExtraSettings()) {
          if(variable.second.GetType() == "ProjectProperty") {
            plugins += "\t\t<variable name=\"" + variable.first + "\" value=\"" + project["Get" + variable.second.GetValue()]() + "\" />\n";
          } else {
            plugins += "\t\t<variable name=\"" + variable.first + "\" value=\"" + variable.second.GetValue() + "\" />\n";
          }
        }

        plugins += "\t</plugin>";
      //}
    }
  }

  str = str.FindAndReplace(
    "<!-- GDJS_PLUGINS -->",
    plugins
  );

  if (!fs.WriteToFile(exportDir + "/config.xml", str)) {
    /* ... */
  }

Is there anything wrong with that logic?

See my answer here: https://github.com/4ian/GDevelop/pull/1717/files#r425121945

For now use gd::Platform::GetAllPlatformExtensions as a workaround. We need to identify a smart way to recognise all the extensions used by a project to fix the method you reported.

We need to identify a smart way to recognise all the extensions used by a project to fix the method you reported.

Maybe adding a "GetExtensionsDependencies" to the DependencyAnalyzer and find a way to merge the Analyzers for every event sheet into one?

Well, I looked into it today, but after coding for 4 hours I didn't get it to work properly. This is the closest I got:

std::set<gd::String> Project::GetUsedExtensions() {
  // Build a map from ever instruction/expresssion name to an extension
  // This is needed to analyze extension dependencies.
  std::map<gd::String, gd::String> extensionConditionsMap;
  std::map<gd::String, gd::String> extensionActionsMap;
  std::map<gd::String, gd::String> extensionExpressionsMap;
  for (auto extension : GetCurrentPlatform().GetAllPlatformExtensions()) {
    gd::String extensionName = extension->GetName();
    for (auto condition : extension->GetAllConditions()) {
      extensionConditionsMap[condition.first] = extensionName;
    }
    for (auto action : extension->GetAllActions()) {
      extensionActionsMap[action.first] = extensionName;
    }
    for (auto expression : extension->GetAllExpressions()) {
      extensionExpressionsMap[expression.first] = extensionName;
    }
    for (auto expression : extension->GetAllStrExpressions()) {
      extensionExpressionsMap[expression.first] = extensionName;
    }
  }

  // Search for extension dependencies
  std::set<gd::String> usedExtensions;
  for (size_t i; GetLayoutsCount(); i++) {
    auto events = GetLayout(i).GetEvents();
    for (size_t eventID; events.size(); eventID++) {
      for (auto actionList : events[eventID].GetAllActionsVectors()) {
        for (size_t actionId = 0; actionList->size(); actionId++) {
          gd::String extension =
              extensionActionsMap[actionList->Get(actionId).GetType()];
          usedExtensions.insert(extension);
        }
      }
      for (auto conditionList : events[eventID].GetAllConditionsVectors()) {
        for (size_t conditionId = 0; conditionList->size(); conditionId++) {
          gd::String extension =
              extensionConditionsMap[conditionList->Get(conditionId).GetType()];
          usedExtensions.insert(extension);
        }
      }
      for (auto expression : events[eventID].GetAllExpressionsWithMetadata()) {
        gd::String extension =
            extensionExpressionsMap[expression.second.GetType()];
        usedExtensions.insert(extension);
      }
    }
  }
  for (size_t i; GetExternalEventsCount(); i++) {
    auto events = GetExternalEvents(i).GetEvents();
    for (size_t eventID; events.size(); eventID++) {
      for (auto actionList : events[eventID].GetAllActionsVectors()) {
        for (size_t actionId = 0; actionList->size(); actionId++) {
          gd::String extension =
              extensionActionsMap[actionList->Get(actionId).GetType()];
          usedExtensions.insert(extension);
        }
      }
      for (auto conditionList : events[eventID].GetAllConditionsVectors()) {
        for (size_t conditionId = 0; conditionList->size(); conditionId++) {
          gd::String extension =
              extensionConditionsMap[conditionList->Get(conditionId).GetType()];
          usedExtensions.insert(extension);
        }
      }
      for (auto expression : events[eventID].GetAllExpressionsWithMetadata()) {
        gd::String extension =
            extensionExpressionsMap[expression.second.GetType()];
        usedExtensions.insert(extension);
      }
    }
  }

  return usedExtensions;
};

I first looked into using the DependenciesAnalyzer with very similar code, but it had 2 problems:

  1. It crashed the browser and the call never ended, I must have done some sort of circular for loop.
  2. I DependenciesAnalyzer doesn't support a const Project reference.

I also noticed that ArbitraryResourceWorker seems to have something similarly scanning through everything 馃

I can't solve it, but I hope someone will manage to, and that my code will help. Good luck!

Was this page helpful?
0 / 5 - 0 ratings