Diff
checker
텍스트
텍스트
이미지
문서
Excel
폴더
Legal
Enterprise
데스크톱
요금제
로그인
데스크톱 앱 다운로드
텍스트 비교
두 텍스트 파일의 차이점을 찾아보세요
도구
기록
실시간 편집
공백 변경 숨기기
변경 없는 행 숨기기
줄바꿈 비활성화
레이아웃
나란히 보기
합쳐 보기
비교 단위
스마트
단어
글자
텍스트 스타일
모양 변경
구문 강조
언어 선택
제외
텍스트 변환
첫 변경으로
수정
Diffchecker Desktop
가장 안전하게 Diffchecker를 사용하는 방법. 데스크톱 앱을 사용하면 비교 데이터가 외부로 전송되지 않습니다!
데스크톱 앱 받기
ClothingPluginManager_patched
생성일
11시간 전
비교 결과 만료 없음
초기화
내보내기
공유
설명
1 삭제
행
총
삭제
글자
총
삭제
이 기능을 계속 사용하려면 업그레이드해 주세요
Diff
checker
Pro
요금제 보기
435 행
복사
36 추가
행
총
추가
글자
총
추가
이 기능을 계속 사용하려면 업그레이드해 주세요
Diff
checker
Pro
요금제 보기
468 행
복사
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;
복사
복사됨
복사
복사됨
} 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);
}
}
복사
복사됨
복사
복사됨
/*
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) {
복사
복사됨
복사
복사됨
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
}
}
}
}
}
}
}
}
저장된 비교 결과
원본
파일 열기
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 } } } }
수정본
파일 열기
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 } } } }
비교하기