Diff
checker
Texte
Texte
Images
Documents
Excel
Dossiers
Legal
Enterprise
Application de bureau
Prix
Se connecter
Télécharger Diffchecker Desktop
Comparer le texte
Trouver la différence entre deux fichiers texte
Outils
Historique
Éditeur live
Masquer les espaces
Cacher identiques
Sans retour à la ligne
Vue
Divisé
Unifié
Niveau de précision
Intelligent
Mot
Caractère
Styles de texte
Modifier l’apparence
Coloration syntaxique
Choisir la syntaxe
Ignorer
Transformer le texte
Aller au premier écart
Modifier l'entrée
Diffchecker Desktop
La façon la plus sécurisée d'utiliser Diffchecker. Obtenez l'application Diffchecker Desktop : vos diffs ne quittent jamais votre ordinateur !
Obtenir Desktop
ClothingPluginManager_patched
Créé
il y a 11 heures
Le diff n'expire jamais
Effacer
Exporter
Partager
Expliquer
1 suppression
Lignes
Total
Supprimé
Caractères
Total
Supprimé
Pour continuer à utiliser cette fonctionnalité, passez à
Diff
checker
Pro
Voir les prix
435 lignes
Copier tout
36 ajouts
Lignes
Total
Ajouté
Caractères
Total
Ajouté
Pour continuer à utiliser cette fonctionnalité, passez à
Diff
checker
Pro
Voir les prix
468 lignes
Copier tout
using System;
using System;
using UnityEngine;
using UnityEngine;
using System.Collections;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Text;
using System.Text;
using SimpleJSON;
using SimpleJSON;
using MVR;
using MVR;
using MeshVR;
using MeshVR;
using System.Linq;
using System.Linq;
using Leap.Unity;
using Leap.Unity;
using MVR.FileManagementSecure;
using MVR.FileManagementSecure;
using System.Security.AccessControl;
using System.Security.AccessControl;
namespace Stopper {
namespace Stopper {
public class ClothingPluginManager : MVRScript {
public class ClothingPluginManager : MVRScript {
string fileExt = "clothingplugins";
string fileExt = "clothingplugins";
DAZDynamic dazDynamic;
DAZDynamic dazDynamic;
DAZDynamicItem dazDynamicItem;
DAZDynamicItem dazDynamicItem;
MVRPluginManager pluginManager;
MVRPluginManager pluginManager;
PresetManager presetManager;
PresetManager presetManager;
Destructor destruct;
Destructor destruct;
bool needsReload = false;
bool needsReload = false;
JSONClass sceneJson = null;
JSONClass sceneJson = null;
bool lastRestoreWasSceneRestore = false;
bool lastRestoreWasSceneRestore = false;
public override void Init() {
public override void Init() {
WalkAndGetParentItems();
WalkAndGetParentItems();
if(containingAtom == null) throw new Exception("Unable to determine containing atom.");
if(containingAtom == null) throw new Exception("Unable to determine containing atom.");
if(pluginManager == null) throw new Exception("Unable to find plugin manager.");
if(pluginManager == null) throw new Exception("Unable to find plugin manager.");
if(dazDynamicItem == null) throw new Exception("Unable to find dynamic item.");
if(dazDynamicItem == null) throw new Exception("Unable to find dynamic item.");
if(dazDynamic == null) throw new Exception("Unable to find dazDynamic item.");
if(dazDynamic == null) throw new Exception("Unable to find dazDynamic item.");
this.overrideId = $"{dazDynamic.uid}:{this.storeId}";
this.overrideId = $"{dazDynamic.uid}:{this.storeId}";
DAZCharacterSelector selector = containingAtom.GetStorableByID("geometry") as DAZCharacterSelector;
DAZCharacterSelector selector = containingAtom.GetStorableByID("geometry") as DAZCharacterSelector;
if(selector == null) throw new Exception("Unable to find CharacterSelector");
if(selector == null) throw new Exception("Unable to find CharacterSelector");
pluginManager.containingAtom = containingAtom;
pluginManager.containingAtom = containingAtom;
pluginManager.gameObject.SetActive(true);
pluginManager.gameObject.SetActive(true);
pluginManager.exclude = true;
pluginManager.exclude = true;
presetManager = dazDynamic.GetComponentInChildren<PresetManager>();
presetManager = dazDynamic.GetComponentInChildren<PresetManager>();
List<PresetManager.SpecificStorable> specificStorables = presetManager.specificStorables.ToList();
List<PresetManager.SpecificStorable> specificStorables = presetManager.specificStorables.ToList();
PresetManager.SpecificStorable specificStorable = null;
PresetManager.SpecificStorable specificStorable = null;
foreach(var storable in specificStorables) {
foreach(var storable in specificStorables) {
if(storable.specificKey == "ClothingPluginManager") {
if(storable.specificKey == "ClothingPluginManager") {
specificStorable = storable;
specificStorable = storable;
specificStorable.specificStorableBucket = this.transform;
specificStorable.specificStorableBucket = this.transform;
}
}
}
}
if(specificStorable == null) {
if(specificStorable == null) {
specificStorable = new PresetManager.SpecificStorable();
specificStorable = new PresetManager.SpecificStorable();
specificStorable.specificKey = "ClothingPluginManager";
specificStorable.specificKey = "ClothingPluginManager";
specificStorable.storeId = this.storeId;
specificStorable.storeId = this.storeId;
specificStorable.specificStorableBucket = this.transform;
specificStorable.specificStorableBucket = this.transform;
specificStorable.includeChildren = false;
specificStorable.includeChildren = false;
}
}
presetManager.specificStorables = specificStorables.ToArray();
presetManager.specificStorables = specificStorables.ToArray();
presetManager.RefreshStorables();
presetManager.RefreshStorables();
// Add the destructor to unload mods if we do a force reload
// Add the destructor to unload mods if we do a force reload
if(dazDynamicItem != selector.femaleClothingCreatorItem && dazDynamicItem != selector.maleClothingCreatorItem) {
if(dazDynamicItem != selector.femaleClothingCreatorItem && dazDynamicItem != selector.maleClothingCreatorItem) {
destruct = dazDynamic.binaryStorableBucket.gameObject.AddComponent<Destructor>();
destruct = dazDynamic.binaryStorableBucket.gameObject.AddComponent<Destructor>();
destruct.pluginManager = pluginManager;
destruct.pluginManager = pluginManager;
destruct.storedContainingAtom = containingAtom;
destruct.storedContainingAtom = containingAtom;
}
}
// If this isn't set on the plugin manager by the time we reach here, then
// If this isn't set on the plugin manager by the time we reach here, then
// this plugin will not show up in the UI. In that case we'll want to reload the plugins.
// this plugin will not show up in the UI. In that case we'll want to reload the plugins.
if(pluginManager.pluginPanelPrefab == null) {
if(pluginManager.pluginPanelPrefab == null) {
needsReload = true;
needsReload = true;
}
}
// Copy over all of the prefabs
// Copy over all of the prefabs
MVRPluginManager containingAtomPluginManager = containingAtom.GetStorableByID("PluginManager") as MVRPluginManager;
MVRPluginManager containingAtomPluginManager = containingAtom.GetStorableByID("PluginManager") as MVRPluginManager;
if(containingAtomPluginManager != null){
if(containingAtomPluginManager != null){
pluginManager.pluginPanelPrefab = containingAtomPluginManager.pluginPanelPrefab;
pluginManager.pluginPanelPrefab = containingAtomPluginManager.pluginPanelPrefab;
pluginManager.scriptControllerPanelPrefab = containingAtomPluginManager.scriptControllerPanelPrefab;
pluginManager.scriptControllerPanelPrefab = containingAtomPluginManager.scriptControllerPanelPrefab;
pluginManager.scriptUIPrefab = containingAtomPluginManager.scriptUIPrefab;
pluginManager.scriptUIPrefab = containingAtomPluginManager.scriptUIPrefab;
pluginManager.configurableSliderPrefab = containingAtomPluginManager.configurableSliderPrefab;
pluginManager.configurableSliderPrefab = containingAtomPluginManager.configurableSliderPrefab;
pluginManager.configurableTogglePrefab = containingAtomPluginManager.configurableTogglePrefab;
pluginManager.configurableTogglePrefab = containingAtomPluginManager.configurableTogglePrefab;
pluginManager.configurableColorPickerPrefab = containingAtomPluginManager.configurableColorPickerPrefab;
pluginManager.configurableColorPickerPrefab = containingAtomPluginManager.configurableColorPickerPrefab;
pluginManager.configurableButtonPrefab = containingAtomPluginManager.configurableButtonPrefab;
pluginManager.configurableButtonPrefab = containingAtomPluginManager.configurableButtonPrefab;
pluginManager.configurablePopupPrefab = containingAtomPluginManager.configurablePopupPrefab;
pluginManager.configurablePopupPrefab = containingAtomPluginManager.configurablePopupPrefab;
pluginManager.configurableScrollablePopupPrefab = containingAtomPluginManager.configurableScrollablePopupPrefab;
pluginManager.configurableScrollablePopupPrefab = containingAtomPluginManager.configurableScrollablePopupPrefab;
pluginManager.configurableFilterablePopupPrefab = containingAtomPluginManager.configurableFilterablePopupPrefab;
pluginManager.configurableFilterablePopupPrefab = containingAtomPluginManager.configurableFilterablePopupPrefab;
pluginManager.configurableTextFieldPrefab = containingAtomPluginManager.configurableTextFieldPrefab;
pluginManager.configurableTextFieldPrefab = containingAtomPluginManager.configurableTextFieldPrefab;
pluginManager.configurableSpacerPrefab = containingAtomPluginManager.configurableSpacerPrefab;
pluginManager.configurableSpacerPrefab = containingAtomPluginManager.configurableSpacerPrefab;
} else {
} else {
throw new Exception("Unable to find plugin manager for main atom.");
throw new Exception("Unable to find plugin manager for main atom.");
}
}
// If the Daz clothing item needs a post-load restore, then we are still in scene restore
// If the Daz clothing item needs a post-load restore, then we are still in scene restore
// In that case we want to load the defaults after the item loads, but before the post-load
// In that case we want to load the defaults after the item loads, but before the post-load
// restore (which will apply scene settings)
// restore (which will apply scene settings)
if(!dazDynamicItem.enabled && dazDynamicItem.needsPostLoadJSONRestore) {
if(!dazDynamicItem.enabled && dazDynamicItem.needsPostLoadJSONRestore) {
dazDynamicItem.onLoadedHandlers += PostLoad;
dazDynamicItem.onLoadedHandlers += PostLoad;
Copier
Copié
Copier
Copié
} else
if (needsReload) {
} else
{
// Always run PostLoad for runtime activation, not just when the UI
// needs a reload. This is what loads the default .clothingplugins
// preset; gating it on needsReload meant defaults only loaded in
// the rare case where the plugin panel prefab wasn't ready yet.
StartCoroutine("PostLoadAfterDelay");
StartCoroutine("PostLoadAfterDelay");
}
}
StartCoroutine("AddPluginTabToUI");
StartCoroutine("AddPluginTabToUI");
}
}
public void Start() {
public void Start() {
// UI
// UI
// Preset code was adapted from AcidBubbles' Collider Editor
// Preset code was adapted from AcidBubbles' Collider Editor
var savePath = dazDynamic.GetStoreFolderPath(false);
var savePath = dazDynamic.GetStoreFolderPath(false);
var savePresetUI = CreateButton("Save Plugin Presets");
var savePresetUI = CreateButton("Save Plugin Presets");
savePresetUI.button.onClick.AddListener(() =>
savePresetUI.button.onClick.AddListener(() =>
{
{
FileManagerSecure.CreateDirectory(savePath);
FileManagerSecure.CreateDirectory(savePath);
var fileBrowserUI = SuperController.singleton.fileBrowserUI;
var fileBrowserUI = SuperController.singleton.fileBrowserUI;
fileBrowserUI.SetTitle("Save Plugin Presets");
fileBrowserUI.SetTitle("Save Plugin Presets");
fileBrowserUI.fileRemovePrefix = null;
fileBrowserUI.fileRemovePrefix = null;
fileBrowserUI.hideExtension = false;
fileBrowserUI.hideExtension = false;
fileBrowserUI.keepOpen = false;
fileBrowserUI.keepOpen = false;
fileBrowserUI.fileFormat = fileExt;
fileBrowserUI.fileFormat = fileExt;
fileBrowserUI.defaultPath = savePath;
fileBrowserUI.defaultPath = savePath;
fileBrowserUI.showDirs = true;
fileBrowserUI.showDirs = true;
fileBrowserUI.shortCuts = null;
fileBrowserUI.shortCuts = null;
fileBrowserUI.browseVarFilesAsDirectories = false;
fileBrowserUI.browseVarFilesAsDirectories = false;
fileBrowserUI.SetTextEntry(true);
fileBrowserUI.SetTextEntry(true);
fileBrowserUI.Show(SavePreset);
fileBrowserUI.Show(SavePreset);
fileBrowserUI.fileEntryField.text = GetDefaultFileName() + "." + fileExt;
fileBrowserUI.fileEntryField.text = GetDefaultFileName() + "." + fileExt;
fileBrowserUI.ActivateFileNameField();
fileBrowserUI.ActivateFileNameField();
});
});
var loadPresetUI = CreateButton("Load Plugin Presets");
var loadPresetUI = CreateButton("Load Plugin Presets");
loadPresetUI.button.onClick.AddListener(() =>
loadPresetUI.button.onClick.AddListener(() =>
{
{
FileManagerSecure.CreateDirectory(savePath);
FileManagerSecure.CreateDirectory(savePath);
var shortcuts = FileManagerSecure.GetShortCutsForDirectory(savePath);
var shortcuts = FileManagerSecure.GetShortCutsForDirectory(savePath);
SuperController.singleton.GetMediaPathDialog(LoadPreset, fileExt, savePath, false, true, false, null, false, shortcuts);
SuperController.singleton.GetMediaPathDialog(LoadPreset, fileExt, savePath, false, true, false, null, false, shortcuts);
});
});
this.exclude = false;
this.exclude = false;
}
}
/*
/*
There might be a more elegant way to do this but this works for now.
There might be a more elegant way to do this but this works for now.
*/
*/
private void WalkAndGetParentItems(){
private void WalkAndGetParentItems(){
GameObject parent = this.gameObject;
GameObject parent = this.gameObject;
while(this.containingAtom == null || parent != this.containingAtom.gameObject){
while(this.containingAtom == null || parent != this.containingAtom.gameObject){
if(parent.transform.parent == null){
if(parent.transform.parent == null){
return;
return;
}
}
if(pluginManager == null){
if(pluginManager == null){
pluginManager = parent.GetComponent<MVRPluginManager>();
pluginManager = parent.GetComponent<MVRPluginManager>();
}
}
if(dazDynamic == null){
if(dazDynamic == null){
dazDynamic = parent.GetComponent<DAZDynamic>();
dazDynamic = parent.GetComponent<DAZDynamic>();
}
}
if(dazDynamicItem == null){
if(dazDynamicItem == null){
dazDynamicItem = parent.GetComponent<DAZDynamicItem>();
dazDynamicItem = parent.GetComponent<DAZDynamicItem>();
}
}
this.containingAtom = parent.GetComponent<Atom>();
this.containingAtom = parent.GetComponent<Atom>();
parent = parent.transform.parent.gameObject;
parent = parent.transform.parent.gameObject;
}
}
}
}
private void PostLoad() {
private void PostLoad() {
SyncClothSimSettings();
SyncClothSimSettings();
string namedDefaultFile = $"{dazDynamic.GetStoreFolderPath(true)}{GetDefaultFileName()}.{fileExt}";
string namedDefaultFile = $"{dazDynamic.GetStoreFolderPath(true)}{GetDefaultFileName()}.{fileExt}";
string defaultFile = $"{dazDynamic.GetStoreFolderPath(true)}default.{fileExt}";
string defaultFile = $"{dazDynamic.GetStoreFolderPath(true)}default.{fileExt}";
if(lastRestoreWasSceneRestore && sceneJson != null && sceneJson.Count > 0){
if(lastRestoreWasSceneRestore && sceneJson != null && sceneJson.Count > 0){
LoadFromJSON(sceneJson);
LoadFromJSON(sceneJson);
} else if(FileManagerSecure.FileExists(namedDefaultFile)){
} else if(FileManagerSecure.FileExists(namedDefaultFile)){
LoadPreset(namedDefaultFile);
LoadPreset(namedDefaultFile);
} else if (FileManagerSecure.FileExists(defaultFile)){
} else if (FileManagerSecure.FileExists(defaultFile)){
LoadPreset(defaultFile);
LoadPreset(defaultFile);
} else if (needsReload) {
} else if (needsReload) {
// If there is no default, we may still want to re-load plugins once now that the
// If there is no default, we may still want to re-load plugins once now that the
// plugin manager is fully set up. This is mostly to make sure that this plugin
// plugin manager is fully set up. This is mostly to make sure that this plugin
// shows up in the UI.
// shows up in the UI.
pluginManager.LateRestoreFromJSON(pluginManager.GetJSON());
pluginManager.LateRestoreFromJSON(pluginManager.GetJSON());
}
}
}
}
// From the dotnet runtime, since plugins don't have access to System.IO.Path
// From the dotnet runtime, since plugins don't have access to System.IO.Path
private static char[] GetInvalidFileNameChars() => new char[] {
private static char[] GetInvalidFileNameChars() => new char[] {
'\"', '<', '>', '|', '\0',
'\"', '<', '>', '|', '\0',
(char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
(char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
(char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20,
(char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20,
(char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30,
(char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30,
(char)31, ':', '*', '?', '\\', '/'
(char)31, ':', '*', '?', '\\', '/'
};
};
private string GetDefaultFileName(){
private string GetDefaultFileName(){
return String.Join("_", dazDynamicItem.displayName.Split(GetInvalidFileNameChars(), StringSplitOptions.RemoveEmptyEntries) ).TrimEnd('.') + ".default";
return String.Join("_", dazDynamicItem.displayName.Split(GetInvalidFileNameChars(), StringSplitOptions.RemoveEmptyEntries) ).TrimEnd('.') + ".default";
}
}
/*
/*
VAM has an issue on subsequent forced reloads where the ClothSimControl
VAM has an issue on subsequent forced reloads where the ClothSimControl
may get out of sync with the ClothSettings. This fixes that.
may get out of sync with the ClothSettings. This fixes that.
*/
*/
private void SyncClothSimSettings(){
private void SyncClothSimSettings(){
ClothSimControl clothSimControl = dazDynamicItem.GetComponentInChildren<ClothSimControl>();
ClothSimControl clothSimControl = dazDynamicItem.GetComponentInChildren<ClothSimControl>();
if(clothSimControl != null) {
if(clothSimControl != null) {
SyncStorable(clothSimControl, "simEnabled");
SyncStorable(clothSimControl, "simEnabled");
SyncStorable(clothSimControl, "integrateEnabled");
SyncStorable(clothSimControl, "integrateEnabled");
SyncStorable(clothSimControl, "collisionEnabled");
SyncStorable(clothSimControl, "collisionEnabled");
SyncStorable(clothSimControl, "collisionRadius");
SyncStorable(clothSimControl, "collisionRadius");
SyncStorable(clothSimControl, "drag");
SyncStorable(clothSimControl, "drag");
SyncStorable(clothSimControl, "weight");
SyncStorable(clothSimControl, "weight");
SyncStorable(clothSimControl, "distanceScale");
SyncStorable(clothSimControl, "distanceScale");
SyncStorable(clothSimControl, "stiffness");
SyncStorable(clothSimControl, "stiffness");
SyncStorable(clothSimControl, "compressionResistance");
SyncStorable(clothSimControl, "compressionResistance");
SyncStorable(clothSimControl, "friction");
SyncStorable(clothSimControl, "friction");
SyncStorable(clothSimControl, "staticMultiplier");
SyncStorable(clothSimControl, "staticMultiplier");
SyncStorable(clothSimControl, "collisionPower");
SyncStorable(clothSimControl, "collisionPower");
SyncStorable(clothSimControl, "gravityMultiplier");
SyncStorable(clothSimControl, "gravityMultiplier");
SyncStorable(clothSimControl, "iterations");
SyncStorable(clothSimControl, "iterations");
SyncStorable(clothSimControl, "allowDetach");
SyncStorable(clothSimControl, "allowDetach");
SyncStorable(clothSimControl, "detachThreshold");
SyncStorable(clothSimControl, "detachThreshold");
SyncStorable(clothSimControl, "jointStrength");
SyncStorable(clothSimControl, "jointStrength");
SyncStorable(clothSimControl, "force");
SyncStorable(clothSimControl, "force");
}
}
}
}
private void SyncStorable(JSONStorable parent, string name) {
private void SyncStorable(JSONStorable parent, string name) {
var storable = parent.GetParam(name);
var storable = parent.GetParam(name);
var storableBool = storable as JSONStorableBool;
var storableBool = storable as JSONStorableBool;
if(storableBool != null && storableBool.setCallbackFunction != null) {
if(storableBool != null && storableBool.setCallbackFunction != null) {
storableBool.setCallbackFunction(storableBool.val);
storableBool.setCallbackFunction(storableBool.val);
}
}
var storableFloat = storable as JSONStorableFloat;
var storableFloat = storable as JSONStorableFloat;
if(storableFloat != null && storableFloat.setCallbackFunction != null) {
if(storableFloat != null && storableFloat.setCallbackFunction != null) {
storableFloat.setCallbackFunction(storableFloat.val);
storableFloat.setCallbackFunction(storableFloat.val);
}
}
var storableVector = storable as JSONStorableVector3;
var storableVector = storable as JSONStorableVector3;
if(storableVector != null && storableVector.setCallbackFunction != null) {
if(storableVector != null && storableVector.setCallbackFunction != null) {
storableVector.setCallbackFunction(storableVector.val);
storableVector.setCallbackFunction(storableVector.val);
}
}
}
}
private IEnumerator PostLoadAfterDelay() {
private IEnumerator PostLoadAfterDelay() {
yield return new WaitForEndOfFrame();
yield return new WaitForEndOfFrame();
PostLoad();
PostLoad();
yield break;
yield break;
}
}
public IEnumerator AddPluginTabToUI() {
public IEnumerator AddPluginTabToUI() {
MVRPluginManager containingAtomPluginManager = containingAtom.GetStorableByID("PluginManager") as MVRPluginManager;
MVRPluginManager containingAtomPluginManager = containingAtom.GetStorableByID("PluginManager") as MVRPluginManager;
if(containingAtomPluginManager == null) throw new Exception("Cannot find plugin manager for parent atom.");
if(containingAtomPluginManager == null) throw new Exception("Cannot find plugin manager for parent atom.");
// Wait for the Person's plugin manager UI to be loaded.
// Wait for the Person's plugin manager UI to be loaded.
// This should happen when the Person's UI is first opened.
// This should happen when the Person's UI is first opened.
Transform pluginManagerUITransform = null;
Transform pluginManagerUITransform = null;
while(pluginManagerUITransform == null) {
while(pluginManagerUITransform == null) {
yield return null;
yield return null;
pluginManagerUITransform = containingAtomPluginManager.UITransform;
pluginManagerUITransform = containingAtomPluginManager.UITransform;
}
}
// Wait for the clothing item's customization UI to be created
// Wait for the clothing item's customization UI to be created
// This should happen when the user clicks 'Customize' on the clothing
// This should happen when the user clicks 'Customize' on the clothing
Transform customizationUI = dazDynamicItem.customizationUI;
Transform customizationUI = dazDynamicItem.customizationUI;
while(customizationUI == null) {
while(customizationUI == null) {
yield return null;
yield return null;
customizationUI = dazDynamicItem.customizationUI;
customizationUI = dazDynamicItem.customizationUI;
}
}
// Wait for the customization UI to be active
// Wait for the customization UI to be active
while(!customizationUI.gameObject.activeInHierarchy) {
while(!customizationUI.gameObject.activeInHierarchy) {
yield return null;
yield return null;
}
}
// This should have been initialized with the PluginManager's IO but I've seen it null here.
// This should have been initialized with the PluginManager's IO but I've seen it null here.
// Race conditions? Me being dumb? Who knows. Adding wait loop.
// Race conditions? Me being dumb? Who knows. Adding wait loop.
MVRPluginManagerUI containingAtomPluginManagerUI = pluginManagerUITransform.GetComponentInChildren<MVRPluginManagerUI>(true);
MVRPluginManagerUI containingAtomPluginManagerUI = pluginManagerUITransform.GetComponentInChildren<MVRPluginManagerUI>(true);
while(containingAtomPluginManagerUI == null) {
while(containingAtomPluginManagerUI == null) {
yield return new WaitForSeconds(0.1f);
yield return new WaitForSeconds(0.1f);
containingAtomPluginManagerUI = pluginManagerUITransform.GetComponentInChildren<MVRPluginManagerUI>(true);
containingAtomPluginManagerUI = pluginManagerUITransform.GetComponentInChildren<MVRPluginManagerUI>(true);
}
}
UIConnectorMaster uiConnectorMaster = customizationUI.GetComponent<UIConnectorMaster>();
UIConnectorMaster uiConnectorMaster = customizationUI.GetComponent<UIConnectorMaster>();
TabbedUIBuilder tabbedUIBuilder = uiConnectorMaster.GetComponentInChildren<TabbedUIBuilder>();
TabbedUIBuilder tabbedUIBuilder = uiConnectorMaster.GetComponentInChildren<TabbedUIBuilder>();
foreach(var child in tabbedUIBuilder.selector.toggleContainer.GetChildren()) {
foreach(var child in tabbedUIBuilder.selector.toggleContainer.GetChildren()) {
if(child.name == "Plugins") {
if(child.name == "Plugins") {
yield break;
yield break;
}
}
}
}
Transform pluginTabPrefab = UnityEngine.Object.Instantiate<Transform>(containingAtomPluginManagerUI.transform,tabbedUIBuilder.selector.transform);
Transform pluginTabPrefab = UnityEngine.Object.Instantiate<Transform>(containingAtomPluginManagerUI.transform,tabbedUIBuilder.selector.transform);
// Remove any plugin UIs we inadvertently copied over
// Remove any plugin UIs we inadvertently copied over
foreach(var script in pluginTabPrefab.GetComponentsInChildren<MVRScriptUI>(true)) {
foreach(var script in pluginTabPrefab.GetComponentsInChildren<MVRScriptUI>(true)) {
Destroy(script.gameObject);
Destroy(script.gameObject);
}
}
foreach(var script in pluginTabPrefab.GetComponentsInChildren<MVRPluginUI>(true)) {
foreach(var script in pluginTabPrefab.GetComponentsInChildren<MVRPluginUI>(true)) {
Destroy(script.gameObject);
Destroy(script.gameObject);
}
}
TabbedUIBuilder.Tab pluginsTab = new TabbedUIBuilder.Tab{name = "Plugins", prefab = pluginTabPrefab, color = Color.blue};
TabbedUIBuilder.Tab pluginsTab = new TabbedUIBuilder.Tab{name = "Plugins", prefab = pluginTabPrefab, color = Color.blue};
tabbedUIBuilder.AddTab(pluginsTab);
tabbedUIBuilder.AddTab(pluginsTab);
pluginManager.UITransform = pluginTabPrefab.transform;
pluginManager.UITransform = pluginTabPrefab.transform;
pluginManager.InitUI();
pluginManager.InitUI();
yield break;
yield break;
}
}
public void OnEnable() {
public void OnEnable() {
if(containingAtom != null) {
if(containingAtom != null) {
StartCoroutine("AddPluginTabToUI");
StartCoroutine("AddPluginTabToUI");
}
}
}
}
public override JSONClass GetJSON(bool includePhysical = true, bool includeAppearance = true, bool forceStore = false) {
public override JSONClass GetJSON(bool includePhysical = true, bool includeAppearance = true, bool forceStore = false) {
JSONClass jc = pluginManager.GetJSON(true,true,true);
JSONClass jc = pluginManager.GetJSON(true,true,true);
jc["id"] = this.storeId;
jc["id"] = this.storeId;
JSONArray storables = new JSONArray();
JSONArray storables = new JSONArray();
foreach(MVRScript script in pluginManager.GetComponentsInChildren<MVRScript>(true)) {
foreach(MVRScript script in pluginManager.GetComponentsInChildren<MVRScript>(true)) {
if(script != this) {
if(script != this) {
storables.Add(script.GetJSON());
storables.Add(script.GetJSON());
}
}
}
}
jc["storables"] = storables;
jc["storables"] = storables;
this.needsStore = true;
this.needsStore = true;
return jc;
return jc;
}
}
public override void RestoreFromJSON(JSONClass jc, bool restorePhysical = true, bool restoreAppearance = true, JSONArray presetAtoms = null, bool setMissingToDefault = true){
public override void RestoreFromJSON(JSONClass jc, bool restorePhysical = true, bool restoreAppearance = true, JSONArray presetAtoms = null, bool setMissingToDefault = true){
if(this.isPresetRestore) {
if(this.isPresetRestore) {
if(jc.Count > 0) {
if(jc.Count > 0) {
LoadFromJSON(jc);
LoadFromJSON(jc);
}
}
} else {
} else {
sceneJson = jc;
sceneJson = jc;
lastRestoreWasSceneRestore = SuperController.singleton.isLoading;
lastRestoreWasSceneRestore = SuperController.singleton.isLoading;
}
}
}
}
private void LoadPreset(string path) {
private void LoadPreset(string path) {
if (string.IsNullOrEmpty(path))
if (string.IsNullOrEmpty(path))
return;
return;
var presetJson = (JSONClass)LoadJSON(path);
var presetJson = (JSONClass)LoadJSON(path);
LoadFromJSON(presetJson);
LoadFromJSON(presetJson);
}
}
Copier
Copié
Copier
Copié
/*
The preset JSON is pluginManager.GetJSON(), and this plugin is itself in
that manager's plugin list. Restoring the list therefore destroys and
re-creates this very script, whose Init() schedules PostLoad(), which
loads the preset again -> infinite reload loop.
Guard: only reload plugins when the list in the JSON actually differs
from what is currently loaded. On the second pass the lists match, so
we skip straight to restoring settings and the recursion terminates.
*/
private bool PluginListMatchesJSON(JSONClass jc) {
if(jc == null || !jc.HasKey("plugins")) return false;
JSONClass target = jc["plugins"].AsObject;
JSONClass current = pluginManager.GetJSON()["plugins"] as JSONClass;
if(target == null) return false;
if(current == null) return target.Count == 0;
if(current.Count != target.Count) return false;
foreach(KeyValuePair<string, JSONNode> kv in target) {
if(current[kv.Key] == null) return false;
if(current[kv.Key].Value != kv.Value.Value) return false;
}
return true;
}
private void LoadFromJSON(JSONClass jc) {
private void LoadFromJSON(JSONClass jc) {
Copier
Copié
Copier
Copié
pluginManager.LateRestoreFromJSON(jc, true, true, false);
if(!PluginListMatchesJSON(jc)) {
pluginManager.LateRestoreFromJSON(jc, true, true, false);
}
if(jc.HasKey("storables")) {
if(jc.HasKey("storables")) {
JSONArray storables = jc["storables"].AsArray;
JSONArray storables = jc["storables"].AsArray;
foreach(MVRScript script in pluginManager.GetComponentsInChildren<MVRScript>(true)) {
foreach(MVRScript script in pluginManager.GetComponentsInChildren<MVRScript>(true)) {
foreach(JSONClass storable in storables) {
foreach(JSONClass storable in storables) {
string id = storable["id"];
string id = storable["id"];
if(id == script.storeId) {
if(id == script.storeId) {
script.RestoreFromJSON(storable);
script.RestoreFromJSON(storable);
script.LateRestoreFromJSON(storable);
script.LateRestoreFromJSON(storable);
}
}
}
}
}
}
}
}
}
}
private void SavePreset(string path) {
private void SavePreset(string path) {
SuperController.singleton.fileBrowserUI.fileFormat = null;
SuperController.singleton.fileBrowserUI.fileFormat = null;
if (string.IsNullOrEmpty(path)) {
if (string.IsNullOrEmpty(path)) {
return;
return;
}
}
if (!path.ToLower().EndsWith($".{fileExt}")) {
if (!path.ToLower().EndsWith($".{fileExt}")) {
path += $".{fileExt}";
path += $".{fileExt}";
}
}
SaveJSON(GetJSON(true,true,true), path);
SaveJSON(GetJSON(true,true,true), path);
}
}
/*
/*
A forced reload of the parent clothing will only clear specific
A forced reload of the parent clothing will only clear specific
types of components. MaterialOptions are one of them, and should
types of components. MaterialOptions are one of them, and should
otherwise be inert.
otherwise be inert.
Using this to detect that the clothing is reloading and clearing
Using this to detect that the clothing is reloading and clearing
the plugins.
the plugins.
Not doing this causes the plugin managers and plugins to accumulate.
Not doing this causes the plugin managers and plugins to accumulate.
*/
*/
public class Destructor : MaterialOptions {
public class Destructor : MaterialOptions {
public MVRPluginManager pluginManager = null;
public MVRPluginManager pluginManager = null;
public Atom storedContainingAtom = null;
public Atom storedContainingAtom = null;
public Destructor() {
public Destructor() {
this.overrideId = "ClothingPluginDestructor";
this.overrideId = "ClothingPluginDestructor";
this.paramMaterialSlots = new int[0];
this.paramMaterialSlots = new int[0];
}
}
public void OnDestroy(){
public void OnDestroy(){
if(pluginManager != null) {
if(pluginManager != null) {
foreach(var manager in pluginManager.gameObject.GetComponents<MVRPluginManager>()) {
foreach(var manager in pluginManager.gameObject.GetComponents<MVRPluginManager>()) {
if(manager != pluginManager) {
if(manager != pluginManager) {
SuperController.LogError("Multiple clothing plugin managers detected on deletion. This generally shouldn't happen. Please file a bug report.");
SuperController.LogError("Multiple clothing plugin managers detected on deletion. This generally shouldn't happen. Please file a bug report.");
}
}
manager.containingAtom = storedContainingAtom;
manager.containingAtom = storedContainingAtom;
manager.RemoveAllPlugins();
manager.RemoveAllPlugins();
Destroy(manager);
Destroy(manager);
}
}
}
}
}
}
public override void SetUI(Transform t) {
public override void SetUI(Transform t) {
// No-op
// No-op
}
}
}
}
}
}
}
}
Différences enregistrées
Texte d'origine
Ouvrir un fichier
using System; using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Text; using SimpleJSON; using MVR; using MeshVR; using System.Linq; using Leap.Unity; using MVR.FileManagementSecure; using System.Security.AccessControl; namespace Stopper { public class ClothingPluginManager : MVRScript { string fileExt = "clothingplugins"; DAZDynamic dazDynamic; DAZDynamicItem dazDynamicItem; MVRPluginManager pluginManager; PresetManager presetManager; Destructor destruct; bool needsReload = false; JSONClass sceneJson = null; bool lastRestoreWasSceneRestore = false; public override void Init() { WalkAndGetParentItems(); if(containingAtom == null) throw new Exception("Unable to determine containing atom."); if(pluginManager == null) throw new Exception("Unable to find plugin manager."); if(dazDynamicItem == null) throw new Exception("Unable to find dynamic item."); if(dazDynamic == null) throw new Exception("Unable to find dazDynamic item."); this.overrideId = $"{dazDynamic.uid}:{this.storeId}"; DAZCharacterSelector selector = containingAtom.GetStorableByID("geometry") as DAZCharacterSelector; if(selector == null) throw new Exception("Unable to find CharacterSelector"); pluginManager.containingAtom = containingAtom; pluginManager.gameObject.SetActive(true); pluginManager.exclude = true; presetManager = dazDynamic.GetComponentInChildren<PresetManager>(); List<PresetManager.SpecificStorable> specificStorables = presetManager.specificStorables.ToList(); PresetManager.SpecificStorable specificStorable = null; foreach(var storable in specificStorables) { if(storable.specificKey == "ClothingPluginManager") { specificStorable = storable; specificStorable.specificStorableBucket = this.transform; } } if(specificStorable == null) { specificStorable = new PresetManager.SpecificStorable(); specificStorable.specificKey = "ClothingPluginManager"; specificStorable.storeId = this.storeId; specificStorable.specificStorableBucket = this.transform; specificStorable.includeChildren = false; } presetManager.specificStorables = specificStorables.ToArray(); presetManager.RefreshStorables(); // Add the destructor to unload mods if we do a force reload if(dazDynamicItem != selector.femaleClothingCreatorItem && dazDynamicItem != selector.maleClothingCreatorItem) { destruct = dazDynamic.binaryStorableBucket.gameObject.AddComponent<Destructor>(); destruct.pluginManager = pluginManager; destruct.storedContainingAtom = containingAtom; } // If this isn't set on the plugin manager by the time we reach here, then // this plugin will not show up in the UI. In that case we'll want to reload the plugins. if(pluginManager.pluginPanelPrefab == null) { needsReload = true; } // Copy over all of the prefabs MVRPluginManager containingAtomPluginManager = containingAtom.GetStorableByID("PluginManager") as MVRPluginManager; if(containingAtomPluginManager != null){ pluginManager.pluginPanelPrefab = containingAtomPluginManager.pluginPanelPrefab; pluginManager.scriptControllerPanelPrefab = containingAtomPluginManager.scriptControllerPanelPrefab; pluginManager.scriptUIPrefab = containingAtomPluginManager.scriptUIPrefab; pluginManager.configurableSliderPrefab = containingAtomPluginManager.configurableSliderPrefab; pluginManager.configurableTogglePrefab = containingAtomPluginManager.configurableTogglePrefab; pluginManager.configurableColorPickerPrefab = containingAtomPluginManager.configurableColorPickerPrefab; pluginManager.configurableButtonPrefab = containingAtomPluginManager.configurableButtonPrefab; pluginManager.configurablePopupPrefab = containingAtomPluginManager.configurablePopupPrefab; pluginManager.configurableScrollablePopupPrefab = containingAtomPluginManager.configurableScrollablePopupPrefab; pluginManager.configurableFilterablePopupPrefab = containingAtomPluginManager.configurableFilterablePopupPrefab; pluginManager.configurableTextFieldPrefab = containingAtomPluginManager.configurableTextFieldPrefab; pluginManager.configurableSpacerPrefab = containingAtomPluginManager.configurableSpacerPrefab; } else { throw new Exception("Unable to find plugin manager for main atom."); } // If the Daz clothing item needs a post-load restore, then we are still in scene restore // In that case we want to load the defaults after the item loads, but before the post-load // restore (which will apply scene settings) if(!dazDynamicItem.enabled && dazDynamicItem.needsPostLoadJSONRestore) { dazDynamicItem.onLoadedHandlers += PostLoad; } else if (needsReload) { StartCoroutine("PostLoadAfterDelay"); } StartCoroutine("AddPluginTabToUI"); } public void Start() { // UI // Preset code was adapted from AcidBubbles' Collider Editor var savePath = dazDynamic.GetStoreFolderPath(false); var savePresetUI = CreateButton("Save Plugin Presets"); savePresetUI.button.onClick.AddListener(() => { FileManagerSecure.CreateDirectory(savePath); var fileBrowserUI = SuperController.singleton.fileBrowserUI; fileBrowserUI.SetTitle("Save Plugin Presets"); fileBrowserUI.fileRemovePrefix = null; fileBrowserUI.hideExtension = false; fileBrowserUI.keepOpen = false; fileBrowserUI.fileFormat = fileExt; fileBrowserUI.defaultPath = savePath; fileBrowserUI.showDirs = true; fileBrowserUI.shortCuts = null; fileBrowserUI.browseVarFilesAsDirectories = false; fileBrowserUI.SetTextEntry(true); fileBrowserUI.Show(SavePreset); fileBrowserUI.fileEntryField.text = GetDefaultFileName() + "." + fileExt; fileBrowserUI.ActivateFileNameField(); }); var loadPresetUI = CreateButton("Load Plugin Presets"); loadPresetUI.button.onClick.AddListener(() => { FileManagerSecure.CreateDirectory(savePath); var shortcuts = FileManagerSecure.GetShortCutsForDirectory(savePath); SuperController.singleton.GetMediaPathDialog(LoadPreset, fileExt, savePath, false, true, false, null, false, shortcuts); }); this.exclude = false; } /* There might be a more elegant way to do this but this works for now. */ private void WalkAndGetParentItems(){ GameObject parent = this.gameObject; while(this.containingAtom == null || parent != this.containingAtom.gameObject){ if(parent.transform.parent == null){ return; } if(pluginManager == null){ pluginManager = parent.GetComponent<MVRPluginManager>(); } if(dazDynamic == null){ dazDynamic = parent.GetComponent<DAZDynamic>(); } if(dazDynamicItem == null){ dazDynamicItem = parent.GetComponent<DAZDynamicItem>(); } this.containingAtom = parent.GetComponent<Atom>(); parent = parent.transform.parent.gameObject; } } private void PostLoad() { SyncClothSimSettings(); string namedDefaultFile = $"{dazDynamic.GetStoreFolderPath(true)}{GetDefaultFileName()}.{fileExt}"; string defaultFile = $"{dazDynamic.GetStoreFolderPath(true)}default.{fileExt}"; if(lastRestoreWasSceneRestore && sceneJson != null && sceneJson.Count > 0){ LoadFromJSON(sceneJson); } else if(FileManagerSecure.FileExists(namedDefaultFile)){ LoadPreset(namedDefaultFile); } else if (FileManagerSecure.FileExists(defaultFile)){ LoadPreset(defaultFile); } else if (needsReload) { // If there is no default, we may still want to re-load plugins once now that the // plugin manager is fully set up. This is mostly to make sure that this plugin // shows up in the UI. pluginManager.LateRestoreFromJSON(pluginManager.GetJSON()); } } // From the dotnet runtime, since plugins don't have access to System.IO.Path private static char[] GetInvalidFileNameChars() => new char[] { '\"', '<', '>', '|', '\0', (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, (char)31, ':', '*', '?', '\\', '/' }; private string GetDefaultFileName(){ return String.Join("_", dazDynamicItem.displayName.Split(GetInvalidFileNameChars(), StringSplitOptions.RemoveEmptyEntries) ).TrimEnd('.') + ".default"; } /* VAM has an issue on subsequent forced reloads where the ClothSimControl may get out of sync with the ClothSettings. This fixes that. */ private void SyncClothSimSettings(){ ClothSimControl clothSimControl = dazDynamicItem.GetComponentInChildren<ClothSimControl>(); if(clothSimControl != null) { SyncStorable(clothSimControl, "simEnabled"); SyncStorable(clothSimControl, "integrateEnabled"); SyncStorable(clothSimControl, "collisionEnabled"); SyncStorable(clothSimControl, "collisionRadius"); SyncStorable(clothSimControl, "drag"); SyncStorable(clothSimControl, "weight"); SyncStorable(clothSimControl, "distanceScale"); SyncStorable(clothSimControl, "stiffness"); SyncStorable(clothSimControl, "compressionResistance"); SyncStorable(clothSimControl, "friction"); SyncStorable(clothSimControl, "staticMultiplier"); SyncStorable(clothSimControl, "collisionPower"); SyncStorable(clothSimControl, "gravityMultiplier"); SyncStorable(clothSimControl, "iterations"); SyncStorable(clothSimControl, "allowDetach"); SyncStorable(clothSimControl, "detachThreshold"); SyncStorable(clothSimControl, "jointStrength"); SyncStorable(clothSimControl, "force"); } } private void SyncStorable(JSONStorable parent, string name) { var storable = parent.GetParam(name); var storableBool = storable as JSONStorableBool; if(storableBool != null && storableBool.setCallbackFunction != null) { storableBool.setCallbackFunction(storableBool.val); } var storableFloat = storable as JSONStorableFloat; if(storableFloat != null && storableFloat.setCallbackFunction != null) { storableFloat.setCallbackFunction(storableFloat.val); } var storableVector = storable as JSONStorableVector3; if(storableVector != null && storableVector.setCallbackFunction != null) { storableVector.setCallbackFunction(storableVector.val); } } private IEnumerator PostLoadAfterDelay() { yield return new WaitForEndOfFrame(); PostLoad(); yield break; } public IEnumerator AddPluginTabToUI() { MVRPluginManager containingAtomPluginManager = containingAtom.GetStorableByID("PluginManager") as MVRPluginManager; if(containingAtomPluginManager == null) throw new Exception("Cannot find plugin manager for parent atom."); // Wait for the Person's plugin manager UI to be loaded. // This should happen when the Person's UI is first opened. Transform pluginManagerUITransform = null; while(pluginManagerUITransform == null) { yield return null; pluginManagerUITransform = containingAtomPluginManager.UITransform; } // Wait for the clothing item's customization UI to be created // This should happen when the user clicks 'Customize' on the clothing Transform customizationUI = dazDynamicItem.customizationUI; while(customizationUI == null) { yield return null; customizationUI = dazDynamicItem.customizationUI; } // Wait for the customization UI to be active while(!customizationUI.gameObject.activeInHierarchy) { yield return null; } // This should have been initialized with the PluginManager's IO but I've seen it null here. // Race conditions? Me being dumb? Who knows. Adding wait loop. MVRPluginManagerUI containingAtomPluginManagerUI = pluginManagerUITransform.GetComponentInChildren<MVRPluginManagerUI>(true); while(containingAtomPluginManagerUI == null) { yield return new WaitForSeconds(0.1f); containingAtomPluginManagerUI = pluginManagerUITransform.GetComponentInChildren<MVRPluginManagerUI>(true); } UIConnectorMaster uiConnectorMaster = customizationUI.GetComponent<UIConnectorMaster>(); TabbedUIBuilder tabbedUIBuilder = uiConnectorMaster.GetComponentInChildren<TabbedUIBuilder>(); foreach(var child in tabbedUIBuilder.selector.toggleContainer.GetChildren()) { if(child.name == "Plugins") { yield break; } } Transform pluginTabPrefab = UnityEngine.Object.Instantiate<Transform>(containingAtomPluginManagerUI.transform,tabbedUIBuilder.selector.transform); // Remove any plugin UIs we inadvertently copied over foreach(var script in pluginTabPrefab.GetComponentsInChildren<MVRScriptUI>(true)) { Destroy(script.gameObject); } foreach(var script in pluginTabPrefab.GetComponentsInChildren<MVRPluginUI>(true)) { Destroy(script.gameObject); } TabbedUIBuilder.Tab pluginsTab = new TabbedUIBuilder.Tab{name = "Plugins", prefab = pluginTabPrefab, color = Color.blue}; tabbedUIBuilder.AddTab(pluginsTab); pluginManager.UITransform = pluginTabPrefab.transform; pluginManager.InitUI(); yield break; } public void OnEnable() { if(containingAtom != null) { StartCoroutine("AddPluginTabToUI"); } } public override JSONClass GetJSON(bool includePhysical = true, bool includeAppearance = true, bool forceStore = false) { JSONClass jc = pluginManager.GetJSON(true,true,true); jc["id"] = this.storeId; JSONArray storables = new JSONArray(); foreach(MVRScript script in pluginManager.GetComponentsInChildren<MVRScript>(true)) { if(script != this) { storables.Add(script.GetJSON()); } } jc["storables"] = storables; this.needsStore = true; return jc; } public override void RestoreFromJSON(JSONClass jc, bool restorePhysical = true, bool restoreAppearance = true, JSONArray presetAtoms = null, bool setMissingToDefault = true){ if(this.isPresetRestore) { if(jc.Count > 0) { LoadFromJSON(jc); } } else { sceneJson = jc; lastRestoreWasSceneRestore = SuperController.singleton.isLoading; } } private void LoadPreset(string path) { if (string.IsNullOrEmpty(path)) return; var presetJson = (JSONClass)LoadJSON(path); LoadFromJSON(presetJson); } private void LoadFromJSON(JSONClass jc) { pluginManager.LateRestoreFromJSON(jc, true, true, false); if(jc.HasKey("storables")) { JSONArray storables = jc["storables"].AsArray; foreach(MVRScript script in pluginManager.GetComponentsInChildren<MVRScript>(true)) { foreach(JSONClass storable in storables) { string id = storable["id"]; if(id == script.storeId) { script.RestoreFromJSON(storable); script.LateRestoreFromJSON(storable); } } } } } private void SavePreset(string path) { SuperController.singleton.fileBrowserUI.fileFormat = null; if (string.IsNullOrEmpty(path)) { return; } if (!path.ToLower().EndsWith($".{fileExt}")) { path += $".{fileExt}"; } SaveJSON(GetJSON(true,true,true), path); } /* A forced reload of the parent clothing will only clear specific types of components. MaterialOptions are one of them, and should otherwise be inert. Using this to detect that the clothing is reloading and clearing the plugins. Not doing this causes the plugin managers and plugins to accumulate. */ public class Destructor : MaterialOptions { public MVRPluginManager pluginManager = null; public Atom storedContainingAtom = null; public Destructor() { this.overrideId = "ClothingPluginDestructor"; this.paramMaterialSlots = new int[0]; } public void OnDestroy(){ if(pluginManager != null) { foreach(var manager in pluginManager.gameObject.GetComponents<MVRPluginManager>()) { if(manager != pluginManager) { SuperController.LogError("Multiple clothing plugin managers detected on deletion. This generally shouldn't happen. Please file a bug report."); } manager.containingAtom = storedContainingAtom; manager.RemoveAllPlugins(); Destroy(manager); } } } public override void SetUI(Transform t) { // No-op } } } }
Texte modifié
Ouvrir un fichier
using System; using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Text; using SimpleJSON; using MVR; using MeshVR; using System.Linq; using Leap.Unity; using MVR.FileManagementSecure; using System.Security.AccessControl; namespace Stopper { public class ClothingPluginManager : MVRScript { string fileExt = "clothingplugins"; DAZDynamic dazDynamic; DAZDynamicItem dazDynamicItem; MVRPluginManager pluginManager; PresetManager presetManager; Destructor destruct; bool needsReload = false; JSONClass sceneJson = null; bool lastRestoreWasSceneRestore = false; public override void Init() { WalkAndGetParentItems(); if(containingAtom == null) throw new Exception("Unable to determine containing atom."); if(pluginManager == null) throw new Exception("Unable to find plugin manager."); if(dazDynamicItem == null) throw new Exception("Unable to find dynamic item."); if(dazDynamic == null) throw new Exception("Unable to find dazDynamic item."); this.overrideId = $"{dazDynamic.uid}:{this.storeId}"; DAZCharacterSelector selector = containingAtom.GetStorableByID("geometry") as DAZCharacterSelector; if(selector == null) throw new Exception("Unable to find CharacterSelector"); pluginManager.containingAtom = containingAtom; pluginManager.gameObject.SetActive(true); pluginManager.exclude = true; presetManager = dazDynamic.GetComponentInChildren<PresetManager>(); List<PresetManager.SpecificStorable> specificStorables = presetManager.specificStorables.ToList(); PresetManager.SpecificStorable specificStorable = null; foreach(var storable in specificStorables) { if(storable.specificKey == "ClothingPluginManager") { specificStorable = storable; specificStorable.specificStorableBucket = this.transform; } } if(specificStorable == null) { specificStorable = new PresetManager.SpecificStorable(); specificStorable.specificKey = "ClothingPluginManager"; specificStorable.storeId = this.storeId; specificStorable.specificStorableBucket = this.transform; specificStorable.includeChildren = false; } presetManager.specificStorables = specificStorables.ToArray(); presetManager.RefreshStorables(); // Add the destructor to unload mods if we do a force reload if(dazDynamicItem != selector.femaleClothingCreatorItem && dazDynamicItem != selector.maleClothingCreatorItem) { destruct = dazDynamic.binaryStorableBucket.gameObject.AddComponent<Destructor>(); destruct.pluginManager = pluginManager; destruct.storedContainingAtom = containingAtom; } // If this isn't set on the plugin manager by the time we reach here, then // this plugin will not show up in the UI. In that case we'll want to reload the plugins. if(pluginManager.pluginPanelPrefab == null) { needsReload = true; } // Copy over all of the prefabs MVRPluginManager containingAtomPluginManager = containingAtom.GetStorableByID("PluginManager") as MVRPluginManager; if(containingAtomPluginManager != null){ pluginManager.pluginPanelPrefab = containingAtomPluginManager.pluginPanelPrefab; pluginManager.scriptControllerPanelPrefab = containingAtomPluginManager.scriptControllerPanelPrefab; pluginManager.scriptUIPrefab = containingAtomPluginManager.scriptUIPrefab; pluginManager.configurableSliderPrefab = containingAtomPluginManager.configurableSliderPrefab; pluginManager.configurableTogglePrefab = containingAtomPluginManager.configurableTogglePrefab; pluginManager.configurableColorPickerPrefab = containingAtomPluginManager.configurableColorPickerPrefab; pluginManager.configurableButtonPrefab = containingAtomPluginManager.configurableButtonPrefab; pluginManager.configurablePopupPrefab = containingAtomPluginManager.configurablePopupPrefab; pluginManager.configurableScrollablePopupPrefab = containingAtomPluginManager.configurableScrollablePopupPrefab; pluginManager.configurableFilterablePopupPrefab = containingAtomPluginManager.configurableFilterablePopupPrefab; pluginManager.configurableTextFieldPrefab = containingAtomPluginManager.configurableTextFieldPrefab; pluginManager.configurableSpacerPrefab = containingAtomPluginManager.configurableSpacerPrefab; } else { throw new Exception("Unable to find plugin manager for main atom."); } // If the Daz clothing item needs a post-load restore, then we are still in scene restore // In that case we want to load the defaults after the item loads, but before the post-load // restore (which will apply scene settings) if(!dazDynamicItem.enabled && dazDynamicItem.needsPostLoadJSONRestore) { dazDynamicItem.onLoadedHandlers += PostLoad; } else { // Always run PostLoad for runtime activation, not just when the UI // needs a reload. This is what loads the default .clothingplugins // preset; gating it on needsReload meant defaults only loaded in // the rare case where the plugin panel prefab wasn't ready yet. StartCoroutine("PostLoadAfterDelay"); } StartCoroutine("AddPluginTabToUI"); } public void Start() { // UI // Preset code was adapted from AcidBubbles' Collider Editor var savePath = dazDynamic.GetStoreFolderPath(false); var savePresetUI = CreateButton("Save Plugin Presets"); savePresetUI.button.onClick.AddListener(() => { FileManagerSecure.CreateDirectory(savePath); var fileBrowserUI = SuperController.singleton.fileBrowserUI; fileBrowserUI.SetTitle("Save Plugin Presets"); fileBrowserUI.fileRemovePrefix = null; fileBrowserUI.hideExtension = false; fileBrowserUI.keepOpen = false; fileBrowserUI.fileFormat = fileExt; fileBrowserUI.defaultPath = savePath; fileBrowserUI.showDirs = true; fileBrowserUI.shortCuts = null; fileBrowserUI.browseVarFilesAsDirectories = false; fileBrowserUI.SetTextEntry(true); fileBrowserUI.Show(SavePreset); fileBrowserUI.fileEntryField.text = GetDefaultFileName() + "." + fileExt; fileBrowserUI.ActivateFileNameField(); }); var loadPresetUI = CreateButton("Load Plugin Presets"); loadPresetUI.button.onClick.AddListener(() => { FileManagerSecure.CreateDirectory(savePath); var shortcuts = FileManagerSecure.GetShortCutsForDirectory(savePath); SuperController.singleton.GetMediaPathDialog(LoadPreset, fileExt, savePath, false, true, false, null, false, shortcuts); }); this.exclude = false; } /* There might be a more elegant way to do this but this works for now. */ private void WalkAndGetParentItems(){ GameObject parent = this.gameObject; while(this.containingAtom == null || parent != this.containingAtom.gameObject){ if(parent.transform.parent == null){ return; } if(pluginManager == null){ pluginManager = parent.GetComponent<MVRPluginManager>(); } if(dazDynamic == null){ dazDynamic = parent.GetComponent<DAZDynamic>(); } if(dazDynamicItem == null){ dazDynamicItem = parent.GetComponent<DAZDynamicItem>(); } this.containingAtom = parent.GetComponent<Atom>(); parent = parent.transform.parent.gameObject; } } private void PostLoad() { SyncClothSimSettings(); string namedDefaultFile = $"{dazDynamic.GetStoreFolderPath(true)}{GetDefaultFileName()}.{fileExt}"; string defaultFile = $"{dazDynamic.GetStoreFolderPath(true)}default.{fileExt}"; if(lastRestoreWasSceneRestore && sceneJson != null && sceneJson.Count > 0){ LoadFromJSON(sceneJson); } else if(FileManagerSecure.FileExists(namedDefaultFile)){ LoadPreset(namedDefaultFile); } else if (FileManagerSecure.FileExists(defaultFile)){ LoadPreset(defaultFile); } else if (needsReload) { // If there is no default, we may still want to re-load plugins once now that the // plugin manager is fully set up. This is mostly to make sure that this plugin // shows up in the UI. pluginManager.LateRestoreFromJSON(pluginManager.GetJSON()); } } // From the dotnet runtime, since plugins don't have access to System.IO.Path private static char[] GetInvalidFileNameChars() => new char[] { '\"', '<', '>', '|', '\0', (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, (char)31, ':', '*', '?', '\\', '/' }; private string GetDefaultFileName(){ return String.Join("_", dazDynamicItem.displayName.Split(GetInvalidFileNameChars(), StringSplitOptions.RemoveEmptyEntries) ).TrimEnd('.') + ".default"; } /* VAM has an issue on subsequent forced reloads where the ClothSimControl may get out of sync with the ClothSettings. This fixes that. */ private void SyncClothSimSettings(){ ClothSimControl clothSimControl = dazDynamicItem.GetComponentInChildren<ClothSimControl>(); if(clothSimControl != null) { SyncStorable(clothSimControl, "simEnabled"); SyncStorable(clothSimControl, "integrateEnabled"); SyncStorable(clothSimControl, "collisionEnabled"); SyncStorable(clothSimControl, "collisionRadius"); SyncStorable(clothSimControl, "drag"); SyncStorable(clothSimControl, "weight"); SyncStorable(clothSimControl, "distanceScale"); SyncStorable(clothSimControl, "stiffness"); SyncStorable(clothSimControl, "compressionResistance"); SyncStorable(clothSimControl, "friction"); SyncStorable(clothSimControl, "staticMultiplier"); SyncStorable(clothSimControl, "collisionPower"); SyncStorable(clothSimControl, "gravityMultiplier"); SyncStorable(clothSimControl, "iterations"); SyncStorable(clothSimControl, "allowDetach"); SyncStorable(clothSimControl, "detachThreshold"); SyncStorable(clothSimControl, "jointStrength"); SyncStorable(clothSimControl, "force"); } } private void SyncStorable(JSONStorable parent, string name) { var storable = parent.GetParam(name); var storableBool = storable as JSONStorableBool; if(storableBool != null && storableBool.setCallbackFunction != null) { storableBool.setCallbackFunction(storableBool.val); } var storableFloat = storable as JSONStorableFloat; if(storableFloat != null && storableFloat.setCallbackFunction != null) { storableFloat.setCallbackFunction(storableFloat.val); } var storableVector = storable as JSONStorableVector3; if(storableVector != null && storableVector.setCallbackFunction != null) { storableVector.setCallbackFunction(storableVector.val); } } private IEnumerator PostLoadAfterDelay() { yield return new WaitForEndOfFrame(); PostLoad(); yield break; } public IEnumerator AddPluginTabToUI() { MVRPluginManager containingAtomPluginManager = containingAtom.GetStorableByID("PluginManager") as MVRPluginManager; if(containingAtomPluginManager == null) throw new Exception("Cannot find plugin manager for parent atom."); // Wait for the Person's plugin manager UI to be loaded. // This should happen when the Person's UI is first opened. Transform pluginManagerUITransform = null; while(pluginManagerUITransform == null) { yield return null; pluginManagerUITransform = containingAtomPluginManager.UITransform; } // Wait for the clothing item's customization UI to be created // This should happen when the user clicks 'Customize' on the clothing Transform customizationUI = dazDynamicItem.customizationUI; while(customizationUI == null) { yield return null; customizationUI = dazDynamicItem.customizationUI; } // Wait for the customization UI to be active while(!customizationUI.gameObject.activeInHierarchy) { yield return null; } // This should have been initialized with the PluginManager's IO but I've seen it null here. // Race conditions? Me being dumb? Who knows. Adding wait loop. MVRPluginManagerUI containingAtomPluginManagerUI = pluginManagerUITransform.GetComponentInChildren<MVRPluginManagerUI>(true); while(containingAtomPluginManagerUI == null) { yield return new WaitForSeconds(0.1f); containingAtomPluginManagerUI = pluginManagerUITransform.GetComponentInChildren<MVRPluginManagerUI>(true); } UIConnectorMaster uiConnectorMaster = customizationUI.GetComponent<UIConnectorMaster>(); TabbedUIBuilder tabbedUIBuilder = uiConnectorMaster.GetComponentInChildren<TabbedUIBuilder>(); foreach(var child in tabbedUIBuilder.selector.toggleContainer.GetChildren()) { if(child.name == "Plugins") { yield break; } } Transform pluginTabPrefab = UnityEngine.Object.Instantiate<Transform>(containingAtomPluginManagerUI.transform,tabbedUIBuilder.selector.transform); // Remove any plugin UIs we inadvertently copied over foreach(var script in pluginTabPrefab.GetComponentsInChildren<MVRScriptUI>(true)) { Destroy(script.gameObject); } foreach(var script in pluginTabPrefab.GetComponentsInChildren<MVRPluginUI>(true)) { Destroy(script.gameObject); } TabbedUIBuilder.Tab pluginsTab = new TabbedUIBuilder.Tab{name = "Plugins", prefab = pluginTabPrefab, color = Color.blue}; tabbedUIBuilder.AddTab(pluginsTab); pluginManager.UITransform = pluginTabPrefab.transform; pluginManager.InitUI(); yield break; } public void OnEnable() { if(containingAtom != null) { StartCoroutine("AddPluginTabToUI"); } } public override JSONClass GetJSON(bool includePhysical = true, bool includeAppearance = true, bool forceStore = false) { JSONClass jc = pluginManager.GetJSON(true,true,true); jc["id"] = this.storeId; JSONArray storables = new JSONArray(); foreach(MVRScript script in pluginManager.GetComponentsInChildren<MVRScript>(true)) { if(script != this) { storables.Add(script.GetJSON()); } } jc["storables"] = storables; this.needsStore = true; return jc; } public override void RestoreFromJSON(JSONClass jc, bool restorePhysical = true, bool restoreAppearance = true, JSONArray presetAtoms = null, bool setMissingToDefault = true){ if(this.isPresetRestore) { if(jc.Count > 0) { LoadFromJSON(jc); } } else { sceneJson = jc; lastRestoreWasSceneRestore = SuperController.singleton.isLoading; } } private void LoadPreset(string path) { if (string.IsNullOrEmpty(path)) return; var presetJson = (JSONClass)LoadJSON(path); LoadFromJSON(presetJson); } /* The preset JSON is pluginManager.GetJSON(), and this plugin is itself in that manager's plugin list. Restoring the list therefore destroys and re-creates this very script, whose Init() schedules PostLoad(), which loads the preset again -> infinite reload loop. Guard: only reload plugins when the list in the JSON actually differs from what is currently loaded. On the second pass the lists match, so we skip straight to restoring settings and the recursion terminates. */ private bool PluginListMatchesJSON(JSONClass jc) { if(jc == null || !jc.HasKey("plugins")) return false; JSONClass target = jc["plugins"].AsObject; JSONClass current = pluginManager.GetJSON()["plugins"] as JSONClass; if(target == null) return false; if(current == null) return target.Count == 0; if(current.Count != target.Count) return false; foreach(KeyValuePair<string, JSONNode> kv in target) { if(current[kv.Key] == null) return false; if(current[kv.Key].Value != kv.Value.Value) return false; } return true; } private void LoadFromJSON(JSONClass jc) { if(!PluginListMatchesJSON(jc)) { pluginManager.LateRestoreFromJSON(jc, true, true, false); } if(jc.HasKey("storables")) { JSONArray storables = jc["storables"].AsArray; foreach(MVRScript script in pluginManager.GetComponentsInChildren<MVRScript>(true)) { foreach(JSONClass storable in storables) { string id = storable["id"]; if(id == script.storeId) { script.RestoreFromJSON(storable); script.LateRestoreFromJSON(storable); } } } } } private void SavePreset(string path) { SuperController.singleton.fileBrowserUI.fileFormat = null; if (string.IsNullOrEmpty(path)) { return; } if (!path.ToLower().EndsWith($".{fileExt}")) { path += $".{fileExt}"; } SaveJSON(GetJSON(true,true,true), path); } /* A forced reload of the parent clothing will only clear specific types of components. MaterialOptions are one of them, and should otherwise be inert. Using this to detect that the clothing is reloading and clearing the plugins. Not doing this causes the plugin managers and plugins to accumulate. */ public class Destructor : MaterialOptions { public MVRPluginManager pluginManager = null; public Atom storedContainingAtom = null; public Destructor() { this.overrideId = "ClothingPluginDestructor"; this.paramMaterialSlots = new int[0]; } public void OnDestroy(){ if(pluginManager != null) { foreach(var manager in pluginManager.gameObject.GetComponents<MVRPluginManager>()) { if(manager != pluginManager) { SuperController.LogError("Multiple clothing plugin managers detected on deletion. This generally shouldn't happen. Please file a bug report."); } manager.containingAtom = storedContainingAtom; manager.RemoveAllPlugins(); Destroy(manager); } } } public override void SetUI(Transform t) { // No-op } } } }
Trouver la différence