CC-Tweaked/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java

247 lines
9.2 KiB
Java

/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.computer.apis;
import com.google.common.collect.ImmutableMap;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.shared.computer.blocks.TileCommandComputer;
import dan200.computercraft.shared.util.NBTUtil;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.server.MinecraftServer;
import net.minecraft.state.IProperty;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.registries.ForgeRegistries;
import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
public class CommandAPI implements ILuaAPI
{
private TileCommandComputer m_computer;
public CommandAPI( TileCommandComputer computer )
{
m_computer = computer;
}
// ILuaAPI implementation
@Override
public String[] getNames()
{
return new String[] { "commands" };
}
@Nonnull
@Override
public String[] getMethodNames()
{
return new String[] {
"exec",
"execAsync",
"list",
"getBlockPosition",
"getBlockInfos",
"getBlockInfo",
};
}
private static Map<Object, Object> createOutput( String output )
{
return Collections.singletonMap( 1, output );
}
private Object[] doCommand( String command )
{
MinecraftServer server = m_computer.getWorld().getServer();
if( server == null || !server.isCommandBlockEnabled() )
{
return new Object[] { false, createOutput( "Command blocks disabled by server" ) };
}
Commands commandManager = server.getCommandManager();
TileCommandComputer.CommandReceiver receiver = m_computer.getReceiver();
try
{
receiver.clearOutput();
int result = commandManager.handleCommand( m_computer.getSource(), command );
return new Object[] { result > 0, receiver.copyOutput() };
}
catch( Throwable t )
{
if( ComputerCraft.logPeripheralErrors ) ComputerCraft.log.error( "Error running command.", t );
return new Object[] { false, createOutput( "Java Exception Thrown: " + t ) };
}
}
private static Object getBlockInfo( World world, BlockPos pos )
{
// Get the details of the block
IBlockState state = world.getBlockState( pos );
Block block = state.getBlock();
Map<Object, Object> table = new HashMap<>();
table.put( "name", ForgeRegistries.BLOCKS.getKey( block ).toString() );
Map<Object, Object> stateTable = new HashMap<>();
for( ImmutableMap.Entry<IProperty<?>, Comparable<?>> entry : state.getValues().entrySet() )
{
IProperty<?> property = entry.getKey();
stateTable.put( property.getName(), getPropertyValue( property, entry.getValue() ) );
}
table.put( "state", stateTable );
TileEntity tile = world.getTileEntity( pos );
if( tile != null ) table.put( "nbt", NBTUtil.toLua( tile.write( new NBTTagCompound() ) ) );
return table;
}
@SuppressWarnings( { "unchecked", "rawtypes" } )
private static Object getPropertyValue( IProperty property, Comparable value )
{
if( value instanceof String || value instanceof Number || value instanceof Boolean ) return value;
return property.getName( value );
}
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
{
switch( method )
{
case 0: // exec
{
final String command = getString( arguments, 0 );
return context.executeMainThreadTask( () -> doCommand( command ) );
}
case 1: // execAsync
{
final String command = getString( arguments, 0 );
long taskID = context.issueMainThreadTask( () -> doCommand( command ) );
return new Object[] { taskID };
}
case 2:
// list
return context.executeMainThreadTask( () ->
{
MinecraftServer server = m_computer.getWorld().getServer();
if( server == null ) return new Object[] { Collections.emptyMap() };
CommandNode<CommandSource> node = server.getCommandManager().getDispatcher().getRoot();
for( int j = 0; j < arguments.length; j++ )
{
String name = getString( arguments, j );
node = node.getChild( name );
if( !(node instanceof LiteralCommandNode) ) return new Object[] { Collections.emptyMap() };
}
int i = 1;
Map<Object, Object> result = new HashMap<>();
for( CommandNode<?> child : node.getChildren() )
{
if( child instanceof LiteralCommandNode<?> ) result.put( i++, child.getName() );
}
return new Object[] { result };
} );
case 3: // getBlockPosition
{
// This is probably safe to do on the Lua thread. Probably.
BlockPos pos = m_computer.getPos();
return new Object[] { pos.getX(), pos.getY(), pos.getZ() };
}
case 4:
{
// getBlockInfos
final int minX = getInt( arguments, 0 );
final int minY = getInt( arguments, 1 );
final int minZ = getInt( arguments, 2 );
final int maxX = getInt( arguments, 3 );
final int maxY = getInt( arguments, 4 );
final int maxZ = getInt( arguments, 5 );
return context.executeMainThreadTask( () ->
{
// Get the details of the block
World world = m_computer.getWorld();
BlockPos min = new BlockPos(
Math.min( minX, maxX ),
Math.min( minY, maxY ),
Math.min( minZ, maxZ )
);
BlockPos max = new BlockPos(
Math.max( minX, maxX ),
Math.max( minY, maxY ),
Math.max( minZ, maxZ )
);
if( !World.isValid( min ) || !World.isValid( max ) )
{
throw new LuaException( "Co-ordinates out or range" );
}
if( (max.getX() - min.getX() + 1) * (max.getY() - min.getY() + 1) * (max.getZ() - min.getZ() + 1) > 4096 )
{
throw new LuaException( "Too many blocks" );
}
int i = 1;
Map<Object, Object> results = new HashMap<>();
for( int y = min.getY(); y <= max.getY(); y++ )
{
for( int z = min.getZ(); z <= max.getZ(); z++ )
{
for( int x = min.getX(); x <= max.getX(); x++ )
{
BlockPos pos = new BlockPos( x, y, z );
results.put( i++, getBlockInfo( world, pos ) );
}
}
}
return new Object[] { results };
} );
}
case 5:
{
// getBlockInfo
final int x = getInt( arguments, 0 );
final int y = getInt( arguments, 1 );
final int z = getInt( arguments, 2 );
return context.executeMainThreadTask( () ->
{
// Get the details of the block
World world = m_computer.getWorld();
BlockPos position = new BlockPos( x, y, z );
if( World.isValid( position ) )
{
return new Object[] { getBlockInfo( world, position ) };
}
else
{
throw new LuaException( "co-ordinates out or range" );
}
} );
}
default:
{
return null;
}
}
}
}