Skip to content

Editor

Interested in Development?

Project EAUploader is always looking for development members! Even if you just want to talk to us! Please visit our project’s Discord server. Join here



This page explains the processing flow of the EAUploader.Editor assembly, the main body of EAUploader. For more detailed specifications, also see the specifications (in English) in the Docs/ folder of the repository.

This document is based on EAUploader v1.6.2.

Startup


EAUploader’s startup is handled by EAUploaderCore (Editor/Scripts/EAUploaderCore.cs).

1. Static Constructor

EAUploaderCore has the [InitializeOnLoad] attribute, so its static constructor runs every time Unity starts or scripts are recompiled (domain reload).

  1. Registers the log output handler to Application.logMessageReceived.
  2. Decides the name of the log file and logs environment information (date, log file GUID, EAUploader version, Unity version, platform, list of VPM packages).
  3. Registers a wait for initialization to EditorApplication.update.

2. Initialization

Once Unity is neither compiling nor entering play mode, PerformInitialization runs only once.

  1. InitializeEAUploader ― Creates the Assets/EAUploader folder and PrefabManager.json if they do not exist.
  2. AvatarUploader.CleanupLeftoverBuildInstances ― Removes temporary upload instances left in the scene by an interrupted multi-platform build.
  3. EAUploaderEditorManager.OnEditorManagerLoad ― Initializes the edit tool registration information (EditorsManage.json).
  4. ShaderChecker.CheckShaders ― Checks for prefabs with missing shaders.
  5. PrefabManager.Initialize ― Prepares the managed folder, runs the migration from older versions (described later), and generates missing preview images.
  6. CheckAvailablePackages ― Reads Packages/packages-lock.json and sets HasVRM, HasAAO, and HasMA.
  7. Finds static methods with the [EAUPlugin] attribute in all assemblies and calls them. (Registration of edit tools and extensions)
  8. Opens the EAUploader window via EditorApplication.delayCall.

If an exception occurs during initialization, a dialog prompting a restart of EAUploader is shown. Pressing “Restart” re-imports Packages/tech.uslog.eauploader.

”EAUploader” → “Reload” in the menu (EAUploaderCore.ReloadSDK) clears the selection and runs steps 3–6 above again.

Global State

MemberContents
EAUploaderCore.selectedPrefabPathPath of the selected item. Changing it raises SelectedPrefabPathChanged. All screens and edit tools act on this item.
EAUploaderCore.HasVRM / HasAAO / HasMAWhether the integrated packages are installed
EAUploaderCore.GetVersion()Gets the version from package.json

Data Storage


PathContentsClass
Assets/EAUploader/settings.jsonlanguage, isLibraryOpen, showErrorReportWindow, managedPrefabsMigratedConfig
Assets/EAUploader/PrefabManager.jsonFor compatibility with older versions (created empty)EAUploaderCore
Assets/EAUploader/EditorsManage.jsonRegistered edit tools (rebuilt at every startup)EAUploaderEditorManager
Assets/EAUploader/tags.jsonTag names and tag groupsTagManager
Assets/EAUploader/Prefabs/<item>/<Variant>.prefabManaged Prefab VariantsManagedPrefabs
Assets/EAUploader/Prefabs/<item>/Textures/, Materials/Assets generated by the Texture EditorTexture Editor
Assets/EAUploader/PrefabPreviews/<GUID>.pngPreview images (1200×900). Named by the Prefab GUID, so they are kept when renamingPrefabPreview
EAUploaderLog/ (project root)Log files (up to 100MB in total)UI.Windows.Logger
EditorPrefs EAUploader.ThemeTheme (white / dark / pastel)ThemeUtility
EditorPrefs EAUploader.CardWidthCard width on the Avatar ListAvatar List

Item Management


Managed Prefab Variants

EAUploader never modifies the Prefabs imported by the user. For every imported Prefab it creates a Prefab Variant and manages and edits only that Variant.

Assets/EAUploader/Prefabs/<item name>/<Variant name>.prefab
  • ManagedPrefabs.ROOT is Assets/EAUploader/Prefabs. One folder is created per imported item.
  • A Variant is created by instantiating the source Prefab in a temporary preview scene, adding EAUploaderMeta, and saving it with PrefabUtility.SaveAsPrefabAsset. The open scene is not touched.
  • EAUploaderMeta is added only to the Variant.
  • Assets generated by edit tools and so on are stored in the item’s folder.
ManagedPrefabs methodContents
IsManaged(path)Whether the path is under ROOT/
IsInEAUploaderFolder(path)Whether the path is under Assets/EAUploader/ (excluded from import)
CreateVariant(sourcePath, configure)Creates a Variant in a new item folder
DuplicateVariant(variantPath)Duplicates the Variant in the same item folder
GetSourcePath(variantPath)Gets the source Prefab of a Variant
RenameItemFolder(variantPath, name)Renames the folder when it contains only that Prefab

Metadata (EAUploaderMeta)

EAUploaderMeta, added to the root of the Variant, stores the display state in the list (status), type (type), genre (genre), last modified time (lastModifiedTicks), memo (memo), tags (tags), and more. See “Runtime” for details.

PrefabInfo is a UI-facing snapshot of a managed Prefab (path, name, last modified time, type, status, genre, tags, memo, preview image).

Processing on Import

AssetImportProcessor (Editor/Scripts/AssetProcessor.cs) does the following for each imported .prefab.

  1. ShaderChecker.CheckShadersInPrefabs ― Shows a dialog if shaders are missing. The dialog’s “Why am I seeing this?” button opens “Shader not found error” in this documentation (https://eauploader-docs.uslog.tech/faq/missing_shader).
  2. PrefabManager.ImportPrefab ― Creates or updates the managed Variant.
  3. Collects non-avatar items that need a genre decision and shows them together in ClothSettingModal on the next delayCall (only when the main window is open).

PrefabManager.ImportPrefab(path) works as follows.

  • Unmanaged Prefab (skipped when outside Assets/, inside Assets/EAUploader/, when a Variant already exists, or while SuppressAutoVariantCreation is set)
    • Creates a Variant and sets type and lastModifiedTicks.
    • Avatars (with VRC_AvatarDescriptor) get genre = Avatar, genreConfirmed = true.
    • Everything else gets genre = Cloth, genreConfirmed = false and becomes a target of the genre selection modal.
    • Saves a preview image and refreshes the list.
  • Managed Prefab
    • Re-detects type. Avatars get genre = Avatar.
    • Updates lastModifiedTicks only if the recorded time is more than 5 seconds old (so the save it triggers does not loop through the import processing again).
    • Saves a preview image when anything changed.

Listing

PrefabManager.GetAllPrefabs() scans managed Variants that have EAUploaderMeta and caches the result for 2 seconds. InvalidateCache() forces a rescan. GetAllPrefabsWithPreview() excludes hidden items, and GetAllPrefabsIncludingHidden() includes them. Both sort pinned items first, then newest first.

Tags (TagManager)

  • Tag names and groups are stored project-wide in Assets/EAUploader/tags.json, and attachment to items is stored in EAUploaderMeta.tags.
  • Comparison is case-insensitive. A new tag that differs only in case reuses the existing spelling.
  • Tag and group names can be up to 32 characters (MAX_TAG_LENGTH).
  • A tag belongs to at most one group. Deleting a tag removes it from all Prefabs; deleting a group keeps its tags.

Deletion

PrefabManager.ShowDeletePrefabDialog(path, completed) opens the in-window deletion confirmation modal.

  1. Searches for the Variant’s source Prefab, materials, and textures. Reference checks cover .prefab/.unity/.controller/.overridecontroller/.anim/.asset/.mat files under Assets/; assets used by other assets are excluded from deletion.
  2. Shows what will be deleted as a read-only list. Deletion can be confirmed once the search completes.
  3. Search results are reused while the asset database is unchanged. If assets change, it searches again before deleting, and asks for confirmation again if the targets changed.
  4. After deletion, removes the item folder if empty, clears the selection if needed, and calls completed(true). On cancellation, it calls completed(false).

Migration from Older Versions (LegacyPrefabMigration)

Older versions attached EAUploaderMeta directly to imported Prefabs. On startup, if managedPrefabsMigrated is not set and such Prefabs exist outside the managed folder, the user is asked to migrate.

  1. Reads the metadata from the source Prefab.
  2. Removes EAUploaderMeta from the source Prefab (before creating the Variant, so it is not inherited).
  3. Creates a Variant carrying the metadata, and moves the old name-based preview image to the GUID-based path.

If creation fails, the source Prefab’s data is restored. Migration is marked complete only if every item succeeds, and choosing “Later” asks again at the next startup.

UI


EAUploader’s UI is built with UI Toolkit (UXML / USS).

  • There is only one main window, UI.EAUploader (menu “EAUploader” → “Open EAUploader”, default size 1280×640, minimum 1080×640).
  • CreateGUI() loads UI/styles.uss and UI/tailwind.uss, clones MainWindow.uxml (only contentRoot), adds the shared Modal and notifications (ToastManager), and applies the theme.
  • Screens are not tabs; one screen at a time is built into contentRoot. EAUploader.Navigate(screen) switches screens, and EAUploader.RefreshCurrentScreen() rebuilds the current screen.
EAUploaderScreenBuilderContents
AvatarListUI.AvatarList.Main.ShowContentAvatar List
SetupUI.Setup.Main.ShowContentEdit screen
UploadUI.Setup.Main.ShowContent (with the upload sheet open)Upload
  • Upload is not a separate screen; UI.Upload.UploadForm is shown as a sheet on the Edit screen.
  • Custom UI components are in Editor/Resources/UI/Components, and modals in Editor/Resources/UI/Modals. In UXML, declare xmlns:eau="EAUploader.UI.Components" to use them. See “UI Components” for details.

Localization

The main window runs LanguageUtility.Localization every 100ms and translates the strings of TextElements in the window. Elements with the USS class no-translate (user content such as names and tags) and text input contents are not translated. Write English strings directly in UXML, and use T7e.Get("English text") to set strings from C#. See “Localization and Resources” for details.

Upload


Build and upload are handled by AvatarUploader (Editor/Scripts/AvatarUploader.cs), using the VRChat SDK builder API (IVRCSdkAvatarBuilderApi). Only items whose genre is Avatar can be uploaded.

Build Instance

Both test and upload create a build instance with CreateBuildInstance(prefabPath).

  1. Loads the managed Prefab and, if it has no Blueprint ID, assigns one and saves it to the Prefab (so the avatar stays linked across domain reloads caused by platform switches).
  2. Instantiates it in the active scene and unpacks it completely. This lets non-destructive tools such as Modular Avatar and AvatarOptimizer (NDMF) process it at build time, and build-time changes are never written back to the Prefab asset.
  3. Names it <name> (EAUploader Upload).

The instance is destroyed after the build. During a VRChat SDK multi-platform build it is kept because the SDK keeps using it, and it is removed by CleanupLeftoverBuildInstances at the next startup.

Test (BuildAndTestAsync)

Opens the VRChat SDK control panel, builds with builder.BuildAndTest, and shows the result with DialogPro. Only one build or upload can run at a time.

Upload (UploadAvatarAsync)

  1. Decides the platforms to build (Windows first, then the selected platforms whose build support is installed).
  2. Creates the build instance and fetches the existing VRCAvatar by Blueprint ID. Creates a new record if none exists.
  3. For a single platform, calls builder.BuildAndUpload. For multiple platforms, sets the SDK’s multi-platform targets and calls builder.BuildAndUploadMultiPlatform (Windows → additional platforms → back to the original platform; a domain reload may happen in between).
  4. Copies the Blueprint ID assigned by the SDK back to the Prefab asset.
  5. Shows success, cancellation, or failure, and raises OnComplete.

Progress is forwarded from the SDK events (OnSdkBuildProgress, OnSdkUploadProgress) to AvatarUploader.ProgressChanged. CancelUpload() stops the upload.

Avatar Performance

The avatar performance in the upload sheet is calculated by PerformanceInfoComputer (Editor/Scripts/VRCSDK/PerformanceInfo.cs) using the VRChat SDK’s AvatarPerformance. It currently uses the PC (Windows) criteria.

Automatic Optimization

When AvatarOptimizer is installed (HAS_AAO), turning on “Automatic avatar optimizing” adds a TraceAndOptimize component to the root of the avatar. Turning it off removes it.

Edit Tools


EAUploaderEditorManager holds the EditorRegistrations registered with RegisterEditor and also writes them to Assets/EAUploader/EditorsManage.json. “Edit tool” on the Edit screen is displayed based on the list obtained with GetRegisteredEditors, and pressing a button executes the menu in EditorRegistration.MenuName with EditorApplication.ExecuteMenuItem.

The built-in edit tools use a shared base in Editor/Resources/UI/TabContents/Setup/Editor/Common/ (namespace EAUploader.UI.Setup.EditTools).

TypeResponsibility
EditToolSessionOpens the managed Prefab with PrefabUtility.LoadPrefabContents into an isolated preview scene, tracks whether there are changes, saves with PrefabUtility.SaveAsPrefabAsset (a Variant stays a Variant), reloads (revert), and retargets.
EditToolModalAbstract base class of the built-in edit tools. Shows a tool as a modal inside the EAUploader window with a common layout: header (tool name, item name, “Unsaved changes”, Back), side panel, live preview, and footer (Revert, Apply). Only one tool can be open at a time.
ToolModalFrameModal frame shared by all tools, including the Viewpoint Position Editor.
EditToolUtilityGenerated-asset folders, file name sanitizing, post-save refresh (OnPrefabSaved), shared UI parts, requirement checks.
  • While a tool has unsaved changes, the EAUploader window reports hasUnsavedChanges and asks to save when the window is closed.
  • After saving, EditToolUtility.OnPrefabSaved invalidates the PrefabManager cache, regenerates the preview image, and refreshes the list and the Edit screen.
  • Tool previews use the live mode of the shared Preview component (new Preview(container, session.Root)).

For details on how the built-in edit tools work, see Docs/EditTools.md in the repository. For how to create extensions, see “Quick Start Guidance”.

Logging


See “Logging Function Specification” for logging and the error report.