mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-16 14:37:39 +00:00
Compare commits
22 Commits
v1.80pr1.1
...
v1.80pr1.3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
abd06133fb | ||
![]() |
29a3a0c48f | ||
![]() |
2728c63512 | ||
![]() |
f3b11bc1c2 | ||
![]() |
04590befb3 | ||
![]() |
4e9034f910 | ||
![]() |
ba9cfa3764 | ||
![]() |
341e3e2f89 | ||
![]() |
3f70ca5192 | ||
![]() |
f11bfc53ee | ||
![]() |
61e3967b8e | ||
![]() |
add86ea100 | ||
![]() |
dd51c89278 | ||
![]() |
788d783745 | ||
![]() |
35da60543e | ||
![]() |
ce7923d248 | ||
![]() |
55847460c5 | ||
![]() |
893524b0a8 | ||
![]() |
8fb3ae405f | ||
![]() |
aa447ec101 | ||
![]() |
56b1cb4521 | ||
![]() |
90cc24614c |
@@ -5,7 +5,9 @@ indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
# Sadly too many files have whitespace errors, so we leave this as is for
|
||||
# now and just make sure we don't introduce any more.
|
||||
# trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
|
@@ -1,8 +1,8 @@
|
||||
# CC: Tweaked
|
||||
# 
|
||||
[](https://travis-ci.org/SquidDev-CC/CC-Tweaked)
|
||||
|
||||
CC: Tweaked is a fork of ComputerCraft which aims to provide more earlier access to the more experimental and
|
||||
in-development features of the mod. For a more stable experience, I recommend checking out the
|
||||
CC: Tweaked is a fork of ComputerCraft which aims to provide earlier access to the more experimental and in-development
|
||||
features of the mod. For a more stable experience, I recommend checking out the
|
||||
[original mod](https://github.com/dan200/ComputerCraft).
|
||||
|
||||
## What?
|
||||
|
23
build.gradle
23
build.gradle
@@ -13,17 +13,16 @@ buildscript {
|
||||
classpath 'org.ajoberstar:gradle-git:1.6.0'
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.matthewprenger.cursegradle' version '1.0.9'
|
||||
}
|
||||
|
||||
apply plugin: 'net.minecraftforge.gradle.forge'
|
||||
apply plugin: 'org.ajoberstar.grgit'
|
||||
|
||||
/*
|
||||
// for people who want stable - not yet functional for MC 1.8.8 - we require the forgegradle 2.1 snapshot
|
||||
plugins {
|
||||
id "net.minecraftforge.gradle.forge" version "2.0.2"
|
||||
}
|
||||
*/
|
||||
|
||||
version = "1.80pr1.1"
|
||||
version = "1.80pr1.3"
|
||||
group = "org.squiddev"
|
||||
archivesBaseName = "cc-tweaked"
|
||||
|
||||
@@ -115,6 +114,16 @@ processResources {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
curseforge {
|
||||
apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : ''
|
||||
project {
|
||||
id = '282001'
|
||||
releaseType = 'beta'
|
||||
changelog = ''
|
||||
}
|
||||
}
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << "-Xlint"
|
||||
|
@@ -9,6 +9,7 @@ package dan200.computercraft;
|
||||
import com.google.common.base.CaseFormat;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||
import dan200.computercraft.api.media.IMedia;
|
||||
import dan200.computercraft.api.media.IMediaProvider;
|
||||
import dan200.computercraft.api.network.IPacketNetwork;
|
||||
@@ -22,12 +23,16 @@ import dan200.computercraft.core.apis.AddressPredicate;
|
||||
import dan200.computercraft.core.filesystem.ComboMount;
|
||||
import dan200.computercraft.core.filesystem.FileMount;
|
||||
import dan200.computercraft.core.filesystem.JarMount;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.command.CommandComputer;
|
||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
||||
import dan200.computercraft.shared.computer.blocks.BlockCommandComputer;
|
||||
import dan200.computercraft.shared.computer.blocks.BlockComputer;
|
||||
import dan200.computercraft.shared.computer.blocks.TileComputer;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputerRegistry;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
|
||||
import dan200.computercraft.shared.media.items.ItemDiskExpanded;
|
||||
import dan200.computercraft.shared.media.items.ItemDiskLegacy;
|
||||
@@ -82,10 +87,7 @@ import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
@@ -110,6 +112,7 @@ public class ComputerCraft
|
||||
// ComputerCraftEdu uses ID 104
|
||||
public static final int printoutGUIID = 105;
|
||||
public static final int pocketComputerGUIID = 106;
|
||||
public static final int viewComputerGUIID = 110;
|
||||
|
||||
// Configuration options
|
||||
private static final String[] DEFAULT_HTTP_WHITELIST = new String[] { "*" };
|
||||
@@ -251,6 +254,7 @@ public class ComputerCraft
|
||||
private static List<IMediaProvider> mediaProviders = new ArrayList<>();
|
||||
private static List<ITurtlePermissionProvider> permissionProviders = new ArrayList<>();
|
||||
private static final Map<String, IPocketUpgrade> pocketUpgrades = new HashMap<>();
|
||||
private static final Set<ILuaAPIFactory> apiFactories = new LinkedHashSet<>();
|
||||
|
||||
// Implementation
|
||||
@Mod.Instance( value = ComputerCraft.MOD_ID )
|
||||
@@ -424,6 +428,7 @@ public class ComputerCraft
|
||||
public void onServerStarting( FMLServerStartingEvent event )
|
||||
{
|
||||
event.registerServerCommand( new CommandComputer() );
|
||||
event.registerServerCommand( new CommandComputerCraft() );
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
@@ -520,6 +525,24 @@ public class ComputerCraft
|
||||
player.openGui( ComputerCraft.instance, ComputerCraft.pocketComputerGUIID, player.getEntityWorld(), hand.ordinal(), 0, 0 );
|
||||
}
|
||||
|
||||
public static void openComputerGUI( EntityPlayer player, ServerComputer computer )
|
||||
{
|
||||
ComputerFamily family = computer.getFamily();
|
||||
int width = 0, height = 0;
|
||||
Terminal terminal = computer.getTerminal();
|
||||
if( terminal != null )
|
||||
{
|
||||
width = terminal.getWidth();
|
||||
height = terminal.getHeight();
|
||||
}
|
||||
|
||||
// Pack useful terminal information into the various coordinate bits.
|
||||
// These are extracted in ComputerCraftProxyCommon.getClientGuiElement
|
||||
player.openGui( ComputerCraft.instance, ComputerCraft.viewComputerGUIID, player.getEntityWorld(),
|
||||
computer.getInstanceID(), family.ordinal(), (width & 0xFFFF) << 16 | (height & 0xFFFF)
|
||||
);
|
||||
}
|
||||
|
||||
public static File getBaseDir()
|
||||
{
|
||||
return FMLCommonHandler.instance().getMinecraftServerInstance().getFile(".");
|
||||
@@ -673,6 +696,14 @@ public class ComputerCraft
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerAPIFactory( ILuaAPIFactory provider )
|
||||
{
|
||||
if( provider != null )
|
||||
{
|
||||
apiFactories.add( provider );
|
||||
}
|
||||
}
|
||||
|
||||
public static IPeripheral getPeripheralAt( World world, BlockPos pos, EnumFacing side )
|
||||
{
|
||||
// Try the handlers in order:
|
||||
@@ -800,6 +831,11 @@ public class ComputerCraft
|
||||
return WirelessNetwork.getUniversal();
|
||||
}
|
||||
|
||||
public static Iterable<ILuaAPIFactory> getAPIFactories()
|
||||
{
|
||||
return apiFactories;
|
||||
}
|
||||
|
||||
public static int createUniqueNumberedSaveDir( World world, String parentSubPath )
|
||||
{
|
||||
return IDAssigner.getNextIDFromDirectory(new File(getWorldDir(world), parentSubPath));
|
||||
|
@@ -8,6 +8,7 @@ package dan200.computercraft.api;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||
import dan200.computercraft.api.media.IMedia;
|
||||
import dan200.computercraft.api.media.IMediaProvider;
|
||||
import dan200.computercraft.api.network.IPacketNetwork;
|
||||
@@ -311,6 +312,22 @@ public final class ComputerCraftAPI
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void registerAPIFactory( @Nonnull ILuaAPIFactory upgrade )
|
||||
{
|
||||
findCC();
|
||||
if( computerCraft_registerAPIFactory != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
computerCraft_registerAPIFactory.invoke( null, upgrade );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
// It failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The functions below here are private, and are used to interface with the non-API ComputerCraft classes.
|
||||
// Reflection is used here so you can develop your mod without decompiling ComputerCraft and including
|
||||
// it in your solution, and so your mod won't crash if ComputerCraft is installed.
|
||||
@@ -354,6 +371,9 @@ public final class ComputerCraftAPI
|
||||
} );
|
||||
computerCraft_getWirelessNetwork = findCCMethod( "getWirelessNetwork", new Class<?>[] {
|
||||
} );
|
||||
computerCraft_registerAPIFactory = findCCMethod( "registerAPIFactory", new Class<?>[] {
|
||||
ILuaAPIFactory.class
|
||||
} );
|
||||
} catch( Exception e ) {
|
||||
System.out.println( "ComputerCraftAPI: ComputerCraft not found." );
|
||||
} finally {
|
||||
@@ -390,4 +410,5 @@ public final class ComputerCraftAPI
|
||||
private static Method computerCraft_registerPermissionProvider = null;
|
||||
private static Method computerCraft_registerPocketUpgrade = null;
|
||||
private static Method computerCraft_getWirelessNetwork = null;
|
||||
private static Method computerCraft_registerAPIFactory = null;
|
||||
}
|
||||
|
@@ -0,0 +1,38 @@
|
||||
package dan200.computercraft.api.filesystem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Provides a mount of the entire computer's file system.
|
||||
*
|
||||
* This exists for use by various APIs - one should not attempt to mount it.
|
||||
*/
|
||||
public interface IFileSystem extends IWritableMount
|
||||
{
|
||||
/**
|
||||
* Combine two paths together, reducing them into a normalised form.
|
||||
*
|
||||
* @param path The main path.
|
||||
* @param child The path to append.
|
||||
* @return The combined, normalised path.
|
||||
*/
|
||||
String combine( String path, String child );
|
||||
|
||||
/**
|
||||
* Copy files from one location to another.
|
||||
*
|
||||
* @param from The location to copy from.
|
||||
* @param to The location to copy to. This should not exist.
|
||||
* @throws IOException If the copy failed.
|
||||
*/
|
||||
void copy( String from, String to ) throws IOException;
|
||||
|
||||
/**
|
||||
* Move files from one location to another.
|
||||
*
|
||||
* @param from The location to move from.
|
||||
* @param to The location to move to. This should not exist.
|
||||
* @throws IOException If the move failed.
|
||||
*/
|
||||
void move( String from, String to ) throws IOException;
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IFileSystem;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An interface passed to {@link ILuaAPIFactory} in order to provide additional information
|
||||
* about a computer.
|
||||
*/
|
||||
public interface IComputerSystem extends IComputerAccess
|
||||
{
|
||||
/**
|
||||
* Get the file system for this computer.
|
||||
*
|
||||
* @return The computer's file system, or {@code null} if it is not initialised.
|
||||
*/
|
||||
@Nullable
|
||||
IFileSystem getFileSystem();
|
||||
|
||||
/**
|
||||
* Get the label for this computer
|
||||
*
|
||||
* @return This computer's label, or {@code null} if it is not set.
|
||||
*/
|
||||
@Nullable
|
||||
String getLabel();
|
||||
}
|
47
src/main/java/dan200/computercraft/api/lua/ILuaAPI.java
Normal file
47
src/main/java/dan200/computercraft/api/lua/ILuaAPI.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2016. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
|
||||
/**
|
||||
* Represents a {@link ILuaObject} which is stored as a global variable on computer startup.
|
||||
*
|
||||
* Before implementing this interface, consider alternative methods of providing methods. It is generally preferred
|
||||
* to use peripherals to provide functionality to users.
|
||||
*
|
||||
* @see ILuaAPIFactory
|
||||
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
||||
*/
|
||||
public interface ILuaAPI extends ILuaObject
|
||||
{
|
||||
/**
|
||||
* Get the globals this API will be assigned to. This will override any other global, so you should
|
||||
*
|
||||
* @return A list of globals this API will be assigned to.
|
||||
*/
|
||||
String[] getNames();
|
||||
|
||||
/**
|
||||
* Called when the computer is turned on.
|
||||
*
|
||||
* One should only interact with the file system.
|
||||
*/
|
||||
default void startup() { }
|
||||
|
||||
/**
|
||||
* Called every time the computer is ticked. This can be used to process various.
|
||||
*/
|
||||
default void update() { }
|
||||
|
||||
/**
|
||||
* Called when the computer is turned off or unloaded.
|
||||
*
|
||||
* This should reset the state of the object, disposing any remaining file handles, or other resources.
|
||||
*/
|
||||
default void shutdown() { }
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Construct an {@link ILuaAPI} for a specific computer.
|
||||
*
|
||||
* @see ILuaAPI
|
||||
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
||||
*/
|
||||
public interface ILuaAPIFactory
|
||||
{
|
||||
/**
|
||||
* Create a new API instance for a given computer.
|
||||
*
|
||||
* @param computer The computer this API is for.
|
||||
* @return The created API, or {@code null} if one should not be injected.
|
||||
*/
|
||||
@Nullable
|
||||
ILuaAPI create( @Nonnull IComputerSystem computer );
|
||||
}
|
@@ -41,7 +41,8 @@ public interface IPacketNetwork
|
||||
* to all receivers within range (or any interdimensional ones).
|
||||
*
|
||||
* @param packet The packet to send.
|
||||
* @see #transmitInterdimensional(Packet)
|
||||
* @param range The maximum distance this packet will be sent.
|
||||
* @see #transmitInterdimensional(Packet)
|
||||
* @see IPacketReceiver#receiveSameDimension(Packet, double)
|
||||
*/
|
||||
void transmitSameDimension( @Nonnull Packet packet, double range );
|
||||
@@ -51,7 +52,7 @@ public interface IPacketNetwork
|
||||
* to all receivers across all dimensions.
|
||||
*
|
||||
* @param packet The packet to send.
|
||||
* @see #transmitSameDimension(Packet, double)
|
||||
* @see #transmitSameDimension(Packet, double)
|
||||
* @see IPacketReceiver#receiveDifferentDimension(Packet)
|
||||
*/
|
||||
void transmitInterdimensional( @Nonnull Packet packet );
|
||||
|
@@ -33,7 +33,7 @@ public class GuiComputer extends GuiContainer
|
||||
private final int m_termHeight;
|
||||
private WidgetTerminal m_terminal;
|
||||
|
||||
protected GuiComputer( Container container, ComputerFamily family, IComputer computer, int termWidth, int termHeight )
|
||||
public GuiComputer( Container container, ComputerFamily family, IComputer computer, int termWidth, int termHeight )
|
||||
{
|
||||
super( container );
|
||||
m_family = family;
|
||||
|
@@ -8,13 +8,16 @@ package dan200.computercraft.client.proxy;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.*;
|
||||
import dan200.computercraft.client.render.ItemPocketRenderer;
|
||||
import dan200.computercraft.client.render.RenderOverlayCable;
|
||||
import dan200.computercraft.client.render.TileEntityCableRenderer;
|
||||
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
|
||||
import dan200.computercraft.shared.command.ContainerViewComputer;
|
||||
import dan200.computercraft.shared.computer.blocks.ComputerState;
|
||||
import dan200.computercraft.shared.computer.blocks.TileComputer;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
import dan200.computercraft.shared.computer.items.ItemComputer;
|
||||
import dan200.computercraft.shared.media.inventory.ContainerHeldItem;
|
||||
import dan200.computercraft.shared.media.items.ItemDiskLegacy;
|
||||
@@ -363,6 +366,13 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getComputerGUI( IComputer computer, int width, int height, ComputerFamily family )
|
||||
{
|
||||
ContainerViewComputer container = new ContainerViewComputer( computer );
|
||||
return new GuiComputer( container, family, computer, width, height );
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getWorldDir( World world )
|
||||
{
|
||||
@@ -454,6 +464,7 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
ForgeHandlers handlers = new ForgeHandlers();
|
||||
MinecraftForge.EVENT_BUS.register( handlers );
|
||||
MinecraftForge.EVENT_BUS.register( new RenderOverlayCable() );
|
||||
MinecraftForge.EVENT_BUS.register( new ItemPocketRenderer() );
|
||||
}
|
||||
|
||||
public class ForgeHandlers
|
||||
|
@@ -0,0 +1,264 @@
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputer;
|
||||
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
||||
import dan200.computercraft.shared.util.Palette;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.ItemRenderer;
|
||||
import net.minecraft.client.renderer.RenderItem;
|
||||
import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumHandSide;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraftforge.client.ForgeHooksClient;
|
||||
import net.minecraftforge.client.event.RenderSpecificHandEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
|
||||
|
||||
/**
|
||||
* Emulates map rendering for pocket computers
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ItemPocketRenderer
|
||||
{
|
||||
@SubscribeEvent
|
||||
public void renderItem( RenderSpecificHandEvent event )
|
||||
{
|
||||
ItemStack stack = event.getItemStack();
|
||||
if( !(stack.getItem() instanceof ItemPocketComputer) ) return;
|
||||
|
||||
event.setCanceled( true );
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
if( event.getHand() == EnumHand.MAIN_HAND && player.getHeldItemOffhand().isEmpty() )
|
||||
{
|
||||
renderItemFirstCentre(
|
||||
event.getInterpolatedPitch(),
|
||||
event.getEquipProgress(),
|
||||
event.getSwingProgress(),
|
||||
stack
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderItemFirstPersonSide(
|
||||
event.getHand() == EnumHand.MAIN_HAND ? player.getPrimaryHand() : player.getPrimaryHand().opposite(),
|
||||
event.getEquipProgress(),
|
||||
event.getSwingProgress(),
|
||||
stack
|
||||
);
|
||||
}
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* The main rendering method for pocket computers and their associated terminal
|
||||
*
|
||||
* @param stack The stack to render
|
||||
* @see ItemRenderer#renderMapFirstPerson(ItemStack)
|
||||
*/
|
||||
private void renderPocketComputerItem( ItemStack stack )
|
||||
{
|
||||
// Setup various transformations. Note that these are partially adapated from the corresponding method
|
||||
// in ItemRenderer
|
||||
GlStateManager.disableLighting();
|
||||
|
||||
GlStateManager.rotate( 180f, 0f, 1f, 0f );
|
||||
GlStateManager.rotate( 180f, 0f, 0f, 1f );
|
||||
GlStateManager.scale( 0.5, 0.5, 0.5 );
|
||||
|
||||
ItemPocketComputer pocketComputer = ComputerCraft.Items.pocketComputer;
|
||||
ClientComputer computer = pocketComputer.createClientComputer( stack );
|
||||
|
||||
{
|
||||
// First render the background item. We use the item's model rather than a direct texture as this ensures
|
||||
// we display the pocket light and other such decorations.
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.scale( 1.0f, -1.0f, 1.0f );
|
||||
|
||||
Minecraft minecraft = Minecraft.getMinecraft();
|
||||
TextureManager textureManager = minecraft.getTextureManager();
|
||||
RenderItem renderItem = minecraft.getRenderItem();
|
||||
|
||||
// Copy of RenderItem#renderItemModelIntoGUI but without the translation or scaling
|
||||
textureManager.bindTexture( TextureMap.LOCATION_BLOCKS_TEXTURE );
|
||||
textureManager.getTexture( TextureMap.LOCATION_BLOCKS_TEXTURE ).setBlurMipmap( false, false );
|
||||
|
||||
GlStateManager.enableRescaleNormal();
|
||||
GlStateManager.enableAlpha();
|
||||
GlStateManager.alphaFunc( GL11.GL_GREATER, 0.1F );
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA );
|
||||
GlStateManager.color( 1.0F, 1.0F, 1.0F, 1.0F );
|
||||
|
||||
IBakedModel bakedmodel = renderItem.getItemModelWithOverrides( stack, null, null );
|
||||
bakedmodel = ForgeHooksClient.handleCameraTransforms( bakedmodel, ItemCameraTransforms.TransformType.GUI, false );
|
||||
renderItem.renderItem( stack, bakedmodel );
|
||||
|
||||
GlStateManager.disableAlpha();
|
||||
GlStateManager.disableRescaleNormal();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
// If we've a computer and terminal then attempt to render it.
|
||||
if( computer != null )
|
||||
{
|
||||
Terminal terminal = computer.getTerminal();
|
||||
if( terminal != null )
|
||||
{
|
||||
synchronized( terminal )
|
||||
{
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.disableDepth();
|
||||
|
||||
// Reset the position to be at the top left corner of the pocket computer
|
||||
// Note we translate towards the screen slightly too.
|
||||
GlStateManager.translate( -8 / 16.0, -8 / 16.0, 0.5 / 16.0 );
|
||||
// Translate to the top left of the screen.
|
||||
GlStateManager.translate( 4 / 16.0, 3 / 16.0, 0 );
|
||||
|
||||
// Work out the scaling required to resize the terminal in order to fit on the computer
|
||||
final int margin = 2;
|
||||
int tw = terminal.getWidth();
|
||||
int th = terminal.getHeight();
|
||||
int width = tw * FONT_WIDTH + margin * 2;
|
||||
int height = th * FONT_HEIGHT + margin * 2;
|
||||
int max = Math.max( height, width );
|
||||
|
||||
// The grid is 8 * 8 wide, so we start with a base of 1/2 (8 / 16).
|
||||
double scale = 1.0 / 2.0 / max;
|
||||
GlStateManager.scale( scale, scale, scale );
|
||||
|
||||
// The margin/start positions are determined in order for the terminal to be centred.
|
||||
int startX = (max - width) / 2 + margin;
|
||||
int startY = (max - height) / 2 + margin;
|
||||
|
||||
FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer) ComputerCraft.getFixedWidthFontRenderer();
|
||||
boolean greyscale = !computer.isColour();
|
||||
Palette palette = terminal.getPalette();
|
||||
|
||||
// Render the actual text
|
||||
for( int line = 0; line < th; ++line )
|
||||
{
|
||||
TextBuffer text = terminal.getLine( line );
|
||||
TextBuffer colour = terminal.getTextColourLine( line );
|
||||
TextBuffer backgroundColour = terminal.getBackgroundColourLine( line );
|
||||
fontRenderer.drawString(
|
||||
text, startX, startY + line * FONT_HEIGHT,
|
||||
colour, backgroundColour, margin, margin, greyscale, palette
|
||||
);
|
||||
}
|
||||
|
||||
// And render the cursor;
|
||||
int tx = terminal.getCursorX(), ty = terminal.getCursorY();
|
||||
if( terminal.getCursorBlink() && ComputerCraft.getGlobalCursorBlink() &&
|
||||
tx >= 0 && ty >= 0 && tx < tw && ty < th )
|
||||
{
|
||||
TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 );
|
||||
fontRenderer.drawString(
|
||||
new TextBuffer( '_', 1 ), startX + FONT_WIDTH * tx, startY + FONT_HEIGHT * ty,
|
||||
cursorColour, null, 0, 0, greyscale, palette
|
||||
);
|
||||
}
|
||||
|
||||
GlStateManager.enableDepth();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a pocket computer to one side of the player.
|
||||
*
|
||||
* @param side The side to render on
|
||||
* @param equipProgress The equip progress of this item
|
||||
* @param swingProgress The swing progress of this item
|
||||
* @param stack The stack to render
|
||||
* @see ItemRenderer#renderMapFirstPersonSide(float, EnumHandSide, float, ItemStack)
|
||||
*/
|
||||
private void renderItemFirstPersonSide( EnumHandSide side, float equipProgress, float swingProgress, ItemStack stack )
|
||||
{
|
||||
Minecraft minecraft = Minecraft.getMinecraft();
|
||||
float offset = side == EnumHandSide.RIGHT ? 1f : -1f;
|
||||
GlStateManager.translate( offset * 0.125f, -0.125f, 0f );
|
||||
|
||||
// If the player is not invisible then render a single arm
|
||||
if( !minecraft.player.isInvisible() )
|
||||
{
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.rotate( offset * 10f, 0f, 0f, 1f );
|
||||
minecraft.getItemRenderer().renderArmFirstPerson( equipProgress, swingProgress, side );
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
// Setup the appropriate transformations. This is just copied from the
|
||||
// corresponding method in ItemRenderer.
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.translate( offset * 0.51f, -0.08f + equipProgress * -1.2f, -0.75f );
|
||||
float f1 = MathHelper.sqrt( swingProgress );
|
||||
float f2 = MathHelper.sin( f1 * (float) Math.PI );
|
||||
float f3 = -0.5f * f2;
|
||||
float f4 = 0.4f * MathHelper.sin( f1 * ((float) Math.PI * 2f) );
|
||||
float f5 = -0.3f * MathHelper.sin( swingProgress * (float) Math.PI );
|
||||
GlStateManager.translate( offset * f3, f4 - 0.3f * f2, f5 );
|
||||
GlStateManager.rotate( f2 * -45f, 1f, 0f, 0f );
|
||||
GlStateManager.rotate( offset * f2 * -30f, 0f, 1f, 0f );
|
||||
|
||||
renderPocketComputerItem( stack );
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an item in the middle of the screen
|
||||
*
|
||||
* @param pitch The pitch of the player
|
||||
* @param equipProgress The equip progress of this item
|
||||
* @param swingProgress The swing progress of this item
|
||||
* @param stack The stack to render
|
||||
* @see ItemRenderer#renderMapFirstPerson(float, float, float)
|
||||
*/
|
||||
private void renderItemFirstCentre( float pitch, float equipProgress, float swingProgress, ItemStack stack )
|
||||
{
|
||||
ItemRenderer itemRenderer = Minecraft.getMinecraft().getItemRenderer();
|
||||
|
||||
// Setup the appropriate transformations. This is just copied from the
|
||||
// corresponding method in ItemRenderer.
|
||||
float swingRt = MathHelper.sqrt( swingProgress );
|
||||
float tX = -0.2f * MathHelper.sin( swingProgress * (float) Math.PI );
|
||||
float tZ = -0.4f * MathHelper.sin( swingRt * (float) Math.PI );
|
||||
GlStateManager.translate( 0f, -tX / 2f, tZ );
|
||||
float pitchAngle = itemRenderer.getMapAngleFromPitch( pitch );
|
||||
GlStateManager.translate( 0f, 0.04f + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f );
|
||||
GlStateManager.rotate( pitchAngle * -85f, 1f, 0f, 0f );
|
||||
itemRenderer.renderArms();
|
||||
float rX = MathHelper.sin( swingRt * (float) Math.PI );
|
||||
GlStateManager.rotate( rX * 20f, 1f, 0f, 0f );
|
||||
GlStateManager.scale( 2f, 2f, 2f );
|
||||
|
||||
renderPocketComputerItem( stack );
|
||||
}
|
||||
}
|
@@ -0,0 +1,264 @@
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.client.model.pipeline.IVertexConsumer;
|
||||
import net.minecraftforge.client.model.pipeline.LightUtil;
|
||||
import net.minecraftforge.client.model.pipeline.VertexTransformer;
|
||||
import net.minecraftforge.common.model.TRSRTransformation;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.vecmath.Matrix4f;
|
||||
import javax.vecmath.Point3f;
|
||||
import javax.vecmath.Vector3f;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Transforms vertices of a model, remaining aware of winding order, and rearranging
|
||||
* vertices if needed.
|
||||
*/
|
||||
public final class ModelTransformer
|
||||
{
|
||||
private static final Matrix4f identity;
|
||||
|
||||
static
|
||||
{
|
||||
identity = new Matrix4f();
|
||||
identity.setIdentity();
|
||||
}
|
||||
|
||||
private ModelTransformer()
|
||||
{
|
||||
}
|
||||
|
||||
public static void transformQuadsTo( List<BakedQuad> output, List<BakedQuad> input, Matrix4f transform )
|
||||
{
|
||||
if( transform == null || transform.equals( identity ) )
|
||||
{
|
||||
output.addAll( input );
|
||||
}
|
||||
else
|
||||
{
|
||||
Matrix4f normalMatrix = new Matrix4f( transform );
|
||||
normalMatrix.invert();
|
||||
normalMatrix.transpose();
|
||||
|
||||
for( BakedQuad quad : input ) output.add( doTransformQuad( quad, transform, normalMatrix ) );
|
||||
}
|
||||
}
|
||||
|
||||
public static BakedQuad transformQuad( BakedQuad input, Matrix4f transform )
|
||||
{
|
||||
if( transform == null || transform.equals( identity ) ) return input;
|
||||
|
||||
Matrix4f normalMatrix = new Matrix4f( transform );
|
||||
normalMatrix.invert();
|
||||
normalMatrix.transpose();
|
||||
return doTransformQuad( input, transform, normalMatrix );
|
||||
}
|
||||
|
||||
private static BakedQuad doTransformQuad( BakedQuad input, Matrix4f positionMatrix, Matrix4f normalMatrix )
|
||||
{
|
||||
|
||||
BakedQuadBuilder builder = new BakedQuadBuilder( input.getFormat() );
|
||||
NormalAwareTransformer transformer = new NormalAwareTransformer( builder, positionMatrix, normalMatrix );
|
||||
input.pipe( transformer );
|
||||
|
||||
if( transformer.areNormalsInverted() )
|
||||
{
|
||||
builder.swap( 1, 3 );
|
||||
transformer.areNormalsInverted();
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* A vertex transformer that tracks whether the normals have been inverted and so the vertices
|
||||
* should be reordered so backface culling works as expected.
|
||||
*/
|
||||
private static class NormalAwareTransformer extends VertexTransformer
|
||||
{
|
||||
private final Matrix4f positionMatrix;
|
||||
private final Matrix4f normalMatrix;
|
||||
|
||||
private int vertexIndex = 0, elementIndex = 0;
|
||||
private final Point3f[] before = new Point3f[ 4 ];
|
||||
private final Point3f[] after = new Point3f[ 4 ];
|
||||
|
||||
public NormalAwareTransformer( IVertexConsumer parent, Matrix4f positionMatrix, Matrix4f normalMatrix )
|
||||
{
|
||||
super( parent );
|
||||
this.positionMatrix = positionMatrix;
|
||||
this.normalMatrix = normalMatrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQuadOrientation( EnumFacing orientation )
|
||||
{
|
||||
super.setQuadOrientation( orientation == null ? orientation : TRSRTransformation.rotate( positionMatrix, orientation ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put( int element, @Nonnull float... data )
|
||||
{
|
||||
switch( getVertexFormat().getElement( element ).getUsage() )
|
||||
{
|
||||
case POSITION:
|
||||
{
|
||||
Point3f vec = new Point3f( data );
|
||||
Point3f newVec = new Point3f();
|
||||
positionMatrix.transform( vec, newVec );
|
||||
|
||||
float[] newData = new float[ 4 ];
|
||||
newVec.get( newData );
|
||||
super.put( element, newData );
|
||||
|
||||
|
||||
before[ vertexIndex ] = vec;
|
||||
after[ vertexIndex ] = newVec;
|
||||
break;
|
||||
}
|
||||
case NORMAL:
|
||||
{
|
||||
Vector3f vec = new Vector3f( data );
|
||||
normalMatrix.transform( vec );
|
||||
|
||||
float[] newData = new float[ 4 ];
|
||||
vec.get( newData );
|
||||
super.put( element, newData );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
super.put( element, data );
|
||||
break;
|
||||
}
|
||||
|
||||
elementIndex++;
|
||||
if( elementIndex == getVertexFormat().getElementCount() )
|
||||
{
|
||||
vertexIndex++;
|
||||
elementIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean areNormalsInverted()
|
||||
{
|
||||
Vector3f temp1 = new Vector3f(), temp2 = new Vector3f();
|
||||
Vector3f crossBefore = new Vector3f(), crossAfter = new Vector3f();
|
||||
|
||||
// Determine what cross product we expect to have
|
||||
temp1.sub( before[ 1 ], before[ 0 ] );
|
||||
temp2.sub( before[ 1 ], before[ 2 ] );
|
||||
crossBefore.cross( temp1, temp2 );
|
||||
normalMatrix.transform( crossBefore );
|
||||
|
||||
// And determine what cross product we actually have
|
||||
temp1.sub( after[ 1 ], after[ 0 ] );
|
||||
temp2.sub( after[ 1 ], after[ 2 ] );
|
||||
crossAfter.cross( temp1, temp2 );
|
||||
|
||||
// If the angle between expected and actual cross product is greater than
|
||||
// pi/2 radians then we will need to reorder our quads.
|
||||
return Math.abs( crossBefore.angle( crossAfter ) ) >= Math.PI / 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A vertex consumer which is capable of building {@link BakedQuad}s.
|
||||
*
|
||||
* Equivalent to {@link net.minecraftforge.client.model.pipeline.UnpackedBakedQuad.Builder} but more memory
|
||||
* efficient.
|
||||
*
|
||||
* This also provides the ability to swap vertices through {@link #swap(int, int)} to allow reordering.
|
||||
*/
|
||||
private static class BakedQuadBuilder implements IVertexConsumer
|
||||
{
|
||||
private final VertexFormat format;
|
||||
|
||||
private final int[] vertexData;
|
||||
private int vertexIndex = 0, elementIndex = 0;
|
||||
|
||||
private EnumFacing orientation;
|
||||
private int quadTint;
|
||||
private boolean diffuse;
|
||||
private TextureAtlasSprite texture;
|
||||
|
||||
private BakedQuadBuilder( VertexFormat format )
|
||||
{
|
||||
this.format = format;
|
||||
this.vertexData = new int[ format.getNextOffset() ];
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public VertexFormat getVertexFormat()
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQuadTint( int tint )
|
||||
{
|
||||
this.quadTint = tint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQuadOrientation( @Nonnull EnumFacing orientation )
|
||||
{
|
||||
this.orientation = orientation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplyDiffuseLighting( boolean diffuse )
|
||||
{
|
||||
this.diffuse = diffuse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTexture( @Nonnull TextureAtlasSprite texture )
|
||||
{
|
||||
this.texture = texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put( int element, @Nonnull float... data )
|
||||
{
|
||||
LightUtil.pack( data, vertexData, format, vertexIndex, element );
|
||||
|
||||
elementIndex++;
|
||||
if( elementIndex == getVertexFormat().getElementCount() )
|
||||
{
|
||||
vertexIndex++;
|
||||
elementIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void swap( int a, int b )
|
||||
{
|
||||
int length = vertexData.length / 4;
|
||||
for( int i = 0; i < length; i++ )
|
||||
{
|
||||
int temp = vertexData[ a * length + i ];
|
||||
vertexData[ a * length + i ] = vertexData[ b * length + i ];
|
||||
vertexData[ b * length + i ] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
public BakedQuad build()
|
||||
{
|
||||
if( elementIndex != 0 || vertexIndex != 4 )
|
||||
{
|
||||
throw new IllegalStateException( "Got an unexpected number of elements/vertices" );
|
||||
}
|
||||
if( texture == null )
|
||||
{
|
||||
throw new IllegalStateException( "Texture has not been set" );
|
||||
}
|
||||
|
||||
return new BakedQuad( vertexData, quadTint, orientation, texture, diffuse, format );
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,7 +9,6 @@ package dan200.computercraft.client.render;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
|
||||
import dan200.computercraft.shared.turtle.entity.TurtleVisionCamera;
|
||||
import dan200.computercraft.shared.util.Holiday;
|
||||
@@ -121,18 +120,22 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt
|
||||
GlStateManager.translate( posX + offset.x, posY + offset.y, posZ + offset.z );
|
||||
|
||||
// Render the label
|
||||
IComputer computer = turtle.getComputer();
|
||||
String label = (computer != null) ? computer.getLabel() : null;
|
||||
String label = turtle.createProxy().getLabel();
|
||||
if( label != null )
|
||||
{
|
||||
renderLabel( turtle.getAccess().getPosition(), label );
|
||||
}
|
||||
|
||||
// Render the turtle
|
||||
GlStateManager.translate( 0.5f, 0.0f, 0.5f );
|
||||
GlStateManager.translate( 0.5f, 0.5f, 0.5f );
|
||||
GlStateManager.rotate( 180.0f - yaw, 0.0f, 1.0f, 0.0f );
|
||||
GlStateManager.translate( -0.5f, 0.0f, -0.5f );
|
||||
|
||||
if( label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )) )
|
||||
{
|
||||
// Flip the model and swap the cull face as winding order will have changed.
|
||||
GlStateManager.scale( 1.0f, -1.0f, 1.0f );
|
||||
GlStateManager.cullFace( GlStateManager.CullFace.FRONT );
|
||||
}
|
||||
GlStateManager.translate( -0.5f, -0.5f, -0.5f );
|
||||
// Render the turtle
|
||||
int colour;
|
||||
ComputerFamily family;
|
||||
@@ -172,6 +175,7 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt
|
||||
finally
|
||||
{
|
||||
GlStateManager.popMatrix();
|
||||
GlStateManager.cullFace( GlStateManager.CullFace.BACK );
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -6,13 +6,10 @@ import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrideList;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormatElement;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.vecmath.Matrix4f;
|
||||
import javax.vecmath.Point3f;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -20,16 +17,17 @@ import java.util.Map;
|
||||
|
||||
public class TurtleMultiModel implements IBakedModel
|
||||
{
|
||||
private IBakedModel m_baseModel;
|
||||
private IBakedModel m_overlayModel;
|
||||
private IBakedModel m_leftUpgradeModel;
|
||||
private Matrix4f m_leftUpgradeTransform;
|
||||
private IBakedModel m_rightUpgradeModel;
|
||||
private Matrix4f m_rightUpgradeTransform;
|
||||
private final IBakedModel m_baseModel;
|
||||
private final IBakedModel m_overlayModel;
|
||||
private final Matrix4f m_generalTransform;
|
||||
private final IBakedModel m_leftUpgradeModel;
|
||||
private final Matrix4f m_leftUpgradeTransform;
|
||||
private final IBakedModel m_rightUpgradeModel;
|
||||
private final Matrix4f m_rightUpgradeTransform;
|
||||
private List<BakedQuad> m_generalQuads;
|
||||
private Map<EnumFacing, List<BakedQuad>> m_faceQuads;
|
||||
|
||||
public TurtleMultiModel( IBakedModel baseModel, IBakedModel overlayModel, IBakedModel leftUpgradeModel, Matrix4f leftUpgradeTransform, IBakedModel rightUpgradeModel, Matrix4f rightUpgradeTransform )
|
||||
public TurtleMultiModel( IBakedModel baseModel, IBakedModel overlayModel, Matrix4f generalTransform, IBakedModel leftUpgradeModel, Matrix4f leftUpgradeTransform, IBakedModel rightUpgradeModel, Matrix4f rightUpgradeTransform )
|
||||
{
|
||||
// Get the models
|
||||
m_baseModel = baseModel;
|
||||
@@ -38,6 +36,7 @@ public class TurtleMultiModel implements IBakedModel
|
||||
m_leftUpgradeTransform = leftUpgradeTransform;
|
||||
m_rightUpgradeModel = rightUpgradeModel;
|
||||
m_rightUpgradeTransform = rightUpgradeTransform;
|
||||
m_generalTransform = generalTransform;
|
||||
m_generalQuads = null;
|
||||
m_faceQuads = new HashMap<>();
|
||||
}
|
||||
@@ -48,51 +47,52 @@ public class TurtleMultiModel implements IBakedModel
|
||||
{
|
||||
if( side != null )
|
||||
{
|
||||
if( !m_faceQuads.containsKey( side ) )
|
||||
{
|
||||
ArrayList<BakedQuad> quads = new ArrayList<>();
|
||||
if( m_overlayModel != null )
|
||||
{
|
||||
quads.addAll( m_overlayModel.getQuads( state, side, rand ) );
|
||||
}
|
||||
if( m_leftUpgradeModel != null )
|
||||
{
|
||||
quads.addAll( transformQuads( m_leftUpgradeModel.getQuads( state, side, rand ), m_leftUpgradeTransform ) );
|
||||
}
|
||||
if( m_rightUpgradeModel != null )
|
||||
{
|
||||
quads.addAll( transformQuads( m_rightUpgradeModel.getQuads( state, side, rand ), m_rightUpgradeTransform ) );
|
||||
}
|
||||
quads.trimToSize();
|
||||
m_faceQuads.put( side, quads );
|
||||
}
|
||||
if( !m_faceQuads.containsKey( side ) ) m_faceQuads.put( side, buildQuads( state, side, rand ) );
|
||||
return m_faceQuads.get( side );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_generalQuads == null )
|
||||
{
|
||||
ArrayList<BakedQuad> quads = new ArrayList<>();
|
||||
quads.addAll( m_baseModel.getQuads( state, side, rand ) );
|
||||
if( m_overlayModel != null )
|
||||
{
|
||||
quads.addAll( m_overlayModel.getQuads( state, side, rand ) );
|
||||
}
|
||||
if( m_leftUpgradeModel != null )
|
||||
{
|
||||
quads.addAll( transformQuads( m_leftUpgradeModel.getQuads( state, side, rand ), m_leftUpgradeTransform ) );
|
||||
}
|
||||
if( m_rightUpgradeModel != null )
|
||||
{
|
||||
quads.addAll( transformQuads( m_rightUpgradeModel.getQuads( state, side, rand ), m_rightUpgradeTransform ) );
|
||||
}
|
||||
quads.trimToSize();
|
||||
m_generalQuads = quads;
|
||||
}
|
||||
if( m_generalQuads == null ) m_generalQuads = buildQuads( state, side, rand );
|
||||
return m_generalQuads;
|
||||
}
|
||||
}
|
||||
|
||||
private List<BakedQuad> buildQuads( IBlockState state, EnumFacing side, long rand )
|
||||
{
|
||||
ArrayList<BakedQuad> quads = new ArrayList<>();
|
||||
ModelTransformer.transformQuadsTo( quads, m_baseModel.getQuads( state, side, rand ), m_generalTransform );
|
||||
if( m_overlayModel != null )
|
||||
{
|
||||
ModelTransformer.transformQuadsTo( quads, m_overlayModel.getQuads( state, side, rand ), m_generalTransform );
|
||||
}
|
||||
if( m_overlayModel != null )
|
||||
{
|
||||
ModelTransformer.transformQuadsTo( quads, m_overlayModel.getQuads( state, side, rand ), m_generalTransform );
|
||||
}
|
||||
if( m_leftUpgradeModel != null )
|
||||
{
|
||||
Matrix4f upgradeTransform = m_generalTransform;
|
||||
if( m_leftUpgradeTransform != null )
|
||||
{
|
||||
upgradeTransform = new Matrix4f( m_generalTransform );
|
||||
upgradeTransform.mul( m_leftUpgradeTransform );
|
||||
}
|
||||
ModelTransformer.transformQuadsTo( quads, m_leftUpgradeModel.getQuads( state, side, rand ), upgradeTransform );
|
||||
}
|
||||
if( m_rightUpgradeModel != null )
|
||||
{
|
||||
Matrix4f upgradeTransform = m_generalTransform;
|
||||
if( m_rightUpgradeTransform != null )
|
||||
{
|
||||
upgradeTransform = new Matrix4f( m_generalTransform );
|
||||
upgradeTransform.mul( m_rightUpgradeTransform );
|
||||
}
|
||||
ModelTransformer.transformQuadsTo( quads, m_rightUpgradeModel.getQuads( state, side, rand ), upgradeTransform );
|
||||
}
|
||||
quads.trimToSize();
|
||||
return quads;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAmbientOcclusion()
|
||||
{
|
||||
@@ -132,63 +132,4 @@ public class TurtleMultiModel implements IBakedModel
|
||||
{
|
||||
return ItemOverrideList.NONE;
|
||||
}
|
||||
|
||||
private List<BakedQuad> transformQuads( List<BakedQuad> input, Matrix4f transform )
|
||||
{
|
||||
if( transform == null || input.size() == 0 )
|
||||
{
|
||||
return input;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<BakedQuad> output = new ArrayList<>( input.size() );
|
||||
for( BakedQuad quad : input )
|
||||
{
|
||||
output.add( transformQuad( quad, transform ) );
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
private BakedQuad transformQuad( BakedQuad quad, Matrix4f transform )
|
||||
{
|
||||
int[] vertexData = quad.getVertexData().clone();
|
||||
int offset = 0;
|
||||
BakedQuad copy = new BakedQuad( vertexData, -1, quad.getFace(), quad.getSprite(), quad.shouldApplyDiffuseLighting(), quad.getFormat() );
|
||||
VertexFormat format = copy.getFormat();
|
||||
for( int i=0; i<format.getElementCount(); ++i ) // For each vertex element
|
||||
{
|
||||
VertexFormatElement element = format.getElement( i );
|
||||
if( element.isPositionElement() &&
|
||||
element.getType() == VertexFormatElement.EnumType.FLOAT &&
|
||||
element.getElementCount() == 3 ) // When we find a position element
|
||||
{
|
||||
for( int j=0; j<4; ++j ) // For each corner of the quad
|
||||
{
|
||||
int start = offset + j * format.getNextOffset();
|
||||
if( (start % 4) == 0 )
|
||||
{
|
||||
start = start / 4;
|
||||
|
||||
// Extract the position
|
||||
Point3f pos = new Point3f(
|
||||
Float.intBitsToFloat( vertexData[ start ] ),
|
||||
Float.intBitsToFloat( vertexData[ start + 1 ] ),
|
||||
Float.intBitsToFloat( vertexData[ start + 2 ] )
|
||||
);
|
||||
|
||||
// Transform the position
|
||||
transform.transform( pos );
|
||||
|
||||
// Insert the position
|
||||
vertexData[ start ] = Float.floatToRawIntBits( pos.x );
|
||||
vertexData[ start + 1 ] = Float.floatToRawIntBits( pos.y );
|
||||
vertexData[ start + 2 ] = Float.floatToRawIntBits( pos.z );
|
||||
}
|
||||
}
|
||||
}
|
||||
offset += element.getSize();
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
@@ -35,6 +35,19 @@ import java.util.List;
|
||||
|
||||
public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReloadListener
|
||||
{
|
||||
private static final Matrix4f s_identity, s_flip;
|
||||
|
||||
static
|
||||
{
|
||||
s_identity = new Matrix4f();
|
||||
s_identity.setIdentity();
|
||||
|
||||
s_flip = new Matrix4f();
|
||||
s_flip.setIdentity();
|
||||
s_flip.m11 = -1; // Flip on the y axis
|
||||
s_flip.m13 = 1; // Models go from (0,0,0) to (1,1,1), so push back up.
|
||||
}
|
||||
|
||||
private static class TurtleModelCombination
|
||||
{
|
||||
public final ComputerFamily m_family;
|
||||
@@ -43,8 +56,9 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
|
||||
public final ITurtleUpgrade m_rightUpgrade;
|
||||
public final ResourceLocation m_overlay;
|
||||
public final boolean m_christmas;
|
||||
public final boolean m_flip;
|
||||
|
||||
public TurtleModelCombination( ComputerFamily family, boolean colour, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, ResourceLocation overlay, boolean christmas )
|
||||
public TurtleModelCombination( ComputerFamily family, boolean colour, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, ResourceLocation overlay, boolean christmas, boolean flip )
|
||||
{
|
||||
m_family = family;
|
||||
m_colour = colour;
|
||||
@@ -52,22 +66,26 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
|
||||
m_rightUpgrade = rightUpgrade;
|
||||
m_overlay = overlay;
|
||||
m_christmas = christmas;
|
||||
m_flip = flip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals( Object other )
|
||||
{
|
||||
if( other == this ) {
|
||||
if( other == this )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if( other instanceof TurtleModelCombination ) {
|
||||
TurtleModelCombination otherCombo = (TurtleModelCombination)other;
|
||||
if( other instanceof TurtleModelCombination )
|
||||
{
|
||||
TurtleModelCombination otherCombo = (TurtleModelCombination) other;
|
||||
if( otherCombo.m_family == m_family &&
|
||||
otherCombo.m_colour == m_colour &&
|
||||
otherCombo.m_leftUpgrade == m_leftUpgrade &&
|
||||
otherCombo.m_rightUpgrade == m_rightUpgrade &&
|
||||
Objects.equal( otherCombo.m_overlay, m_overlay ) &&
|
||||
otherCombo.m_christmas == m_christmas )
|
||||
otherCombo.m_christmas == m_christmas &&
|
||||
otherCombo.m_flip == m_flip )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -86,10 +104,11 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
|
||||
result = prime * result + (m_rightUpgrade != null ? m_rightUpgrade.hashCode() : 0);
|
||||
result = prime * result + (m_overlay != null ? m_overlay.hashCode() : 0);
|
||||
result = prime * result + (m_christmas ? 1 : 0);
|
||||
result = prime * result + (m_flip ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private HashMap<TurtleModelCombination, IBakedModel> m_cachedModels;
|
||||
private ItemOverrideList m_overrides;
|
||||
private final TurtleModelCombination m_defaultCombination;
|
||||
@@ -97,12 +116,12 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
|
||||
public TurtleSmartItemModel()
|
||||
{
|
||||
m_cachedModels = new HashMap<>();
|
||||
m_defaultCombination = new TurtleModelCombination( ComputerFamily.Normal, false, null, null, null, false );
|
||||
m_defaultCombination = new TurtleModelCombination( ComputerFamily.Normal, false, null, null, null, false, false );
|
||||
m_overrides = new ItemOverrideList( new ArrayList<>() )
|
||||
{
|
||||
@Nonnull
|
||||
@Override
|
||||
public IBakedModel handleItemState( @Nonnull IBakedModel originalModel, @Nonnull ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity)
|
||||
public IBakedModel handleItemState( @Nonnull IBakedModel originalModel, @Nonnull ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity )
|
||||
{
|
||||
ItemTurtleBase turtle = (ItemTurtleBase) stack.getItem();
|
||||
ComputerFamily family = turtle.getFamily( stack );
|
||||
@@ -111,7 +130,9 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
|
||||
ITurtleUpgrade rightUpgrade = turtle.getUpgrade( stack, TurtleSide.Right );
|
||||
ResourceLocation overlay = turtle.getOverlay( stack );
|
||||
boolean christmas = HolidayUtil.getCurrentHoliday() == Holiday.Christmas;
|
||||
TurtleModelCombination combo = new TurtleModelCombination( family, colour != -1, leftUpgrade, rightUpgrade, overlay, christmas );
|
||||
String label = turtle.getLabel( stack );
|
||||
boolean flip = label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" ));
|
||||
TurtleModelCombination combo = new TurtleModelCombination( family, colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip );
|
||||
if( m_cachedModels.containsKey( combo ) )
|
||||
{
|
||||
return m_cachedModels.get( combo );
|
||||
@@ -147,27 +168,24 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
|
||||
ModelResourceLocation overlayModelLocation = TileEntityTurtleRenderer.getTurtleOverlayModel( combo.m_family, combo.m_overlay, combo.m_christmas );
|
||||
IBakedModel baseModel = modelManager.getModel( baseModelLocation );
|
||||
IBakedModel overlayModel = (overlayModelLocation != null) ? modelManager.getModel( baseModelLocation ) : null;
|
||||
Matrix4f transform = combo.m_flip ? s_flip : s_identity;
|
||||
Pair<IBakedModel, Matrix4f> leftModel = (combo.m_leftUpgrade != null) ? combo.m_leftUpgrade.getModel( null, TurtleSide.Left ) : null;
|
||||
Pair<IBakedModel, Matrix4f> rightModel = (combo.m_rightUpgrade != null) ? combo.m_rightUpgrade.getModel( null, TurtleSide.Right ) : null;
|
||||
if( leftModel != null && rightModel != null )
|
||||
{
|
||||
return new TurtleMultiModel( baseModel, overlayModel, leftModel.getLeft(), leftModel.getRight(), rightModel.getLeft(), rightModel.getRight() );
|
||||
return new TurtleMultiModel( baseModel, overlayModel, transform, leftModel.getLeft(), leftModel.getRight(), rightModel.getLeft(), rightModel.getRight() );
|
||||
}
|
||||
else if( leftModel != null )
|
||||
{
|
||||
return new TurtleMultiModel( baseModel, overlayModel, leftModel.getLeft(), leftModel.getRight(), null, null );
|
||||
return new TurtleMultiModel( baseModel, overlayModel, transform, leftModel.getLeft(), leftModel.getRight(), null, null );
|
||||
}
|
||||
else if( rightModel != null )
|
||||
{
|
||||
return new TurtleMultiModel( baseModel, overlayModel, null, null, rightModel.getLeft(), rightModel.getRight() );
|
||||
}
|
||||
else if( overlayModel != null )
|
||||
{
|
||||
return new TurtleMultiModel( baseModel, overlayModel, null, null, null, null );
|
||||
return new TurtleMultiModel( baseModel, overlayModel, transform, null, null, rightModel.getLeft(), rightModel.getRight() );
|
||||
}
|
||||
else
|
||||
{
|
||||
return baseModel;
|
||||
return new TurtleMultiModel( baseModel, overlayModel, transform, null, null, null, null );
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -6,6 +6,7 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ILuaObject;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
@@ -100,21 +101,6 @@ public class BufferAPI implements ILuaAPI
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startup()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
|
155
src/main/java/dan200/computercraft/core/apis/ComputerAccess.java
Normal file
155
src/main/java/dan200/computercraft/core/apis/ComputerAccess.java
Normal file
@@ -0,0 +1,155 @@
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class ComputerAccess implements IComputerAccess
|
||||
{
|
||||
private final IAPIEnvironment m_environment;
|
||||
private final Set<String> m_mounts = new HashSet<>();
|
||||
|
||||
protected ComputerAccess( IAPIEnvironment m_environment )
|
||||
{
|
||||
this.m_environment = m_environment;
|
||||
}
|
||||
|
||||
public void unmountAll()
|
||||
{
|
||||
FileSystem fileSystem = m_environment.getFileSystem();
|
||||
for( String m_mount : m_mounts )
|
||||
{
|
||||
fileSystem.unmount( m_mount );
|
||||
}
|
||||
m_mounts.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mount( @Nonnull String desiredLoc, @Nonnull IMount mount )
|
||||
{
|
||||
return mount( desiredLoc, mount, getAttachmentName() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String mount( @Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName )
|
||||
{
|
||||
// Mount the location
|
||||
String location;
|
||||
FileSystem fileSystem = m_environment.getFileSystem();
|
||||
if( fileSystem == null )
|
||||
{
|
||||
throw new IllegalStateException( "File system has not been created" );
|
||||
}
|
||||
|
||||
synchronized( fileSystem )
|
||||
{
|
||||
location = findFreeLocation( desiredLoc );
|
||||
if( location != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
fileSystem.mount( driveName, location, mount );
|
||||
}
|
||||
catch( FileSystemException ignored )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
if( location != null )
|
||||
{
|
||||
m_mounts.add( location );
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mountWritable( @Nonnull String desiredLoc, @Nonnull IWritableMount mount )
|
||||
{
|
||||
return mountWritable( desiredLoc, mount, getAttachmentName() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String mountWritable( @Nonnull String desiredLoc, @Nonnull IWritableMount mount, @Nonnull String driveName )
|
||||
{
|
||||
// Mount the location
|
||||
String location;
|
||||
FileSystem fileSystem = m_environment.getFileSystem();
|
||||
if( fileSystem == null )
|
||||
{
|
||||
throw new IllegalStateException( "File system has not been created" );
|
||||
}
|
||||
|
||||
synchronized( fileSystem )
|
||||
{
|
||||
location = findFreeLocation( desiredLoc );
|
||||
if( location != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
fileSystem.mountWritable( driveName, location, mount );
|
||||
}
|
||||
catch( FileSystemException ignored )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
if( location != null )
|
||||
{
|
||||
m_mounts.add( location );
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unmount( String location )
|
||||
{
|
||||
if( location != null )
|
||||
{
|
||||
if( !m_mounts.contains( location ) )
|
||||
{
|
||||
throw new RuntimeException( "You didn't mount this location" );
|
||||
}
|
||||
|
||||
m_environment.getFileSystem().unmount( location );
|
||||
m_mounts.remove( location );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getID()
|
||||
{
|
||||
return m_environment.getComputerID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueEvent( @Nonnull final String event, final Object[] arguments )
|
||||
{
|
||||
m_environment.queueEvent( event, arguments );
|
||||
}
|
||||
|
||||
private String findFreeLocation( String desiredLoc )
|
||||
{
|
||||
try
|
||||
{
|
||||
FileSystem fileSystem = m_environment.getFileSystem();
|
||||
if( !fileSystem.exists( desiredLoc ) )
|
||||
{
|
||||
return desiredLoc;
|
||||
}
|
||||
|
||||
// We used to check foo2,foo3,foo4,etc here
|
||||
// but the disk drive does this itself now
|
||||
return null;
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,6 +6,7 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
|
||||
@@ -48,11 +49,6 @@ public class FSAPI implements ILuaAPI
|
||||
m_fileSystem = m_env.getFileSystem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown( )
|
||||
{
|
||||
|
@@ -7,6 +7,7 @@
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
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.core.apis.http.*;
|
||||
@@ -43,12 +44,7 @@ public class HTTPAPI implements ILuaAPI
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startup( )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
public void update()
|
||||
{
|
||||
// Wait for all of our http requests
|
||||
synchronized( m_httpTasks )
|
||||
|
@@ -1,17 +1,16 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
import dan200.computercraft.api.lua.ILuaObject;
|
||||
|
||||
public interface ILuaAPI extends ILuaObject
|
||||
/**
|
||||
* This exists purely to ensure binary compatibility.
|
||||
*
|
||||
* @see dan200.computercraft.api.lua.ILuaAPI
|
||||
*/
|
||||
public interface ILuaAPI extends dan200.computercraft.api.lua.ILuaAPI
|
||||
{
|
||||
String[] getNames();
|
||||
void advance( double v );
|
||||
|
||||
void startup(); // LT
|
||||
void advance( double _dt ); // MT
|
||||
void shutdown(); // LT
|
||||
default void update()
|
||||
{
|
||||
advance( 0.05 );
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.shared.util.StringUtil;
|
||||
@@ -102,7 +103,7 @@ public class OSAPI implements ILuaAPI
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double dt )
|
||||
public void update()
|
||||
{
|
||||
synchronized( m_timers )
|
||||
{
|
||||
|
@@ -8,24 +8,25 @@ package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.ComputerThread;
|
||||
import dan200.computercraft.core.computer.ITask;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
|
||||
|
||||
public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChangeListener
|
||||
{
|
||||
private class PeripheralWrapper implements IComputerAccess
|
||||
private class PeripheralWrapper extends ComputerAccess
|
||||
{
|
||||
private final String m_side;
|
||||
private final IPeripheral m_peripheral;
|
||||
@@ -35,10 +36,9 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
private Map<String, Integer> m_methodMap;
|
||||
private boolean m_attached;
|
||||
|
||||
private Set<String> m_mounts;
|
||||
|
||||
public PeripheralWrapper( IPeripheral peripheral, String side )
|
||||
{
|
||||
super(m_environment);
|
||||
m_side = side;
|
||||
m_peripheral = peripheral;
|
||||
m_attached = false;
|
||||
@@ -54,8 +54,6 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
m_methodMap.put( m_methods[i], i );
|
||||
}
|
||||
}
|
||||
|
||||
m_mounts = new HashSet<>();
|
||||
}
|
||||
|
||||
public IPeripheral getPeripheral()
|
||||
@@ -91,14 +89,11 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
|
||||
synchronized( this )
|
||||
{
|
||||
m_attached = false;
|
||||
// Unmount everything the detach function forgot to do
|
||||
for( String m_mount : m_mounts )
|
||||
{
|
||||
m_fileSystem.unmount( m_mount );
|
||||
}
|
||||
m_mounts.clear();
|
||||
unmountAll();
|
||||
}
|
||||
|
||||
m_attached = false;
|
||||
}
|
||||
|
||||
public Object[] call( ILuaContext context, String methodName, Object[] arguments ) throws LuaException, InterruptedException
|
||||
@@ -122,13 +117,6 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
|
||||
// IComputerAccess implementation
|
||||
|
||||
@Override
|
||||
public String mount( @Nonnull String desiredLoc, @Nonnull IMount mount )
|
||||
{
|
||||
return mount( desiredLoc, mount, m_side );
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String mount( @Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName )
|
||||
{
|
||||
@@ -137,31 +125,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
|
||||
// Mount the location
|
||||
String location;
|
||||
synchronized( m_fileSystem )
|
||||
{
|
||||
location = findFreeLocation( desiredLoc );
|
||||
if( location != null )
|
||||
{
|
||||
try {
|
||||
m_fileSystem.mount( driveName, location, mount );
|
||||
} catch( FileSystemException e ) {
|
||||
// fail and return null
|
||||
}
|
||||
}
|
||||
}
|
||||
if( location != null )
|
||||
{
|
||||
m_mounts.add( location );
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mountWritable( @Nonnull String desiredLoc, @Nonnull IWritableMount mount )
|
||||
{
|
||||
return mountWritable( desiredLoc, mount, m_side );
|
||||
return super.mount( desiredLoc, mount, driveName );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -172,68 +136,46 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
|
||||
// Mount the location
|
||||
String location;
|
||||
synchronized( m_fileSystem )
|
||||
{
|
||||
location = findFreeLocation( desiredLoc );
|
||||
if( location != null )
|
||||
{
|
||||
try {
|
||||
m_fileSystem.mountWritable( driveName, location, mount );
|
||||
} catch( FileSystemException e ) {
|
||||
// fail and return null
|
||||
}
|
||||
}
|
||||
}
|
||||
if( location != null )
|
||||
{
|
||||
m_mounts.add( location );
|
||||
}
|
||||
return location;
|
||||
return super.mountWritable( desiredLoc, mount, driveName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void unmount( String location )
|
||||
{
|
||||
if( !m_attached ) {
|
||||
if( !m_attached )
|
||||
{
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
|
||||
if( location != null )
|
||||
{
|
||||
if( !m_mounts.contains( location ) ) {
|
||||
throw new RuntimeException( "You didn't mount this location" );
|
||||
}
|
||||
|
||||
m_fileSystem.unmount( location );
|
||||
m_mounts.remove( location );
|
||||
}
|
||||
super.unmount( location );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getID()
|
||||
{
|
||||
if( !m_attached ) {
|
||||
if( !m_attached )
|
||||
{
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
return m_environment.getComputerID();
|
||||
return super.getID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueEvent( @Nonnull final String event, final Object[] arguments )
|
||||
{
|
||||
if( !m_attached ) {
|
||||
if( !m_attached )
|
||||
{
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
m_environment.queueEvent( event, arguments );
|
||||
super.queueEvent( event, arguments );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getAttachmentName()
|
||||
{
|
||||
if( !m_attached ) {
|
||||
if( !m_attached )
|
||||
{
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
return m_side;
|
||||
@@ -356,11 +298,6 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown( )
|
||||
{
|
||||
@@ -504,25 +441,4 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private String findFreeLocation( String desiredLoc )
|
||||
{
|
||||
try
|
||||
{
|
||||
synchronized( m_fileSystem )
|
||||
{
|
||||
if( !m_fileSystem.exists( desiredLoc ) )
|
||||
{
|
||||
return desiredLoc;
|
||||
}
|
||||
// We used to check foo2,foo3,foo4,etc here
|
||||
// but the disk drive does this itself now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
@@ -33,21 +34,6 @@ public class RedstoneAPI implements ILuaAPI
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startup( )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown( )
|
||||
{
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
|
@@ -6,6 +6,7 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.computer.IComputerEnvironment;
|
||||
@@ -36,21 +37,6 @@ public class TermAPI implements ILuaAPI
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startup( )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown( )
|
||||
{
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
|
@@ -8,16 +8,21 @@ package dan200.computercraft.core.computer;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.filesystem.IFileSystem;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.apis.*;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
import dan200.computercraft.core.lua.CobaltLuaMachine;
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
@@ -173,7 +178,91 @@ public class Computer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ComputerSystem extends ComputerAccess implements IComputerSystem
|
||||
{
|
||||
private final IAPIEnvironment m_environment;
|
||||
|
||||
private ComputerSystem( IAPIEnvironment m_environment )
|
||||
{
|
||||
super( m_environment );
|
||||
this.m_environment = m_environment;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getAttachmentName()
|
||||
{
|
||||
return "computer";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IFileSystem getFileSystem()
|
||||
{
|
||||
FileSystem fs = m_environment.getFileSystem();
|
||||
return fs == null ? null : fs.getMountWrapper();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getLabel()
|
||||
{
|
||||
return m_environment.getLabel();
|
||||
}
|
||||
}
|
||||
|
||||
private static class APIWrapper implements ILuaAPI
|
||||
{
|
||||
private final ILuaAPI delegate;
|
||||
private final ComputerSystem system;
|
||||
|
||||
private APIWrapper( ILuaAPI delegate, ComputerSystem system )
|
||||
{
|
||||
this.delegate = delegate;
|
||||
this.system = system;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNames()
|
||||
{
|
||||
return delegate.getNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startup()
|
||||
{
|
||||
delegate.startup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update()
|
||||
{
|
||||
delegate.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown()
|
||||
{
|
||||
delegate.shutdown();
|
||||
system.unmountAll();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
return delegate.getMethodNames();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
return delegate.callMethod( context, method, arguments );
|
||||
}
|
||||
}
|
||||
|
||||
private static IMount s_romMount = null;
|
||||
|
||||
private int m_id;
|
||||
@@ -371,7 +460,7 @@ public class Computer
|
||||
{
|
||||
for(ILuaAPI api : m_apis)
|
||||
{
|
||||
api.advance( _dt );
|
||||
api.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -420,14 +509,13 @@ public class Computer
|
||||
}
|
||||
}
|
||||
|
||||
public boolean pollChanged()
|
||||
public boolean pollAndResetChanged()
|
||||
{
|
||||
return m_externalOutputChanged;
|
||||
}
|
||||
|
||||
public void clearChanged()
|
||||
{
|
||||
m_externalOutputChanged = false;
|
||||
synchronized(this) {
|
||||
boolean changed = m_externalOutputChanged;
|
||||
m_externalOutputChanged = false;
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBlinking()
|
||||
@@ -578,6 +666,11 @@ public class Computer
|
||||
{
|
||||
m_apis.add( api );
|
||||
}
|
||||
|
||||
public void addAPI( dan200.computercraft.core.apis.ILuaAPI api )
|
||||
{
|
||||
addAPI( (ILuaAPI) api );
|
||||
}
|
||||
|
||||
public void setPeripheral( int side, IPeripheral peripheral )
|
||||
{
|
||||
@@ -616,6 +709,16 @@ public class Computer
|
||||
{
|
||||
m_apis.add( new HTTPAPI( m_apiEnvironment ) );
|
||||
}
|
||||
|
||||
for( ILuaAPIFactory factory : ComputerCraft.getAPIFactories() )
|
||||
{
|
||||
ComputerSystem system = new ComputerSystem( m_apiEnvironment );
|
||||
ILuaAPI api = factory.create( system );
|
||||
if( api != null )
|
||||
{
|
||||
m_apis.add( api );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initLua()
|
||||
@@ -687,6 +790,7 @@ public class Computer
|
||||
return;
|
||||
}
|
||||
m_state = State.Starting;
|
||||
m_externalOutputChanged = true;
|
||||
m_ticksSinceStart = 0;
|
||||
}
|
||||
|
||||
@@ -746,6 +850,7 @@ public class Computer
|
||||
|
||||
// Start a new state
|
||||
m_state = State.Running;
|
||||
m_externalOutputChanged = true;
|
||||
synchronized( m_machine )
|
||||
{
|
||||
m_machine.handleEvent( null, null );
|
||||
@@ -764,6 +869,7 @@ public class Computer
|
||||
return;
|
||||
}
|
||||
m_state = State.Stopping;
|
||||
m_externalOutputChanged = true;
|
||||
}
|
||||
|
||||
// Turn the computercraft off
|
||||
@@ -788,7 +894,7 @@ public class Computer
|
||||
// Shutdown our APIs
|
||||
synchronized( m_apis )
|
||||
{
|
||||
for(ILuaAPI api : m_apis)
|
||||
for( ILuaAPI api : m_apis )
|
||||
{
|
||||
api.shutdown();
|
||||
}
|
||||
@@ -827,6 +933,7 @@ public class Computer
|
||||
}
|
||||
|
||||
m_state = State.Off;
|
||||
m_externalOutputChanged = true;
|
||||
if( reboot )
|
||||
{
|
||||
m_startRequested = true;
|
||||
|
@@ -7,6 +7,7 @@
|
||||
package dan200.computercraft.core.filesystem;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.filesystem.IFileSystem;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
|
||||
@@ -290,6 +291,7 @@ public class FileSystem
|
||||
}
|
||||
}
|
||||
|
||||
private final FileSystemMount m_wrapper = new FileSystemMount( this );
|
||||
private final Map<String, MountWrapper> m_mounts = new HashMap<>();
|
||||
private final Set<Closeable> m_openFiles = Collections.newSetFromMap( new WeakHashMap<Closeable, Boolean>() );
|
||||
|
||||
@@ -734,6 +736,11 @@ public class FileSystem
|
||||
return match;
|
||||
}
|
||||
|
||||
public IFileSystem getMountWrapper()
|
||||
{
|
||||
return m_wrapper;
|
||||
}
|
||||
|
||||
private static String sanitizePath( String path )
|
||||
{
|
||||
return sanitizePath( path, false );
|
||||
|
@@ -0,0 +1,185 @@
|
||||
package dan200.computercraft.core.filesystem;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IFileSystem;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class FileSystemMount implements IFileSystem
|
||||
{
|
||||
private final FileSystem m_filesystem;
|
||||
|
||||
public FileSystemMount( FileSystem m_filesystem )
|
||||
{
|
||||
this.m_filesystem = m_filesystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeDirectory( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
m_filesystem.makeDir( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
m_filesystem.delete( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public OutputStream openForWrite( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.openForWrite( path, false );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public OutputStream openForAppend( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.openForWrite( path, true );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRemainingSpace() throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.getFreeSpace( "/" );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.exists( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.exists( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
Collections.addAll( contents, m_filesystem.list( path ) );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.getSize( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InputStream openForRead( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.openForRead( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String combine( String path, String child )
|
||||
{
|
||||
return m_filesystem.combine( path, child );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy( String from, String to ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
m_filesystem.copy( from, to );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move( String from, String to ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
m_filesystem.move( from, to );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,7 +11,7 @@ import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ILuaObject;
|
||||
import dan200.computercraft.api.lua.ILuaTask;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.apis.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.ITask;
|
||||
import dan200.computercraft.core.computer.MainThread;
|
||||
|
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.lua;
|
||||
import dan200.computercraft.core.apis.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
@@ -8,6 +8,8 @@ package dan200.computercraft.server.proxy;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.computer.blocks.TileComputer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
import dan200.computercraft.shared.network.ComputerCraftPacket;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
|
||||
import dan200.computercraft.shared.peripheral.printer.TilePrinter;
|
||||
@@ -97,6 +99,12 @@ public class ComputerCraftProxyServer extends ComputerCraftProxyCommon
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getComputerGUI( IComputer computer, int width, int height, ComputerFamily family )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getWorldDir( World world )
|
||||
{
|
||||
|
@@ -0,0 +1,256 @@
|
||||
package dan200.computercraft.shared.command;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.shared.command.framework.*;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import net.minecraft.command.CommandException;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static dan200.computercraft.shared.command.framework.ChatHelpers.*;
|
||||
|
||||
public final class CommandComputerCraft extends CommandDelegate
|
||||
{
|
||||
public CommandComputerCraft()
|
||||
{
|
||||
super( create() );
|
||||
}
|
||||
|
||||
private static ISubCommand create()
|
||||
{
|
||||
CommandRoot root = new CommandRoot(
|
||||
"computercraft", "Various commands for controlling computers.",
|
||||
"The /computercraft command provides various debugging and administrator tools for controlling and " +
|
||||
"interacting with computers."
|
||||
);
|
||||
|
||||
root.register( new SubCommandBase(
|
||||
"dump", "[id]", "Display the status of computers.", UserLevel.OWNER_OP,
|
||||
"Display the status of all computers or specific information about one computer. You can either specify the computer's instance " +
|
||||
"id (e.g. 123) or computer id (e.g #123)."
|
||||
)
|
||||
{
|
||||
@Override
|
||||
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
|
||||
{
|
||||
if( arguments.size() == 0 )
|
||||
{
|
||||
TextTable table = new TextTable( "Instance", "Id", "On", "Position" );
|
||||
|
||||
int max = 50;
|
||||
for( ServerComputer computer : ComputerCraft.serverComputerRegistry.getComputers() )
|
||||
{
|
||||
table.addRow(
|
||||
linkComputer( computer ),
|
||||
text( Integer.toString( computer.getID() ) ),
|
||||
bool( computer.isOn() ),
|
||||
linkPosition( context, computer )
|
||||
);
|
||||
|
||||
if( max-- < 0 ) break;
|
||||
}
|
||||
|
||||
table.displayTo( context.getSender() );
|
||||
}
|
||||
else if( arguments.size() == 1 )
|
||||
{
|
||||
ServerComputer computer = ComputerSelector.getComputer( arguments.get( 0 ) );
|
||||
|
||||
TextTable table = new TextTable();
|
||||
table.addRow( header( "Instance" ), text( Integer.toString( computer.getInstanceID() ) ) );
|
||||
table.addRow( header( "Id" ), text( Integer.toString( computer.getID() ) ) );
|
||||
table.addRow( header( "Label" ), text( computer.getLabel() ) );
|
||||
table.addRow( header( "On" ), bool( computer.isOn() ) );
|
||||
table.addRow( header( "Position" ), linkPosition( context, computer ) );
|
||||
table.addRow( header( "Family" ), text( computer.getFamily().toString() ) );
|
||||
|
||||
for( int i = 0; i < 6; i++ )
|
||||
{
|
||||
IPeripheral peripheral = computer.getPeripheral( i );
|
||||
if( peripheral != null )
|
||||
{
|
||||
table.addRow( header( "Peripheral " + Computer.s_sideNames[ i ] ), text( peripheral.getType() ) );
|
||||
}
|
||||
}
|
||||
|
||||
table.displayTo( context.getSender() );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new CommandException( context.getFullUsage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments )
|
||||
{
|
||||
return arguments.size() == 1
|
||||
? ComputerSelector.completeComputer( arguments.get( 0 ) )
|
||||
: Collections.<String>emptyList();
|
||||
}
|
||||
} );
|
||||
|
||||
root.register( new SubCommandBase(
|
||||
"shutdown", "[ids...]", "Shutdown computers remotely.", UserLevel.OWNER_OP,
|
||||
"Shutdown the listed computers or all if none are specified. You can either specify the computer's instance " +
|
||||
"id (e.g. 123) or computer id (e.g #123)."
|
||||
)
|
||||
{
|
||||
@Override
|
||||
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
|
||||
{
|
||||
List<ServerComputer> computers = Lists.newArrayList();
|
||||
if( arguments.size() > 0 )
|
||||
{
|
||||
for( String arg : arguments )
|
||||
{
|
||||
computers.add( ComputerSelector.getComputer( arg ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
computers.addAll( ComputerCraft.serverComputerRegistry.getComputers() );
|
||||
}
|
||||
|
||||
int shutdown = 0;
|
||||
for( ServerComputer computer : computers )
|
||||
{
|
||||
if( computer.isOn() ) shutdown++;
|
||||
computer.unload();
|
||||
}
|
||||
context.getSender().sendMessage( text( "Shutdown " + shutdown + " / " + computers.size() + " computers" ) );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments )
|
||||
{
|
||||
return arguments.size() == 0
|
||||
? Collections.<String>emptyList()
|
||||
: ComputerSelector.completeComputer( arguments.get( arguments.size() - 1 ) );
|
||||
}
|
||||
} );
|
||||
|
||||
root.register( new SubCommandBase(
|
||||
"tp", "<id>", "Teleport to a specific computer.", UserLevel.OP,
|
||||
"Teleport to the location of a computer. You can either specify the computer's instance " +
|
||||
"id (e.g. 123) or computer id (e.g #123)."
|
||||
)
|
||||
{
|
||||
@Override
|
||||
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
|
||||
{
|
||||
if( arguments.size() != 1 ) throw new CommandException( context.getFullUsage() );
|
||||
|
||||
ServerComputer computer = ComputerSelector.getComputer( arguments.get( 0 ) );
|
||||
World world = computer.getWorld();
|
||||
BlockPos pos = computer.getPosition();
|
||||
|
||||
if( world == null || pos == null ) throw new CommandException( "Cannot locate computer in world" );
|
||||
|
||||
ICommandSender sender = context.getSender();
|
||||
if( !(sender instanceof Entity) ) throw new CommandException( "Sender is not an entity" );
|
||||
|
||||
if( sender instanceof EntityPlayerMP )
|
||||
{
|
||||
EntityPlayerMP entity = (EntityPlayerMP) sender;
|
||||
if( entity.getEntityWorld() != world )
|
||||
{
|
||||
context.getServer().getPlayerList().changePlayerDimension( entity, world.provider.getDimension() );
|
||||
}
|
||||
|
||||
entity.setPositionAndUpdate( pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5 );
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity entity = (Entity) sender;
|
||||
if( entity.getEntityWorld() != world )
|
||||
{
|
||||
entity.changeDimension( world.provider.getDimension() );
|
||||
}
|
||||
|
||||
entity.setLocationAndAngles(
|
||||
pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5,
|
||||
entity.rotationYaw, entity.rotationPitch
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments )
|
||||
{
|
||||
return arguments.size() == 1
|
||||
? ComputerSelector.completeComputer( arguments.get( 0 ) )
|
||||
: Collections.<String>emptyList();
|
||||
}
|
||||
} );
|
||||
|
||||
root.register(new SubCommandBase(
|
||||
"view", "<id>", "View the terminal of a computer.", UserLevel.OP,
|
||||
"Open the terminal of a computer, allowing remote control of a computer. This does not provide access to " +
|
||||
"turtle's inventories. You can either specify the computer's instance id (e.g. 123) or computer id (e.g #123)."
|
||||
) {
|
||||
@Override
|
||||
public void execute(@Nonnull CommandContext context, @Nonnull List<String> arguments) throws CommandException {
|
||||
if (arguments.size() != 1) throw new CommandException(context.getFullUsage());
|
||||
|
||||
ICommandSender sender = context.getSender();
|
||||
if (!(sender instanceof EntityPlayerMP)) {
|
||||
throw new CommandException("Cannot open terminal for non-player");
|
||||
}
|
||||
|
||||
ServerComputer computer = ComputerSelector.getComputer(arguments.get(0));
|
||||
ComputerCraft.openComputerGUI( (EntityPlayerMP) sender, computer );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> getCompletion(@Nonnull CommandContext context, @Nonnull List<String> arguments) {
|
||||
return arguments.size() == 1
|
||||
? ComputerSelector.completeComputer( arguments.get( 0 ) )
|
||||
: Collections.emptyList();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private static ITextComponent linkComputer( ServerComputer computer )
|
||||
{
|
||||
return link(
|
||||
text( Integer.toString( computer.getInstanceID() ) ),
|
||||
"/computercraft dump " + computer.getInstanceID(),
|
||||
"View more info about this computer"
|
||||
);
|
||||
}
|
||||
|
||||
private static ITextComponent linkPosition( CommandContext context, ServerComputer computer )
|
||||
{
|
||||
if( UserLevel.OP.canExecute( context ) )
|
||||
{
|
||||
return link(
|
||||
position( computer.getPosition() ),
|
||||
"/computercraft tp " + computer.getInstanceID(),
|
||||
"Teleport to this computer"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
return position( computer.getPosition() );
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,126 @@
|
||||
package dan200.computercraft.shared.command;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import net.minecraft.command.CommandException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public final class ComputerSelector
|
||||
{
|
||||
public static ServerComputer getComputer( String selector ) throws CommandException
|
||||
{
|
||||
if( selector.length() > 0 && selector.charAt( 0 ) == '#' )
|
||||
{
|
||||
selector = selector.substring( 1 );
|
||||
|
||||
int id;
|
||||
try
|
||||
{
|
||||
id = Integer.parseInt( selector );
|
||||
}
|
||||
catch( NumberFormatException e )
|
||||
{
|
||||
throw new CommandException( "'" + selector + "' is not a valid number" );
|
||||
}
|
||||
|
||||
// We copy it to prevent concurrent modifications.
|
||||
List<ServerComputer> computers = Lists.newArrayList( ComputerCraft.serverComputerRegistry.getComputers() );
|
||||
List<ServerComputer> candidates = Lists.newArrayList();
|
||||
for( ServerComputer searchComputer : computers )
|
||||
{
|
||||
if( searchComputer.getID() == id )
|
||||
{
|
||||
candidates.add( searchComputer );
|
||||
}
|
||||
}
|
||||
|
||||
if( candidates.size() == 0 )
|
||||
{
|
||||
throw new CommandException( "No such computer for id " + id );
|
||||
}
|
||||
else if( candidates.size() == 1 )
|
||||
{
|
||||
return candidates.get( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder builder = new StringBuilder( "Multiple computers with id " )
|
||||
.append( id ).append( " (instances " );
|
||||
|
||||
boolean first = true;
|
||||
for( ServerComputer computer : candidates )
|
||||
{
|
||||
if( first )
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.append( ", " );
|
||||
}
|
||||
|
||||
builder.append( computer.getInstanceID() );
|
||||
}
|
||||
|
||||
builder.append( ")" );
|
||||
|
||||
throw new CommandException( builder.toString() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int instance;
|
||||
try
|
||||
{
|
||||
instance = Integer.parseInt( selector );
|
||||
}
|
||||
catch( NumberFormatException e )
|
||||
{
|
||||
throw new CommandException( "'" + selector + "' is not a valid number" );
|
||||
}
|
||||
|
||||
ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instance );
|
||||
if( computer == null )
|
||||
{
|
||||
throw new CommandException( "No such computer for instance id " + instance );
|
||||
}
|
||||
else
|
||||
{
|
||||
return computer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> completeComputer( String selector )
|
||||
{
|
||||
Set<String> options = Sets.newHashSet();
|
||||
|
||||
// We copy it to prevent concurrent modifications.
|
||||
List<ServerComputer> computers = Lists.newArrayList( ComputerCraft.serverComputerRegistry.getComputers() );
|
||||
|
||||
if( selector.length() > 0 && selector.charAt( 0 ) == '#' )
|
||||
{
|
||||
selector = selector.substring( 1 );
|
||||
|
||||
for( ServerComputer computer : computers )
|
||||
{
|
||||
String id = Integer.toString( computer.getID() );
|
||||
if( id.startsWith( selector ) ) options.add( "#" + id );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( ServerComputer computer : computers )
|
||||
{
|
||||
String id = Integer.toString( computer.getInstanceID() );
|
||||
if( id.startsWith( selector ) ) options.add( id );
|
||||
}
|
||||
}
|
||||
|
||||
return Lists.newArrayList( options );
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
package dan200.computercraft.shared.command;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
import dan200.computercraft.shared.computer.core.IContainerComputer;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.Container;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ContainerViewComputer extends Container implements IContainerComputer
|
||||
{
|
||||
private final IComputer computer;
|
||||
|
||||
public ContainerViewComputer( IComputer computer )
|
||||
{
|
||||
this.computer = computer;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IComputer getComputer()
|
||||
{
|
||||
return computer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInteractWith( @Nonnull EntityPlayer player )
|
||||
{
|
||||
if( computer instanceof ServerComputer )
|
||||
{
|
||||
ServerComputer serverComputer = (ServerComputer) computer;
|
||||
|
||||
// If this computer no longer exists then discard it.
|
||||
if( ComputerCraft.serverComputerRegistry.get( serverComputer.getInstanceID() ) != serverComputer )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we're a command computer then ensure we're in creative
|
||||
if( serverComputer.getFamily() == ComputerFamily.Command )
|
||||
{
|
||||
MinecraftServer server = player.getServer();
|
||||
if( server == null || !server.isCommandBlockEnabled() )
|
||||
{
|
||||
player.sendMessage( new TextComponentTranslation( "advMode.notEnabled" ) );
|
||||
return false;
|
||||
}
|
||||
else if( !ComputerCraft.canPlayerUseCommands( player ) || !player.capabilities.isCreativeMode )
|
||||
{
|
||||
player.sendMessage( new TextComponentTranslation( "advMode.notAllowed" ) );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -0,0 +1,117 @@
|
||||
package dan200.computercraft.shared.command.framework;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.Style;
|
||||
import net.minecraft.util.text.TextComponentString;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraft.util.text.event.ClickEvent;
|
||||
import net.minecraft.util.text.event.HoverEvent;
|
||||
|
||||
/**
|
||||
* Various helpers for building chat messages
|
||||
*/
|
||||
public final class ChatHelpers
|
||||
{
|
||||
private static final TextFormatting HEADER = TextFormatting.LIGHT_PURPLE;
|
||||
private static final TextFormatting SYNOPSIS = TextFormatting.AQUA;
|
||||
private static final TextFormatting NAME = TextFormatting.GREEN;
|
||||
|
||||
public static ITextComponent coloured( String text, TextFormatting colour )
|
||||
{
|
||||
ITextComponent component = new TextComponentString( text == null ? "" : text );
|
||||
component.getStyle().setColor( colour );
|
||||
return component;
|
||||
}
|
||||
|
||||
public static ITextComponent text( String text )
|
||||
{
|
||||
return new TextComponentString( text == null ? "" : text );
|
||||
}
|
||||
|
||||
public static ITextComponent list( ITextComponent... children )
|
||||
{
|
||||
ITextComponent component = new TextComponentString( "" );
|
||||
for( ITextComponent child : children )
|
||||
{
|
||||
component.appendSibling( child );
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
public static ITextComponent getHelp( CommandContext context, ISubCommand command, String prefix )
|
||||
{
|
||||
ITextComponent output = new TextComponentString( "" )
|
||||
.appendSibling( coloured( "/" + prefix + " " + command.getUsage( context ), HEADER ) )
|
||||
.appendText( " " )
|
||||
.appendSibling( coloured( command.getSynopsis(), SYNOPSIS ) );
|
||||
|
||||
String desc = command.getDescription();
|
||||
if( !Strings.isNullOrEmpty( desc ) ) output.appendText( "\n" + desc );
|
||||
|
||||
if( command instanceof CommandRoot )
|
||||
{
|
||||
for( ISubCommand subCommand : ((CommandRoot) command).getSubCommands().values() )
|
||||
{
|
||||
if( !subCommand.checkPermission( context ) ) continue;
|
||||
|
||||
output.appendText( "\n" );
|
||||
|
||||
ITextComponent component = coloured( subCommand.getName(), NAME );
|
||||
component.getStyle().setClickEvent( new ClickEvent(
|
||||
ClickEvent.Action.SUGGEST_COMMAND,
|
||||
"/" + prefix + " " + subCommand.getName()
|
||||
) );
|
||||
output.appendSibling( component );
|
||||
|
||||
output.appendText( " - " + subCommand.getSynopsis() );
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static ITextComponent position( BlockPos pos )
|
||||
{
|
||||
if( pos == null ) return text( "<no pos>" );
|
||||
return formatted( "%d, %d, %d", pos.getX(), pos.getY(), pos.getZ() );
|
||||
}
|
||||
|
||||
public static ITextComponent bool( boolean value )
|
||||
{
|
||||
if( value )
|
||||
{
|
||||
ITextComponent component = new TextComponentString( "Y" );
|
||||
component.getStyle().setColor( TextFormatting.GREEN );
|
||||
return component;
|
||||
}
|
||||
else
|
||||
{
|
||||
ITextComponent component = new TextComponentString( "N" );
|
||||
component.getStyle().setColor( TextFormatting.RED );
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
||||
public static ITextComponent formatted( String format, Object... args )
|
||||
{
|
||||
return new TextComponentString( String.format( format, args ) );
|
||||
}
|
||||
|
||||
public static ITextComponent link( ITextComponent component, String command, String toolTip )
|
||||
{
|
||||
Style style = component.getStyle();
|
||||
|
||||
if( style.getColor() == null ) style.setColor( TextFormatting.YELLOW );
|
||||
style.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, command ) );
|
||||
style.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new TextComponentString( toolTip ) ) );
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
public static ITextComponent header( String text )
|
||||
{
|
||||
return coloured( text, HEADER );
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
package dan200.computercraft.shared.command.framework;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents the way a command was invoked, including the command sender, the current server and
|
||||
* the "path" to this command.
|
||||
*/
|
||||
public final class CommandContext
|
||||
{
|
||||
private final MinecraftServer server;
|
||||
private final ICommandSender sender;
|
||||
private final List<ISubCommand> path;
|
||||
|
||||
public CommandContext( MinecraftServer server, ICommandSender sender, ISubCommand initial )
|
||||
{
|
||||
this.server = server;
|
||||
this.sender = sender;
|
||||
this.path = Collections.singletonList( initial );
|
||||
}
|
||||
|
||||
private CommandContext( MinecraftServer server, ICommandSender sender, List<ISubCommand> path )
|
||||
{
|
||||
this.server = server;
|
||||
this.sender = sender;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public CommandContext enter( ISubCommand child )
|
||||
{
|
||||
List<ISubCommand> newPath = Lists.newArrayListWithExpectedSize( path.size() + 1 );
|
||||
newPath.addAll( path );
|
||||
newPath.add( child );
|
||||
return new CommandContext( server, sender, newPath );
|
||||
}
|
||||
|
||||
public CommandContext parent()
|
||||
{
|
||||
if( path.size() == 1 ) throw new IllegalStateException( "No parent command" );
|
||||
return new CommandContext( server, sender, path.subList( 0, path.size() - 1 ) );
|
||||
}
|
||||
|
||||
public String getFullPath()
|
||||
{
|
||||
StringBuilder out = new StringBuilder();
|
||||
boolean first = true;
|
||||
for( ISubCommand command : path )
|
||||
{
|
||||
if( first )
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
out.append( ' ' );
|
||||
}
|
||||
|
||||
out.append( command.getName() );
|
||||
}
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
public String getFullUsage()
|
||||
{
|
||||
return "/" + getFullPath() + " " + path.get( path.size() - 1 ).getUsage( this );
|
||||
}
|
||||
|
||||
public List<ISubCommand> getPath()
|
||||
{
|
||||
return Collections.unmodifiableList( path );
|
||||
}
|
||||
|
||||
public String getRootCommand()
|
||||
{
|
||||
return path.get( 0 ).getName();
|
||||
}
|
||||
|
||||
public MinecraftServer getServer()
|
||||
{
|
||||
return server;
|
||||
}
|
||||
|
||||
public ICommandSender getSender()
|
||||
{
|
||||
return sender;
|
||||
}
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
package dan200.computercraft.shared.command.framework;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import net.minecraft.command.CommandException;
|
||||
import net.minecraft.command.ICommand;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link net.minecraft.command.ICommand} which delegates to a {@link ISubCommand}.
|
||||
*/
|
||||
public class CommandDelegate implements ICommand
|
||||
{
|
||||
private final ISubCommand command;
|
||||
|
||||
public CommandDelegate( ISubCommand command )
|
||||
{
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return command.getName();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUsage( @Nonnull ICommandSender sender )
|
||||
{
|
||||
return "/" + command.getName() + " " + command.getUsage( new CommandContext( sender.getServer(), sender, command ) );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> getAliases()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute( @Nonnull MinecraftServer server, @Nonnull ICommandSender sender, @Nonnull String[] args ) throws CommandException
|
||||
{
|
||||
try
|
||||
{
|
||||
command.execute( new CommandContext( server, sender, command ), Arrays.asList( args ) );
|
||||
}
|
||||
catch( CommandException e )
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
catch( Throwable e )
|
||||
{
|
||||
ComputerCraft.log.error( "Unhandled exception in command", e );
|
||||
throw new CommandException( "Unhandled exception: " + e.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> getTabCompletions( @Nonnull MinecraftServer server, @Nonnull ICommandSender sender, @Nonnull String[] args, @Nullable BlockPos pos )
|
||||
{
|
||||
return command.getCompletion( new CommandContext( server, sender, command ), Arrays.asList( args ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPermission( @Nonnull MinecraftServer server, @Nonnull ICommandSender sender )
|
||||
{
|
||||
return command.checkPermission( new CommandContext( server, sender, command ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsernameIndex( @Nonnull String[] args, int index )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo( @Nonnull ICommand o )
|
||||
{
|
||||
return getName().compareTo( o.getName() );
|
||||
}
|
||||
}
|
@@ -0,0 +1,149 @@
|
||||
package dan200.computercraft.shared.command.framework;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import net.minecraft.command.CommandBase;
|
||||
import net.minecraft.command.CommandException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A command which delegates to a series of sub commands
|
||||
*/
|
||||
public class CommandRoot implements ISubCommand
|
||||
{
|
||||
private final String name;
|
||||
private final String synopsis;
|
||||
private final String description;
|
||||
private final Map<String, ISubCommand> subCommands = Maps.newHashMap();
|
||||
|
||||
public CommandRoot( String name, String synopsis, String description )
|
||||
{
|
||||
this.name = name;
|
||||
this.synopsis = synopsis;
|
||||
this.description = description;
|
||||
|
||||
register( new SubCommandHelp( this ) );
|
||||
}
|
||||
|
||||
public void register( ISubCommand command )
|
||||
{
|
||||
subCommands.put( command.getName(), command );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUsage( CommandContext context )
|
||||
{
|
||||
StringBuilder out = new StringBuilder( "<" );
|
||||
boolean first = true;
|
||||
for( ISubCommand command : subCommands.values() )
|
||||
{
|
||||
if( command.checkPermission( context ) )
|
||||
{
|
||||
if( first )
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
out.append( "|" );
|
||||
}
|
||||
|
||||
out.append( command.getName() );
|
||||
}
|
||||
}
|
||||
|
||||
return out.append( ">" ).toString();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getSynopsis()
|
||||
{
|
||||
return synopsis;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getDescription()
|
||||
{
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPermission( @Nonnull CommandContext context )
|
||||
{
|
||||
for( ISubCommand command : subCommands.values() )
|
||||
{
|
||||
if( command.checkPermission( context ) ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Map<String, ISubCommand> getSubCommands()
|
||||
{
|
||||
return Collections.unmodifiableMap( subCommands );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
|
||||
{
|
||||
if( arguments.size() == 0 )
|
||||
{
|
||||
context.getSender().sendMessage( ChatHelpers.getHelp( context, this, context.getFullPath() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
ISubCommand command = subCommands.get( arguments.get( 0 ) );
|
||||
if( command == null || !command.checkPermission( context ) )
|
||||
{
|
||||
throw new CommandException( getName() + " " + getUsage( context ) );
|
||||
}
|
||||
|
||||
command.execute( context.enter( command ), arguments.subList( 1, arguments.size() ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments )
|
||||
{
|
||||
if( arguments.size() == 0 )
|
||||
{
|
||||
return Lists.newArrayList( subCommands.keySet() );
|
||||
}
|
||||
else if( arguments.size() == 1 )
|
||||
{
|
||||
List<String> list = Lists.newArrayList();
|
||||
String match = arguments.get( 0 );
|
||||
|
||||
for( ISubCommand command : subCommands.values() )
|
||||
{
|
||||
if( CommandBase.doesStringStartWith( match, command.getName() ) && command.checkPermission( context ) )
|
||||
{
|
||||
list.add( command.getName() );
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
else
|
||||
{
|
||||
ISubCommand command = subCommands.get( arguments.get( 0 ) );
|
||||
if( command == null || !command.checkPermission( context ) ) return Collections.emptyList();
|
||||
|
||||
return command.getCompletion( context, arguments.subList( 1, arguments.size() ) );
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
package dan200.computercraft.shared.command.framework;
|
||||
|
||||
import net.minecraft.command.CommandException;
|
||||
import net.minecraft.command.ICommand;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A slightly different implementation of {@link ICommand} which is delegated to.
|
||||
*/
|
||||
public interface ISubCommand
|
||||
{
|
||||
/**
|
||||
* Get the name of this command
|
||||
*
|
||||
* @return The name of this command
|
||||
* @see ICommand#getName()
|
||||
*/
|
||||
@Nonnull
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Get the usage of this command
|
||||
*
|
||||
* @param context The context this command is executed in
|
||||
* @return The usage of this command
|
||||
* @see ICommand#getUsage(ICommandSender)
|
||||
*/
|
||||
@Nonnull
|
||||
String getUsage( CommandContext context );
|
||||
|
||||
/**
|
||||
* Get a short description of this command, including its usage.
|
||||
*
|
||||
* @return The command's synopsis
|
||||
*/
|
||||
@Nonnull
|
||||
String getSynopsis();
|
||||
|
||||
/**
|
||||
* Get the lengthy description of this command. This synopsis is prepended to this.
|
||||
*
|
||||
* @return The command's description
|
||||
*/
|
||||
@Nonnull
|
||||
String getDescription();
|
||||
|
||||
/**
|
||||
* Determine whether a given command sender has permission to execute this command.
|
||||
*
|
||||
* @param context The current command context.
|
||||
* @return Whether this command can be executed.
|
||||
* @see ICommand#checkPermission(MinecraftServer, ICommandSender)
|
||||
*/
|
||||
boolean checkPermission( @Nonnull CommandContext context );
|
||||
|
||||
/**
|
||||
* Execute this command
|
||||
*
|
||||
* @param context The current command context.
|
||||
* @param arguments The arguments passed @throws CommandException When an error occurs
|
||||
* @see ICommand#execute(MinecraftServer, ICommandSender, String[])
|
||||
*/
|
||||
void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException;
|
||||
|
||||
/**
|
||||
* Get a list of possible completions
|
||||
*
|
||||
* @param context The current command context.
|
||||
* @param arguments The arguments passed. You should complete the last one.
|
||||
* @return List of possible completions
|
||||
* @see ICommand#getTabCompletions(MinecraftServer, ICommandSender, String[], BlockPos)
|
||||
*/
|
||||
@Nonnull
|
||||
List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments );
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
package dan200.computercraft.shared.command.framework;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class SubCommandBase implements ISubCommand
|
||||
{
|
||||
private final String name;
|
||||
private final String usage;
|
||||
private final String synopsis;
|
||||
private final String description;
|
||||
private final UserLevel level;
|
||||
|
||||
public SubCommandBase( String name, String usage, String synopsis, UserLevel level, String description )
|
||||
{
|
||||
this.name = name;
|
||||
this.usage = usage;
|
||||
this.synopsis = synopsis;
|
||||
this.description = description;
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public SubCommandBase( String name, String synopsis, UserLevel level, String description )
|
||||
{
|
||||
this( name, "", synopsis, level, description );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUsage( CommandContext context )
|
||||
{
|
||||
return usage;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getSynopsis()
|
||||
{
|
||||
return synopsis;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getDescription()
|
||||
{
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPermission( @Nonnull CommandContext context )
|
||||
{
|
||||
return level.canExecute( context );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments )
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@@ -0,0 +1,127 @@
|
||||
package dan200.computercraft.shared.command.framework;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import joptsimple.internal.Strings;
|
||||
import net.minecraft.command.CommandBase;
|
||||
import net.minecraft.command.CommandException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class SubCommandHelp implements ISubCommand
|
||||
{
|
||||
private final CommandRoot branchCommand;
|
||||
|
||||
public SubCommandHelp( CommandRoot branchCommand )
|
||||
{
|
||||
this.branchCommand = branchCommand;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "help";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUsage( CommandContext context )
|
||||
{
|
||||
return "[command]";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getSynopsis()
|
||||
{
|
||||
return "Provide help for a specific command";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getDescription()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPermission( @Nonnull CommandContext context )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
|
||||
{
|
||||
ISubCommand command = branchCommand;
|
||||
|
||||
for( int i = 0; i < arguments.size(); i++ )
|
||||
{
|
||||
String commandName = arguments.get( i );
|
||||
if( command instanceof CommandRoot )
|
||||
{
|
||||
command = ((CommandRoot) command).getSubCommands().get( commandName );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new CommandException( Strings.join( arguments.subList( 0, i ), " " ) + " has no sub-commands" );
|
||||
}
|
||||
|
||||
if( command == null )
|
||||
{
|
||||
throw new CommandException( "No such command " + Strings.join( arguments.subList( 0, i + 1 ), " " ) );
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder prefix = new StringBuilder( context.parent().getFullPath() );
|
||||
for( String argument : arguments )
|
||||
{
|
||||
prefix.append( ' ' ).append( argument );
|
||||
}
|
||||
context.getSender().sendMessage( ChatHelpers.getHelp( context, command, prefix.toString() ) );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments )
|
||||
{
|
||||
CommandRoot command = branchCommand;
|
||||
|
||||
for( int i = 0; i < arguments.size() - 1; i++ )
|
||||
{
|
||||
String commandName = arguments.get( i );
|
||||
ISubCommand subCommand = command.getSubCommands().get( commandName );
|
||||
|
||||
if( subCommand instanceof CommandRoot )
|
||||
{
|
||||
command = (CommandRoot) subCommand;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
if( arguments.size() == 0 )
|
||||
{
|
||||
return Lists.newArrayList( command.getSubCommands().keySet() );
|
||||
}
|
||||
else
|
||||
{
|
||||
List<String> list = Lists.newArrayList();
|
||||
String match = arguments.get( arguments.size() - 1 );
|
||||
|
||||
for( String entry : command.getSubCommands().keySet() )
|
||||
{
|
||||
if( CommandBase.doesStringStartWith( match, entry ) )
|
||||
{
|
||||
list.add( entry );
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,250 @@
|
||||
package dan200.computercraft.shared.command.framework;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TextComponentString;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
import static dan200.computercraft.shared.command.framework.ChatHelpers.coloured;
|
||||
import static dan200.computercraft.shared.command.framework.ChatHelpers.text;
|
||||
|
||||
public class TextTable
|
||||
{
|
||||
private static final String CHARACTERS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
|
||||
private static final int[] CHAR_WIDTHS = new int[] {
|
||||
6, 6, 6, 6, 6, 6, 4, 6, 6, 6, 6, 6, 6, 6, 6, 4,
|
||||
4, 6, 7, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 2, 5, 6, 6, 6, 6, 3, 5, 5, 5, 6, 2, 6, 2, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 5, 6, 5, 6,
|
||||
7, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 4, 6, 6,
|
||||
3, 6, 6, 6, 6, 6, 5, 6, 6, 2, 6, 5, 3, 6, 6, 6,
|
||||
6, 6, 6, 6, 4, 6, 6, 6, 6, 6, 6, 5, 2, 5, 7, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 3, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6,
|
||||
6, 3, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 2, 6, 6,
|
||||
8, 9, 9, 6, 6, 6, 8, 8, 6, 8, 8, 8, 8, 8, 6, 6,
|
||||
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||
9, 9, 9, 9, 9, 9, 9, 9, 9, 6, 9, 9, 9, 5, 9, 9,
|
||||
8, 7, 7, 8, 7, 8, 8, 8, 7, 8, 8, 7, 9, 9, 6, 7,
|
||||
7, 7, 7, 7, 9, 6, 7, 8, 7, 6, 6, 9, 7, 6, 7, 1
|
||||
};
|
||||
|
||||
private static final ITextComponent SEPARATOR = coloured( " | ", TextFormatting.GRAY );
|
||||
private static final ITextComponent LINE = text( "\n" );
|
||||
|
||||
private static int getWidth( char character, ICommandSender sender )
|
||||
{
|
||||
if( sender instanceof EntityPlayerMP && !(sender instanceof FakePlayer) )
|
||||
{
|
||||
// Use font widths here.
|
||||
if( character == 167 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if( character == 32 )
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
else if( CHARACTERS.indexOf( character ) != -1 )
|
||||
{
|
||||
return CHAR_WIDTHS[ character ];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Eh, close enough.
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getWidth( ITextComponent text, ICommandSender sender )
|
||||
{
|
||||
int sum = 0;
|
||||
String chars = text.getUnformattedText();
|
||||
for( int i = 0; i < chars.length(); i++ )
|
||||
{
|
||||
sum += getWidth( chars.charAt( i ), sender );
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
private static boolean isPlayer( ICommandSender sender )
|
||||
{
|
||||
return sender instanceof EntityPlayerMP && !(sender instanceof FakePlayer);
|
||||
}
|
||||
|
||||
private static int getMaxWidth( ICommandSender sender )
|
||||
{
|
||||
return isPlayer( sender ) ? 320 : 80;
|
||||
}
|
||||
|
||||
private int columns = -1;
|
||||
private final ITextComponent[] header;
|
||||
private final List<ITextComponent[]> rows = Lists.newArrayList();
|
||||
|
||||
public TextTable( @Nonnull ITextComponent... header )
|
||||
{
|
||||
this.header = header;
|
||||
this.columns = header.length;
|
||||
}
|
||||
|
||||
public TextTable()
|
||||
{
|
||||
header = null;
|
||||
}
|
||||
|
||||
public TextTable( @Nonnull String... header )
|
||||
{
|
||||
this.header = new ITextComponent[ header.length ];
|
||||
for( int i = 0; i < header.length; i++ )
|
||||
{
|
||||
this.header[ i ] = ChatHelpers.header( header[ i ] );
|
||||
}
|
||||
this.columns = header.length;
|
||||
}
|
||||
|
||||
public void addRow( @Nonnull ITextComponent... row )
|
||||
{
|
||||
if( columns == -1 )
|
||||
{
|
||||
columns = row.length;
|
||||
}
|
||||
else if( row.length != columns )
|
||||
{
|
||||
throw new IllegalArgumentException( "Row is the incorrect length" );
|
||||
}
|
||||
|
||||
rows.add( row );
|
||||
}
|
||||
|
||||
public void displayTo( ICommandSender sender )
|
||||
{
|
||||
if( columns <= 0 ) return;
|
||||
|
||||
final int maxWidth = getMaxWidth( sender );
|
||||
|
||||
int[] minWidths = new int[ columns ];
|
||||
int[] maxWidths = new int[ columns ];
|
||||
int[] rowWidths = new int[ columns ];
|
||||
|
||||
if( header != null )
|
||||
{
|
||||
for( int i = 0; i < columns; i++ )
|
||||
{
|
||||
maxWidths[ i ] = minWidths[ i ] = getWidth( header[ i ], sender );
|
||||
}
|
||||
}
|
||||
|
||||
for( ITextComponent[] row : rows )
|
||||
{
|
||||
for( int i = 0; i < row.length; i++ )
|
||||
{
|
||||
int width = getWidth( row[ i ], sender );
|
||||
rowWidths[ i ] += width;
|
||||
if( width > maxWidths[ i ] )
|
||||
{
|
||||
maxWidths[ i ] = width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the average width
|
||||
for( int i = 0; i < columns; i++ )
|
||||
{
|
||||
rowWidths[ i ] = Math.max( rowWidths[ i ], rows.size() );
|
||||
}
|
||||
|
||||
int totalWidth = (columns - 1) * getWidth( SEPARATOR, sender );
|
||||
for( int x : maxWidths ) totalWidth += x;
|
||||
|
||||
// TODO: Limit the widths of some entries if totalWidth > maxWidth
|
||||
|
||||
ITextComponent out = new TextComponentString( "" );
|
||||
|
||||
if( header != null )
|
||||
{
|
||||
for( int i = 0; i < columns; i++ )
|
||||
{
|
||||
if( i != 0 ) out.appendSibling( SEPARATOR );
|
||||
appendFixed( out, sender, header[ i ], maxWidths[ i ] );
|
||||
}
|
||||
out.appendSibling( LINE );
|
||||
|
||||
// Round the width up rather than down
|
||||
int rowCharWidth = getWidth( '=', sender );
|
||||
int rowWidth = totalWidth / rowCharWidth + (totalWidth % rowCharWidth == 0 ? 0 : 1);
|
||||
out.appendSibling( coloured( StringUtils.repeat( '=', rowWidth ), TextFormatting.GRAY ) );
|
||||
out.appendSibling( LINE );
|
||||
}
|
||||
|
||||
for( int i = 0; i < rows.size(); i++ )
|
||||
{
|
||||
ITextComponent[] row = rows.get( i );
|
||||
if( i != 0 ) out.appendSibling( LINE );
|
||||
for( int j = 0; j < columns; j++ )
|
||||
{
|
||||
if( j != 0 ) out.appendSibling( SEPARATOR );
|
||||
appendFixed( out, sender, row[ j ], maxWidths[ j ] );
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendMessage( out );
|
||||
}
|
||||
|
||||
private static void appendFixed( ITextComponent out, ICommandSender sender, ITextComponent entry, int maxWidth )
|
||||
{
|
||||
int length = getWidth( entry, sender );
|
||||
int delta = length - maxWidth;
|
||||
if( delta < 0 )
|
||||
{
|
||||
// Convert to overflow;
|
||||
delta = -delta;
|
||||
|
||||
// We have to remove some padding as there is a padding added between formatted and unformatted text
|
||||
if( !entry.getStyle().isEmpty() && isPlayer( sender ) ) delta -= 1;
|
||||
|
||||
out.appendSibling( entry );
|
||||
|
||||
int spaceWidth = getWidth( ' ', sender );
|
||||
|
||||
int spaces = delta / spaceWidth;
|
||||
int missing = delta % spaceWidth;
|
||||
spaces -= missing;
|
||||
|
||||
ITextComponent component = new TextComponentString( StringUtils.repeat( ' ', spaces < 0 ? 0 : spaces ) );
|
||||
if( missing > 0 )
|
||||
{
|
||||
ITextComponent bold = new TextComponentString( StringUtils.repeat( ' ', missing ) );
|
||||
bold.getStyle().setBold( true );
|
||||
component.appendSibling( bold );
|
||||
}
|
||||
|
||||
out.appendSibling( component );
|
||||
}
|
||||
else if( delta > 0 )
|
||||
{
|
||||
out.appendSibling( entry );
|
||||
}
|
||||
else
|
||||
{
|
||||
out.appendSibling( entry );
|
||||
|
||||
// We have to add some padding as we expect a padding between formatted and unformatted text
|
||||
// and there won't be.
|
||||
if( entry.getStyle().isEmpty() && isPlayer( sender ) ) out.appendText( " " );
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
package dan200.computercraft.shared.command.framework;
|
||||
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
/**
|
||||
* The level a user must be at in order to execute a command.
|
||||
*/
|
||||
public enum UserLevel
|
||||
{
|
||||
/**
|
||||
* Only can be used by the owner of the server: namely the server console or the player in SSP.
|
||||
*/
|
||||
OWNER,
|
||||
|
||||
/**
|
||||
* Can only be used by ops.
|
||||
*/
|
||||
OP,
|
||||
|
||||
/**
|
||||
* Can be used by any op, or the player in SSP.
|
||||
*/
|
||||
OWNER_OP,
|
||||
|
||||
/**
|
||||
* Can be used by anyone.
|
||||
*/
|
||||
ANYONE;
|
||||
|
||||
public int toLevel()
|
||||
{
|
||||
switch( this )
|
||||
{
|
||||
case OWNER:
|
||||
return 4;
|
||||
case OP:
|
||||
case OWNER_OP:
|
||||
return 2;
|
||||
case ANYONE:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canExecute( CommandContext context )
|
||||
{
|
||||
if( this == ANYONE ) return true;
|
||||
|
||||
// We *always* allow level 0 stuff, even if the
|
||||
MinecraftServer server = context.getServer();
|
||||
ICommandSender sender = context.getSender();
|
||||
|
||||
if( server.isSinglePlayer() && sender instanceof EntityPlayerMP &&
|
||||
((EntityPlayerMP) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerOwner() ) )
|
||||
{
|
||||
if( this == OWNER || this == OWNER_OP ) return true;
|
||||
}
|
||||
|
||||
return sender.canUseCommand( toLevel(), context.getRootCommand() );
|
||||
}
|
||||
}
|
@@ -241,10 +241,9 @@ public abstract class BlockGeneric extends Block implements
|
||||
if( collision.size() > 0 )
|
||||
{
|
||||
AxisAlignedBB aabb = collision.get( 0 );
|
||||
for (int i=1; i<collision.size(); ++i )
|
||||
for( int i = 1; i < collision.size(); i++ )
|
||||
{
|
||||
AxisAlignedBB other = collision.get( 1 );
|
||||
aabb = aabb.union( other );
|
||||
aabb = aabb.union( collision.get( i ) );
|
||||
}
|
||||
return aabb;
|
||||
}
|
||||
|
@@ -8,9 +8,9 @@ package dan200.computercraft.shared.computer.apis;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
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.core.apis.ILuaAPI;
|
||||
import dan200.computercraft.shared.computer.blocks.TileCommandComputer;
|
||||
import dan200.computercraft.shared.util.WorldUtil;
|
||||
import net.minecraft.block.Block;
|
||||
@@ -49,21 +49,6 @@ public class CommandAPI implements ILuaAPI
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startup()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double dt )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
|
@@ -1,9 +1,9 @@
|
||||
package dan200.computercraft.shared.computer.blocks;
|
||||
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
|
||||
/**
|
||||
* A proxy object for computer objects, delegating to {@link ServerComputer} or {@link TileComputer} where appropriate.
|
||||
* A proxy object for computer objects, delegating to {@link IComputer} or {@link TileComputer} where appropriate.
|
||||
*/
|
||||
public abstract class ComputerProxy
|
||||
{
|
||||
@@ -12,7 +12,7 @@ public abstract class ComputerProxy
|
||||
public void turnOn()
|
||||
{
|
||||
TileComputerBase tile = getTile();
|
||||
ServerComputer computer = tile.getServerComputer();
|
||||
IComputer computer = tile.getComputer();
|
||||
if( computer == null )
|
||||
{
|
||||
tile.m_startOn = true;
|
||||
@@ -26,7 +26,7 @@ public abstract class ComputerProxy
|
||||
public void shutdown()
|
||||
{
|
||||
TileComputerBase tile = getTile();
|
||||
ServerComputer computer = tile.getServerComputer();
|
||||
IComputer computer = tile.getComputer();
|
||||
if( computer == null )
|
||||
{
|
||||
tile.m_startOn = false;
|
||||
@@ -40,7 +40,7 @@ public abstract class ComputerProxy
|
||||
public void reboot()
|
||||
{
|
||||
TileComputerBase tile = getTile();
|
||||
ServerComputer computer = tile.getServerComputer();
|
||||
IComputer computer = tile.getComputer();
|
||||
if( computer == null )
|
||||
{
|
||||
tile.m_startOn = true;
|
||||
@@ -54,7 +54,7 @@ public abstract class ComputerProxy
|
||||
public int assignID()
|
||||
{
|
||||
TileComputerBase tile = getTile();
|
||||
ServerComputer computer = tile.getServerComputer();
|
||||
IComputer computer = tile.getComputer();
|
||||
if( computer == null )
|
||||
{
|
||||
return tile.m_computerID;
|
||||
@@ -67,14 +67,14 @@ public abstract class ComputerProxy
|
||||
|
||||
public boolean isOn()
|
||||
{
|
||||
ServerComputer computer = getTile().getServerComputer();
|
||||
IComputer computer = getTile().getComputer();
|
||||
return computer != null && computer.isOn();
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
TileComputerBase tile = getTile();
|
||||
ServerComputer computer = tile.getServerComputer();
|
||||
IComputer computer = tile.getComputer();
|
||||
if( computer == null )
|
||||
{
|
||||
return tile.m_label;
|
||||
|
@@ -10,9 +10,9 @@ import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
import dan200.computercraft.core.apis.ILuaAPI;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.IComputerEnvironment;
|
||||
import dan200.computercraft.shared.common.ServerTerminal;
|
||||
@@ -100,8 +100,7 @@ public class ServerComputer extends ServerTerminal
|
||||
super.update();
|
||||
m_computer.advance( 0.05 );
|
||||
|
||||
m_changedLastFrame = m_changed || m_computer.pollChanged();
|
||||
m_computer.clearChanged();
|
||||
m_changedLastFrame = m_computer.pollAndResetChanged() || m_changed;
|
||||
m_changed = false;
|
||||
|
||||
m_ticksSincePing++;
|
||||
@@ -314,6 +313,11 @@ public class ServerComputer extends ServerTerminal
|
||||
m_computer.addAPI( api );
|
||||
}
|
||||
|
||||
public void addAPI( dan200.computercraft.core.apis.ILuaAPI api )
|
||||
{
|
||||
m_computer.addAPI( api );
|
||||
}
|
||||
|
||||
public void setPeripheral( int side, IPeripheral peripheral )
|
||||
{
|
||||
m_computer.setPeripheral( side, peripheral );
|
||||
|
@@ -290,7 +290,8 @@ public class TileMonitor extends TilePeripheralBase
|
||||
|
||||
public double getTextScale()
|
||||
{
|
||||
return m_textScale * 0.5;
|
||||
TileMonitor origin = getOrigin();
|
||||
return (origin == null ? m_textScale : origin.m_textScale) * 0.5;
|
||||
}
|
||||
|
||||
private void rebuildTerminal()
|
||||
|
@@ -7,10 +7,10 @@
|
||||
package dan200.computercraft.shared.pocket.apis;
|
||||
|
||||
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.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.core.apis.ILuaAPI;
|
||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
import dan200.computercraft.shared.util.WorldUtil;
|
||||
@@ -39,21 +39,6 @@ public class PocketAPI implements ILuaAPI
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startup()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double dt )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
|
@@ -10,6 +10,7 @@ import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.core.computer.MainThread;
|
||||
import dan200.computercraft.shared.command.ContainerViewComputer;
|
||||
import dan200.computercraft.shared.common.ColourableRecipe;
|
||||
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
||||
import dan200.computercraft.shared.common.TileGeneric;
|
||||
@@ -17,10 +18,7 @@ import dan200.computercraft.shared.computer.blocks.BlockCommandComputer;
|
||||
import dan200.computercraft.shared.computer.blocks.BlockComputer;
|
||||
import dan200.computercraft.shared.computer.blocks.TileCommandComputer;
|
||||
import dan200.computercraft.shared.computer.blocks.TileComputer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
import dan200.computercraft.shared.computer.core.IContainerComputer;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.core.*;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
|
||||
import dan200.computercraft.shared.computer.items.ItemCommandComputer;
|
||||
import dan200.computercraft.shared.computer.items.ItemComputer;
|
||||
@@ -184,6 +182,9 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
||||
@Override
|
||||
public abstract Object getPocketComputerGUI( EntityPlayer player, EnumHand hand );
|
||||
|
||||
@Override
|
||||
public abstract Object getComputerGUI( IComputer computer, int width, int height, ComputerFamily family );
|
||||
|
||||
@Override
|
||||
public abstract File getWorldDir( World world );
|
||||
|
||||
@@ -553,6 +554,11 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
||||
{
|
||||
return new ContainerPocketComputer( player, x == 0 ? EnumHand.MAIN_HAND : EnumHand.OFF_HAND );
|
||||
}
|
||||
case ComputerCraft.viewComputerGUIID:
|
||||
{
|
||||
ServerComputer computer = ComputerCraft.serverComputerRegistry.get( x );
|
||||
return computer == null ? null : new ContainerViewComputer( computer );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -611,6 +617,27 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
||||
{
|
||||
return getPocketComputerGUI( player, x == 0 ? EnumHand.MAIN_HAND : EnumHand.OFF_HAND );
|
||||
}
|
||||
case ComputerCraft.viewComputerGUIID:
|
||||
{
|
||||
ClientComputer computer = ComputerCraft.clientComputerRegistry.get( x );
|
||||
|
||||
// We extract some terminal information from the various coordinate flags.
|
||||
// See ComputerCraft.openComputerGUI for how they are packed.
|
||||
ComputerFamily family = ComputerFamily.values()[ y ];
|
||||
int width = (z >> 16) & 0xFFFF, height = z & 0xFF;
|
||||
|
||||
if( computer == null )
|
||||
{
|
||||
computer = new ClientComputer( x );
|
||||
ComputerCraft.clientComputerRegistry.add( x, computer );
|
||||
}
|
||||
else if( computer.getTerminal() != null )
|
||||
{
|
||||
width = computer.getTerminal().getWidth();
|
||||
height = computer.getTerminal().getHeight();
|
||||
}
|
||||
return getComputerGUI( computer, width, height, family );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@
|
||||
package dan200.computercraft.shared.proxy;
|
||||
|
||||
import dan200.computercraft.shared.computer.blocks.TileComputer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
import dan200.computercraft.shared.network.ComputerCraftPacket;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
|
||||
import dan200.computercraft.shared.peripheral.printer.TilePrinter;
|
||||
@@ -42,6 +44,7 @@ public interface IComputerCraftProxy
|
||||
Object getTurtleGUI( InventoryPlayer inventory, TileTurtle turtle );
|
||||
Object getPrintoutGUI( EntityPlayer player, EnumHand hand );
|
||||
Object getPocketComputerGUI( EntityPlayer player, EnumHand hand );
|
||||
Object getComputerGUI( IComputer computer, int width, int height, ComputerFamily family );
|
||||
|
||||
File getWorldDir( World world );
|
||||
void handlePacket( ComputerCraftPacket packet, EntityPlayer player );
|
||||
|
@@ -6,13 +6,13 @@
|
||||
|
||||
package dan200.computercraft.shared.turtle.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleCommand;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
import dan200.computercraft.core.apis.ILuaAPI;
|
||||
import dan200.computercraft.shared.turtle.core.*;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -44,21 +44,6 @@ public class TurtleAPI implements ILuaAPI
|
||||
"turtle"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startup( )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown( )
|
||||
{
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
|
@@ -1,3 +1,7 @@
|
||||
# RecordMedia (and related methods)
|
||||
public net.minecraft.item.ItemRecord field_185076_b # sound
|
||||
public net.minecraft.item.ItemRecord field_185077_c # displayName
|
||||
# ItemPocketRenderer
|
||||
public net.minecraft.client.renderer.ItemRenderer func_187466_c()V # renderArms
|
||||
public net.minecraft.client.renderer.ItemRenderer func_178100_c(F)F # getMapAngleFromPitch
|
||||
public net.minecraft.client.renderer.ItemRenderer func_187456_a(FFLnet/minecraft/util/EnumHandSide;)V # renderArmFirstPerson
|
||||
|
@@ -737,6 +737,28 @@ if http then
|
||||
if url == _url then return ok, err end
|
||||
end
|
||||
end
|
||||
|
||||
local nativeWebsocket = http.websocket
|
||||
http.websocketAsync = nativeWebsocket
|
||||
http.websocket = function( _url, _headers )
|
||||
if type( _url ) ~= "string" then
|
||||
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
||||
end
|
||||
if _headers ~= nil and type( _headers ) ~= "table" then
|
||||
error( "bad argument #2 (expected table, got " .. type( _headers ) .. ")", 2 )
|
||||
end
|
||||
local ok, err = nativeWebsocket( _url, _headers )
|
||||
if not ok then return ok, err end
|
||||
|
||||
while true do
|
||||
local event, url, param = os.pullEvent( )
|
||||
if event == "websocket_success" and url == _url then
|
||||
return param
|
||||
elseif event == "websocket_failure" and url == _url then
|
||||
return false, param
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Install the lua part of the FS api
|
||||
|
Reference in New Issue
Block a user