308 lines
15 KiB
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));
|
|
}
|
|
}
|
|
}
|