DiscardResult enum

Пакет: com.hypixel.hytale.builtin.asseteditor

Файл: com/hypixel/hytale/builtin/asseteditor/AssetEditorPlugin.java

Поля (1)

МодификаторыТипИмя
FAILED, SUCCEEDED, SUCCEEDED_COMMON_ASSETS_CHANGED

Исходный код

Показать/скрыть
class="kw">package com.hypixel.hytale.builtin.asseteditor;

class="kw">import com.hypixel.hytale.assetstore.AssetMap;
class="kw">import com.hypixel.hytale.assetstore.AssetPack;
class="kw">import com.hypixel.hytale.assetstore.AssetRegistry;
class="kw">import com.hypixel.hytale.assetstore.AssetStore;
class="kw">import com.hypixel.hytale.assetstore.AssetUpdateQuery;
class="kw">import com.hypixel.hytale.assetstore.event.AssetMonitorEvent;
class="kw">import com.hypixel.hytale.assetstore.event.AssetStoreMonitorEvent;
class="kw">import com.hypixel.hytale.assetstore.event.RegisterAssetStoreEvent;
class="kw">import com.hypixel.hytale.assetstore.event.RemoveAssetStoreEvent;
class="kw">import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;
class="kw">import com.hypixel.hytale.builtin.asseteditor.assettypehandler.AssetStoreTypeHandler;
class="kw">import com.hypixel.hytale.builtin.asseteditor.assettypehandler.AssetTypeHandler;
class="kw">import com.hypixel.hytale.builtin.asseteditor.assettypehandler.CommonAssetTypeHandler;
class="kw">import com.hypixel.hytale.builtin.asseteditor.assettypehandler.JsonTypeHandler;
class="kw">import com.hypixel.hytale.builtin.asseteditor.data.AssetUndoRedoInfo;
class="kw">import com.hypixel.hytale.builtin.asseteditor.data.ModifiedAsset;
class="kw">import com.hypixel.hytale.builtin.asseteditor.datasource.DataSource;
class="kw">import com.hypixel.hytale.builtin.asseteditor.datasource.StandardDataSource;
class="kw">import com.hypixel.hytale.builtin.asseteditor.event.AssetEditorAssetCreatedEvent;
class="kw">import com.hypixel.hytale.builtin.asseteditor.event.AssetEditorClientDisconnectEvent;
class="kw">import com.hypixel.hytale.builtin.asseteditor.event.AssetEditorSelectAssetEvent;
class="kw">import com.hypixel.hytale.builtin.asseteditor.util.AssetPathUtil;
class="kw">import com.hypixel.hytale.builtin.asseteditor.util.AssetStoreUtil;
class="kw">import com.hypixel.hytale.builtin.asseteditor.util.BsonTransformationUtil;
class="kw">import com.hypixel.hytale.codec.EmptyExtraInfo;
class="kw">import com.hypixel.hytale.codec.schema.SchemaContext;
class="kw">import com.hypixel.hytale.codec.schema.config.Schema;
class="kw">import com.hypixel.hytale.common.plugin.AuthorInfo;
class="kw">import com.hypixel.hytale.common.plugin.PluginIdentifier;
class="kw">import com.hypixel.hytale.common.plugin.PluginManifest;
class="kw">import com.hypixel.hytale.common.semver.Semver;
class="kw">import com.hypixel.hytale.common.semver.SemverRange;
class="kw">import com.hypixel.hytale.common.util.ArrayUtil;
class="kw">import com.hypixel.hytale.common.util.FormatUtil;
class="kw">import com.hypixel.hytale.common.util.PathUtil;
class="kw">import com.hypixel.hytale.component.ComponentAccessor;
class="kw">import com.hypixel.hytale.component.Ref;
class="kw">import com.hypixel.hytale.event.EventRegistry;
class="kw">import com.hypixel.hytale.event.IEventDispatcher;
class="kw">import com.hypixel.hytale.logger.HytaleLogger;
class="kw">import com.hypixel.hytale.protocol.ToClientPacket;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorAsset;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorAssetListUpdate;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorAssetPackSetup;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorAssetUpdated;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorCapabilities;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorDeleteAssetPack;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorEditorType;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorExportAssetFinalize;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorExportAssetInitialize;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorExportAssetPart;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorExportComplete;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorExportDeleteAssets;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorFetchAssetReply;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorFetchJsonAssetWithParentsReply;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorFileEntry;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorJsonAssetUpdated;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorLastModifiedAssets;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorModsDirectories;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorPopupNotificationType;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorRebuildCaches;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorRequestAssetInheritanceReply;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorRequestChildrenListReply;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorSetupSchemas;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorUndoRedoReply;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorUpdateAssetPack;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetInfo;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetInheritanceTreeNode;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.AssetPackManifest;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.JsonUpdateCommand;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.JsonUpdateType;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.SchemaFile;
class="kw">import com.hypixel.hytale.protocol.packets.asseteditor.TimestampedAssetReference;
class="kw">import com.hypixel.hytale.protocol.packets.assets.UpdateTranslations;
class="kw">import com.hypixel.hytale.server.core.HytaleServer;
class="kw">import com.hypixel.hytale.server.core.HytaleServerConfig;
class="kw">import com.hypixel.hytale.server.core.Message;
class="kw">import com.hypixel.hytale.server.core.Options;
class="kw">import com.hypixel.hytale.server.core.asset.AssetModule;
class="kw">import com.hypixel.hytale.server.core.asset.AssetPackRegisterEvent;
class="kw">import com.hypixel.hytale.server.core.asset.AssetPackUnregisterEvent;
class="kw">import com.hypixel.hytale.server.core.asset.common.events.CommonAssetMonitorEvent;
class="kw">import com.hypixel.hytale.server.core.config.ModConfig;
class="kw">import com.hypixel.hytale.server.core.io.PacketHandler;
class="kw">import com.hypixel.hytale.server.core.io.ServerManager;
class="kw">import com.hypixel.hytale.server.core.io.handlers.InitialPacketHandler;
class="kw">import com.hypixel.hytale.server.core.modules.i18n.I18nModule;
class="kw">import com.hypixel.hytale.server.core.modules.i18n.event.MessagesUpdated;
class="kw">import com.hypixel.hytale.server.core.permissions.HytalePermissions;
class="kw">import com.hypixel.hytale.server.core.plugin.JavaPlugin;
class="kw">import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
class="kw">import com.hypixel.hytale.server.core.plugin.PluginManager;
class="kw">import com.hypixel.hytale.server.core.plugin.PluginState;
class="kw">import com.hypixel.hytale.server.core.schema.SchemaGenerator;
class="kw">import com.hypixel.hytale.server.core.universe.PlayerRef;
class="kw">import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
class="kw">import com.hypixel.hytale.server.core.util.BsonUtil;
class="kw">import com.hypixel.hytale.server.core.util.io.FileUtil;
class="kw">import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
class="kw">import it.unimi.dsi.fastutil.objects.ObjectArrayList;
class="kw">import java.io.IOException;
class="kw">import java.nio.charset.StandardCharsets;
class="kw">import java.nio.file.Files;
class="kw">import java.nio.file.Path;
class="kw">import java.time.Instant;
class="kw">import java.util.Collection;
class="kw">import java.util.Collections;
class="kw">import java.util.HashSet;
class="kw">import java.util.List;
class="kw">import java.util.Map;
class="kw">import java.util.Set;
class="kw">import java.util.UUID;
class="kw">import java.util.Map.Entry;
class="kw">import java.util.concurrent.ConcurrentHashMap;
class="kw">import java.util.concurrent.ForkJoinPool;
class="kw">import java.util.concurrent.ScheduledFuture;
class="kw">import java.util.concurrent.TimeUnit;
class="kw">import java.util.concurrent.locks.StampedLock;
class="kw">import java.util.logging.Level;
class="kw">import javax.annotation.Nonnull;
class="kw">import javax.annotation.Nullable;
class="kw">import org.bson.BsonDocument;
class="kw">import org.bson.BsonValue;

class="kw">public class AssetEditorPlugin class="kw">extends JavaPlugin {
   class="kw">private class="kw">static AssetEditorPlugin instance;
   @Nonnull
   class="kw">private class="kw">final StampedLock globalEditLock = new StampedLock();
   @Nonnull
   class="kw">private class="kw">final Map<UUID, Set<EditorClient>> uuidToEditorClients = new ConcurrentHashMap<>();
   @Nonnull
   class="kw">private class="kw">final Map<EditorClient, AssetPath> clientOpenAssetPathMapping = new ConcurrentHashMap<>();
   @Nonnull
   class="kw">private class="kw">final Set<EditorClient> clientsSubscribedToModifiedAssetsChanges = ConcurrentHashMap.newKeySet();
   @Nonnull
   class="kw">private Map<String, Schema> schemas = new Object2ObjectOpenHashMap();
   class="kw">private AssetEditorSetupSchemas setupSchemasPacket;
   @Nonnull
   class="kw">private class="kw">final StampedLock initLock = new StampedLock();
   @Nonnull
   class="kw">private class="kw">final Set<EditorClient> initQueue = new HashSet<>();
   @Nonnull
   class="kw">private AssetEditorPlugin.InitState initState = AssetEditorPlugin.InitState.NOT_INITIALIZED;
   @Nullable
   class="kw">private ScheduledFuture<?> scheduledReinitFuture;
   @Nonnull
   class="kw">private class="kw">final Map<String, DataSource> assetPackDataSources = new ConcurrentHashMap<>();
   @Nonnull
   class="kw">private class="kw">final AssetTypeRegistry assetTypeRegistry = new AssetTypeRegistry();
   @Nonnull
   class="kw">private class="kw">final UndoRedoManager undoRedoManager = new UndoRedoManager();
   @Nonnull
   class="kw">private class="kw">final AssetInheritanceResolver inheritanceResolver = new AssetInheritanceResolver(
      () -> AssetModule.get().getAssetPacks(), this::getDataSourceForPack
   );
   @Nullable
   class="kw">private ScheduledFuture<?> pingClientsTask;

   class="kw">public class="kw">static AssetEditorPlugin get() {
      class="kw">return instance;
   }

   class="kw">public AssetEditorPlugin(@Nonnull JavaPluginInit var1) {
      super(var1);
   }

   @Nullable
   DataSource registerDataSourceForPack(@Nonnull AssetPack var1) {
      PluginManifest var2 = var1.getManifest();
      if (var2 == null) {
         this.getLogger().at(Level.SEVERE).log("Could not load asset pack manifest for %s", var1.getName());
         class="kw">return null;
      } else {
         StandardDataSource var3 = new StandardDataSource(var1.getName(), var1.getRoot(), var1.isImmutable(), var2);
         this.assetPackDataSources.put(var1.getName(), var3);
         class="kw">return var3;
      }
   }

   @Override
   class="kw">protected void setup() {
      instance = this;
      EventRegistry var1 = this.getEventRegistry();

      for (AssetPack var3 : AssetModule.get().getAssetPacks()) {
         this.registerDataSourceForPack(var3);
      }

      ServerManager.get().registerSubPacketHandlers(AssetEditorGamePacketHandler::new);
      InitialPacketHandler.EDITOR_PACKET_HANDLER_SUPPLIER = AssetEditorPacketHandler::new;

      for (AssetStore var5 : AssetRegistry.getStoreMap().values()) {
         if (var5.getPath() != null && !var5.getPath().startsWith("../")) {
            this.assetTypeRegistry.registerAssetType(new AssetStoreTypeHandler(var5));
         }
      }

      this.assetTypeRegistry.registerAssetType(new CommonAssetTypeHandler("Texture", "Texture.png", ".png", AssetEditorEditorType.Texture));
      this.assetTypeRegistry.registerAssetType(new CommonAssetTypeHandler("Model", "Model.png", ".blockymodel", AssetEditorEditorType.JsonSource));
      this.assetTypeRegistry.registerAssetType(new CommonAssetTypeHandler("Animation", "Animation.png", ".blockyanim", AssetEditorEditorType.JsonSource));
      this.assetTypeRegistry.registerAssetType(new CommonAssetTypeHandler("Sound", null, ".ogg", AssetEditorEditorType.None));
      this.assetTypeRegistry.registerAssetType(new CommonAssetTypeHandler("UI", null, ".ui", AssetEditorEditorType.Text));
      this.assetTypeRegistry.registerAssetType(new CommonAssetTypeHandler("Language", null, ".lang", AssetEditorEditorType.Text));
      var1.register(RegisterAssetStoreEvent.class, this::onRegisterAssetStore);
      var1.register(RemoveAssetStoreEvent.class, this::onUnregisterAssetStore);
      var1.register(AssetPackRegisterEvent.class, this::onRegisterAssetPack);
      var1.register(AssetPackUnregisterEvent.class, this::onUnregisterAssetPack);
      var1.register(AssetStoreMonitorEvent.class, this::onAssetMonitor);
      var1.register(CommonAssetMonitorEvent.class, this::onAssetMonitor);
      var1.register(MessagesUpdated.class, this::onI18nMessagesUpdated);
      AssetSpecificFunctionality.setup();
   }

   @Override
   class="kw">protected void start() {
      for (DataSource var2 : this.assetPackDataSources.values()) {
         var2.start();
      }

      this.pingClientsTask = HytaleServer.SCHEDULED_EXECUTOR.scheduleAtFixedRate(this::sendPingPackets, 1L, 1L, PacketHandler.PingInfo.PING_FREQUENCY_UNIT);
   }

   @Override
   class="kw">protected void shutdown() {
      InitialPacketHandler.EDITOR_PACKET_HANDLER_SUPPLIER = null;
      Message var1 = HytaleServer.get().isShuttingDown()
         ? Message.translation("server.general.disconnect.stoppingServer")
         : Message.translation("server.general.disconnect.assetEditorDisabled");

      for (Set var3 : this.uuidToEditorClients.values()) {
         for (EditorClient var5 : var3) {
            var5.getPacketHandler().disconnect(var1);
         }
      }

      if (this.pingClientsTask != null) {
         this.pingClientsTask.cancel(false);
      } else {
         this.getLogger().at(Level.WARNING).log("Failed to cancel ping clients task as it was null");
      }

      for (DataSource var7 : this.assetPackDataSources.values()) {
         var7.shutdown();
      }
   }

   @Nullable
   class="kw">public DataSource getDataSourceForPath(@Nonnull AssetPath var1) {
      class="kw">return this.getDataSourceForPack(var1.packId());
   }

   @Nullable
   class="kw">public DataSource getDataSourceForPack(@Nonnull String var1) {
      class="kw">return this.assetPackDataSources.get(var1);
   }

   @Nonnull
   class="kw">public Collection<DataSource> getDataSources() {
      class="kw">return this.assetPackDataSources.values();
   }

   @Nonnull
   class="kw">public AssetTypeRegistry getAssetTypeRegistry() {
      class="kw">return this.assetTypeRegistry;
   }

   @Nullable
   class="kw">public Schema getSchema(@Nonnull String var1) {
      class="kw">return this.schemas.get(var1);
   }

   @Nonnull
   class="kw">public Map<EditorClient, AssetPath> getClientOpenAssetPathMapping() {
      class="kw">return this.clientOpenAssetPathMapping;
   }

   @Nullable
   class="kw">public Set<EditorClient> getEditorClients(@Nonnull UUID var1) {
      class="kw">return this.uuidToEditorClients.get(var1);
   }

   class="kw">private void sendPingPackets() {
      for (Set var2 : this.uuidToEditorClients.values()) {
         for (EditorClient var4 : var2) {
            try {
               var4.getPacketHandler().sendPing();
            } catch (Exception var6) {
               ((HytaleLogger.Api)this.getLogger().at(Level.SEVERE).withCause(var6)).log("Failed to send ping to %s", var4);
               var4.getPacketHandler().disconnect(Message.translation("server.general.disconnect.pingException"));
            }
         }
      }
   }

   @Nonnull
   class="kw">private List<EditorClient> getClientsWithOpenAssetPath(@Nonnull AssetPath var1) {
      if (this.clientOpenAssetPathMapping.isEmpty()) {
         class="kw">return Collections.emptyList();
      }

      List var2 = new ObjectArrayList();

      for (Entry var4 : this.clientOpenAssetPathMapping.entrySet()) {
         if (var4.getValue().equals(var1)) {
            var2.add(var4.getKey());
         }
      }

      class="kw">return var2;
   }

   @Nullable
   class="kw">public AssetPath getOpenAssetPath(@Nonnull EditorClient var1) {
      class="kw">return this.clientOpenAssetPathMapping.get(var1);
   }

   class="kw">private void onRegisterAssetPack(@Nonnull AssetPackRegisterEvent var1) {
      if (!this.assetPackDataSources.containsKey(var1.getAssetPack().getName())) {
         DataSource var2 = this.registerDataSourceForPack(var1.getAssetPack());
         if (var2 != null) {
            if (this.getState() == PluginState.ENABLED) {
               var2.start();
            }

            AssetTree var3 = var2.loadAssetTree(this.assetTypeRegistry.getRegisteredAssetTypeHandlers().values());
            long var4 = this.globalEditLock.writeLock();

            try {
               var2.getAssetTree().replaceAssetTree(var3);
            } class="kw">finally {
               this.globalEditLock.unlockWrite(var4);
            }

            this.broadcastPackAddedOrUpdated(var1.getAssetPack().getName(), var2.getManifest());

            for (Set var7 : this.uuidToEditorClients.values()) {
               for (EditorClient var9 : var7) {
                  var2.getAssetTree().sendPackets(var9);
               }
            }
         }
      }
   }

   class="kw">private void onUnregisterAssetPack(@Nonnull AssetPackUnregisterEvent var1) {
      if (this.assetPackDataSources.containsKey(var1.getAssetPack().getName())) {
         DataSource var2 = this.assetPackDataSources.remove(var1.getAssetPack().getName());
         var2.shutdown();

         for (Set var4 : this.uuidToEditorClients.values()) {
            for (EditorClient var6 : var4) {
               var6.getPacketHandler().write(new AssetEditorDeleteAssetPack(var1.getAssetPack().getName()));
            }
         }
      }
   }

   class="kw">private void onI18nMessagesUpdated(@Nonnull MessagesUpdated var1) {
      if (!this.clientOpenAssetPathMapping.isEmpty()) {
         I18nModule var2 = I18nModule.get();
         Map var3 = var1.getChangedMessages();
         Map var4 = var1.getRemovedMessages();
         Map var5 = new Object2ObjectOpenHashMap();

         for (EditorClient var7 : this.clientOpenAssetPathMapping.keySet()) {
            String var8 = var7.getLanguage();
            UpdateTranslations[] var9 = var5.get(var8);
            if (var9 == null) {
               var9 = var2.getUpdatePacketsForChanges(var8, var3, var4);
               var5.put(var8, var9);
            }

            if (var9.length != 0) {
               var7.getPacketHandler().write(var9);
            }
         }
      }
   }

   class="kw">private void onRegisterAssetStore(@Nonnull RegisterAssetStoreEvent var1) {
      AssetStore var2 = (AssetStore<?, ? class="kw">extends JsonAssetWithMap<?, ? class="kw">extends AssetMap<?, ?>>, ? class="kw">extends AssetMap<?, ? class="kw">extends JsonAssetWithMap<?, ?>>>)var1.getAssetStore();
      if (var2.getPath() != null && !var2.getPath().startsWith("../")) {
         this.assetTypeRegistry.registerAssetType(new AssetStoreTypeHandler(var2));
         long var3 = this.initLock.readLock();

         try {
            if (this.initState != AssetEditorPlugin.InitState.NOT_INITIALIZED) {
               if (this.scheduledReinitFuture != null) {
                  this.scheduledReinitFuture.cancel(false);
               }

               this.scheduledReinitFuture = HytaleServer.SCHEDULED_EXECUTOR.schedule(this::tryReinitializeAssetEditor, 1L, TimeUnit.SECONDS);
            }
         } class="kw">finally {
            this.initLock.unlockRead(var3);
         }
      }
   }

   class="kw">private void onUnregisterAssetStore(@Nonnull RemoveAssetStoreEvent var1) {
      AssetStore var2 = (AssetStore<?, ? class="kw">extends JsonAssetWithMap<?, ? class="kw">extends AssetMap<?, ?>>, ? class="kw">extends AssetMap<?, ? class="kw">extends JsonAssetWithMap<?, ?>>>)var1.getAssetStore();
      if (var2.getPath() != null && !var2.getPath().startsWith("../")) {
         this.assetTypeRegistry.unregisterAssetType(new AssetStoreTypeHandler(var2));
         long var3 = this.initLock.readLock();

         try {
            if (this.initState != AssetEditorPlugin.InitState.NOT_INITIALIZED) {
               if (this.scheduledReinitFuture != null) {
                  this.scheduledReinitFuture.cancel(false);
               }

               this.scheduledReinitFuture = HytaleServer.SCHEDULED_EXECUTOR.schedule(this::tryReinitializeAssetEditor, 1L, TimeUnit.SECONDS);
            }
         } class="kw">finally {
            this.initLock.unlockRead(var3);
         }
      }
   }

   class="kw">private void tryReinitializeAssetEditor() {
      long var1 = this.initLock.writeLock();

      try {
         class="kw">switch (this.initState) {
            case NOT_INITIALIZED:
            class="kw">default:
               break;
            case INITIALIZING:
               this.scheduledReinitFuture = HytaleServer.SCHEDULED_EXECUTOR.schedule(this::tryReinitializeAssetEditor, 1L, TimeUnit.SECONDS);
               break;
            case INITIALIZED:
               this.initState = AssetEditorPlugin.InitState.INITIALIZING;
               this.scheduledReinitFuture = null;
               this.getLogger().at(Level.INFO).log("Starting asset editor re-initialization");
               ForkJoinPool.commonPool().execute(() -> this.initializeAssetEditor(true));
         }
      } class="kw">finally {
         this.initLock.unlockWrite(var1);
      }
   }

   class="kw">private void onAssetMonitor(@Nonnull AssetMonitorEvent<Void> var1) {
      AssetEditorAssetListUpdate var2 = new AssetEditorAssetListUpdate();
      var2.pack = var1.getAssetPack();
      ObjectArrayList var3 = new ObjectArrayList();
      DataSource var4 = this.getDataSourceForPack(var1.getAssetPack());
      if (var4 != null) {
         if (!var1.getRemovedFilesAndDirectories().isEmpty()) {
            ObjectArrayList var5 = new ObjectArrayList();

            for (Path var7 : var1.getRemovedFilesAndDirectories()) {
               Path var8 = PathUtil.relativizePretty(var4.getRootPath(), var7);
               AssetEditorFileEntry var9 = var4.getAssetTree().removeAsset(var8);
               if (var9 != null) {
                  var5.add(var9);
               }
            }

            var2.deletions = (AssetEditorFileEntry[])var5.toArray(AssetEditorFileEntry[]::new);
         }

         if (!var1.getRemovedFilesToUnload().isEmpty()) {
            var1.getRemovedFilesToUnload()
               .removeIf(
                  var3x -> {
                     Path var4 = PathUtil.relativizePretty(var4.getRootPath(), var3x);
                     if (!var4.shouldReloadAssetFromDisk(var4)) {
                        this.getLogger()
                           .at(Level.INFO)
                           .log("Skipping reloading %s from file monitor event because there is changes made via the asset editor", var3x);
                        class="kw">return true;
                     }

                     long var5 = this.globalEditLock.writeLock();

                     try {
                        this.undoRedoManager.clearUndoRedoStack(new AssetPath(var1.getAssetPack(), var4));
                     } class="kw">finally {
                        this.globalEditLock.unlockWrite(var5);
                     }

                     class="kw">return false;
                  }
               );
         }

         if (!var1.getCreatedOrModifiedDirectories().isEmpty()) {
            for (Path var11 : var1.getCreatedOrModifiedDirectories()) {
               Path var12 = PathUtil.relativizePretty(var4.getRootPath(), var11);
               AssetEditorFileEntry var13 = var4.getAssetTree().ensureAsset(var12, true);
               if (var13 != null) {
                  var3.add(var13);
               }
            }
         }

         if (!var1.getCreatedOrModifiedFilesToLoad().isEmpty()) {
            var1.getCreatedOrModifiedFilesToLoad()
               .removeIf(
                  var4x -> {
                     Path var5 = PathUtil.relativizePretty(var4.getRootPath(), var4x);
                     AssetEditorFileEntry var6 = var4.getAssetTree().ensureAsset(var5, false);
                     if (var6 != null) {
                        var3.add(var6);
                        class="kw">return false;
                     }

                     if (!var4.shouldReloadAssetFromDisk(var5)) {
                        this.getLogger()
                           .at(Level.INFO)
                           .log("Skipping reloading %s from file monitor event because there is changes made via the asset editor", var4x);
                        class="kw">return true;
                     }

                     AssetPath var7 = new AssetPath(var1.getAssetPack(), var5);
                     long var8 = this.globalEditLock.writeLock();

                     try {
                        this.undoRedoManager.clearUndoRedoStack(var7);
                     } class="kw">finally {
                        this.globalEditLock.unlockWrite(var8);
                     }

                     List var10 = this.getClientsWithOpenAssetPath(var7);
                     if (!var10.isEmpty()) {
                        AssetEditorAssetUpdated var11 = new AssetEditorAssetUpdated(var7.toPacket(), var4.getAssetBytes(var5));

                        for (EditorClient var13 : var10) {
                           var13.getPacketHandler().write(var11);
                        }
                     }

                     class="kw">return false;
                  }
               );
            if (!var3.isEmpty()) {
               var2.additions = (AssetEditorFileEntry[])var3.toArray(AssetEditorFileEntry[]::new);
            }
         }

         if (!var3.isEmpty()) {
            var2.additions = (AssetEditorFileEntry[])var3.toArray(AssetEditorFileEntry[]::new);
         }

         if (var2.deletions != null || var2.additions != null) {
            this.sendPacketToAllEditorUsers(var2);
         }
      }
   }

   class="kw">public void handleInitializeEditor(@Nonnull Ref<EntityStore> var1, @Nonnull ComponentAccessor<EntityStore> var2) {
      PlayerRef var3 = var2.getComponent(var1, PlayerRef.getComponentType());
      assert var3 != null;
      String var4 = var3.getUsername();
      this.getLogger().at(Level.INFO).log("%s is attempting to initialize asset editor", var4);
      long var5 = this.initLock.writeLock();

      try {
         if (this.initState == AssetEditorPlugin.InitState.NOT_INITIALIZED) {
            this.initState = AssetEditorPlugin.InitState.INITIALIZING;
            ForkJoinPool.commonPool().execute(() -> this.initializeAssetEditor(false));
            this.getLogger().at(Level.INFO).log("%s starting asset editor initialization", var4);
         }
      } class="kw">finally {
         this.initLock.unlockWrite(var5);
      }
   }

   class="kw">public void handleInitializeClient(@Nonnull EditorClient var1) {
      this.getLogger().at(Level.INFO).log("Initializing %s", var1.getUsername());
      this.uuidToEditorClients.computeIfAbsent(var1.getUuid(), var0 -> ConcurrentHashMap.newKeySet()).add(var1);
      this.clientOpenAssetPathMapping.put(var1, new AssetPath("", Path.of("")));
      I18nModule.get().sendTranslations(var1.getPacketHandler(), var1.getLanguage());
      long var2 = this.initLock.writeLock();

      try {
         class="kw">switch (this.initState) {
            case NOT_INITIALIZED:
               this.initState = AssetEditorPlugin.InitState.INITIALIZING;
               this.initQueue.add(var1);
               ForkJoinPool.commonPool().execute(() -> this.initializeAssetEditor(false));
               this.getLogger().at(Level.INFO).log("%s starting asset editor initialization", var1.getUsername());
               class="kw">return;
            case INITIALIZING:
               this.getLogger().at(Level.INFO).log("%s waiting for asset editor initialization to complete", var1.getUsername());
               this.initQueue.add(var1);
               class="kw">return;
            case INITIALIZED:
         }
      } class="kw">finally {
         this.initLock.unlockWrite(var2);
      }

      this.initializeClient(var1);
   }

   class="kw">private void initializeAssetEditor(boolean var1) {
      long var2 = System.nanoTime();
      Map var4 = SchemaGenerator.generateAssetSchemas();
      var4.remove("NPCRole.json");
      var4.remove("other.json");
      AssetEditorSetupSchemas var5 = new AssetEditorSetupSchemas(new SchemaFile[var4.size()]);
      int var6 = 0;

      for (Schema var8 : var4.values()) {
         String var9 = Schema.CODEC.encode(var8, EmptyExtraInfo.EMPTY).asDocument().toJson();
         var5.schemas[var6++] = new SchemaFile(var9);
      }

      for (DataSource var29 : this.assetPackDataSources.values()) {
         AssetTree var30 = var29.loadAssetTree(this.assetTypeRegistry.getRegisteredAssetTypeHandlers().values());
         long var10 = this.globalEditLock.writeLock();

         try {
            var29.getAssetTree().replaceAssetTree(var30);
            this.assetTypeRegistry.setupPacket();
            if (var1) {
               var29.updateRuntimeAssets();
            }
         } class="kw">finally {
            this.globalEditLock.unlockWrite(var10);
         }
      }

      long var28 = this.globalEditLock.writeLock();

      try {
         this.schemas = var4;
         this.setupSchemasPacket = var5;
         this.assetTypeRegistry.setupPacket();
      } class="kw">finally {
         this.globalEditLock.unlockWrite(var28);
      }

      long var31 = this.initLock.writeLock();

      try {
         this.initState = AssetEditorPlugin.InitState.INITIALIZED;
         this.getLogger().at(Level.INFO).log("Asset editor initialization complete! Took: %s", FormatUtil.nanosToString(System.nanoTime() - var2));

         for (EditorClient var12 : this.clientOpenAssetPathMapping.keySet()) {
            this.initializeClient(var12);
         }

         this.initQueue.clear();
      } class="kw">finally {
         this.initLock.unlockWrite(var31);
      }
   }

   class="kw">private void initializeClient(@Nonnull EditorClient var1) {
      DataSource var2 = this.assetPackDataSources.get("Hytale:Hytale");
      boolean var3 = false;
      boolean var4 = var1.hasPermission(HytalePermissions.ASSET_EDITOR);
      boolean var5 = var1.hasPermission(HytalePermissions.ASSET_EDITOR_PACKS_EDIT);
      boolean var6 = var1.hasPermission(HytalePermissions.ASSET_EDITOR_PACKS_CREATE);
      boolean var7 = var1.hasPermission(HytalePermissions.ASSET_EDITOR_PACKS_DELETE);
      var1.getPacketHandler().write(new AssetEditorCapabilities(false, var4, var6, var5, var7));
      var1.getPacketHandler().write(this.setupSchemasPacket);
      this.assetTypeRegistry.sendPacket(var1);
      AssetEditorAssetPackSetup var8 = new AssetEditorAssetPackSetup();
      var8.packs = new Object2ObjectOpenHashMap();

      for (Entry var10 : this.assetPackDataSources.entrySet()) {
         DataSource var11 = var10.getValue();
         PluginManifest var12 = var11.getManifest();
         var8.packs.put(var10.getKey(), toManifestPacket(var12));
      }

      var1.getPacketHandler().write(var8);
      if (var6) {
         List var13 = this.getModsDirectories();
         String[] var15 = new String[var13.size()];

         for (int var17 = 0; var17 < var13.size(); var17++) {
            var15[var17] = var13.get(var17).toString();
         }

         var1.getPacketHandler().write(new AssetEditorModsDirectories(var15));
      }

      for (DataSource var16 : this.assetPackDataSources.values()) {
         var16.getAssetTree().sendPackets(var1);
      }

      this.getLogger().at(Level.INFO).log("Done Initializing %s", var1.getUsername());
   }

   class="kw">public void handleEditorClientDisconnected(@Nonnull EditorClient var1, @Nonnull PacketHandler.DisconnectReason var2) {
      IEventDispatcher var3 = HytaleServer.get().getEventBus().dispatchFor(AssetEditorClientDisconnectEvent.class);
      if (var3.hasListener()) {
         var3.dispatch(new AssetEditorClientDisconnectEvent(var1, var2));
      }

      this.uuidToEditorClients.compute(var1.getUuid(), (var1x, var2x) -> {
         if (var2x == null) {
            class="kw">return null;
         }

         var2x.remove(var1);
         class="kw">return var2x.isEmpty() ? null : var2x;
      });
      this.clientOpenAssetPathMapping.remove(var1);
      this.clientsSubscribedToModifiedAssetsChanges.remove(var1);
   }

   class="kw">public void handleDeleteAssetPack(@Nonnull EditorClient var1, @Nonnull String var2) {
      if (var2.equalsIgnoreCase("Hytale:Hytale")) {
         var1.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.UNKNOWN_ASSET_PACK);
      } else {
         DataSource var3 = this.getDataSourceForPack(var2);
         if (var3 == null) {
            var1.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.UNKNOWN_ASSET_PACK);
         } else {
            AssetModule.get().unregisterPack(var2);

            Path var4;
            try {
               var4 = var3.getRootPath().toRealPath();
            } catch (IOException var11) {
               throw new RuntimeException("Failed to resolve the real path for asset pack directory while deleting asset pack '" + var2 + "'.", var11);
            }

            boolean var5 = false;

            try {
               if (var4.startsWith(PluginManager.MODS_PATH.toRealPath())) {
                  var5 = true;
               }
            } catch (IOException var10) {
            }

            if (!var5) {
               for (Path var7 : Options.getOptionSet().valuesOf(Options.MODS_DIRECTORIES)) {
                  try {
                     if (var4.startsWith(var7.toRealPath())) {
                        var5 = true;
                        break;
                     }
                  } catch (IOException var12) {
                  }
               }
            }

            if (!var5) {
               var1.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.PACK_OUTSIDE_DIRECTORY);
            } else {
               try {
                  FileUtil.deleteDirectory(var4);
                  var1.sendPopupNotification(AssetEditorPopupNotificationType.Success, Messages.PACK_DELETED);
               } catch (Exception var9) {
                  ((HytaleLogger.Api)this.getLogger().at(Level.SEVERE).withCause(var9)).log("Failed to delete asset pack %s from disk", var2);
                  var1.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.PACK_DELETION_FAILED);
               }
            }
         }
      }
   }

   class="kw">public void handleUpdateAssetPack(@Nonnull EditorClient var1, @Nonnull String var2, @Nonnull AssetPackManifest var3) {
      if (var2.equals("Hytale:Hytale")) {
         var1.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.UNKNOWN_ASSET_PACK);
      } else {
         DataSource var4 = this.getDataSourceForPack(var2);
         if (var4 == null) {
            var1.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.UNKNOWN_ASSET_PACK);
         } else if (var4.isImmutable()) {
            var1.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.ASSETS_READ_ONLY);
         } else {
            PluginManifest var5 = var4.getManifest();
            if (var5 == null) {
               var1.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.MANIFEST_NOT_FOUND);
            } else {
               boolean var6 = false;
               if (var3.name != null && !var3.name.isEmpty() && !var5.getName().equals(var3.name)) {
                  var5.setName(var3.name);
                  var6 = true;
               }

               if (var3.group != null && !var3.group.isEmpty() && !var5.getGroup().equals(var3.group)) {
                  var5.setGroup(var3.group);
                  var6 = true;
               }

               if (var3.description != null) {
                  var5.setDescription(var3.description);
               }

               if (var3.website != null) {
                  var5.setWebsite(var3.website);
               }

               if (var3.version != null && !var3.version.isEmpty()) {
                  try {
                     var5.setVersion(Semver.fromString(var3.version));
                  } catch (IllegalArgumentException var16) {
                     ((HytaleLogger.Api)this.getLogger().at(Level.WARNING).withCause(var16)).log("Invalid version format: %s", var3.version);
                     var1.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.INVALID_VERSION_FORMAT);
                     class="kw">return;
                  }
               }

               if (var3.authors != null) {
                  List var7 = new ObjectArrayList();

                  for (com.hypixel.hytale.protocol.packets.asseteditor.AuthorInfo var11 : var3.authors) {
                     AuthorInfo var12 = new AuthorInfo();
                     var12.setName(var11.name);
                     var12.setEmail(var11.email);
                     var12.setUrl(var11.url);
                     var7.add(var12);
                  }

                  var5.setAuthors(var7);
               }

               if (var3.serverVersion != null) {
                  if (var3.serverVersion.isBlank()) {
                     var5.setServerVersion(SemverRange.WILDCARD);
                  } else {
                     try {
                        var5.setServerVersion(SemverRange.fromString(var3.serverVersion));
                     } catch (IllegalArgumentException var15) {
                        ((HytaleLogger.Api)this.getLogger().at(Level.WARNING).withCause(var15))
                           .log("Rejected manifest update for pack %s: invalid ServerVersion range '%s'", var2, var3.serverVersion);
                        var1.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.INVALID_SERVER_VERSION);
                        class="kw">return;
                     }
                  }
               }

               Path var17 = var4.getRootPath().resolve("manifest.json");

               try {
                  BsonUtil.writeSync(var17, PluginManifest.CODEC, var5, this.getLogger());
                  this.getLogger().at(Level.INFO).log("Saved manifest for pack %s", var2);
                  var1.sendPopupNotification(AssetEditorPopupNotificationType.Success, Messages.MANIFEST_SAVED);
               } catch (IOException var14) {
                  ((HytaleLogger.Api)this.getLogger().at(Level.SEVERE).withCause(var14)).log("Failed to save manifest for pack %s", var2);
                  var1.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.MANIFEST_SAVE_FAILED);
               }

               this.broadcastPackAddedOrUpdated(var2, var5);
               if (var6) {
                  PluginIdentifier var18 = new PluginIdentifier(var5);
                  String var19 = var18.toString();
                  Path var20 = var4.getRootPath();
                  HytaleServerConfig var21 = HytaleServer.get().getConfig();
                  HytaleServerConfig.setBoot(var21, var18, true);
                  Map var22 = var21.getModConfig();
                  var22.remove(PluginIdentifier.fromString(var2));
                  var21.markChanged();
                  if (var21.consumeHasChanged()) {
                     HytaleServerConfig.save(var21).join();
                  }

                  AssetModule var13 = AssetModule.get();
                  var13.unregisterPack(var2);
                  if (!var13.registerPack(var19, var20, var5, AssetPack.PackSource.RUNTIME)) {
                     this.getLogger().at(Level.SEVERE).log("Failed to re-register asset pack '%s' after update", var19);
                  }
               }
            }
         }
      }
   }

   @Nonnull
   class="kw">private List<Path> getModsDirectories() {
      List var1 = new ObjectArrayList();
      var1.add(PluginManager.MODS_PATH.toAbsolutePath());

      for (Path var3 : Options.getOptionSet().valuesOf(Options.MODS_DIRECTORIES)) {
         var1.add(var3.toAbsolutePath());
      }

      class="kw">return var1;
   }

   class="kw">public void handleCreateAssetPack(@Nonnull EditorClient var1, @Nonnull AssetPackManifest var2, int var3, int var4) {
      if (var2.name == null || var2.name.isEmpty()) {
         var1.sendFailureReply(var3, Messages.PACK_NAME_REQUIRED);
      } else if (var2.group != null && !var2.group.isEmpty()) {
         PluginManifest var5 = new PluginManifest();
         var5.setName(var2.name);
         var5.setGroup(var2.group);
         if (var2.description != null) {
            var5.setDescription(var2.description);
         }

         if (var2.website != null) {
            var5.setWebsite(var2.website);
         }

         if (var2.version != null && !var2.version.isEmpty()) {
            try {
               var5.setVersion(Semver.fromString(var2.version));
            } catch (IllegalArgumentException var16) {
               ((HytaleLogger.Api)this.getLogger().at(Level.WARNING).withCause(var16)).log("Invalid version format: %s", var2.version);
               var1.sendFailureReply(var3, Messages.INVALID_VERSION_FORMAT);
               class="kw">return;
            }
         }

         if (var2.authors != null) {
            List var6 = new ObjectArrayList();

            for (com.hypixel.hytale.protocol.packets.asseteditor.AuthorInfo var10 : var2.authors) {
               AuthorInfo var11 = new AuthorInfo();
               var11.setName(var10.name);
               var11.setEmail(var10.email);
               var11.setUrl(var10.url);
               var6.add(var11);
            }

            var5.setAuthors(var6);
         }

         if (var2.serverVersion != null && !var2.serverVersion.isBlank()) {
            try {
               var5.setServerVersion(SemverRange.fromString(var2.serverVersion));
            } catch (IllegalArgumentException var15) {
               ((HytaleLogger.Api)this.getLogger().at(Level.WARNING).withCause(var15))
                  .log("Rejected pack creation: invalid ServerVersion range '%s'", var2.serverVersion);
               var1.sendFailureReply(var3, Messages.INVALID_SERVER_VERSION);
               class="kw">return;
            }
         } else {
            var5.setServerVersion(SemverRange.WILDCARD);
         }

         String var17 = new PluginIdentifier(var5).toString();
         if (this.assetPackDataSources.containsKey(var17)) {
            var1.sendFailureReply(var3, Messages.PACK_ALREADY_EXISTS);
         } else {
            List var18 = this.getModsDirectories();
            if (var4 >= 0 && var4 < var18.size()) {
               Path var19 = var18.get(var4);
               String var20 = AssetPathUtil.removeInvalidFileNameChars(var2.group != null ? var2.group + "." + var2.name : var2.name);
               Path var21 = Path.of(var20).normalize();
               if (AssetPathUtil.isInvalidFileName(var21)) {
                  var1.sendFailureReply(var3, Messages.INVALID_FILE_NAME);
               } else {
                  Path var22 = var19.resolve(var21).normalize();
                  if (!var22.startsWith(var19)) {
                     var1.sendFailureReply(var3, Messages.PACK_OUTSIDE_DIRECTORY);
                  } else if (Files.exists(var22)) {
                     var1.sendFailureReply(var3, Messages.PACK_ALREADY_EXISTS_AT_PATH);
                  } else {
                     try {
                        Files.createDirectories(var22);
                        Path var12 = var22.resolve("manifest.json");
                        BsonUtil.writeSync(var12, PluginManifest.CODEC, var5, this.getLogger());
                        HytaleServerConfig var13 = HytaleServer.get().getConfig();
                        HytaleServerConfig.setBoot(var13, new PluginIdentifier(var5), true);
                        var13.markChanged();
                        if (var13.consumeHasChanged()) {
                           HytaleServerConfig.save(var13).join();
                        }

                        if (!AssetModule.get().registerPack(var17, var22, var5, AssetPack.PackSource.RUNTIME)) {
                           var1.sendFailureReply(var3, Messages.PACK_CREATION_FAILED);
                           class="kw">return;
                        }

                        var1.sendSuccessReply(var3, Messages.PACK_CREATED);
                        this.getLogger().at(Level.INFO).log("Created new pack: %s at %s", var17, var22);
                     } catch (IOException var14) {
                        ((HytaleLogger.Api)this.getLogger().at(Level.SEVERE).withCause(var14)).log("Failed to create pack %s", var17);
                        var1.sendFailureReply(var3, Messages.PACK_CREATION_FAILED);
                     }
                  }
               }
            } else {
               var1.sendFailureReply(var3, Messages.INVALID_TARGET_DIRECTORY);
            }
         }
      } else {
         var1.sendFailureReply(var3, Messages.PACK_GROUP_REQUIRED);
      }
   }

   @Nonnull
   class="kw">private class="kw">static AssetPackManifest toManifestPacket(@Nonnull PluginManifest var0) {
      AssetPackManifest var1 = new AssetPackManifest();
      var1.name = var0.getName();
      var1.description = var0.getDescription() != null ? var0.getDescription() : "";
      var1.group = var0.getGroup();
      var1.version = var0.getVersion() != null ? var0.getVersion().toString() : "";
      var1.website = var0.getWebsite() != null ? var0.getWebsite() : "";
      var1.serverVersion = var0.getServerVersion() != null ? var0.getServerVersion().toString() : "";
      List var2 = new ObjectArrayList();

      for (AuthorInfo var4 : var0.getAuthors()) {
         com.hypixel.hytale.protocol.packets.asseteditor.AuthorInfo var5 = new com.hypixel.hytale.protocol.packets.asseteditor.AuthorInfo(
            var4.getName(), var4.getEmail(), var4.getUrl()
         );
         var2.add(var5);
      }

      var1.authors = var2.toArray(new com.hypixel.hytale.protocol.packets.asseteditor.AuthorInfo[0]);
      class="kw">return var1;
   }

   class="kw">private void broadcastPackAddedOrUpdated(@Nonnull String var1, @Nonnull PluginManifest var2) {
      AssetPackManifest var3 = toManifestPacket(var2);

      for (Set var5 : this.uuidToEditorClients.values()) {
         for (EditorClient var7 : var5) {
            var7.getPacketHandler().write(new AssetEditorUpdateAssetPack(var1, var3));
         }
      }
   }

   class="kw">public void handleExportAssets(@Nonnull EditorClient var1, @Nonnull List<AssetPath> var2) {
      ObjectArrayList var3 = new ObjectArrayList();

      for (AssetPath var5 : var2) {
         DataSource var6 = this.getDataSourceForPath(var5);
         if (var6 == null) {
            this.getLogger().at(Level.WARNING).log("%s has no valid data source", var5);
            AssetEditorAsset var13 = new AssetEditorAsset(null, var5.toPacket());
            var1.getPacketHandler().write(new AssetEditorExportAssetInitialize(var13, null, 0, true));
         } else if (!this.isValidPath(var6, var5)) {
            this.getLogger().at(Level.WARNING).log("%s is an invalid path", var5);
            AssetEditorAsset var12 = new AssetEditorAsset(null, var5.toPacket());
            var1.getPacketHandler().write(new AssetEditorExportAssetInitialize(var12, null, 0, true));
         } else if (this.assetTypeRegistry.getAssetTypeHandlerForPath(var5.path()) == null) {
            this.getLogger().at(Level.WARNING).log("%s is not a valid asset type", var5);
            AssetEditorAsset var11 = new AssetEditorAsset(null, var5.toPacket());
            var1.getPacketHandler().write(new AssetEditorExportAssetInitialize(var11, null, 0, true));
         } else if (!var6.doesAssetExist(var5.path())) {
            var1.getPacketHandler().write(new AssetEditorExportDeleteAssets(new AssetEditorAsset[]{new AssetEditorAsset(null, var5.toPacket())}));
         } else {
            byte[] var7 = var6.getAssetBytes(var5.path());
            if (var7 == null) {
               this.getLogger().at(Level.WARNING).log("Tried to load %s for export but failed", var5);
               var1.getPacketHandler().write(new AssetEditorExportAssetInitialize(new AssetEditorAsset(null, var5.toPacket()), null, 0, false));
            } else {
               byte[][] var8 = ArrayUtil.split(var7, 2621440);
               ToClientPacket[] var9 = new ToClientPacket[2 + var8.length];
               var9[0] = new AssetEditorExportAssetInitialize(new AssetEditorAsset(null, var5.toPacket()), null, var7.length, false);

               for (int var10 = 0; var10 < var8.length; var10++) {
                  var9[1 + var10] = new AssetEditorExportAssetPart(var8[var10]);
               }

               var9[var9.length - 1] = new AssetEditorExportAssetFinalize();
               var1.getPacketHandler().write(var9);
               Instant var14 = var6.getLastModificationTimestamp(var5.path());
               var3.add(new TimestampedAssetReference(var5.toPacket(), var14 != null ? var14.toString() : null));
            }
         }
      }

      var1.getPacketHandler().write(new AssetEditorExportComplete((TimestampedAssetReference[])var3.toArray(TimestampedAssetReference[]::new)));
   }

   class="kw">public void handleSelectAsset(@Nonnull EditorClient var1, @Nullable AssetPath var2) {
      if (var2 != null) {
         DataSource var3 = this.getDataSourceForPath(var2);
         if (var3 == null) {
            class="kw">return;
         }
      }

      String var7 = null;
      String var4 = null;
      AssetPath var5 = this.clientOpenAssetPathMapping.get(var1);
      if (var5 != null && !var5.equals(AssetPath.EMPTY_PATH)) {
         AssetTypeHandler var6 = this.assetTypeRegistry.tryGetAssetTypeHandler(var5.path(), var1, -1);
         if (var6 != null) {
            var4 = var6.getConfig().id;
         }
      }

      if (var2 != null) {
         AssetTypeHandler var8 = this.assetTypeRegistry.tryGetAssetTypeHandler(var2.path(), var1, -1);
         if (var8 == null) {
            class="kw">return;
         }

         var7 = var8.getConfig().id;
         this.clientOpenAssetPathMapping.put(var1, var2);
      } else {
         this.clientOpenAssetPathMapping.put(var1, AssetPath.EMPTY_PATH);
      }

      IEventDispatcher var9 = HytaleServer.get().getEventBus().dispatchFor(AssetEditorSelectAssetEvent.class);
      if (var9.hasListener()) {
         var9.dispatch(new AssetEditorSelectAssetEvent(var1, var7, var2, var4, var5));
      }
   }

   class="kw">public void handleFetchLastModifiedAssets(@Nonnull EditorClient var1) {
      long var2 = this.globalEditLock.readLock();

      try {
         AssetEditorLastModifiedAssets var4 = this.buildAssetEditorLastModifiedAssetsPacket();
         var1.getPacketHandler().write(var4);
      } class="kw">finally {
         this.globalEditLock.unlockRead(var2);
      }
   }

   class="kw">public void handleAssetUpdate(@Nonnull EditorClient var1, @Nonnull AssetPath var2, @Nonnull byte[] var3, int var4) {
      DataSource var5 = this.getDataSourceForPath(var2);
      if (var5 == null) {
         var1.sendFailureReply(var4, Messages.UNKNOWN_ASSET_PACK);
      } else if (var5.isImmutable()) {
         var1.sendFailureReply(var4, Messages.ASSETS_READ_ONLY);
      } else if (!this.isValidPath(var5, var2)) {
         var1.sendFailureReply(var4, Messages.DIRECTORY_OUTSIDE_ASSET_TYPE_ROOT);
      } else {
         AssetTypeHandler var6 = this.assetTypeRegistry.tryGetAssetTypeHandler(var2.path(), var1, var4);
         if (var6 != null) {
            long var7 = this.globalEditLock.writeLock();

            label79: {
               try {
                  if (!var5.doesAssetExist(var2.path())) {
                     this.getLogger().at(Level.WARNING).log("%s does not exist", var2);
                     var1.sendFailureReply(var4, Messages.UPDATE_DOESNT_EXIST);
                     class="kw">return;
                  }

                  if (!var6.isValidData(var3)) {
                     this.getLogger().at(Level.WARNING).log("Failed to validate data for %s", var2);
                     var1.sendFailureReply(var4, Messages.CREATE_ASSET_FAILED);
                     class="kw">return;
                  }

                  if (var5.updateAsset(var2.path(), var3, var1)) {
                     this.updateAssetForConnectedClients(var2, var3, var1);
                     this.sendModifiedAssetsUpdateToConnectedUsers();
                     var1.sendSuccessReply(var4);
                     var6.loadAsset(var2, var5.getFullPathToAssetData(var2.path()), var3, var1);
                     break label79;
                  }

                  this.getLogger().at(Level.WARNING).log("Failed to update asset %s in data source!", var2);
                  var1.sendFailureReply(var4, Messages.UPDATE_FAILED);
               } class="kw">finally {
                  this.globalEditLock.unlockWrite(var7);
               }

               class="kw">return;
            }

            this.getLogger().at(Level.INFO).log("Updated asset at %s", var2);
         }
      }
   }

   class="kw">public void handleJsonAssetUpdate(@Nonnull EditorClient var1, AssetPath var2, @Nonnull String var3, int var4, @Nonnull JsonUpdateCommand[] var5, int var6) {
      AssetTypeHandler var7 = this.assetTypeRegistry.getAssetTypeHandler(var3);
      if (!(var7 class="kw">instanceof JsonTypeHandler var8)) {
         this.getLogger().at(Level.WARNING).log("Invalid asset type %s", var3);
         var1.sendFailureReply(var6, Message.translation("server.assetEditor.messages.unknownAssetType").param("assetType", var3));
      } else {
         DataSource var9;
         if (var4 > -1 && var7 class="kw">instanceof AssetStoreTypeHandler var10) {
            AssetStore var11 = var10.getAssetStore();
            AssetMap var12 = var11.getAssetMap();
            String var13 = AssetStoreUtil.getIdFromIndex(var11, var4);
            Object var14 = var11.decodeStringKey(var13);
            Path var15 = var12.getPath(var14);
            String var16 = var12.getAssetPack(var14);
            if (var15 == null || var16 == null) {
               var1.sendFailureReply(var6, Messages.UNKNOWN_ASSET_INDEX);
               class="kw">return;
            }

            var9 = this.getDataSourceForPack(var16);
            if (var9 == null) {
               var1.sendFailureReply(var6, Messages.UNKNOWN_ASSET_PACK);
               class="kw">return;
            }

            var2 = new AssetPath(var16, PathUtil.relativizePretty(var9.getRootPath(), var15));
         } else {
            var9 = this.getDataSourceForPath(var2);
            if (var9 == null) {
               var1.sendFailureReply(var6, Messages.UNKNOWN_ASSET_PACK);
               class="kw">return;
            }
         }

         if (var9.isImmutable()) {
            var1.sendFailureReply(var6, Messages.ASSETS_READ_ONLY);
         } else if (!this.isValidPath(var9, var2)) {
            var1.sendFailureReply(var6, Messages.DIRECTORY_OUTSIDE_ASSET_TYPE_ROOT);
         } else if (!var2.path().startsWith(var7.getRootPath())) {
            this.getLogger().at(Level.WARNING).log("%s is not within valid asset directory", var2);
            var1.sendFailureReply(var6, Messages.DIRECTORY_OUTSIDE_ROOT);
         } else {
            String var26 = PathUtil.getFileExtension(var2.path());
            if (!var26.equalsIgnoreCase(var7.getConfig().fileExtension)) {
               this.getLogger().at(Level.WARNING).log("File extension not matching. Expected %s, got %s", var7.getConfig().fileExtension, var26);
               this.getLogger().at(Level.WARNING).log("File extension not matching. Expected %s, got %s", var7.getConfig().fileExtension, var26);
               var1.sendFailureReply(
                  var6, Message.translation("server.assetEditor.messages.fileExtensionMismatch").param("fileExtension", var7.getConfig().fileExtension)
               );
            } else {
               long var27 = this.globalEditLock.writeLock();

               try {
                  byte[] var28 = var9.getAssetBytes(var2.path());
                  if (var28 == null) {
                     this.getLogger().at(Level.WARNING).log("%s does not exist", var2);
                     var1.sendFailureReply(var6, Messages.UPDATE_DOESNT_EXIST);
                     class="kw">return;
                  }

                  AssetUpdateQuery.RebuildCacheBuilder var30 = AssetUpdateQuery.RebuildCache.builder();

                  BsonDocument var31;
                  try {
                     var31 = this.applyCommandsToAsset(var28, var2, var5, var30);
                     String var32 = BsonUtil.toJson(var31) + "\n";
                     var28 = var32.getBytes(StandardCharsets.UTF_8);
                  } catch (Exception var24) {
                     ((HytaleLogger.Api)this.getLogger().at(Level.WARNING).withCause(var24)).log("Failed to apply commands to %s", var2);
                     var1.sendFailureReply(var6, Messages.UPDATE_FAILED);
                     class="kw">return;
                  }

                  if (var9.updateAsset(var2.path(), var28, var1)) {
                     AssetUndoRedoInfo var33 = this.undoRedoManager.getOrCreateUndoRedoStack(var2);
                     var33.redoStack.clear();

                     for (JsonUpdateCommand var20 : var5) {
                        var33.undoStack.push(var20);
                     }

                     this.updateJsonAssetForConnectedClients(var2, var5, var1);
                     var1.sendSuccessReply(var6);
                     this.sendModifiedAssetsUpdateToConnectedUsers();
                     var8.loadAssetFromDocument(var2, var9.getFullPathToAssetData(var2.path()), var31.clone(), new AssetUpdateQuery(var30.build()), var1);
                     class="kw">return;
                  }

                  var1.sendFailureReply(var6, Messages.UPDATE_FAILED);
               } class="kw">finally {
                  this.globalEditLock.unlockWrite(var27);
               }
            }
         }
      }
   }

   class="kw">public void handleUndo(@Nonnull EditorClient var1, @Nonnull AssetPath var2, int var3) {
      DataSource var4 = this.getDataSourceForPath(var2);
      if (var4 == null) {
         var1.sendFailureReply(var3, Messages.UNKNOWN_ASSET_PACK);
      } else if (var4.isImmutable()) {
         var1.sendFailureReply(var3, Messages.ASSETS_READ_ONLY);
      } else if (!this.isValidPath(var4, var2)) {
         var1.sendFailureReply(var3, Messages.DIRECTORY_OUTSIDE_ASSET_TYPE_ROOT);
      } else {
         AssetTypeHandler var5 = this.assetTypeRegistry.tryGetAssetTypeHandler(var2.path(), var1, var3);
         if (var5 != null) {
            if (!(var5 class="kw">instanceof JsonTypeHandler)) {
               this.getLogger().at(Level.WARNING).log("Undo can only be applied to an instance of JsonTypeHandler");
               var1.sendFailureReply(var3, Messages.INVALID_ASSET_TYPE);
            } else {
               long var6 = this.globalEditLock.writeLock();

               try {
                  AssetUndoRedoInfo var8 = this.undoRedoManager.getUndoRedoStack(var2);
                  if (var8 == null || var8.undoStack.isEmpty()) {
                     this.getLogger().at(Level.INFO).log("Nothing to undo");
                     var1.sendFailureReply(var3, Messages.UNDO_EMPTY);
                     class="kw">return;
                  }

                  JsonUpdateCommand var9 = var8.undoStack.peek();
                  JsonUpdateCommand var10 = new JsonUpdateCommand();
                  var10.rebuildCaches = var9.rebuildCaches;
                  if (var9.firstCreatedProperty != null) {
                     var10.type = JsonUpdateType.RemoveProperty;
                     var10.path = var9.firstCreatedProperty;
                  } else {
                     var10.type = var9.type == JsonUpdateType.RemoveProperty ? JsonUpdateType.InsertProperty : JsonUpdateType.SetProperty;
                     var10.path = var9.path;
                     var10.value = var9.previousValue;
                  }

                  byte[] var11 = var4.getAssetBytes(var2.path());
                  if (var11 == null) {
                     this.getLogger().at(Level.WARNING).log("%s does not exist", var2);
                     var1.sendFailureReply(var3, Messages.UPDATE_DOESNT_EXIST);
                     class="kw">return;
                  }

                  AssetUpdateQuery.RebuildCacheBuilder var13 = AssetUpdateQuery.RebuildCache.builder();

                  BsonDocument var12;
                  try {
                     var12 = this.applyCommandsToAsset(var11, var2, new JsonUpdateCommand[]{var10}, var13);
                     String var14 = BsonUtil.toJson(var12) + "\n";
                     var11 = var14.getBytes(StandardCharsets.UTF_8);
                  } catch (Exception var18) {
                     ((HytaleLogger.Api)this.getLogger().at(Level.WARNING).withCause(var18)).log("Failed to undo for %s", var2);
                     var1.sendFailureReply(var3, Messages.UNDO_FAILED);
                     class="kw">return;
                  }

                  if (var4.updateAsset(var2.path(), var11, var1)) {
                     var8.undoStack.poll();
                     var8.redoStack.push(var9);
                     this.updateJsonAssetForConnectedClients(var2, new JsonUpdateCommand[]{var10}, var1);
                     var1.getPacketHandler().write(new AssetEditorUndoRedoReply(var3, var10));
                     this.sendModifiedAssetsUpdateToConnectedUsers();
                     ((JsonTypeHandler)var5)
                        .loadAssetFromDocument(var2, var4.getFullPathToAssetData(var2.path()), var12.clone(), new AssetUpdateQuery(var13.build()), var1);
                     class="kw">return;
                  }

                  var1.sendFailureReply(var3, Messages.UNDO_FAILED);
               } class="kw">finally {
                  this.globalEditLock.unlockWrite(var6);
               }
            }
         }
      }
   }

   class="kw">public void handleRedo(@Nonnull EditorClient var1, @Nonnull AssetPath var2, int var3) {
      DataSource var4 = this.getDataSourceForPath(var2);
      if (var4 == null) {
         var1.sendFailureReply(var3, Messages.UNKNOWN_ASSET_PACK);
      } else if (var4.isImmutable()) {
         var1.sendFailureReply(var3, Messages.ASSETS_READ_ONLY);
      } else if (!this.isValidPath(var4, var2)) {
         var1.sendFailureReply(var3, Messages.DIRECTORY_OUTSIDE_ASSET_TYPE_ROOT);
      } else {
         AssetTypeHandler var5 = this.assetTypeRegistry.tryGetAssetTypeHandler(var2.path(), var1, var3);
         if (var5 != null) {
            if (!(var5 class="kw">instanceof JsonTypeHandler)) {
               this.getLogger().at(Level.WARNING).log("Redo can only be applied to an instance of JsonTypeHandler");
               var1.sendFailureReply(var3, Messages.INVALID_ASSET_TYPE);
            } else {
               long var6 = this.globalEditLock.writeLock();

               try {
                  AssetUndoRedoInfo var8 = this.undoRedoManager.getUndoRedoStack(var2);
                  if (var8 == null || var8.redoStack.isEmpty()) {
                     this.getLogger().at(Level.WARNING).log("Nothing to redo");
                     var1.sendFailureReply(var3, Messages.REDO_EMPTY);
                     class="kw">return;
                  }

                  byte[] var9 = var4.getAssetBytes(var2.path());
                  if (var9 == null) {
                     this.getLogger().at(Level.WARNING).log("%s does not exist", var2);
                     var1.sendFailureReply(var3, Messages.UPDATE_DOESNT_EXIST);
                     class="kw">return;
                  }

                  JsonUpdateCommand var10 = var8.redoStack.peek();
                  AssetUpdateQuery.RebuildCacheBuilder var11 = AssetUpdateQuery.RebuildCache.builder();

                  BsonDocument var12;
                  try {
                     var12 = this.applyCommandsToAsset(var9, var2, new JsonUpdateCommand[]{var10}, var11);
                     String var13 = BsonUtil.toJson(var12) + "\n";
                     var9 = var13.getBytes(StandardCharsets.UTF_8);
                  } catch (Exception var17) {
                     ((HytaleLogger.Api)this.getLogger().at(Level.WARNING).withCause(var17)).log("Failed to redo for %s", var2);
                     var1.sendFailureReply(var3, Messages.REDO_FAILED);
                     class="kw">return;
                  }

                  if (var4.updateAsset(var2.path(), var9, var1)) {
                     var8.redoStack.poll();
                     var8.undoStack.push(var10);
                     this.updateJsonAssetForConnectedClients(var2, new JsonUpdateCommand[]{var10}, var1);
                     var1.getPacketHandler().write(new AssetEditorUndoRedoReply(var3, var10));
                     this.sendModifiedAssetsUpdateToConnectedUsers();
                     ((JsonTypeHandler)var5)
                        .loadAssetFromDocument(var2, var4.getFullPathToAssetData(var2.path()), var12.clone(), new AssetUpdateQuery(var11.build()), var1);
                     class="kw">return;
                  }

                  var1.sendFailureReply(var3, Messages.REDO_FAILED);
               } class="kw">finally {
                  this.globalEditLock.unlockWrite(var6);
               }
            }
         }
      }
   }

   class="kw">public void handleFetchAsset(@Nonnull EditorClient var1, @Nonnull AssetPath var2, int var3) {
      DataSource var4 = this.getValidatedDataSource(var1, var2, var3);
      if (var4 != null) {
         long var5 = this.globalEditLock.readLock();

         try {
            if (!var4.doesAssetExist(var2.path())) {
               this.getLogger().at(Level.WARNING).log("%s is not a regular file", var2);
               var1.sendFailureReply(var3, Messages.FETCH_ASSET_DOESNT_EXIST);
               class="kw">return;
            }

            byte[] var7 = var4.getAssetBytes(var2.path());
            if (var7 != null) {
               this.getLogger().at(Level.INFO).log("Got '%s'", var2);
               var1.getPacketHandler().write(new AssetEditorFetchAssetReply(var3, var7));
               class="kw">return;
            }

            this.getLogger().at(Level.INFO).log("Failed to get '%s'", var2);
            var1.sendFailureReply(var3, Messages.FETCH_ASSET_FAILED);
         } class="kw">finally {
            this.globalEditLock.unlockRead(var5);
         }
      }
   }

   class="kw">public void handleFetchJsonAssetWithParents(@Nonnull EditorClient var1, @Nonnull AssetPath var2, boolean var3, int var4) {
      DataSource var5 = this.getValidatedDataSource(var1, var2, var4);
      if (var5 != null) {
         long var6 = this.globalEditLock.readLock();

         try {
            byte[] var8 = var5.getAssetBytes(var2.path());
            if (var8 != null) {
               this.getLogger().at(Level.INFO).log("Got '%s'", var2);
               BsonDocument var9 = BsonDocument.parse(new String(var8, StandardCharsets.UTF_8));
               Object2ObjectOpenHashMap var10 = new Object2ObjectOpenHashMap();
               var10.put(var2.toPacket(), BsonUtil.toJson(var9));
               var1.getPacketHandler().write(new AssetEditorFetchJsonAssetWithParentsReply(var4, var10));
               class="kw">return;
            }

            this.getLogger().at(Level.INFO).log("Failed to get '%s'", var2);
            var1.sendFailureReply(var4, Messages.FETCH_ASSET_FAILED);
         } class="kw">finally {
            this.globalEditLock.unlockRead(var6);
         }
      }
   }

   class="kw">public void handleRequestChildIds(@Nonnull EditorClient var1, @Nonnull AssetPath var2) {
      DataSource var3 = this.getDataSourceForPath(var2);
      if (var3 == null) {
         var1.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.UNKNOWN_ASSET_PACK);
      } else if (!this.isValidPath(var3, var2)) {
         var1.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.DIRECTORY_OUTSIDE_ASSET_TYPE_ROOT);
      } else if (!(this.assetTypeRegistry.getAssetTypeHandlerForPath(var2.path()) class="kw">instanceof AssetStoreTypeHandler var5)) {
         this.getLogger().at(Level.WARNING).log("Invalid asset type for %s", var2);
         var1.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.REQUEST_CHILD_IDS_ASSET_TYPE_MISSING);
      } else {
         AssetStore var6 = var5.getAssetStore();
         AssetMap var7 = var6.getAssetMap();
         Object var8 = var6.decodeFilePathKey(var2.path());
         Set var9 = var7.getChildren(var8);
         HashSet var10 = new HashSet();
         if (var9 != null) {
            for (Object var12 : var9) {
               if (var7.getPath(var12) != null) {
                  var10.add(var12.toString());
               }
            }
         }

         this.getLogger().at(Level.INFO).log("Children ids for '%s': %s", var8.toString(), var10);
         var1.getPacketHandler().write(new AssetEditorRequestChildrenListReply(var2.toPacket(), var10.toArray(String[]::new)));
      }
   }

   class="kw">public void handleDeleteAsset(@Nonnull EditorClient var1, @Nonnull AssetPath var2, int var3) {
      DataSource var4 = this.getDataSourceForPath(var2);
      if (var4 == null) {
         var1.sendFailureReply(var3, Messages.UNKNOWN_ASSET_PACK);
      } else if (var4.isImmutable()) {
         var1.sendFailureReply(var3, Messages.ASSETS_READ_ONLY);
      } else if (!this.isValidPath(var4, var2)) {
         var1.sendFailureReply(var3, Messages.DIRECTORY_OUTSIDE_ASSET_TYPE_ROOT);
      } else {
         AssetTypeHandler var5 = this.assetTypeRegistry.tryGetAssetTypeHandler(var2.path(), var1, var3);
         if (var5 != null) {
            long var6 = this.globalEditLock.writeLock();

            label68: {
               try {
                  if (!var4.doesAssetExist(var2.path())) {
                     this.getLogger().at(Level.WARNING).log("%s does not exist", var2);
                     var1.sendFailureReply(var3, Messages.DELETE_ASSET_ALREADY_DELETED);
                     class="kw">return;
                  }

                  if (var4.deleteAsset(var2.path(), var1)) {
                     this.undoRedoManager.clearUndoRedoStack(var2);
                     AssetEditorFileEntry var8 = var4.getAssetTree().removeAsset(var2.path());
                     AssetEditorAssetListUpdate var9 = new AssetEditorAssetListUpdate(var2.packId(), null, new AssetEditorFileEntry[]{var8});
                     var1.sendSuccessReply(var3);
                     this.sendPacketToAllEditorUsersExcept(var9, var1);
                     this.sendModifiedAssetsUpdateToConnectedUsers();
                     var5.unloadAsset(var2);
                     break label68;
                  }

                  this.getLogger().at(Level.WARNING).log("Failed to delete %s from data source", var2);
                  var1.sendFailureReply(var3, Messages.FAILED_TO_DELETE_ASSET);
               } class="kw">finally {
                  this.globalEditLock.unlockWrite(var6);
               }

               class="kw">return;
            }

            this.getLogger().at(Level.INFO).log("Deleted asset %s", var2);
         }
      }
   }

   class="kw">public void handleSubscribeToModifiedAssetsChanges(@Nonnull EditorClient var1) {
      this.clientsSubscribedToModifiedAssetsChanges.add(var1);
   }

   class="kw">public void handleUnsubscribeFromModifiedAssetsChanges(@Nonnull EditorClient var1) {
      this.clientsSubscribedToModifiedAssetsChanges.remove(var1);
   }

   class="kw">public void handleRenameAsset(@Nonnull EditorClient var1, @Nonnull AssetPath var2, @Nonnull AssetPath var3, int var4) {
      if (!var2.packId().equals(var3.packId())) {
         this.getLogger().at(Level.WARNING).log("Rejected cross-pack asset rename from %s to %s", var2, var3);
         var1.sendFailureReply(var4, Messages.RENAME_ASSET_CROSS_PACK_UNSUPPORTED);
      } else {
         DataSource var5 = this.getDataSourceForPath(var2);
         if (var5 == null) {
            var1.sendFailureReply(var4, Messages.UNKNOWN_ASSET_PACK);
         } else if (var5.isImmutable()) {
            var1.sendFailureReply(var4, Messages.ASSETS_READ_ONLY);
         } else if (!this.isValidPath(var5, var2)) {
            var1.sendFailureReply(var4, Messages.DIRECTORY_OUTSIDE_ASSET_TYPE_ROOT);
         } else if (!this.isValidPath(var5, var3)) {
            var1.sendFailureReply(var4, Messages.DIRECTORY_OUTSIDE_ASSET_TYPE_ROOT);
         } else {
            AssetTypeHandler var6 = this.assetTypeRegistry.tryGetAssetTypeHandler(var2.path(), var1, var4);
            if (var6 != null) {
               String var7 = PathUtil.getFileExtension(var3.path());
               if (!var7.equalsIgnoreCase(var6.getConfig().fileExtension)) {
                  this.getLogger().at(Level.WARNING).log("File extension not matching. Expected %s, got %s", var6.getConfig().fileExtension, var7);
                  var1.sendFailureReply(
                     var4, Message.translation("server.assetEditor.messages.fileExtensionMismatch").param("fileExtension", var6.getConfig().fileExtension)
                  );
               } else if (!var3.path().startsWith(var6.getRootPath())) {
                  this.getLogger().at(Level.WARNING).log("%s is not within valid asset directory", var3);
                  var1.sendFailureReply(var4, Messages.DIRECTORY_OUTSIDE_ROOT);
               } else {
                  long var8 = this.globalEditLock.writeLock();

                  try {
                     if (var5.doesAssetExist(var3.path())) {
                        this.getLogger().at(Level.WARNING).log("%s already exists", var3);
                        var1.sendFailureReply(var4, Messages.RENAME_ASSET_ALREADY_EXISTS);
                        class="kw">return;
                     }

                     byte[] var10 = var5.getAssetBytes(var2.path());
                     if (var10 == null) {
                        this.getLogger().at(Level.WARNING).log("%s is not a regular file", var2);
                        var1.sendFailureReply(var4, Messages.RENAME_ASSET_DOESNT_EXIST);
                        class="kw">return;
                     }

                     if (var5.moveAsset(var2.path(), var3.path(), var1)) {
                        AssetUndoRedoInfo var11 = this.undoRedoManager.clearUndoRedoStack(var2);
                        if (var11 != null) {
                           this.undoRedoManager.putUndoRedoStack(var3, var11);
                        }

                        this.getLogger().at(Level.WARNING).log("Moved %s to %s", var2, var3);
                        AssetEditorFileEntry var12 = var5.getAssetTree().removeAsset(var2.path());
                        AssetEditorFileEntry var13 = var5.getAssetTree().ensureAsset(var3.path(), false);
                        AssetEditorAssetListUpdate var14 = new AssetEditorAssetListUpdate(
                           var2.packId(), new AssetEditorFileEntry[]{var13}, new AssetEditorFileEntry[]{var12}
                        );
                        this.sendPacketToAllEditorUsersExcept(var14, var1);
                        var1.sendSuccessReply(var4);
                        var6.unloadAsset(var2);
                        var6.loadAsset(var3, var5.getFullPathToAssetData(var3.path()), var10, var1);
                        class="kw">return;
                     }

                     this.getLogger().at(Level.WARNING).log("Failed to move file %s to %s", var2, var3);
                     var1.sendFailureReply(var4, Messages.RENAME_ASSET_FAILED);
                  } class="kw">finally {
                     this.globalEditLock.unlockWrite(var8);
                  }
               }
            }
         }
      }
   }

   class="kw">public void handleDeleteDirectory(@Nonnull EditorClient var1, @Nonnull AssetPath var2, int var3) {
      DataSource var4 = this.getDataSourceForPath(var2);
      if (var4.isImmutable()) {
         var1.sendFailureReply(var3, Messages.ASSETS_READ_ONLY);
      } else if (!this.isValidPath(var4, var2)) {
         var1.sendFailureReply(var3, Messages.DIRECTORY_OUTSIDE_ROOT);
      } else if (!this.getAssetTypeRegistry().isPathInAssetTypeFolder(var2.path())) {
         var1.sendFailureReply(var3, Messages.DIRECTORY_OUTSIDE_ASSET_TYPE_ROOT);
      } else {
         long var5 = this.globalEditLock.writeLock();

         try {
            if (!var4.doesDirectoryExist(var2.path())) {
               this.getLogger().at(Level.WARNING).log("Directory doesn't exist %s", var2);
               var1.sendFailureReply(var3, Messages.CREATE_DIRECTORY_ALREADY_EXISTS);
               class="kw">return;
            }

            if (!var4.getAssetTree().isDirectoryEmpty(var2.path())) {
               this.getLogger().at(Level.WARNING).log("%s must be empty", var2);
               var1.sendFailureReply(var3, Messages.DELETE_DIRECTORY_NOT_EMPTY);
               class="kw">return;
            }

            if (var4.deleteDirectory(var2.path())) {
               AssetEditorFileEntry var7 = var4.getAssetTree().removeAsset(var2.path());
               AssetEditorAssetListUpdate var8 = new AssetEditorAssetListUpdate(var2.packId(), null, new AssetEditorFileEntry[]{var7});
               this.sendPacketToAllEditorUsersExcept(var8, var1);
               var1.sendSuccessReply(var3);
               this.getLogger().at(Level.INFO).log("Deleted directory %s", var2);
               class="kw">return;
            }

            this.getLogger().at(Level.WARNING).log("Directory %s could not be deleted!", var2);
            var1.sendFailureReply(var3, Messages.DELETE_DIRECTORY_FAILED);
         } class="kw">finally {
            this.globalEditLock.unlockWrite(var5);
         }
      }
   }

   class="kw">public void handleRenameDirectory(@Nonnull EditorClient var1, AssetPath var2, AssetPath var3, int var4) {
      var1.sendFailureReply(var4, Messages.RENAME_DIRECTORY_UNSUPPORTED);
   }

   class="kw">public void handleCreateDirectory(@Nonnull EditorClient var1, @Nonnull AssetPath var2, int var3) {
      DataSource var4 = this.getDataSourceForPath(var2);
      if (var4 == null) {
         var1.sendFailureReply(var3, Messages.CREATE_DIRECTORY_NO_DATA_SOURCE);
      } else if (var4.isImmutable()) {
         var1.sendFailureReply(var3, Messages.ASSETS_READ_ONLY);
      } else if (!this.isValidPath(var4, var2)) {
         var1.sendFailureReply(var3, Messages.CREATE_DIRECTORY_NO_PATH);
      } else {
         long var5 = this.globalEditLock.writeLock();

         try {
            if (var4.doesDirectoryExist(var2.path())) {
               this.getLogger().at(Level.WARNING).log("Directory already exists at %s", var2);
               var1.sendFailureReply(var3, Messages.CREATE_DIRECTORY_ALREADY_EXISTS);
               class="kw">return;
            }

            Path var7 = var2.path().getParent();
            if (!var4.doesDirectoryExist(var7)) {
               this.getLogger().at(Level.WARNING).log("Parent directory is missing for %s", var2);
               var1.sendFailureReply(var3, Messages.PARENT_DIRECTORY_MISSING);
               class="kw">return;
            }

            if (var4.createDirectory(var2.path(), var1)) {
               AssetEditorFileEntry var8 = var4.getAssetTree().ensureAsset(var2.path(), true);
               if (var8 != null) {
                  AssetEditorAssetListUpdate var9 = new AssetEditorAssetListUpdate(var2.packId(), new AssetEditorFileEntry[]{var8}, null);
                  this.sendPacketToAllEditorUsersExcept(var9, var1);
               }

               var1.sendSuccessReply(var3);
               this.getLogger().at(Level.WARNING).log("Created directory %s", var2);
               class="kw">return;
            }

            this.getLogger().at(Level.WARNING).log("Failed to create directory %s", var2);
            var1.sendFailureReply(var3, Messages.FAILED_TO_CREATE_DIRECTORY);
         } class="kw">finally {
            this.globalEditLock.unlockWrite(var5);
         }
      }
   }

   class="kw">public void handleCreateAsset(
      @Nonnull EditorClient var1, @Nonnull AssetPath var2, @Nonnull byte[] var3, @Nonnull AssetEditorRebuildCaches var4, String var5, int var6
   ) {
      DataSource var7 = this.getDataSourceForPath(var2);
      if (var7 == null) {
         var1.sendFailureReply(var6, Messages.UNKNOWN_ASSET_PACK);
      } else if (var7.isImmutable()) {
         var1.sendFailureReply(var6, Messages.ASSETS_READ_ONLY);
      } else if (!this.isValidPath(var7, var2)) {
         var1.sendFailureReply(var6, Messages.DIRECTORY_OUTSIDE_ASSET_TYPE_ROOT);
      } else {
         AssetTypeHandler var8 = this.assetTypeRegistry.tryGetAssetTypeHandler(var2.path(), var1, var6);
         if (var8 != null) {
            long var9 = this.globalEditLock.writeLock();

            try {
               if (var7.doesAssetExist(var2.path())) {
                  this.getLogger().at(Level.WARNING).log("%s already exists", var2);
                  var1.sendFailureReply(var6, Messages.CREATE_ASSET_ID_ALREADY_EXISTS);
                  class="kw">return;
               }

               if (!var8.isValidData(var3)) {
                  this.getLogger().at(Level.WARNING).log("Failed to validate data for %s", var2);
                  var1.sendFailureReply(var6, Messages.CREATE_ASSET_FAILED);
                  class="kw">return;
               }

               if (var7.createAsset(var2.path(), var3, var1)) {
                  this.getLogger().at(Level.INFO).log("Created asset %s", var2);
                  AssetEditorFileEntry var11 = var7.getAssetTree().ensureAsset(var2.path(), false);
                  if (var11 != null) {
                     AssetEditorAssetListUpdate var12 = new AssetEditorAssetListUpdate(var2.packId(), new AssetEditorFileEntry[]{var11}, null);
                     this.sendPacketToAllEditorUsersExcept(var12, var1);
                  }

                  this.sendModifiedAssetsUpdateToConnectedUsers();
                  AssetUpdateQuery.RebuildCache var17 = new AssetUpdateQuery.RebuildCache(
                     var4.blockTextures,
                     var4.models,
                     var4.modelTextures,
                     var4.mapGeometry,
                     var4.itemIcons,
                     var2.path().startsWith(AssetPathUtil.PATH_DIR_COMMON)
                  );
                  var8.loadAsset(var2, var7.getFullPathToAssetData(var2.path()), var3, new AssetUpdateQuery(var17), var1);
                  IEventDispatcher var13 = HytaleServer.get().getEventBus().dispatchFor(AssetEditorAssetCreatedEvent.class, var8.getConfig().id);
                  if (var13.hasListener()) {
                     var13.dispatch(new AssetEditorAssetCreatedEvent(var1, var8.getConfig().id, var2.path(), var3, var5));
                  }

                  var1.sendSuccessReply(var6);
                  class="kw">return;
               }

               this.getLogger().at(Level.WARNING).log("Failed to create asset %s", var2);
               var1.sendFailureReply(var6, Messages.CREATE_ASSET_FAILED);
            } class="kw">finally {
               this.globalEditLock.unlockWrite(var9);
            }
         }
      }
   }

   class="kw">public void handleRequestAssetInheritance(@Nonnull EditorClient var1, @Nonnull AssetPath var2, int var3) {
      DataSource var4 = this.getValidatedDataSource(var1, var2, var3);
      if (var4 != null) {
         if (this.assetTypeRegistry.getAssetTypeHandlerForPath(var2.path()) class="kw">instanceof AssetStoreTypeHandler var5) {
            long var20 = this.globalEditLock.readLock();

            try {
               byte[] var8 = var4.getAssetBytes(var2.path());
               if (var8 == null) {
                  this.getLogger().at(Level.INFO).log("Failed to get '%s'", var2);
                  var1.sendFailureReply(var3, Messages.FETCH_ASSET_FAILED);
               } else {
                  BsonDocument var9;
                  try {
                     var9 = BsonDocument.parse(new String(var8, StandardCharsets.UTF_8));
                  } catch (RuntimeException var18) {
                     this.getLogger().at(Level.WARNING).log("Failed to parse asset '%s': %s", var2, var18.getMessage());
                     var1.sendFailureReply(var3, Messages.FETCH_ASSET_FAILED);
                     class="kw">return;
                  }

                  Schema var10 = this.getSchemaById(var5.getConfig().id);

                  AssetInheritanceTreeNode[] var11;
                  try {
                     var11 = var10 == null ? null : this.inheritanceResolver.resolveAncestors(var9, var10, var5.getAssetStore(), var2);
                  } catch (AssetInheritanceResolver.InheritanceResolutionException var17) {
                     this.getLogger().at(Level.WARNING).log("Failed to build inheritance chain for '%s': %s", var2, var17.getMessage());
                     var11 = null;
                  }

                  AssetInheritanceTreeNode var12 = new AssetInheritanceTreeNode(var2.toPacket(), BsonUtil.toJson(var9), var11);
                  var1.getPacketHandler().write(new AssetEditorRequestAssetInheritanceReply(var3, var12));
               }
            } class="kw">finally {
               this.globalEditLock.unlockRead(var20);
            }
         } else {
            var1.sendFailureReply(var3, Messages.FETCH_ASSET_FAILED);
         }
      }
   }

   class="kw">private BsonDocument applyCommandsToAsset(
      @Nonnull byte[] var1, AssetPath var2, @Nonnull JsonUpdateCommand[] var3, @Nonnull AssetUpdateQuery.RebuildCacheBuilder var4
   ) {
      BsonDocument var5 = BsonUtil.parseWithMaxDepth(new String(var1, StandardCharsets.UTF_8));
      this.getLogger().at(Level.INFO).log("Applying commands to %s with %s", var2, var5);

      for (JsonUpdateCommand var9 : var3) {
         class="kw">switch (var9.type) {
            case SetProperty: {
               BsonValue var15 = BsonUtil.parseWithMaxDepth(var9.value).get("value");
               this.getLogger().at(Level.INFO).log("Setting property %s to %s", String.join(".", var9.path), var15);
               if (var9.path.length > 0) {
                  BsonTransformationUtil.setProperty(var5, var9.path, var15);
               } else {
                  var5 = (BsonDocument)var15;
               }
               break;
            }
            case InsertProperty: {
               BsonValue var10 = BsonDocument.parse(var9.value).get("value");
               this.getLogger().at(Level.INFO).log("Inserting property %s with %s", String.join(".", var9.path), var10);
               BsonTransformationUtil.insertProperty(var5, var9.path, var10);
               break;
            }
            case RemoveProperty:
               this.getLogger().at(Level.INFO).log("Removing property %s", String.join(".", var9.path));
               BsonTransformationUtil.removeProperty(var5, var9.path);
         }
      }

      this.getLogger().at(Level.INFO).log("Updated %s resulting: %s", var2, var5);

      for (JsonUpdateCommand var14 : var3) {
         if (var14.rebuildCaches != null) {
            if (var14.rebuildCaches.blockTextures) {
               var4.setBlockTextures(true);
            }

            if (var14.rebuildCaches.modelTextures) {
               var4.setModelTextures(true);
            }

            if (var14.rebuildCaches.models) {
               var4.setModels(true);
            }

            if (var14.rebuildCaches.mapGeometry) {
               var4.setMapGeometry(true);
            }

            if (var14.rebuildCaches.itemIcons) {
               var4.setItemIcons(true);
            }
         }
      }

      class="kw">return var5;
   }

   class="kw">private void sendModifiedAssetsUpdateToConnectedUsers() {
      if (!this.clientOpenAssetPathMapping.isEmpty()) {
         if (!this.clientsSubscribedToModifiedAssetsChanges.isEmpty()) {
            AssetEditorLastModifiedAssets var1 = this.buildAssetEditorLastModifiedAssetsPacket();

            for (EditorClient var3 : this.clientsSubscribedToModifiedAssetsChanges) {
               var3.getPacketHandler().write(var1);
            }
         }
      }
   }

   class="kw">private void sendPacketToAllEditorUsers(@Nonnull ToClientPacket var1) {
      for (EditorClient var3 : this.clientOpenAssetPathMapping.keySet()) {
         var3.getPacketHandler().write(var1);
      }
   }

   class="kw">private void sendPacketToAllEditorUsersExcept(@Nonnull ToClientPacket var1, EditorClient var2) {
      for (EditorClient var4 : this.clientOpenAssetPathMapping.keySet()) {
         if (!var4.equals(var2)) {
            var4.getPacketHandler().write(var1);
         }
      }
   }

   class="kw">private void updateAssetForConnectedClients(@Nonnull AssetPath var1) {
      this.updateAssetForConnectedClients(var1, null);
   }

   class="kw">private void updateAssetForConnectedClients(@Nonnull AssetPath var1, @Nullable EditorClient var2) {
      DataSource var3 = this.getDataSourceForPath(var1);
      byte[] var4 = var3.getAssetBytes(var1.path());
      this.updateAssetForConnectedClients(var1, var4, var2);
   }

   class="kw">private void updateAssetForConnectedClients(@Nonnull AssetPath var1, byte[] var2, @Nullable EditorClient var3) {
      AssetEditorAssetUpdated var4 = new AssetEditorAssetUpdated(var1.toPacket(), var2);

      for (Entry var6 : this.clientOpenAssetPathMapping.entrySet()) {
         if (!var6.getKey().equals(var3) && var1.equals(var6.getValue())) {
            var6.getKey().getPacketHandler().write(var4);
         }
      }
   }

   class="kw">private void updateJsonAssetForConnectedClients(@Nonnull AssetPath var1, @Nonnull JsonUpdateCommand[] var2) {
      this.updateJsonAssetForConnectedClients(var1, var2, null);
   }

   class="kw">private void updateJsonAssetForConnectedClients(@Nonnull AssetPath var1, @Nonnull JsonUpdateCommand[] var2, @Nullable EditorClient var3) {
      AssetEditorJsonAssetUpdated var4 = new AssetEditorJsonAssetUpdated(var1.toPacket(), var2);

      for (Entry var6 : this.clientOpenAssetPathMapping.entrySet()) {
         if (!var6.getKey().equals(var3) && var1.equals(var6.getValue())) {
            var6.getKey().getPacketHandler().write(var4);
         }
      }
   }

   @Nonnull
   class="kw">private AssetEditorLastModifiedAssets buildAssetEditorLastModifiedAssetsPacket() {
      List var1 = new ObjectArrayList();

      for (Entry var3 : this.assetPackDataSources.entrySet()) {
         if (var3.getValue() class="kw">instanceof StandardDataSource var4) {
            for (ModifiedAsset var6 : var4.getRecentlyModifiedAssets().values()) {
               var1.add(var6.toAssetInfoPacket(var3.getKey()));
            }
         }
      }

      class="kw">return new AssetEditorLastModifiedAssets(var1.toArray(new AssetInfo[0]));
   }

   @Nullable
   class="kw">private DataSource getValidatedDataSource(@Nonnull EditorClient var1, @Nonnull AssetPath var2, int var3) {
      DataSource var4 = this.getDataSourceForPath(var2);
      if (var4 == null) {
         var1.sendFailureReply(var3, Messages.UNKNOWN_ASSET_PACK);
         class="kw">return null;
      } else if (!this.isValidPath(var4, var2)) {
         var1.sendFailureReply(var3, Messages.DIRECTORY_OUTSIDE_ASSET_TYPE_ROOT);
         class="kw">return null;
      } else {
         class="kw">return this.assetTypeRegistry.tryGetAssetTypeHandler(var2.path(), var1, var3) == null ? null : var4;
      }
   }

   @Nullable
   class="kw">private Schema getSchemaById(@Nullable String var1) {
      class="kw">return this.getSchema(var1 + ".json");
   }

   boolean isValidPath(@Nonnull DataSource var1, @Nonnull AssetPath var2) {
      String var3 = PathUtil.toUnixPathString(var2.path());
      Path var4 = var1.getRootPath();
      Path var5 = var4.resolve(var3).toAbsolutePath().normalize();
      if (!var5.startsWith(var4)) {
         class="kw">return false;
      }

      String var6 = PathUtil.toUnixPathString(var4.relativize(var5));
      class="kw">return var6.equals(var3);
   }

   class="kw">static {
      new SchemaContext();
   }

   class="kw">public class="kw">static class AssetToDiscard {
      class="kw">public class="kw">final AssetPath path;
      @Nullable
      class="kw">public class="kw">final Instant lastModificationDate;

      class="kw">public AssetToDiscard(AssetPath var1, @Nullable String var2) {
         this.path = var1;
         if (var2 != null) {
            this.lastModificationDate = Instant.parse(var2);
         } else {
            this.lastModificationDate = null;
         }
      }
   }

   enum DiscardResult {
      FAILED,
      SUCCEEDED,
      SUCCEEDED_COMMON_ASSETS_CHANGED;

      DiscardResult() {
      }
   }

   enum InitState {
      NOT_INITIALIZED,
      INITIALIZING,
      INITIALIZED;

      InitState() {
      }
   }
}