1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-16 06:27:39 +00:00

Compare commits

...

16 Commits

Author SHA1 Message Date
SquidDev
8ae65d1bdd A couple of minor cleanups on the last commit
- Remove a redundant logger
 - Provide a getter for the ComputerCraft thread group. This allows us
   to monitor child threads within prometheus.
 - Replace a deprecated call with a fastutils alternative.
2018-11-22 12:48:06 +00:00
SquidDev
0829506176 Remove last usage of Trove
Minecraft uses it internally, so we can rely on this always being
around. I do not belive Trove exists within 1.13.
2018-11-22 12:15:55 +00:00
SquidDev
71ee692da0 Some more improvements to threading
- Put all ComputerCraft threads into a thread group
 - Be a little more aggressive in when we cleanup/abandon threads.
2018-11-22 12:09:24 +00:00
SquidDev
acd0092ed5 Add back JarMount for backwards compatibility
Some mods (*cough* Computronics *cough*) directly access this class,
rather than using the API. We add this back to ensure they still behave
as expected.

Truth be told, I can't really complain, as Plethora also does dodgy
thing with CC internals.
2018-11-22 10:06:43 +00:00
SquidDev
1160ffbf9e Bump version and require a minimum version of Forge 2018-11-21 18:11:14 +00:00
SquidDev
93cb6547bd Improvements for coroutine creation
- Keep track of the number of created and destroyed coroutines for each
   computer.
 - Run coroutines with a thread pool executor, which will keep stale
   threads around for 60 seconds. This substantially reduces the
   pressure from short-lived coroutines.
 - Update to the latest Cobalt version.
2018-11-21 16:47:06 +00:00
SquidDev
f9c91f288f Try to remove some locking
Ideally we would be able to avoid locking at all on the main thread, but
needs must.
2018-11-19 20:56:44 +00:00
SquidDev
62cf921cc6 Allow modems to handle being attached to multiple computers
- Introduce a ModemState, which shares the open channels across all
   modem instances of a wired modem.
 - Keep a set of computers for all modem peripherals.
 - Keep a map of computers -> (string, peripheral) for wired modem
   peripherals. We shouldn't need this one, as you cannot attach one
   modem to another, but it's good to be consistent.

One major change here is that modems will continue to be "on", even if
no computers are attached. This would substantially increase
implementation complexity, so I think this is an acceptable compromise
for now.

Should fix #74
2018-11-17 11:52:12 +00:00
SquidDev
4bd7381827 Add support for Forge's ISelectiveResourceReloadListener
This means we don't reload turtles every time someone just reloads
shaders or the language pack.
2018-11-17 10:54:42 +00:00
SquidDev
43459ec825 Fix read methods failing on malformed unicode.
Java configured the charset decoders/encoders for streams to REPLACE
malformed characters rather than the default REPORT. It does not do the
same for channels, and so we were catching an IO exception and returning
null.
2018-11-16 20:58:49 +00:00
SquidDev
5fa01f8b96 Truncate files when writing them
This is performed by default, but as we don't pass any options, this
flag is removed.

Closes #75
2018-11-16 13:09:12 +00:00
SquidDev
67d5693d2a Fix tile entities being registered with incorrect names
We'd somehow added spaces, which means they weren't registered under the
computercraft domain (rather, the "computercraft " one). We also create
a datafixer to ensure old worlds are handled correctly.
2018-11-16 12:29:29 +00:00
SquidDev
c2a782afa4 Remove some old scripts
These were part of the original CC repo, but aren't really needed now
that everything important (LuaJ, bundling API docs+source), has been
moved to Gradle.

We also remove the LuaJ lib. It's sad to see it go, but it was rather
redundant. We're keeping the LuaJ sources for now, as I don't really
want a really large diff.
2018-11-16 11:43:42 +00:00
SquidDev
14c9558ee6 Bump version 2018-11-03 15:44:36 +00:00
SquidDev
d53a73e7e7 Fix resource handle loading
See #70
2018-10-29 15:59:43 +00:00
SquidDev
7e334bd4a5 Fix a couple of other bugs with the fs rewrite
- Fix stdin not being considered a "readable" input
 - Return an unsigned byte rather than a signed one for no-args .read()
2018-10-28 08:39:44 +00:00
45 changed files with 999 additions and 559 deletions

12
.gitignore vendored
View File

@@ -1,12 +1,12 @@
build
out
run
deploy
/build
/out
/run
*.ipr
*.iws
*.iml
.idea
.gradle
luaj-2.0.3/lib
luaj-2.0.3/*.jar
/luaj-2.0.3/lib
/luaj-2.0.3/*.jar
*.DS_Store
/test-files

View File

@@ -23,7 +23,7 @@ apply plugin: 'org.ajoberstar.grgit'
apply plugin: 'maven-publish'
apply plugin: 'maven'
version = "1.80pr1.9"
version = "1.80pr1.11"
group = "org.squiddev"
archivesBaseName = "cc-tweaked"
@@ -66,7 +66,7 @@ dependencies {
runtime "mezz.jei:jei_1.12.2:4.8.5.159"
shade 'org.squiddev:Cobalt:0.3.2'
shade 'org.squiddev:Cobalt:0.4.0'
testCompile 'junit:junit:4.11'

View File

@@ -1,12 +0,0 @@
#!/bin/sh
cd luaj-2.0.3
echo "Building LuaJ..."
ant clean
ant
echo "Copying output to libs..."
rm ../libs/luaj-jse-2.0.3.jar
cp luaj-jse-2.0.3.jar ../libs
echo "Done."
cd ..

View File

@@ -1,10 +0,0 @@
#!/bin/sh
echo "Java code:"
cat `find src | grep \\.java$` | wc
echo "Lua code:"
cat `find src/main/resources/assets/computercraft/lua | grep \\.lua$` | wc
echo "JSON:"
cat `find src/main/resources/assets/computercraft | grep \\.json$` | wc

View File

@@ -1,21 +0,0 @@
#!/bin/sh
echo "Building with gradle..."
rm -rf build/libs
rm -rf build/resources
rm -rf build/classes
chmod -R +rw src/main/resources
chmod +x gradlew
./gradlew build
echo "Deleting old deployment..."
rm -rf deploy
mkdir deploy
echo "Making new deployment..."
INPUTJAR=`ls -1 build/libs | grep -v sources`
OUTPUTJAR=`ls -1 build/libs | grep -v sources | sed s/\-//g`
FRIENDLYNAME=`ls -1 build/libs | grep -v sources | sed s/\-/\ /g | sed s/\.jar//g`
cp build/libs/$INPUTJAR deploy/$OUTPUTJAR
echo "Done."

Binary file not shown.

View File

@@ -1,7 +0,0 @@
echo "Setting up IntelliJ development environment with gradle..."
rmdir /s /q .\build
call gradlew.bat --stacktrace setupDecompWorkspace --refresh-dependencies
call gradlew.bat --stacktrace cleanIdea idea
echo "Done."
pause

View File

@@ -1,14 +0,0 @@
#!/bin/sh
echo "Setting permissions..."
chmod +x codesize.sh
chmod +x build_luaj.sh
chmod +x deploy.sh
chmod +x gradlew
echo "Setting up IntelliJ development environment with gradle..."
rm -rf build
./gradlew --stacktrace setupDecompWorkspace --refresh-dependencies
./gradlew --stacktrace cleanIdea idea
echo "Done."

View File

@@ -29,8 +29,6 @@ import dan200.computercraft.core.filesystem.FileMount;
import dan200.computercraft.core.filesystem.FileSystemMount;
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;
import dan200.computercraft.shared.computer.blocks.BlockCommandComputer;
import dan200.computercraft.shared.computer.blocks.BlockComputer;
@@ -60,7 +58,10 @@ import dan200.computercraft.shared.proxy.IComputerCraftProxy;
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.util.CreativeTabMain;
import dan200.computercraft.shared.util.IDAssigner;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.WorldUtil;
import dan200.computercraft.shared.wired.CapabilityWiredElement;
import dan200.computercraft.shared.wired.WiredNode;
import io.netty.buffer.Unpooled;
@@ -99,7 +100,6 @@ import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.ProviderNotFoundException;
import java.util.*;
import java.util.function.Function;
import java.util.zip.ZipEntry;
@@ -111,12 +111,12 @@ import java.util.zip.ZipFile;
@Mod(
modid = ComputerCraft.MOD_ID, name = "CC: Tweaked", version = "${version}",
guiFactory = "dan200.computercraft.client.gui.GuiConfigCC$Factory"
guiFactory = "dan200.computercraft.client.gui.GuiConfigCC$Factory",
dependencies = "required:forge@[14.23.4.2746,)"
)
public class ComputerCraft
{
public static final String MOD_ID = "computercraft";
public static final String LOWER_ID = "computercraft";
// GUI IDs
public static final int diskDriveGUIID = 100;
@@ -570,7 +570,7 @@ public class ComputerCraft
width = terminal.getWidth();
height = terminal.getHeight();
}
// Pack useful terminal information into the various coordinate bits.
// These are extracted in ComputerCraftProxyCommon.getClientGuiElement
player.openGui( ComputerCraft.instance, ComputerCraft.viewComputerGUIID, player.getEntityWorld(),
@@ -928,7 +928,7 @@ public class ComputerCraft
FileSystem fs = FileSystems.newFileSystem( modJar.toPath(), ComputerCraft.class.getClassLoader() );
mounts.add( new FileSystemMount( fs, subPath ) );
}
catch( IOException | ProviderNotFoundException | ServiceConfigurationError e )
catch( IOException | RuntimeException | ServiceConfigurationError e )
{
ComputerCraft.log.error( "Could not load mount from mod jar", e );
// Ignore
@@ -940,16 +940,16 @@ public class ComputerCraft
if( resourcePackDir.exists() && resourcePackDir.isDirectory() )
{
String[] resourcePacks = resourcePackDir.list();
for( String resourcePack1 : resourcePacks )
for( String resourcePackName : resourcePacks )
{
try
{
File resourcePack = new File( resourcePackDir, resourcePack1 );
File resourcePack = new File( resourcePackDir, resourcePackName );
if( !resourcePack.isDirectory() )
{
// Mount a resource pack from a jar
IMount resourcePackMount = new FileSystemMount( FileSystems.getFileSystem( resourcePack.toURI() ), subPath );
mounts.add( resourcePackMount );
FileSystem fs = FileSystems.newFileSystem( resourcePack.toPath(), ComputerCraft.class.getClassLoader() );
mounts.add( new FileSystemMount( fs, subPath ) );
}
else
{
@@ -962,9 +962,9 @@ public class ComputerCraft
}
}
}
catch( IOException e )
catch( IOException | RuntimeException | ServiceConfigurationError e )
{
ComputerCraft.log.error( "Could not load resource pack '" + resourcePack1 + "'", e );
ComputerCraft.log.error( "Could not load resource pack '" + resourcePackName + "'", e );
}
}
}
@@ -1113,7 +1113,7 @@ public class ComputerCraft
{
return turtleProxy.getTurtleUpgrade( id );
}
public static ITurtleUpgrade getTurtleUpgrade( int legacyID )
{
return turtleProxy.getTurtleUpgrade( legacyID );

View File

@@ -8,11 +8,7 @@ package dan200.computercraft.client.proxy;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.*;
import dan200.computercraft.client.render.ItemPocketRenderer;
import dan200.computercraft.client.render.ItemPrintoutRenderer;
import dan200.computercraft.client.render.RenderOverlayCable;
import dan200.computercraft.client.render.TileEntityCableRenderer;
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.client.render.*;
import dan200.computercraft.shared.command.ContainerViewComputer;
import dan200.computercraft.shared.computer.blocks.ComputerState;
import dan200.computercraft.shared.computer.blocks.TileComputer;
@@ -35,7 +31,7 @@ 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 it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiNewChat;
@@ -75,7 +71,7 @@ import java.util.List;
public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
{
private static TIntIntHashMap lastCounts = new TIntIntHashMap();
private static Int2IntOpenHashMap lastCounts = new Int2IntOpenHashMap();
private long m_tick;
private long m_renderFrame;

View File

@@ -18,12 +18,14 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.*;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.IResourceManagerReloadListener;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.client.resource.IResourceType;
import net.minecraftforge.client.resource.ISelectiveResourceReloadListener;
import net.minecraftforge.client.resource.VanillaResourceType;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nonnull;
@@ -32,8 +34,9 @@ import javax.vecmath.Matrix4f;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReloadListener
public class TurtleSmartItemModel implements IBakedModel, ISelectiveResourceReloadListener
{
private static final Matrix4f s_identity, s_flip;
@@ -155,9 +158,9 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
}
@Override
public void onResourceManagerReload( @Nonnull IResourceManager resourceManager )
public void onResourceManagerReload( @Nonnull IResourceManager resourceManager, @Nonnull Predicate<IResourceType> resourcePredicate )
{
m_cachedModels.clear();
if( resourcePredicate.test( VanillaResourceType.MODELS ) ) m_cachedModels.clear();
}
private IBakedModel buildModel( TurtleModelCombination combo )

View File

@@ -33,7 +33,7 @@ public class BinaryReadableHandle extends HandleGeneric
{
super( closeable );
this.m_reader = channel;
this.m_seekable = channel instanceof SeekableByteChannel ? (SeekableByteChannel) channel : null;
this.m_seekable = asSeekable( channel );
}
public BinaryReadableHandle( ReadableByteChannel channel )
@@ -118,7 +118,7 @@ public class BinaryReadableHandle extends HandleGeneric
{
single.clear();
int b = m_reader.read( single );
return b == -1 ? null : new Object[] { single.get( 0 ) };
return b == -1 ? null : new Object[] { single.get( 0 ) & 0xFF };
}
}
catch( IOException e )

View File

@@ -27,7 +27,7 @@ public class BinaryWritableHandle extends HandleGeneric
{
super( closeable );
this.m_writer = channel;
this.m_seekable = channel instanceof SeekableByteChannel ? (SeekableByteChannel) channel : null;
this.m_seekable = asSeekable( channel );
}
public BinaryWritableHandle( WritableByteChannel channel )

View File

@@ -10,6 +10,8 @@ import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import static dan200.computercraft.core.apis.ArgumentHelper.optBoolean;
@@ -35,7 +37,7 @@ public class EncodedReadableHandle extends HandleGeneric
@Nonnull
@Override
public String[] getMethodNames()
{
{
return new String[] {
"readLine",
"readAll",
@@ -160,6 +162,11 @@ public class EncodedReadableHandle extends HandleGeneric
public static BufferedReader open( ReadableByteChannel channel, Charset charset )
{
return new BufferedReader( Channels.newReader( channel, charset.newDecoder(), -1 ) );
// Create a charset decoder with the same properties as StreamDecoder does for
// InputStreams: namely, replace everything instead of erroring.
CharsetDecoder decoder = charset.newDecoder()
.onMalformedInput( CodingErrorAction.REPLACE )
.onUnmappableCharacter( CodingErrorAction.REPLACE );
return new BufferedReader( Channels.newReader( channel, decoder, -1 ) );
}
}

View File

@@ -10,6 +10,8 @@ import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
public class EncodedWritableHandle extends HandleGeneric
@@ -120,6 +122,11 @@ public class EncodedWritableHandle extends HandleGeneric
public static BufferedWriter open( WritableByteChannel channel, Charset charset )
{
return new BufferedWriter( Channels.newWriter( channel, charset.newEncoder(), -1 ) );
// Create a charset encoder with the same properties as StreamEncoder does for
// OutputStreams: namely, replace everything instead of erroring.
CharsetEncoder encoder = charset.newEncoder()
.onMalformedInput( CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
return new BufferedWriter( Channels.newWriter( channel, encoder, -1 ) );
}
}

View File

@@ -6,6 +6,7 @@ import dan200.computercraft.api.lua.LuaException;
import javax.annotation.Nonnull;
import java.io.Closeable;
import java.io.IOException;
import java.nio.channels.Channel;
import java.nio.channels.SeekableByteChannel;
import static dan200.computercraft.core.apis.ArgumentHelper.optInt;
@@ -69,15 +70,31 @@ public abstract class HandleGeneric implements ILuaObject
throw new LuaException( "bad argument #1 to 'seek' (invalid option '" + whence + "'" );
}
return new Object[] { channel.position() };
return new Object[]{ channel.position() };
}
catch( IllegalArgumentException e )
{
return new Object[] { false, "Position is negative" };
return new Object[]{ false, "Position is negative" };
}
catch( IOException e )
{
return null;
}
}
protected static SeekableByteChannel asSeekable( Channel channel )
{
if( !(channel instanceof SeekableByteChannel) ) return null;
SeekableByteChannel seekable = (SeekableByteChannel) channel;
try
{
seekable.position( seekable.position() );
return seekable;
}
catch( IOException | UnsupportedOperationException e )
{
return null;
}
}
}

View File

@@ -8,7 +8,7 @@ package dan200.computercraft.core.apis.http;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import dan200.computercraft.shared.util.ThreadUtils;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
@@ -24,18 +24,14 @@ public final class HTTPExecutor
public static final ListeningExecutorService EXECUTOR = MoreExecutors.listeningDecorator( new ThreadPoolExecutor(
4, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setDaemon( true )
new SynchronousQueue<>(),
ThreadUtils.builder( "HTTP" )
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
.setNameFormat( "ComputerCraft-HTTP-%d" )
.build()
) );
public static final EventLoopGroup LOOP_GROUP = new NioEventLoopGroup( 4, new ThreadFactoryBuilder()
.setDaemon( true )
public static final EventLoopGroup LOOP_GROUP = new NioEventLoopGroup( 4, ThreadUtils.builder( "Netty" )
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
.setNameFormat( "ComputerCraft-Netty-%d" )
.build()
);

View File

@@ -8,13 +8,14 @@ package dan200.computercraft.core.computer;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.shared.util.ThreadUtils;
import java.util.HashSet;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.ThreadFactory;
public class ComputerThread
{
@@ -56,8 +57,8 @@ public class ComputerThread
*/
private static Thread[] s_threads = null;
private static final AtomicInteger s_ManagerCounter = new AtomicInteger( 1 );
private static final AtomicInteger s_DelegateCounter = new AtomicInteger( 1 );
private static final ThreadFactory s_ManagerFactory = ThreadUtils.factory( "Computer-Manager" );
private static final ThreadFactory s_RunnerFactory = ThreadUtils.factory( "Computer-Runner" );
/**
* Start the computer thread
@@ -72,16 +73,12 @@ public class ComputerThread
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];
if( thread == null || !thread.isAlive() )
{
thread = s_threads[i] = new Thread( group, new TaskExecutor(), "ComputerCraft-Computer-Manager-" + s_ManagerCounter.getAndIncrement() );
thread.setDaemon( true );
thread.start();
(s_threads[i] = s_ManagerFactory.newThread( new TaskExecutor() )).start();
}
}
}
@@ -188,12 +185,7 @@ public class ComputerThread
if( thread == null || !thread.isAlive() )
{
runner = new TaskRunner();
SecurityManager manager = System.getSecurityManager();
final ThreadGroup group = manager == null ? Thread.currentThread().getThreadGroup() : manager.getThreadGroup();
Thread thread = this.thread = new Thread( group, runner, "ComputerCraft-Computer-Runner" + s_DelegateCounter.getAndIncrement() );
thread.setDaemon( true );
thread.start();
(thread = s_RunnerFactory.newThread( runner )).start();
}
long start = System.nanoTime();

View File

@@ -6,6 +6,7 @@
package dan200.computercraft.core.filesystem;
import com.google.common.collect.Sets;
import dan200.computercraft.api.filesystem.IWritableMount;
import javax.annotation.Nonnull;
@@ -13,12 +14,18 @@ import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.StandardOpenOption;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public class FileMount implements IWritableMount
{
private static final int MINIMUM_FILE_SIZE = 500;
private static final Set<OpenOption> READ_OPTIONS = Collections.singleton( StandardOpenOption.READ );
private static final Set<OpenOption> WRITE_OPTIONS = Sets.newHashSet( StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING );
private static final Set<OpenOption> APPEND_OPTIONS = Sets.newHashSet( StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND );
private class WritableCountingChannel implements WritableByteChannel
{
@@ -252,7 +259,7 @@ public class FileMount implements IWritableMount
File file = getRealPath( path );
if( file.exists() && !file.isDirectory() )
{
return FileChannel.open( file.toPath(), StandardOpenOption.READ );
return FileChannel.open( file.toPath(), READ_OPTIONS );
}
}
throw new IOException( "/" + path + ": No such file" );
@@ -386,8 +393,7 @@ public class FileMount implements IWritableMount
m_usedSpace -= Math.max( file.length(), MINIMUM_FILE_SIZE );
m_usedSpace += MINIMUM_FILE_SIZE;
}
return new SeekableCountingChannel( Files.newByteChannel( file.toPath(), StandardOpenOption.WRITE, StandardOpenOption.CREATE ),
MINIMUM_FILE_SIZE );
return new SeekableCountingChannel( Files.newByteChannel( file.toPath(), WRITE_OPTIONS ), MINIMUM_FILE_SIZE );
}
}
@@ -409,8 +415,10 @@ public class FileMount implements IWritableMount
else
{
// Allowing seeking when appending is not recommended, so we use a separate channel.
return new WritableCountingChannel( Files.newByteChannel( file.toPath(), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND ),
Math.max( MINIMUM_FILE_SIZE - file.length(), 0 ) );
return new WritableCountingChannel(
Files.newByteChannel( file.toPath(), APPEND_OPTIONS ),
Math.max( MINIMUM_FILE_SIZE - file.length(), 0 )
);
}
}
else

View File

@@ -0,0 +1,250 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.filesystem;
import dan200.computercraft.api.filesystem.IMount;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@Deprecated
public class JarMount implements IMount
{
private static class FileInZip
{
private String m_path;
private boolean m_directory;
private long m_size;
private Map<String, FileInZip> m_children;
public FileInZip( String path, boolean directory, long size )
{
m_path = path;
m_directory = directory;
m_size = m_directory ? 0 : size;
m_children = new LinkedHashMap<>();
}
public String getPath()
{
return m_path;
}
public boolean isDirectory()
{
return m_directory;
}
public long getSize()
{
return m_size;
}
public void list( List<String> contents )
{
contents.addAll( m_children.keySet() );
}
public void insertChild( FileInZip child )
{
String localPath = FileSystem.toLocal( child.getPath(), m_path );
m_children.put( localPath, child );
}
public FileInZip getFile( String path )
{
// If we've reached the target, return this
if( path.equals( m_path ) )
{
return this;
}
// Otherwise, get the next component of the path
String localPath = FileSystem.toLocal( path, m_path );
int slash = localPath.indexOf( "/" );
if( slash >= 0 )
{
localPath = localPath.substring( 0, slash );
}
// And recurse down using it
FileInZip subFile = m_children.get( localPath );
if( subFile != null )
{
return subFile.getFile( path );
}
return null;
}
public FileInZip getParent( String path )
{
if( path.length() == 0 )
{
return null;
}
FileInZip file = getFile( FileSystem.getDirectory( path ) );
if( file.isDirectory() )
{
return file;
}
return null;
}
}
private ZipFile m_zipFile;
private FileInZip m_root;
private String m_rootPath;
@Deprecated
public JarMount( File jarFile, String subPath ) throws IOException
{
if( !jarFile.exists() || jarFile.isDirectory() )
{
throw new FileNotFoundException();
}
// Open the zip file
try
{
m_zipFile = new ZipFile( jarFile );
}
catch( Exception e )
{
throw new IOException( "Error loading zip file" );
}
if( m_zipFile.getEntry( subPath ) == null )
{
m_zipFile.close();
throw new IOException( "Zip does not contain path" );
}
// Read in all the entries
Enumeration<? extends ZipEntry> zipEntries = m_zipFile.entries();
while( zipEntries.hasMoreElements() )
{
ZipEntry entry = zipEntries.nextElement();
String entryName = entry.getName();
if( entryName.startsWith( subPath ) )
{
entryName = FileSystem.toLocal( entryName, subPath );
if( m_root == null )
{
if( entryName.equals( "" ) )
{
m_root = new FileInZip( entryName, entry.isDirectory(), entry.getSize() );
m_rootPath = subPath;
if( !m_root.isDirectory() )
{
break;
}
}
else
{
// TODO: handle this case. The code currently assumes we find the root before anything else
}
}
else
{
FileInZip parent = m_root.getParent( entryName );
if( parent != null )
{
parent.insertChild( new FileInZip( entryName, entry.isDirectory(), entry.getSize() ) );
}
else
{
// TODO: handle this case. The code currently assumes we find folders before their contents
}
}
}
}
}
// IMount implementation
@Override
public boolean exists( @Nonnull String path )
{
FileInZip file = m_root.getFile( path );
return file != null;
}
@Override
public boolean isDirectory( @Nonnull String path )
{
FileInZip file = m_root.getFile( path );
if( file != null )
{
return file.isDirectory();
}
return false;
}
@Override
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
{
FileInZip file = m_root.getFile( path );
if( file != null && file.isDirectory() )
{
file.list( contents );
}
else
{
throw new IOException( "/" + path + ": Not a directory" );
}
}
@Override
public long getSize( @Nonnull String path ) throws IOException
{
FileInZip file = m_root.getFile( path );
if( file != null )
{
return file.getSize();
}
throw new IOException( "/" + path + ": No such file" );
}
@Nonnull
@Override
public InputStream openForRead( @Nonnull String path ) throws IOException
{
FileInZip file = m_root.getFile( path );
if( file != null && !file.isDirectory() )
{
try
{
String fullPath = m_rootPath;
if( path.length() > 0 )
{
fullPath = fullPath + "/" + path;
}
ZipEntry entry = m_zipFile.getEntry( fullPath );
if( entry != null )
{
return m_zipFile.getInputStream( entry );
}
}
catch( Exception e )
{
// treat errors as non-existance of file
}
}
throw new IOException( "/" + path + ": No such file" );
}
}

View File

@@ -7,14 +7,13 @@
package dan200.computercraft.core.lua;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaObject;
import dan200.computercraft.api.lua.ILuaTask;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.*;
import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.ITask;
import dan200.computercraft.core.computer.MainThread;
import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.core.tracking.TrackingField;
import dan200.computercraft.shared.util.ThreadUtils;
import org.squiddev.cobalt.*;
import org.squiddev.cobalt.compiler.CompileException;
import org.squiddev.cobalt.compiler.LoadState;
@@ -25,7 +24,7 @@ import org.squiddev.cobalt.function.LibFunction;
import org.squiddev.cobalt.function.LuaFunction;
import org.squiddev.cobalt.function.VarArgFunction;
import org.squiddev.cobalt.lib.*;
import org.squiddev.cobalt.lib.platform.AbstractResourceManipulator;
import org.squiddev.cobalt.lib.platform.VoidResourceManipulator;
import javax.annotation.Nonnull;
import java.io.IOException;
@@ -35,6 +34,9 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import static org.squiddev.cobalt.Constants.NONE;
import static org.squiddev.cobalt.ValueFactory.valueOf;
@@ -42,12 +44,19 @@ import static org.squiddev.cobalt.ValueFactory.varargsOf;
public class CobaltLuaMachine implements ILuaMachine
{
private static final ThreadPoolExecutor coroutines = new ThreadPoolExecutor(
0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<>(),
ThreadUtils.factory( "Coroutine" )
);
private final Computer m_computer;
private final LuaState m_state;
private final LuaTable m_globals;
private LuaState m_state;
private LuaTable m_globals;
private LuaThread m_mainRoutine;
private String m_eventFilter;
private String m_softAbortMessage;
private String m_hardAbortMessage;
@@ -57,60 +66,71 @@ public class CobaltLuaMachine implements ILuaMachine
m_computer = computer;
// Create an environment to run in
final LuaState state = this.m_state = new LuaState( new AbstractResourceManipulator()
{
@Override
public InputStream findResource( String filename )
LuaState state = this.m_state = LuaState.builder()
.resourceManipulator( new VoidResourceManipulator() )
.debug( new DebugHandler()
{
return null;
}
} );
state.debug = new DebugHandler( state )
{
private int count = 0;
private boolean hasSoftAbort;
private int count = 0;
private boolean hasSoftAbort;
@Override
public void onInstruction( DebugState ds, DebugFrame di, int pc, Varargs extras, int top ) throws LuaError
{
int count = ++this.count;
if( count > 100000 )
@Override
public void onInstruction( DebugState ds, DebugFrame di, int pc, Varargs extras, int top ) throws LuaError
{
if( m_hardAbortMessage != null ) LuaThread.yield( state, NONE );
this.count = 0;
int count = ++this.count;
if( count > 100000 )
{
if( m_hardAbortMessage != null ) LuaThread.yield( m_state, NONE );
this.count = 0;
}
else
{
handleSoftAbort();
}
super.onInstruction( ds, di, pc, extras, top );
}
else
@Override
public void poll() throws LuaError
{
if( m_hardAbortMessage != null ) LuaThread.yield( m_state, NONE );
handleSoftAbort();
}
super.onInstruction( ds, di, pc, extras, top );
}
private void handleSoftAbort() throws LuaError
{
// If the soft abort has been cleared then we can reset our flags and continue.
String message = m_softAbortMessage;
if( message == null )
{
hasSoftAbort = false;
return;
}
@Override
public void poll() throws LuaError
{
if( m_hardAbortMessage != null ) LuaThread.yield( state, NONE );
handleSoftAbort();
}
if( hasSoftAbort && m_hardAbortMessage == null )
{
// If we have fired our soft abort, but we haven't been hard aborted then everything is OK.
return;
}
private void handleSoftAbort() throws LuaError {
// If the soft abort has been cleared then we can reset our flags and continue.
String message = m_softAbortMessage;
if (message == null) {
hasSoftAbort = false;
return;
hasSoftAbort = true;
throw new LuaError( message );
}
if (hasSoftAbort && m_hardAbortMessage == null) {
// If we have fired our soft abort, but we haven't been hard aborted then everything is OK.
return;
}
hasSoftAbort = true;
throw new LuaError(message);
}
};
} )
.coroutineFactory( command -> {
Tracking.addValue( m_computer, TrackingField.COROUTINES_CREATED, 1 );
coroutines.execute( () -> {
try
{
command.run();
}
finally
{
Tracking.addValue( m_computer, TrackingField.COROUTINES_DISPOSED, 1 );
}
} );
} )
.build();
m_globals = new LuaTable();
state.setupThread( m_globals );
@@ -166,10 +186,7 @@ public class CobaltLuaMachine implements ILuaMachine
public void loadBios( InputStream bios )
{
// Begin executing a file (ie, the bios)
if( m_mainRoutine != null )
{
return;
}
if( m_mainRoutine != null ) return;
try
{
@@ -178,30 +195,19 @@ public class CobaltLuaMachine implements ILuaMachine
}
catch( CompileException e )
{
if( m_mainRoutine != null )
{
m_mainRoutine.abandon();
m_mainRoutine = null;
}
unload();
}
catch( IOException e )
{
ComputerCraft.log.warn( "Could not load bios.lua ", e );
if( m_mainRoutine != null )
{
m_mainRoutine.abandon();
m_mainRoutine = null;
}
unload();
}
}
@Override
public void handleEvent( String eventName, Object[] arguments )
{
if( m_mainRoutine == null )
{
return;
}
if( m_mainRoutine == null ) return;
if( m_eventFilter != null && eventName != null && !eventName.equals( m_eventFilter ) && !eventName.equals( "terminate" ) )
{
@@ -228,26 +234,14 @@ public class CobaltLuaMachine implements ILuaMachine
else
{
LuaValue filter = results.arg( 2 );
if( filter.isString() )
{
m_eventFilter = filter.toString();
}
else
{
m_eventFilter = null;
}
m_eventFilter = filter.isString() ? filter.toString() : null;
}
LuaThread mainThread = m_mainRoutine;
if( mainThread.getStatus().equals( "dead" ) )
{
m_mainRoutine = null;
}
if( m_mainRoutine.getStatus().equals( "dead" ) ) unload();
}
catch( LuaError e )
{
m_mainRoutine.abandon();
m_mainRoutine = null;
unload();
}
finally
{
@@ -284,18 +278,18 @@ public class CobaltLuaMachine implements ILuaMachine
@Override
public boolean isFinished()
{
return (m_mainRoutine == null);
return m_mainRoutine == null;
}
@Override
public void unload()
{
if( m_mainRoutine != null )
{
LuaThread mainThread = m_mainRoutine;
mainThread.abandon();
m_mainRoutine = null;
}
if( m_state == null ) return;
m_state.abandon();
m_mainRoutine = null;
m_state = null;
m_globals = null;
}
private LuaTable wrapLuaObject( ILuaObject object )
@@ -691,7 +685,7 @@ public class CobaltLuaMachine implements ILuaMachine
private byte[] bytes;
private int offset, remaining = 0;
public StringInputStream( LuaState state, LuaValue func )
StringInputStream( LuaState state, LuaValue func )
{
this.state = state;
this.func = func;

View File

@@ -1,7 +1,7 @@
package dan200.computercraft.core.tracking;
import dan200.computercraft.core.computer.Computer;
import gnu.trove.map.hash.TObjectLongHashMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
@@ -18,13 +18,13 @@ public class ComputerTracker
private long serverCount;
private long serverTime;
private final TObjectLongHashMap<TrackingField> fields;
private final Object2LongOpenHashMap<TrackingField> fields;
public ComputerTracker( Computer computer )
{
this.computer = new WeakReference<>( computer );
this.computerId = computer.getID();
this.fields = new TObjectLongHashMap<>();
this.fields = new Object2LongOpenHashMap<>();
}
ComputerTracker( ComputerTracker timings )
@@ -39,7 +39,7 @@ public class ComputerTracker
this.serverCount = timings.serverCount;
this.serverTime = timings.serverTime;
this.fields = new TObjectLongHashMap<>( timings.fields );
this.fields = new Object2LongOpenHashMap<>( timings.fields );
}
@Nullable
@@ -90,7 +90,7 @@ public class ComputerTracker
{
synchronized( fields )
{
fields.adjustOrPutValue( field, change, change );
fields.addTo( field, change );
}
}
@@ -106,7 +106,7 @@ public class ComputerTracker
synchronized( fields )
{
return fields.get( field );
return fields.getLong( field );
}
}

View File

@@ -28,6 +28,9 @@ public class TrackingField
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 );
public static final TrackingField COROUTINES_CREATED = TrackingField.of( "coroutines_created", "Coroutines created", x -> String.format( "%4d", x ) );
public static final TrackingField COROUTINES_DISPOSED = TrackingField.of( "coroutines_dead", "Coroutines disposed", x -> String.format( "%4d", x ) );
private final String id;
private final String displayName;
private final LongFunction<String> format;

View File

@@ -0,0 +1,23 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.datafix;
import dan200.computercraft.ComputerCraft;
import net.minecraft.util.datafix.FixTypes;
import net.minecraftforge.common.util.CompoundDataFixer;
import net.minecraftforge.common.util.ModFixs;
public class Fixes
{
public static final int VERSION = 1;
public static void register( CompoundDataFixer fixer )
{
ModFixs fixes = fixer.init( ComputerCraft.MOD_ID, VERSION );
fixes.registerFix( FixTypes.BLOCK_ENTITY, new TileEntityDataFixer() );
}
}

View File

@@ -0,0 +1,39 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.datafix;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.datafix.IFixableData;
import javax.annotation.Nonnull;
import static dan200.computercraft.ComputerCraft.MOD_ID;
import static dan200.computercraft.shared.datafix.Fixes.VERSION;
/**
* Fixes up the botched tile entity IDs from the 1.11 port.
*/
public class TileEntityDataFixer implements IFixableData
{
@Override
public int getFixVersion()
{
return VERSION;
}
@Nonnull
@Override
public NBTTagCompound fixTagCompound( @Nonnull NBTTagCompound tag )
{
String id = tag.getString( "id" );
if( id.startsWith( MOD_ID + " : " ) )
{
tag.setString( "id", id.replaceFirst( MOD_ID + " : ", MOD_ID + ":" ) );
}
return tag;
}
}

View File

@@ -69,7 +69,6 @@ final class BundledCapabilityProvider implements ICapabilityProvider
emitter = emitters[index] = () -> toBytes( tile.getBundledRedstoneOutput( side ) );
}
}
;
return CAPABILITY_EMITTER.cast( emitter );
}

View File

@@ -14,53 +14,43 @@ import dan200.computercraft.api.network.IPacketSender;
import dan200.computercraft.api.network.Packet;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import gnu.trove.set.TIntSet;
import gnu.trove.set.hash.TIntHashSet;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import java.util.HashSet;
import java.util.Set;
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
public abstract class ModemPeripheral
implements IPeripheral, IPacketSender, IPacketReceiver
public abstract class ModemPeripheral implements IPeripheral, IPacketSender, IPacketReceiver
{
private IPacketNetwork m_network;
private IComputerAccess m_computer;
private final TIntSet m_channels;
private final Set<IComputerAccess> m_computers = new HashSet<>( 1 );
private final ModemState m_state;
private boolean m_open;
private boolean m_changed;
public ModemPeripheral()
protected ModemPeripheral( ModemState state )
{
m_network = null;
m_computer = null;
m_channels = new TIntHashSet();
m_open = false;
m_changed = true;
this.m_state = state;
}
public ModemState getModemState()
{
return m_state;
}
private synchronized void setNetwork( IPacketNetwork network )
{
if( m_network != network )
{
// Leave old network
if( m_network != null )
{
m_network.removeReceiver( this );
}
if( m_network == network ) return;
// Set new network
m_network = network;
// Leave old network
if( m_network != null ) m_network.removeReceiver( this );
// Join new network
if( m_network != null )
{
m_network.addReceiver( this );
}
}
// Set new network
m_network = network;
// Join new network
if( m_network != null ) m_network.addReceiver( this );
}
protected void switchNetwork()
@@ -68,39 +58,22 @@ public abstract class ModemPeripheral
setNetwork( getNetwork() );
}
public synchronized void destroy()
public void destroy()
{
setNetwork( null );
m_channels.clear();
m_open = false;
}
public boolean pollChanged()
{
if( m_changed )
{
m_changed = false;
return true;
}
return false;
}
public boolean isActive()
{
return m_computer != null && m_open;
}
@Override
public void receiveSameDimension( @Nonnull Packet packet, double distance )
{
if( packet.getSender() == this ) return;
if( packet.getSender() == this || !m_state.isOpen( packet.getChannel() ) ) return;
synchronized (m_channels)
synchronized( m_computers )
{
if( m_computer != null && m_channels.contains( packet.getChannel() ) )
for( IComputerAccess computer : m_computers )
{
m_computer.queueEvent( "modem_message", new Object[] {
m_computer.getAttachmentName(), packet.getChannel(), packet.getReplyChannel(), packet.getPayload(), distance
computer.queueEvent( "modem_message", new Object[]{
computer.getAttachmentName(), packet.getChannel(), packet.getReplyChannel(), packet.getPayload(), distance
} );
}
}
@@ -109,21 +82,21 @@ public abstract class ModemPeripheral
@Override
public void receiveDifferentDimension( @Nonnull Packet packet )
{
if( packet.getSender() == this ) return;
if( packet.getSender() == this || !m_state.isOpen( packet.getChannel() ) ) return;
synchronized (m_channels)
synchronized( m_computers )
{
if( m_computer != null && m_channels.contains( packet.getChannel() ) )
for( IComputerAccess computer : m_computers )
{
m_computer.queueEvent( "modem_message", new Object[] {
m_computer.getAttachmentName(), packet.getChannel(), packet.getReplyChannel(), packet.getPayload()
computer.queueEvent( "modem_message", new Object[]{
computer.getAttachmentName(), packet.getChannel(), packet.getReplyChannel(), packet.getPayload()
} );
}
}
}
protected abstract IPacketNetwork getNetwork();
// IPeripheral implementation
@Nonnull
@@ -132,12 +105,12 @@ public abstract class ModemPeripheral
{
return "modem";
}
@Nonnull
@Override
public String[] getMethodNames()
{
return new String[] {
return new String[]{
"open",
"isOpen",
"close",
@@ -146,7 +119,7 @@ public abstract class ModemPeripheral
"isWireless",
};
}
private static int parseChannel( Object[] arguments, int index ) throws LuaException
{
int channel = getInt( arguments, index );
@@ -156,7 +129,7 @@ public abstract class ModemPeripheral
}
return channel;
}
@Override
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
{
@@ -166,68 +139,26 @@ public abstract class ModemPeripheral
{
// open
int channel = parseChannel( arguments, 0 );
synchronized( this )
{
if( !m_channels.contains( channel ) )
{
if( m_channels.size() >= 128 )
{
throw new LuaException( "Too many open channels" );
}
m_channels.add( channel );
if( !m_open )
{
m_open = true;
m_changed = true;
}
}
}
m_state.open( channel );
return null;
}
case 1:
{
// isOpen
int channel = parseChannel( arguments, 0 );
synchronized( this )
{
boolean open = m_channels.contains( channel );
return new Object[] { open };
}
return new Object[]{ m_state.isOpen( channel ) };
}
case 2:
{
// close
int channel = parseChannel( arguments, 0 );
synchronized( this )
{
if( m_channels.remove( channel ) )
{
if( m_channels.size() == 0 )
{
m_open = false;
m_changed = true;
}
}
}
m_state.close( channel );
return null;
}
case 3:
{
// closeAll
synchronized( this )
{
if( m_channels.size() > 0 )
{
m_channels.clear();
if( m_open )
{
m_open = false;
m_changed = true;
}
}
}
m_state.closeAll();
return null;
}
case 4:
@@ -235,12 +166,12 @@ public abstract class ModemPeripheral
// transmit
int channel = parseChannel( arguments, 0 );
int replyChannel = parseChannel( arguments, 1 );
Object payload = (arguments.length >= 3) ? arguments[2] : null;
Object payload = arguments.length > 2 ? arguments[2] : null;
synchronized( this )
{
World world = getWorld();
Vec3d position = getPosition();
if( world != null && position != null && m_network != null)
if( world != null && position != null && m_network != null )
{
Packet packet = new Packet( channel, replyChannel, payload, this );
if( isInterdimensional() )
@@ -258,14 +189,8 @@ public abstract class ModemPeripheral
case 5:
{
// isWireless
synchronized( this )
{
if( m_network != null )
{
return new Object[] { m_network.isWireless() };
}
}
return new Object[] { false };
IPacketNetwork network = m_network;
return new Object[]{ network != null && network.isWireless() };
}
default:
{
@@ -273,50 +198,46 @@ public abstract class ModemPeripheral
}
}
}
@Override
public synchronized void attach( @Nonnull IComputerAccess computer )
{
m_computer = computer;
synchronized( m_computers )
{
m_computers.add( computer );
}
setNetwork( getNetwork() );
m_open = !m_channels.isEmpty();
}
@Override
public synchronized void detach( @Nonnull IComputerAccess computer )
{
if( m_network != null )
boolean empty;
synchronized( m_computers )
{
m_network.removeReceiver( this );
m_channels.clear();
m_network = null;
m_computers.remove( computer );
empty = m_computers.isEmpty();
}
m_computer = null;
if( m_open )
{
m_open = false;
m_changed = true;
}
}
public IComputerAccess getComputer()
{
return m_computer;
if( empty ) setNetwork( null );
}
@Nonnull
@Override
public String getSenderID()
{
if( m_computer == null )
synchronized( m_computers )
{
return "unknown";
}
else
{
return m_computer.getID() + "_" + m_computer.getAttachmentName();
if( m_computers.size() != 1 )
{
return "unknown";
}
else
{
IComputerAccess computer = m_computers.iterator().next();
return computer.getID() + "_" + computer.getAttachmentName();
}
}
}
}

View File

@@ -0,0 +1,77 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.peripheral.modem;
import dan200.computercraft.api.lua.LuaException;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.concurrent.atomic.AtomicBoolean;
public class ModemState
{
private boolean open = false;
private AtomicBoolean changed = new AtomicBoolean( true );
private final IntSet channels = new IntOpenHashSet();
private void setOpen( boolean open )
{
if( this.open == open ) return;
this.open = open;
this.changed.set( true );
}
public boolean pollChanged()
{
return changed.getAndSet( false );
}
public boolean isOpen()
{
return open;
}
public boolean isOpen( int channel )
{
synchronized( channels )
{
return channels.contains( channel );
}
}
public void open( int channel ) throws LuaException
{
synchronized( channels )
{
if( !channels.contains( channel ) )
{
if( channels.size() >= 128 ) throw new LuaException( "Too many open channels" );
channels.add( channel );
setOpen( true );
}
}
}
public void close( int channel )
{
synchronized( channels )
{
channels.remove( channel );
if( channels.isEmpty() ) setOpen( false );
}
}
public void closeAll()
{
synchronized( channels )
{
channels.clear();
setOpen( false );
}
}
}

View File

@@ -25,7 +25,7 @@ public class TileAdvancedModem extends TileModemBase
public Peripheral( TileModemBase entity )
{
super( true );
super( new ModemState(), true );
m_entity = entity;
}

View File

@@ -97,7 +97,7 @@ public class TileCable extends TileModemBase
private boolean m_hasDirection = false;
private boolean m_connectionsFormed = false;
private WiredModemElement m_cable;
private IWiredNode m_node;
@@ -106,7 +106,7 @@ public class TileCable extends TileModemBase
{
m_cable = new CableElement( this );
m_node = m_cable.getNode();
return new WiredModemPeripheral( m_cable )
return new WiredModemPeripheral( new ModemState(), m_cable )
{
@Nonnull
@Override
@@ -453,14 +453,8 @@ public class TileCable extends TileModemBase
protected void updateAnim()
{
int anim = 0;
if( m_modem.isActive() )
{
anim += 1;
}
if( m_peripheralAccessAllowed )
{
anim += 2;
}
if( m_modem.getModemState().isOpen() ) anim |= 1;
if( m_peripheralAccessAllowed ) anim |= 2;
setAnim( anim );
}

View File

@@ -56,10 +56,7 @@ public abstract class TileModemBase extends TilePeripheralBase
public void onNeighbourChange()
{
EnumFacing dir = getDirection();
if( !getWorld().isSideSolid(
getPos().offset( dir ),
dir.getOpposite()
) )
if( !getWorld().isSideSolid( getPos().offset( dir ), dir.getOpposite() ) )
{
// Drop everything and remove block
((BlockGeneric)getBlockType()).dropAllItems( getWorld(), getPos(), false );
@@ -79,22 +76,15 @@ public abstract class TileModemBase extends TilePeripheralBase
public void update()
{
super.update();
if( !getWorld().isRemote && m_modem.pollChanged() )
if( !getWorld().isRemote && m_modem.getModemState().pollChanged() )
{
updateAnim();
}
}
protected void updateAnim()
{
if( m_modem.isActive() )
{
setAnim(1);
}
else
{
setAnim(0);
}
setAnim( m_modem.getModemState().isOpen() ? 1 : 0 );
}
@Override
@@ -109,15 +99,6 @@ public abstract class TileModemBase extends TilePeripheralBase
@Override
public IPeripheral getPeripheral( EnumFacing side )
{
if( side == getDirection() )
{
return m_modem;
}
return null;
}
protected boolean isAttached()
{
return (m_modem != null) && (m_modem.getComputer() != null);
return side == getDirection() ? m_modem : null;
}
}

View File

@@ -83,6 +83,7 @@ public class TileWiredModemFull extends TilePeripheralBase
private boolean m_destroyed = false;
private boolean m_connectionsFormed = false;
private final ModemState m_modemState = new ModemState();
private final WiredModemElement m_element = new FullElement( this );
private final IWiredNode m_node = m_element.getNode();
@@ -236,19 +237,8 @@ public class TileWiredModemFull extends TilePeripheralBase
protected void updateAnim()
{
int anim = 0;
for( WiredModemPeripheral modem : m_modems )
{
if( modem != null && modem.isActive() )
{
anim += 1;
break;
}
}
if( m_peripheralAccessAllowed )
{
anim += 2;
}
if( m_modemState.isOpen() ) anim |= 1;
if( m_peripheralAccessAllowed ) anim |= 2;
setAnim( anim );
}
@@ -264,12 +254,7 @@ public class TileWiredModemFull extends TilePeripheralBase
{
if( !getWorld().isRemote )
{
boolean changed = false;
for( WiredModemPeripheral peripheral : m_modems )
{
if( peripheral != null && peripheral.pollChanged() ) changed = true;
}
if( changed ) updateAnim();
if( m_modemState.pollChanged() ) updateAnim();
if( !m_connectionsFormed )
{
@@ -402,7 +387,7 @@ public class TileWiredModemFull extends TilePeripheralBase
if( peripheral == null )
{
WiredModemLocalPeripheral localPeripheral = m_peripherals[side.ordinal()];
peripheral = m_modems[side.ordinal()] = new WiredModemPeripheral( m_element )
peripheral = m_modems[side.ordinal()] = new WiredModemPeripheral( m_modemState, m_element )
{
@Nonnull
@Override

View File

@@ -29,7 +29,7 @@ public class TileWirelessModem extends TileModemBase
public Peripheral( TileModemBase entity )
{
super( false );
super( new ModemState(), false );
m_entity = entity;
}

View File

@@ -18,6 +18,8 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
@@ -25,10 +27,11 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
{
private final WiredModemElement modem;
private final Map<String, RemotePeripheralWrapper> peripheralWrappers = new HashMap<>();
private final Map<IComputerAccess, ConcurrentMap<String, RemotePeripheralWrapper>> peripheralWrappers = new HashMap<>( 1 );
public WiredModemPeripheral( WiredModemElement modem )
public WiredModemPeripheral( ModemState state, WiredModemElement modem )
{
super( state );
this.modem = modem;
}
@@ -88,56 +91,54 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
case 0:
{
// getNamesRemote
synchronized( peripheralWrappers )
Map<String, RemotePeripheralWrapper> wrappers = getWrappers( computer );
Map<Object, Object> table = new HashMap<>();
if( wrappers != null )
{
int idx = 1;
Map<Object, Object> table = new HashMap<>();
for( String name : peripheralWrappers.keySet() )
{
table.put( idx++, name );
}
return new Object[]{ table };
for( String name : wrappers.keySet() ) table.put( idx++, name );
}
return new Object[]{ table };
}
case 1:
{
// isPresentRemote
String type = getTypeRemote( getString( arguments, 0 ) );
return new Object[]{ type != null };
String name = getString( arguments, 0 );
return new Object[]{ getWrapper( computer, name ) != null };
}
case 2:
{
// getTypeRemote
String type = getTypeRemote( getString( arguments, 0 ) );
if( type != null )
{
return new Object[]{ type };
}
return null;
String name = getString( arguments, 0 );
RemotePeripheralWrapper wrapper = getWrapper( computer, name );
return wrapper != null ? new Object[]{ wrapper.getType() } : null;
}
case 3:
{
// getMethodsRemote
String[] methodNames = getMethodNamesRemote( getString( arguments, 0 ) );
if( methodNames != null )
String name = getString( arguments, 0 );
RemotePeripheralWrapper wrapper = getWrapper( computer, name );
if( wrapper == null ) return null;
String[] methodNames = wrapper.getMethodNames();
Map<Object, Object> table = new HashMap<>();
for( int i = 0; i < methodNames.length; ++i )
{
Map<Object, Object> table = new HashMap<>();
for( int i = 0; i < methodNames.length; ++i )
{
table.put( i + 1, methodNames[i] );
}
return new Object[]{ table };
table.put( i + 1, methodNames[i] );
}
return null;
return new Object[]{ table };
}
case 4:
{
// callRemote
String remoteName = getString( arguments, 0 );
String methodName = getString( arguments, 1 );
RemotePeripheralWrapper wrapper = getWrapper( computer, remoteName );
if( wrapper == null ) throw new LuaException( "No peripheral: " + remoteName );
Object[] methodArgs = new Object[arguments.length - 2];
System.arraycopy( arguments, 2, methodArgs, 0, arguments.length - 2 );
return callMethodRemote( remoteName, context, methodName, methodArgs );
return wrapper.callMethod( context, methodName, methodArgs );
}
case 5:
{
@@ -157,29 +158,37 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
public void attach( @Nonnull IComputerAccess computer )
{
super.attach( computer );
ConcurrentMap<String, RemotePeripheralWrapper> wrappers;
synchronized( peripheralWrappers )
{
wrappers = peripheralWrappers.get( computer );
if( wrappers == null ) peripheralWrappers.put( computer, wrappers = new ConcurrentHashMap<>() );
}
synchronized( modem.getRemotePeripherals() )
{
synchronized( peripheralWrappers )
for( Map.Entry<String, IPeripheral> entry : modem.getRemotePeripherals().entrySet() )
{
for( Map.Entry<String, IPeripheral> entry : modem.getRemotePeripherals().entrySet() )
{
attachPeripheralImpl( entry.getKey(), entry.getValue() );
}
attachPeripheralImpl( computer, wrappers, entry.getKey(), entry.getValue() );
}
}
}
@Override
public synchronized void detach( @Nonnull IComputerAccess computer )
public void detach( @Nonnull IComputerAccess computer )
{
Map<String, RemotePeripheralWrapper> wrappers;
synchronized( peripheralWrappers )
{
for( RemotePeripheralWrapper wrapper : peripheralWrappers.values() )
{
wrapper.detach();
}
peripheralWrappers.clear();
wrappers = peripheralWrappers.remove( computer );
}
if( wrappers != null )
{
for( RemotePeripheralWrapper wrapper : wrappers.values() ) wrapper.detach();
wrappers.clear();
}
super.detach( computer );
}
@@ -204,11 +213,12 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
public void attachPeripheral( String name, IPeripheral peripheral )
{
if( getComputer() == null ) return;
synchronized( peripheralWrappers )
{
attachPeripheralImpl( name, peripheral );
for( Map.Entry<IComputerAccess, ConcurrentMap<String, RemotePeripheralWrapper>> entry : peripheralWrappers.entrySet() )
{
attachPeripheralImpl( entry.getKey(), entry.getValue(), name, peripheral );
}
}
}
@@ -216,63 +226,35 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
{
synchronized( peripheralWrappers )
{
RemotePeripheralWrapper wrapper = peripheralWrappers.get( name );
if( wrapper != null )
{
peripheralWrappers.remove( name );
wrapper.detach();
for(ConcurrentMap<String, RemotePeripheralWrapper> wrappers : peripheralWrappers.values()) {
RemotePeripheralWrapper wrapper = wrappers.remove( name );
if( wrapper != null ) wrapper.detach();
}
}
}
private void attachPeripheralImpl( String periphName, IPeripheral peripheral )
private void attachPeripheralImpl( IComputerAccess computer, ConcurrentMap<String, RemotePeripheralWrapper> peripherals, String periphName, IPeripheral peripheral )
{
if( !peripheralWrappers.containsKey( periphName ) && !periphName.equals( getLocalPeripheral().getConnectedName() ) )
if( !peripherals.containsKey( periphName ) && !periphName.equals( getLocalPeripheral().getConnectedName() ) )
{
RemotePeripheralWrapper wrapper = new RemotePeripheralWrapper( modem, peripheral, getComputer(), periphName );
peripheralWrappers.put( periphName, wrapper );
RemotePeripheralWrapper wrapper = new RemotePeripheralWrapper( modem, peripheral, computer, periphName );
peripherals.put( periphName, wrapper );
wrapper.attach();
}
}
private String getTypeRemote( String remoteName )
{
private ConcurrentMap<String, RemotePeripheralWrapper> getWrappers( IComputerAccess computer ) {
synchronized( peripheralWrappers )
{
RemotePeripheralWrapper wrapper = peripheralWrappers.get( remoteName );
if( wrapper != null )
{
return wrapper.getType();
}
return peripheralWrappers.get( computer );
}
return null;
}
private String[] getMethodNamesRemote( String remoteName )
private RemotePeripheralWrapper getWrapper( IComputerAccess computer, String remoteName )
{
synchronized( peripheralWrappers )
{
RemotePeripheralWrapper wrapper = peripheralWrappers.get( remoteName );
if( wrapper != null )
{
return wrapper.getMethodNames();
}
}
return null;
}
private Object[] callMethodRemote( String remoteName, ILuaContext context, String method, Object[] arguments ) throws LuaException, InterruptedException
{
RemotePeripheralWrapper wrapper;
synchronized( peripheralWrappers )
{
wrapper = peripheralWrappers.get( remoteName );
}
if( wrapper != null )
{
return wrapper.callMethod( context, method, arguments );
}
throw new LuaException( "No peripheral: " + remoteName );
ConcurrentMap<String, RemotePeripheralWrapper> wrappers = getWrappers( computer );
return wrappers == null ? null : wrappers.get( remoteName );
}
private static class RemotePeripheralWrapper implements IComputerAccess, IComputerOwned

View File

@@ -15,9 +15,16 @@ public abstract class WirelessModemPeripheral extends ModemPeripheral
{
private boolean m_advanced;
public WirelessModemPeripheral( ModemState state, boolean advanced )
{
super( state );
m_advanced = advanced;
}
@Deprecated
public WirelessModemPeripheral( boolean advanced )
{
m_advanced = advanced;
this( new ModemState(), advanced );
}
@Override

View File

@@ -13,8 +13,9 @@ import dan200.computercraft.api.network.IPacketSender;
import dan200.computercraft.api.network.Packet;
import javax.annotation.Nonnull;
import java.util.HashSet;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class WirelessNetwork implements IPacketNetwork
{
@@ -34,45 +35,34 @@ public class WirelessNetwork implements IPacketNetwork
s_universalNetwork = null;
}
private final Set<IPacketReceiver> m_receivers;
private WirelessNetwork()
{
m_receivers = new HashSet<>();
}
private final Set<IPacketReceiver> m_receivers = Collections.newSetFromMap( new ConcurrentHashMap<>() );
@Override
public synchronized void addReceiver( @Nonnull IPacketReceiver receiver )
public void addReceiver( @Nonnull IPacketReceiver receiver )
{
Preconditions.checkNotNull( receiver, "device cannot be null" );
m_receivers.add( receiver );
}
@Override
public synchronized void removeReceiver( @Nonnull IPacketReceiver receiver )
public void removeReceiver( @Nonnull IPacketReceiver receiver )
{
Preconditions.checkNotNull( receiver, "device cannot be null" );
m_receivers.remove( receiver );
}
@Override
public synchronized void transmitSameDimension( @Nonnull Packet packet, double range )
public void transmitSameDimension( @Nonnull Packet packet, double range )
{
Preconditions.checkNotNull( packet, "packet cannot be null" );
for( IPacketReceiver device : m_receivers )
{
tryTransmit( device, packet, range, false );
}
for( IPacketReceiver device : m_receivers ) tryTransmit( device, packet, range, false );
}
@Override
public synchronized void transmitInterdimensional( @Nonnull Packet packet )
public void transmitInterdimensional( @Nonnull Packet packet )
{
Preconditions.checkNotNull( packet, "packet cannot be null" );
for (IPacketReceiver device : m_receivers)
{
tryTransmit( device, packet, 0, true );
}
for( IPacketReceiver device : m_receivers ) tryTransmit( device, packet, 0, true );
}
private void tryTransmit( IPacketReceiver receiver, Packet packet, double range, boolean interdimensional )

View File

@@ -5,6 +5,7 @@ import dan200.computercraft.api.pocket.IPocketAccess;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.shared.peripheral.PeripheralType;
import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory;
import dan200.computercraft.shared.peripheral.modem.ModemState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
@@ -75,7 +76,8 @@ public class PocketModem implements IPocketUpgrade
modem.setLocation( entity.getEntityWorld(), entity.posX, entity.posY, entity.posZ );
}
access.setLight( modem.isActive() ? 0xBA0000 : -1 );
ModemState state = modem.getModemState();
if( state.pollChanged() ) access.setLight( state.isOpen() ? 0xBA0000 : -1 );
}
}
}

View File

@@ -6,6 +6,7 @@
package dan200.computercraft.shared.pocket.peripherals;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.peripheral.modem.WirelessModemPeripheral;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
@@ -19,7 +20,7 @@ public class PocketModemPeripheral extends WirelessModemPeripheral
public PocketModemPeripheral( boolean advanced )
{
super( advanced );
super( new ModemState(), advanced );
m_world = null;
m_position = new Vec3d( 0.0, 0.0, 0.0 );
}

View File

@@ -469,9 +469,9 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
private void registerTileEntities()
{
// TileEntities
GameRegistry.registerTileEntity( TileTurtle.class, ComputerCraft.LOWER_ID + " : " + "turtle" );
GameRegistry.registerTileEntity( TileTurtleExpanded.class, ComputerCraft.LOWER_ID + " : " + "turtleex" );
GameRegistry.registerTileEntity( TileTurtleAdvanced.class, ComputerCraft.LOWER_ID + " : " + "turtleadv" );
GameRegistry.registerTileEntity( TileTurtle.class, new ResourceLocation( ComputerCraft.MOD_ID, "turtle" ) );
GameRegistry.registerTileEntity( TileTurtleExpanded.class, new ResourceLocation( ComputerCraft.MOD_ID, "turtleex" ) );
GameRegistry.registerTileEntity( TileTurtleAdvanced.class, new ResourceLocation( ComputerCraft.MOD_ID, "turtleadv" ) );
}
private void registerForgeHandlers()

View File

@@ -24,6 +24,7 @@ import dan200.computercraft.shared.computer.core.*;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.items.ItemCommandComputer;
import dan200.computercraft.shared.computer.items.ItemComputer;
import dan200.computercraft.shared.datafix.Fixes;
import dan200.computercraft.shared.integration.charset.IntegrationCharset;
import dan200.computercraft.shared.media.common.DefaultMediaProvider;
import dan200.computercraft.shared.media.inventory.ContainerHeldItem;
@@ -77,6 +78,7 @@ import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.client.event.ConfigChangedEvent;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
@@ -121,6 +123,7 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
registerTileEntities();
registerForgeHandlers();
Fixes.register( FMLCommonHandler.instance().getDataFixer() );
if( Loader.isModLoaded( ModCharset.MODID ) ) IntegrationCharset.register();
}
@@ -478,16 +481,16 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
private void registerTileEntities()
{
// Tile Entities
GameRegistry.registerTileEntity( TileComputer.class, ComputerCraft.LOWER_ID + " : " + "computer" );
GameRegistry.registerTileEntity( TileDiskDrive.class, ComputerCraft.LOWER_ID + " : " + "diskdrive" );
GameRegistry.registerTileEntity( TileWirelessModem.class, ComputerCraft.LOWER_ID + " : " + "wirelessmodem" );
GameRegistry.registerTileEntity( TileMonitor.class, ComputerCraft.LOWER_ID + " : " + "monitor" );
GameRegistry.registerTileEntity( TilePrinter.class, ComputerCraft.LOWER_ID + " : " + "ccprinter" );
GameRegistry.registerTileEntity( TileCable.class, ComputerCraft.LOWER_ID + " : " + "wiredmodem" );
GameRegistry.registerTileEntity( TileCommandComputer.class, ComputerCraft.LOWER_ID + " : " + "command_computer" );
GameRegistry.registerTileEntity( TileAdvancedModem.class, ComputerCraft.LOWER_ID + " : " + "advanced_modem" );
GameRegistry.registerTileEntity( TileSpeaker.class, ComputerCraft.LOWER_ID + " : " + "speaker" );
GameRegistry.registerTileEntity( TileWiredModemFull.class, ComputerCraft.LOWER_ID + " : " + "wired_modem_full" );
GameRegistry.registerTileEntity( TileComputer.class, new ResourceLocation( ComputerCraft.MOD_ID, "computer" ) );
GameRegistry.registerTileEntity( TileDiskDrive.class, new ResourceLocation( ComputerCraft.MOD_ID, "diskdrive" ) );
GameRegistry.registerTileEntity( TileWirelessModem.class, new ResourceLocation( ComputerCraft.MOD_ID, "wirelessmodem" ) );
GameRegistry.registerTileEntity( TileMonitor.class, new ResourceLocation( ComputerCraft.MOD_ID, "monitor" ) );
GameRegistry.registerTileEntity( TilePrinter.class, new ResourceLocation( ComputerCraft.MOD_ID, "ccprinter" ) );
GameRegistry.registerTileEntity( TileCable.class, new ResourceLocation( ComputerCraft.MOD_ID, "wiredmodem" ) );
GameRegistry.registerTileEntity( TileCommandComputer.class, new ResourceLocation( ComputerCraft.MOD_ID, "command_computer" ) );
GameRegistry.registerTileEntity( TileAdvancedModem.class, new ResourceLocation( ComputerCraft.MOD_ID, "advanced_modem" ) );
GameRegistry.registerTileEntity( TileSpeaker.class, new ResourceLocation( ComputerCraft.MOD_ID, "speaker" ) );
GameRegistry.registerTileEntity( TileWiredModemFull.class, new ResourceLocation( ComputerCraft.MOD_ID, "wired_modem_full" ) );
// Register peripheral providers
ComputerCraftAPI.registerPeripheralProvider( new DefaultPeripheralProvider() );

View File

@@ -10,6 +10,7 @@ import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.*;
import dan200.computercraft.shared.peripheral.PeripheralType;
import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory;
import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.peripheral.modem.WirelessModemPeripheral;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.IBakedModel;
@@ -17,9 +18,9 @@ import net.minecraft.client.renderer.block.model.ModelManager;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
@@ -37,7 +38,7 @@ public class TurtleModem implements ITurtleUpgrade
public Peripheral( ITurtleAccess turtle, boolean advanced )
{
super( advanced );
super( new ModemState(), advanced );
m_turtle = turtle;
}
@@ -221,12 +222,12 @@ public class TurtleModem implements ITurtleUpgrade
if( !turtle.getWorld().isRemote )
{
IPeripheral peripheral = turtle.getPeripheral( side );
if( peripheral != null && peripheral instanceof Peripheral )
if( peripheral instanceof Peripheral )
{
Peripheral modemPeripheral = (Peripheral)peripheral;
if( modemPeripheral.pollChanged() )
ModemState state = ((Peripheral) peripheral).getModemState();
if( state.pollChanged() )
{
turtle.getUpgradeNBTData( side ).setBoolean( "active", modemPeripheral.isActive() );
turtle.getUpgradeNBTData( side ).setBoolean( "active", state.isOpen() );
turtle.updateUpgradeNBTData( side );
}
}

View File

@@ -0,0 +1,78 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.util;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.ThreadFactory;
/**
* Provides some utilities to create thread groups
*/
public final class ThreadUtils
{
private static final ThreadGroup baseGroup = new ThreadGroup( "ComputerCraft" );
private ThreadUtils()
{
}
/**
* Get the base thread group, that all off-thread ComputerCraft activities are run on.
*
* @return The ComputerCraft group.
*/
public static ThreadGroup group()
{
return baseGroup;
}
/**
* Construct a group under ComputerCraft's shared group
*
* @param name The group's name. This will be prefixed with "ComputerCraft-".
* @return The constructed thread group.
*/
public static ThreadGroup group( String name )
{
return new ThreadGroup( baseGroup, baseGroup.getName() + "-" + name );
}
/**
* Create a new {@link ThreadFactoryBuilder}, which constructs threads under a group of the given {@code name}.
*
* Each thread will be of the format {@code ComputerCraft-<name>-<number>}, and belong to a group
* called {@code ComputerCraft-<name>} (which in turn will be a child group of the main {@code ComputerCraft} group.
*
* @param name The name for the thread group and child threads.
* @return The constructed thread factory builder, which may be extended with other properties.
* @see #factory(String)
*/
public static ThreadFactoryBuilder builder( String name )
{
ThreadGroup group = group( name );
return new ThreadFactoryBuilder()
.setDaemon( true )
.setNameFormat( group.getName().replace( "%", "%%" ) + "-%d" )
.setThreadFactory( x -> new Thread( group, x ) );
}
/**
* Create a new {@link ThreadFactory}, which constructs threads under a group of the given {@code name}.
*
* Each thread will be of the format {@code ComputerCraft-<name>-<number>}, and belong to a group
* called {@code ComputerCraft-<name>} (which in turn will be a child group of the main {@code ComputerCraft} group.
*
* @param name The name for the thread group and child threads.
* @return The constructed thread factory.
* @see #builder(String)
*/
public static ThreadFactory factory( String name )
{
return builder( name ).build();
}
}

View File

@@ -63,7 +63,7 @@ handleMetatable = {
if self._closed then error("attempt to use a closed file", 2) end
local handle = self._handle
if not handle.read then return nil, "Not opened for reading" end
if not handle.read and not handle.readLine then return nil, "Not opened for reading" end
local n = select('#', ...)
local output = {}

View File

@@ -0,0 +1,58 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core;
import com.google.common.io.Files;
import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.ObjectWrapper;
import dan200.computercraft.core.apis.handles.EncodedWritableHandle;
import dan200.computercraft.core.filesystem.FileMount;
import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.filesystem.FileSystemException;
import dan200.computercraft.core.filesystem.FileSystemWrapper;
import org.junit.Test;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static org.junit.Assert.assertEquals;
public class FileSystemTest
{
private static final File ROOT = new File( "test-files/filesystem" );
/**
* Ensures writing a file truncates it.
*/
@Test
public void testWriteTruncates() throws FileSystemException, LuaException, IOException
{
IWritableMount writableMount = new FileMount( ROOT, 1000000 );
FileSystem fs = new FileSystem( "hdd", writableMount );
{
FileSystemWrapper<BufferedWriter> writer = fs.openForWrite( "out.txt", false, EncodedWritableHandle::openUtf8 );
ObjectWrapper wrapper = new ObjectWrapper( new EncodedWritableHandle( writer.get(), writer ) );
wrapper.call( "write", "This is a long line" );
wrapper.call( "close" );
}
assertEquals( "This is a long line", Files.toString( new File( ROOT, "out.txt" ), StandardCharsets.UTF_8 ) );
{
FileSystemWrapper<BufferedWriter> writer = fs.openForWrite( "out.txt", false, EncodedWritableHandle::openUtf8 );
ObjectWrapper wrapper = new ObjectWrapper( new EncodedWritableHandle( writer.get(), writer ) );
wrapper.call( "write", "Tiny line" );
wrapper.call( "close" );
}
assertEquals( "Tiny line", Files.toString( new File( ROOT, "out.txt" ), StandardCharsets.UTF_8 ) );
}
}

View File

@@ -0,0 +1,90 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaObject;
import dan200.computercraft.api.lua.ILuaTask;
import dan200.computercraft.api.lua.LuaException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class ObjectWrapper implements ILuaContext
{
private final ILuaObject object;
private final String[] methods;
public ObjectWrapper( ILuaObject object )
{
this.object = object;
this.methods = object.getMethodNames();
}
private int findMethod( String method )
{
for( int i = 0; i < methods.length; i++ )
{
if( method.equals( methods[i] ) ) return i;
}
return -1;
}
public boolean hasMethod( String method )
{
return findMethod( method ) >= 0;
}
public Object[] call( String name, Object... args ) throws LuaException
{
int method = findMethod( name );
if( method < 0 ) throw new IllegalStateException( "No such method '" + name + "'" );
try
{
return object.callMethod( this, method, args );
}
catch( InterruptedException e )
{
throw new IllegalStateException( "Should never be interrupted", e );
}
}
@Nonnull
@Override
public Object[] pullEvent( @Nullable String filter )
{
throw new IllegalStateException( "Method should never yield" );
}
@Nonnull
@Override
public Object[] pullEventRaw( @Nullable String filter )
{
throw new IllegalStateException( "Method should never yield" );
}
@Nonnull
@Override
public Object[] yield( @Nullable Object[] arguments )
{
throw new IllegalStateException( "Method should never yield" );
}
@Nullable
@Override
public Object[] executeMainThreadTask( @Nonnull ILuaTask task )
{
throw new IllegalStateException( "Method should never yield" );
}
@Override
public long issueMainThreadTask( @Nonnull ILuaTask task )
{
throw new IllegalStateException( "Method should never queue events" );
}
}