diff --git a/src/main/java/dan200/computercraft/shared/command/ClientCommands.java b/src/main/java/dan200/computercraft/shared/command/ClientCommands.java new file mode 100644 index 000000000..c16b3fdd1 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/command/ClientCommands.java @@ -0,0 +1,58 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.command; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.shared.util.IDAssigner; +import net.minecraft.util.Util; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.ClientChatEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +import java.io.File; + +/** + * Basic client-side commands. + * + * Simply hooks into client chat messages and intercepts matching strings. + */ +@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT ) +public final class ClientCommands +{ + public static final String OPEN_COMPUTER = "/computercraft open-computer "; + + private ClientCommands() + { + } + + @SubscribeEvent + public static void onClientSendMessage( ClientChatEvent event ) + { + // Emulate the command on the client side + if( event.getMessage().startsWith( OPEN_COMPUTER ) ) + { + event.setCanceled( true ); + + String idStr = event.getMessage().substring( OPEN_COMPUTER.length() ).trim(); + int id; + try + { + id = Integer.parseInt( idStr ); + } + catch( NumberFormatException ignore ) + { + return; + } + + File file = new File( IDAssigner.getDir(), "computer/" + id ); + if( !file.isDirectory() ) return; + + Util.getPlatform().openFile( file ); + } + } + +} diff --git a/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java b/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java index 33ae57947..04c798f38 100644 --- a/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java +++ b/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java @@ -21,6 +21,7 @@ import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.inventory.ContainerViewComputer; import dan200.computercraft.shared.network.container.ViewComputerContainerData; +import dan200.computercraft.shared.util.IDAssigner; import net.minecraft.command.CommandSource; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; @@ -37,6 +38,7 @@ import net.minecraft.world.World; import net.minecraft.world.server.ServerWorld; import javax.annotation.Nonnull; +import java.io.File; import java.util.*; import static dan200.computercraft.shared.command.CommandUtils.isPlayer; @@ -320,6 +322,12 @@ public final class CommandComputerCraft ) ); } + if( UserLevel.OWNER.test( source ) && isPlayer( source ) ) + { + ITextComponent linkPath = linkStorage( computerId ); + if( linkPath != null ) out.append( " " ).append( linkPath ); + } + return out; } @@ -339,6 +347,18 @@ public final class CommandComputerCraft } } + private static ITextComponent linkStorage( int id ) + { + File file = new File( IDAssigner.getDir(), "computer/" + id ); + if( !file.isDirectory() ) return null; + + return link( + text( "\u270E" ), + ClientCommands.OPEN_COMPUTER + id, + translate( "commands.computercraft.dump.open_path" ) + ); + } + @Nonnull private static TrackingContext getTimingContext( CommandSource source ) { diff --git a/src/main/java/dan200/computercraft/shared/command/CommandCopy.java b/src/main/java/dan200/computercraft/shared/command/CommandCopy.java deleted file mode 100644 index 435346c20..000000000 --- a/src/main/java/dan200/computercraft/shared/command/CommandCopy.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. - * Send enquiries to dratcliffe@gmail.com - */ -package dan200.computercraft.shared.command; - -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.arguments.StringArgumentType; -import dan200.computercraft.ComputerCraft; -import net.minecraft.client.Minecraft; -import net.minecraft.command.CommandSource; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.StringTextComponent; -import net.minecraft.util.text.TranslationTextComponent; -import net.minecraft.util.text.event.ClickEvent; -import net.minecraft.util.text.event.HoverEvent; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.ClientChatEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; - -import static net.minecraft.command.Commands.argument; -import static net.minecraft.command.Commands.literal; - -@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT ) -public final class CommandCopy -{ - private static final String PREFIX = "/computercraft copy "; - - private CommandCopy() - { - } - - public static void register( CommandDispatcher registry ) - { - registry.register( literal( "computercraft" ) - .then( literal( "copy" ) ) - .then( argument( "message", StringArgumentType.greedyString() ) ) - .executes( context -> { - Minecraft.getInstance().keyboardHandler.setClipboard( context.getArgument( "message", String.class ) ); - return 1; - } ) - ); - } - - @SubscribeEvent - public static void onClientSendMessage( ClientChatEvent event ) - { - // Emulate the command on the client side - if( event.getMessage().startsWith( PREFIX ) ) - { - Minecraft.getInstance().keyboardHandler.setClipboard( event.getMessage().substring( PREFIX.length() ) ); - event.setCanceled( true ); - } - } - - public static ITextComponent createCopyText( String text ) - { - StringTextComponent name = new StringTextComponent( text ); - name.getStyle() - .setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, PREFIX + text ) ) - .setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new TranslationTextComponent( "gui.computercraft.tooltip.copy" ) ) ); - return name; - } -} diff --git a/src/main/java/dan200/computercraft/shared/command/UserLevel.java b/src/main/java/dan200/computercraft/shared/command/UserLevel.java index 2492bd119..daccef334 100644 --- a/src/main/java/dan200/computercraft/shared/command/UserLevel.java +++ b/src/main/java/dan200/computercraft/shared/command/UserLevel.java @@ -57,14 +57,15 @@ public enum UserLevel implements Predicate { if( this == ANYONE ) return true; - // We *always* allow level 0 stuff, even if the - MinecraftServer server = source.getServer(); - Entity sender = source.getEntity(); - - if( server.isSingleplayer() && sender instanceof PlayerEntity && - ((PlayerEntity) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ) ) + if( this == OWNER || this == OWNER_OP ) { - if( this == OWNER || this == OWNER_OP ) return true; + MinecraftServer server = source.getServer(); + Entity sender = source.getEntity(); + if( server.isSingleplayer() && sender instanceof PlayerEntity && + ((PlayerEntity) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ) ) + { + return true; + } } return source.hasPermission( toLevel() ); diff --git a/src/main/java/dan200/computercraft/shared/command/text/ChatHelpers.java b/src/main/java/dan200/computercraft/shared/command/text/ChatHelpers.java index 0b2d8de35..18a10c248 100644 --- a/src/main/java/dan200/computercraft/shared/command/text/ChatHelpers.java +++ b/src/main/java/dan200/computercraft/shared/command/text/ChatHelpers.java @@ -71,11 +71,16 @@ public final class ChatHelpers } public static ITextComponent link( ITextComponent component, String command, ITextComponent toolTip ) + { + return link( component, new ClickEvent( ClickEvent.Action.RUN_COMMAND, command ), toolTip ); + } + + public static ITextComponent link( ITextComponent component, ClickEvent click, ITextComponent toolTip ) { Style style = component.getStyle(); if( style.getColor() == null ) style.setColor( TextFormatting.YELLOW ); - style.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, command ) ); + style.setClickEvent( click ); style.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, toolTip ) ); return component; @@ -85,4 +90,13 @@ public final class ChatHelpers { return coloured( text, HEADER ); } + + public static ITextComponent copy( String text ) + { + StringTextComponent name = new StringTextComponent( text ); + name.getStyle() + .setClickEvent( new ClickEvent( ClickEvent.Action.COPY_TO_CLIPBOARD, text ) ) + .setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new TranslationTextComponent( "gui.computercraft.tooltip.copy" ) ) ); + return name; + } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java index 449444664..371fc19e4 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java @@ -11,7 +11,7 @@ import dan200.computercraft.api.network.wired.IWiredElement; import dan200.computercraft.api.network.wired.IWiredNode; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.shared.Registry; -import dan200.computercraft.shared.command.CommandCopy; +import dan200.computercraft.shared.command.text.ChatHelpers; import dan200.computercraft.shared.common.TileGeneric; import dan200.computercraft.shared.peripheral.modem.ModemState; import dan200.computercraft.shared.util.CapabilityUtil; @@ -268,12 +268,12 @@ public class TileCable extends TileGeneric if( oldName != null ) { player.displayClientMessage( new TranslationTextComponent( "chat.computercraft.wired_modem.peripheral_disconnected", - CommandCopy.createCopyText( oldName ) ), false ); + ChatHelpers.copy( oldName ) ), false ); } if( newName != null ) { player.displayClientMessage( new TranslationTextComponent( "chat.computercraft.wired_modem.peripheral_connected", - CommandCopy.createCopyText( newName ) ), false ); + ChatHelpers.copy( newName ) ), false ); } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java index 74696d132..4cedc5ad8 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java @@ -10,7 +10,7 @@ import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.network.wired.IWiredElement; import dan200.computercraft.api.network.wired.IWiredNode; import dan200.computercraft.api.peripheral.IPeripheral; -import dan200.computercraft.shared.command.CommandCopy; +import dan200.computercraft.shared.command.text.ChatHelpers; import dan200.computercraft.shared.common.TileGeneric; import dan200.computercraft.shared.peripheral.modem.ModemState; import dan200.computercraft.shared.util.CapabilityUtil; @@ -218,7 +218,7 @@ public class TileWiredModemFull extends TileGeneric for( int i = 0; i < names.size(); i++ ) { if( i > 0 ) base.append( ", " ); - base.append( CommandCopy.createCopyText( names.get( i ) ) ); + base.append( ChatHelpers.copy( names.get( i ) ) ); } player.displayClientMessage( new TranslationTextComponent( kind, base ), false ); diff --git a/src/main/resources/assets/computercraft/lang/en_us.json b/src/main/resources/assets/computercraft/lang/en_us.json index 0de1b7c4b..f97fc40d7 100644 --- a/src/main/resources/assets/computercraft/lang/en_us.json +++ b/src/main/resources/assets/computercraft/lang/en_us.json @@ -48,6 +48,7 @@ "commands.computercraft.dump.synopsis": "Display the status of computers.", "commands.computercraft.dump.desc": "Display the status of all computers or specific information about one computer. You can specify the computer's instance id (e.g. 123), computer id (e.g #123) or label (e.g. \"@My Computer\").", "commands.computercraft.dump.action": "View more info about this computer", + "commands.computercraft.dump.open_path": "View this computer's files", "commands.computercraft.shutdown.synopsis": "Shutdown computers remotely.", "commands.computercraft.shutdown.desc": "Shutdown the listed computers or all if none are specified. You can specify the computer's instance id (e.g. 123), computer id (e.g #123) or label (e.g. \"@My Computer\").", "commands.computercraft.shutdown.done": "Shutdown %s/%s computers",