hytale-server/com/hypixel/hytale/builtin/buildertools/commands/PrefabCommand.java

308 lines
15 KiB
Java

package com.hypixel.hytale.builtin.buildertools.commands;
import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin;
import com.hypixel.hytale.builtin.buildertools.prefablist.PrefabPage;
import com.hypixel.hytale.builtin.buildertools.prefablist.PrefabSavePage;
import com.hypixel.hytale.builtin.buildertools.utils.RecursivePrefabLoader;
import com.hypixel.hytale.common.util.PathUtil;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand;
import com.hypixel.hytale.server.core.command.system.basecommands.CommandBase;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.modules.singleplayer.SingleplayerModule;
import com.hypixel.hytale.server.core.prefab.PrefabStore;
import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.util.io.FileUtil;
import com.hypixel.hytale.server.core.util.message.MessageFormat;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.Random;
import java.util.function.BiFunction;
import java.util.function.Function;
import javax.annotation.Nonnull;
public class PrefabCommand extends AbstractCommandCollection {
public PrefabCommand() {
super("prefab", "server.commands.prefab.desc");
this.addAliases("p");
this.setPermissionGroup(GameMode.Creative);
this.addSubCommand(new PrefabCommand.PrefabSaveCommand());
this.addSubCommand(new PrefabCommand.PrefabLoadCommand());
this.addSubCommand(new PrefabCommand.PrefabDeleteCommand());
this.addSubCommand(new PrefabCommand.PrefabListCommand());
}
private static class PrefabDeleteCommand extends CommandBase {
@Nonnull
private final RequiredArg<String> nameArg = this.withRequiredArg("name", "server.commands.prefab.delete.name.desc", ArgTypes.STRING);
public PrefabDeleteCommand() {
super("delete", "server.commands.prefab.delete.desc", true);
this.requirePermission("hytale.editor.prefab.manage");
}
@Override
protected void executeSync(@Nonnull CommandContext context) {
String name = this.nameArg.get(context);
if (!name.endsWith(".prefab.json")) {
name = name + ".prefab.json";
}
PrefabStore module = PrefabStore.get();
Path serverPrefabsPath = module.getServerPrefabsPath();
Path resolve = serverPrefabsPath.resolve(name);
try {
Ref<EntityStore> ref = context.senderAsPlayerRef();
boolean isOwner = false;
if (ref != null && ref.isValid()) {
Store<EntityStore> store = ref.getStore();
PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType());
if (playerRefComponent != null) {
isOwner = SingleplayerModule.isOwner(playerRefComponent);
}
}
if (!PathUtil.isChildOf(serverPrefabsPath, resolve) && !isOwner) {
context.sendMessage(Message.translation("server.builderTools.attemptedToSaveOutsidePrefabsDir"));
return;
}
Path relativize = PathUtil.relativize(serverPrefabsPath, resolve);
if (Files.isRegularFile(resolve)) {
Files.delete(resolve);
context.sendMessage(Message.translation("server.builderTools.prefab.deleted").param("name", relativize.toString()));
} else {
context.sendMessage(Message.translation("server.builderTools.prefab.prefabNotFound").param("name", relativize.toString()));
}
} catch (IOException var10) {
context.sendMessage(Message.translation("server.builderTools.prefab.errorOccured").param("reason", var10.getMessage()));
}
}
}
private static class PrefabListCommand extends CommandBase {
@Nonnull
private final DefaultArg<String> storeTypeArg = this.withDefaultArg(
"storeType", "server.commands.prefab.list.storeType.desc", ArgTypes.STRING, "asset", "server.commands.prefab.list.storeType.desc"
);
@Nonnull
private final FlagArg textFlag = this.withFlagArg("text", "server.commands.prefab.list.text.desc");
public PrefabListCommand() {
super("list", "server.commands.prefab.list.desc");
}
@Override
protected void executeSync(@Nonnull CommandContext context) {
String storeType = this.storeTypeArg.get(context);
final Path prefabStorePath = switch (storeType) {
case "server" -> PrefabStore.get().getServerPrefabsPath();
case "asset" -> {
List<PrefabStore.AssetPackPrefabPath> assetPaths = PrefabStore.get().getAllAssetPrefabPaths();
yield assetPaths.isEmpty() ? PrefabStore.get().getAssetPrefabsPath() : assetPaths.getFirst().prefabsPath();
}
case "worldgen" -> PrefabStore.get().getWorldGenPrefabsPath();
default -> throw new IllegalStateException("Unexpected value: " + storeType);
};
Ref<EntityStore> ref = context.senderAsPlayerRef();
if (ref != null && ref.isValid() && !this.textFlag.get(context)) {
Store<EntityStore> store = ref.getStore();
World world = store.getExternalData().getWorld();
world.execute(() -> {
Player playerComponent = store.getComponent(ref, Player.getComponentType());
assert playerComponent != null;
PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType());
assert playerRefComponent != null;
BuilderToolsPlugin.BuilderState builderState = BuilderToolsPlugin.getState(playerComponent, playerRefComponent);
playerComponent.getPageManager().openCustomPage(ref, store, new PrefabPage(playerRefComponent, prefabStorePath, builderState));
});
} else {
try {
final List<Message> prefabFiles = new ObjectArrayList();
if ("asset".equals(storeType)) {
for (PrefabStore.AssetPackPrefabPath packPath : PrefabStore.get().getAllAssetPrefabPaths()) {
final Path path = packPath.prefabsPath();
final String packPrefix = packPath.isBasePack() ? "" : "[" + packPath.getPackName() + "] ";
if (Files.isDirectory(path)) {
Files.walkFileTree(path, FileUtil.DEFAULT_WALK_TREE_OPTIONS_SET, Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
@Nonnull
public FileVisitResult visitFile(@Nonnull Path file, @Nonnull BasicFileAttributes attrs) {
String fileName = file.getFileName().toString();
if (fileName.endsWith(".prefab.json")) {
prefabFiles.add(Message.raw(packPrefix + PathUtil.relativize(path, file).toString()));
}
return FileVisitResult.CONTINUE;
}
});
}
}
} else if (Files.isDirectory(prefabStorePath)) {
Files.walkFileTree(prefabStorePath, FileUtil.DEFAULT_WALK_TREE_OPTIONS_SET, Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
@Nonnull
public FileVisitResult visitFile(@Nonnull Path file, @Nonnull BasicFileAttributes attrs) {
String fileName = file.getFileName().toString();
if (fileName.endsWith(".prefab.json")) {
prefabFiles.add(Message.raw(PathUtil.relativize(prefabStorePath, file).toString()));
}
return FileVisitResult.CONTINUE;
}
});
}
context.sendMessage(MessageFormat.list(Message.translation("server.commands.prefab.list.header"), prefabFiles));
} catch (IOException var10) {
context.sendMessage(Message.translation("server.builderTools.prefab.errorListingPrefabs").param("reason", var10.getMessage()));
}
}
}
}
private static class PrefabLoadByNameCommand extends AbstractPlayerCommand {
@Nonnull
private final RequiredArg<String> nameArg = this.withRequiredArg("name", "server.commands.prefab.load.name.desc", ArgTypes.STRING);
@Nonnull
private final DefaultArg<String> storeTypeArg = this.withDefaultArg(
"storeType", "server.commands.prefab.load.storeType.desc", ArgTypes.STRING, "asset", "server.commands.prefab.load.storeType.desc"
);
@Nonnull
private final DefaultArg<String> storeNameArg = this.withDefaultArg("storeName", "server.commands.prefab.load.storeName.desc", ArgTypes.STRING, null, "");
@Nonnull
private final FlagArg childrenFlag = this.withFlagArg("children", "server.commands.prefab.load.children.desc");
public PrefabLoadByNameCommand() {
super("server.commands.prefab.load.desc");
}
@Override
protected void execute(
@Nonnull CommandContext context, @Nonnull Store<EntityStore> store, @Nonnull Ref<EntityStore> ref, @Nonnull PlayerRef playerRef, @Nonnull World world
) {
Player playerComponent = store.getComponent(ref, Player.getComponentType());
assert playerComponent != null;
String storeType = this.storeTypeArg.get(context);
String storeName = this.storeNameArg.get(context);
String name = this.nameArg.get(context);
if (!name.endsWith(".prefab.json")) {
name = name + ".prefab.json";
}
Path prefabStorePath = null;
Path resolvedPrefabPath = null;
String finalName = name;
Function<String, BlockSelection> prefabGetter = switch (storeType) {
case "server" -> {
prefabStorePath = PrefabStore.get().getServerPrefabsPath();
yield PrefabStore.get()::getServerPrefab;
}
case "asset" -> {
Path foundPath = PrefabStore.get().findAssetPrefabPath(finalName);
if (foundPath != null) {
resolvedPrefabPath = foundPath;
prefabStorePath = foundPath.getParent();
yield key -> PrefabStore.get().getPrefab(foundPath);
} else {
prefabStorePath = PrefabStore.get().getAssetPrefabsPath();
yield PrefabStore.get()::getAssetPrefab;
}
}
case "worldgen" -> {
Path storePath = PrefabStore.get().getWorldGenPrefabsPath(storeName);
prefabStorePath = PrefabStore.get().getWorldGenPrefabsPath(storeName);
yield key -> PrefabStore.get().getWorldGenPrefab(storePath, key);
}
default -> {
context.sendMessage(Message.translation("server.commands.prefab.invalidStoreType").param("storeType", storeType));
yield null;
}
};
if (prefabGetter != null) {
BiFunction<String, Random, BlockSelection> loader;
if (this.childrenFlag.get(context)) {
loader = new RecursivePrefabLoader.BlockSelectionLoader(prefabStorePath, prefabGetter);
} else {
loader = (prefabFile, rand) -> prefabGetter.apply(prefabFile);
}
boolean prefabExists = resolvedPrefabPath != null && Files.isRegularFile(resolvedPrefabPath) || Files.isRegularFile(prefabStorePath.resolve(name));
if (prefabExists) {
BuilderToolsPlugin.addToQueue(
playerComponent, playerRef, (r, s, componentAccessor) -> s.load(finalName, loader.apply(finalName, s.getRandom()), componentAccessor)
);
} else {
context.sendMessage(Message.translation("server.builderTools.prefab.prefabNotFound").param("name", name));
}
}
}
}
private static class PrefabLoadCommand extends AbstractPlayerCommand {
public PrefabLoadCommand() {
super("load", "server.commands.prefab.load.desc");
this.requirePermission("hytale.editor.prefab.use");
this.addUsageVariant(new PrefabCommand.PrefabLoadByNameCommand());
}
@Override
protected void execute(
@Nonnull CommandContext context, @Nonnull Store<EntityStore> store, @Nonnull Ref<EntityStore> ref, @Nonnull PlayerRef playerRef, @Nonnull World world
) {
Player playerComponent = store.getComponent(ref, Player.getComponentType());
assert playerComponent != null;
List<PrefabStore.AssetPackPrefabPath> assetPaths = PrefabStore.get().getAllAssetPrefabPaths();
Path defaultRoot = assetPaths.isEmpty() ? PrefabStore.get().getServerPrefabsPath() : assetPaths.getFirst().prefabsPath();
BuilderToolsPlugin.BuilderState builderState = BuilderToolsPlugin.getState(playerComponent, playerRef);
playerComponent.getPageManager().openCustomPage(ref, store, new PrefabPage(playerRef, defaultRoot, builderState));
}
}
private static class PrefabSaveCommand extends AbstractPlayerCommand {
public PrefabSaveCommand() {
super("save", "server.commands.prefab.save.desc");
this.requirePermission("hytale.editor.prefab.manage");
}
@Override
protected void execute(
@Nonnull CommandContext context, @Nonnull Store<EntityStore> store, @Nonnull Ref<EntityStore> ref, @Nonnull PlayerRef playerRef, @Nonnull World world
) {
Player playerComponent = store.getComponent(ref, Player.getComponentType());
assert playerComponent != null;
BuilderToolsPlugin.BuilderState builderState = BuilderToolsPlugin.getState(playerComponent, playerRef);
playerComponent.getPageManager().openCustomPage(ref, store, new PrefabSavePage(playerRef));
}
}
}