using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using VF.Model.StateAction; using VRC.SDK3.Avatars.Components; using VRC.SDK3.Avatars.ScriptableObjects; using VRC.SDKBase; // Notes for the future: // Don't ever remove a class -- it will break the entire list of SerializedReferences that contained it // Don't mark a class as Obsolete or MovedFrom -- unity 2019 will go into an infinite loop and die namespace VF.Model.Feature { [AttributeUsage(AttributeTargets.Class)] public class NoBuilder : Attribute { } [Serializable] public abstract class FeatureModel { } [Serializable] public abstract class NewFeatureModel : FeatureModel, ISerializationCallbackReceiver { public int version = -1; private int GetVersion() { return version < 0 ? GetLatestVersion() : version; } public void Upgrade() { var fromVersion = GetVersion(); var latestVersion = GetLatestVersion(); if (fromVersion != latestVersion) { Upgrade(fromVersion); version = latestVersion; } } public virtual void Upgrade(int fromVersion) { } public virtual int GetLatestVersion() { return 0; } public void OnAfterDeserialize() { if (version < 0) { // Object was deserialized, but had no version. Default to version 0. version = 0; } } public void OnBeforeSerialize() { if (version < 0) { // Object was created fresh (not deserialized), so it's automatically the newest version = GetLatestVersion(); } } } /** * This class exists because of an annoying unity bug. If a class is serialized with no fields (an empty class), * then if you try to deserialize it into a class with fields, unity blows up and fails. This means we can never * add fields to a class which started without any. This, all features must be migrated to NewFeatureModel, * which contains one field by default (version). */ [Serializable] public abstract class LegacyFeatureModel : FeatureModel { public abstract NewFeatureModel CreateNewInstance(); } [Serializable] public class AvatarScale : LegacyFeatureModel { public override NewFeatureModel CreateNewInstance() { return new AvatarScale2(); } } [Serializable] public class AvatarScale2 : NewFeatureModel { } [Serializable] public class Blinking : NewFeatureModel { public State state; public float transitionTime = -1; public float holdTime = -1; } [Serializable] public class Breathing : NewFeatureModel { public State inState; public State outState; public override void Upgrade(int fromVersion) { if (fromVersion < 1) { if (obj != null) { inState.actions.Add(new ScaleAction { obj = obj, scale = scaleMin }); outState.actions.Add(new ScaleAction { obj = obj, scale = scaleMax }); obj = null; } if (!string.IsNullOrWhiteSpace(blendshape)) { inState.actions.Add(new BlendShapeAction() { blendShape = blendshape, blendShapeValue = 0 }); outState.actions.Add(new BlendShapeAction() { blendShape = blendshape, blendShapeValue = 100 }); blendshape = null; } } } public override int GetLatestVersion() { return 1; } // legacy public GameObject obj; public string blendshape; public float scaleMin; public float scaleMax; } [Serializable] public class FullController : NewFeatureModel { public List controllers = new List(); public List menus = new List(); public List prms = new List(); public List globalParams = new List(); public List removePrefixes = new List(); public string addPrefix = ""; public bool allNonsyncedAreGlobal = false; public bool ignoreSaved; public string toggleParam; public bool useSecurityForToggle = false; public GameObject rootObjOverride; public bool rootBindingsApplyToAvatar; // obsolete public RuntimeAnimatorController controller; public VRCExpressionsMenu menu; public VRCExpressionParameters parameters; public string submenu; [Serializable] public class ControllerEntry { public RuntimeAnimatorController controller; public VRCAvatarDescriptor.AnimLayerType type = VRCAvatarDescriptor.AnimLayerType.FX; public bool ResetMePlease; } [Serializable] public class MenuEntry { public VRCExpressionsMenu menu; public string prefix; public bool ResetMePlease; } [Serializable] public class ParamsEntry { public VRCExpressionParameters parameters; public bool ResetMePlease; } public override void Upgrade(int fromVersion) { if (fromVersion < 1) { allNonsyncedAreGlobal = true; } if (fromVersion < 2) { if (controller != null) { controllers.Add(new ControllerEntry { controller = controller }); controller = null; } if (menu != null) { menus.Add(new MenuEntry { menu = menu, prefix = submenu }); menu = null; } if (parameters != null) { prms.Add(new ParamsEntry { parameters = parameters }); parameters = null; } } } public override int GetLatestVersion() { return 2; } } // Obsolete and removed [Serializable] public class LegacyPrefabSupport : LegacyFeatureModel { public override NewFeatureModel CreateNewInstance() { return new LegacyPrefabSupport2(); } } public class LegacyPrefabSupport2 : NewFeatureModel { } [Serializable] public class ZawooIntegration : NewFeatureModel { public string submenu; } [Serializable] public class Modes : NewFeatureModel { public string name; public bool saved; public bool securityEnabled; public List modes = new List(); public List resetPhysbones = new List(); [Serializable] public class Mode { public State state; public Mode() { this.state = new State(); } public Mode(State state) { this.state = state; } public bool ResetMePlease; } } [Serializable] public class Toggle : NewFeatureModel { public string name; public State state; public bool saved; public bool slider; public bool securityEnabled; public bool defaultOn; public bool includeInRest; public bool exclusiveOffState; public bool enableExclusiveTag; public string exclusiveTag; public List resetPhysbones = new List(); [NonSerialized] public bool addMenuItem = true; [NonSerialized] public bool usePrefixOnParam = true; [NonSerialized] public string paramOverride = null; [NonSerialized] public bool useInt = false; public bool enableIcon; public Texture2D icon; public bool enableDriveGlobalParam; public string driveGlobalParam; public bool separateLocal; public State localState; public bool hasTransition; public State transitionStateIn; public State transitionStateOut; public State localTransitionStateIn; public State localTransitionStateOut; public bool simpleOutTransition = true; public float defaultSliderValue = 1; } [Serializable] public class Puppet : NewFeatureModel { public string name; public bool saved; public bool slider; public List stops = new List(); public float defaultX = 0; public float defaultY = 0; public bool enableIcon; public Texture2D icon; [Serializable] public class Stop { public float x; public float y; public State state; public Stop(float x, float y, State state) { this.x = x; this.y = y; this.state = state; } } } [Serializable] public class SecurityLock : NewFeatureModel { public string pinNumber; } [Serializable] public class SenkyGestureDriver : NewFeatureModel { public State eyesClosed; public State eyesHappy; public State eyesSad; public State eyesAngry; public State mouthBlep; public State mouthSuck; public State mouthSad; public State mouthAngry; public State mouthHappy; public State earsBack; public float transitionTime = -1; } [Serializable] public class Talking : NewFeatureModel { public State state; } [Serializable] public class Toes : NewFeatureModel { public State down; public State up; public State splay; } [Serializable] public class Visemes : NewFeatureModel { public float transitionTime = -1; public State state_sil; public State state_PP; public State state_FF; public State state_TH; public State state_DD; public State state_kk; public State state_CH; public State state_SS; public State state_nn; public State state_RR; public State state_aa; public State state_E; public State state_I; public State state_O; public State state_U; } [Serializable] public class ArmatureLink : NewFeatureModel { public enum ArmatureLinkMode { SkinRewrite, MergeAsChildren, ParentConstraint, ReparentRoot, Auto, } public enum KeepBoneOffsets { Auto, Yes, No } public ArmatureLinkMode linkMode = ArmatureLinkMode.Auto; public GameObject propBone; public HumanBodyBones boneOnAvatar; public string bonePathOnAvatar; public KeepBoneOffsets keepBoneOffsets2 = KeepBoneOffsets.Auto; public string removeBoneSuffix; public bool physbonesOnAvatarBones; public List fallbackBones = new List(); public float skinRewriteScalingFactor = 0; // legacy public bool useOptimizedUpload; public bool useBoneMerging; public bool keepBoneOffsets; public override void Upgrade(int fromVersion) { if (fromVersion < 1) { if (useBoneMerging) { linkMode = ArmatureLinkMode.SkinRewrite; } else { linkMode = ArmatureLinkMode.MergeAsChildren; } } if (fromVersion < 2) { skinRewriteScalingFactor = 1; } if (fromVersion < 3) { keepBoneOffsets2 = keepBoneOffsets ? KeepBoneOffsets.Yes : KeepBoneOffsets.No; } } public override int GetLatestVersion() { return 3; } } [Serializable] public class TPSIntegration : LegacyFeatureModel { public override NewFeatureModel CreateNewInstance() { return new TPSIntegration2(); } } [Serializable] public class TPSIntegration2 : NewFeatureModel { } [Serializable] public class BoundingBoxFix : LegacyFeatureModel { public override NewFeatureModel CreateNewInstance() { return new BoundingBoxFix2(); } } [Serializable] public class BoundingBoxFix2 : NewFeatureModel { [NonSerialized] public SkinnedMeshRenderer singleRenderer; } [Serializable] public class BoneConstraint : NewFeatureModel { public GameObject obj; public HumanBodyBones bone; } [Serializable] public class MakeWriteDefaultsOff : LegacyFeatureModel { public override NewFeatureModel CreateNewInstance() { return new FixWriteDefaults { mode = FixWriteDefaults.FixWriteDefaultsMode.ForceOff }; } } [Serializable] public class MakeWriteDefaultsOff2 : LegacyFeatureModel { public override NewFeatureModel CreateNewInstance() { return new FixWriteDefaults { mode = FixWriteDefaults.FixWriteDefaultsMode.ForceOff }; } } [Serializable] [NoBuilder] public class FixWriteDefaults : NewFeatureModel { public enum FixWriteDefaultsMode { Auto, ForceOff, ForceOn, Disabled } public FixWriteDefaultsMode mode = FixWriteDefaultsMode.Auto; } [Serializable] public class OGBIntegration : LegacyFeatureModel { public override NewFeatureModel CreateNewInstance() { return new OGBIntegration2(); } } [Serializable] public class OGBIntegration2 : NewFeatureModel { } [Serializable] public class RemoveHandGestures : LegacyFeatureModel { public override NewFeatureModel CreateNewInstance() { return new RemoveHandGestures2(); } } [Serializable] public class RemoveHandGestures2 : NewFeatureModel { } [Serializable] public class CrossEyeFix : LegacyFeatureModel { public override NewFeatureModel CreateNewInstance() { return new CrossEyeFix2(); } } [Serializable] public class CrossEyeFix2 : NewFeatureModel { } [Serializable] public class AnchorOverrideFix : LegacyFeatureModel { public override NewFeatureModel CreateNewInstance() { return new AnchorOverrideFix2(); } } [Serializable] public class AnchorOverrideFix2 : NewFeatureModel { } [Serializable] public class MoveMenuItem : NewFeatureModel { public string fromPath; public string toPath; } [Serializable] public class GestureDriver : NewFeatureModel { public List gestures = new List(); [Serializable] public class Gesture { public Hand hand; public HandSign sign; public HandSign comboSign; public State state; public bool disableBlinking; public bool customTransitionTime; public float transitionTime = 0; public bool enableLockMenuItem; public string lockMenuItem; public bool enableExclusiveTag; public string exclusiveTag; public bool enableWeight; public bool ResetMePlease; } public enum Hand { EITHER, LEFT, RIGHT, COMBO } public enum HandSign { NEUTRAL, FIST, HANDOPEN, FINGERPOINT, VICTORY, ROCKNROLL, HANDGUN, THUMBSUP } } [Serializable] public class Gizmo : NewFeatureModel { public Vector3 rotation; public string text; public float sphereRadius; public float arrowLength; } [Serializable] public class ObjectState : NewFeatureModel { public List states = new List(); [Serializable] public class ObjState { public GameObject obj; public Action action = Action.DEACTIVATE; public bool ResetMePlease; } public enum Action { DEACTIVATE, ACTIVATE, DELETE } } [Serializable] public class BlendShapeLink : NewFeatureModel { public List objs; public string baseObj; public bool includeAll = true; public List excludes = new List(); public List includes = new List(); [Serializable] public class Exclude { public string name; } [Serializable] public class Include { public string nameOnBase; public string nameOnLinked; } } [Serializable] public class SetIcon : NewFeatureModel { public string path; public Texture2D icon; } [Serializable] [NoBuilder] public class OverrideMenuSettings : NewFeatureModel { public string nextText; public Texture2D nextIcon; } [Serializable] public class Customizer : NewFeatureModel { [SerializeReference] public List items = new List(); [Serializable] public abstract class CustomizerItem { } public class MenuItem : CustomizerItem { public string key; public string title; public string path; } public class ClipItem : CustomizerItem { public string key; public string title; public AnimationClip clip; } } [Serializable] public class BlendshapeOptimizer : NewFeatureModel { public bool keepMmdShapes; } [Serializable] public class Slot4Fix : NewFeatureModel { } [Serializable] public class DirectTreeOptimizer : NewFeatureModel { } }