StandardDataSource class

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

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

implements: DataSource

Поля (25)

МодификаторыТипИмя
final HytaleLogger LOGGER
return
Path var1
var1
BsonDocument var1
BsonDocument var2
BsonArray var2
Path var2
Deque var2
ModifiedAsset var2
var2
var2
ModifiedAsset var3
byte[] var3
Deque var3
Path var4
Path var4
String var4
ModifiedAsset var4
String var5
String var5
long var5
ModifiedAsset var6
ModifiedAsset var6
ModifiedAsset var6

Методы (18)

МодификаторыВозвратСигнатура
abstract return new AssetTreereturn new AssetTree(this.rootPath, this.packKey, this.isImmutable, this.isAssetPackBeDeleteable, var1)
record EditorFileSaveInforecord EditorFileSaveInfo(String hash, long expiryMs)
abstract throw new IllegalArgumentExceptionthrow new IllegalArgumentException("Invalid path: " + var1)
public boolean canAssetPackBeDeletedboolean canAssetPackBeDeleted()
for for(StandardDataSource.EditorFileSaveInfo var9 : var2)
public Map<Path, ModifiedAsset> getRecentlyModifiedAssetsMap<Path, ModifiedAsset> getRecentlyModifiedAssets()
if if(this.saveSchedule != null)
if if(var6 != null)
if if(var3 == null)
if if(var2 == null)
static boolean isInModsDirectoryboolean isInModsDirectory(Path var0)
private void loadRecentModificationsvoid loadRecentModifications()
public void putModifiedAssetvoid putModifiedAsset(ModifiedAsset var1)
public Path resolveAbsolutePathPath resolveAbsolutePath(Path var1)
public void saveRecentModificationsvoid saveRecentModifications()
synchronized synchronized(var2)
synchronized synchronized(var3)
private void trackEditorFileSavevoid trackEditorFileSave(Path var1, String var2)

Исходный код

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

class="kw">import com.hypixel.hytale.builtin.asseteditor.AssetTree;
class="kw">import com.hypixel.hytale.builtin.asseteditor.EditorClient;
class="kw">import com.hypixel.hytale.builtin.asseteditor.assettypehandler.AssetTypeHandler;
class="kw">import com.hypixel.hytale.builtin.asseteditor.data.AssetState;
class="kw">import com.hypixel.hytale.builtin.asseteditor.data.ModifiedAsset;
class="kw">import com.hypixel.hytale.codec.ExtraInfo;
class="kw">import com.hypixel.hytale.common.plugin.PluginManifest;
class="kw">import com.hypixel.hytale.common.util.PathUtil;
class="kw">import com.hypixel.hytale.logger.HytaleLogger;
class="kw">import com.hypixel.hytale.server.core.HytaleServer;
class="kw">import com.hypixel.hytale.server.core.Options;
class="kw">import com.hypixel.hytale.server.core.plugin.PluginManager;
class="kw">import com.hypixel.hytale.server.core.util.BsonUtil;
class="kw">import com.hypixel.hytale.server.core.util.HashUtil;
class="kw">import java.io.IOException;
class="kw">import java.nio.file.Files;
class="kw">import java.nio.file.Path;
class="kw">import java.nio.file.StandardOpenOption;
class="kw">import java.time.Instant;
class="kw">import java.util.ArrayDeque;
class="kw">import java.util.Collection;
class="kw">import java.util.Deque;
class="kw">import java.util.Map;
class="kw">import java.util.Map.Entry;
class="kw">import java.util.concurrent.ConcurrentHashMap;
class="kw">import java.util.concurrent.ScheduledFuture;
class="kw">import java.util.concurrent.TimeUnit;
class="kw">import java.util.concurrent.atomic.AtomicBoolean;
class="kw">import java.util.logging.Level;
class="kw">import org.bson.BsonArray;
class="kw">import org.bson.BsonDocument;
class="kw">import org.bson.BsonValue;

class="kw">public class StandardDataSource class="kw">implements DataSource {
   class="kw">private class="kw">static class="kw">final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
   class="kw">private class="kw">final Path rootPath;
   class="kw">private class="kw">final ConcurrentHashMap<Path, Deque<StandardDataSource.EditorFileSaveInfo>> editorSaves;
   class="kw">private class="kw">final AssetTree assetTree;
   class="kw">private class="kw">final String packKey;
   class="kw">private class="kw">final PluginManifest manifest;
   class="kw">private class="kw">final boolean isImmutable;
   class="kw">private class="kw">final Path recentModificationsFilePath;
   class="kw">private class="kw">final AtomicBoolean indexNeedsSaving = new AtomicBoolean();
   class="kw">private class="kw">final Map<Path, ModifiedAsset> modifiedAssets = new ConcurrentHashMap<>();
   class="kw">private ScheduledFuture<?> saveSchedule;
   class="kw">private boolean isAssetPackBeDeleteable;

   class="kw">public StandardDataSource(String var1, Path var2, boolean var3, PluginManifest var4) {
      this.rootPath = var2;
      this.editorSaves = new ConcurrentHashMap<>();
      this.packKey = var1;
      this.isImmutable = var3;
      this.manifest = var4;
      this.isAssetPackBeDeleteable = !var3 && isInModsDirectory(var2);
      this.assetTree = new AssetTree(var2, var1, var3, this.isAssetPackBeDeleteable);
      this.recentModificationsFilePath = Path.of("assetEditor", "recentAssetEdits_" + var1.replace(':', '-') + ".json");
   }

   class="kw">private class="kw">static boolean isInModsDirectory(Path var0) {
      if (var0.startsWith(PluginManager.MODS_PATH.toAbsolutePath())) {
         class="kw">return true;
      }

      for (Path var2 : Options.getOptionSet().valuesOf(Options.MODS_DIRECTORIES)) {
         if (var0.startsWith(var2)) {
            class="kw">return true;
         }
      }

      class="kw">return false;
   }

   @Override
   class="kw">public void start() {
      this.loadRecentModifications();
      this.saveSchedule = HytaleServer.SCHEDULED_EXECUTOR.scheduleWithFixedDelay(() -> {
         try {
            this.saveRecentModifications();
         } catch (Exception var2) {
            ((HytaleLogger.Api)LOGGER.at(Level.SEVERE).withCause(var2)).log("Failed to save assets index");
         }
      }, 1L, 1L, TimeUnit.MINUTES);
   }

   @Override
   class="kw">public void shutdown() {
      if (this.saveSchedule != null) {
         this.saveSchedule.cancel(false);
      }

      this.saveRecentModifications();
   }

   class="kw">private void loadRecentModifications() {
      Path var1 = this.recentModificationsFilePath;
      if (!Files.exists(var1)) {
         var1 = var1.resolveSibling(var1.getFileName() + ".bak");
         if (!Files.exists(var1)) {
            class="kw">return;
         }
      }

      BsonDocument var2 = BsonUtil.readDocument(var1).join();

      for (BsonValue var5 : var2.getArray("Assets")) {
         ModifiedAsset var6 = ModifiedAsset.CODEC.decode(var5, new ExtraInfo());
         if (var6 != null) {
            this.modifiedAssets.put(var6.path, var6);
         }
      }
   }

   class="kw">public void saveRecentModifications() {
      if (this.indexNeedsSaving.getAndSet(false)) {
         LOGGER.at(Level.INFO).log("Saving recent asset modification index...");
         BsonDocument var1 = new BsonDocument();
         BsonArray var2 = new BsonArray();

         for (Entry var4 : this.modifiedAssets.entrySet()) {
            var2.add(ModifiedAsset.CODEC.encode(var4.getValue(), new ExtraInfo()));
         }

         var1.append("Assets", var2);
         BsonUtil.writeDocument(this.recentModificationsFilePath, var1).exceptionally(var1x -> {
            ((HytaleLogger.Api)LOGGER.at(Level.SEVERE).withCause(var1x)).log("Failed to save recent asset modification index...");
            this.indexNeedsSaving.set(true);
            class="kw">return null;
         });
      }
   }

   class="kw">public boolean canAssetPackBeDeleted() {
      class="kw">return this.isAssetPackBeDeleteable;
   }

   class="kw">public Path resolveAbsolutePath(Path var1) {
      Path var2 = this.rootPath.resolve(var1.toString()).toAbsolutePath();
      if (!PathUtil.isChildOf(this.rootPath, var2)) {
         throw new IllegalArgumentException("Invalid path: " + var1);
      } else {
         class="kw">return var2;
      }
   }

   @Override
   class="kw">public Path getFullPathToAssetData(Path var1) {
      class="kw">return this.resolveAbsolutePath(var1);
   }

   @Override
   class="kw">public AssetTree getAssetTree() {
      class="kw">return this.assetTree;
   }

   @Override
   class="kw">public boolean isImmutable() {
      class="kw">return this.isImmutable;
   }

   @Override
   class="kw">public Path getRootPath() {
      class="kw">return this.rootPath;
   }

   @Override
   class="kw">public PluginManifest getManifest() {
      class="kw">return this.manifest;
   }

   @Override
   class="kw">public boolean doesDirectoryExist(Path var1) {
      class="kw">return Files.isDirectory(this.resolveAbsolutePath(var1));
   }

   @Override
   class="kw">public boolean createDirectory(Path var1, EditorClient var2) {
      try {
         Files.createDirectory(this.resolveAbsolutePath(var1));
         class="kw">return true;
      } catch (IOException var4) {
         ((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(var4)).log("Failed to create directory %s", var1);
         class="kw">return false;
      }
   }

   @Override
   class="kw">public boolean deleteDirectory(Path var1) {
      try {
         Files.deleteIfExists(this.resolveAbsolutePath(var1));
         class="kw">return true;
      } catch (IOException var3) {
         ((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(var3)).log("Failed to delete directory %s", var1);
         class="kw">return false;
      }
   }

   @Override
   class="kw">public boolean moveDirectory(Path var1, Path var2) {
      try {
         Files.move(this.resolveAbsolutePath(var1), this.resolveAbsolutePath(var2));
         class="kw">return true;
      } catch (IOException var4) {
         ((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(var4)).log("Failed to move directory %s to %s", var1, var2);
         class="kw">return false;
      }
   }

   @Override
   class="kw">public boolean doesAssetExist(Path var1) {
      class="kw">return Files.isRegularFile(this.resolveAbsolutePath(var1));
   }

   @Override
   class="kw">public byte[] getAssetBytes(Path var1) {
      try {
         class="kw">return Files.readAllBytes(this.resolveAbsolutePath(var1));
      } catch (IOException var3) {
         ((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(var3)).log("Failed to read asset %s", var1);
         class="kw">return null;
      }
   }

   @Override
   class="kw">public boolean updateAsset(Path var1, byte[] var2, EditorClient var3) {
      Path var4 = this.resolveAbsolutePath(var1);

      try {
         String var5 = HashUtil.sha256(var2);
         this.trackEditorFileSave(var1, var5);
         Files.write(var4, var2, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
         ModifiedAsset var6 = new ModifiedAsset();
         var6.path = var1;
         var6.state = AssetState.CHANGED;
         var6.markEditedBy(var3);
         this.putModifiedAsset(var6);
         class="kw">return true;
      } catch (IOException var7) {
         ((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(var7)).log("Failed to update asset %s", var1);
         class="kw">return false;
      }
   }

   @Override
   class="kw">public boolean createAsset(Path var1, byte[] var2, EditorClient var3) {
      Path var4 = this.resolveAbsolutePath(var1);

      try {
         String var5 = HashUtil.sha256(var2);
         this.trackEditorFileSave(var1, var5);
         Files.createDirectories(var4.getParent());
         Files.write(var4, var2, StandardOpenOption.CREATE);
         ModifiedAsset var6 = new ModifiedAsset();
         var6.path = var1;
         var6.state = AssetState.NEW;
         var6.markEditedBy(var3);
         this.putModifiedAsset(var6);
         class="kw">return true;
      } catch (IOException var7) {
         ((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(var7)).log("Failed to create asset %s", var1);
         class="kw">return false;
      }
   }

   @Override
   class="kw">public boolean deleteAsset(Path var1, EditorClient var2) {
      try {
         Files.deleteIfExists(this.resolveAbsolutePath(var1));
         ModifiedAsset var3 = new ModifiedAsset();
         var3.path = var1;
         var3.state = AssetState.DELETED;
         var3.markEditedBy(var2);
         this.putModifiedAsset(var3);
         class="kw">return true;
      } catch (IOException var4) {
         ((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(var4)).log("Failed to delete asset %s", var1);
         class="kw">return false;
      }
   }

   @Override
   class="kw">public boolean shouldReloadAssetFromDisk(Path var1) {
      Deque var2 = this.editorSaves.get(var1);
      if (var2 != null && !var2.isEmpty()) {
         byte[] var3 = this.getAssetBytes(var1);
         if (var3 == null) {
            class="kw">return true;
         }

         String var4 = HashUtil.sha256(var3);
         long var5 = System.currentTimeMillis();
         class="kw">synchronized (var2) {
            var2.removeIf(var2x -> var2x.expiryMs <= var5);

            for (StandardDataSource.EditorFileSaveInfo var9 : var2) {
               if (var9.hash.equals(var4)) {
                  class="kw">return false;
               }
            }

            class="kw">return true;
         }
      } else {
         class="kw">return true;
      }
   }

   @Override
   class="kw">public Instant getLastModificationTimestamp(Path var1) {
      class="kw">return null;
   }

   @Override
   class="kw">public boolean moveAsset(Path var1, Path var2, EditorClient var3) {
      try {
         Files.move(this.resolveAbsolutePath(var1), this.resolveAbsolutePath(var2));
         ModifiedAsset var4 = new ModifiedAsset();
         var4.path = var2;
         var4.oldPath = var1;
         var4.state = AssetState.CHANGED;
         var4.markEditedBy(var3);
         this.putModifiedAsset(var4);
         class="kw">return true;
      } catch (IOException var5) {
         ((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(var5)).log("Failed to move asset %s to %s", var1, var2);
         class="kw">return false;
      }
   }

   @Override
   class="kw">public AssetTree loadAssetTree(Collection<AssetTypeHandler> var1) {
      class="kw">return new AssetTree(this.rootPath, this.packKey, this.isImmutable, this.isAssetPackBeDeleteable, var1);
   }

   class="kw">public void putModifiedAsset(ModifiedAsset var1) {
      this.modifiedAssets.put(var1.path, var1);
      if (this.modifiedAssets.size() > 50) {
         ModifiedAsset var2 = null;

         for (ModifiedAsset var4 : this.modifiedAssets.values()) {
            if (var2 == null) {
               var2 = var4;
            } else if (var4.lastModificationTimestamp.isBefore(var2.lastModificationTimestamp)) {
               var2 = var4;
            }
         }

         this.modifiedAssets.remove(var2.path);
      }

      this.indexNeedsSaving.set(true);
   }

   class="kw">public Map<Path, ModifiedAsset> getRecentlyModifiedAssets() {
      class="kw">return this.modifiedAssets;
   }

   class="kw">private void trackEditorFileSave(Path var1, String var2) {
      Deque var3 = this.editorSaves.computeIfAbsent(var1, var0 -> new ArrayDeque<>());
      class="kw">synchronized (var3) {
         var3.addLast(new StandardDataSource.EditorFileSaveInfo(var2, System.currentTimeMillis() + 30000L));

         while (var3.size() > 20) {
            var3.removeFirst();
         }
      }
   }

   record EditorFileSaveInfo(String hash, long expiryMs) {
      EditorFileSaveInfo {
      }
   }
}