mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-15 22:17:39 +00:00
Compare commits
49 Commits
v1.80pr1.4
...
v1.80pr1.6
Author | SHA1 | Date | |
---|---|---|---|
![]() |
18429c50f9 | ||
![]() |
7ec8ddcf7d | ||
![]() |
5bf9f9e3c5 | ||
![]() |
e4164ee9a1 | ||
![]() |
b522af3075 | ||
![]() |
8775052dee | ||
![]() |
c0c5d57e10 | ||
![]() |
4c2e97b1af | ||
![]() |
f17df15117 | ||
![]() |
bfbb18bdfc | ||
![]() |
cac65ef755 | ||
![]() |
a42793024b | ||
![]() |
7dbc4e6455 | ||
![]() |
a0d71cb3ad | ||
![]() |
83546d0acb | ||
![]() |
e2f9ddd534 | ||
![]() |
911e404bfa | ||
![]() |
bfeafe163f | ||
![]() |
6cf32f1f74 | ||
![]() |
04f162ef25 | ||
![]() |
b2aa390ae1 | ||
![]() |
6ca61f000f | ||
![]() |
20a47a7f88 | ||
![]() |
e2e6946c92 | ||
![]() |
abe917cd54 | ||
![]() |
a1d77ab8e7 | ||
![]() |
c8db671409 | ||
![]() |
52641b7bea | ||
![]() |
3e751ee94a | ||
![]() |
2b28cc3558 | ||
![]() |
f9761388b1 | ||
![]() |
d28694eb57 | ||
![]() |
d758895578 | ||
![]() |
043d5f00ca | ||
![]() |
36878e75b7 | ||
![]() |
ebb50cba48 | ||
![]() |
7c218361d9 | ||
![]() |
bb2eab0bed | ||
![]() |
e8c0cf3857 | ||
![]() |
db825a7aab | ||
![]() |
dbcae810f0 | ||
![]() |
914df8b0c7 | ||
![]() |
f753513289 | ||
![]() |
7bb8efed1d | ||
![]() |
0cec4aee8c | ||
![]() |
e0c9dc24e7 | ||
![]() |
244907a39a | ||
![]() |
5eadf5533d | ||
![]() |
1ef7c8e8db |
70
build.gradle
70
build.gradle
@@ -15,18 +15,20 @@ buildscript {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.matthewprenger.cursegradle' version '1.0.9'
|
||||
id 'com.matthewprenger.cursegradle' version '1.0.10'
|
||||
}
|
||||
|
||||
apply plugin: 'net.minecraftforge.gradle.forge'
|
||||
apply plugin: 'org.ajoberstar.grgit'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'maven'
|
||||
|
||||
version = "1.80pr1.4"
|
||||
version = "1.80pr1.6"
|
||||
group = "org.squiddev"
|
||||
archivesBaseName = "cc-tweaked"
|
||||
|
||||
minecraft {
|
||||
version = "1.12-14.21.1.2387"
|
||||
version = "1.12.2-14.23.2.2634"
|
||||
runDir = "run"
|
||||
replace '${version}', project.version
|
||||
|
||||
@@ -35,7 +37,7 @@ minecraft {
|
||||
// stable_# stables are built at the discretion of the MCP team.
|
||||
// Use non-default mappings at your own risk. they may not allways work.
|
||||
// simply re-run your setup task after changing the mappings to update your workspace.
|
||||
mappings = "snapshot_20170629"
|
||||
mappings = "snapshot_20180324"
|
||||
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
|
||||
}
|
||||
|
||||
@@ -53,14 +55,17 @@ repositories {
|
||||
configurations {
|
||||
shade
|
||||
compile.extendsFrom shade
|
||||
deployerJars
|
||||
}
|
||||
|
||||
dependencies {
|
||||
deobfProvided "mezz.jei:jei_1.12:4.7.5.86:api"
|
||||
runtime "mezz.jei:jei_1.12:4.7.5.86"
|
||||
deobfProvided "mezz.jei:jei_1.12.2:4.8.5.159:api"
|
||||
runtime "mezz.jei:jei_1.12.2:4.8.5.159"
|
||||
shade 'org.squiddev:Cobalt:0.3.1'
|
||||
|
||||
testCompile 'junit:junit:4.11'
|
||||
|
||||
deployerJars "org.apache.maven.wagon:wagon-ssh:3.0.0"
|
||||
}
|
||||
|
||||
javadoc {
|
||||
@@ -125,6 +130,59 @@ curseforge {
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
artifact sourceJar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uploadArchives {
|
||||
repositories {
|
||||
if(project.hasProperty('mavenUploadUrl')) {
|
||||
mavenDeployer {
|
||||
configuration = configurations.deployerJars
|
||||
|
||||
repository(url: project.property('mavenUploadUrl')) {
|
||||
authentication(
|
||||
userName: project.property('mavenUploadUser'),
|
||||
privateKey: project.property('mavenUploadKey'))
|
||||
}
|
||||
|
||||
pom.project {
|
||||
name 'CC: Tweaked'
|
||||
packaging 'jar'
|
||||
description 'A fork of ComputerCraft which aims to provide earlier access to the more experimental and in-development features of the mod.'
|
||||
url 'https://github.com/SquidDev-CC/CC-Tweaked'
|
||||
|
||||
scm {
|
||||
url 'https://github.com/dan200/ComputerCraft.git'
|
||||
}
|
||||
|
||||
issueManagement {
|
||||
system 'github'
|
||||
url 'https://github.com/dan200/ComputerCraft/issues'
|
||||
}
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name 'ComputerCraft Public License, Version 1.0'
|
||||
url 'https://github.com/dan200/ComputerCraft/blob/master/LICENSE'
|
||||
distribution 'repo'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pom.whenConfigured { pom ->
|
||||
pom.dependencies.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << "-Xlint"
|
||||
|
1
settings.gradle
Normal file
1
settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
rootProject.name = 'cc-tweaked'
|
@@ -16,7 +16,6 @@ import dan200.computercraft.api.media.IMediaProvider;
|
||||
import dan200.computercraft.api.network.IPacketNetwork;
|
||||
import dan200.computercraft.api.network.wired.IWiredElement;
|
||||
import dan200.computercraft.api.network.wired.IWiredNode;
|
||||
import dan200.computercraft.api.network.wired.IWiredProvider;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.peripheral.IPeripheralProvider;
|
||||
import dan200.computercraft.api.permissions.ITurtlePermissionProvider;
|
||||
@@ -29,6 +28,7 @@ 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.core.tracking.Tracking;
|
||||
import dan200.computercraft.shared.command.CommandComputer;
|
||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
||||
@@ -61,6 +61,7 @@ import dan200.computercraft.shared.turtle.blocks.BlockTurtle;
|
||||
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
|
||||
import dan200.computercraft.shared.turtle.upgrades.*;
|
||||
import dan200.computercraft.shared.util.*;
|
||||
import dan200.computercraft.shared.wired.CapabilityWiredElement;
|
||||
import dan200.computercraft.shared.wired.WiredNode;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -69,6 +70,7 @@ import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.NonNullList;
|
||||
@@ -266,7 +268,6 @@ public class ComputerCraft
|
||||
private static List<ITurtlePermissionProvider> permissionProviders = new ArrayList<>();
|
||||
private static final Map<String, IPocketUpgrade> pocketUpgrades = new HashMap<>();
|
||||
private static final Set<ILuaAPIFactory> apiFactories = new LinkedHashSet<>();
|
||||
private static final Set<IWiredProvider> wiredProviders = new LinkedHashSet<>();
|
||||
|
||||
// Implementation
|
||||
@Mod.Instance( value = ComputerCraft.MOD_ID )
|
||||
@@ -461,8 +462,7 @@ public class ComputerCraft
|
||||
@Mod.EventHandler
|
||||
public void onServerStarting( FMLServerStartingEvent event )
|
||||
{
|
||||
event.registerServerCommand( new CommandComputer() );
|
||||
event.registerServerCommand( new CommandComputerCraft() );
|
||||
proxy.initServer( event.getServer() );
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
@@ -472,6 +472,7 @@ public class ComputerCraft
|
||||
{
|
||||
ComputerCraft.serverComputerRegistry.reset();
|
||||
WirelessNetwork.resetNetworks();
|
||||
Tracking.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,6 +483,7 @@ public class ComputerCraft
|
||||
{
|
||||
ComputerCraft.serverComputerRegistry.reset();
|
||||
WirelessNetwork.resetNetworks();
|
||||
Tracking.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,11 +507,6 @@ public class ComputerCraft
|
||||
return proxy.getRenderFrame();
|
||||
}
|
||||
|
||||
public static void deleteDisplayLists( int list, int range )
|
||||
{
|
||||
proxy.deleteDisplayLists( list, range );
|
||||
}
|
||||
|
||||
public static Object getFixedWidthFontRenderer()
|
||||
{
|
||||
return proxy.getFixedWidthFontRenderer();
|
||||
@@ -738,11 +735,6 @@ public class ComputerCraft
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerWiredProvider( IWiredProvider provider )
|
||||
{
|
||||
if( provider != null ) wiredProviders.add( provider );
|
||||
}
|
||||
|
||||
public static IWiredNode createWiredNodeForElement( IWiredElement element )
|
||||
{
|
||||
return new WiredNode( element );
|
||||
@@ -771,20 +763,10 @@ public class ComputerCraft
|
||||
|
||||
public static IWiredElement getWiredElementAt( IBlockAccess world, BlockPos pos, EnumFacing side )
|
||||
{
|
||||
// Try the handlers in order:
|
||||
for( IWiredProvider provider : wiredProviders )
|
||||
{
|
||||
try
|
||||
{
|
||||
IWiredElement element = provider.getElement( world, pos, side );
|
||||
if( element != null ) return element;
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
ComputerCraft.log.error( "Wired element provider " + provider + " errored.", e );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
return tile != null && tile.hasCapability( CapabilityWiredElement.CAPABILITY, side )
|
||||
? tile.getCapability( CapabilityWiredElement.CAPABILITY, side )
|
||||
: null;
|
||||
}
|
||||
|
||||
public static int getDefaultBundledRedstoneOutput( World world, BlockPos pos, EnumFacing side )
|
||||
|
@@ -14,7 +14,6 @@ import dan200.computercraft.api.media.IMediaProvider;
|
||||
import dan200.computercraft.api.network.IPacketNetwork;
|
||||
import dan200.computercraft.api.network.wired.IWiredElement;
|
||||
import dan200.computercraft.api.network.wired.IWiredNode;
|
||||
import dan200.computercraft.api.network.wired.IWiredProvider;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.peripheral.IPeripheralProvider;
|
||||
@@ -332,26 +331,6 @@ public final class ComputerCraftAPI
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a peripheral handler to convert blocks into {@link IPeripheral} implementations.
|
||||
*
|
||||
* @param handler The peripheral provider to register.
|
||||
* @see dan200.computercraft.api.peripheral.IPeripheral
|
||||
* @see dan200.computercraft.api.peripheral.IPeripheralProvider
|
||||
*/
|
||||
public static void registerWiredProvider( @Nonnull IWiredProvider handler )
|
||||
{
|
||||
findCC();
|
||||
if ( computerCraft_registerWiredProvider != null)
|
||||
{
|
||||
try {
|
||||
computerCraft_registerWiredProvider.invoke( null, handler );
|
||||
} catch (Exception e){
|
||||
// It failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new wired node for a given wired element
|
||||
*
|
||||
@@ -453,9 +432,6 @@ public final class ComputerCraftAPI
|
||||
computerCraft_registerAPIFactory = findCCMethod( "registerAPIFactory", new Class<?>[] {
|
||||
ILuaAPIFactory.class
|
||||
} );
|
||||
computerCraft_registerWiredProvider = findCCMethod( "registerWiredProvider", new Class<?>[] {
|
||||
IWiredProvider.class
|
||||
} );
|
||||
computerCraft_createWiredNodeForElement = findCCMethod( "createWiredNodeForElement", new Class<?>[] {
|
||||
IWiredElement.class
|
||||
} );
|
||||
@@ -499,7 +475,6 @@ public final class ComputerCraftAPI
|
||||
private static Method computerCraft_registerPocketUpgrade = null;
|
||||
private static Method computerCraft_getWirelessNetwork = null;
|
||||
private static Method computerCraft_registerAPIFactory = null;
|
||||
private static Method computerCraft_registerWiredProvider = null;
|
||||
private static Method computerCraft_createWiredNodeForElement = null;
|
||||
private static Method computerCraft_getWiredElementAt = null;
|
||||
}
|
||||
|
@@ -1,11 +1,8 @@
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An object which may be part of a wired network.
|
||||
@@ -14,30 +11,11 @@ import java.util.Map;
|
||||
* as a proxy for all network objects. Whilst the node may change networks, an element's node should remain constant
|
||||
* for its lifespan.
|
||||
*
|
||||
* Elements are generally tied to a block or tile entity in world. One should either register an {@link IWiredProvider}
|
||||
* or implement {@link IWiredElementTile} on your tile entity.
|
||||
*
|
||||
* @see IWiredProvider
|
||||
* @see ComputerCraftAPI#registerWiredProvider(IWiredProvider)
|
||||
* @see IWiredElementTile
|
||||
* Elements are generally tied to a block or tile entity in world. In such as case, one should provide the
|
||||
* {@link IWiredElement} capability for the appropriate sides.
|
||||
*/
|
||||
public interface IWiredElement extends IWiredSender
|
||||
{
|
||||
/**
|
||||
* Fetch the peripherals this network element provides.
|
||||
*
|
||||
* This is only called when initially attaching to a network and after a call to {@link IWiredNode#invalidate()}}, so
|
||||
* one does not <em>need</em> to cache the return value.
|
||||
*
|
||||
* @return The peripherals this node provides.
|
||||
* @see IWiredNode#invalidate()
|
||||
*/
|
||||
@Nonnull
|
||||
default Map<String, IPeripheral> getPeripherals()
|
||||
{
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when objects on the network change. This may occur when network nodes are added or removed, or when
|
||||
* peripherals change.
|
||||
|
@@ -1,22 +0,0 @@
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import net.minecraft.util.EnumFacing;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link net.minecraft.tileentity.TileEntity} which provides a {@link IWiredElement}. This acts
|
||||
* as a simpler alternative to a full-blown {@link IWiredProvider}.
|
||||
*/
|
||||
public interface IWiredElementTile
|
||||
{
|
||||
/**
|
||||
* Get the wired element of this tile for a given side.
|
||||
*
|
||||
* @param side The side to get the network element from.
|
||||
* @return A network element, or {@code null} if there is no element here.
|
||||
*/
|
||||
@Nullable
|
||||
IWiredElement getWiredElement( @Nonnull EnumFacing side );
|
||||
}
|
@@ -1,6 +1,9 @@
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A wired network is composed of one of more {@link IWiredNode}s, a set of connections between them, and a series
|
||||
@@ -51,7 +54,8 @@ public interface IWiredNetwork
|
||||
/**
|
||||
* Sever all connections this node has, removing it from this network.
|
||||
*
|
||||
* This should only be used on the server thread.
|
||||
* This should only be used on the server thread. You should only call this on nodes
|
||||
* that your network element owns.
|
||||
*
|
||||
* @param node The node to remove
|
||||
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
|
||||
@@ -62,13 +66,15 @@ public interface IWiredNetwork
|
||||
boolean remove( @Nonnull IWiredNode node );
|
||||
|
||||
/**
|
||||
* Mark this node's peripherals as having changed.
|
||||
* Update the peripherals a node provides.
|
||||
*
|
||||
* This should only be used on the server thread.
|
||||
* This should only be used on the server thread. You should only call this on nodes
|
||||
* that your network element owns.
|
||||
*
|
||||
* @param node The node to mark as invalid.
|
||||
* @param node The node to attach peripherals for.
|
||||
* @param peripherals The new peripherals for this node.
|
||||
* @throws IllegalArgumentException If the node is not in the network.
|
||||
* @see IWiredElement#getPeripherals()
|
||||
* @see IWiredNode#updatePeripherals(Map)
|
||||
*/
|
||||
void invalidate( @Nonnull IWiredNode node );
|
||||
void updatePeripherals( @Nonnull IWiredNode node, @Nonnull Map<String, IPeripheral> peripherals );
|
||||
}
|
||||
|
@@ -1,8 +1,10 @@
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.network.IPacketNetwork;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Wired nodes act as a layer between {@link IWiredElement}s and {@link IWiredNetwork}s.
|
||||
@@ -72,7 +74,8 @@ public interface IWiredNode extends IPacketNetwork
|
||||
/**
|
||||
* Sever all connections this node has, removing it from this network.
|
||||
*
|
||||
* This should only be used on the server thread.
|
||||
* This should only be used on the server thread. You should only call this on nodes
|
||||
* that your network element owns.
|
||||
*
|
||||
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
|
||||
* only element.
|
||||
@@ -87,12 +90,14 @@ public interface IWiredNode extends IPacketNetwork
|
||||
/**
|
||||
* Mark this node's peripherals as having changed.
|
||||
*
|
||||
* This should only be used on the server thread.
|
||||
* This should only be used on the server thread. You should only call this on nodes
|
||||
* that your network element owns.
|
||||
*
|
||||
* @see IWiredElement#getPeripherals()
|
||||
* @param peripherals The new peripherals for this node.
|
||||
* @see IWiredNetwork#updatePeripherals(IWiredNode, Map)
|
||||
*/
|
||||
default void invalidate()
|
||||
default void updatePeripherals( @Nonnull Map<String, IPeripheral> peripherals )
|
||||
{
|
||||
getNetwork().invalidate( this );
|
||||
getNetwork().updatePeripherals( this, peripherals );
|
||||
}
|
||||
}
|
||||
|
@@ -1,29 +0,0 @@
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Fetch or create an {@link IWiredElement} for a block at a given position.
|
||||
*
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerWiredProvider(IWiredProvider)
|
||||
* @see IWiredElementTile
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface IWiredProvider
|
||||
{
|
||||
/**
|
||||
* Extract a wired network element from a block location.
|
||||
*
|
||||
* @param world The world the block is in.
|
||||
* @param pos The position the block is at.
|
||||
* @param side The side to get the network element from.
|
||||
* @return A network element, or {@code null} if there is not an element here you'd like to handle.
|
||||
*/
|
||||
@Nullable
|
||||
IWiredElement getElement( @Nonnull IBlockAccess world, @Nonnull BlockPos pos, @Nonnull EnumFacing side );
|
||||
}
|
@@ -210,7 +210,7 @@ public class CCTurtleProxyClient extends CCTurtleProxyCommon
|
||||
private static class TurtleItemColour implements IItemColor
|
||||
{
|
||||
@Override
|
||||
public int getColorFromItemstack( @Nonnull ItemStack stack, int tintIndex )
|
||||
public int colorMultiplier( @Nonnull ItemStack stack, int tintIndex )
|
||||
{
|
||||
if( tintIndex == 0 )
|
||||
{
|
||||
|
@@ -25,6 +25,7 @@ import dan200.computercraft.shared.media.items.ItemPrintout;
|
||||
import dan200.computercraft.shared.network.ComputerCraftPacket;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
|
||||
import dan200.computercraft.shared.peripheral.modem.TileCable;
|
||||
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
|
||||
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
|
||||
import dan200.computercraft.shared.peripheral.printer.TilePrinter;
|
||||
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
|
||||
@@ -33,9 +34,10 @@ import dan200.computercraft.shared.proxy.ComputerCraftProxyCommon;
|
||||
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
|
||||
import dan200.computercraft.shared.turtle.entity.TurtleVisionCamera;
|
||||
import dan200.computercraft.shared.util.Colour;
|
||||
import gnu.trove.map.hash.TIntIntHashMap;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.gui.GuiNewChat;
|
||||
import net.minecraft.client.renderer.ItemMeshDefinition;
|
||||
import net.minecraft.client.renderer.block.model.ModelBakery;
|
||||
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
|
||||
@@ -50,6 +52,7 @@ import net.minecraft.util.IThreadListener;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.ModelRegistryEvent;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
@@ -57,6 +60,7 @@ import net.minecraftforge.client.event.RenderHandEvent;
|
||||
import net.minecraftforge.client.event.RenderPlayerEvent;
|
||||
import net.minecraftforge.client.model.ModelLoader;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.fml.client.registry.ClientRegistry;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
@@ -70,6 +74,8 @@ import java.util.List;
|
||||
|
||||
public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
{
|
||||
private static TIntIntHashMap lastCounts = new TIntIntHashMap();
|
||||
|
||||
private long m_tick;
|
||||
private long m_renderFrame;
|
||||
private FixedWidthFontRenderer m_fixedWidthFontRenderer;
|
||||
@@ -297,12 +303,6 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
return m_renderFrame;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDisplayLists( int list, int range )
|
||||
{
|
||||
GlStateManager.glDeleteLists( list, range );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getFixedWidthFontRenderer()
|
||||
{
|
||||
@@ -389,6 +389,7 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
case ComputerCraftPacket.ComputerTerminalChanged:
|
||||
case ComputerCraftPacket.ComputerDeleted:
|
||||
case ComputerCraftPacket.PlayRecord:
|
||||
case ComputerCraftPacket.PostChat:
|
||||
{
|
||||
// Packet from Server to Client
|
||||
IThreadListener listener = Minecraft.getMinecraft();
|
||||
@@ -455,6 +456,36 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
{
|
||||
mc.world.playRecord( pos, null );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ComputerCraftPacket.PostChat:
|
||||
{
|
||||
/*
|
||||
This allows us to send delete chat messages of the same "category" as the previous one.
|
||||
It's used by the various /computercraft commands to avoid filling the chat with repetitive
|
||||
messages.
|
||||
*/
|
||||
|
||||
int id = packet.m_dataInt[0];
|
||||
ITextComponent[] components = new ITextComponent[packet.m_dataString.length];
|
||||
for( int i = 0; i < packet.m_dataString.length; i++ )
|
||||
{
|
||||
components[i] = ITextComponent.Serializer.jsonToComponent( packet.m_dataString[i] );
|
||||
}
|
||||
|
||||
GuiNewChat chat = Minecraft.getMinecraft().ingameGUI.getChatGUI();
|
||||
|
||||
// Keep track of how many lines we wrote last time, deleting any extra ones.
|
||||
int lastCount = lastCounts.get( id );
|
||||
for( int i = components.length; i < lastCount; i++ ) chat.deleteChatLine( i + id );
|
||||
lastCounts.put( id, components.length );
|
||||
|
||||
// Add new lines
|
||||
for( int i = 0; i < components.length; i++ )
|
||||
{
|
||||
chat.printChatMessageWithOptionalDeletion( components[i], id + i );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -552,6 +583,15 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
m_renderFrame++;
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onWorldUnload( WorldEvent.Unload event )
|
||||
{
|
||||
if( event.getWorld().isRemote )
|
||||
{
|
||||
ClientMonitor.destroyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@@ -565,7 +605,7 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColorFromItemstack( @Nonnull ItemStack stack, int layer )
|
||||
public int colorMultiplier( @Nonnull ItemStack stack, int layer )
|
||||
{
|
||||
return layer == 0 ? 0xFFFFFF : disk.getColour( stack );
|
||||
}
|
||||
|
@@ -189,7 +189,7 @@ public final class ModelTransformer
|
||||
private BakedQuadBuilder( VertexFormat format )
|
||||
{
|
||||
this.format = format;
|
||||
this.vertexData = new int[ format.getNextOffset() ];
|
||||
this.vertexData = new int[ format.getSize() ];
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@@ -18,6 +18,7 @@ import dan200.computercraft.shared.util.Palette;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
@@ -47,19 +48,20 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
|
||||
if( originTerminal == null ) return;
|
||||
TileMonitor origin = originTerminal.getOrigin();
|
||||
BlockPos monitorPos = monitor.getPos();
|
||||
|
||||
// Ensure each monitor is rendered only once
|
||||
// Ensure each monitor terminal is rendered only once. We allow rendering a specific tile
|
||||
// multiple times in a single frame to ensure compatibility with shaders which may run a
|
||||
// pass multiple times.
|
||||
long renderFrame = ComputerCraft.getRenderFrame();
|
||||
if( originTerminal.lastRenderFrame == renderFrame )
|
||||
if( originTerminal.lastRenderFrame == renderFrame && !monitorPos.equals( originTerminal.lastRenderPos ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
originTerminal.lastRenderFrame = renderFrame;
|
||||
}
|
||||
|
||||
BlockPos monitorPos = monitor.getPos();
|
||||
originTerminal.lastRenderFrame = renderFrame;
|
||||
originTerminal.lastRenderPos = monitorPos;
|
||||
|
||||
BlockPos originPos = origin.getPos();
|
||||
posX += originPos.getX() - monitorPos.getX();
|
||||
posY += originPos.getY() - monitorPos.getY();
|
||||
@@ -96,6 +98,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
|
||||
// Draw the contents
|
||||
GlStateManager.depthMask( false );
|
||||
OpenGlHelper.setLightmapTextureCoords( OpenGlHelper.lightmapTexUnit, 0xFF, 0xFF );
|
||||
GlStateManager.disableLighting();
|
||||
mc.entityRenderer.disableLightmap();
|
||||
try
|
||||
@@ -106,9 +109,9 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
Palette palette = terminal.getPalette();
|
||||
|
||||
// Allocate display lists
|
||||
if( originTerminal.renderDisplayList < 0 )
|
||||
if( originTerminal.renderDisplayLists == null )
|
||||
{
|
||||
originTerminal.renderDisplayList = GlStateManager.glGenLists( 3 );
|
||||
originTerminal.createLists();
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
@@ -132,7 +135,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
if( redraw )
|
||||
{
|
||||
// Build background display list
|
||||
GlStateManager.glNewList( originTerminal.renderDisplayList, GL11.GL_COMPILE );
|
||||
GlStateManager.glNewList( originTerminal.renderDisplayLists[0], GL11.GL_COMPILE );
|
||||
try
|
||||
{
|
||||
double marginXSize = TileMonitor.RENDER_MARGIN / xScale;
|
||||
@@ -171,7 +174,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
GlStateManager.glEndList();
|
||||
}
|
||||
}
|
||||
GlStateManager.callList( originTerminal.renderDisplayList );
|
||||
GlStateManager.callList( originTerminal.renderDisplayLists[0] );
|
||||
GlStateManager.resetColor();
|
||||
|
||||
// Draw text
|
||||
@@ -179,7 +182,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
if( redraw )
|
||||
{
|
||||
// Build text display list
|
||||
GlStateManager.glNewList( originTerminal.renderDisplayList + 1, GL11.GL_COMPILE );
|
||||
GlStateManager.glNewList( originTerminal.renderDisplayLists[1], GL11.GL_COMPILE );
|
||||
try
|
||||
{
|
||||
// Lines
|
||||
@@ -199,7 +202,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
GlStateManager.glEndList();
|
||||
}
|
||||
}
|
||||
GlStateManager.callList( originTerminal.renderDisplayList + 1 );
|
||||
GlStateManager.callList( originTerminal.renderDisplayLists[1] );
|
||||
GlStateManager.resetColor();
|
||||
|
||||
// Draw cursor
|
||||
@@ -207,7 +210,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
if( redraw )
|
||||
{
|
||||
// Build cursor display list
|
||||
GlStateManager.glNewList( originTerminal.renderDisplayList + 2, GL11.GL_COMPILE );
|
||||
GlStateManager.glNewList( originTerminal.renderDisplayLists[2], GL11.GL_COMPILE );
|
||||
try
|
||||
{
|
||||
// Cursor
|
||||
@@ -233,7 +236,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
}
|
||||
if( ComputerCraft.getGlobalCursorBlink() )
|
||||
{
|
||||
GlStateManager.callList( originTerminal.renderDisplayList + 2 );
|
||||
GlStateManager.callList( originTerminal.renderDisplayLists[2] );
|
||||
GlStateManager.resetColor();
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ import dan200.computercraft.core.apis.handles.EncodedInputHandle;
|
||||
import dan200.computercraft.core.apis.handles.EncodedOutputHandle;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.InputStream;
|
||||
@@ -88,6 +89,7 @@ public class FSAPI implements ILuaAPI
|
||||
{
|
||||
// list
|
||||
String path = getString( args, 0 );
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
try {
|
||||
String[] results = m_fileSystem.list( path );
|
||||
Map<Object,Object> table = new HashMap<>();
|
||||
@@ -162,6 +164,7 @@ public class FSAPI implements ILuaAPI
|
||||
// makeDir
|
||||
String path = getString( args, 0 );
|
||||
try {
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
m_fileSystem.makeDir( path );
|
||||
return null;
|
||||
} catch( FileSystemException e ) {
|
||||
@@ -174,6 +177,7 @@ public class FSAPI implements ILuaAPI
|
||||
String path = getString( args, 0 );
|
||||
String dest = getString( args, 1 );
|
||||
try {
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
m_fileSystem.move( path, dest );
|
||||
return null;
|
||||
} catch( FileSystemException e ) {
|
||||
@@ -186,6 +190,7 @@ public class FSAPI implements ILuaAPI
|
||||
String path = getString( args, 0 );
|
||||
String dest = getString( args, 1 );
|
||||
try {
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
m_fileSystem.copy( path, dest );
|
||||
return null;
|
||||
} catch( FileSystemException e ) {
|
||||
@@ -197,6 +202,7 @@ public class FSAPI implements ILuaAPI
|
||||
// delete
|
||||
String path = getString( args, 0 );
|
||||
try {
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
m_fileSystem.delete( path );
|
||||
return null;
|
||||
} catch( FileSystemException e ) {
|
||||
@@ -208,6 +214,7 @@ public class FSAPI implements ILuaAPI
|
||||
// open
|
||||
String path = getString( args, 0 );
|
||||
String mode = getString( args, 1 );
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
try {
|
||||
switch( mode )
|
||||
{
|
||||
@@ -288,6 +295,7 @@ public class FSAPI implements ILuaAPI
|
||||
// find
|
||||
String path = getString( args, 0 );
|
||||
try {
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
String[] results = m_fileSystem.find( path );
|
||||
Map<Object,Object> table = new HashMap<>();
|
||||
for(int i=0; i<results.length; ++i ) {
|
||||
|
@@ -6,6 +6,7 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
@@ -21,9 +22,14 @@ import java.util.*;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.*;
|
||||
import static dan200.computercraft.core.apis.TableHelper.*;
|
||||
|
||||
public class HTTPAPI implements ILuaAPI
|
||||
{
|
||||
private static final Set<String> HTTP_METHODS = ImmutableSet.of(
|
||||
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE"
|
||||
);
|
||||
|
||||
private final IAPIEnvironment m_apiEnvironment;
|
||||
private final List<Future<?>> m_httpTasks;
|
||||
private final Set<Closeable> m_closeables;
|
||||
@@ -38,7 +44,7 @@ public class HTTPAPI implements ILuaAPI
|
||||
@Override
|
||||
public String[] getNames()
|
||||
{
|
||||
return new String[] {
|
||||
return new String[]{
|
||||
"http"
|
||||
};
|
||||
}
|
||||
@@ -59,7 +65,7 @@ public class HTTPAPI implements ILuaAPI
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown( )
|
||||
public void shutdown()
|
||||
{
|
||||
synchronized( m_httpTasks )
|
||||
{
|
||||
@@ -89,7 +95,7 @@ public class HTTPAPI implements ILuaAPI
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
return new String[] {
|
||||
return new String[]{
|
||||
"request",
|
||||
"checkURL",
|
||||
"websocket",
|
||||
@@ -101,52 +107,68 @@ public class HTTPAPI implements ILuaAPI
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
case 0:
|
||||
case 0: // request
|
||||
{
|
||||
// request
|
||||
// Get URL
|
||||
String urlString = getString( args, 0 );
|
||||
String urlString, postString, requestMethod;
|
||||
Map<Object, Object> headerTable;
|
||||
boolean binary, redirect;
|
||||
|
||||
// Get POST
|
||||
String postString = optString( args, 1, null );
|
||||
|
||||
// Get Headers
|
||||
Map<String, String> headers = null;
|
||||
Map<Object, Object> table = optTable( args, 2, null );
|
||||
if( table != null )
|
||||
if( args.length >= 1 && args[0] instanceof Map )
|
||||
{
|
||||
headers = new HashMap<>( table.size() );
|
||||
for( Object key : table.keySet() )
|
||||
Map<?, ?> options = (Map) args[0];
|
||||
urlString = getStringField( options, "url" );
|
||||
postString = optStringField( options, "body", null );
|
||||
headerTable = optTableField( options, "headers", null );
|
||||
binary = optBooleanField( options, "binary", false );
|
||||
requestMethod = optStringField( options, "method", null );
|
||||
redirect = optBooleanField( options, "redirect", true );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get URL and post information
|
||||
urlString = getString( args, 0 );
|
||||
postString = optString( args, 1, null );
|
||||
headerTable = optTable( args, 2, null );
|
||||
binary = optBoolean( args, 3, false );
|
||||
requestMethod = null;
|
||||
redirect = true;
|
||||
}
|
||||
|
||||
Map<String, String> headers = null;
|
||||
if( headerTable != null )
|
||||
{
|
||||
headers = new HashMap<>( headerTable.size() );
|
||||
for( Object key : headerTable.keySet() )
|
||||
{
|
||||
Object value = table.get( key );
|
||||
Object value = headerTable.get( key );
|
||||
if( key instanceof String && value instanceof String )
|
||||
{
|
||||
headers.put( (String)key, (String)value );
|
||||
headers.put( (String) key, (String) value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get binary
|
||||
boolean binary = false;
|
||||
if( args.length >= 4 )
|
||||
|
||||
if( requestMethod != null && !HTTP_METHODS.contains( requestMethod ) )
|
||||
{
|
||||
binary = args[ 3 ] != null && !args[ 3 ].equals( Boolean.FALSE );
|
||||
throw new LuaException( "Unsupported HTTP method" );
|
||||
}
|
||||
|
||||
// Make the request
|
||||
try
|
||||
{
|
||||
URL url = HTTPRequest.checkURL( urlString );
|
||||
HTTPRequest request = new HTTPRequest( m_apiEnvironment, urlString, url, postString, headers, binary );
|
||||
HTTPRequest request = new HTTPRequest( m_apiEnvironment, urlString, url, postString, headers, binary, requestMethod, redirect );
|
||||
synchronized( m_httpTasks )
|
||||
{
|
||||
m_httpTasks.add( HTTPExecutor.EXECUTOR.submit( request ) );
|
||||
}
|
||||
return new Object[] { true };
|
||||
return new Object[]{ true };
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
return new Object[] { false, e.getMessage() };
|
||||
return new Object[]{ false, e.getMessage() };
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
@@ -164,11 +186,11 @@ public class HTTPAPI implements ILuaAPI
|
||||
{
|
||||
m_httpTasks.add( HTTPExecutor.EXECUTOR.submit( check ) );
|
||||
}
|
||||
return new Object[] { true };
|
||||
return new Object[]{ true };
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
return new Object[] { false, e.getMessage() };
|
||||
return new Object[]{ false, e.getMessage() };
|
||||
}
|
||||
}
|
||||
case 2: // websocket
|
||||
@@ -201,11 +223,11 @@ public class HTTPAPI implements ILuaAPI
|
||||
{
|
||||
m_httpTasks.add( connector );
|
||||
}
|
||||
return new Object[] { true };
|
||||
return new Object[]{ true };
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
return new Object[] { false, e.getMessage() };
|
||||
return new Object[]{ false, e.getMessage() };
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
@@ -11,6 +11,7 @@ import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.IComputerEnvironment;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
|
||||
public interface IAPIEnvironment
|
||||
{
|
||||
@@ -42,4 +43,11 @@ public interface IAPIEnvironment
|
||||
|
||||
String getLabel();
|
||||
void setLabel( String label );
|
||||
|
||||
void addTrackingChange( TrackingField field, long change );
|
||||
|
||||
default void addTrackingChange( TrackingField field )
|
||||
{
|
||||
addTrackingChange( field, 1 );
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ 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.tracking.TrackingField;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -109,6 +110,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
if( method >= 0 )
|
||||
{
|
||||
m_environment.addTrackingChange( TrackingField.PERIPHERAL_OPS );
|
||||
return m_peripheral.callMethod( this, context, method, arguments );
|
||||
}
|
||||
else
|
||||
|
214
src/main/java/dan200/computercraft/core/apis/TableHelper.java
Normal file
214
src/main/java/dan200/computercraft/core/apis/TableHelper.java
Normal file
@@ -0,0 +1,214 @@
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Various helpers for tables
|
||||
*/
|
||||
public final class TableHelper
|
||||
{
|
||||
private TableHelper()
|
||||
{
|
||||
throw new IllegalStateException( "Cannot instantiate singleton " + getClass().getName() );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static LuaException badKey( @Nonnull String key, @Nonnull String expected, @Nullable Object actual )
|
||||
{
|
||||
return badKey( key, expected, ArgumentHelper.getType( actual ) );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static LuaException badKey( @Nonnull String key, @Nonnull String expected, @Nonnull String actual )
|
||||
{
|
||||
return new LuaException( "bad field '" + key + "' (" + expected + " expected, got " + actual + ")" );
|
||||
}
|
||||
|
||||
public static double getNumberField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value instanceof Number )
|
||||
{
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "number", value );
|
||||
}
|
||||
}
|
||||
|
||||
public static int getIntField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value instanceof Number )
|
||||
{
|
||||
return (int) ((Number) value).longValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "number", value );
|
||||
}
|
||||
}
|
||||
|
||||
public static double getRealField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||
{
|
||||
return checkReal( key, getNumberField( table, key ) );
|
||||
}
|
||||
|
||||
public static boolean getBooleanField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value instanceof Boolean )
|
||||
{
|
||||
return (Boolean) value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "boolean", value );
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static String getStringField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value instanceof String )
|
||||
{
|
||||
return (String) value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "string", value );
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings( "unchecked" )
|
||||
@Nonnull
|
||||
public static Map<Object, Object> getTableField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value instanceof Map )
|
||||
{
|
||||
return (Map<Object, Object>) value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "table", value );
|
||||
}
|
||||
}
|
||||
|
||||
public static double optNumberField( @Nonnull Map<?, ?> table, @Nonnull String key, double def ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value == null )
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else if( value instanceof Number )
|
||||
{
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "number", value );
|
||||
}
|
||||
}
|
||||
|
||||
public static int optIntField( @Nonnull Map<?, ?> table, @Nonnull String key, int def ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value == null )
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else if( value instanceof Number )
|
||||
{
|
||||
return (int) ((Number) value).longValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "number", value );
|
||||
}
|
||||
}
|
||||
|
||||
public static double optRealField( @Nonnull Map<?, ?> table, @Nonnull String key, double def ) throws LuaException
|
||||
{
|
||||
return checkReal( key, optNumberField( table, key, def ) );
|
||||
}
|
||||
|
||||
public static boolean optBooleanField( @Nonnull Map<?, ?> table, @Nonnull String key, boolean def ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value == null )
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else if( value instanceof Boolean )
|
||||
{
|
||||
return (Boolean) value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "boolean", value );
|
||||
}
|
||||
}
|
||||
|
||||
public static String optStringField( @Nonnull Map<?, ?> table, @Nonnull String key, String def ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value == null )
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else if( value instanceof String )
|
||||
{
|
||||
return (String) value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "string", value );
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings( "unchecked" )
|
||||
public static Map<Object, Object> optTableField( @Nonnull Map<?, ?> table, @Nonnull String key, Map<Object, Object> def ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value == null )
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else if( value instanceof Map )
|
||||
{
|
||||
return (Map<Object, Object>) value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "table", value );
|
||||
}
|
||||
}
|
||||
|
||||
private static double checkReal( @Nonnull String key, double value ) throws LuaException
|
||||
{
|
||||
if( Double.isNaN( value ) )
|
||||
{
|
||||
throw badKey( key, "number", "nan" );
|
||||
}
|
||||
else if( value == Double.POSITIVE_INFINITY )
|
||||
{
|
||||
throw badKey( key, "number", "inf" );
|
||||
}
|
||||
else if( value == Double.NEGATIVE_INFINITY )
|
||||
{
|
||||
throw badKey( key, "number", "-inf" );
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,6 +5,7 @@ import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
@@ -13,6 +14,8 @@ import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
|
||||
|
||||
public class BinaryInputHandle extends HandleGeneric
|
||||
{
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
private final InputStream m_stream;
|
||||
|
||||
public BinaryInputHandle( InputStream reader )
|
||||
@@ -45,16 +48,46 @@ public class BinaryInputHandle extends HandleGeneric
|
||||
if( args.length > 0 && args[ 0 ] != null )
|
||||
{
|
||||
int count = getInt( args, 0 );
|
||||
if( count <= 0 || count >= 1024 * 16 )
|
||||
if( count < 0 )
|
||||
{
|
||||
throw new LuaException( "Count out of range" );
|
||||
// Whilst this may seem absurd to allow reading 0 bytes, PUC Lua it so
|
||||
// it seems best to remain somewhat consistent.
|
||||
throw new LuaException( "Cannot read a negative number of bytes" );
|
||||
}
|
||||
else if( count <= BUFFER_SIZE )
|
||||
{
|
||||
// If we've got a small count, then allocate that and read it.
|
||||
byte[] bytes = new byte[ count ];
|
||||
int read = m_stream.read( bytes );
|
||||
|
||||
byte[] bytes = new byte[ count ];
|
||||
count = m_stream.read( bytes );
|
||||
if( count < 0 ) return null;
|
||||
if( count < bytes.length ) bytes = Arrays.copyOf( bytes, count );
|
||||
return new Object[] { bytes };
|
||||
if( read < 0 ) return null;
|
||||
if( read < count ) bytes = Arrays.copyOf( bytes, read );
|
||||
return new Object[] { bytes };
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] buffer = new byte[ BUFFER_SIZE ];
|
||||
|
||||
// Read the initial set of bytes, failing if none are read.
|
||||
int read = m_stream.read( buffer, 0, Math.min( buffer.length, count ) );
|
||||
if( read == -1 ) return null;
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream( read );
|
||||
count -= read;
|
||||
out.write( buffer, 0, read );
|
||||
|
||||
// Otherwise read until we either reach the limit or we no longer consume
|
||||
// the full buffer.
|
||||
while( read >= buffer.length && count > 0 )
|
||||
{
|
||||
read = m_stream.read( buffer, 0, Math.min( BUFFER_SIZE, count ) );
|
||||
if( read == -1 ) break;
|
||||
count -= read;
|
||||
out.write( buffer, 0, read );
|
||||
}
|
||||
|
||||
return new Object[] { out.toByteArray() };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -6,10 +6,12 @@ import dan200.computercraft.api.lua.LuaException;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.*;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.*;
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.optInt;
|
||||
|
||||
public class EncodedInputHandle extends HandleGeneric
|
||||
{
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
private final BufferedReader m_reader;
|
||||
|
||||
public EncodedInputHandle( BufferedReader reader )
|
||||
@@ -111,15 +113,45 @@ public class EncodedInputHandle extends HandleGeneric
|
||||
try
|
||||
{
|
||||
int count = optInt( args, 0, 1 );
|
||||
if( count <= 0 || count >= 1024 * 16 )
|
||||
if( count < 0 )
|
||||
{
|
||||
throw new LuaException( "Count out of range" );
|
||||
// Whilst this may seem absurd to allow reading 0 characters, PUC Lua it so
|
||||
// it seems best to remain somewhat consistent.
|
||||
throw new LuaException( "Cannot read a negative number of characters" );
|
||||
}
|
||||
else if( count <= BUFFER_SIZE )
|
||||
{
|
||||
// If we've got a small count, then allocate that and read it.
|
||||
char[] chars = new char[ count ];
|
||||
int read = m_reader.read( chars );
|
||||
|
||||
return read < 0 ? null : new Object[] { new String( chars, 0, read ) };
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we've got a large count, read in bunches of 8192.
|
||||
char[] buffer = new char[ BUFFER_SIZE ];
|
||||
|
||||
// Read the initial set of characters, failing if none are read.
|
||||
int read = m_reader.read( buffer, 0, Math.min( buffer.length, count ) );
|
||||
if( read == -1 ) return null;
|
||||
|
||||
StringBuilder out = new StringBuilder( read );
|
||||
count -= read;
|
||||
out.append( buffer, 0, read );
|
||||
|
||||
// Otherwise read until we either reach the limit or we no longer consume
|
||||
// the full buffer.
|
||||
while( read >= BUFFER_SIZE && count > 0 )
|
||||
{
|
||||
read = m_reader.read( buffer, 0, Math.min( BUFFER_SIZE, count ) );
|
||||
if( read == -1 ) break;
|
||||
count -= read;
|
||||
out.append( buffer, 0, read );
|
||||
}
|
||||
|
||||
return new Object[] { out.toString() };
|
||||
}
|
||||
char[] bytes = new char[ count ];
|
||||
count = m_reader.read( bytes );
|
||||
if( count < 0 ) return null;
|
||||
String str = new String( bytes, 0, count );
|
||||
return new Object[] { str };
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
|
@@ -15,6 +15,7 @@ import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
|
||||
import dan200.computercraft.core.apis.handles.EncodedInputHandle;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.*;
|
||||
@@ -78,8 +79,10 @@ public class HTTPRequest implements Runnable
|
||||
private final String m_postText;
|
||||
private final Map<String, String> m_headers;
|
||||
private boolean m_binary;
|
||||
private final String m_method;
|
||||
private final boolean m_followRedirects;
|
||||
|
||||
public HTTPRequest( IAPIEnvironment environment, String urlString, URL url, final String postText, final Map<String, String> headers, boolean binary ) throws HTTPRequestException
|
||||
public HTTPRequest( IAPIEnvironment environment, String urlString, URL url, final String postText, final Map<String, String> headers, boolean binary, final String method, final boolean followRedirects ) throws HTTPRequestException
|
||||
{
|
||||
m_environment = environment;
|
||||
m_urlString = urlString;
|
||||
@@ -87,6 +90,8 @@ public class HTTPRequest implements Runnable
|
||||
m_binary = binary;
|
||||
m_postText = postText;
|
||||
m_headers = headers;
|
||||
m_method = method;
|
||||
m_followRedirects = followRedirects;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,7 +106,7 @@ public class HTTPRequest implements Runnable
|
||||
{
|
||||
// Queue the failure event if not.
|
||||
String error = e.getMessage();
|
||||
m_environment.queueEvent( "http_failure", new Object[] { m_urlString, error == null ? "Could not connect" : error, null } );
|
||||
m_environment.queueEvent( "http_failure", new Object[]{ m_urlString, error == null ? "Could not connect" : error, null } );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -119,6 +124,8 @@ public class HTTPRequest implements Runnable
|
||||
{
|
||||
connection.setRequestMethod( "GET" );
|
||||
}
|
||||
if( m_method != null ) connection.setRequestMethod( m_method );
|
||||
connection.setInstanceFollowRedirects( m_followRedirects );
|
||||
|
||||
// Set headers
|
||||
connection.setRequestProperty( "accept-charset", "UTF-8" );
|
||||
@@ -134,6 +141,11 @@ public class HTTPRequest implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
// Add request size and count to the tracker before opening the connection
|
||||
m_environment.addTrackingChange( TrackingField.HTTP_REQUESTS );
|
||||
m_environment.addTrackingChange( TrackingField.HTTP_UPLOAD,
|
||||
getHeaderSize( connection.getRequestProperties() ) + (m_postText == null ? 0 : m_postText.length()) );
|
||||
|
||||
// Send POST text
|
||||
if( m_postText != null )
|
||||
{
|
||||
@@ -178,6 +190,9 @@ public class HTTPRequest implements Runnable
|
||||
headers.put( header.getKey(), joiner.join( header.getValue() ) );
|
||||
}
|
||||
|
||||
m_environment.addTrackingChange( TrackingField.HTTP_DOWNLOAD,
|
||||
getHeaderSize( connection.getHeaderFields() ) + result.length );
|
||||
|
||||
InputStream contents = new ByteArrayInputStream( result );
|
||||
ILuaObject stream = wrapStream(
|
||||
m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, connection.getContentEncoding() ),
|
||||
@@ -189,17 +204,17 @@ public class HTTPRequest implements Runnable
|
||||
// Queue the appropriate event.
|
||||
if( responseSuccess )
|
||||
{
|
||||
m_environment.queueEvent( "http_success", new Object[] { m_urlString, stream } );
|
||||
m_environment.queueEvent( "http_success", new Object[]{ m_urlString, stream } );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_environment.queueEvent( "http_failure", new Object[] { m_urlString, "Could not connect", stream } );
|
||||
m_environment.queueEvent( "http_failure", new Object[]{ m_urlString, "Could not connect", stream } );
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
// There was an error
|
||||
m_environment.queueEvent( "http_failure", new Object[] { m_urlString, "Could not connect", null } );
|
||||
m_environment.queueEvent( "http_failure", new Object[]{ m_urlString, "Could not connect", null } );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,8 +224,8 @@ public class HTTPRequest implements Runnable
|
||||
final int methodOffset = oldMethods.length;
|
||||
|
||||
final String[] newMethods = Arrays.copyOf( oldMethods, oldMethods.length + 2 );
|
||||
newMethods[ methodOffset + 0 ] = "getResponseCode";
|
||||
newMethods[ methodOffset + 1 ] = "getResponseHeaders";
|
||||
newMethods[methodOffset + 0] = "getResponseCode";
|
||||
newMethods[methodOffset + 1] = "getResponseHeaders";
|
||||
|
||||
return new ILuaObject()
|
||||
{
|
||||
@@ -233,12 +248,12 @@ public class HTTPRequest implements Runnable
|
||||
case 0:
|
||||
{
|
||||
// getResponseCode
|
||||
return new Object[] { responseCode };
|
||||
return new Object[]{ responseCode };
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// getResponseHeaders
|
||||
return new Object[] { responseHeaders };
|
||||
return new Object[]{ responseHeaders };
|
||||
}
|
||||
default:
|
||||
{
|
||||
@@ -248,4 +263,15 @@ public class HTTPRequest implements Runnable
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static long getHeaderSize( Map<String, List<String>> headers )
|
||||
{
|
||||
long size = 0;
|
||||
for( Map.Entry<String, List<String>> header : headers.entrySet() )
|
||||
{
|
||||
size += header.getKey() == null ? 0 : header.getKey().length();
|
||||
for( String value : header.getValue() ) size += value == null ? 0 : value.length() + 1;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@ import dan200.computercraft.api.lua.ILuaObject;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.apis.HTTPAPI;
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
@@ -71,7 +72,7 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp
|
||||
private void onClosed()
|
||||
{
|
||||
close( true );
|
||||
computer.queueEvent( CLOSE_EVENT, new Object[] { url } );
|
||||
computer.queueEvent( CLOSE_EVENT, new Object[]{ url } );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,7 +103,7 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp
|
||||
if( !handshaker.isHandshakeComplete() )
|
||||
{
|
||||
handshaker.finishHandshake( ch, (FullHttpResponse) msg );
|
||||
computer.queueEvent( SUCCESS_EVENT, new Object[] { url, this } );
|
||||
computer.queueEvent( SUCCESS_EVENT, new Object[]{ url, this } );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -115,14 +116,19 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp
|
||||
WebSocketFrame frame = (WebSocketFrame) msg;
|
||||
if( frame instanceof TextWebSocketFrame )
|
||||
{
|
||||
computer.queueEvent( MESSAGE_EVENT, new Object[] { url, ((TextWebSocketFrame) frame).text() } );
|
||||
String data = ((TextWebSocketFrame) frame).text();
|
||||
|
||||
computer.addTrackingChange( TrackingField.WEBSOCKET_INCOMING, data.length() );
|
||||
computer.queueEvent( MESSAGE_EVENT, new Object[]{ url, data } );
|
||||
}
|
||||
else if( frame instanceof BinaryWebSocketFrame )
|
||||
{
|
||||
ByteBuf data = frame.content();
|
||||
byte[] converted = new byte[ data.readableBytes() ];
|
||||
byte[] converted = new byte[data.readableBytes()];
|
||||
data.readBytes( converted );
|
||||
computer.queueEvent( MESSAGE_EVENT, new Object[] { url, data } );
|
||||
|
||||
computer.addTrackingChange( TrackingField.WEBSOCKET_INCOMING, converted.length );
|
||||
computer.queueEvent( MESSAGE_EVENT, new Object[]{ url, converted } );
|
||||
}
|
||||
else if( frame instanceof CloseWebSocketFrame )
|
||||
{
|
||||
@@ -135,7 +141,7 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp
|
||||
public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause )
|
||||
{
|
||||
ctx.close();
|
||||
computer.queueEvent( FAILURE_EVENT, new Object[] {
|
||||
computer.queueEvent( FAILURE_EVENT, new Object[]{
|
||||
url,
|
||||
cause instanceof WebSocketHandshakeException ? cause.getMessage() : "Could not connect"
|
||||
} );
|
||||
@@ -145,7 +151,7 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
return new String[] { "receive", "send", "close" };
|
||||
return new String[]{ "receive", "send", "close" };
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -159,15 +165,16 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp
|
||||
{
|
||||
checkOpen();
|
||||
Object[] event = context.pullEvent( MESSAGE_EVENT );
|
||||
if( event.length >= 3 && Objects.equal( event[ 1 ], url ) )
|
||||
if( event.length >= 3 && Objects.equal( event[1], url ) )
|
||||
{
|
||||
return new Object[] { event[ 2 ] };
|
||||
return new Object[]{ event[2] };
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
checkOpen();
|
||||
String text = arguments.length > 0 && arguments[ 0 ] != null ? arguments[ 0 ].toString() : "";
|
||||
String text = arguments.length > 0 && arguments[0] != null ? arguments[0].toString() : "";
|
||||
computer.addTrackingChange( TrackingField.WEBSOCKET_OUTGOING, text.length() );
|
||||
channel.writeAndFlush( new TextWebSocketFrame( text ) );
|
||||
return null;
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
|
||||
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
|
||||
@@ -34,7 +35,7 @@ import java.security.KeyStore;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/*
|
||||
/**
|
||||
* Provides functionality to verify and connect to a remote websocket.
|
||||
*/
|
||||
public final class WebsocketConnector
|
||||
@@ -177,7 +178,7 @@ public final class WebsocketConnector
|
||||
httpHeaders.add( header.getKey(), header.getValue() );
|
||||
}
|
||||
|
||||
WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker( uri, WebSocketVersion.V13, null, false, httpHeaders );
|
||||
WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker( uri, WebSocketVersion.V13, null, true, httpHeaders );
|
||||
final WebsocketConnection connection = new WebsocketConnection( environment, api, handshaker, address );
|
||||
|
||||
new Bootstrap()
|
||||
@@ -190,7 +191,12 @@ public final class WebsocketConnector
|
||||
{
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
if( ssl != null ) p.addLast( ssl.newHandler( ch.alloc(), uri.getHost(), port ) );
|
||||
p.addLast( new HttpClientCodec(), new HttpObjectAggregator( 8192 ), connection );
|
||||
p.addLast(
|
||||
new HttpClientCodec(),
|
||||
new HttpObjectAggregator( 8192 ),
|
||||
WebSocketClientCompressionHandler.INSTANCE,
|
||||
connection
|
||||
);
|
||||
}
|
||||
} )
|
||||
.remoteAddress( socketAddress )
|
||||
|
@@ -20,6 +20,8 @@ import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
import dan200.computercraft.core.lua.CobaltLuaMachine;
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -167,6 +169,12 @@ public class Computer
|
||||
m_computer.setLabel( label );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTrackingChange( TrackingField field, long change )
|
||||
{
|
||||
Tracking.addValue( m_computer, field, change );
|
||||
}
|
||||
|
||||
public void onPeripheralChanged( int side, IPeripheral peripheral )
|
||||
{
|
||||
synchronized( m_computer.m_peripherals )
|
||||
|
@@ -7,6 +7,7 @@
|
||||
package dan200.computercraft.core.computer;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -68,17 +69,17 @@ public class ComputerThread
|
||||
s_stopped = false;
|
||||
if( s_threads == null || s_threads.length != ComputerCraft.computer_threads )
|
||||
{
|
||||
s_threads = new Thread[ ComputerCraft.computer_threads ];
|
||||
s_threads = new Thread[ComputerCraft.computer_threads];
|
||||
}
|
||||
|
||||
SecurityManager manager = System.getSecurityManager();
|
||||
final ThreadGroup group = manager == null ? Thread.currentThread().getThreadGroup() : manager.getThreadGroup();
|
||||
for( int i = 0; i < s_threads.length; i++ )
|
||||
{
|
||||
Thread thread = s_threads[ i ];
|
||||
Thread thread = s_threads[i];
|
||||
if( thread == null || !thread.isAlive() )
|
||||
{
|
||||
thread = s_threads[ i ] = new Thread( group, new TaskExecutor(), "ComputerCraft-Computer-Manager-" + s_ManagerCounter.getAndIncrement() );
|
||||
thread = s_threads[i] = new Thread( group, new TaskExecutor(), "ComputerCraft-Computer-Manager-" + s_ManagerCounter.getAndIncrement() );
|
||||
thread.setDaemon( true );
|
||||
thread.start();
|
||||
}
|
||||
@@ -223,6 +224,23 @@ public class ComputerThread
|
||||
// Interrupt the thread
|
||||
if( !done )
|
||||
{
|
||||
StringBuilder builder = new StringBuilder( "Terminating " );
|
||||
if( computer != null )
|
||||
{
|
||||
builder.append( "computer " ).append( computer.getID() );
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.append( "unknown computer" );
|
||||
}
|
||||
|
||||
builder.append( ". Thread is currently running" );
|
||||
for( StackTraceElement element : thread.getStackTrace() )
|
||||
{
|
||||
builder.append( "\n at " ).append( element );
|
||||
}
|
||||
ComputerCraft.log.error( builder.toString() );
|
||||
|
||||
thread.interrupt();
|
||||
thread = null;
|
||||
runner = null;
|
||||
@@ -233,8 +251,8 @@ public class ComputerThread
|
||||
{
|
||||
long stop = System.nanoTime();
|
||||
Computer computer = task.getOwner();
|
||||
if( computer != null ) ComputerTimeTracker.addTiming( computer, stop - start );
|
||||
|
||||
if( computer != null ) Tracking.addTaskTiming( computer, stop - start );
|
||||
|
||||
// Re-add it back onto the queue or remove it
|
||||
synchronized( s_taskLock )
|
||||
{
|
||||
|
@@ -1,116 +0,0 @@
|
||||
package dan200.computercraft.core.computer;
|
||||
|
||||
import com.google.common.collect.MapMaker;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Tracks timing information about computers, including how long they ran for
|
||||
* and the number of events they handled.
|
||||
*
|
||||
* Note that this <em>will</em> track computers which have been deleted (hence
|
||||
* the presence of {@link #timingLookup} and {@link #timings}
|
||||
*/
|
||||
public class ComputerTimeTracker
|
||||
{
|
||||
public static class Timings
|
||||
{
|
||||
private final WeakReference<Computer> computer;
|
||||
private final int computerId;
|
||||
|
||||
private int tasks;
|
||||
|
||||
private long totalTime;
|
||||
private long maxTime;
|
||||
|
||||
public Timings( @Nonnull Computer computer )
|
||||
{
|
||||
this.computer = new WeakReference<>( computer );
|
||||
this.computerId = computer.getID();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Computer getComputer()
|
||||
{
|
||||
return computer.get();
|
||||
}
|
||||
|
||||
public int getComputerId()
|
||||
{
|
||||
return computerId;
|
||||
}
|
||||
|
||||
public int getTasks()
|
||||
{
|
||||
return tasks;
|
||||
}
|
||||
|
||||
public long getTotalTime()
|
||||
{
|
||||
return totalTime;
|
||||
}
|
||||
|
||||
public long getMaxTime()
|
||||
{
|
||||
return maxTime;
|
||||
}
|
||||
|
||||
public double getAverage()
|
||||
{
|
||||
return totalTime / (double) tasks;
|
||||
}
|
||||
|
||||
void update( long time )
|
||||
{
|
||||
tasks++;
|
||||
totalTime += time;
|
||||
if( time > maxTime ) maxTime = time;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean tracking;
|
||||
private static final List<Timings> timings = new ArrayList<>();
|
||||
private static final Map<Computer, Timings> timingLookup = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
public synchronized static void start()
|
||||
{
|
||||
tracking = true;
|
||||
|
||||
timings.clear();
|
||||
timingLookup.clear();
|
||||
}
|
||||
|
||||
public synchronized static boolean stop()
|
||||
{
|
||||
if( !tracking ) return false;
|
||||
|
||||
tracking = false;
|
||||
timingLookup.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static synchronized List<Timings> getTimings()
|
||||
{
|
||||
return new ArrayList<>( timings );
|
||||
}
|
||||
|
||||
public static synchronized void addTiming( Computer computer, long time )
|
||||
{
|
||||
if( !tracking ) return;
|
||||
|
||||
Timings timings = ComputerTimeTracker.timingLookup.get( computer );
|
||||
if( timings == null )
|
||||
{
|
||||
timings = new Timings( computer );
|
||||
timingLookup.put( computer, timings );
|
||||
ComputerTimeTracker.timings.add( timings );
|
||||
}
|
||||
|
||||
timings.update( time );
|
||||
}
|
||||
}
|
@@ -6,14 +6,17 @@
|
||||
|
||||
package dan200.computercraft.core.computer;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
|
||||
public class MainThread
|
||||
{
|
||||
private static final int MAX_TASKS_PER_TICK = 1000;
|
||||
private static final int MAX_TASKS_TOTAL = 50000;
|
||||
|
||||
private static final LinkedList<ITask> m_outstandingTasks = new LinkedList<>();
|
||||
private static final Queue<ITask> m_outstandingTasks = new ArrayDeque<>();
|
||||
private static final Object m_nextUnusedTaskIDLock = new Object();
|
||||
private static long m_nextUnusedTaskID = 0;
|
||||
|
||||
@@ -31,7 +34,7 @@ public class MainThread
|
||||
{
|
||||
if( m_outstandingTasks.size() < MAX_TASKS_TOTAL )
|
||||
{
|
||||
m_outstandingTasks.addLast( task );
|
||||
m_outstandingTasks.offer( task );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -46,14 +49,17 @@ public class MainThread
|
||||
ITask task = null;
|
||||
synchronized( m_outstandingTasks )
|
||||
{
|
||||
if( m_outstandingTasks.size() > 0 )
|
||||
{
|
||||
task = m_outstandingTasks.removeFirst();
|
||||
}
|
||||
task = m_outstandingTasks.poll();
|
||||
}
|
||||
if( task != null )
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
task.execute();
|
||||
|
||||
long stop = System.nanoTime();
|
||||
Computer computer = task.getOwner();
|
||||
if( computer != null ) Tracking.addServerTiming( computer, stop - start );
|
||||
|
||||
++tasksThisTick;
|
||||
}
|
||||
else
|
||||
|
@@ -56,7 +56,7 @@ public class Terminal
|
||||
m_palette = new Palette();
|
||||
}
|
||||
|
||||
public void reset()
|
||||
public synchronized void reset()
|
||||
{
|
||||
m_cursorColour = 0;
|
||||
m_cursorBackgroundColour = 15;
|
||||
@@ -76,7 +76,7 @@ public class Terminal
|
||||
return m_height;
|
||||
}
|
||||
|
||||
public void resize( int width, int height )
|
||||
public synchronized void resize( int width, int height )
|
||||
{
|
||||
if( width == m_width && height == m_height )
|
||||
{
|
||||
@@ -189,7 +189,7 @@ public class Terminal
|
||||
return m_palette;
|
||||
}
|
||||
|
||||
public void blit( String text, String textColour, String backgroundColour )
|
||||
public synchronized void blit( String text, String textColour, String backgroundColour )
|
||||
{
|
||||
int x = m_cursorX;
|
||||
int y = m_cursorY;
|
||||
@@ -202,7 +202,7 @@ public class Terminal
|
||||
}
|
||||
}
|
||||
|
||||
public void write( String text )
|
||||
public synchronized void write( String text )
|
||||
{
|
||||
int x = m_cursorX;
|
||||
int y = m_cursorY;
|
||||
@@ -215,7 +215,7 @@ public class Terminal
|
||||
}
|
||||
}
|
||||
|
||||
public void scroll( int yDiff )
|
||||
public synchronized void scroll( int yDiff )
|
||||
{
|
||||
if( yDiff != 0 )
|
||||
{
|
||||
@@ -245,7 +245,7 @@ public class Terminal
|
||||
}
|
||||
}
|
||||
|
||||
public void clear()
|
||||
public synchronized void clear()
|
||||
{
|
||||
for( int y = 0; y < m_height; ++y )
|
||||
{
|
||||
@@ -256,7 +256,7 @@ public class Terminal
|
||||
m_changed = true;
|
||||
}
|
||||
|
||||
public void clearLine()
|
||||
public synchronized void clearLine()
|
||||
{
|
||||
int y = m_cursorY;
|
||||
if( y >= 0 && y < m_height )
|
||||
@@ -268,7 +268,7 @@ public class Terminal
|
||||
}
|
||||
}
|
||||
|
||||
public TextBuffer getLine( int y )
|
||||
public synchronized TextBuffer getLine( int y )
|
||||
{
|
||||
if( y >= 0 && y < m_height )
|
||||
{
|
||||
@@ -277,7 +277,7 @@ public class Terminal
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setLine( int y, String text, String textColour, String backgroundColour )
|
||||
public synchronized void setLine( int y, String text, String textColour, String backgroundColour )
|
||||
{
|
||||
m_text[y].write( text );
|
||||
m_textColour[y].write( textColour );
|
||||
@@ -285,7 +285,7 @@ public class Terminal
|
||||
m_changed = true;
|
||||
}
|
||||
|
||||
public TextBuffer getTextColourLine( int y )
|
||||
public synchronized TextBuffer getTextColourLine( int y )
|
||||
{
|
||||
if( y>=0 && y<m_height )
|
||||
{
|
||||
@@ -294,7 +294,7 @@ public class Terminal
|
||||
return null;
|
||||
}
|
||||
|
||||
public TextBuffer getBackgroundColourLine( int y )
|
||||
public synchronized TextBuffer getBackgroundColourLine( int y )
|
||||
{
|
||||
if( y>=0 && y<m_height )
|
||||
{
|
||||
@@ -318,7 +318,7 @@ public class Terminal
|
||||
m_changed = false;
|
||||
}
|
||||
|
||||
public NBTTagCompound writeToNBT( NBTTagCompound nbttagcompound )
|
||||
public synchronized NBTTagCompound writeToNBT( NBTTagCompound nbttagcompound )
|
||||
{
|
||||
nbttagcompound.setInteger( "term_cursorX", m_cursorX );
|
||||
nbttagcompound.setInteger( "term_cursorY", m_cursorY );
|
||||
@@ -338,7 +338,7 @@ public class Terminal
|
||||
return nbttagcompound;
|
||||
}
|
||||
|
||||
public void readFromNBT( NBTTagCompound nbttagcompound )
|
||||
public synchronized void readFromNBT( NBTTagCompound nbttagcompound )
|
||||
{
|
||||
m_cursorX = nbttagcompound.getInteger( "term_cursorX" );
|
||||
m_cursorY = nbttagcompound.getInteger( "term_cursorY" );
|
||||
|
@@ -0,0 +1,117 @@
|
||||
package dan200.computercraft.core.tracking;
|
||||
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import gnu.trove.map.hash.TObjectLongHashMap;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class ComputerTracker
|
||||
{
|
||||
private final WeakReference<Computer> computer;
|
||||
private final int computerId;
|
||||
|
||||
private long tasks;
|
||||
private long totalTime;
|
||||
private long maxTime;
|
||||
|
||||
private long serverCount;
|
||||
private long serverTime;
|
||||
|
||||
private final TObjectLongHashMap<TrackingField> fields;
|
||||
|
||||
public ComputerTracker( Computer computer )
|
||||
{
|
||||
this.computer = new WeakReference<>( computer );
|
||||
this.computerId = computer.getID();
|
||||
this.fields = new TObjectLongHashMap<>();
|
||||
}
|
||||
|
||||
ComputerTracker( ComputerTracker timings )
|
||||
{
|
||||
this.computer = timings.computer;
|
||||
this.computerId = timings.computerId;
|
||||
|
||||
this.tasks = timings.tasks;
|
||||
this.totalTime = timings.totalTime;
|
||||
this.maxTime = timings.maxTime;
|
||||
|
||||
this.serverCount = timings.serverCount;
|
||||
this.serverTime = timings.serverTime;
|
||||
|
||||
this.fields = new TObjectLongHashMap<>( timings.fields );
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Computer getComputer()
|
||||
{
|
||||
return computer.get();
|
||||
}
|
||||
|
||||
public int getComputerId()
|
||||
{
|
||||
return computerId;
|
||||
}
|
||||
|
||||
public long getTasks()
|
||||
{
|
||||
return tasks;
|
||||
}
|
||||
|
||||
public long getTotalTime()
|
||||
{
|
||||
return totalTime;
|
||||
}
|
||||
|
||||
public long getMaxTime()
|
||||
{
|
||||
return maxTime;
|
||||
}
|
||||
|
||||
public long getAverage()
|
||||
{
|
||||
return totalTime / tasks;
|
||||
}
|
||||
|
||||
void addTaskTiming( long time )
|
||||
{
|
||||
tasks++;
|
||||
totalTime += time;
|
||||
if( time > maxTime ) maxTime = time;
|
||||
}
|
||||
|
||||
void addMainTiming( long time )
|
||||
{
|
||||
serverCount++;
|
||||
serverTime += time;
|
||||
}
|
||||
|
||||
void addValue( TrackingField field, long change )
|
||||
{
|
||||
synchronized( fields )
|
||||
{
|
||||
fields.adjustOrPutValue( field, change, change );
|
||||
}
|
||||
}
|
||||
|
||||
public long get( TrackingField field )
|
||||
{
|
||||
if( field == TrackingField.TASKS ) return tasks;
|
||||
if( field == TrackingField.MAX_TIME ) return maxTime;
|
||||
if( field == TrackingField.TOTAL_TIME ) return totalTime;
|
||||
if( field == TrackingField.AVERAGE_TIME ) return tasks == 0 ? 0 : totalTime / tasks;
|
||||
|
||||
if( field == TrackingField.SERVER_COUNT ) return serverCount;
|
||||
if( field == TrackingField.SERVER_TIME ) return serverTime;
|
||||
|
||||
synchronized( fields )
|
||||
{
|
||||
return fields.get( field );
|
||||
}
|
||||
}
|
||||
|
||||
public String getFormatted( TrackingField field )
|
||||
{
|
||||
return field.format( get( field ) );
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
package dan200.computercraft.core.tracking;
|
||||
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
|
||||
public interface Tracker
|
||||
{
|
||||
@Deprecated
|
||||
default void addTiming( Computer computer, long time )
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Report how long a task executed on the computer thread took.
|
||||
*
|
||||
* Computer thread tasks include events or a computer being turned on/off.
|
||||
*
|
||||
* @param computer The computer processing this task
|
||||
* @param time The time taken for this task.
|
||||
*/
|
||||
default void addTaskTiming( Computer computer, long time )
|
||||
{
|
||||
//noinspection deprecation
|
||||
addTiming( computer, time );
|
||||
}
|
||||
|
||||
/**
|
||||
* Report how long a task executed on the server thread took.
|
||||
*
|
||||
* Server tasks include actions performed by peripherals.
|
||||
*
|
||||
* @param computer The computer processing this task
|
||||
* @param time The time taken for this task.
|
||||
*/
|
||||
default void addServerTiming( Computer computer, long time )
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment an arbitrary field by some value. Implementations may track how often this is called
|
||||
* as well as the change, to compute some level of "average".
|
||||
*
|
||||
* @param computer The computer to increment
|
||||
* @param field The field to increment.
|
||||
* @param change The amount to increment said field by.
|
||||
*/
|
||||
default void addValue( Computer computer, TrackingField field, long change )
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
package dan200.computercraft.core.tracking;
|
||||
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class Tracking
|
||||
{
|
||||
static final AtomicInteger tracking = new AtomicInteger( 0 );
|
||||
|
||||
private static final Object lock = new Object();
|
||||
private static final HashMap<UUID, TrackingContext> contexts = new HashMap<>();
|
||||
private static final List<Tracker> trackers = new ArrayList<>();
|
||||
|
||||
public static TrackingContext getContext( UUID uuid )
|
||||
{
|
||||
synchronized( lock )
|
||||
{
|
||||
TrackingContext context = contexts.get( uuid );
|
||||
if( context == null ) contexts.put( uuid, context = new TrackingContext() );
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
public static void add( Tracker tracker )
|
||||
{
|
||||
synchronized( lock )
|
||||
{
|
||||
trackers.add( tracker );
|
||||
tracking.incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
public static void addTaskTiming( Computer computer, long time )
|
||||
{
|
||||
if( tracking.get() == 0 ) return;
|
||||
|
||||
synchronized( contexts )
|
||||
{
|
||||
for( TrackingContext context : contexts.values() ) context.addTaskTiming( computer, time );
|
||||
for( Tracker tracker : trackers ) tracker.addTaskTiming( computer, time );
|
||||
}
|
||||
}
|
||||
|
||||
public static void addServerTiming( Computer computer, long time )
|
||||
{
|
||||
if( tracking.get() == 0 ) return;
|
||||
|
||||
synchronized( contexts )
|
||||
{
|
||||
for( TrackingContext context : contexts.values() ) context.addServerTiming( computer, time );
|
||||
for( Tracker tracker : trackers ) tracker.addServerTiming( computer, time );
|
||||
}
|
||||
}
|
||||
|
||||
public static void addValue( Computer computer, TrackingField field, long change )
|
||||
{
|
||||
if( tracking.get() == 0 ) return;
|
||||
|
||||
synchronized( lock )
|
||||
{
|
||||
for( TrackingContext context : contexts.values() ) context.addValue( computer, field, change );
|
||||
for( Tracker tracker : trackers ) tracker.addValue( computer, field, change );
|
||||
}
|
||||
}
|
||||
|
||||
public static void reset()
|
||||
{
|
||||
synchronized( lock )
|
||||
{
|
||||
contexts.clear();
|
||||
trackers.clear();
|
||||
tracking.set( 0 );
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,111 @@
|
||||
package dan200.computercraft.core.tracking;
|
||||
|
||||
import com.google.common.collect.MapMaker;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Tracks timing information about computers, including how long they ran for
|
||||
* and the number of events they handled.
|
||||
*
|
||||
* Note that this <em>will</em> track computers which have been deleted (hence
|
||||
* the presence of {@link #timingLookup} and {@link #timings}
|
||||
*/
|
||||
public class TrackingContext implements Tracker
|
||||
{
|
||||
private boolean tracking = false;
|
||||
|
||||
private final List<ComputerTracker> timings = new ArrayList<>();
|
||||
private final Map<Computer, ComputerTracker> timingLookup = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
public synchronized void start()
|
||||
{
|
||||
if( !tracking ) Tracking.tracking.incrementAndGet();
|
||||
tracking = true;
|
||||
|
||||
timings.clear();
|
||||
timingLookup.clear();
|
||||
}
|
||||
|
||||
public synchronized boolean stop()
|
||||
{
|
||||
if( !tracking ) return false;
|
||||
|
||||
Tracking.tracking.decrementAndGet();
|
||||
tracking = false;
|
||||
timingLookup.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized List<ComputerTracker> getImmutableTimings()
|
||||
{
|
||||
ArrayList<ComputerTracker> timings = new ArrayList<>( this.timings.size() );
|
||||
for( ComputerTracker timing : this.timings ) timings.add( new ComputerTracker( timing ) );
|
||||
return timings;
|
||||
}
|
||||
|
||||
public synchronized List<ComputerTracker> getTimings()
|
||||
{
|
||||
return new ArrayList<>( timings );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTaskTiming( Computer computer, long time )
|
||||
{
|
||||
if( !tracking ) return;
|
||||
|
||||
synchronized( this )
|
||||
{
|
||||
ComputerTracker computerTimings = timingLookup.get( computer );
|
||||
if( computerTimings == null )
|
||||
{
|
||||
computerTimings = new ComputerTracker( computer );
|
||||
timingLookup.put( computer, computerTimings );
|
||||
timings.add( computerTimings );
|
||||
}
|
||||
|
||||
computerTimings.addTaskTiming( time );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addServerTiming( Computer computer, long time )
|
||||
{
|
||||
if( !tracking ) return;
|
||||
|
||||
synchronized( this )
|
||||
{
|
||||
ComputerTracker computerTimings = timingLookup.get( computer );
|
||||
if( computerTimings == null )
|
||||
{
|
||||
computerTimings = new ComputerTracker( computer );
|
||||
timingLookup.put( computer, computerTimings );
|
||||
timings.add( computerTimings );
|
||||
}
|
||||
|
||||
computerTimings.addMainTiming( time );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addValue( Computer computer, TrackingField field, long change )
|
||||
{
|
||||
if( !tracking ) return;
|
||||
|
||||
synchronized( this )
|
||||
{
|
||||
ComputerTracker computerTimings = timingLookup.get( computer );
|
||||
if( computerTimings == null )
|
||||
{
|
||||
computerTimings = new ComputerTracker( computer );
|
||||
timingLookup.put( computer, computerTimings );
|
||||
timings.add( computerTimings );
|
||||
}
|
||||
|
||||
computerTimings.addValue( field, change );
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
package dan200.computercraft.core.tracking;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.LongFunction;
|
||||
|
||||
public class TrackingField
|
||||
{
|
||||
private static final Map<String, TrackingField> fields = new HashMap<>();
|
||||
|
||||
public static final TrackingField TASKS = TrackingField.of( "tasks", "Tasks", x -> String.format( "%4d", x ) );
|
||||
public static final TrackingField TOTAL_TIME = TrackingField.of( "total", "Total time", x -> String.format( "%7.1fms", x / 1e6 ) );
|
||||
public static final TrackingField AVERAGE_TIME = TrackingField.of( "average", "Average time", x -> String.format( "%4.1fms", x / 1e6 ) );
|
||||
public static final TrackingField MAX_TIME = TrackingField.of( "max", "Max time", x -> String.format( "%5.1fms", x / 1e6 ) );
|
||||
|
||||
public static final TrackingField SERVER_COUNT = TrackingField.of( "server_count", "Server task count", x -> String.format( "%4d", x ) );
|
||||
public static final TrackingField SERVER_TIME = TrackingField.of( "server_time", "Server task time", x -> String.format( "%7.1fms", x / 1e6 ) );
|
||||
|
||||
public static final TrackingField PERIPHERAL_OPS = TrackingField.of( "peripheral", "Peripheral calls", TrackingField::formatDefault );
|
||||
public static final TrackingField FS_OPS = TrackingField.of( "fs", "Filesystem operations", TrackingField::formatDefault );
|
||||
public static final TrackingField TURTLE_OPS = TrackingField.of( "turtle", "Turtle operations", TrackingField::formatDefault );
|
||||
|
||||
public static final TrackingField HTTP_REQUESTS = TrackingField.of( "http", "HTTP requests", TrackingField::formatDefault );
|
||||
public static final TrackingField HTTP_UPLOAD = TrackingField.of( "http_upload", "HTTP upload", TrackingField::formatBytes );
|
||||
public static final TrackingField HTTP_DOWNLOAD = TrackingField.of( "http_download", "HTTT download", TrackingField::formatBytes );
|
||||
|
||||
public static final TrackingField WEBSOCKET_INCOMING = TrackingField.of( "websocket_incoming", "Websocket incoming", TrackingField::formatBytes );
|
||||
public static final TrackingField WEBSOCKET_OUTGOING = TrackingField.of( "websocket_outgoing", "Websocket outgoing", TrackingField::formatBytes );
|
||||
|
||||
private final String id;
|
||||
private final String displayName;
|
||||
private final LongFunction<String> format;
|
||||
|
||||
public String id()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public String displayName()
|
||||
{
|
||||
return displayName;
|
||||
}
|
||||
|
||||
private TrackingField( String id, String displayName, LongFunction<String> format )
|
||||
{
|
||||
this.id = id;
|
||||
this.displayName = displayName;
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public String format( long value )
|
||||
{
|
||||
return format.apply( value );
|
||||
}
|
||||
|
||||
public static TrackingField of( String id, String displayName, LongFunction<String> format )
|
||||
{
|
||||
TrackingField field = new TrackingField( id, displayName, format );
|
||||
fields.put( id, field );
|
||||
return field;
|
||||
}
|
||||
|
||||
public static Map<String, TrackingField> fields()
|
||||
{
|
||||
return Collections.unmodifiableMap( fields );
|
||||
}
|
||||
|
||||
private static String formatDefault( long value )
|
||||
{
|
||||
return String.format( "%6d", value );
|
||||
}
|
||||
|
||||
/**
|
||||
* So technically a kibibyte, but let's not argue here.
|
||||
*/
|
||||
private static final int KILOBYTE_SIZE = 1024;
|
||||
|
||||
private static final String SI_PREFIXES = "KMGT";
|
||||
|
||||
private static String formatBytes( long bytes )
|
||||
{
|
||||
if( bytes < 1024 ) return String.format( "%10d B", bytes );
|
||||
int exp = (int) (Math.log( bytes ) / Math.log( KILOBYTE_SIZE ));
|
||||
if( exp > SI_PREFIXES.length() ) exp = SI_PREFIXES.length();
|
||||
return String.format( "%10.1f %siB", bytes / Math.pow( KILOBYTE_SIZE, exp ), SI_PREFIXES.charAt( exp - 1 ) );
|
||||
}
|
||||
}
|
@@ -4,9 +4,13 @@ import com.google.common.collect.Sets;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.ComputerTimeTracker;
|
||||
import dan200.computercraft.core.tracking.ComputerTracker;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
import dan200.computercraft.core.tracking.TrackingContext;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
import dan200.computercraft.shared.command.framework.*;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import net.minecraft.command.CommandBase;
|
||||
import net.minecraft.command.CommandException;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -25,6 +29,12 @@ import static dan200.computercraft.shared.command.framework.ChatHelpers.*;
|
||||
|
||||
public final class CommandComputerCraft extends CommandDelegate
|
||||
{
|
||||
public static final UUID SYSTEM_UUID = new UUID( 0, 0 );
|
||||
|
||||
private static final int DUMP_LIST_ID = 5373952;
|
||||
private static final int DUMP_SINGLE_ID = 1844510720;
|
||||
private static final int TRACK_ID = 373882880;
|
||||
|
||||
public CommandComputerCraft()
|
||||
{
|
||||
super( create() );
|
||||
@@ -49,7 +59,7 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
{
|
||||
if( arguments.size() == 0 )
|
||||
{
|
||||
TextTable table = new TextTable( "Instance", "Id", "On", "Position" );
|
||||
TextTable table = new TextTable( DUMP_LIST_ID, "Instance", "Id", "On", "Position" );
|
||||
|
||||
List<ServerComputer> computers = new ArrayList<>( ComputerCraft.serverComputerRegistry.getComputers() );
|
||||
|
||||
@@ -95,7 +105,7 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
{
|
||||
ServerComputer computer = ComputerSelector.getComputer( arguments.get( 0 ) );
|
||||
|
||||
TextTable table = new TextTable();
|
||||
TextTable table = new TextTable( DUMP_SINGLE_ID );
|
||||
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() ) );
|
||||
@@ -311,7 +321,7 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
@Override
|
||||
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments )
|
||||
{
|
||||
ComputerTimeTracker.start();
|
||||
getTimingContext( context ).start();
|
||||
|
||||
String stopCommand = "/" + context.parent().getFullPath() + " stop";
|
||||
context.getSender().sendMessage( list(
|
||||
@@ -330,23 +340,54 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
@Override
|
||||
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
|
||||
{
|
||||
if( !ComputerTimeTracker.stop() ) throw new CommandException( "Tracking not enabled" );
|
||||
displayTimings( context );
|
||||
TrackingContext timings = getTimingContext( context );
|
||||
if( !timings.stop() ) throw new CommandException( "Tracking not enabled" );
|
||||
displayTimings( context, timings.getImmutableTimings(), TrackingField.AVERAGE_TIME );
|
||||
}
|
||||
} );
|
||||
|
||||
track.register( new SubCommandBase(
|
||||
"dump", "Dump the latest track results", UserLevel.OWNER_OP,
|
||||
"dump", "[kind]", "Dump the latest track results", UserLevel.OWNER_OP,
|
||||
"Dump the latest results of computer tracking."
|
||||
)
|
||||
{
|
||||
@Override
|
||||
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
|
||||
{
|
||||
displayTimings( context );
|
||||
TrackingField field = TrackingField.AVERAGE_TIME;
|
||||
if( arguments.size() >= 1 )
|
||||
{
|
||||
field = TrackingField.fields().get( arguments.get( 0 ) );
|
||||
if( field == null ) throw new CommandException( "Unknown field '" + arguments.get( 0 ) + "'" );
|
||||
}
|
||||
|
||||
displayTimings( context, getTimingContext( context ).getImmutableTimings(), field );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments )
|
||||
{
|
||||
if( arguments.size() == 1 )
|
||||
{
|
||||
String match = arguments.get( 0 );
|
||||
|
||||
List<String> out = new ArrayList<>();
|
||||
for( String key : TrackingField.fields().keySet() )
|
||||
{
|
||||
if( CommandBase.doesStringStartWith( match, key ) ) out.add( key );
|
||||
}
|
||||
|
||||
out.sort( Comparator.naturalOrder() );
|
||||
return out;
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.getCompletion( context, arguments );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
|
||||
root.register( new SubCommandBase(
|
||||
"reload", "Reload the ComputerCraft config file", UserLevel.OWNER_OP,
|
||||
"Reload the ComputerCraft config file"
|
||||
@@ -390,13 +431,22 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
}
|
||||
}
|
||||
|
||||
private static void displayTimings( CommandContext context ) throws CommandException
|
||||
private static TrackingContext getTimingContext( CommandContext context )
|
||||
{
|
||||
List<ComputerTimeTracker.Timings> timings = ComputerTimeTracker.getTimings();
|
||||
if( timings.isEmpty() ) throw new CommandException( "No timings available" );
|
||||
Entity entity = context.getSender().getCommandSenderEntity();
|
||||
if( entity instanceof EntityPlayerMP )
|
||||
{
|
||||
return Tracking.getContext( entity.getUniqueID() );
|
||||
}
|
||||
else
|
||||
{
|
||||
return Tracking.getContext( SYSTEM_UUID );
|
||||
}
|
||||
}
|
||||
|
||||
timings.sort( Comparator.comparing( ComputerTimeTracker.Timings::getAverage ).reversed() );
|
||||
TextTable table = new TextTable( "Computer", "Tasks", "Total", "Average", "Maximum" );
|
||||
private static void displayTimings( CommandContext context, List<ComputerTracker> timings, TrackingField field ) throws CommandException
|
||||
{
|
||||
if( timings.isEmpty() ) throw new CommandException( "No timings available" );
|
||||
|
||||
Map<Computer, ServerComputer> lookup = new HashMap<>();
|
||||
int maxId = 0, maxInstance = 0;
|
||||
@@ -411,7 +461,17 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
ICommandSender sender = context.getSender();
|
||||
boolean isPlayer = sender instanceof EntityPlayerMP && !(sender instanceof FakePlayer);
|
||||
|
||||
for( ComputerTimeTracker.Timings entry : timings )
|
||||
timings.sort( Comparator.<ComputerTracker, Long>comparing( x -> x.get( field ) ).reversed() );
|
||||
|
||||
boolean defaultLayout = field == TrackingField.TASKS || field == TrackingField.TOTAL_TIME
|
||||
|| field == TrackingField.AVERAGE_TIME || field == TrackingField.MAX_TIME;
|
||||
|
||||
|
||||
TextTable table = defaultLayout
|
||||
? new TextTable( TRACK_ID, "Computer", "Tasks", "Total", "Average", "Maximum" )
|
||||
: new TextTable( TRACK_ID, "Computer", field.displayName() );
|
||||
|
||||
for( ComputerTracker entry : timings )
|
||||
{
|
||||
Computer computer = entry.getComputer();
|
||||
ServerComputer serverComputer = computer == null ? null : lookup.get( computer );
|
||||
@@ -437,13 +497,20 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
) );
|
||||
}
|
||||
|
||||
table.addRow(
|
||||
computerComponent,
|
||||
formatted( "%4d", entry.getTasks() ),
|
||||
text( String.format( "%7.1f", entry.getTotalTime() / 1e6 ) + "ms" ),
|
||||
text( String.format( "%4.1f", entry.getAverage() / 1e6 ) + "ms" ),
|
||||
text( String.format( "%5.1f", entry.getMaxTime() / 1e6 ) + "ms" )
|
||||
);
|
||||
if( defaultLayout )
|
||||
{
|
||||
table.addRow(
|
||||
computerComponent,
|
||||
text( entry.getFormatted( TrackingField.TASKS ) ),
|
||||
text( entry.getFormatted( TrackingField.TOTAL_TIME ) ),
|
||||
text( entry.getFormatted( TrackingField.AVERAGE_TIME ) ),
|
||||
text( entry.getFormatted( TrackingField.MAX_TIME ) )
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
table.addRow( computerComponent, text( entry.getFormatted( field ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
table.displayTo( context.getSender() );
|
||||
|
@@ -0,0 +1,150 @@
|
||||
package dan200.computercraft.shared.command.framework;
|
||||
|
||||
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 java.util.Arrays;
|
||||
|
||||
import static dan200.computercraft.shared.command.framework.ChatHelpers.coloured;
|
||||
|
||||
/**
|
||||
* Adapated from Sponge's PaginationCalculator
|
||||
*/
|
||||
public class TextFormatter
|
||||
{
|
||||
private static final int SPACE_WIDTH = 4;
|
||||
private static final char PADDING_CHAR = '\u02cc';
|
||||
|
||||
/**
|
||||
* Yoinked from FontRenderer
|
||||
*
|
||||
* @see net.minecraft.client.gui.FontRenderer#charWidth
|
||||
* @see net.minecraft.client.gui.FontRenderer#getCharWidth(char)
|
||||
*/
|
||||
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 int[] EXTRA_CHARS = new int[]{
|
||||
'\u20e2', '\u261b',
|
||||
};
|
||||
|
||||
private static final byte[] EXTRA_WIDTHS = new byte[]{
|
||||
8, 4,
|
||||
};
|
||||
|
||||
private static int getWidth( int codePoint )
|
||||
{
|
||||
// Escape codes
|
||||
if( codePoint == 167 ) return -1;
|
||||
|
||||
// Space and non-breaking space
|
||||
if( codePoint == 32 || codePoint == 160 ) return 4;
|
||||
|
||||
// Built-in characters
|
||||
int nonUnicodeIdx = CHARACTERS.indexOf( codePoint );
|
||||
if( codePoint > 0 && nonUnicodeIdx != -1 ) return CHAR_WIDTHS[nonUnicodeIdx];
|
||||
|
||||
// Other special characters we use.
|
||||
int extraIdx = Arrays.binarySearch( EXTRA_CHARS, codePoint );
|
||||
if( extraIdx >= 0 ) return EXTRA_WIDTHS[extraIdx];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int getWidth( ITextComponent component )
|
||||
{
|
||||
int total = 0;
|
||||
if( component instanceof TextComponentString )
|
||||
{
|
||||
String contents = component.getUnformattedComponentText();
|
||||
|
||||
int bold = component.getStyle().getBold() ? 1 : 0;
|
||||
for( int i = 0; i < contents.length(); i++ )
|
||||
{
|
||||
int cp = contents.charAt( i );
|
||||
assert cp != '\n';
|
||||
int width = getWidth( cp );
|
||||
if( width < 0 )
|
||||
{
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
total += width + bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( ITextComponent child : component.getSiblings() )
|
||||
{
|
||||
total += getWidth( child );
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public static boolean isPlayer( ICommandSender sender )
|
||||
{
|
||||
return sender instanceof EntityPlayerMP && !(sender instanceof FakePlayer);
|
||||
}
|
||||
|
||||
public static int getMaxWidth( ICommandSender sender )
|
||||
{
|
||||
return isPlayer( sender ) ? 320 : 80;
|
||||
}
|
||||
|
||||
public static int getWidthFor( ITextComponent component, ICommandSender sender )
|
||||
{
|
||||
return isPlayer( sender ) ? getWidth( component ) : component.getUnformattedText().length();
|
||||
}
|
||||
|
||||
public static int getWidthFor( int codepoint, ICommandSender sender )
|
||||
{
|
||||
return isPlayer( sender ) ? getWidth( codepoint ) : 1;
|
||||
}
|
||||
|
||||
public static void appendFixedWidth( ITextComponent out, ICommandSender sender, ITextComponent entry, int maxWidth )
|
||||
{
|
||||
out.appendSibling( entry );
|
||||
|
||||
int width = getWidthFor( entry, sender );
|
||||
int delta = maxWidth - width;
|
||||
|
||||
if( delta > 0 )
|
||||
{
|
||||
int spaces = delta / SPACE_WIDTH;
|
||||
int extra = delta % SPACE_WIDTH;
|
||||
|
||||
// Append a fixed number of spaces
|
||||
if( spaces > 0 ) out.appendSibling( new TextComponentString( StringUtils.repeat( ' ', spaces ) ) );
|
||||
|
||||
// Append several minor characters to pad to a full string
|
||||
if( extra > 0 )
|
||||
{
|
||||
out.appendSibling( coloured( StringUtils.repeat( PADDING_CHAR, extra ), TextFormatting.GRAY ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,113 +1,49 @@
|
||||
package dan200.computercraft.shared.command.framework;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.network.ComputerCraftPacket;
|
||||
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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static dan200.computercraft.shared.command.framework.ChatHelpers.coloured;
|
||||
import static dan200.computercraft.shared.command.framework.ChatHelpers.text;
|
||||
import static dan200.computercraft.shared.command.framework.TextFormatter.*;
|
||||
|
||||
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 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 final int id;
|
||||
private int columns = -1;
|
||||
private final ITextComponent[] header;
|
||||
private final List<ITextComponent[]> rows = Lists.newArrayList();
|
||||
|
||||
public TextTable( @Nonnull ITextComponent... header )
|
||||
public TextTable( int id, @Nonnull ITextComponent... header )
|
||||
{
|
||||
this.id = id;
|
||||
this.header = header;
|
||||
this.columns = header.length;
|
||||
}
|
||||
|
||||
public TextTable()
|
||||
public TextTable(int id)
|
||||
{
|
||||
header = null;
|
||||
this.id = id;
|
||||
this.header = null;
|
||||
}
|
||||
|
||||
public TextTable( @Nonnull String... header )
|
||||
public TextTable( int id, @Nonnull String... header )
|
||||
{
|
||||
this.id = id;
|
||||
this.header = new ITextComponent[header.length];
|
||||
for( int i = 0; i < header.length; i++ )
|
||||
{
|
||||
@@ -134,128 +70,100 @@ public class TextTable
|
||||
{
|
||||
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 );
|
||||
maxWidths[i] = getWidthFor( header[i], sender );
|
||||
}
|
||||
}
|
||||
|
||||
// Limit the number of rows to something sensible.
|
||||
int limit = isPlayer( sender ) ? 30 : 100;
|
||||
if( limit > rows.size() ) limit = rows.size();
|
||||
// Limit the number of rows to fit within a single chat window on default Minecraft
|
||||
// options.
|
||||
int height = isPlayer( sender ) ? 18 : 100;
|
||||
int limit = rows.size() <= height ? rows.size() : height - 1;
|
||||
|
||||
for( int y = 0; y < limit; y++ )
|
||||
{
|
||||
ITextComponent[] row = rows.get( y );
|
||||
for( int i = 0; i < row.length; i++ )
|
||||
{
|
||||
int width = getWidth( row[i], sender );
|
||||
rowWidths[i] += width;
|
||||
if( width > maxWidths[i] )
|
||||
{
|
||||
maxWidths[i] = width;
|
||||
}
|
||||
int width = getWidthFor( row[i], sender ) + 3;
|
||||
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() );
|
||||
}
|
||||
// Add a small amount of extra padding. We include this here instead of the separator to allow
|
||||
// for "extra" characters
|
||||
for( int i = 0; i < maxWidths.length; i++ ) maxWidths[i] += 4;
|
||||
|
||||
int totalWidth = (columns - 1) * getWidth( SEPARATOR, sender );
|
||||
int totalWidth = (columns - 1) * getWidthFor( SEPARATOR, sender );
|
||||
for( int x : maxWidths ) totalWidth += x;
|
||||
|
||||
// TODO: Limit the widths of some entries if totalWidth > maxWidth
|
||||
|
||||
ITextComponent out = new TextComponentString( "" );
|
||||
List<ITextComponent> out = new ArrayList<>();
|
||||
|
||||
if( header != null )
|
||||
{
|
||||
for( int i = 0; i < columns; i++ )
|
||||
TextComponentString line = new TextComponentString( "" );
|
||||
for( int i = 0; i < columns - 1; i++ )
|
||||
{
|
||||
if( i != 0 ) out.appendSibling( SEPARATOR );
|
||||
appendFixed( out, sender, header[i], maxWidths[i] );
|
||||
appendFixedWidth( line, sender, header[i], maxWidths[i] );
|
||||
line.appendSibling( SEPARATOR );
|
||||
}
|
||||
out.appendSibling( LINE );
|
||||
line.appendSibling( header[columns - 1] );
|
||||
out.add( line );
|
||||
|
||||
// Round the width up rather than down
|
||||
int rowCharWidth = getWidth( '=', sender );
|
||||
int rowCharWidth = getWidthFor( '=', sender );
|
||||
int rowWidth = totalWidth / rowCharWidth + (totalWidth % rowCharWidth == 0 ? 0 : 1);
|
||||
out.appendSibling( coloured( StringUtils.repeat( '=', rowWidth ), TextFormatting.GRAY ) );
|
||||
out.appendSibling( LINE );
|
||||
out.add( coloured( StringUtils.repeat( '=', rowWidth ), TextFormatting.GRAY ) );
|
||||
}
|
||||
|
||||
for( int i = 0; i < limit; i++ )
|
||||
{
|
||||
TextComponentString line = new TextComponentString( "" );
|
||||
ITextComponent[] row = rows.get( i );
|
||||
if( i != 0 ) out.appendSibling( LINE );
|
||||
for( int j = 0; j < columns; j++ )
|
||||
for( int j = 0; j < columns - 1; j++ )
|
||||
{
|
||||
if( j != 0 ) out.appendSibling( SEPARATOR );
|
||||
appendFixed( out, sender, row[j], maxWidths[j] );
|
||||
appendFixedWidth( line, sender, row[j], maxWidths[j] );
|
||||
line.appendSibling( SEPARATOR );
|
||||
}
|
||||
line.appendSibling( row[columns - 1] );
|
||||
out.add( line );
|
||||
}
|
||||
|
||||
if( limit != rows.size() )
|
||||
if( rows.size() > limit )
|
||||
{
|
||||
out.appendSibling( LINE );
|
||||
out.appendSibling( coloured( (rows.size() - limit) + " additional rows...", TextFormatting.AQUA ) );
|
||||
out.add( coloured( (rows.size() - limit) + " additional rows...", TextFormatting.AQUA ) );
|
||||
}
|
||||
|
||||
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 )
|
||||
if( isPlayer( sender ) && id != 0 )
|
||||
{
|
||||
// Convert to overflow;
|
||||
delta = -delta;
|
||||
ComputerCraftPacket packet = new ComputerCraftPacket();
|
||||
packet.m_packetType = ComputerCraftPacket.PostChat;
|
||||
packet.m_dataInt = new int[]{ id };
|
||||
|
||||
// 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 )
|
||||
String[] lines = packet.m_dataString = new String[out.size()];
|
||||
for( int i = 0; i < out.size(); i++ )
|
||||
{
|
||||
ITextComponent bold = new TextComponentString( StringUtils.repeat( ' ', missing ) );
|
||||
bold.getStyle().setBold( true );
|
||||
component.appendSibling( bold );
|
||||
lines[i] = ITextComponent.Serializer.componentToJson( out.get( i ) );
|
||||
}
|
||||
|
||||
out.appendSibling( component );
|
||||
}
|
||||
else if( delta > 0 )
|
||||
{
|
||||
out.appendSibling( entry );
|
||||
ComputerCraft.sendToPlayer( (EntityPlayerMP) sender, packet );
|
||||
}
|
||||
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( " " );
|
||||
ITextComponent result = new TextComponentString( "" );
|
||||
for( int i = 0; i < out.size(); i++ )
|
||||
{
|
||||
if( i > 0 ) result.appendSibling( LINE );
|
||||
result.appendSibling( out.get( 0 ) );
|
||||
}
|
||||
sender.sendMessage( result );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ public abstract class BlockGeneric extends Block implements
|
||||
protected BlockGeneric( Material material )
|
||||
{
|
||||
super( material );
|
||||
this.isBlockContainer = true;
|
||||
this.hasTileEntity = true;
|
||||
}
|
||||
|
||||
protected abstract IBlockState getDefaultBlockState( int damage, EnumFacing placedSide );
|
||||
|
@@ -86,7 +86,7 @@ public class ColourableRecipe extends IForgeRegistryEntry.Impl<IRecipe> implemen
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden()
|
||||
public boolean isDynamic()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -16,4 +16,5 @@ public interface IComputerItem
|
||||
int getComputerID( @Nonnull ItemStack stack );
|
||||
String getLabel( @Nonnull ItemStack stack );
|
||||
ComputerFamily getFamily( @Nonnull ItemStack stack );
|
||||
ItemStack withFamily(@Nonnull ItemStack stack, @Nonnull ComputerFamily family);
|
||||
}
|
||||
|
@@ -27,7 +27,7 @@ import javax.annotation.Nullable;
|
||||
public class ItemComputer extends ItemComputerBase
|
||||
{
|
||||
public static int HIGHEST_DAMAGE_VALUE_ID = 16382;
|
||||
|
||||
|
||||
public ItemComputer( Block block )
|
||||
{
|
||||
super( block );
|
||||
@@ -87,7 +87,7 @@ public class ItemComputer extends ItemComputerBase
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
if( tile != null && tile instanceof IComputerTile )
|
||||
{
|
||||
IComputerTile computer = (IComputerTile)tile;
|
||||
IComputerTile computer = (IComputerTile) tile;
|
||||
setupComputerAfterPlacement( stack, computer );
|
||||
}
|
||||
return true;
|
||||
@@ -146,10 +146,16 @@ public class ItemComputer extends ItemComputerBase
|
||||
else
|
||||
{
|
||||
int damage = stack.getItemDamage() & 0x3fff;
|
||||
return ( damage - 1 );
|
||||
return (damage - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack withFamily( @Nonnull ItemStack stack, @Nonnull ComputerFamily family )
|
||||
{
|
||||
return ComputerItemFactory.create( getComputerID( stack ), getLabel( stack ), family );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputerFamily getFamily( int damage )
|
||||
{
|
||||
|
@@ -0,0 +1,66 @@
|
||||
package dan200.computercraft.shared.computer.recipe;
|
||||
|
||||
import dan200.computercraft.shared.computer.items.IComputerItem;
|
||||
import net.minecraft.inventory.InventoryCrafting;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
import net.minecraft.item.crafting.ShapedRecipes;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.crafting.CraftingHelper;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Represents a recipe which converts a computer from one form into another.
|
||||
*/
|
||||
public abstract class ComputerConvertRecipe extends ShapedRecipes
|
||||
{
|
||||
public ComputerConvertRecipe( String group, @Nonnull CraftingHelper.ShapedPrimer primer, @Nonnull ItemStack result )
|
||||
{
|
||||
super( group, primer.width, primer.height, primer.input, result );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected abstract ItemStack convert( @Nonnull ItemStack stack );
|
||||
|
||||
@Override
|
||||
public boolean matches( @Nonnull InventoryCrafting inventory, @Nonnull World world )
|
||||
{
|
||||
// See if we match the recipe, and extract the input computercraft ID
|
||||
ItemStack computerStack = null;
|
||||
for( int y = 0; y < 3; ++y )
|
||||
{
|
||||
for( int x = 0; x < 3; ++x )
|
||||
{
|
||||
ItemStack stack = inventory.getStackInRowAndColumn( x, y );
|
||||
Ingredient target = getIngredients().get( x + y * 3 );
|
||||
|
||||
// First verify we match the ingredient
|
||||
if( !target.apply( stack ) ) return false;
|
||||
|
||||
// We want to ensure we have a computer item somewhere in the recipe
|
||||
if( stack.getItem() instanceof IComputerItem ) computerStack = stack;
|
||||
}
|
||||
}
|
||||
|
||||
return computerStack != null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ItemStack getCraftingResult( @Nonnull InventoryCrafting inventory )
|
||||
{
|
||||
for( int y = 0; y < 3; ++y )
|
||||
{
|
||||
for( int x = 0; x < 3; ++x )
|
||||
{
|
||||
ItemStack item = inventory.getStackInRowAndColumn( x, y );
|
||||
|
||||
// If we're a computer, convert!
|
||||
if( item.getItem() instanceof IComputerItem ) return convert( item );
|
||||
}
|
||||
}
|
||||
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package dan200.computercraft.shared.computer.recipe;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.items.IComputerItem;
|
||||
import dan200.computercraft.shared.util.RecipeUtil;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.IRecipe;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraftforge.common.crafting.CraftingHelper;
|
||||
import net.minecraftforge.common.crafting.IRecipeFactory;
|
||||
import net.minecraftforge.common.crafting.JsonContext;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class ComputerFamilyRecipe extends ComputerConvertRecipe
|
||||
{
|
||||
private final ComputerFamily family;
|
||||
|
||||
public ComputerFamilyRecipe( String group, @Nonnull CraftingHelper.ShapedPrimer primer, @Nonnull ItemStack result, ComputerFamily family )
|
||||
{
|
||||
super( group, primer, result );
|
||||
this.family = family;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
protected ItemStack convert( @Nonnull ItemStack stack )
|
||||
{
|
||||
return ((IComputerItem) stack.getItem()).withFamily( stack, family );
|
||||
}
|
||||
|
||||
public static class Factory implements IRecipeFactory
|
||||
{
|
||||
@Override
|
||||
public IRecipe parse( JsonContext context, JsonObject json )
|
||||
{
|
||||
String group = JsonUtils.getString( json, "group", "" );
|
||||
ComputerFamily family = RecipeUtil.getFamily( json, "family" );
|
||||
|
||||
CraftingHelper.ShapedPrimer primer = RecipeUtil.getPrimer( context, json );
|
||||
ItemStack result = deserializeItem( JsonUtils.getJsonObject( json, "result" ), false );
|
||||
|
||||
return new ComputerFamilyRecipe( group, primer, result, family );
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
package dan200.computercraft.shared.computer.recipe;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.items.IComputerItem;
|
||||
import dan200.computercraft.shared.util.RecipeUtil;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.common.crafting.IIngredientFactory;
|
||||
import net.minecraftforge.common.crafting.JsonContext;
|
||||
import net.minecraftforge.fml.common.registry.ForgeRegistries;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Represents an ingredient which requires an item to have a specific
|
||||
* computer family. This allows us to have operations which only work
|
||||
* on normal or
|
||||
*/
|
||||
public class ComputerIngredient extends Ingredient
|
||||
{
|
||||
private final IComputerItem item;
|
||||
private final ComputerFamily family;
|
||||
|
||||
public <T extends Item & IComputerItem> ComputerIngredient( T item, int data, ComputerFamily family )
|
||||
{
|
||||
super( new ItemStack( item, 1, data ) );
|
||||
|
||||
this.item = item;
|
||||
this.family = family;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply( @Nullable ItemStack stack )
|
||||
{
|
||||
return stack != null && stack.getItem() == item && item.getFamily( stack ) == family;
|
||||
}
|
||||
|
||||
public static class Factory implements IIngredientFactory
|
||||
{
|
||||
@Nonnull
|
||||
@Override
|
||||
public Ingredient parse( JsonContext context, JsonObject json )
|
||||
{
|
||||
String itemName = context.appendModId( JsonUtils.getString( json, "item" ) );
|
||||
int data = JsonUtils.getInt( json, "data", 0 );
|
||||
ComputerFamily family = RecipeUtil.getFamily( json, "family" );
|
||||
|
||||
Item item = ForgeRegistries.ITEMS.getValue( new ResourceLocation( itemName ) );
|
||||
|
||||
if( item == null ) throw new JsonSyntaxException( "Unknown item '" + itemName + "'" );
|
||||
if( !(item instanceof IComputerItem) )
|
||||
{
|
||||
throw new JsonSyntaxException( "Item '" + itemName + "' is not a computer item" );
|
||||
}
|
||||
|
||||
|
||||
return new ComputerIngredient( (Item & IComputerItem) item, data, family );
|
||||
}
|
||||
}
|
||||
}
|
@@ -54,15 +54,7 @@ public class ItemDiskLegacy extends Item
|
||||
@Nonnull
|
||||
public static ItemStack createFromIDAndColour( int id, String label, int colour )
|
||||
{
|
||||
if( colour != Colour.Blue.getHex() )
|
||||
{
|
||||
return ItemDiskExpanded.createFromIDAndColour( id, label, colour );
|
||||
}
|
||||
|
||||
ItemStack stack = new ItemStack( ComputerCraft.Items.disk, 1 );
|
||||
ComputerCraft.Items.disk.setDiskID( stack, id );
|
||||
ComputerCraft.Items.disk.setLabel( stack, label );
|
||||
return stack;
|
||||
return ItemDiskExpanded.createFromIDAndColour( id, label, colour );
|
||||
}
|
||||
|
||||
public int getDiskID( @Nonnull ItemStack stack )
|
||||
|
@@ -91,7 +91,7 @@ public class DiskRecipe extends IForgeRegistryEntry.Impl<IRecipe> implements IRe
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden()
|
||||
public boolean isDynamic()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ public class PrintoutRecipe extends IForgeRegistryEntry.Impl<IRecipe> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden()
|
||||
public boolean isDynamic()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@ public class ComputerCraftPacket
|
||||
public static final byte ComputerTerminalChanged = 8;
|
||||
public static final byte ComputerDeleted = 9;
|
||||
public static final byte PlayRecord = 10;
|
||||
public static final byte PostChat = 11;
|
||||
|
||||
// Packet class
|
||||
public byte m_packetType;
|
||||
|
@@ -332,7 +332,8 @@ public class BlockCable extends BlockPeripheralBase
|
||||
item = PeripheralItemFactory.create( PeripheralType.Cable, null, 1 );
|
||||
}
|
||||
|
||||
cable.networkChanged();
|
||||
cable.modemChanged();
|
||||
cable.connectionsChanged();
|
||||
if( !world.isRemote && !player.capabilities.isCreativeMode ) dropItem( world, pos, item );
|
||||
|
||||
return false;
|
||||
@@ -382,7 +383,7 @@ public class BlockCable extends BlockPeripheralBase
|
||||
TileCable cable = (TileCable) tile;
|
||||
if( cable.getPeripheralType() != PeripheralType.WiredModem )
|
||||
{
|
||||
cable.networkChanged();
|
||||
cable.connectionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -98,8 +98,8 @@ public class ItemCable extends ItemPeripheralBase
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
if( tile != null && tile instanceof TileCable )
|
||||
{
|
||||
TileCable cable = (TileCable)tile;
|
||||
cable.networkChanged();
|
||||
TileCable cable = (TileCable) tile;
|
||||
cable.connectionsChanged();
|
||||
}
|
||||
return EnumActionResult.SUCCESS;
|
||||
}
|
||||
@@ -131,7 +131,8 @@ public class ItemCable extends ItemPeripheralBase
|
||||
if( tile != null && tile instanceof TileCable )
|
||||
{
|
||||
TileCable cable = (TileCable)tile;
|
||||
cable.networkChanged();
|
||||
cable.modemChanged();
|
||||
cable.connectionsChanged();
|
||||
}
|
||||
return EnumActionResult.SUCCESS;
|
||||
}
|
||||
@@ -153,7 +154,8 @@ public class ItemCable extends ItemPeripheralBase
|
||||
if( tile != null && tile instanceof TileCable )
|
||||
{
|
||||
TileCable cable = (TileCable)tile;
|
||||
cable.networkChanged();
|
||||
cable.modemChanged();
|
||||
cable.connectionsChanged();
|
||||
}
|
||||
return EnumActionResult.SUCCESS;
|
||||
}
|
||||
|
@@ -62,9 +62,9 @@ public class DiskDrivePeripheral implements IPeripheral
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// isPresent
|
||||
// isDiskPresent
|
||||
return new Object[] {
|
||||
m_diskDrive.getDiskStack() != null
|
||||
!m_diskDrive.getDiskStack().isEmpty()
|
||||
};
|
||||
}
|
||||
case 1:
|
||||
|
@@ -422,6 +422,7 @@ public class TileDiskDrive extends TilePeripheralBase
|
||||
return new DiskDrivePeripheral( this );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ItemStack getDiskStack()
|
||||
{
|
||||
synchronized( this )
|
||||
|
@@ -16,10 +16,7 @@ import dan200.computercraft.shared.peripheral.PeripheralType;
|
||||
import dan200.computercraft.shared.peripheral.common.BlockCable;
|
||||
import dan200.computercraft.shared.peripheral.common.BlockCableModemVariant;
|
||||
import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory;
|
||||
import dan200.computercraft.shared.util.IDAssigner;
|
||||
import dan200.computercraft.shared.util.PeripheralUtil;
|
||||
import dan200.computercraft.api.network.wired.IWiredElementTile;
|
||||
import net.minecraft.block.Block;
|
||||
import dan200.computercraft.shared.wired.CapabilityWiredElement;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -31,15 +28,15 @@ import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class TileCable extends TileModemBase implements IWiredElementTile
|
||||
public class TileCable extends TileModemBase
|
||||
{
|
||||
public static final double MIN = 0.375;
|
||||
public static final double MAX = 1 - MIN;
|
||||
@@ -75,26 +72,13 @@ public class TileCable extends TileModemBase implements IWiredElementTile
|
||||
public Vec3d getPosition()
|
||||
{
|
||||
BlockPos pos = m_entity.getPos();
|
||||
return new Vec3d( (double) pos.getX() + 0.5, (double) pos.getY() + 0.5, (double) pos.getZ() + 0.5 );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Map<String, IPeripheral> getPeripherals()
|
||||
{
|
||||
IPeripheral peripheral = m_entity.getConnectedPeripheral();
|
||||
return peripheral != null
|
||||
? Collections.singletonMap( m_entity.getConnectedPeripheralName(), peripheral )
|
||||
: Collections.emptyMap();
|
||||
return new Vec3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachPeripheral( String name, IPeripheral peripheral )
|
||||
{
|
||||
if( !name.equals( m_entity.getConnectedPeripheralName() ) )
|
||||
{
|
||||
((WiredModemPeripheral) m_entity.m_modem).attachPeripheral( name, peripheral );
|
||||
}
|
||||
((WiredModemPeripheral) m_entity.m_modem).attachPeripheral( name, peripheral );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -107,9 +91,9 @@ public class TileCable extends TileModemBase implements IWiredElementTile
|
||||
// Members
|
||||
|
||||
private boolean m_peripheralAccessAllowed;
|
||||
private int m_attachedPeripheralID;
|
||||
private WiredModemLocalPeripheral m_peripheral = new WiredModemLocalPeripheral();
|
||||
|
||||
private boolean m_destroyed;
|
||||
private boolean m_destroyed = false;
|
||||
|
||||
private boolean m_hasDirection = false;
|
||||
private boolean m_connectionsFormed = false;
|
||||
@@ -117,14 +101,6 @@ public class TileCable extends TileModemBase implements IWiredElementTile
|
||||
private WiredModemElement m_cable;
|
||||
private IWiredNode m_node;
|
||||
|
||||
public TileCable()
|
||||
{
|
||||
m_peripheralAccessAllowed = false;
|
||||
m_attachedPeripheralID = -1;
|
||||
|
||||
m_destroyed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModemPeripheral createPeripheral()
|
||||
{
|
||||
@@ -132,6 +108,12 @@ public class TileCable extends TileModemBase implements IWiredElementTile
|
||||
m_node = m_cable.getNode();
|
||||
return new WiredModemPeripheral( m_cable )
|
||||
{
|
||||
@Override
|
||||
protected boolean canSeePeripheral( @Nonnull String peripheralName )
|
||||
{
|
||||
return !peripheralName.equals( m_peripheral.getConnectedName() );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Vec3d getPosition()
|
||||
@@ -287,12 +269,32 @@ public class TileCable extends TileModemBase implements IWiredElementTile
|
||||
((BlockGeneric) getBlockType()).dropItem( getWorld(), getPos(), PeripheralItemFactory.create( PeripheralType.WiredModem, getLabel(), 1 ) );
|
||||
setLabel( null );
|
||||
setBlockState( getBlockState().withProperty( BlockCable.Properties.MODEM, BlockCableModemVariant.None ) );
|
||||
networkChanged();
|
||||
modemChanged();
|
||||
connectionsChanged();
|
||||
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !world.isRemote && m_peripheralAccessAllowed )
|
||||
{
|
||||
if( m_peripheral.attach( world, getPos(), dir ) ) updateConnectedPeripherals();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour )
|
||||
{
|
||||
super.onNeighbourTileEntityChange( neighbour );
|
||||
if( !world.isRemote && m_peripheralAccessAllowed )
|
||||
{
|
||||
EnumFacing facing = getDirection();
|
||||
if( getPos().offset( facing ).equals( neighbour ) )
|
||||
{
|
||||
if( m_peripheral.attach( world, getPos(), facing ) ) updateConnectedPeripherals();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AxisAlignedBB getModemBounds()
|
||||
@@ -395,9 +397,9 @@ public class TileCable extends TileModemBase implements IWiredElementTile
|
||||
if( !getWorld().isRemote )
|
||||
{
|
||||
// On server, we interacted if a peripheral was found
|
||||
String oldPeriphName = getConnectedPeripheralName();
|
||||
String oldPeriphName = m_peripheral.getConnectedName();
|
||||
togglePeripheralAccess();
|
||||
String periphName = getConnectedPeripheralName();
|
||||
String periphName = m_peripheral.getConnectedName();
|
||||
|
||||
if( !Objects.equal( periphName, oldPeriphName ) )
|
||||
{
|
||||
@@ -432,7 +434,7 @@ public class TileCable extends TileModemBase implements IWiredElementTile
|
||||
// Read properties
|
||||
super.readFromNBT( nbttagcompound );
|
||||
m_peripheralAccessAllowed = nbttagcompound.getBoolean( "peripheralAccess" );
|
||||
m_attachedPeripheralID = nbttagcompound.getInteger( "peripheralID" );
|
||||
m_peripheral.readNBT( nbttagcompound, "" );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -442,7 +444,7 @@ public class TileCable extends TileModemBase implements IWiredElementTile
|
||||
// Write properties
|
||||
nbttagcompound = super.writeToNBT( nbttagcompound );
|
||||
nbttagcompound.setBoolean( "peripheralAccess", m_peripheralAccessAllowed );
|
||||
nbttagcompound.setInteger( "peripheralID", m_attachedPeripheralID );
|
||||
m_peripheral.writeNBT( nbttagcompound, "" );
|
||||
return nbttagcompound;
|
||||
}
|
||||
|
||||
@@ -470,27 +472,31 @@ public class TileCable extends TileModemBase implements IWiredElementTile
|
||||
{
|
||||
if( !m_connectionsFormed )
|
||||
{
|
||||
networkChanged();
|
||||
if( m_peripheralAccessAllowed ) m_node.invalidate();
|
||||
m_connectionsFormed = true;
|
||||
|
||||
connectionsChanged();
|
||||
if( m_peripheralAccessAllowed )
|
||||
{
|
||||
m_peripheral.attach( world, pos, m_dir );
|
||||
updateConnectedPeripherals();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void networkChanged()
|
||||
public void connectionsChanged()
|
||||
{
|
||||
if( getWorld().isRemote ) return;
|
||||
|
||||
if( modemChanged() ) m_node.invalidate();
|
||||
|
||||
IBlockState state = getBlockState();
|
||||
World world = getWorld();
|
||||
BlockPos current = getPos();
|
||||
for( EnumFacing facing : EnumFacing.VALUES )
|
||||
{
|
||||
if( !world.isBlockLoaded( pos ) ) continue;
|
||||
|
||||
IWiredElement element = ComputerCraft.getWiredElementAt( world, current.offset( facing ), facing.getOpposite() );
|
||||
BlockPos offset = current.offset( facing );
|
||||
if( !world.isBlockLoaded( offset ) ) continue;
|
||||
|
||||
IWiredElement element = ComputerCraft.getWiredElementAt( world, offset, facing.getOpposite() );
|
||||
if( element == null ) continue;
|
||||
|
||||
if( BlockCable.canConnectIn( state, facing ) )
|
||||
@@ -506,27 +512,19 @@ public class TileCable extends TileModemBase implements IWiredElementTile
|
||||
}
|
||||
}
|
||||
|
||||
private boolean modemChanged()
|
||||
public void modemChanged()
|
||||
{
|
||||
if( getWorld().isRemote ) return false;
|
||||
|
||||
boolean requiresUpdate = false;
|
||||
if( getWorld().isRemote ) return;
|
||||
|
||||
PeripheralType type = getPeripheralType();
|
||||
if( type == PeripheralType.Cable )
|
||||
{
|
||||
m_attachedPeripheralID = -1;
|
||||
}
|
||||
|
||||
if( type != PeripheralType.WiredModemWithCable && m_peripheralAccessAllowed )
|
||||
{
|
||||
m_peripheralAccessAllowed = false;
|
||||
requiresUpdate = true;
|
||||
m_peripheral.detach();
|
||||
m_node.updatePeripherals( Collections.emptyMap() );
|
||||
markDirty();
|
||||
updateAnim();
|
||||
}
|
||||
|
||||
return requiresUpdate;
|
||||
}
|
||||
|
||||
// private stuff
|
||||
@@ -534,61 +532,34 @@ public class TileCable extends TileModemBase implements IWiredElementTile
|
||||
{
|
||||
if( !m_peripheralAccessAllowed )
|
||||
{
|
||||
m_peripheral.attach( world, getPos(), getDirection() );
|
||||
if( !m_peripheral.hasPeripheral() ) return;
|
||||
|
||||
m_peripheralAccessAllowed = true;
|
||||
if( getConnectedPeripheral() == null )
|
||||
{
|
||||
m_peripheralAccessAllowed = false;
|
||||
return;
|
||||
}
|
||||
m_node.updatePeripherals( m_peripheral.toMap() );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_peripheral.detach();
|
||||
|
||||
m_peripheralAccessAllowed = false;
|
||||
m_node.updatePeripherals( Collections.emptyMap() );
|
||||
}
|
||||
|
||||
updateAnim();
|
||||
m_node.invalidate();
|
||||
}
|
||||
|
||||
private String getConnectedPeripheralName()
|
||||
private void updateConnectedPeripherals()
|
||||
{
|
||||
IPeripheral periph = getConnectedPeripheral();
|
||||
if( periph != null )
|
||||
Map<String, IPeripheral> peripherals = m_peripheral.toMap();
|
||||
if( peripherals.isEmpty() )
|
||||
{
|
||||
String type = periph.getType();
|
||||
if( m_attachedPeripheralID < 0 )
|
||||
{
|
||||
m_attachedPeripheralID = IDAssigner.getNextIDFromFile( new File(
|
||||
ComputerCraft.getWorldDir( getWorld() ),
|
||||
"computer/lastid_" + type + ".txt"
|
||||
) );
|
||||
}
|
||||
return type + "_" + m_attachedPeripheralID;
|
||||
// If there are no peripherals then disable access and update the display state.
|
||||
m_peripheralAccessAllowed = false;
|
||||
updateAnim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private IPeripheral getConnectedPeripheral()
|
||||
{
|
||||
if( m_peripheralAccessAllowed )
|
||||
{
|
||||
if( getPeripheralType() == PeripheralType.WiredModemWithCable )
|
||||
{
|
||||
EnumFacing facing = getDirection();
|
||||
BlockPos neighbour = getPos().offset( facing );
|
||||
IPeripheral peripheral = getPeripheral( getWorld(), neighbour, facing.getOpposite() );
|
||||
return peripheral == null || peripheral instanceof WiredModemPeripheral ? null : peripheral;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IPeripheral getPeripheral( World world, BlockPos pos, EnumFacing facing )
|
||||
{
|
||||
Block block = world.getBlockState( pos ).getBlock();
|
||||
if( block == ComputerCraft.Blocks.wiredModemFull || block == ComputerCraft.Blocks.cable ) return null;
|
||||
|
||||
return PeripheralUtil.getPeripheral( world, pos, facing );
|
||||
m_node.updatePeripherals( peripherals );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -597,13 +568,25 @@ public class TileCable extends TileModemBase implements IWiredElementTile
|
||||
return true;
|
||||
}
|
||||
|
||||
// IWiredElement tile
|
||||
// IWiredElement capability
|
||||
|
||||
@Override
|
||||
public boolean hasCapability( @Nonnull Capability<?> capability, @Nullable EnumFacing facing )
|
||||
{
|
||||
if( capability == CapabilityWiredElement.CAPABILITY ) return BlockCable.canConnectIn( getBlockState(), facing );
|
||||
return super.hasCapability( capability, facing );
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IWiredElement getWiredElement( @Nonnull EnumFacing side )
|
||||
public <T> T getCapability( @Nonnull Capability<T> capability, @Nullable EnumFacing facing )
|
||||
{
|
||||
return BlockCable.canConnectIn( getBlockState(), side ) ? m_cable : null;
|
||||
if( capability == CapabilityWiredElement.CAPABILITY )
|
||||
{
|
||||
return BlockCable.canConnectIn( getBlockState(), facing ) ? CapabilityWiredElement.CAPABILITY.cast( m_cable ) : null;
|
||||
}
|
||||
|
||||
return super.getCapability( capability, facing );
|
||||
}
|
||||
|
||||
// IPeripheralTile
|
||||
|
@@ -9,12 +9,11 @@ package dan200.computercraft.shared.peripheral.modem;
|
||||
import com.google.common.base.Objects;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.network.wired.IWiredElement;
|
||||
import dan200.computercraft.api.network.wired.IWiredElementTile;
|
||||
import dan200.computercraft.api.network.wired.IWiredNode;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.peripheral.common.BlockCable;
|
||||
import dan200.computercraft.shared.peripheral.common.TilePeripheralBase;
|
||||
import dan200.computercraft.shared.util.IDAssigner;
|
||||
import dan200.computercraft.shared.wired.CapabilityWiredElement;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
@@ -23,13 +22,13 @@ import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.Constants;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.File;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
|
||||
public class TileWiredModemFull extends TilePeripheralBase implements IWiredElementTile
|
||||
public class TileWiredModemFull extends TilePeripheralBase
|
||||
{
|
||||
private static class FullElement extends WiredModemElement
|
||||
{
|
||||
@@ -46,10 +45,7 @@ public class TileWiredModemFull extends TilePeripheralBase implements IWiredElem
|
||||
for( int i = 0; i < 6; i++ )
|
||||
{
|
||||
WiredModemPeripheral modem = m_entity.m_modems[i];
|
||||
if( modem != null && !name.equals( m_entity.getCachedPeripheralName( EnumFacing.VALUES[i] ) ) )
|
||||
{
|
||||
modem.attachPeripheral( name, peripheral );
|
||||
}
|
||||
if( modem != null ) modem.attachPeripheral( name, peripheral );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,37 +73,29 @@ public class TileWiredModemFull extends TilePeripheralBase implements IWiredElem
|
||||
BlockPos pos = m_entity.getPos();
|
||||
return new Vec3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Map<String, IPeripheral> getPeripherals()
|
||||
{
|
||||
return m_entity.getPeripherals();
|
||||
}
|
||||
}
|
||||
|
||||
private WiredModemPeripheral[] m_modems = new WiredModemPeripheral[6];
|
||||
|
||||
private boolean m_peripheralAccessAllowed = false;
|
||||
private int[] m_attachedPeripheralIDs = new int[6];
|
||||
private String[] m_attachedPeripheralTypes = new String[6];
|
||||
private WiredModemLocalPeripheral[] m_peripherals = new WiredModemLocalPeripheral[6];
|
||||
|
||||
private boolean m_destroyed = false;
|
||||
private boolean m_connectionsFormed = false;
|
||||
|
||||
private final WiredModemElement m_element = new FullElement( this );
|
||||
private final IWiredNode node = m_element.getNode();
|
||||
private final IWiredNode m_node = m_element.getNode();
|
||||
|
||||
public TileWiredModemFull()
|
||||
{
|
||||
Arrays.fill( m_attachedPeripheralIDs, -1 );
|
||||
for( int i = 0; i < m_peripherals.length; i++ ) m_peripherals[i] = new WiredModemLocalPeripheral();
|
||||
}
|
||||
|
||||
private void remove()
|
||||
{
|
||||
if( world == null || !world.isRemote )
|
||||
{
|
||||
node.remove();
|
||||
m_node.remove();
|
||||
m_connectionsFormed = false;
|
||||
}
|
||||
}
|
||||
@@ -153,18 +141,29 @@ public class TileWiredModemFull extends TilePeripheralBase implements IWiredElem
|
||||
{
|
||||
if( !world.isRemote && m_peripheralAccessAllowed )
|
||||
{
|
||||
Map<String, IPeripheral> updated = getPeripherals();
|
||||
|
||||
if( updated.isEmpty() )
|
||||
boolean hasChanged = false;
|
||||
for( EnumFacing facing : EnumFacing.VALUES )
|
||||
{
|
||||
// If there are no peripherals then disable access and update the display state.
|
||||
m_peripheralAccessAllowed = false;
|
||||
updateAnim();
|
||||
hasChanged |= m_peripherals[facing.ordinal()].attach( world, getPos(), facing );
|
||||
}
|
||||
|
||||
// Always invalidate the node: it's more accurate than checking if the peripherals
|
||||
// have changed
|
||||
node.invalidate();
|
||||
if( hasChanged ) updateConnectedPeripherals();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour )
|
||||
{
|
||||
if( !world.isRemote && m_peripheralAccessAllowed )
|
||||
{
|
||||
for( EnumFacing facing : EnumFacing.VALUES )
|
||||
{
|
||||
if( getPos().offset( facing ).equals( neighbour ) )
|
||||
{
|
||||
WiredModemLocalPeripheral peripheral = m_peripherals[facing.ordinal()];
|
||||
if( peripheral.attach( world, getPos(), facing ) ) updateConnectedPeripherals();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,9 +180,9 @@ public class TileWiredModemFull extends TilePeripheralBase implements IWiredElem
|
||||
if( !getWorld().isRemote )
|
||||
{
|
||||
// On server, we interacted if a peripheral was found
|
||||
Set<String> oldPeriphName = getPeripherals().keySet();
|
||||
Set<String> oldPeriphName = getConnectedPeripheralNames();
|
||||
togglePeripheralAccess();
|
||||
Set<String> periphName = getPeripherals().keySet();
|
||||
Set<String> periphName = getConnectedPeripheralNames();
|
||||
|
||||
if( !Objects.equal( periphName, oldPeriphName ) )
|
||||
{
|
||||
@@ -221,17 +220,7 @@ public class TileWiredModemFull extends TilePeripheralBase implements IWiredElem
|
||||
{
|
||||
super.readFromNBT( tag );
|
||||
m_peripheralAccessAllowed = tag.getBoolean( "peripheralAccess" );
|
||||
for( int i = 0; i < m_attachedPeripheralIDs.length; i++ )
|
||||
{
|
||||
if( tag.hasKey( "peripheralID_" + i, Constants.NBT.TAG_ANY_NUMERIC ) )
|
||||
{
|
||||
m_attachedPeripheralIDs[i] = tag.getInteger( "peripheralID_" + i );
|
||||
}
|
||||
if( tag.hasKey( "peripheralType_" + i, Constants.NBT.TAG_STRING ) )
|
||||
{
|
||||
m_attachedPeripheralTypes[i] = tag.getString( "peripheralType_" + i );
|
||||
}
|
||||
}
|
||||
for( int i = 0; i < m_peripherals.length; i++ ) m_peripherals[i].readNBT( tag, "_" + i );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -240,17 +229,7 @@ public class TileWiredModemFull extends TilePeripheralBase implements IWiredElem
|
||||
{
|
||||
tag = super.writeToNBT( tag );
|
||||
tag.setBoolean( "peripheralAccess", m_peripheralAccessAllowed );
|
||||
for( int i = 0; i < m_attachedPeripheralIDs.length; i++ )
|
||||
{
|
||||
if( m_attachedPeripheralIDs[i] >= 0 )
|
||||
{
|
||||
tag.setInteger( "peripheralID_" + i, m_attachedPeripheralIDs[i] );
|
||||
}
|
||||
if( m_attachedPeripheralTypes[i] != null )
|
||||
{
|
||||
tag.setString( "peripheralType_" + i, m_attachedPeripheralTypes[i] );
|
||||
}
|
||||
}
|
||||
for( int i = 0; i < m_peripherals.length; i++ ) m_peripherals[i].writeNBT( tag, "_" + i );
|
||||
return tag;
|
||||
}
|
||||
|
||||
@@ -294,15 +273,24 @@ public class TileWiredModemFull extends TilePeripheralBase implements IWiredElem
|
||||
|
||||
if( !m_connectionsFormed )
|
||||
{
|
||||
networkChanged();
|
||||
m_connectionsFormed = true;
|
||||
|
||||
connectionsChanged();
|
||||
if( m_peripheralAccessAllowed )
|
||||
{
|
||||
for( EnumFacing facing : EnumFacing.VALUES )
|
||||
{
|
||||
m_peripherals[facing.ordinal()].attach( world, getPos(), facing );
|
||||
}
|
||||
updateConnectedPeripherals();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.update();
|
||||
}
|
||||
|
||||
private void networkChanged()
|
||||
private void connectionsChanged()
|
||||
{
|
||||
if( getWorld().isRemote ) return;
|
||||
|
||||
@@ -310,16 +298,15 @@ public class TileWiredModemFull extends TilePeripheralBase implements IWiredElem
|
||||
BlockPos current = getPos();
|
||||
for( EnumFacing facing : EnumFacing.VALUES )
|
||||
{
|
||||
if( !world.isBlockLoaded( pos ) ) continue;
|
||||
BlockPos offset = current.offset( facing );
|
||||
if( !world.isBlockLoaded( offset ) ) continue;
|
||||
|
||||
IWiredElement element = ComputerCraft.getWiredElementAt( world, current.offset( facing ), facing.getOpposite() );
|
||||
IWiredElement element = ComputerCraft.getWiredElementAt( world, offset, facing.getOpposite() );
|
||||
if( element == null ) continue;
|
||||
|
||||
// If we can connect to it then do so
|
||||
node.connectTo( element.getNode() );
|
||||
m_node.connectTo( element.getNode() );
|
||||
}
|
||||
|
||||
node.invalidate();
|
||||
}
|
||||
|
||||
// private stuff
|
||||
@@ -327,69 +314,83 @@ public class TileWiredModemFull extends TilePeripheralBase implements IWiredElem
|
||||
{
|
||||
if( !m_peripheralAccessAllowed )
|
||||
{
|
||||
m_peripheralAccessAllowed = true;
|
||||
if( getPeripherals().isEmpty() )
|
||||
boolean hasAny = false;
|
||||
for( EnumFacing facing : EnumFacing.VALUES )
|
||||
{
|
||||
m_peripheralAccessAllowed = false;
|
||||
return;
|
||||
WiredModemLocalPeripheral peripheral = m_peripherals[facing.ordinal()];
|
||||
peripheral.attach( world, getPos(), facing );
|
||||
hasAny |= peripheral.hasPeripheral();
|
||||
}
|
||||
|
||||
if( !hasAny ) return;
|
||||
|
||||
m_peripheralAccessAllowed = true;
|
||||
m_node.updatePeripherals( getConnectedPeripherals() );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_peripheralAccessAllowed = false;
|
||||
|
||||
for( WiredModemLocalPeripheral peripheral : m_peripherals ) peripheral.detach();
|
||||
m_node.updatePeripherals( Collections.emptyMap() );
|
||||
}
|
||||
|
||||
updateAnim();
|
||||
node.invalidate();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private Map<String, IPeripheral> getPeripherals()
|
||||
private Set<String> getConnectedPeripheralNames()
|
||||
{
|
||||
if( !m_peripheralAccessAllowed ) return Collections.emptySet();
|
||||
|
||||
Set<String> peripherals = new HashSet<>( 6 );
|
||||
for( WiredModemLocalPeripheral m_peripheral : m_peripherals )
|
||||
{
|
||||
String name = m_peripheral.getConnectedName();
|
||||
if( name != null ) peripherals.add( name );
|
||||
}
|
||||
return peripherals;
|
||||
}
|
||||
|
||||
private Map<String, IPeripheral> getConnectedPeripherals()
|
||||
{
|
||||
if( !m_peripheralAccessAllowed ) return Collections.emptyMap();
|
||||
|
||||
Map<String, IPeripheral> peripherals = new HashMap<>( 6 );
|
||||
for( EnumFacing facing : EnumFacing.VALUES )
|
||||
{
|
||||
BlockPos neighbour = getPos().offset( facing );
|
||||
IPeripheral peripheral = TileCable.getPeripheral( getWorld(), neighbour, facing.getOpposite() );
|
||||
if( peripheral != null && !(peripheral instanceof WiredModemPeripheral) )
|
||||
{
|
||||
String type = peripheral.getType();
|
||||
int id = m_attachedPeripheralIDs[facing.ordinal()];
|
||||
String oldType = m_attachedPeripheralTypes[facing.ordinal()];
|
||||
if( id < 0 || !type.equals( oldType ) )
|
||||
{
|
||||
m_attachedPeripheralTypes[facing.ordinal()] = type;
|
||||
id = m_attachedPeripheralIDs[facing.ordinal()] = IDAssigner.getNextIDFromFile( new File(
|
||||
ComputerCraft.getWorldDir( getWorld() ),
|
||||
"computer/lastid_" + type + ".txt"
|
||||
) );
|
||||
}
|
||||
|
||||
peripherals.put( type + "_" + id, peripheral );
|
||||
}
|
||||
}
|
||||
|
||||
for( WiredModemLocalPeripheral m_peripheral : m_peripherals ) m_peripheral.extendMap( peripherals );
|
||||
return peripherals;
|
||||
}
|
||||
|
||||
private String getCachedPeripheralName( EnumFacing facing )
|
||||
private void updateConnectedPeripherals()
|
||||
{
|
||||
if( !m_peripheralAccessAllowed ) return null;
|
||||
Map<String, IPeripheral> peripherals = getConnectedPeripherals();
|
||||
if( peripherals.isEmpty() )
|
||||
{
|
||||
// If there are no peripherals then disable access and update the display state.
|
||||
m_peripheralAccessAllowed = false;
|
||||
updateAnim();
|
||||
}
|
||||
|
||||
int id = m_attachedPeripheralIDs[facing.ordinal()];
|
||||
String type = m_attachedPeripheralTypes[facing.ordinal()];
|
||||
return id < 0 || type == null ? null : type + "_" + id;
|
||||
m_node.updatePeripherals( peripherals );
|
||||
}
|
||||
|
||||
// IWiredElementTile
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public IWiredElement getWiredElement( @Nonnull EnumFacing side )
|
||||
public boolean hasCapability( @Nonnull Capability<?> capability, @Nullable EnumFacing facing )
|
||||
{
|
||||
return m_element;
|
||||
return capability == CapabilityWiredElement.CAPABILITY || super.hasCapability( capability, facing );
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T getCapability( @Nonnull Capability<T> capability, @Nullable EnumFacing facing )
|
||||
{
|
||||
if( capability == CapabilityWiredElement.CAPABILITY )
|
||||
{
|
||||
return CapabilityWiredElement.CAPABILITY.cast( m_element );
|
||||
}
|
||||
|
||||
return super.getCapability( capability, facing );
|
||||
}
|
||||
|
||||
// IPeripheralTile
|
||||
@@ -402,6 +403,12 @@ public class TileWiredModemFull extends TilePeripheralBase implements IWiredElem
|
||||
{
|
||||
peripheral = m_modems[side.ordinal()] = new WiredModemPeripheral( m_element )
|
||||
{
|
||||
@Override
|
||||
protected boolean canSeePeripheral( @Nonnull String peripheralName )
|
||||
{
|
||||
return !peripheralName.equals( m_peripherals[side.ordinal()].getConnectedName() );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Vec3d getPosition()
|
||||
|
@@ -1,7 +1,7 @@
|
||||
package dan200.computercraft.shared.peripheral.modem;
|
||||
|
||||
import dan200.computercraft.api.network.wired.IWiredNetworkChange;
|
||||
import dan200.computercraft.api.network.wired.IWiredElement;
|
||||
import dan200.computercraft.api.network.wired.IWiredNetworkChange;
|
||||
import dan200.computercraft.api.network.wired.IWiredNode;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.wired.WiredNode;
|
||||
|
@@ -0,0 +1,139 @@
|
||||
package dan200.computercraft.shared.peripheral.modem;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.util.IDAssigner;
|
||||
import dan200.computercraft.shared.util.PeripheralUtil;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.Constants;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a local peripheral exposed on the wired network
|
||||
*
|
||||
* This is responsible for getting the peripheral in world, tracking id and type and determining whether
|
||||
* it has changed.
|
||||
*/
|
||||
public final class WiredModemLocalPeripheral
|
||||
{
|
||||
private int id;
|
||||
private String type;
|
||||
|
||||
private IPeripheral peripheral;
|
||||
|
||||
/**
|
||||
* Attach a new peripheral from the world
|
||||
*
|
||||
* @param world The world to search in
|
||||
* @param origin The position to search from
|
||||
* @param direction The direction so search in
|
||||
* @return Whether the peripheral changed.
|
||||
*/
|
||||
public boolean attach( @Nonnull World world, @Nonnull BlockPos origin, @Nonnull EnumFacing direction )
|
||||
{
|
||||
IPeripheral oldPeripheral = this.peripheral;
|
||||
IPeripheral peripheral = this.peripheral = getPeripheralFrom( world, origin, direction );
|
||||
|
||||
if( peripheral == null )
|
||||
{
|
||||
return oldPeripheral != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
String type = peripheral.getType();
|
||||
int id = this.id;
|
||||
|
||||
if( id > 0 && this.type == null )
|
||||
{
|
||||
// If we had an ID but no type, then just set the type.
|
||||
this.type = type;
|
||||
}
|
||||
else if( id < 0 || !type.equals( this.type ) )
|
||||
{
|
||||
this.type = type;
|
||||
this.id = IDAssigner.getNextIDFromFile( new File(
|
||||
ComputerCraft.getWorldDir( world ),
|
||||
"computer/lastid_" + type + ".txt"
|
||||
) );
|
||||
}
|
||||
|
||||
return oldPeripheral == null || !oldPeripheral.equals( peripheral );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach the current peripheral
|
||||
*
|
||||
* @return Whether the peripheral changed
|
||||
*/
|
||||
public boolean detach()
|
||||
{
|
||||
if( peripheral == null ) return false;
|
||||
peripheral = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getConnectedName()
|
||||
{
|
||||
return peripheral != null ? type + "_" + id : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IPeripheral getPeripheral()
|
||||
{
|
||||
return peripheral;
|
||||
}
|
||||
|
||||
public boolean hasPeripheral()
|
||||
{
|
||||
return peripheral != null;
|
||||
}
|
||||
|
||||
public void extendMap( @Nonnull Map<String, IPeripheral> peripherals )
|
||||
{
|
||||
if( peripheral != null ) peripherals.put( type + "_" + id, peripheral );
|
||||
}
|
||||
|
||||
public Map<String, IPeripheral> toMap()
|
||||
{
|
||||
return peripheral == null
|
||||
? Collections.emptyMap()
|
||||
: Collections.singletonMap( type + "_" + id, peripheral );
|
||||
}
|
||||
|
||||
public void writeNBT( @Nonnull NBTTagCompound tag, @Nonnull String suffix )
|
||||
{
|
||||
if( id >= 0 ) tag.setInteger( "peripheralID" + suffix, id );
|
||||
if( type != null ) tag.setString( "peripheralType" + suffix, type );
|
||||
}
|
||||
|
||||
public void readNBT( @Nonnull NBTTagCompound tag, @Nonnull String suffix )
|
||||
{
|
||||
id = tag.hasKey( "peripheralID" + suffix, Constants.NBT.TAG_ANY_NUMERIC )
|
||||
? tag.getInteger( "peripheralID" + suffix ) : -1;
|
||||
|
||||
type = tag.hasKey( "peripheralType" + suffix, Constants.NBT.TAG_STRING )
|
||||
? tag.getString( "peripheralType" + suffix ) : null;
|
||||
}
|
||||
|
||||
private static IPeripheral getPeripheralFrom( World world, BlockPos pos, EnumFacing direction )
|
||||
{
|
||||
BlockPos offset = pos.offset( direction );
|
||||
|
||||
Block block = world.getBlockState( offset ).getBlock();
|
||||
if( block == ComputerCraft.Blocks.wiredModemFull || block == ComputerCraft.Blocks.cable ) return null;
|
||||
|
||||
IPeripheral peripheral = PeripheralUtil.getPeripheral( world, offset, direction.getOpposite() );
|
||||
return peripheral instanceof WiredModemPeripheral ? null : peripheral;
|
||||
}
|
||||
}
|
@@ -10,7 +10,6 @@ import dan200.computercraft.api.network.wired.IWiredNode;
|
||||
import dan200.computercraft.api.network.wired.IWiredSender;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -20,7 +19,7 @@ import java.util.Map;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
|
||||
|
||||
public class WiredModemPeripheral extends ModemPeripheral implements IWiredSender
|
||||
public abstract class WiredModemPeripheral extends ModemPeripheral implements IWiredSender
|
||||
{
|
||||
private final WiredModemElement modem;
|
||||
|
||||
@@ -57,12 +56,7 @@ public class WiredModemPeripheral extends ModemPeripheral implements IWiredSende
|
||||
return modem.getWorld();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Vec3d getPosition()
|
||||
{
|
||||
return modem.getPosition();
|
||||
}
|
||||
protected abstract boolean canSeePeripheral( @Nonnull String peripheralName );
|
||||
//endregion
|
||||
|
||||
//region IPeripheral
|
||||
@@ -223,7 +217,7 @@ public class WiredModemPeripheral extends ModemPeripheral implements IWiredSende
|
||||
|
||||
private void attachPeripheralImpl( String periphName, IPeripheral peripheral )
|
||||
{
|
||||
if( !peripheralWrappers.containsKey( periphName ) )
|
||||
if( !peripheralWrappers.containsKey( periphName ) && canSeePeripheral( periphName ) )
|
||||
{
|
||||
RemotePeripheralWrapper wrapper = new RemotePeripheralWrapper( modem, peripheral, getComputer(), periphName );
|
||||
peripheralWrappers.put( periphName, wrapper );
|
||||
|
@@ -1,17 +1,22 @@
|
||||
package dan200.computercraft.shared.peripheral.monitor;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.common.ClientTerminal;
|
||||
import gnu.trove.set.hash.TIntHashSet;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
public class ClientMonitor extends ClientTerminal
|
||||
{
|
||||
private static final TIntHashSet displayLists = new TIntHashSet();
|
||||
private static final Set<ClientMonitor> allMonitors = new HashSet<>();
|
||||
|
||||
private final TileMonitor origin;
|
||||
|
||||
public long lastRenderFrame = -1;
|
||||
public int renderDisplayList = -1;
|
||||
public BlockPos lastRenderPos = null;
|
||||
public int[] renderDisplayLists = null;
|
||||
|
||||
public ClientMonitor( boolean colour, TileMonitor origin )
|
||||
{
|
||||
@@ -24,11 +29,60 @@ public class ClientMonitor extends ClientTerminal
|
||||
return origin;
|
||||
}
|
||||
|
||||
public void createLists()
|
||||
{
|
||||
if( renderDisplayLists == null )
|
||||
{
|
||||
renderDisplayLists = new int[3];
|
||||
|
||||
for( int i = 0; i < renderDisplayLists.length; i++ )
|
||||
{
|
||||
renderDisplayLists[i] = GlStateManager.glGenLists( 1 );
|
||||
}
|
||||
|
||||
synchronized( allMonitors )
|
||||
{
|
||||
allMonitors.add( this );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy()
|
||||
{
|
||||
if( renderDisplayList != -1 )
|
||||
if( renderDisplayLists != null )
|
||||
{
|
||||
ComputerCraft.deleteDisplayLists( renderDisplayList, 3 );
|
||||
synchronized( allMonitors )
|
||||
{
|
||||
allMonitors.remove( this );
|
||||
}
|
||||
|
||||
for( int list : renderDisplayLists )
|
||||
{
|
||||
GlStateManager.glDeleteLists( list, 1 );
|
||||
}
|
||||
|
||||
renderDisplayLists = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void destroyAll()
|
||||
{
|
||||
synchronized( allMonitors )
|
||||
{
|
||||
for( Iterator<ClientMonitor> iterator = allMonitors.iterator(); iterator.hasNext(); )
|
||||
{
|
||||
ClientMonitor monitor = iterator.next();
|
||||
if( monitor.renderDisplayLists != null )
|
||||
{
|
||||
for( int list : monitor.renderDisplayLists )
|
||||
{
|
||||
GlStateManager.glDeleteLists( list, 1 );
|
||||
}
|
||||
monitor.renderDisplayLists = null;
|
||||
}
|
||||
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -243,7 +243,7 @@ public class MonitorPeripheral implements IPeripheral
|
||||
case 24:
|
||||
{
|
||||
// getTextScale
|
||||
return new Object[] { monitor.getTextScale() };
|
||||
return new Object[] { monitor.getTextScale() / 2.0 };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@@ -371,6 +371,15 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack withFamily( @Nonnull ItemStack stack, @Nonnull ComputerFamily family )
|
||||
{
|
||||
return PocketComputerItemFactory.create(
|
||||
getComputerID( stack ), getLabel( stack ), getColour( stack ),
|
||||
family, getUpgrade( stack )
|
||||
);
|
||||
}
|
||||
|
||||
// IMedia
|
||||
|
||||
@Override
|
||||
|
@@ -34,7 +34,7 @@ public class PocketComputerUpgradeRecipe extends IForgeRegistryEntry.Impl<IRecip
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden()
|
||||
public boolean isDynamic()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -10,6 +10,8 @@ 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.CommandComputer;
|
||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||
import dan200.computercraft.shared.command.ContainerViewComputer;
|
||||
import dan200.computercraft.shared.common.ColourableRecipe;
|
||||
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
||||
@@ -49,8 +51,9 @@ import dan200.computercraft.shared.pocket.recipes.PocketComputerUpgradeRecipe;
|
||||
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
|
||||
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
|
||||
import dan200.computercraft.shared.util.*;
|
||||
import dan200.computercraft.shared.wired.DefaultWiredProvider;
|
||||
import dan200.computercraft.shared.wired.CapabilityWiredElement;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.command.CommandHandler;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
@@ -63,6 +66,7 @@ import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.IRecipe;
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
import net.minecraft.network.play.server.SPacketUpdateTileEntity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.*;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -115,6 +119,14 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
||||
registerForgeHandlers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initServer( MinecraftServer server )
|
||||
{
|
||||
CommandHandler handler = (CommandHandler) server.getCommandManager();
|
||||
handler.registerCommand( new CommandComputer() );
|
||||
handler.registerCommand( new CommandComputerCraft() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract boolean isClient();
|
||||
|
||||
@@ -124,11 +136,6 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
||||
@Override
|
||||
public abstract long getRenderFrame();
|
||||
|
||||
@Override
|
||||
public void deleteDisplayLists( int list, int range )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract Object getFixedWidthFontRenderer();
|
||||
|
||||
@@ -159,7 +166,8 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
||||
packet.m_dataInt = new int[] { pos.getX(), pos.getY(), pos.getZ() };
|
||||
}
|
||||
|
||||
ComputerCraft.sendToAllPlayers( packet );
|
||||
NetworkRegistry.TargetPoint point = new NetworkRegistry.TargetPoint( world.provider.getDimension(), pos.getX(), pos.getY(), pos.getZ(), 64 );
|
||||
ComputerCraft.sendToAllAround( packet, point );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -490,7 +498,7 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
||||
ComputerCraftAPI.registerMediaProvider( new DefaultMediaProvider() );
|
||||
|
||||
// Register network providers
|
||||
ComputerCraftAPI.registerWiredProvider( new DefaultWiredProvider() );
|
||||
CapabilityWiredElement.register();
|
||||
}
|
||||
|
||||
private void registerForgeHandlers()
|
||||
|
@@ -16,6 +16,7 @@ import dan200.computercraft.shared.turtle.blocks.TileTurtle;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -28,11 +29,11 @@ public interface IComputerCraftProxy
|
||||
{
|
||||
void preInit();
|
||||
void init();
|
||||
void initServer( MinecraftServer server );
|
||||
boolean isClient();
|
||||
|
||||
boolean getGlobalCursorBlink();
|
||||
long getRenderFrame();
|
||||
void deleteDisplayLists( int list, int range );
|
||||
Object getFixedWidthFontRenderer();
|
||||
|
||||
String getRecordInfo( @Nonnull ItemStack item );
|
||||
|
@@ -15,6 +15,7 @@ import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.turtle.event.TurtleAction;
|
||||
import dan200.computercraft.api.turtle.event.TurtleActionEvent;
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
import dan200.computercraft.shared.turtle.core.*;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -161,70 +162,83 @@ public class TurtleAPI implements ILuaAPI
|
||||
case 0:
|
||||
{
|
||||
// forward
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleMoveCommand( MoveDirection.Forward ) );
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// back
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleMoveCommand( MoveDirection.Back ) );
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// up
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleMoveCommand( MoveDirection.Up ) );
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// down
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleMoveCommand( MoveDirection.Down ) );
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
// turnLeft
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleTurnCommand( TurnDirection.Left ) );
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
// turnRight
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleTurnCommand( TurnDirection.Right ) );
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
// dig
|
||||
Optional<TurtleSide> side = parseSide( args, 0 );
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleDigCommand( InteractDirection.Forward, side ) );
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
// digUp
|
||||
Optional<TurtleSide> side = parseSide( args, 0 );
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleDigCommand( InteractDirection.Up, side ) );
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
// digDown
|
||||
Optional<TurtleSide> side = parseSide( args, 0 );
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleDigCommand( InteractDirection.Down, side ) );
|
||||
}
|
||||
case 9:
|
||||
{
|
||||
// place
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtlePlaceCommand( InteractDirection.Forward, args ) );
|
||||
}
|
||||
case 10:
|
||||
{
|
||||
// placeUp
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtlePlaceCommand( InteractDirection.Up, args ) );
|
||||
}
|
||||
case 11:
|
||||
{
|
||||
// placeDown
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtlePlaceCommand( InteractDirection.Down, args ) );
|
||||
}
|
||||
case 12:
|
||||
{
|
||||
// drop
|
||||
int count = parseCount( args, 0 );
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleDropCommand( InteractDirection.Forward, count ) );
|
||||
}
|
||||
case 13:
|
||||
@@ -294,48 +308,56 @@ public class TurtleAPI implements ILuaAPI
|
||||
{
|
||||
// attack
|
||||
Optional<TurtleSide> side = parseSide( args, 0 );
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleAttackCommand( InteractDirection.Forward, side ) );
|
||||
}
|
||||
case 23:
|
||||
{
|
||||
// attackUp
|
||||
Optional<TurtleSide> side = parseSide( args, 0 );
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleAttackCommand( InteractDirection.Up, side ) );
|
||||
}
|
||||
case 24:
|
||||
{
|
||||
// attackDown
|
||||
Optional<TurtleSide> side = parseSide( args, 0 );
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleAttackCommand( InteractDirection.Down, side ) );
|
||||
}
|
||||
case 25:
|
||||
{
|
||||
// dropUp
|
||||
int count = parseCount( args, 0 );
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleDropCommand( InteractDirection.Up, count ) );
|
||||
}
|
||||
case 26:
|
||||
{
|
||||
// dropDown
|
||||
int count = parseCount( args, 0 );
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleDropCommand( InteractDirection.Down, count ) );
|
||||
}
|
||||
case 27:
|
||||
{
|
||||
// suck
|
||||
int count = parseCount( args, 0 );
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleSuckCommand( InteractDirection.Forward, count ) );
|
||||
}
|
||||
case 28:
|
||||
{
|
||||
// suckUp
|
||||
int count = parseCount( args, 0 );
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleSuckCommand( InteractDirection.Up, count ) );
|
||||
}
|
||||
case 29:
|
||||
{
|
||||
// suckDown
|
||||
int count = parseCount( args, 0 );
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleSuckCommand( InteractDirection.Down, count ) );
|
||||
}
|
||||
case 30:
|
||||
@@ -389,11 +411,13 @@ public class TurtleAPI implements ILuaAPI
|
||||
case 36:
|
||||
{
|
||||
// equipLeft
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleEquipCommand( TurtleSide.Left ) );
|
||||
}
|
||||
case 37:
|
||||
{
|
||||
// equipRight
|
||||
m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
||||
return tryCommand( context, new TurtleEquipCommand( TurtleSide.Right ) );
|
||||
}
|
||||
case 38:
|
||||
|
@@ -11,10 +11,16 @@ import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
import dan200.computercraft.shared.util.WorldUtil;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.IMerchant;
|
||||
import net.minecraft.entity.passive.AbstractHorse;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntitySign;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.IInteractionObject;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
@@ -27,7 +33,7 @@ public class TurtlePlayer extends FakePlayer
|
||||
{
|
||||
public final static GameProfile DEFAULT_PROFILE = new GameProfile(
|
||||
UUID.fromString( "0d0c4ca0-4ff1-11e4-916c-0800200c9a66" ),
|
||||
"ComputerCraft"
|
||||
"[ComputerCraft]"
|
||||
);
|
||||
|
||||
@Deprecated
|
||||
@@ -87,6 +93,12 @@ public class TurtlePlayer extends FakePlayer
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3d getPositionVector()
|
||||
{
|
||||
return new Vec3d( posX, posY, posZ );
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getEyeHeight()
|
||||
{
|
||||
@@ -99,6 +111,63 @@ public class TurtlePlayer extends FakePlayer
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEnterCombat()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEndCombat()
|
||||
{
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public SleepResult trySleep( @Nonnull BlockPos bedLocation )
|
||||
{
|
||||
return SleepResult.OTHER_PROBLEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openEditSign( TileEntitySign signTile )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayGui( IInteractionObject guiOwner )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayGUIChest( IInventory chestInventory )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayVillagerTradeGui( IMerchant villager )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openGuiHorseInventory( AbstractHorse horse, IInventory inventoryIn )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openBook( ItemStack stack, @Nonnull EnumHand hand )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHeldItem()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onItemUseFinish()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mountEntityAndWakeUp()
|
||||
{
|
||||
@@ -108,9 +177,4 @@ public class TurtlePlayer extends FakePlayer
|
||||
public void dismountEntity( @Nonnull Entity entity )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openEditSign( TileEntitySign signTile )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@@ -170,6 +170,17 @@ public abstract class ItemTurtleBase extends ItemComputerBase implements ITurtle
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack withFamily( @Nonnull ItemStack stack, @Nonnull ComputerFamily family )
|
||||
{
|
||||
return TurtleItemFactory.create(
|
||||
getComputerID( stack ), getLabel( stack ),
|
||||
getColour( stack ), family,
|
||||
getUpgrade( stack, TurtleSide.Left ), getUpgrade( stack, TurtleSide.Right ),
|
||||
getFuelLevel( stack ), getOverlay( stack )
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack setColour( ItemStack stack, int colour )
|
||||
{
|
||||
|
@@ -7,82 +7,41 @@
|
||||
package dan200.computercraft.shared.turtle.recipes;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.items.IComputerItem;
|
||||
import dan200.computercraft.shared.computer.recipe.ComputerConvertRecipe;
|
||||
import dan200.computercraft.shared.turtle.items.TurtleItemFactory;
|
||||
import dan200.computercraft.shared.util.RecipeUtil;
|
||||
import net.minecraft.inventory.InventoryCrafting;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.IRecipe;
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
import net.minecraft.item.crafting.ShapedRecipes;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.crafting.CraftingHelper;
|
||||
import net.minecraftforge.common.crafting.IRecipeFactory;
|
||||
import net.minecraftforge.common.crafting.JsonContext;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class TurtleRecipe extends ShapedRecipes
|
||||
public class TurtleRecipe extends ComputerConvertRecipe
|
||||
{
|
||||
private final NonNullList<Ingredient> m_recipe;
|
||||
private final ComputerFamily m_family;
|
||||
private final ComputerFamily family;
|
||||
|
||||
public TurtleRecipe( String group, int width, int height, NonNullList<Ingredient> recipe, ComputerFamily family )
|
||||
public TurtleRecipe( String group, @Nonnull CraftingHelper.ShapedPrimer primer, ComputerFamily family )
|
||||
{
|
||||
super( group, width, height, recipe, TurtleItemFactory.create( -1, null, -1, family, null, null, 0, null ) );
|
||||
m_recipe = recipe;
|
||||
m_family = family;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches( @Nonnull InventoryCrafting _inventory, @Nonnull World world )
|
||||
{
|
||||
return !getCraftingResult( _inventory ).isEmpty();
|
||||
super( group, primer, TurtleItemFactory.create( -1, null, -1, family, null, null, 0, null ) );
|
||||
this.family = family;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ItemStack getCraftingResult( @Nonnull InventoryCrafting inventory )
|
||||
protected ItemStack convert( @Nonnull ItemStack stack )
|
||||
{
|
||||
// See if we match the recipe, and extract the input computercraft ID
|
||||
int computerID = -1;
|
||||
String label = null;
|
||||
for( int y = 0; y < 3; ++y )
|
||||
{
|
||||
for( int x = 0; x < 3; ++x )
|
||||
{
|
||||
ItemStack item = inventory.getStackInRowAndColumn( x, y );
|
||||
Ingredient target = m_recipe.get( x + y * 3 );
|
||||
IComputerItem item = (IComputerItem) stack.getItem();
|
||||
int computerID = item.getComputerID( stack );
|
||||
String label = item.getLabel( stack );
|
||||
|
||||
if( item.getItem() instanceof IComputerItem )
|
||||
{
|
||||
IComputerItem itemComputer = (IComputerItem) item.getItem();
|
||||
if( itemComputer.getFamily( item ) != m_family ) return ItemStack.EMPTY;
|
||||
if( family == ComputerFamily.Beginners ) computerID = -1;
|
||||
|
||||
computerID = itemComputer.getComputerID( item );
|
||||
label = itemComputer.getLabel( item );
|
||||
}
|
||||
else if( !target.apply( item ) )
|
||||
{
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build a turtle with the same ID the computer had
|
||||
// Construct the new stack
|
||||
if( m_family != ComputerFamily.Beginners )
|
||||
{
|
||||
return TurtleItemFactory.create( computerID, label, -1, m_family, null, null, 0, null );
|
||||
}
|
||||
else
|
||||
{
|
||||
return TurtleItemFactory.create( -1, label, -1, m_family, null, null, 0, null );
|
||||
}
|
||||
return TurtleItemFactory.create( computerID, label, -1, family, null, null, 0, null );
|
||||
}
|
||||
|
||||
public static class Factory implements IRecipeFactory
|
||||
@@ -91,20 +50,10 @@ public class TurtleRecipe extends ShapedRecipes
|
||||
public IRecipe parse( JsonContext context, JsonObject json )
|
||||
{
|
||||
String group = JsonUtils.getString( json, "group", "" );
|
||||
|
||||
String familyName = JsonUtils.getString( json, "family" );
|
||||
ComputerFamily family;
|
||||
try
|
||||
{
|
||||
family = ComputerFamily.valueOf( familyName );
|
||||
}
|
||||
catch( IllegalArgumentException e )
|
||||
{
|
||||
throw new JsonSyntaxException( "Unknown computer family '" + familyName + "'" );
|
||||
}
|
||||
|
||||
ComputerFamily family = RecipeUtil.getFamily( json, "family" );
|
||||
CraftingHelper.ShapedPrimer primer = RecipeUtil.getPrimer( context, json );
|
||||
return new TurtleRecipe( group, primer.width, primer.height, primer.input, family );
|
||||
|
||||
return new TurtleRecipe( group, primer, family );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -37,7 +37,7 @@ public class TurtleUpgradeRecipe extends IForgeRegistryEntry.Impl<IRecipe> imple
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden()
|
||||
public boolean isDynamic()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ package dan200.computercraft.shared.util;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.*;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.NonNullList;
|
||||
@@ -102,4 +103,17 @@ public class RecipeUtil
|
||||
|
||||
return ings;
|
||||
}
|
||||
|
||||
public static ComputerFamily getFamily( JsonObject json, String name )
|
||||
{
|
||||
String familyName = JsonUtils.getString( json, name );
|
||||
try
|
||||
{
|
||||
return ComputerFamily.valueOf( familyName );
|
||||
}
|
||||
catch( IllegalArgumentException e )
|
||||
{
|
||||
throw new JsonSyntaxException( "Unknown computer family '" + familyName + "' for field " + name );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -126,22 +126,8 @@ public class WorldUtil
|
||||
}
|
||||
|
||||
public static Vec3d getRayEnd( EntityPlayer player) {
|
||||
double reach = 4.5;
|
||||
if( player instanceof EntityPlayerMP )
|
||||
{
|
||||
reach = ((EntityPlayerMP) player).interactionManager.getBlockReachDistance();
|
||||
}
|
||||
else if( player.getEntityWorld().isRemote )
|
||||
{
|
||||
reach = Minecraft.getMinecraft().playerController.getBlockReachDistance();
|
||||
}
|
||||
else if( player.capabilities.isCreativeMode )
|
||||
{
|
||||
reach = 5.0;
|
||||
}
|
||||
|
||||
double reach = player.getEntityAttribute(EntityPlayer.REACH_DISTANCE).getAttributeValue();
|
||||
Vec3d look = player.getLookVec();
|
||||
|
||||
return getRayStart( player ).addVector( look.x * reach, look.y * reach, look.z * reach );
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,69 @@
|
||||
package dan200.computercraft.shared.wired;
|
||||
|
||||
import dan200.computercraft.api.network.wired.IWiredElement;
|
||||
import dan200.computercraft.api.network.wired.IWiredNode;
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.CapabilityInject;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class CapabilityWiredElement
|
||||
{
|
||||
@CapabilityInject( IWiredElement.class )
|
||||
public static Capability<IWiredElement> CAPABILITY = null;
|
||||
|
||||
public static void register()
|
||||
{
|
||||
CapabilityManager.INSTANCE.register( IWiredElement.class, new NullStorage(), NullElement::new );
|
||||
}
|
||||
|
||||
private static class NullElement implements IWiredElement
|
||||
{
|
||||
@Nonnull
|
||||
@Override
|
||||
public IWiredNode getNode()
|
||||
{
|
||||
throw new IllegalStateException( "Should not use the default element implementation" );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public World getWorld()
|
||||
{
|
||||
throw new IllegalStateException( "Should not use the default element implementation" );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Vec3d getPosition()
|
||||
{
|
||||
throw new IllegalStateException( "Should not use the default element implementation" );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getSenderID()
|
||||
{
|
||||
throw new IllegalStateException( "Should not use the default element implementation" );
|
||||
}
|
||||
}
|
||||
|
||||
private static class NullStorage implements Capability.IStorage<IWiredElement>
|
||||
{
|
||||
@Override
|
||||
public NBTBase writeNBT( Capability<IWiredElement> capability, IWiredElement instance, EnumFacing side )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNBT( Capability<IWiredElement> capability, IWiredElement instance, EnumFacing side, NBTBase base )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
package dan200.computercraft.shared.wired;
|
||||
|
||||
import dan200.computercraft.api.network.wired.IWiredElement;
|
||||
import dan200.computercraft.api.network.wired.IWiredElementTile;
|
||||
import dan200.computercraft.api.network.wired.IWiredProvider;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class DefaultWiredProvider implements IWiredProvider
|
||||
{
|
||||
@Nullable
|
||||
@Override
|
||||
public IWiredElement getElement( @Nonnull IBlockAccess world, @Nonnull BlockPos pos, @Nonnull EnumFacing side )
|
||||
{
|
||||
TileEntity te = world.getTileEntity( pos );
|
||||
return te instanceof IWiredElementTile ? ((IWiredElementTile) te).getWiredElement( side ) : null;
|
||||
}
|
||||
}
|
@@ -1,5 +1,7 @@
|
||||
package dan200.computercraft.shared.wired;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import dan200.computercraft.api.network.Packet;
|
||||
import dan200.computercraft.api.network.wired.IWiredNetwork;
|
||||
import dan200.computercraft.api.network.wired.IWiredNode;
|
||||
@@ -283,9 +285,10 @@ public final class WiredNetwork implements IWiredNetwork
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate( @Nonnull IWiredNode node )
|
||||
public void updatePeripherals( @Nonnull IWiredNode node, @Nonnull Map<String, IPeripheral> newPeripherals )
|
||||
{
|
||||
WiredNode wired = checkNode( node );
|
||||
Preconditions.checkNotNull( peripherals, "peripherals cannot be null" );
|
||||
|
||||
lock.writeLock().lock();
|
||||
try
|
||||
@@ -293,11 +296,10 @@ public final class WiredNetwork implements IWiredNetwork
|
||||
if( wired.network != this ) throw new IllegalStateException( "Node is not on this network" );
|
||||
|
||||
Map<String, IPeripheral> oldPeripherals = wired.peripherals;
|
||||
Map<String, IPeripheral> newPeripherals = wired.element.getPeripherals();
|
||||
WiredNetworkChange change = WiredNetworkChange.changeOf( oldPeripherals, newPeripherals );
|
||||
if( change.isEmpty() ) return;
|
||||
|
||||
wired.peripherals = newPeripherals;
|
||||
wired.peripherals = ImmutableMap.copyOf( newPeripherals );
|
||||
|
||||
// Detach the old peripherals then remove them.
|
||||
peripherals.keySet().removeAll( change.peripheralsRemoved().keySet() );
|
||||
|
@@ -10,6 +10,8 @@ import java.util.Map;
|
||||
|
||||
public class WiredNetworkChange implements IWiredNetworkChange
|
||||
{
|
||||
private static final WiredNetworkChange EMPTY = new WiredNetworkChange( Collections.emptyMap(), Collections.emptyMap() );
|
||||
|
||||
private final Map<String, IPeripheral> removed;
|
||||
private final Map<String, IPeripheral> added;
|
||||
|
||||
@@ -26,16 +28,30 @@ public class WiredNetworkChange implements IWiredNetworkChange
|
||||
|
||||
public static WiredNetworkChange added( Map<String, IPeripheral> added )
|
||||
{
|
||||
return new WiredNetworkChange( Collections.emptyMap(), Collections.unmodifiableMap( added ) );
|
||||
return added.isEmpty() ? EMPTY : new WiredNetworkChange( Collections.emptyMap(), Collections.unmodifiableMap( added ) );
|
||||
}
|
||||
|
||||
public static WiredNetworkChange removed( Map<String, IPeripheral> removed )
|
||||
{
|
||||
return new WiredNetworkChange( Collections.unmodifiableMap( removed ), Collections.emptyMap() );
|
||||
return removed.isEmpty() ? EMPTY : new WiredNetworkChange( Collections.unmodifiableMap( removed ), Collections.emptyMap() );
|
||||
}
|
||||
|
||||
public static WiredNetworkChange changeOf( Map<String, IPeripheral> oldPeripherals, Map<String, IPeripheral> newPeripherals )
|
||||
{
|
||||
// Handle the trivial cases, where all peripherals have been added or removed.
|
||||
if( oldPeripherals.isEmpty() && newPeripherals.isEmpty() )
|
||||
{
|
||||
return EMPTY;
|
||||
}
|
||||
else if( oldPeripherals.isEmpty() )
|
||||
{
|
||||
return new WiredNetworkChange( Collections.emptyMap(), newPeripherals );
|
||||
}
|
||||
else if( newPeripherals.isEmpty() )
|
||||
{
|
||||
return new WiredNetworkChange( oldPeripherals, Collections.emptyMap() );
|
||||
}
|
||||
|
||||
Map<String, IPeripheral> added = new HashMap<>( newPeripherals );
|
||||
Map<String, IPeripheral> removed = new HashMap<>();
|
||||
|
||||
|
@@ -662,8 +662,36 @@ end
|
||||
if http then
|
||||
local nativeHTTPRequest = http.request
|
||||
|
||||
local function wrapRequest( _url, _post, _headers, _binary )
|
||||
local ok, err = nativeHTTPRequest( _url, _post, _headers, _binary )
|
||||
local methods = {
|
||||
GET = true, POST = true, HEAD = true,
|
||||
OPTIONS = true, PUT = true, DELETE = true
|
||||
}
|
||||
|
||||
local function checkKey( options, key, ty, opt )
|
||||
local value = options[key]
|
||||
local valueTy = type(value)
|
||||
|
||||
if (value ~= nil or not opt) and valueTy ~= ty then
|
||||
error(("bad field '%s' (expected %s, got %s"):format(key, ty, valueTy), 4)
|
||||
end
|
||||
end
|
||||
|
||||
local function checkOptions( options, body )
|
||||
checkKey( options, "url", "string")
|
||||
if body == false
|
||||
then checkKey( options, "body", "nil" )
|
||||
else checkKey( options, "body", "string", not body ) end
|
||||
checkKey( options, "headers", "table", true )
|
||||
checkKey( options, "method", "string", true )
|
||||
checkKey( options, "redirect", "boolean", true )
|
||||
|
||||
if options.method and not methods[options.method] then
|
||||
error( "Unsupported HTTP method", 3 )
|
||||
end
|
||||
end
|
||||
|
||||
local function wrapRequest( _url, ... )
|
||||
local ok, err = nativeHTTPRequest( ... )
|
||||
if ok then
|
||||
while true do
|
||||
local event, param1, param2, param3 = os.pullEvent()
|
||||
@@ -678,6 +706,11 @@ if http then
|
||||
end
|
||||
|
||||
http.get = function( _url, _headers, _binary)
|
||||
if type( _url ) == "table" then
|
||||
checkOptions( _url, false )
|
||||
return wrapRequest( _url.url, _url )
|
||||
end
|
||||
|
||||
if type( _url ) ~= "string" then
|
||||
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
||||
end
|
||||
@@ -687,10 +720,15 @@ if http then
|
||||
if _binary ~= nil and type( _binary ) ~= "boolean" then
|
||||
error( "bad argument #3 (expected boolean, got " .. type( _binary ) .. ")", 2 )
|
||||
end
|
||||
return wrapRequest( _url, nil, _headers, _binary)
|
||||
return wrapRequest( _url, _url, nil, _headers, _binary )
|
||||
end
|
||||
|
||||
http.post = function( _url, _post, _headers, _binary)
|
||||
if type( _url ) == "table" then
|
||||
checkOptions( _url, true )
|
||||
return wrapRequest( _url.url, _url )
|
||||
end
|
||||
|
||||
if type( _url ) ~= "string" then
|
||||
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
||||
end
|
||||
@@ -703,25 +741,34 @@ if http then
|
||||
if _binary ~= nil and type( _binary ) ~= "boolean" then
|
||||
error( "bad argument #4 (expected boolean, got " .. type( _binary ) .. ")", 2 )
|
||||
end
|
||||
return wrapRequest( _url, _post or "", _headers, _binary)
|
||||
return wrapRequest( _url, _url, _post, _headers, _binary )
|
||||
end
|
||||
|
||||
http.request = function( _url, _post, _headers, _binary )
|
||||
if type( _url ) ~= "string" then
|
||||
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
||||
end
|
||||
if _post ~= nil and type( _post ) ~= "string" then
|
||||
error( "bad argument #2 (expected string, got " .. type( _post ) .. ")", 2 )
|
||||
end
|
||||
if _headers ~= nil and type( _headers ) ~= "table" then
|
||||
error( "bad argument #3 (expected table, got " .. type( _headers ) .. ")", 2 )
|
||||
end
|
||||
if _binary ~= nil and type( _binary ) ~= "boolean" then
|
||||
error( "bad argument #4 (expected boolean, got " .. type( _binary ) .. ")", 2 )
|
||||
local url
|
||||
if type( _url ) == "table" then
|
||||
checkOptions( _url )
|
||||
url = _url.url
|
||||
else
|
||||
if type( _url ) ~= "string" then
|
||||
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
||||
end
|
||||
if _post ~= nil and type( _post ) ~= "string" then
|
||||
error( "bad argument #2 (expected string, got " .. type( _post ) .. ")", 2 )
|
||||
end
|
||||
if _headers ~= nil and type( _headers ) ~= "table" then
|
||||
error( "bad argument #3 (expected table, got " .. type( _headers ) .. ")", 2 )
|
||||
end
|
||||
if _binary ~= nil and type( _binary ) ~= "boolean" then
|
||||
error( "bad argument #4 (expected boolean, got " .. type( _binary ) .. ")", 2 )
|
||||
end
|
||||
|
||||
url = _url.url
|
||||
end
|
||||
|
||||
local ok, err = nativeHTTPRequest( _url, _post, _headers, _binary )
|
||||
if not ok then
|
||||
os.queueEvent( "http_failure", _url, err )
|
||||
os.queueEvent( "http_failure", url, err )
|
||||
end
|
||||
return ok, err
|
||||
end
|
||||
|
@@ -16,7 +16,7 @@ local tKeys = {
|
||||
"comma", "period", "slash", "rightShift","multiply", -- 51
|
||||
"leftAlt", "space", "capsLock", "f1", "f2", -- 56
|
||||
"f3", "f4", "f5", "f6", "f7", -- 61
|
||||
"f8", "f9", "f10", "numLock", "scollLock", -- 66
|
||||
"f8", "f9", "f10", "numLock", "scrollLock", -- 66
|
||||
"numPad7", "numPad8", "numPad9", "numPadSubtract","numPad4", -- 71
|
||||
"numPad5", "numPad6", "numPadAdd","numPad1", "numPad2", -- 76
|
||||
"numPad3", "numPad0", "numPadDecimal",nil, nil, -- 81
|
||||
@@ -31,7 +31,7 @@ local tKeys = {
|
||||
nil, nil, nil, nil, nil, -- 126
|
||||
nil, nil, nil, nil, nil, -- 131
|
||||
nil, nil, nil, nil, nil, -- 136
|
||||
"numPadEquals",nil, nil, "cimcumflex","at", -- 141
|
||||
"numPadEquals",nil, nil, "circumflex","at", -- 141
|
||||
"colon", "underscore","kanji", "stop", "ax", -- 146
|
||||
nil, nil, nil, nil, nil, -- 151
|
||||
"numPadEnter","rightCtrl",nil, nil, nil, -- 156
|
||||
@@ -53,6 +53,9 @@ for nKey, sKey in pairs( tKeys ) do
|
||||
keys[sKey] = nKey
|
||||
end
|
||||
keys["return"] = keys.enter
|
||||
--backwards compatibility to earlier, typo prone, versions
|
||||
keys.scollLock = keys.scrollLock
|
||||
keys.cimcumflex = keys.circumflex
|
||||
|
||||
function getName( _nKey )
|
||||
if type( _nKey ) ~= "number" then
|
||||
|
@@ -1,5 +1,7 @@
|
||||
wget is a program for downloading files from the internet. This is useful for downloading programs created by other players.
|
||||
If no filename is specified wget will try to determine the filename from the URL by stripping any anchors, parameters and trailing slashes and then taking everything remaining after the last slash.
|
||||
The HTTP API must be enabled in ComputerCraft.cfg to use this program.
|
||||
|
||||
ex:
|
||||
"wget http://pastebin.com/raw/CxaWmPrX test" will download the file from the URL http://pastebin.com/raw/CxaWmPrX, and save it as "test".
|
||||
"wget http://example.org/test.lua/?foo=bar#qzu" will download the file from the URL http://example.org/test.lua/?foo=bar#qzu and save it as "test.lua"
|
||||
"wget http://example.org/" will download the file from the URL http://example.org and save it as "example.org"
|
||||
|
@@ -1,11 +1,11 @@
|
||||
|
||||
local function printUsage()
|
||||
print( "Usage:" )
|
||||
print( "wget <url> <filename>" )
|
||||
print( "wget <url> [filename]" )
|
||||
end
|
||||
|
||||
local tArgs = { ... }
|
||||
if #tArgs < 2 then
|
||||
if #tArgs < 1 then
|
||||
printUsage()
|
||||
return
|
||||
end
|
||||
@@ -15,19 +15,15 @@ if not http then
|
||||
printError( "Set http_enable to true in ComputerCraft.cfg" )
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
local function getFilename( sUrl )
|
||||
sUrl = sUrl:gsub( "[#?].*" , "" ):gsub( "/+$" , "" )
|
||||
return sUrl:match( "/([^/]+)$" )
|
||||
end
|
||||
|
||||
local function get( sUrl )
|
||||
write( "Connecting to " .. sUrl .. "... " )
|
||||
|
||||
local ok, err = http.checkURL( sUrl )
|
||||
if not ok then
|
||||
print( "Failed." )
|
||||
if err then
|
||||
printError( err )
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local response = http.get( sUrl , nil , true )
|
||||
if not response then
|
||||
print( "Failed." )
|
||||
@@ -40,10 +36,18 @@ local function get( sUrl )
|
||||
response.close()
|
||||
return sResponse
|
||||
end
|
||||
|
||||
|
||||
-- Determine file to download
|
||||
local sUrl = tArgs[1]
|
||||
local sFile = tArgs[2]
|
||||
|
||||
--Check if the URL is valid
|
||||
local ok, err = http.checkURL( sUrl )
|
||||
if not ok then
|
||||
printError( err or "Invalid URL." )
|
||||
return
|
||||
end
|
||||
|
||||
local sFile = tArgs[2] or getFilename( sUrl )
|
||||
local sPath = shell.resolve( sFile )
|
||||
if fs.exists( sPath ) then
|
||||
print( "File already exists" )
|
||||
|
@@ -2,6 +2,10 @@
|
||||
"recipes" : {
|
||||
"impostor_shaped" : "dan200.computercraft.shared.util.ImpostorRecipe$Factory",
|
||||
"impostor_shapeless" : "dan200.computercraft.shared.util.ImpostorShapelessRecipe$Factory",
|
||||
"turtle" : "dan200.computercraft.shared.turtle.recipes.TurtleRecipe$Factory"
|
||||
"turtle" : "dan200.computercraft.shared.turtle.recipes.TurtleRecipe$Factory",
|
||||
"computer_upgrade" : "dan200.computercraft.shared.computer.recipe.ComputerFamilyRecipe$Factory"
|
||||
},
|
||||
"ingredients": {
|
||||
"computer": "dan200.computercraft.shared.computer.recipe.ComputerIngredient$Factory"
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"type": "computercraft:computer_upgrade",
|
||||
"pattern": [
|
||||
"###",
|
||||
"#C#",
|
||||
"# #"
|
||||
],
|
||||
"key": {
|
||||
"#": { "type": "forge:ore_dict", "ore": "ingotGold" },
|
||||
"C": { "type": "computercraft:computer", "item": "computercraft:computer", "family": "Normal" }
|
||||
},
|
||||
"family": "Advanced",
|
||||
"result": { "item": "computercraft:computer", "data": 16384 }
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"type": "computercraft:computer_upgrade",
|
||||
"pattern": [
|
||||
"###",
|
||||
"#C#",
|
||||
"# #"
|
||||
],
|
||||
"key": {
|
||||
"#": { "type": "forge:ore_dict", "ore": "ingotGold" },
|
||||
"C": { "item": "computercraft:pocket_computer", "data": 0 }
|
||||
},
|
||||
"family": "Advanced",
|
||||
"result": { "item": "computercraft:pocket_computer", "data": 1 }
|
||||
}
|
@@ -7,7 +7,7 @@
|
||||
],
|
||||
"key": {
|
||||
"#": { "type": "forge:ore_dict", "ore": "ingotGold" },
|
||||
"C": { "item": "computercraft:computer", "data": 16384 },
|
||||
"C": { "type": "computercraft:computer", "item": "computercraft:computer", "data": 16384, "family": "Advanced" },
|
||||
"I": { "type": "forge:ore_dict", "ore": "chestWood" }
|
||||
},
|
||||
"family": "Advanced"
|
||||
|
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"type": "computercraft:computer_upgrade",
|
||||
"pattern": [
|
||||
"###",
|
||||
"#C#",
|
||||
" B "
|
||||
],
|
||||
"key": {
|
||||
"#": { "type": "forge:ore_dict", "ore": "ingotGold" },
|
||||
"B": { "type": "forge:ore_dict", "ore": "blockGold" },
|
||||
"C": [
|
||||
{ "type": "computercraft:computer", "item": "computercraft:turtle", "family": "Normal" },
|
||||
{ "item": "computercraft:turtle_expanded", "data": 0 }
|
||||
]
|
||||
},
|
||||
"family": "Advanced",
|
||||
"result": { "item": "computercraft:turtle_advanced", "data": 0 }
|
||||
}
|
@@ -7,7 +7,7 @@
|
||||
],
|
||||
"key": {
|
||||
"#": { "type": "forge:ore_dict", "ore": "ingotIron" },
|
||||
"C": { "item": "computercraft:computer", "data": 0 },
|
||||
"C": { "type": "computercraft:computer", "item": "computercraft:computer", "family": "Normal" },
|
||||
"I": { "type": "forge:ore_dict", "ore": "chestWood" }
|
||||
},
|
||||
"family": "Normal"
|
||||
|
@@ -23,7 +23,6 @@ import org.junit.Test;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
@@ -379,17 +378,10 @@ public class NetworkTest
|
||||
remotePeripherals.putAll( change.peripheralsAdded() );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Map<String, IPeripheral> getPeripherals()
|
||||
{
|
||||
return Collections.unmodifiableMap( localPeripherals );
|
||||
}
|
||||
|
||||
public NetworkElement addPeripheral( String name )
|
||||
{
|
||||
localPeripherals.put( name, new NetworkPeripheral() );
|
||||
getNode().invalidate();
|
||||
getNode().updatePeripherals( localPeripherals );
|
||||
return this;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user