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).
- Registers the log output handler to
Application.logMessageReceived. - Decides the name of the log file and logs environment information (date, log file GUID, EAUploader version, Unity version, platform, list of VPM packages).
- Registers a wait for initialization to
EditorApplication.update.
2. Initialization
Once Unity is neither compiling nor entering play mode, PerformInitialization runs only once.
InitializeEAUploader― Creates theAssets/EAUploaderfolder andPrefabManager.jsonif they do not exist.AvatarUploader.CleanupLeftoverBuildInstances― Removes temporary upload instances left in the scene by an interrupted multi-platform build.EAUploaderEditorManager.OnEditorManagerLoad― Initializes the edit tool registration information (EditorsManage.json).ShaderChecker.CheckShaders― Checks for prefabs with missing shaders.PrefabManager.Initialize― Prepares the managed folder, runs the migration from older versions (described later), and generates missing preview images.CheckAvailablePackages― ReadsPackages/packages-lock.jsonand setsHasVRM,HasAAO, andHasMA.- Finds static methods with the
[EAUPlugin]attribute in all assemblies and calls them. (Registration of edit tools and extensions) - 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
| Member | Contents |
|---|---|
EAUploaderCore.selectedPrefabPath | Path of the selected item. Changing it raises SelectedPrefabPathChanged. All screens and edit tools act on this item. |
EAUploaderCore.HasVRM / HasAAO / HasMA | Whether the integrated packages are installed |
EAUploaderCore.GetVersion() | Gets the version from package.json |
Data Storage
| Path | Contents | Class |
|---|---|---|
Assets/EAUploader/settings.json | language, isLibraryOpen, showErrorReportWindow, managedPrefabsMigrated | Config |
Assets/EAUploader/PrefabManager.json | For compatibility with older versions (created empty) | EAUploaderCore |
Assets/EAUploader/EditorsManage.json | Registered edit tools (rebuilt at every startup) | EAUploaderEditorManager |
Assets/EAUploader/tags.json | Tag names and tag groups | TagManager |
Assets/EAUploader/Prefabs/<item>/<Variant>.prefab | Managed Prefab Variants | ManagedPrefabs |
Assets/EAUploader/Prefabs/<item>/Textures/, Materials/ | Assets generated by the Texture Editor | Texture Editor |
Assets/EAUploader/PrefabPreviews/<GUID>.png | Preview images (1200×900). Named by the Prefab GUID, so they are kept when renaming | PrefabPreview |
EAUploaderLog/ (project root) | Log files (up to 100MB in total) | UI.Windows.Logger |
EditorPrefs EAUploader.Theme | Theme (white / dark / pastel) | ThemeUtility |
EditorPrefs EAUploader.CardWidth | Card width on the Avatar List | Avatar 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>.prefabManagedPrefabs.ROOTisAssets/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 withPrefabUtility.SaveAsPrefabAsset. The open scene is not touched. EAUploaderMetais added only to the Variant.- Assets generated by edit tools and so on are stored in the item’s folder.
ManagedPrefabs method | Contents |
|---|---|
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.
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).PrefabManager.ImportPrefab― Creates or updates the managed Variant.- Collects non-avatar items that need a genre decision and shows them together in
ClothSettingModalon the nextdelayCall(only when the main window is open).
PrefabManager.ImportPrefab(path) works as follows.
- Unmanaged Prefab (skipped when outside
Assets/, insideAssets/EAUploader/, when a Variant already exists, or whileSuppressAutoVariantCreationis set)- Creates a Variant and sets
typeandlastModifiedTicks. - Avatars (with
VRC_AvatarDescriptor) getgenre = Avatar,genreConfirmed = true. - Everything else gets
genre = Cloth,genreConfirmed = falseand becomes a target of the genre selection modal. - Saves a preview image and refreshes the list.
- Creates a Variant and sets
- Managed Prefab
- Re-detects
type. Avatars getgenre = Avatar. - Updates
lastModifiedTicksonly 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.
- Re-detects
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 inEAUploaderMeta.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.
- Searches for the Variant’s source Prefab, materials, and textures. Reference checks cover
.prefab/.unity/.controller/.overridecontroller/.anim/.asset/.matfiles underAssets/; assets used by other assets are excluded from deletion. - Shows what will be deleted as a read-only list. Deletion can be confirmed once the search completes.
- 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.
- After deletion, removes the item folder if empty, clears the selection if needed, and calls
completed(true). On cancellation, it callscompleted(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.
- Reads the metadata from the source Prefab.
- Removes
EAUploaderMetafrom the source Prefab (before creating the Variant, so it is not inherited). - 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()loadsUI/styles.ussandUI/tailwind.uss, clonesMainWindow.uxml(onlycontentRoot), adds the sharedModaland notifications (ToastManager), and applies the theme.- Screens are not tabs; one screen at a time is built into
contentRoot.EAUploader.Navigate(screen)switches screens, andEAUploader.RefreshCurrentScreen()rebuilds the current screen.
EAUploaderScreen | Builder | Contents |
|---|---|---|
AvatarList | UI.AvatarList.Main.ShowContent | Avatar List |
Setup | UI.Setup.Main.ShowContent | Edit screen |
Upload | UI.Setup.Main.ShowContent (with the upload sheet open) | Upload |
- Upload is not a separate screen;
UI.Upload.UploadFormis shown as a sheet on the Edit screen. - Custom UI components are in
Editor/Resources/UI/Components, and modals inEditor/Resources/UI/Modals. In UXML, declarexmlns: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).
- 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).
- 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.
- 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)
- Decides the platforms to build (Windows first, then the selected platforms whose build support is installed).
- Creates the build instance and fetches the existing
VRCAvatarby Blueprint ID. Creates a new record if none exists. - For a single platform, calls
builder.BuildAndUpload. For multiple platforms, sets the SDK’s multi-platform targets and callsbuilder.BuildAndUploadMultiPlatform(Windows → additional platforms → back to the original platform; a domain reload may happen in between). - Copies the Blueprint ID assigned by the SDK back to the Prefab asset.
- 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).
| Type | Responsibility |
|---|---|
EditToolSession | Opens 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. |
EditToolModal | Abstract 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. |
ToolModalFrame | Modal frame shared by all tools, including the Viewpoint Position Editor. |
EditToolUtility | Generated-asset folders, file name sanitizing, post-save refresh (OnPrefabSaved), shared UI parts, requirement checks. |
- While a tool has unsaved changes, the EAUploader window reports
hasUnsavedChangesand asks to save when the window is closed. - After saving,
EditToolUtility.OnPrefabSavedinvalidates thePrefabManagercache, regenerates the preview image, and refreshes the list and the Edit screen. - Tool previews use the live mode of the shared
Previewcomponent (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.