mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-16 14:37:39 +00:00
Compare commits
89 Commits
1.80pr1
...
v1.80pr1.1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d7301ff15e | ||
![]() |
1cf10c5c47 | ||
![]() |
6691ec8e3a | ||
![]() |
a9f77221ff | ||
![]() |
dd3b69a633 | ||
![]() |
d766f8b34e | ||
![]() |
2ae6fb47e7 | ||
![]() |
dd5698241b | ||
![]() |
ed8e9d7817 | ||
![]() |
6c29b44c3c | ||
![]() |
0caa133089 | ||
![]() |
a8b08bd971 | ||
![]() |
c9181a121f | ||
![]() |
30f4e0829f | ||
![]() |
2155fce036 | ||
![]() |
27602ec8fc | ||
![]() |
66f683d9c9 | ||
![]() |
ac08a52323 | ||
![]() |
fe0f998c27 | ||
![]() |
bcf79165f9 | ||
![]() |
9b2a50cdfc | ||
![]() |
c5d99db654 | ||
![]() |
5253ab3e58 | ||
![]() |
11d8253d9c | ||
![]() |
bc2b481918 | ||
![]() |
b26564ccb9 | ||
![]() |
28e3ffe978 | ||
![]() |
540e2e25aa | ||
![]() |
845118e9e2 | ||
![]() |
b2b8753ee7 | ||
![]() |
060fb21bdb | ||
![]() |
ef008709c7 | ||
![]() |
09da119f27 | ||
![]() |
f8487d1e1c | ||
![]() |
b6f773ffce | ||
![]() |
c8673473ef | ||
![]() |
3829815756 | ||
![]() |
7eac8faf0d | ||
![]() |
0bd0f4d313 | ||
![]() |
73873eb8cb | ||
![]() |
0420b6c831 | ||
![]() |
bb741975b7 | ||
![]() |
8bffec6964 | ||
![]() |
9e19dd7070 | ||
![]() |
aba0e3d2d4 | ||
![]() |
d9d025e33b | ||
![]() |
1fe29ab098 | ||
![]() |
53f16782ab | ||
![]() |
bfb4f88304 | ||
![]() |
fb6d65ec23 | ||
![]() |
6b364052c7 | ||
![]() |
7169abcd7b | ||
![]() |
75ccfbdb3d | ||
![]() |
728644c104 | ||
![]() |
999351e667 | ||
![]() |
11e879db41 | ||
![]() |
a4a774fcdf | ||
![]() |
80ec54eaf6 | ||
![]() |
9e4ae3a494 | ||
![]() |
19e4c03d3a | ||
![]() |
c6b8cb1fab | ||
![]() |
01f5d006fc | ||
![]() |
cd6b076efe | ||
![]() |
f8193a4d23 | ||
![]() |
fbbfe33e21 | ||
![]() |
7a916ed8c2 | ||
![]() |
60305cd106 | ||
![]() |
1c8480a329 | ||
![]() |
5219648128 | ||
![]() |
282aa804f8 | ||
![]() |
e959051239 | ||
![]() |
373b7ba293 | ||
![]() |
70c6f3498b | ||
![]() |
afec3743f3 | ||
![]() |
baa8993999 | ||
![]() |
92f5860de6 | ||
![]() |
12abd4292e | ||
![]() |
4bd5b0d236 | ||
![]() |
0115bc8dca | ||
![]() |
5f323a85a7 | ||
![]() |
85c556d324 | ||
![]() |
0c1114edbc | ||
![]() |
2c264728d9 | ||
![]() |
5df97e5133 | ||
![]() |
acb5f65e16 | ||
![]() |
c9e7b45509 | ||
![]() |
6fca136327 | ||
![]() |
084bbe8480 | ||
![]() |
225ec594e7 |
15
.editorconfig
Normal file
15
.editorconfig
Normal file
@@ -0,0 +1,15 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.properties]
|
||||
insert_final_newline = false
|
43
README.md
43
README.md
@@ -1,26 +1,31 @@
|
||||
ComputerCraft
|
||||
=============
|
||||
[](https://travis-ci.org/dan200/ComputerCraft)
|
||||
# CC: Tweaked
|
||||
[](https://travis-ci.org/SquidDev-CC/CC-Tweaked)
|
||||
|
||||
ComputerCraft is a Minecraft modification which adds programmable Robots and Computers to the world of Minecraft.
|
||||
If you're not familiar with ComputerCraft, visit the [Website](http://www.computercraft.info/download) or the [Wiki](http://www.computercraft.info/wiki) to find out more.
|
||||
CC: Tweaked is a fork of ComputerCraft which aims to provide more earlier access to the more experimental and
|
||||
in-development features of the mod. For a more stable experience, I recommend checking out the
|
||||
[original mod](https://github.com/dan200/ComputerCraft).
|
||||
|
||||
About this Repository
|
||||
=====================
|
||||
## What?
|
||||
CC: Tweaked does not aim to create a competing fork of ComputerCraft, nor am I planning to take it in in a vastly
|
||||
different direction to the original mod. In fact, CC: Tweaked aims to be a nurturing ground for various features, with
|
||||
a pull request against the original mod being the end goal.
|
||||
|
||||
ComputerCraft was originally released in late 2011 by [Daniel Ratcliffe](https://twitter.com/DanTwoHundred). In early 2017, after working on the mod solo for five years, it was decided to release the source code publicly to allow Dan to devote time to other projects. This repository marks the first public release of this source code.
|
||||
CC: Tweaked also includes many pull requests from the community which have not yet been merged, offering a large number
|
||||
of additional bug fixes and features over the original mod.
|
||||
|
||||
The code in this repository will always represent the "bleeding edge" of the ComputerCraft codebase, but stable builds back to 1.79 will be marked on the [Releases](https://github.com/dan200/ComputerCraft/releases) page.
|
||||
## Relation to CCTweaks?
|
||||
This mod has nothing to do with CCTweaks, though there is no denying the name is a throwback to it. However, I do plan
|
||||
to migrate some features of CCTweaks into CC: Tweaked.
|
||||
|
||||
Contributing
|
||||
============
|
||||
## Contributing
|
||||
Any contribution is welcome, be that using the mod, reporting bugs or contributing code. If you do wish to contribute
|
||||
code, do consider submitting it to the ComputerCraft repository instead.
|
||||
|
||||
While ComputerCraft will no longer be actively developed by Daniel Ratcliffe, you may still contribute pull requests which will be reviewed and incorporated into releases periodically. A pull request is more likely to be accepted if it meets the following criteria:
|
||||
That being said, in order to start helping develop CC: Tweaked, you'll need to follow these steps:
|
||||
|
||||
* It does not add any new dependencies for compiling, running or using the mod.
|
||||
* It does not break compatibility with world saves or programs created with previous versions of the mod.
|
||||
* It does not add unneccessary complexity for users of the mod, and maintains the accessibility for which the mod is known.
|
||||
* It does not add unneccessary complexity or stylistic changes to the code, especially where functionality is not being changed.
|
||||
* It does not create bugs!
|
||||
|
||||
The pull requests most likely to be accepted are those which fix bugs, simplify code, or make the mod compatible with newer versions of Minecraft.
|
||||
- **Clone the repository:** `git clone https://github.com/SquidDev-CC/CC-Tweaked.git && cd CC-Tweaked`
|
||||
- **Setup Forge:** `./gradlew setupDecompWorkspace`
|
||||
- **Test your changes:** `./gradlew runClient` (or run the `GradleStart` class from your IDE).
|
||||
|
||||
If you want to run CC: Tweaked in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from
|
||||
`build/libs`.
|
||||
|
60
build.gradle
60
build.gradle
@@ -23,9 +23,9 @@ plugins {
|
||||
}
|
||||
*/
|
||||
|
||||
version = "1.80pr1"
|
||||
group = "dan200.computercraft"
|
||||
archivesBaseName = "ComputerCraft"
|
||||
version = "1.80pr1.1"
|
||||
group = "org.squiddev"
|
||||
archivesBaseName = "cc-tweaked"
|
||||
|
||||
minecraft {
|
||||
version = "1.12-14.21.1.2387"
|
||||
@@ -41,34 +41,46 @@ minecraft {
|
||||
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "JEI"
|
||||
url = "http://dvs1.progwml6.com/files/maven"
|
||||
}
|
||||
maven {
|
||||
name = "squiddev"
|
||||
url = "https://dl.bintray.com/squiddev/maven"
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
shade
|
||||
compile.extendsFrom shade
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// you may put jars on which you depend on in ./libs
|
||||
// or you may define them like so..
|
||||
//compile "some.group:artifact:version:classifier"
|
||||
//compile "some.group:artifact:version"
|
||||
|
||||
// real examples
|
||||
//compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env
|
||||
//compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env
|
||||
|
||||
// the 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime.
|
||||
//provided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
|
||||
|
||||
// the deobf configurations: 'deobfCompile' and 'deobfProvided' are the same as the normal compile and provided,
|
||||
// except that these dependencies get remapped to your current MCP mappings
|
||||
//deobfCompile 'com.mod-buildcraft:buildcraft:6.0.8:dev'
|
||||
//deobfProvided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
|
||||
|
||||
// for more info...
|
||||
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
||||
// http://www.gradle.org/docs/current/userguide/dependency_management.html
|
||||
deobfProvided "mezz.jei:jei_1.12:4.7.5.86:api"
|
||||
runtime "mezz.jei:jei_1.12:4.7.5.86"
|
||||
shade 'org.squiddev:Cobalt:0.3.0'
|
||||
}
|
||||
|
||||
javadoc {
|
||||
include "dan200/computercraft/api/**/*.java"
|
||||
}
|
||||
|
||||
jar {
|
||||
dependsOn javadoc
|
||||
|
||||
manifest {
|
||||
attributes('FMLAT': 'computercraft_at.cfg')
|
||||
}
|
||||
|
||||
into("docs", { from (javadoc.destinationDir) })
|
||||
|
||||
into("api", { from (sourceSets.main.allSource) {
|
||||
include "dan200/computercraft/api/**/*.java"
|
||||
}})
|
||||
|
||||
from configurations.shade.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
}
|
||||
|
||||
import org.ajoberstar.grgit.Grgit
|
||||
@@ -109,3 +121,5 @@ gradle.projectsEvaluated {
|
||||
}
|
||||
}
|
||||
|
||||
runClient.outputs.upToDateWhen { false }
|
||||
runServer.outputs.upToDateWhen { false }
|
||||
|
25
deploy.sh
25
deploy.sh
@@ -18,29 +18,4 @@ 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 "Creating API..."
|
||||
mkdir -p deploy/api/src/dan200/computercraft
|
||||
cp -r build/sources/main/java/dan200/computercraft/api deploy/api/src/dan200/computercraft/api
|
||||
|
||||
echo "Creating API Javadocs..."
|
||||
mkdir -p deploy/api/doc
|
||||
cd src/main/java/dan200/computercraft/api
|
||||
find . -type f -name "*.java" | xargs javadoc -d ../../../../../../deploy/api/doc -windowtitle "$FRIENDLYNAME"
|
||||
cd ../../../../../..
|
||||
|
||||
echo "Adding API and Javadocs to deployment..."
|
||||
cd deploy
|
||||
zip -r $OUTPUTJAR api/doc > /dev/null
|
||||
zip -r $OUTPUTJAR api/src/dan200/computercraft > /dev/null
|
||||
cd ..
|
||||
rm -rf deploy/api
|
||||
|
||||
echo "Adding LuaJ to deployment..."
|
||||
mkdir deploy/luaj
|
||||
cd deploy/luaj
|
||||
jar xf ../../libs/luaj-jse-2.0.3.jar
|
||||
zip -r ../$OUTPUTJAR org > /dev/null
|
||||
cd ../..
|
||||
rm -rf deploy/luaj
|
||||
|
||||
echo "Done."
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,5 @@
|
||||
#Mon Sep 14 12:28:28 PDT 2015
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.7-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-bin.zip
|
||||
|
110
gradlew
vendored
110
gradlew
vendored
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
@@ -6,47 +6,6 @@
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS="-Xmx2048m"
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
@@ -61,9 +20,49 @@ while [ -h "$PRG" ] ; do
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
@@ -90,7 +89,7 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
@@ -114,6 +113,7 @@ fi
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
@@ -154,11 +154,19 @@ if $cygwin ; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
14
gradlew.bat
vendored
14
gradlew.bat
vendored
@@ -8,14 +8,14 @@
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-d64 -Xmx2048m"
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
@@ -46,10 +46,9 @@ echo location of your Java installation.
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
@@ -60,11 +59,6 @@ set _SKIP=2
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
Binary file not shown.
181
luaj-2.0.3/src/core/org/luaj/vm2/lib/Bit32Lib.java
Normal file
181
luaj-2.0.3/src/core/org/luaj/vm2/lib/Bit32Lib.java
Normal file
@@ -0,0 +1,181 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2012 Luaj.org. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
package org.luaj.vm2.lib;
|
||||
|
||||
import org.luaj.vm2.LuaInteger;
|
||||
import org.luaj.vm2.LuaTable;
|
||||
import org.luaj.vm2.LuaValue;
|
||||
import org.luaj.vm2.Varargs;
|
||||
|
||||
/**
|
||||
* Subclass of LibFunction that implements the Lua standard {@code bit32} library.
|
||||
*/
|
||||
public class Bit32Lib extends ZeroArgFunction
|
||||
{
|
||||
public LuaValue call( )
|
||||
{
|
||||
LuaTable t = new LuaTable();
|
||||
bind( t, Bit32LibV.class, new String[] {
|
||||
"band", "bnot", "bor", "btest", "bxor", "extract", "replace"
|
||||
} );
|
||||
bind( t, Bit32Lib2.class, new String[] {
|
||||
"arshift", "lrotate", "lshift", "rrotate", "rshift"
|
||||
} );
|
||||
env.set( "bit32", t );
|
||||
return t;
|
||||
}
|
||||
|
||||
public static final class Bit32LibV extends VarArgFunction
|
||||
{
|
||||
public Varargs invoke( Varargs args )
|
||||
{
|
||||
switch( opcode )
|
||||
{
|
||||
case 0: // band
|
||||
{
|
||||
int result = -1;
|
||||
for( int i = 1; i <= args.narg(); i++ )
|
||||
{
|
||||
result &= args.checkint( i );
|
||||
}
|
||||
return bitsToValue( result );
|
||||
}
|
||||
case 1: // bnot
|
||||
return bitsToValue( ~args.checkint( 1 ) );
|
||||
case 2: // bot
|
||||
{
|
||||
int result = 0;
|
||||
for( int i = 1; i <= args.narg(); i++ )
|
||||
{
|
||||
result |= args.checkint( i );
|
||||
}
|
||||
return bitsToValue( result );
|
||||
}
|
||||
case 3: // btest
|
||||
{
|
||||
int bits = -1;
|
||||
for( int i = 1; i <= args.narg(); i++ )
|
||||
{
|
||||
bits &= args.checkint( i );
|
||||
}
|
||||
return valueOf( bits != 0 );
|
||||
}
|
||||
case 4: // bxor
|
||||
{
|
||||
int result = 0;
|
||||
for( int i = 1; i <= args.narg(); i++ )
|
||||
{
|
||||
result ^= args.checkint( i );
|
||||
}
|
||||
return bitsToValue( result );
|
||||
}
|
||||
case 5: // extract
|
||||
{
|
||||
int field = args.checkint( 2 );
|
||||
int width = args.optint( 3, 1 );
|
||||
|
||||
if( field < 0 ) argerror( 2, "field cannot be negative" );
|
||||
if( width <= 0 ) argerror( 3, "width must be postive" );
|
||||
if( field + width > 32 ) error( "trying to access non-existent bits" );
|
||||
|
||||
return bitsToValue( (args.checkint( 1 ) >>> field) & (-1 >>> (32 - width)) );
|
||||
}
|
||||
case 6: // replace
|
||||
{
|
||||
int n = args.checkint( 1 );
|
||||
int v = args.checkint( 2 );
|
||||
int field = args.checkint( 3 );
|
||||
int width = args.optint( 4, 1 );
|
||||
|
||||
if( field < 0 ) argerror( 3, "field cannot be negative" );
|
||||
if( width <= 0 ) argerror( 4, "width must be postive" );
|
||||
if( field + width > 32 ) error( "trying to access non-existent bits" );
|
||||
|
||||
int mask = (-1 >>> (32 - width)) << field;
|
||||
n = (n & ~mask) | ((v << field) & mask);
|
||||
return bitsToValue( n );
|
||||
}
|
||||
}
|
||||
return NIL;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Bit32Lib2 extends TwoArgFunction
|
||||
{
|
||||
public LuaValue call( LuaValue arg1, LuaValue arg2 )
|
||||
{
|
||||
switch( opcode )
|
||||
{
|
||||
case 0: // arshift
|
||||
{
|
||||
int x = arg1.checkint();
|
||||
int disp = arg2.checkint();
|
||||
return disp >= 0 ? bitsToValue( x >> disp ) : bitsToValue( x << -disp );
|
||||
}
|
||||
case 1: // lrotate
|
||||
return rotate( arg1.checkint(), arg2.checkint() );
|
||||
case 2: // lshift
|
||||
return shift( arg1.checkint(), arg2.checkint() );
|
||||
case 3: // rrotate
|
||||
return rotate( arg1.checkint(), -arg2.checkint() );
|
||||
case 4: // rshift
|
||||
return shift( arg1.checkint(), -arg2.checkint() );
|
||||
}
|
||||
return NIL;
|
||||
}
|
||||
}
|
||||
|
||||
static LuaValue rotate( int x, int disp )
|
||||
{
|
||||
if( disp < 0 )
|
||||
{
|
||||
disp = -disp & 31;
|
||||
return bitsToValue( (x >>> disp) | (x << (32 - disp)) );
|
||||
}
|
||||
else
|
||||
{
|
||||
disp = disp & 31;
|
||||
return bitsToValue( (x << disp) | (x >>> (32 - disp)) );
|
||||
}
|
||||
}
|
||||
|
||||
static LuaValue shift( int x, int disp )
|
||||
{
|
||||
if( disp >= 32 || disp <= -32 )
|
||||
{
|
||||
return ZERO;
|
||||
}
|
||||
else if( disp >= 0 )
|
||||
{
|
||||
return bitsToValue( x << disp );
|
||||
}
|
||||
else
|
||||
{
|
||||
return bitsToValue( x >>> -disp );
|
||||
}
|
||||
}
|
||||
|
||||
private static LuaValue bitsToValue( int x )
|
||||
{
|
||||
return x < 0 ? LuaValue.valueOf( (long) x & 0xFFFFFFFFL ) : LuaInteger.valueOf( x );
|
||||
}
|
||||
}
|
@@ -22,6 +22,7 @@ import dan200.computercraft.core.apis.AddressPredicate;
|
||||
import dan200.computercraft.core.filesystem.ComboMount;
|
||||
import dan200.computercraft.core.filesystem.FileMount;
|
||||
import dan200.computercraft.core.filesystem.JarMount;
|
||||
import dan200.computercraft.shared.command.CommandComputer;
|
||||
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
||||
import dan200.computercraft.shared.computer.blocks.BlockCommandComputer;
|
||||
import dan200.computercraft.shared.computer.blocks.BlockComputer;
|
||||
@@ -93,7 +94,7 @@ import java.util.zip.ZipFile;
|
||||
///////////////
|
||||
|
||||
@Mod(
|
||||
modid = ComputerCraft.MOD_ID, name = "ComputerCraft", version = "${version}",
|
||||
modid = ComputerCraft.MOD_ID, name = "CC: Tweaked", version = "${version}",
|
||||
guiFactory = "dan200.computercraft.client.gui.GuiConfigCC$Factory"
|
||||
)
|
||||
public class ComputerCraft
|
||||
@@ -119,12 +120,15 @@ public class ComputerCraft
|
||||
"192.168.0.0/16",
|
||||
"fd00::/8",
|
||||
};
|
||||
|
||||
|
||||
public static boolean http_enable = true;
|
||||
public static boolean http_websocket_enable = true;
|
||||
public static AddressPredicate http_whitelist = new AddressPredicate( DEFAULT_HTTP_WHITELIST );
|
||||
public static AddressPredicate http_blacklist = new AddressPredicate( DEFAULT_HTTP_BLACKLIST );
|
||||
public static boolean disable_lua51_features = false;
|
||||
public static String default_computer_settings = "";
|
||||
public static boolean debug_enable = false;
|
||||
public static int computer_threads = 1;
|
||||
public static boolean logPeripheralErrors = false;
|
||||
|
||||
public static boolean enableCommandBlock = false;
|
||||
@@ -200,10 +204,13 @@ public class ComputerCraft
|
||||
public static Configuration config;
|
||||
|
||||
public static Property http_enable;
|
||||
public static Property http_websocket_enable;
|
||||
public static Property http_whitelist;
|
||||
public static Property http_blacklist;
|
||||
public static Property disable_lua51_features;
|
||||
public static Property default_computer_settings;
|
||||
public static Property debug_enable;
|
||||
public static Property computer_threads;
|
||||
public static Property logPeripheralErrors;
|
||||
|
||||
public static Property enableCommandBlock;
|
||||
@@ -271,6 +278,9 @@ public class ComputerCraft
|
||||
Config.http_enable = Config.config.get( Configuration.CATEGORY_GENERAL, "http_enable", http_enable );
|
||||
Config.http_enable.setComment( "Enable the \"http\" API on Computers (see \"http_whitelist\" and \"http_blacklist\" for more fine grained control than this)" );
|
||||
|
||||
Config.http_websocket_enable = Config.config.get( Configuration.CATEGORY_GENERAL, "http_websocket_enable", http_websocket_enable );
|
||||
Config.http_websocket_enable.setComment( "Enable use of http websockets. This requires the \"http_enable\" option to also be true." );
|
||||
|
||||
{
|
||||
ConfigCategory category = Config.config.getCategory( Configuration.CATEGORY_GENERAL );
|
||||
Property currentProperty = category.get( "http_whitelist" );
|
||||
@@ -298,10 +308,20 @@ public class ComputerCraft
|
||||
Config.default_computer_settings = Config.config.get( Configuration.CATEGORY_GENERAL, "default_computer_settings", default_computer_settings );
|
||||
Config.default_computer_settings.setComment( "A comma seperated list of default system settings to set on new computers. Example: \"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\" will disable all autocompletion" );
|
||||
|
||||
Config.debug_enable = Config.config.get( Configuration.CATEGORY_GENERAL, "debug_enable", debug_enable );
|
||||
Config.debug_enable.setComment( "Enable Lua's debug library. Whilst this should be safe for general use, it may allow players to interact with other computers. Enable at your own risk." );
|
||||
|
||||
Config.computer_threads = Config.config.get( Configuration.CATEGORY_GENERAL, "computer_threads", computer_threads );
|
||||
Config.computer_threads
|
||||
.setMinValue( 1 )
|
||||
.setRequiresWorldRestart( true )
|
||||
.setComment( "Set the number of threads computers can run on. A higher number means more computers can run at once, but may induce lag.\n" +
|
||||
"Please note that some mods may not work with a thread count higher than 1. Use with caution." );
|
||||
|
||||
Config.logPeripheralErrors = Config.config.get( Configuration.CATEGORY_GENERAL, "logPeripheralErrors", logPeripheralErrors );
|
||||
Config.logPeripheralErrors.setComment( "Log exceptions thrown by peripherals and other Lua objects.\n" +
|
||||
"This makes it easier for mod authors to debug problems, but may result in log spam should people use buggy methods." );
|
||||
|
||||
|
||||
Config.enableCommandBlock = Config.config.get( Configuration.CATEGORY_GENERAL, "enableCommandBlock", enableCommandBlock );
|
||||
Config.enableCommandBlock.setComment( "Enable Command Block peripheral support" );
|
||||
|
||||
@@ -362,10 +382,13 @@ public class ComputerCraft
|
||||
public static void syncConfig() {
|
||||
|
||||
http_enable = Config.http_enable.getBoolean();
|
||||
http_websocket_enable = Config.http_websocket_enable.getBoolean();
|
||||
http_whitelist = new AddressPredicate( Config.http_whitelist.getStringList() );
|
||||
http_blacklist = new AddressPredicate( Config.http_blacklist.getStringList() );
|
||||
disable_lua51_features = Config.disable_lua51_features.getBoolean();
|
||||
default_computer_settings = Config.default_computer_settings.getString();
|
||||
debug_enable = Config.debug_enable.getBoolean();
|
||||
computer_threads = Config.computer_threads.getInt();
|
||||
logPeripheralErrors = Config.logPeripheralErrors.getBoolean();
|
||||
|
||||
enableCommandBlock = Config.enableCommandBlock.getBoolean();
|
||||
@@ -400,6 +423,7 @@ public class ComputerCraft
|
||||
@Mod.EventHandler
|
||||
public void onServerStarting( FMLServerStartingEvent event )
|
||||
{
|
||||
event.registerServerCommand( new CommandComputer() );
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
@@ -533,6 +557,11 @@ public class ComputerCraft
|
||||
networkEventChannel.sendToServer( encode( packet ) );
|
||||
}
|
||||
|
||||
public static void sendToAllAround( ComputerCraftPacket packet, NetworkRegistry.TargetPoint point )
|
||||
{
|
||||
networkEventChannel.sendToAllAround( encode( packet ), point );
|
||||
}
|
||||
|
||||
public static void handlePacket( ComputerCraftPacket packet, EntityPlayer player )
|
||||
{
|
||||
proxy.handlePacket( packet, player );
|
||||
|
@@ -141,10 +141,10 @@ public class FixedWidthFontRenderer
|
||||
}
|
||||
|
||||
// Draw char
|
||||
int index = (int)s.charAt( i );
|
||||
int index = s.charAt( i );
|
||||
if( index < 0 || index > 255 )
|
||||
{
|
||||
index = (int)'?';
|
||||
index = '?';
|
||||
}
|
||||
drawChar( renderer, x + i * FONT_WIDTH, y, index, colour, p, greyScale );
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ public class GuiConfigCC extends GuiConfig
|
||||
{
|
||||
public GuiConfigCC( GuiScreen parentScreen )
|
||||
{
|
||||
super( parentScreen, getConfigElements(), ComputerCraft.MOD_ID, false, false, "ComputerCraft" );
|
||||
super( parentScreen, getConfigElements(), ComputerCraft.MOD_ID, false, false, "CC: Tweaked" );
|
||||
}
|
||||
|
||||
private static List<IConfigElement> getConfigElements()
|
||||
|
@@ -43,4 +43,12 @@ public class GuiDiskDrive extends GuiContainer
|
||||
int i1 = (height - ySize) / 2;
|
||||
drawTexturedModalRect(l, i1, 0, 0, xSize, ySize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen( int mouseX, int mouseY, float partialTicks)
|
||||
{
|
||||
drawDefaultBackground();
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
renderHoveredToolTip(mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
|
@@ -51,4 +51,12 @@ public class GuiPrinter extends GuiContainer
|
||||
drawTexturedModalRect(startX + 34, startY + 21, 176, 0, 25, 45);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen( int mouseX, int mouseY, float partialTicks)
|
||||
{
|
||||
drawDefaultBackground();
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
renderHoveredToolTip(mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
|
@@ -155,4 +155,12 @@ public class GuiTurtle extends GuiContainer
|
||||
|
||||
drawSelectionSlot( advanced );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen( int mouseX, int mouseY, float partialTicks)
|
||||
{
|
||||
drawDefaultBackground();
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
renderHoveredToolTip(mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
|
@@ -175,10 +175,10 @@ public abstract class Widget extends Gui
|
||||
{
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
tessellator.getBuffer().begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX );
|
||||
tessellator.getBuffer().pos( (double) ( x + 0 ), (double) ( y + h ), (double) this.zLevel ).tex( 0.0, 1.0 ).endVertex();
|
||||
tessellator.getBuffer().pos( (double) ( x + w ), (double) ( y + h ), (double) this.zLevel ).tex( 1.0, 1.0 ).endVertex();
|
||||
tessellator.getBuffer().pos( (double) ( x + w ), (double) ( y + 0 ), (double) this.zLevel ).tex( 1.0, 0.0 ).endVertex();
|
||||
tessellator.getBuffer().pos( (double) ( x + 0 ), (double) ( y + 0 ), (double) this.zLevel ).tex( 0.0, 0.0 ).endVertex();
|
||||
tessellator.getBuffer().pos( x + 0, y + h, this.zLevel ).tex( 0.0, 1.0 ).endVertex();
|
||||
tessellator.getBuffer().pos( x + w, y + h, this.zLevel ).tex( 1.0, 1.0 ).endVertex();
|
||||
tessellator.getBuffer().pos( x + w, y + 0, this.zLevel ).tex( 1.0, 0.0 ).endVertex();
|
||||
tessellator.getBuffer().pos( x + 0, y + 0, this.zLevel ).tex( 0.0, 0.0 ).endVertex();
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,8 @@ package dan200.computercraft.client.proxy;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.*;
|
||||
import dan200.computercraft.client.render.RenderOverlayCable;
|
||||
import dan200.computercraft.client.render.TileEntityCableRenderer;
|
||||
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
|
||||
import dan200.computercraft.shared.computer.blocks.ComputerState;
|
||||
import dan200.computercraft.shared.computer.blocks.TileComputer;
|
||||
@@ -19,6 +21,7 @@ import dan200.computercraft.shared.media.items.ItemDiskLegacy;
|
||||
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.TileMonitor;
|
||||
import dan200.computercraft.shared.peripheral.printer.TilePrinter;
|
||||
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
|
||||
@@ -51,7 +54,6 @@ 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.fml.client.FMLClientHandler;
|
||||
import net.minecraftforge.fml.client.registry.ClientRegistry;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
@@ -222,6 +224,7 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
|
||||
// Setup renderers
|
||||
ClientRegistry.bindTileEntitySpecialRenderer( TileMonitor.class, new TileEntityMonitorRenderer() );
|
||||
ClientRegistry.bindTileEntitySpecialRenderer( TileCable.class, new TileEntityCableRenderer() );
|
||||
}
|
||||
|
||||
private void registerItemModel( Block block, int damage, String name )
|
||||
@@ -314,17 +317,6 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playRecord( SoundEvent record, String recordInfo, World world, BlockPos pos )
|
||||
{
|
||||
Minecraft mc = FMLClientHandler.instance().getClient();
|
||||
world.playRecord( pos, record );
|
||||
if( record != null )
|
||||
{
|
||||
mc.ingameGUI.setRecordPlayingMessage( recordInfo );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDiskDriveGUI( InventoryPlayer inventory, TileDiskDrive drive )
|
||||
{
|
||||
@@ -383,7 +375,9 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
switch( packet.m_packetType )
|
||||
{
|
||||
case ComputerCraftPacket.ComputerChanged:
|
||||
case ComputerCraftPacket.ComputerTerminalChanged:
|
||||
case ComputerCraftPacket.ComputerDeleted:
|
||||
case ComputerCraftPacket.PlayRecord:
|
||||
{
|
||||
// Packet from Server to Client
|
||||
IThreadListener listener = Minecraft.getMinecraft();
|
||||
@@ -417,6 +411,7 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
// Packets from Server to Client //
|
||||
///////////////////////////////////
|
||||
case ComputerCraftPacket.ComputerChanged:
|
||||
case ComputerCraftPacket.ComputerTerminalChanged:
|
||||
{
|
||||
int instanceID = packet.m_dataInt[ 0 ];
|
||||
if( !ComputerCraft.clientComputerRegistry.contains( instanceID ) )
|
||||
@@ -435,6 +430,22 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ComputerCraftPacket.PlayRecord:
|
||||
{
|
||||
BlockPos pos = new BlockPos( packet.m_dataInt[ 0 ], packet.m_dataInt[ 1 ], packet.m_dataInt[ 2 ] );
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
if( packet.m_dataInt.length > 3 )
|
||||
{
|
||||
SoundEvent sound = SoundEvent.REGISTRY.getObjectById( packet.m_dataInt[ 3 ] );
|
||||
mc.world.playRecord( pos, sound );
|
||||
mc.ingameGUI.setRecordPlayingMessage( packet.m_dataString[ 0 ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
mc.world.playRecord( pos, null );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,6 +453,7 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
{
|
||||
ForgeHandlers handlers = new ForgeHandlers();
|
||||
MinecraftForge.EVENT_BUS.register( handlers );
|
||||
MinecraftForge.EVENT_BUS.register( new RenderOverlayCable() );
|
||||
}
|
||||
|
||||
public class ForgeHandlers
|
||||
|
@@ -0,0 +1,215 @@
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.peripheral.PeripheralType;
|
||||
import dan200.computercraft.shared.peripheral.common.BlockCable;
|
||||
import dan200.computercraft.shared.peripheral.modem.TileCable;
|
||||
import dan200.computercraft.shared.util.WorldUtil;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.RenderGlobal;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class RenderOverlayCable
|
||||
{
|
||||
private static final float EXPAND = 0.002f;
|
||||
private static final double MIN = TileCable.MIN - EXPAND;
|
||||
private static final double MAX = TileCable.MAX + EXPAND;
|
||||
|
||||
@SubscribeEvent
|
||||
public void drawHighlight( DrawBlockHighlightEvent event )
|
||||
{
|
||||
if( event.getTarget().typeOfHit != RayTraceResult.Type.BLOCK ) return;
|
||||
|
||||
BlockPos pos = event.getTarget().getBlockPos();
|
||||
World world = event.getPlayer().getEntityWorld();
|
||||
|
||||
IBlockState state = world.getBlockState( pos );
|
||||
if( state.getBlock() != ComputerCraft.Blocks.cable ) return;
|
||||
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
if( tile == null || !(tile instanceof TileCable) ) return;
|
||||
|
||||
event.setCanceled( true );
|
||||
TileCable cable = (TileCable) tile;
|
||||
|
||||
PeripheralType type = cable.getPeripheralType();
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.tryBlendFuncSeparate( GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0 );
|
||||
GlStateManager.color( 0.0f, 0.0f, 0.0f, 0.4f );
|
||||
GL11.glLineWidth( 2.0F );
|
||||
GlStateManager.disableTexture2D();
|
||||
GlStateManager.depthMask( false );
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
EnumFacing direction = type != PeripheralType.Cable ? cable.getDirection() : null;
|
||||
|
||||
{
|
||||
EntityPlayer player = event.getPlayer();
|
||||
double x = player.lastTickPosX + (player.posX - player.lastTickPosX) * event.getPartialTicks();
|
||||
double y = player.lastTickPosY + (player.posY - player.lastTickPosY) * event.getPartialTicks();
|
||||
double z = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * event.getPartialTicks();
|
||||
|
||||
GlStateManager.translate( -x + pos.getX(), -y + pos.getY(), -z + pos.getZ() );
|
||||
}
|
||||
|
||||
if( type != PeripheralType.Cable && WorldUtil.isVecInsideInclusive( cable.getModemBounds(), event.getTarget().hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) ) )
|
||||
{
|
||||
RenderGlobal.drawSelectionBoundingBox( cable.getModemBounds(), 0, 0, 0, 0.4f );
|
||||
}
|
||||
else
|
||||
{
|
||||
int flags = 0;
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
for( EnumFacing facing : EnumFacing.VALUES )
|
||||
{
|
||||
if( direction == facing || BlockCable.isCable( world, pos.offset( facing ) ) )
|
||||
{
|
||||
flags |= 1 << facing.ordinal();
|
||||
|
||||
|
||||
switch( facing.getAxis() )
|
||||
{
|
||||
case X:
|
||||
{
|
||||
double offset = facing == EnumFacing.WEST ? -EXPAND : 1 + EXPAND;
|
||||
double centre = facing == EnumFacing.WEST ? MIN : MAX;
|
||||
|
||||
buffer.begin( GL11.GL_LINE_STRIP, DefaultVertexFormats.POSITION );
|
||||
buffer.pos( offset, MIN, MIN ).endVertex();
|
||||
buffer.pos( offset, MAX, MIN ).endVertex();
|
||||
buffer.pos( offset, MAX, MAX ).endVertex();
|
||||
buffer.pos( offset, MIN, MAX ).endVertex();
|
||||
buffer.pos( offset, MIN, MIN ).endVertex();
|
||||
tessellator.draw();
|
||||
|
||||
buffer.begin( GL11.GL_LINES, DefaultVertexFormats.POSITION );
|
||||
buffer.pos( offset, MIN, MIN ).endVertex();
|
||||
buffer.pos( centre, MIN, MIN ).endVertex();
|
||||
buffer.pos( offset, MAX, MIN ).endVertex();
|
||||
buffer.pos( centre, MAX, MIN ).endVertex();
|
||||
buffer.pos( offset, MAX, MAX ).endVertex();
|
||||
buffer.pos( centre, MAX, MAX ).endVertex();
|
||||
buffer.pos( offset, MIN, MAX ).endVertex();
|
||||
buffer.pos( centre, MIN, MAX ).endVertex();
|
||||
tessellator.draw();
|
||||
break;
|
||||
}
|
||||
case Y:
|
||||
{
|
||||
double offset = facing == EnumFacing.DOWN ? -EXPAND : 1 + EXPAND;
|
||||
double centre = facing == EnumFacing.DOWN ? MIN : MAX;
|
||||
|
||||
buffer.begin( GL11.GL_LINE_STRIP, DefaultVertexFormats.POSITION );
|
||||
buffer.pos( MIN, offset, MIN ).endVertex();
|
||||
buffer.pos( MAX, offset, MIN ).endVertex();
|
||||
buffer.pos( MAX, offset, MAX ).endVertex();
|
||||
buffer.pos( MIN, offset, MAX ).endVertex();
|
||||
buffer.pos( MIN, offset, MIN ).endVertex();
|
||||
tessellator.draw();
|
||||
|
||||
buffer.begin( GL11.GL_LINES, DefaultVertexFormats.POSITION );
|
||||
buffer.pos( MIN, offset, MIN ).endVertex();
|
||||
buffer.pos( MIN, centre, MIN ).endVertex();
|
||||
buffer.pos( MAX, offset, MIN ).endVertex();
|
||||
buffer.pos( MAX, centre, MIN ).endVertex();
|
||||
buffer.pos( MAX, offset, MAX ).endVertex();
|
||||
buffer.pos( MAX, centre, MAX ).endVertex();
|
||||
buffer.pos( MIN, offset, MAX ).endVertex();
|
||||
buffer.pos( MIN, centre, MAX ).endVertex();
|
||||
tessellator.draw();
|
||||
break;
|
||||
}
|
||||
case Z:
|
||||
{
|
||||
double offset = facing == EnumFacing.NORTH ? -EXPAND : 1 + EXPAND;
|
||||
double centre = facing == EnumFacing.NORTH ? MIN : MAX;
|
||||
|
||||
buffer.begin( GL11.GL_LINE_STRIP, DefaultVertexFormats.POSITION );
|
||||
buffer.pos( MIN, MIN, offset ).endVertex();
|
||||
buffer.pos( MAX, MIN, offset ).endVertex();
|
||||
buffer.pos( MAX, MAX, offset ).endVertex();
|
||||
buffer.pos( MIN, MAX, offset ).endVertex();
|
||||
buffer.pos( MIN, MIN, offset ).endVertex();
|
||||
tessellator.draw();
|
||||
|
||||
buffer.begin( GL11.GL_LINES, DefaultVertexFormats.POSITION );
|
||||
buffer.pos( MIN, MIN, offset ).endVertex();
|
||||
buffer.pos( MIN, MIN, centre ).endVertex();
|
||||
buffer.pos( MAX, MIN, offset ).endVertex();
|
||||
buffer.pos( MAX, MIN, centre ).endVertex();
|
||||
buffer.pos( MAX, MAX, offset ).endVertex();
|
||||
buffer.pos( MAX, MAX, centre ).endVertex();
|
||||
buffer.pos( MIN, MAX, offset ).endVertex();
|
||||
buffer.pos( MIN, MAX, centre ).endVertex();
|
||||
tessellator.draw();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer.begin( GL11.GL_LINES, DefaultVertexFormats.POSITION );
|
||||
|
||||
draw( buffer, flags, EnumFacing.WEST, EnumFacing.DOWN, EnumFacing.Axis.Z );
|
||||
draw( buffer, flags, EnumFacing.WEST, EnumFacing.UP, EnumFacing.Axis.Z );
|
||||
draw( buffer, flags, EnumFacing.EAST, EnumFacing.DOWN, EnumFacing.Axis.Z );
|
||||
draw( buffer, flags, EnumFacing.EAST, EnumFacing.UP, EnumFacing.Axis.Z );
|
||||
|
||||
draw( buffer, flags, EnumFacing.WEST, EnumFacing.NORTH, EnumFacing.Axis.Y );
|
||||
draw( buffer, flags, EnumFacing.WEST, EnumFacing.SOUTH, EnumFacing.Axis.Y );
|
||||
draw( buffer, flags, EnumFacing.EAST, EnumFacing.NORTH, EnumFacing.Axis.Y );
|
||||
draw( buffer, flags, EnumFacing.EAST, EnumFacing.SOUTH, EnumFacing.Axis.Y );
|
||||
|
||||
draw( buffer, flags, EnumFacing.DOWN, EnumFacing.NORTH, EnumFacing.Axis.X );
|
||||
draw( buffer, flags, EnumFacing.DOWN, EnumFacing.SOUTH, EnumFacing.Axis.X );
|
||||
draw( buffer, flags, EnumFacing.UP, EnumFacing.NORTH, EnumFacing.Axis.X );
|
||||
draw( buffer, flags, EnumFacing.UP, EnumFacing.SOUTH, EnumFacing.Axis.X );
|
||||
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
GlStateManager.depthMask( true );
|
||||
GlStateManager.enableTexture2D();
|
||||
GlStateManager.disableBlend();
|
||||
}
|
||||
|
||||
private static void draw( BufferBuilder buffer, int flags, EnumFacing a, EnumFacing b, EnumFacing.Axis other )
|
||||
{
|
||||
if( ((flags >> a.ordinal()) & 1) != ((flags >> b.ordinal()) & 1) ) return;
|
||||
|
||||
double offA = a.getAxisDirection() == EnumFacing.AxisDirection.NEGATIVE ? MIN : MAX;
|
||||
double offB = b.getAxisDirection() == EnumFacing.AxisDirection.NEGATIVE ? MIN : MAX;
|
||||
switch( other )
|
||||
{
|
||||
case X:
|
||||
buffer.pos( MIN, offA, offB ).endVertex();
|
||||
buffer.pos( MAX, offA, offB ).endVertex();
|
||||
break;
|
||||
case Y:
|
||||
buffer.pos( offA, MIN, offB ).endVertex();
|
||||
buffer.pos( offA, MAX, offB ).endVertex();
|
||||
break;
|
||||
case Z:
|
||||
buffer.pos( offA, offB, MIN ).endVertex();
|
||||
buffer.pos( offA, offB, MAX ).endVertex();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,122 @@
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
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.modem.TileCable;
|
||||
import dan200.computercraft.shared.util.WorldUtil;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.RenderGlobal;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.util.BlockRenderLayer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.ForgeHooksClient;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Render breaking animation only over part of a {@link TileCable}.
|
||||
*/
|
||||
public class TileEntityCableRenderer extends TileEntitySpecialRenderer<TileCable>
|
||||
{
|
||||
@Override
|
||||
public void render( @Nonnull TileCable te, double x, double y, double z, float partialTicks, int destroyStage, float alpha )
|
||||
{
|
||||
if( destroyStage < 0 ) return;
|
||||
|
||||
BlockPos pos = te.getPos();
|
||||
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
RayTraceResult hit = mc.objectMouseOver;
|
||||
if( hit == null || !hit.getBlockPos().equals( pos ) ) return;
|
||||
|
||||
if( MinecraftForgeClient.getRenderPass() != 0 ) return;
|
||||
|
||||
World world = te.getWorld();
|
||||
IBlockState state = world.getBlockState( pos );
|
||||
Block block = state.getBlock();
|
||||
if( block != ComputerCraft.Blocks.cable ) return;
|
||||
|
||||
state = state.getActualState( world, pos );
|
||||
if( te.getPeripheralType() != PeripheralType.Cable && WorldUtil.isVecInsideInclusive( te.getModemBounds(), hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) ) )
|
||||
{
|
||||
state = block.getDefaultState().withProperty( BlockCable.Properties.MODEM, state.getValue( BlockCable.Properties.MODEM ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
state = state.withProperty( BlockCable.Properties.MODEM, BlockCableModemVariant.None );
|
||||
}
|
||||
|
||||
IBakedModel model = mc.getBlockRendererDispatcher().getModelForState( state );
|
||||
if( model == null ) return;
|
||||
|
||||
preRenderDamagedBlocks();
|
||||
|
||||
BufferBuilder buffer = Tessellator.getInstance().getBuffer();
|
||||
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.BLOCK );
|
||||
buffer.setTranslation( x - pos.getX(), y - pos.getY(), z - pos.getZ() );
|
||||
buffer.noColor();
|
||||
|
||||
ForgeHooksClient.setRenderLayer( block.getBlockLayer() );
|
||||
|
||||
// See BlockRendererDispatcher#renderBlockDamage
|
||||
TextureAtlasSprite breakingTexture = mc.getTextureMapBlocks().getAtlasSprite( "minecraft:blocks/destroy_stage_" + destroyStage );
|
||||
Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelRenderer().renderModel(
|
||||
world,
|
||||
ForgeHooksClient.getDamageModel( model, breakingTexture, state, world, pos ),
|
||||
state, pos, buffer, true
|
||||
);
|
||||
|
||||
ForgeHooksClient.setRenderLayer( BlockRenderLayer.SOLID );
|
||||
|
||||
buffer.setTranslation( 0, 0, 0 );
|
||||
Tessellator.getInstance().draw();
|
||||
|
||||
postRenderDamagedBlocks();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see RenderGlobal#preRenderDamagedBlocks()
|
||||
*/
|
||||
private void preRenderDamagedBlocks()
|
||||
{
|
||||
GlStateManager.disableLighting();
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.tryBlendFuncSeparate( GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.SRC_COLOR, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO );
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.color( 1.0F, 1.0F, 1.0F, 0.5F );
|
||||
GlStateManager.doPolygonOffset( -3.0F, -3.0F );
|
||||
GlStateManager.enablePolygonOffset();
|
||||
GlStateManager.alphaFunc( 516, 0.1F );
|
||||
GlStateManager.enableAlpha();
|
||||
GlStateManager.pushMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see RenderGlobal#postRenderDamagedBlocks()
|
||||
*/
|
||||
private void postRenderDamagedBlocks()
|
||||
{
|
||||
GlStateManager.disableAlpha();
|
||||
GlStateManager.doPolygonOffset( 0.0F, 0.0F );
|
||||
GlStateManager.disablePolygonOffset();
|
||||
GlStateManager.disablePolygonOffset();
|
||||
GlStateManager.depthMask( true );
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
@@ -82,11 +82,11 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
GlStateManager.rotate( pitch, 1.0f, 0.0f, 0.0f );
|
||||
GlStateManager.translate(
|
||||
-0.5 + TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN,
|
||||
((double)origin.getHeight() - 0.5) - (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN),
|
||||
(origin.getHeight() - 0.5) - (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN),
|
||||
0.5
|
||||
);
|
||||
double xSize = (double)origin.getWidth() - 2.0 * ( TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER );
|
||||
double ySize = (double)origin.getHeight() - 2.0 * ( TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER );
|
||||
double xSize = origin.getWidth() - 2.0 * ( TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER );
|
||||
double ySize = origin.getHeight() - 2.0 * ( TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER );
|
||||
|
||||
// Get renderers
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
@@ -126,8 +126,8 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
GlStateManager.pushMatrix();
|
||||
try
|
||||
{
|
||||
double xScale = xSize / (double) ( width * FixedWidthFontRenderer.FONT_WIDTH );
|
||||
double yScale = ySize / (double) ( height * FixedWidthFontRenderer.FONT_HEIGHT );
|
||||
double xScale = xSize / ( width * FixedWidthFontRenderer.FONT_WIDTH );
|
||||
double yScale = ySize / ( height * FixedWidthFontRenderer.FONT_HEIGHT );
|
||||
GlStateManager.scale( xScale, -yScale, 1.0 );
|
||||
|
||||
// Draw background
|
||||
@@ -140,7 +140,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
{
|
||||
double marginXSize = TileMonitor.RENDER_MARGIN / xScale;
|
||||
double marginYSize = TileMonitor.RENDER_MARGIN / yScale;
|
||||
double marginSquash = marginYSize / (double) FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
double marginSquash = marginYSize / FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
|
||||
// Top and bottom margins
|
||||
GlStateManager.pushMatrix();
|
||||
|
@@ -116,20 +116,12 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt
|
||||
// Setup the transform
|
||||
Vec3d offset;
|
||||
float yaw;
|
||||
if( turtle != null )
|
||||
{
|
||||
offset = turtle.getRenderOffset( f );
|
||||
yaw = turtle.getRenderYaw( f );
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = new Vec3d( 0.0, 0.0, 0.0 );
|
||||
yaw = 0.0f;
|
||||
}
|
||||
offset = turtle.getRenderOffset( f );
|
||||
yaw = turtle.getRenderYaw( f );
|
||||
GlStateManager.translate( posX + offset.x, posY + offset.y, posZ + offset.z );
|
||||
|
||||
// Render the label
|
||||
IComputer computer = (turtle != null) ? turtle.getComputer() : null;
|
||||
IComputer computer = turtle.getComputer();
|
||||
String label = (computer != null) ? computer.getLabel() : null;
|
||||
if( label != null )
|
||||
{
|
||||
@@ -145,18 +137,9 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt
|
||||
int colour;
|
||||
ComputerFamily family;
|
||||
ResourceLocation overlay;
|
||||
if( turtle != null )
|
||||
{
|
||||
colour = turtle.getColour();
|
||||
family = turtle.getFamily();
|
||||
overlay = turtle.getOverlay();
|
||||
}
|
||||
else
|
||||
{
|
||||
colour = -1;
|
||||
family = ComputerFamily.Normal;
|
||||
overlay = null;
|
||||
}
|
||||
colour = turtle.getColour();
|
||||
family = turtle.getFamily();
|
||||
overlay = turtle.getOverlay();
|
||||
|
||||
renderModel( state, getTurtleModel( family, colour != -1 ), colour == -1 ? null : new int[] { colour } );
|
||||
|
||||
@@ -183,11 +166,8 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt
|
||||
}
|
||||
|
||||
// Render the upgrades
|
||||
if( turtle != null )
|
||||
{
|
||||
renderUpgrade( state, turtle, TurtleSide.Left, f );
|
||||
renderUpgrade( state, turtle, TurtleSide.Right, f );
|
||||
}
|
||||
renderUpgrade( state, turtle, TurtleSide.Left, f );
|
||||
renderUpgrade( state, turtle, TurtleSide.Right, f );
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -310,10 +290,10 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder renderer = tessellator.getBuffer();
|
||||
renderer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR );
|
||||
renderer.pos( (double) ( -xOffset - 1 ), (double) ( -1 + yOffset ), 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
|
||||
renderer.pos( (double) ( -xOffset - 1 ), (double) ( 8 + yOffset ), 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
|
||||
renderer.pos( (double) ( xOffset + 1 ), (double) ( 8 + yOffset ), 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
|
||||
renderer.pos( (double) ( xOffset + 1 ), (double) ( -1 + yOffset ), 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
|
||||
renderer.pos( -xOffset - 1, -1 + yOffset, 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
|
||||
renderer.pos( -xOffset - 1, 8 + yOffset, 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
|
||||
renderer.pos( xOffset + 1, 8 + yOffset, 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
|
||||
renderer.pos( xOffset + 1, -1 + yOffset, 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
|
||||
tessellator.draw();
|
||||
}
|
||||
finally
|
||||
|
@@ -45,8 +45,8 @@ public class AddressPredicate
|
||||
|
||||
public AddressPredicate( String... filters )
|
||||
{
|
||||
List<Pattern> wildcards = this.wildcards = new ArrayList<Pattern>();
|
||||
List<HostRange> ranges = this.ranges = new ArrayList<HostRange>();
|
||||
List<Pattern> wildcards = this.wildcards = new ArrayList<>();
|
||||
List<HostRange> ranges = this.ranges = new ArrayList<>();
|
||||
|
||||
for( String filter : filters )
|
||||
{
|
||||
|
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
|
||||
|
||||
// Contributed by Nia
|
||||
// Based on LuaBit (http://luaforge.net/projects/bit)
|
||||
|
||||
public class BitAPI implements ILuaAPI
|
||||
{
|
||||
private static final int BNOT = 0;
|
||||
private static final int BAND = 1;
|
||||
private static final int BOR = 2;
|
||||
private static final int BXOR = 3;
|
||||
private static final int BRSHIFT = 4;
|
||||
private static final int BLSHIFT = 5;
|
||||
private static final int BLOGIC_RSHIFT = 6;
|
||||
|
||||
public BitAPI( IAPIEnvironment _environment )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNames()
|
||||
{
|
||||
return new String[] {
|
||||
"bit"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startup( )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown( )
|
||||
{
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames() {
|
||||
return new String[] {
|
||||
"bnot", "band", "bor", "bxor",
|
||||
"brshift", "blshift", "blogic_rshift"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
int ret = 0;
|
||||
switch(method) {
|
||||
case BNOT:
|
||||
ret = ~getInt( args, 0 );
|
||||
break;
|
||||
case BAND:
|
||||
ret = getInt( args, 0 ) & getInt( args, 1 );
|
||||
break;
|
||||
case BOR:
|
||||
ret = getInt( args, 0 ) | getInt( args, 1 );
|
||||
break;
|
||||
case BXOR:
|
||||
ret = getInt( args, 0 ) ^ getInt( args, 1 );
|
||||
break;
|
||||
case BRSHIFT:
|
||||
ret = getInt( args, 0 ) >> getInt( args, 1 );
|
||||
break;
|
||||
case BLSHIFT:
|
||||
ret = getInt( args, 0 ) << getInt( args, 1 );
|
||||
break;
|
||||
case BLOGIC_RSHIFT:
|
||||
ret = getInt( args, 0 ) >>> getInt( args, 1 );
|
||||
break;
|
||||
}
|
||||
|
||||
return new Object[]{ ret&0xFFFFFFFFL };
|
||||
}
|
||||
}
|
@@ -6,29 +6,34 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.apis.http.HTTPCheck;
|
||||
import dan200.computercraft.core.apis.http.HTTPRequest;
|
||||
import dan200.computercraft.core.apis.http.HTTPTask;
|
||||
import dan200.computercraft.core.apis.http.*;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.*;
|
||||
|
||||
public class HTTPAPI implements ILuaAPI
|
||||
{
|
||||
private final IAPIEnvironment m_apiEnvironment;
|
||||
private final List<HTTPTask> m_httpTasks;
|
||||
|
||||
private final List<Future<?>> m_httpTasks;
|
||||
private final Set<Closeable> m_closeables;
|
||||
|
||||
public HTTPAPI( IAPIEnvironment environment )
|
||||
{
|
||||
m_apiEnvironment = environment;
|
||||
m_httpTasks = new ArrayList<>();
|
||||
m_closeables = new HashSet<>();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String[] getNames()
|
||||
{
|
||||
@@ -45,42 +50,53 @@ public class HTTPAPI implements ILuaAPI
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
{
|
||||
// Wait for all of our http requests
|
||||
// Wait for all of our http requests
|
||||
synchronized( m_httpTasks )
|
||||
{
|
||||
Iterator<HTTPTask> it = m_httpTasks.iterator();
|
||||
Iterator<Future<?>> it = m_httpTasks.iterator();
|
||||
while( it.hasNext() )
|
||||
{
|
||||
final HTTPTask h = it.next();
|
||||
if( h.isFinished() )
|
||||
{
|
||||
h.whenFinished( m_apiEnvironment );
|
||||
it.remove();
|
||||
}
|
||||
final Future<?> h = it.next();
|
||||
if( h.isDone() ) it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void shutdown( )
|
||||
{
|
||||
synchronized( m_httpTasks )
|
||||
{
|
||||
for( HTTPTask r : m_httpTasks )
|
||||
for( Future<?> r : m_httpTasks )
|
||||
{
|
||||
r.cancel();
|
||||
r.cancel( false );
|
||||
}
|
||||
m_httpTasks.clear();
|
||||
}
|
||||
synchronized( m_closeables )
|
||||
{
|
||||
for( Closeable x : m_closeables )
|
||||
{
|
||||
try
|
||||
{
|
||||
x.close();
|
||||
}
|
||||
catch( IOException ignored )
|
||||
{
|
||||
}
|
||||
}
|
||||
m_closeables.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
return new String[] {
|
||||
return new String[] {
|
||||
"request",
|
||||
"checkURL"
|
||||
"checkURL",
|
||||
"websocket",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -113,7 +129,7 @@ public class HTTPAPI implements ILuaAPI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get binary
|
||||
boolean binary = false;
|
||||
if( args.length >= 4 )
|
||||
@@ -125,10 +141,10 @@ public class HTTPAPI implements ILuaAPI
|
||||
try
|
||||
{
|
||||
URL url = HTTPRequest.checkURL( urlString );
|
||||
HTTPRequest request = new HTTPRequest( urlString, url, postString, headers, binary );
|
||||
HTTPRequest request = new HTTPRequest( m_apiEnvironment, urlString, url, postString, headers, binary );
|
||||
synchronized( m_httpTasks )
|
||||
{
|
||||
m_httpTasks.add( HTTPTask.submit( request ) );
|
||||
m_httpTasks.add( HTTPExecutor.EXECUTOR.submit( request ) );
|
||||
}
|
||||
return new Object[] { true };
|
||||
}
|
||||
@@ -147,9 +163,47 @@ public class HTTPAPI implements ILuaAPI
|
||||
try
|
||||
{
|
||||
URL url = HTTPRequest.checkURL( urlString );
|
||||
HTTPCheck check = new HTTPCheck( urlString, url );
|
||||
synchronized( m_httpTasks ) {
|
||||
m_httpTasks.add( HTTPTask.submit( check ) );
|
||||
HTTPCheck check = new HTTPCheck( m_apiEnvironment, urlString, url );
|
||||
synchronized( m_httpTasks )
|
||||
{
|
||||
m_httpTasks.add( HTTPExecutor.EXECUTOR.submit( check ) );
|
||||
}
|
||||
return new Object[] { true };
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
return new Object[] { false, e.getMessage() };
|
||||
}
|
||||
}
|
||||
case 2: // websocket
|
||||
{
|
||||
String address = getString( args, 0 );
|
||||
Map<Object, Object> headerTbl = optTable( args, 1, Collections.emptyMap() );
|
||||
|
||||
HashMap<String, String> headers = new HashMap<String, String>( headerTbl.size() );
|
||||
for( Object key : headerTbl.keySet() )
|
||||
{
|
||||
Object value = headerTbl.get( key );
|
||||
if( key instanceof String && value instanceof String )
|
||||
{
|
||||
headers.put( (String) key, (String) value );
|
||||
}
|
||||
}
|
||||
|
||||
if( !ComputerCraft.http_websocket_enable )
|
||||
{
|
||||
throw new LuaException( "Websocket connections are disabled" );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
URI uri = WebsocketConnector.checkURI( address );
|
||||
int port = WebsocketConnector.getPort( uri );
|
||||
|
||||
Future<?> connector = WebsocketConnector.createConnector( m_apiEnvironment, this, uri, address, port, headers );
|
||||
synchronized( m_httpTasks )
|
||||
{
|
||||
m_httpTasks.add( connector );
|
||||
}
|
||||
return new Object[] { true };
|
||||
}
|
||||
@@ -164,4 +218,20 @@ public class HTTPAPI implements ILuaAPI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addCloseable( Closeable closeable )
|
||||
{
|
||||
synchronized( m_closeables )
|
||||
{
|
||||
m_closeables.add( closeable );
|
||||
}
|
||||
}
|
||||
|
||||
public void removeCloseable( Closeable closeable )
|
||||
{
|
||||
synchronized( m_closeables )
|
||||
{
|
||||
m_closeables.remove( closeable );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class HTTPRequest
|
||||
{
|
||||
public static URL checkURL( String urlString ) throws HTTPRequestException
|
||||
{
|
||||
URL url;
|
||||
try
|
||||
{
|
||||
url = new URL( urlString );
|
||||
}
|
||||
catch( MalformedURLException e )
|
||||
{
|
||||
throw new HTTPRequestException( "URL malformed" );
|
||||
}
|
||||
|
||||
// Validate the URL
|
||||
String protocol = url.getProtocol().toLowerCase();
|
||||
if( !protocol.equals("http") && !protocol.equals("https") )
|
||||
{
|
||||
throw new HTTPRequestException( "URL not http" );
|
||||
}
|
||||
|
||||
// Compare the URL to the whitelist
|
||||
if( !ComputerCraft.http_whitelist.matches( url.getHost() ) || ComputerCraft.http_blacklist.matches( url.getHost() ) )
|
||||
{
|
||||
throw new HTTPRequestException( "Domain not permitted" );
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
public HTTPRequest( String url, final String postText, final Map<String, String> headers, boolean binary ) throws HTTPRequestException
|
||||
{
|
||||
// Parse the URL
|
||||
m_urlString = url;
|
||||
m_url = checkURL( m_urlString );
|
||||
m_binary = binary;
|
||||
|
||||
// Start the thread
|
||||
m_cancelled = false;
|
||||
m_complete = false;
|
||||
m_success = false;
|
||||
m_result = null;
|
||||
m_responseCode = -1;
|
||||
|
||||
Thread thread = new Thread( () ->
|
||||
{
|
||||
try
|
||||
{
|
||||
// Connect to the URL
|
||||
HttpURLConnection connection = (HttpURLConnection)m_url.openConnection();
|
||||
|
||||
if( postText != null )
|
||||
{
|
||||
connection.setRequestMethod( "POST" );
|
||||
connection.setDoOutput( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
connection.setRequestMethod( "GET" );
|
||||
}
|
||||
|
||||
// Set headers
|
||||
connection.setRequestProperty( "accept-charset", "UTF-8" );
|
||||
if( postText != null )
|
||||
{
|
||||
connection.setRequestProperty( "content-type", "application/x-www-form-urlencoded; charset=utf-8" );
|
||||
connection.setRequestProperty( "content-encoding", "UTF-8" );
|
||||
}
|
||||
if( headers != null )
|
||||
{
|
||||
for( Map.Entry<String, String> header : headers.entrySet() )
|
||||
{
|
||||
connection.setRequestProperty( header.getKey(), header.getValue() );
|
||||
}
|
||||
}
|
||||
|
||||
// Send POST text
|
||||
if( postText != null )
|
||||
{
|
||||
OutputStream os = connection.getOutputStream();
|
||||
OutputStreamWriter osw;
|
||||
try
|
||||
{
|
||||
osw = new OutputStreamWriter( os, "UTF-8" );
|
||||
}
|
||||
catch( UnsupportedEncodingException e )
|
||||
{
|
||||
osw = new OutputStreamWriter( os );
|
||||
}
|
||||
BufferedWriter writer = new BufferedWriter( osw );
|
||||
writer.write( postText, 0, postText.length() );
|
||||
writer.close();
|
||||
}
|
||||
|
||||
// Read response
|
||||
InputStream is;
|
||||
int code = connection.getResponseCode();
|
||||
boolean responseSuccess;
|
||||
if (code >= 200 && code < 400) {
|
||||
is = connection.getInputStream();
|
||||
responseSuccess = true;
|
||||
} else {
|
||||
is = connection.getErrorStream();
|
||||
responseSuccess = false;
|
||||
}
|
||||
|
||||
byte[] result = ByteStreams.toByteArray( is );
|
||||
is.close();
|
||||
|
||||
synchronized( m_lock )
|
||||
{
|
||||
if( m_cancelled )
|
||||
{
|
||||
// We cancelled
|
||||
m_complete = true;
|
||||
m_success = false;
|
||||
m_result = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We completed
|
||||
m_complete = true;
|
||||
m_success = responseSuccess;
|
||||
m_result = result;
|
||||
m_responseCode = connection.getResponseCode();
|
||||
m_encoding = connection.getContentEncoding();
|
||||
|
||||
Joiner joiner = Joiner.on( ',' );
|
||||
Map<String, String> headers1 = m_responseHeaders = new HashMap<>();
|
||||
for (Map.Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
|
||||
headers1.put(header.getKey(), joiner.join( header.getValue() ));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connection.disconnect(); // disconnect
|
||||
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
synchronized( m_lock )
|
||||
{
|
||||
// There was an error
|
||||
m_complete = true;
|
||||
m_success = false;
|
||||
m_result = null;
|
||||
}
|
||||
}
|
||||
} );
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public String getURL() {
|
||||
return m_urlString;
|
||||
}
|
||||
|
||||
public void cancel()
|
||||
{
|
||||
synchronized(m_lock) {
|
||||
m_cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isComplete()
|
||||
{
|
||||
synchronized(m_lock) {
|
||||
return m_complete;
|
||||
}
|
||||
}
|
||||
|
||||
public int getResponseCode() {
|
||||
synchronized(m_lock) {
|
||||
return m_responseCode;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> getResponseHeaders() {
|
||||
synchronized (m_lock) {
|
||||
return m_responseHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean wasSuccessful()
|
||||
{
|
||||
synchronized(m_lock) {
|
||||
return m_success;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBinary()
|
||||
{
|
||||
return m_binary;
|
||||
}
|
||||
|
||||
public InputStream getContents()
|
||||
{
|
||||
byte[] result;
|
||||
synchronized(m_lock) {
|
||||
result = m_result;
|
||||
}
|
||||
|
||||
if( result != null ) {
|
||||
return new ByteArrayInputStream( result );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return m_encoding;
|
||||
}
|
||||
|
||||
private final Object m_lock = new Object();
|
||||
private final URL m_url;
|
||||
private final String m_urlString;
|
||||
|
||||
private boolean m_complete;
|
||||
private boolean m_cancelled;
|
||||
private boolean m_success;
|
||||
private String m_encoding;
|
||||
private byte[] m_result;
|
||||
private boolean m_binary;
|
||||
private int m_responseCode;
|
||||
private Map<String, String> m_responseHeaders;
|
||||
}
|
@@ -52,8 +52,8 @@ public class OSAPI implements ILuaAPI
|
||||
@Override
|
||||
public int compareTo( @Nonnull Alarm o )
|
||||
{
|
||||
double t = (double)m_day * 24.0 + m_time;
|
||||
double ot = (double)m_day * 24.0 + m_time;
|
||||
double t = m_day * 24.0 + m_time;
|
||||
double ot = m_day * 24.0 + m_time;
|
||||
if( t < ot ) {
|
||||
return -1;
|
||||
} else if( t > ot ) {
|
||||
@@ -135,13 +135,13 @@ public class OSAPI implements ILuaAPI
|
||||
|
||||
if( time > previousTime || day > previousDay )
|
||||
{
|
||||
double now = (double)m_day * 24.0 + m_time;
|
||||
double now = m_day * 24.0 + m_time;
|
||||
Iterator<Map.Entry<Integer, Alarm>> it = m_alarms.entrySet().iterator();
|
||||
while( it.hasNext() )
|
||||
{
|
||||
Map.Entry<Integer, Alarm> entry = it.next();
|
||||
Alarm alarm = entry.getValue();
|
||||
double t = (double)alarm.m_day * 24.0 + alarm.m_time;
|
||||
double t = alarm.m_day * 24.0 + alarm.m_time;
|
||||
if( now >= t )
|
||||
{
|
||||
queueLuaEvent( "alarm", new Object[]{ entry.getKey() } );
|
||||
@@ -196,8 +196,8 @@ public class OSAPI implements ILuaAPI
|
||||
private float getTimeForCalendar(Calendar c)
|
||||
{
|
||||
float time = c.get(Calendar.HOUR_OF_DAY);
|
||||
time += (float)c.get(Calendar.MINUTE) / 60.0f;
|
||||
time += (float)c.get(Calendar.SECOND) / (60.0f * 60.0f);
|
||||
time += c.get(Calendar.MINUTE) / 60.0f;
|
||||
time += c.get(Calendar.SECOND) / (60.0f * 60.0f);
|
||||
return time;
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ public class OSAPI implements ILuaAPI
|
||||
// clock
|
||||
synchronized( m_timers )
|
||||
{
|
||||
return new Object[] { (double)m_clock * 0.05 };
|
||||
return new Object[] { m_clock * 0.05 };
|
||||
}
|
||||
}
|
||||
case 11:
|
||||
|
@@ -34,74 +34,77 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
private String[] m_methods;
|
||||
private Map<String, Integer> m_methodMap;
|
||||
private boolean m_attached;
|
||||
|
||||
|
||||
private Set<String> m_mounts;
|
||||
|
||||
|
||||
public PeripheralWrapper( IPeripheral peripheral, String side )
|
||||
{
|
||||
m_side = side;
|
||||
m_peripheral = peripheral;
|
||||
m_attached = false;
|
||||
|
||||
|
||||
m_type = peripheral.getType();
|
||||
m_methods = peripheral.getMethodNames();
|
||||
assert( m_type != null );
|
||||
assert( m_methods != null );
|
||||
|
||||
|
||||
m_methodMap = new HashMap<>();
|
||||
for(int i=0; i<m_methods.length; ++i ) {
|
||||
if( m_methods[i] != null ) {
|
||||
m_methodMap.put( m_methods[i], i );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_mounts = new HashSet<>();
|
||||
}
|
||||
|
||||
|
||||
public IPeripheral getPeripheral()
|
||||
{
|
||||
return m_peripheral;
|
||||
}
|
||||
|
||||
|
||||
public String getType()
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
|
||||
public String[] getMethods()
|
||||
{
|
||||
return m_methods;
|
||||
}
|
||||
|
||||
|
||||
public synchronized boolean isAttached()
|
||||
{
|
||||
return m_attached;
|
||||
}
|
||||
|
||||
|
||||
public synchronized void attach()
|
||||
{
|
||||
m_attached = true;
|
||||
m_peripheral.attach( this );
|
||||
}
|
||||
|
||||
public synchronized void detach()
|
||||
|
||||
public void detach()
|
||||
{
|
||||
// Call detach
|
||||
m_peripheral.detach( this );
|
||||
m_attached = false;
|
||||
|
||||
// Unmount everything the detach function forgot to do
|
||||
for( String m_mount : m_mounts )
|
||||
|
||||
synchronized( this )
|
||||
{
|
||||
m_fileSystem.unmount( m_mount );
|
||||
m_attached = false;
|
||||
// Unmount everything the detach function forgot to do
|
||||
for( String m_mount : m_mounts )
|
||||
{
|
||||
m_fileSystem.unmount( m_mount );
|
||||
}
|
||||
m_mounts.clear();
|
||||
}
|
||||
m_mounts.clear();
|
||||
}
|
||||
|
||||
|
||||
public Object[] call( ILuaContext context, String methodName, Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
int method = -1;
|
||||
synchronized( this )
|
||||
synchronized( this )
|
||||
{
|
||||
if( m_methodMap.containsKey( methodName ) )
|
||||
{
|
||||
@@ -133,7 +136,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
{
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
|
||||
|
||||
// Mount the location
|
||||
String location;
|
||||
synchronized( m_fileSystem )
|
||||
@@ -151,7 +154,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
if( location != null )
|
||||
{
|
||||
m_mounts.add( location );
|
||||
}
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
@@ -168,7 +171,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
{
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
|
||||
|
||||
// Mount the location
|
||||
String location;
|
||||
synchronized( m_fileSystem )
|
||||
@@ -186,49 +189,49 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
if( location != null )
|
||||
{
|
||||
m_mounts.add( location );
|
||||
}
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void unmount( String location )
|
||||
{
|
||||
if( !m_attached ) {
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
|
||||
|
||||
if( location != null )
|
||||
{
|
||||
if( !m_mounts.contains( location ) ) {
|
||||
throw new RuntimeException( "You didn't mount this location" );
|
||||
}
|
||||
|
||||
|
||||
m_fileSystem.unmount( location );
|
||||
m_mounts.remove( location );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized int getID()
|
||||
public int getID()
|
||||
{
|
||||
if( !m_attached ) {
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
return m_environment.getComputerID();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void queueEvent( @Nonnull final String event, final Object[] arguments )
|
||||
public void queueEvent( @Nonnull final String event, final Object[] arguments )
|
||||
{
|
||||
if( !m_attached ) {
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
}
|
||||
m_environment.queueEvent( event, arguments );
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public synchronized String getAttachmentName()
|
||||
public String getAttachmentName()
|
||||
{
|
||||
if( !m_attached ) {
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
@@ -236,7 +239,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
return m_side;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final IAPIEnvironment m_environment;
|
||||
private FileSystem m_fileSystem;
|
||||
private final PeripheralWrapper[] m_peripherals;
|
||||
@@ -246,16 +249,16 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
{
|
||||
m_environment = _environment;
|
||||
m_environment.setPeripheralChangeListener( this );
|
||||
|
||||
|
||||
m_peripherals = new PeripheralWrapper[6];
|
||||
for(int i=0; i<6; ++i)
|
||||
{
|
||||
m_peripherals[i] = null;
|
||||
}
|
||||
|
||||
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
|
||||
// IPeripheralChangeListener
|
||||
|
||||
@Override
|
||||
@@ -282,11 +285,11 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
|
||||
// Queue a detachment event
|
||||
m_environment.queueEvent( "peripheral_detach", new Object[] { Computer.s_sideNames[side] } );
|
||||
}
|
||||
|
||||
|
||||
// Assign the new peripheral
|
||||
if( newPeripheral != null )
|
||||
{
|
||||
@@ -296,7 +299,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
{
|
||||
m_peripherals[side] = null;
|
||||
}
|
||||
|
||||
|
||||
if( m_peripherals[side] != null )
|
||||
{
|
||||
// Queue an attachment
|
||||
@@ -318,7 +321,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
}
|
||||
}, null );
|
||||
|
||||
|
||||
// Queue an attachment event
|
||||
m_environment.queueEvent( "peripheral", new Object[] { Computer.s_sideNames[side] } );
|
||||
}
|
||||
@@ -326,7 +329,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
|
||||
// ILuaAPI implementation
|
||||
|
||||
|
||||
@Override
|
||||
public String[] getNames()
|
||||
{
|
||||
@@ -352,12 +355,12 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void shutdown( )
|
||||
{
|
||||
@@ -464,8 +467,8 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
// call
|
||||
int side = parseSide( args );
|
||||
String methodName = getString( args, 1 );
|
||||
Object[] methodArgs = trimArray( args, 2 );
|
||||
|
||||
Object[] methodArgs = Arrays.copyOfRange( args, 2, args.length );
|
||||
|
||||
if( side >= 0 )
|
||||
{
|
||||
PeripheralWrapper p;
|
||||
@@ -486,13 +489,8 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Privates
|
||||
|
||||
private Object[] trimArray( Object[] array, int skip )
|
||||
{
|
||||
return Arrays.copyOfRange( array, skip, array.length );
|
||||
}
|
||||
// Privates
|
||||
|
||||
private int parseSide( Object[] args ) throws LuaException
|
||||
{
|
||||
@@ -506,7 +504,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
private String findFreeLocation( String desiredLoc )
|
||||
{
|
||||
try
|
||||
|
@@ -6,6 +6,8 @@ import dan200.computercraft.api.lua.LuaException;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.*;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.*;
|
||||
|
||||
public class EncodedInputHandle extends HandleGeneric
|
||||
{
|
||||
private final BufferedReader m_reader;
|
||||
@@ -49,6 +51,7 @@ public class EncodedInputHandle extends HandleGeneric
|
||||
"readLine",
|
||||
"readAll",
|
||||
"close",
|
||||
"read",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -102,6 +105,26 @@ public class EncodedInputHandle extends HandleGeneric
|
||||
// close
|
||||
close();
|
||||
return null;
|
||||
case 3:
|
||||
// read
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
int count = optInt( args, 0, 1 );
|
||||
if( count <= 0 || count >= 1024 * 16 )
|
||||
{
|
||||
throw new LuaException( "Count out of range" );
|
||||
}
|
||||
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 )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
@@ -1,18 +1,18 @@
|
||||
package dan200.computercraft.core.apis.http;
|
||||
|
||||
import dan200.computercraft.core.apis.HTTPRequestException;
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
public class HTTPCheck implements HTTPTask.IHTTPTask
|
||||
public class HTTPCheck implements Runnable
|
||||
{
|
||||
private final IAPIEnvironment environment;
|
||||
private final String urlString;
|
||||
private final URL url;
|
||||
private String error;
|
||||
|
||||
public HTTPCheck( String urlString, URL url )
|
||||
public HTTPCheck( IAPIEnvironment environment, String urlString, URL url )
|
||||
{
|
||||
this.environment = environment;
|
||||
this.urlString = urlString;
|
||||
this.url = url;
|
||||
}
|
||||
@@ -22,24 +22,12 @@ public class HTTPCheck implements HTTPTask.IHTTPTask
|
||||
{
|
||||
try
|
||||
{
|
||||
HTTPRequest.checkHost( url );
|
||||
HTTPRequest.checkHost( url.getHost() );
|
||||
environment.queueEvent( "http_check", new Object[] { urlString, true } );
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
error = e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void whenFinished( IAPIEnvironment environment )
|
||||
{
|
||||
if( error == null )
|
||||
{
|
||||
environment.queueEvent( "http_check", new Object[] { urlString, true } );
|
||||
}
|
||||
else
|
||||
{
|
||||
environment.queueEvent( "http_check", new Object[] { urlString, false, error } );
|
||||
environment.queueEvent( "http_check", new Object[] { urlString, false, e.getMessage() } );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.apis.http;
|
||||
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Just a shared object for executing simple HTTP related tasks.
|
||||
*/
|
||||
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 )
|
||||
.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 )
|
||||
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
|
||||
.setNameFormat( "ComputerCraft-Netty-%d" )
|
||||
.build()
|
||||
);
|
||||
|
||||
private HTTPExecutor()
|
||||
{
|
||||
}
|
||||
}
|
@@ -12,7 +12,6 @@ import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ILuaObject;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.apis.HTTPRequestException;
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
|
||||
import dan200.computercraft.core.apis.handles.EncodedInputHandle;
|
||||
@@ -25,7 +24,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class HTTPRequest implements HTTPTask.IHTTPTask
|
||||
public class HTTPRequest implements Runnable
|
||||
{
|
||||
public static URL checkURL( String urlString ) throws HTTPRequestException
|
||||
{
|
||||
@@ -55,11 +54,11 @@ public class HTTPRequest implements HTTPTask.IHTTPTask
|
||||
return url;
|
||||
}
|
||||
|
||||
public static InetAddress checkHost( URL url ) throws HTTPRequestException
|
||||
public static InetAddress checkHost( String host ) throws HTTPRequestException
|
||||
{
|
||||
try
|
||||
{
|
||||
InetAddress resolved = InetAddress.getByName( url.getHost() );
|
||||
InetAddress resolved = InetAddress.getByName( host );
|
||||
if( !ComputerCraft.http_whitelist.matches( resolved ) || ComputerCraft.http_blacklist.matches( resolved ) )
|
||||
{
|
||||
throw new HTTPRequestException( "Domain not permitted" );
|
||||
@@ -73,22 +72,16 @@ public class HTTPRequest implements HTTPTask.IHTTPTask
|
||||
}
|
||||
}
|
||||
|
||||
private final IAPIEnvironment m_environment;
|
||||
private final URL m_url;
|
||||
private final String m_urlString;
|
||||
private final String m_postText;
|
||||
private final Map<String, String> m_headers;
|
||||
|
||||
private boolean m_success = false;
|
||||
private String m_encoding;
|
||||
private byte[] m_result;
|
||||
private boolean m_binary;
|
||||
private int m_responseCode = -1;
|
||||
private Map<String, String> m_responseHeaders;
|
||||
private String m_errorMessage;
|
||||
|
||||
public HTTPRequest( 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 ) throws HTTPRequestException
|
||||
{
|
||||
// Parse the URL
|
||||
m_environment = environment;
|
||||
m_urlString = urlString;
|
||||
m_url = url;
|
||||
m_binary = binary;
|
||||
@@ -96,28 +89,19 @@ public class HTTPRequest implements HTTPTask.IHTTPTask
|
||||
m_headers = headers;
|
||||
}
|
||||
|
||||
public InputStream getContents()
|
||||
{
|
||||
byte[] result = m_result;
|
||||
if( result != null )
|
||||
{
|
||||
return new ByteArrayInputStream( result );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
// First verify the address is allowed.
|
||||
try
|
||||
{
|
||||
checkHost( m_url );
|
||||
checkHost( m_url.getHost() );
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
m_success = false;
|
||||
m_errorMessage = e.getMessage();
|
||||
// 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 } );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -186,58 +170,36 @@ public class HTTPRequest implements HTTPTask.IHTTPTask
|
||||
byte[] result = ByteStreams.toByteArray( is );
|
||||
is.close();
|
||||
|
||||
// We completed
|
||||
m_success = responseSuccess;
|
||||
m_result = result;
|
||||
m_responseCode = connection.getResponseCode();
|
||||
m_encoding = connection.getContentEncoding();
|
||||
|
||||
// We've got some sort of response, so let's build a resulting object.
|
||||
Joiner joiner = Joiner.on( ',' );
|
||||
Map<String, String> headers = m_responseHeaders = new HashMap<String, String>();
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
for( Map.Entry<String, List<String>> header : connection.getHeaderFields().entrySet() )
|
||||
{
|
||||
headers.put( header.getKey(), joiner.join( header.getValue() ) );
|
||||
}
|
||||
|
||||
InputStream contents = new ByteArrayInputStream( result );
|
||||
ILuaObject stream = wrapStream(
|
||||
m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, connection.getContentEncoding() ),
|
||||
connection.getResponseCode(), headers
|
||||
);
|
||||
|
||||
connection.disconnect(); // disconnect
|
||||
|
||||
// Queue the appropriate event.
|
||||
if( responseSuccess )
|
||||
{
|
||||
m_environment.queueEvent( "http_success", new Object[] { m_urlString, stream } );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_environment.queueEvent( "http_failure", new Object[] { m_urlString, "Could not connect", stream } );
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
// There was an error
|
||||
m_success = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void whenFinished( IAPIEnvironment environment )
|
||||
{
|
||||
final String url = m_urlString;
|
||||
if( m_success )
|
||||
{
|
||||
// Queue the "http_success" event
|
||||
InputStream contents = getContents();
|
||||
Object result = wrapStream(
|
||||
m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, m_encoding ),
|
||||
m_responseCode, m_responseHeaders
|
||||
);
|
||||
environment.queueEvent( "http_success", new Object[] { url, result } );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Queue the "http_failure" event
|
||||
String error = "Could not connect";
|
||||
if( m_errorMessage != null ) error = m_errorMessage;
|
||||
|
||||
InputStream contents = getContents();
|
||||
Object result = null;
|
||||
if( contents != null )
|
||||
{
|
||||
result = wrapStream(
|
||||
m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, m_encoding ),
|
||||
m_responseCode, m_responseHeaders
|
||||
);
|
||||
}
|
||||
environment.queueEvent( "http_failure", new Object[] { url, error, result } );
|
||||
m_environment.queueEvent( "http_failure", new Object[] { m_urlString, "Could not connect", null } );
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package dan200.computercraft.core.apis;
|
||||
package dan200.computercraft.core.apis.http;
|
||||
|
||||
public class HTTPRequestException extends Exception
|
||||
{
|
@@ -1,61 +0,0 @@
|
||||
package dan200.computercraft.core.apis.http;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* A task which executes asynchronously on a new thread.
|
||||
*
|
||||
* This functions very similarly to a {@link Future}, but with an additional
|
||||
* method which is called on the main thread when the task is completed.
|
||||
*/
|
||||
public class HTTPTask
|
||||
{
|
||||
public interface IHTTPTask extends Runnable
|
||||
{
|
||||
void whenFinished( IAPIEnvironment environment );
|
||||
}
|
||||
|
||||
private static final ExecutorService httpThreads = new ThreadPoolExecutor(
|
||||
4, Integer.MAX_VALUE,
|
||||
60L, TimeUnit.SECONDS,
|
||||
new SynchronousQueue<Runnable>(),
|
||||
new ThreadFactoryBuilder()
|
||||
.setDaemon( true )
|
||||
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
|
||||
.setNameFormat( "ComputerCraft-HTTP-%d" )
|
||||
.build()
|
||||
);
|
||||
|
||||
private final Future<?> future;
|
||||
private final IHTTPTask task;
|
||||
|
||||
private HTTPTask( Future<?> future, IHTTPTask task )
|
||||
{
|
||||
this.future = future;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
public static HTTPTask submit( IHTTPTask task )
|
||||
{
|
||||
Future<?> future = httpThreads.submit( task );
|
||||
return new HTTPTask( future, task );
|
||||
}
|
||||
|
||||
public void cancel()
|
||||
{
|
||||
future.cancel( false );
|
||||
}
|
||||
|
||||
public boolean isFinished()
|
||||
{
|
||||
return future.isDone();
|
||||
}
|
||||
|
||||
public void whenFinished( IAPIEnvironment environment )
|
||||
{
|
||||
task.whenFinished( environment );
|
||||
}
|
||||
}
|
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.apis.http;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
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 io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.websocketx.*;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
public class WebsocketConnection extends SimpleChannelInboundHandler<Object> implements ILuaObject, Closeable
|
||||
{
|
||||
public static final String SUCCESS_EVENT = "websocket_success";
|
||||
public static final String FAILURE_EVENT = "websocket_failure";
|
||||
public static final String CLOSE_EVENT = "websocket_closed";
|
||||
public static final String MESSAGE_EVENT = "websocket_message";
|
||||
|
||||
private final String url;
|
||||
private final IAPIEnvironment computer;
|
||||
private final HTTPAPI api;
|
||||
private boolean open = true;
|
||||
|
||||
private Channel channel;
|
||||
private final WebSocketClientHandshaker handshaker;
|
||||
|
||||
public WebsocketConnection( IAPIEnvironment computer, HTTPAPI api, WebSocketClientHandshaker handshaker, String url )
|
||||
{
|
||||
this.computer = computer;
|
||||
this.api = api;
|
||||
this.handshaker = handshaker;
|
||||
this.url = url;
|
||||
|
||||
api.addCloseable( this );
|
||||
}
|
||||
|
||||
private void close( boolean remove )
|
||||
{
|
||||
open = false;
|
||||
if( remove ) api.removeCloseable( this );
|
||||
|
||||
if( channel != null )
|
||||
{
|
||||
channel.close();
|
||||
channel = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
close( false );
|
||||
}
|
||||
|
||||
private void onClosed()
|
||||
{
|
||||
close( true );
|
||||
computer.queueEvent( CLOSE_EVENT, new Object[] { url } );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded( ChannelHandlerContext ctx ) throws Exception
|
||||
{
|
||||
channel = ctx.channel();
|
||||
super.handlerAdded( ctx );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive( ChannelHandlerContext ctx ) throws Exception
|
||||
{
|
||||
handshaker.handshake( ctx.channel() );
|
||||
super.channelActive( ctx );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive( ChannelHandlerContext ctx ) throws Exception
|
||||
{
|
||||
onClosed();
|
||||
super.channelInactive( ctx );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead0( ChannelHandlerContext ctx, Object msg ) throws Exception
|
||||
{
|
||||
Channel ch = ctx.channel();
|
||||
if( !handshaker.isHandshakeComplete() )
|
||||
{
|
||||
handshaker.finishHandshake( ch, (FullHttpResponse) msg );
|
||||
computer.queueEvent( SUCCESS_EVENT, new Object[] { url, this } );
|
||||
return;
|
||||
}
|
||||
|
||||
if( msg instanceof FullHttpResponse )
|
||||
{
|
||||
FullHttpResponse response = (FullHttpResponse) msg;
|
||||
throw new IllegalStateException( "Unexpected FullHttpResponse (getStatus=" + response.status() + ", content=" + response.content().toString( CharsetUtil.UTF_8 ) + ')' );
|
||||
}
|
||||
|
||||
WebSocketFrame frame = (WebSocketFrame) msg;
|
||||
if( frame instanceof TextWebSocketFrame )
|
||||
{
|
||||
computer.queueEvent( MESSAGE_EVENT, new Object[] { url, ((TextWebSocketFrame) frame).text() } );
|
||||
}
|
||||
else if( frame instanceof BinaryWebSocketFrame )
|
||||
{
|
||||
ByteBuf data = frame.content();
|
||||
byte[] converted = new byte[ data.readableBytes() ];
|
||||
data.readBytes( converted );
|
||||
computer.queueEvent( MESSAGE_EVENT, new Object[] { url, data } );
|
||||
}
|
||||
else if( frame instanceof CloseWebSocketFrame )
|
||||
{
|
||||
ch.close();
|
||||
onClosed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause )
|
||||
{
|
||||
ctx.close();
|
||||
computer.queueEvent( FAILURE_EVENT, new Object[] {
|
||||
url,
|
||||
cause instanceof WebSocketHandshakeException ? cause.getMessage() : "Could not connect"
|
||||
} );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
return new String[] { "receive", "send", "close" };
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
case 0:
|
||||
while( true )
|
||||
{
|
||||
checkOpen();
|
||||
Object[] event = context.pullEvent( MESSAGE_EVENT );
|
||||
if( event.length >= 3 && Objects.equal( event[ 1 ], url ) )
|
||||
{
|
||||
return new Object[] { event[ 2 ] };
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
checkOpen();
|
||||
String text = arguments.length > 0 && arguments[ 0 ] != null ? arguments[ 0 ].toString() : "";
|
||||
channel.writeAndFlush( new TextWebSocketFrame( text ) );
|
||||
return null;
|
||||
}
|
||||
case 2:
|
||||
close( true );
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkOpen() throws LuaException
|
||||
{
|
||||
if( !open ) throw new LuaException( "attempt to use a closed file" );
|
||||
}
|
||||
}
|
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.apis.http;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.core.apis.HTTPAPI;
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http.DefaultHttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpClientCodec;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
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.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
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
|
||||
{
|
||||
private static final Object lock = new Object();
|
||||
private static TrustManagerFactory trustManager;
|
||||
|
||||
private WebsocketConnector()
|
||||
{
|
||||
}
|
||||
|
||||
private static TrustManagerFactory getTrustManager()
|
||||
{
|
||||
if( trustManager != null ) return trustManager;
|
||||
synchronized( lock )
|
||||
{
|
||||
if( trustManager != null ) return trustManager;
|
||||
|
||||
TrustManagerFactory tmf = null;
|
||||
try
|
||||
{
|
||||
tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() );
|
||||
tmf.init( (KeyStore) null );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
ComputerCraft.log.error( "Cannot setup trust manager", e );
|
||||
}
|
||||
|
||||
return trustManager = tmf;
|
||||
}
|
||||
}
|
||||
|
||||
public static URI checkURI( String address ) throws HTTPRequestException
|
||||
{
|
||||
URI uri = null;
|
||||
try
|
||||
{
|
||||
uri = new URI( address );
|
||||
}
|
||||
catch( URISyntaxException ignored )
|
||||
{
|
||||
}
|
||||
|
||||
if( uri == null || uri.getHost() == null )
|
||||
{
|
||||
try
|
||||
{
|
||||
uri = new URI( "ws://" + address );
|
||||
}
|
||||
catch( URISyntaxException ignored )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if( uri == null || uri.getHost() == null ) throw new HTTPRequestException( "URL malformed" );
|
||||
|
||||
String scheme = uri.getScheme();
|
||||
if( scheme == null )
|
||||
{
|
||||
try
|
||||
{
|
||||
uri = new URI( "ws://" + uri.toString() );
|
||||
}
|
||||
catch( URISyntaxException e )
|
||||
{
|
||||
throw new HTTPRequestException( "URL malformed" );
|
||||
}
|
||||
}
|
||||
else if( !scheme.equalsIgnoreCase( "wss" ) && !scheme.equalsIgnoreCase( "ws" ) )
|
||||
{
|
||||
throw new HTTPRequestException( "Invalid scheme '" + scheme + "'" );
|
||||
}
|
||||
|
||||
if( !ComputerCraft.http_whitelist.matches( uri.getHost() ) || ComputerCraft.http_blacklist.matches( uri.getHost() ) )
|
||||
{
|
||||
throw new HTTPRequestException( "Domain not permitted" );
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
public static int getPort( URI uri ) throws HTTPRequestException
|
||||
{
|
||||
int port = uri.getPort();
|
||||
if( port >= 0 ) return port;
|
||||
|
||||
String scheme = uri.getScheme();
|
||||
if( scheme.equalsIgnoreCase( "ws" ) )
|
||||
{
|
||||
return 80;
|
||||
}
|
||||
else if( scheme.equalsIgnoreCase( "wss" ) )
|
||||
{
|
||||
return 443;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HTTPRequestException( "Invalid scheme '" + scheme + "'" );
|
||||
}
|
||||
}
|
||||
|
||||
public static Future<?> createConnector( final IAPIEnvironment environment, final HTTPAPI api, final URI uri, final String address, final int port, final Map<String, String> headers )
|
||||
{
|
||||
return HTTPExecutor.EXECUTOR.submit( () -> {
|
||||
InetAddress resolved;
|
||||
try
|
||||
{
|
||||
resolved = HTTPRequest.checkHost( uri.getHost() );
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
environment.queueEvent( WebsocketConnection.FAILURE_EVENT, new Object[] { address, e.getMessage() } );
|
||||
return;
|
||||
}
|
||||
|
||||
InetSocketAddress socketAddress = new InetSocketAddress( resolved, uri.getPort() == -1 ? port : uri.getPort() );
|
||||
|
||||
final SslContext ssl;
|
||||
if( uri.getScheme().equalsIgnoreCase( "wss" ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
ssl = SslContextBuilder.forClient().trustManager( getTrustManager() ).build();
|
||||
}
|
||||
catch( SSLException e )
|
||||
{
|
||||
environment.queueEvent( WebsocketConnection.FAILURE_EVENT, new Object[] { address, "Cannot create secure socket" } );
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ssl = null;
|
||||
}
|
||||
|
||||
HttpHeaders httpHeaders = new DefaultHttpHeaders();
|
||||
for( Map.Entry<String, String> header : headers.entrySet() )
|
||||
{
|
||||
httpHeaders.add( header.getKey(), header.getValue() );
|
||||
}
|
||||
|
||||
WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker( uri, WebSocketVersion.V13, null, false, httpHeaders );
|
||||
final WebsocketConnection connection = new WebsocketConnection( environment, api, handshaker, address );
|
||||
|
||||
new Bootstrap()
|
||||
.group( HTTPExecutor.LOOP_GROUP )
|
||||
.channel( NioSocketChannel.class )
|
||||
.handler( new ChannelInitializer<SocketChannel>()
|
||||
{
|
||||
@Override
|
||||
protected void initChannel( SocketChannel ch ) throws Exception
|
||||
{
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
if( ssl != null ) p.addLast( ssl.newHandler( ch.alloc(), uri.getHost(), port ) );
|
||||
p.addLast( new HttpClientCodec(), new HttpObjectAggregator( 8192 ), connection );
|
||||
}
|
||||
} )
|
||||
.remoteAddress( socketAddress )
|
||||
.connect();
|
||||
} );
|
||||
}
|
||||
}
|
@@ -15,7 +15,7 @@ import dan200.computercraft.core.apis.*;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
import dan200.computercraft.core.lua.LuaJLuaMachine;
|
||||
import dan200.computercraft.core.lua.CobaltLuaMachine;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -452,7 +452,7 @@ public class Computer
|
||||
private boolean initFileSystem()
|
||||
{
|
||||
// Create the file system
|
||||
int id = assignID();
|
||||
assignID();
|
||||
try
|
||||
{
|
||||
m_fileSystem = new FileSystem( "hdd", getRootMount() );
|
||||
@@ -611,7 +611,6 @@ public class Computer
|
||||
m_apis.add( new FSAPI( m_apiEnvironment ) );
|
||||
m_apis.add( new PeripheralAPI( m_apiEnvironment ) );
|
||||
m_apis.add( new OSAPI( m_apiEnvironment ) );
|
||||
m_apis.add( new BitAPI( m_apiEnvironment ) );
|
||||
//m_apis.add( new BufferAPI( m_apiEnvironment ) );
|
||||
if( ComputerCraft.http_enable )
|
||||
{
|
||||
@@ -622,7 +621,7 @@ public class Computer
|
||||
private void initLua()
|
||||
{
|
||||
// Create the lua machine
|
||||
ILuaMachine machine = new LuaJLuaMachine( this );
|
||||
ILuaMachine machine = new CobaltLuaMachine( this );
|
||||
|
||||
// Add the APIs
|
||||
for(ILuaAPI api : m_apis)
|
||||
|
@@ -8,223 +8,322 @@ package dan200.computercraft.core.computer;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
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;
|
||||
|
||||
public class ComputerThread
|
||||
{
|
||||
private static final Object m_lock;
|
||||
|
||||
private static Thread m_thread;
|
||||
private static final WeakHashMap <Object, LinkedBlockingQueue<ITask>> m_computerTasks;
|
||||
private static final ArrayList <LinkedBlockingQueue<ITask>> m_computerTasksActive;
|
||||
private static final ArrayList <LinkedBlockingQueue<ITask>> m_computerTasksPending;
|
||||
private static final Object m_defaultQueue;
|
||||
private static final Object m_monitor;
|
||||
private static final int QUEUE_LIMIT = 256;
|
||||
|
||||
private static boolean m_running;
|
||||
private static boolean m_stopped;
|
||||
|
||||
static
|
||||
{
|
||||
m_lock = new Object();
|
||||
m_thread = null;
|
||||
m_computerTasks = new WeakHashMap<>();
|
||||
m_computerTasksPending = new ArrayList<>();
|
||||
m_computerTasksActive = new ArrayList<>();
|
||||
m_defaultQueue = new Object();
|
||||
m_monitor = new Object();
|
||||
m_running = false;
|
||||
m_stopped = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock used for modifications to the object
|
||||
*/
|
||||
private static final Object s_stateLock = new Object();
|
||||
|
||||
/**
|
||||
* Lock for various task operations
|
||||
*/
|
||||
private static final Object s_taskLock = new Object();
|
||||
|
||||
/**
|
||||
* Map of objects to task list
|
||||
*/
|
||||
private static final WeakHashMap<Object, BlockingQueue<ITask>> s_computerTaskQueues = new WeakHashMap<>();
|
||||
|
||||
/**
|
||||
* Active queues to execute
|
||||
*/
|
||||
private static final BlockingQueue<BlockingQueue<ITask>> s_computerTasksActive = new LinkedBlockingQueue<>();
|
||||
private static final Set<BlockingQueue<ITask>> s_computerTasksActiveSet = new HashSet<>();
|
||||
|
||||
/**
|
||||
* The default object for items which don't have an owner
|
||||
*/
|
||||
private static final Object s_defaultOwner = new Object();
|
||||
|
||||
/**
|
||||
* Whether the thread is stopped or should be stopped
|
||||
*/
|
||||
private static boolean s_stopped = false;
|
||||
|
||||
/**
|
||||
* The thread tasks execute on
|
||||
*/
|
||||
private static Thread[] s_threads = null;
|
||||
|
||||
private static final AtomicInteger s_ManagerCounter = new AtomicInteger( 1 );
|
||||
private static final AtomicInteger s_DelegateCounter = new AtomicInteger( 1 );
|
||||
|
||||
/**
|
||||
* Start the computer thread
|
||||
*/
|
||||
public static void start()
|
||||
{
|
||||
synchronized( m_lock )
|
||||
synchronized( s_stateLock )
|
||||
{
|
||||
if( m_running )
|
||||
s_stopped = false;
|
||||
if( s_threads == null || s_threads.length != ComputerCraft.computer_threads )
|
||||
{
|
||||
m_stopped = false;
|
||||
return;
|
||||
s_threads = new Thread[ ComputerCraft.computer_threads ];
|
||||
}
|
||||
|
||||
m_thread = new Thread( () ->
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to stop the computer thread
|
||||
*/
|
||||
public static void stop()
|
||||
{
|
||||
synchronized( s_stateLock )
|
||||
{
|
||||
if( s_threads != null )
|
||||
{
|
||||
s_stopped = true;
|
||||
for( Thread thread : s_threads )
|
||||
{
|
||||
if( thread != null && thread.isAlive() )
|
||||
{
|
||||
thread.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized( s_taskLock )
|
||||
{
|
||||
s_computerTaskQueues.clear();
|
||||
s_computerTasksActive.clear();
|
||||
s_computerTasksActiveSet.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a task to execute on the thread
|
||||
*
|
||||
* @param task The task to execute
|
||||
* @param computer The computer to execute it on, use {@code null} to execute on the default object.
|
||||
*/
|
||||
public static void queueTask( ITask task, Computer computer )
|
||||
{
|
||||
Object queueObject = computer == null ? s_defaultOwner : computer;
|
||||
|
||||
BlockingQueue<ITask> queue;
|
||||
synchronized( s_computerTaskQueues )
|
||||
{
|
||||
queue = s_computerTaskQueues.get( queueObject );
|
||||
if( queue == null )
|
||||
{
|
||||
s_computerTaskQueues.put( queueObject, queue = new LinkedBlockingQueue<>( QUEUE_LIMIT ) );
|
||||
}
|
||||
}
|
||||
|
||||
synchronized( s_taskLock )
|
||||
{
|
||||
if( queue.offer( task ) && !s_computerTasksActiveSet.contains( queue ) )
|
||||
{
|
||||
s_computerTasksActive.add( queue );
|
||||
s_computerTasksActiveSet.add( queue );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for pulling and managing computer tasks. This pulls a task from {@link #s_computerTasksActive},
|
||||
* creates a new thread using {@link TaskRunner} or reuses a previous one and uses that to execute the task.
|
||||
*
|
||||
* If the task times out, then it will attempt to interrupt the {@link TaskRunner} instance.
|
||||
*/
|
||||
private static final class TaskExecutor implements Runnable
|
||||
{
|
||||
private TaskRunner runner;
|
||||
private Thread thread;
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
synchronized( m_computerTasksPending )
|
||||
{
|
||||
if (!m_computerTasksPending.isEmpty())
|
||||
{
|
||||
Iterator<LinkedBlockingQueue<ITask>> it = m_computerTasksPending.iterator();
|
||||
while(it.hasNext())
|
||||
{
|
||||
LinkedBlockingQueue<ITask> queue = it.next();
|
||||
|
||||
if (!m_computerTasksActive.contains(queue))
|
||||
{
|
||||
m_computerTasksActive.add(queue);
|
||||
}
|
||||
it.remove();
|
||||
}
|
||||
/*
|
||||
m_computerTasksActive.addAll(m_computerTasksPending); // put any that have been added since
|
||||
m_computerTasksPending.clear();
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
Iterator<LinkedBlockingQueue<ITask>> it = m_computerTasksActive.iterator();
|
||||
|
||||
while (it.hasNext())
|
||||
{
|
||||
LinkedBlockingQueue<ITask> queue = it.next();
|
||||
|
||||
if (queue == null || queue.isEmpty()) // we don't need the blocking part of the queue. Null check to ensure it exists due to a weird NPE I got
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
synchronized( m_lock )
|
||||
{
|
||||
if( m_stopped )
|
||||
{
|
||||
m_running = false;
|
||||
m_thread = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
final ITask task = queue.take();
|
||||
// Wait for an active queue to execute
|
||||
BlockingQueue<ITask> queue = s_computerTasksActive.take();
|
||||
|
||||
// Create the task
|
||||
Thread worker = new Thread( () ->
|
||||
{
|
||||
try {
|
||||
task.execute();
|
||||
} catch( Throwable e ) {
|
||||
ComputerCraft.log.error( "Error running task", e );
|
||||
}
|
||||
} );
|
||||
|
||||
// Run the task
|
||||
worker.setDaemon(true);
|
||||
worker.start();
|
||||
worker.join( 7000 );
|
||||
|
||||
if( worker.isAlive() )
|
||||
{
|
||||
// Task ran for too long
|
||||
// Initiate escape plan
|
||||
Computer computer = task.getOwner();
|
||||
if( computer != null )
|
||||
{
|
||||
// Step 1: Soft abort
|
||||
computer.abort( false );
|
||||
worker.join( 1500 );
|
||||
|
||||
if( worker.isAlive() )
|
||||
{
|
||||
// Step 2: Hard abort
|
||||
computer.abort( true );
|
||||
worker.join( 1500 );
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: abandon
|
||||
if( worker.isAlive() )
|
||||
{
|
||||
// ComputerCraft.log.warn( "Failed to abort Computer " + computer.getID() + ". Dangling lua thread could cause errors." );
|
||||
worker.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( InterruptedException e )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
synchronized (queue)
|
||||
{
|
||||
if (queue.isEmpty())
|
||||
{
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (m_computerTasksActive.isEmpty() && m_computerTasksPending.isEmpty())
|
||||
// If threads should be stopped then return
|
||||
synchronized( s_stateLock )
|
||||
{
|
||||
synchronized (m_monitor)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_monitor.wait();
|
||||
}
|
||||
catch( InterruptedException e )
|
||||
{
|
||||
}
|
||||
}
|
||||
if( s_stopped ) return;
|
||||
}
|
||||
|
||||
execute( queue );
|
||||
}
|
||||
}, "Computer Dispatch Thread" );
|
||||
|
||||
m_thread.setDaemon(true);
|
||||
m_thread.start();
|
||||
m_running = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void stop()
|
||||
{
|
||||
synchronized( m_lock )
|
||||
{
|
||||
if( m_running )
|
||||
}
|
||||
catch( InterruptedException ignored )
|
||||
{
|
||||
m_stopped = true;
|
||||
m_thread.interrupt();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void queueTask( ITask _task, Computer computer )
|
||||
{
|
||||
Object queueObject = computer;
|
||||
|
||||
if (queueObject == null)
|
||||
{
|
||||
queueObject = m_defaultQueue;
|
||||
}
|
||||
|
||||
LinkedBlockingQueue<ITask> queue = m_computerTasks.get(queueObject);
|
||||
|
||||
if (queue == null)
|
||||
private void execute( BlockingQueue<ITask> queue ) throws InterruptedException
|
||||
{
|
||||
m_computerTasks.put(queueObject, queue = new LinkedBlockingQueue<>( 256 ));
|
||||
}
|
||||
|
||||
synchronized ( m_computerTasksPending )
|
||||
{
|
||||
if( queue.offer( _task ) )
|
||||
ITask task = queue.remove();
|
||||
|
||||
if( thread == null || !thread.isAlive() )
|
||||
{
|
||||
if( !m_computerTasksPending.contains( queue ) )
|
||||
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();
|
||||
}
|
||||
|
||||
// Execute the task
|
||||
runner.submit( task );
|
||||
|
||||
try
|
||||
{
|
||||
// If we timed out rather than exiting:
|
||||
boolean done = runner.await( 7000 );
|
||||
if( !done )
|
||||
{
|
||||
m_computerTasksPending.add( queue );
|
||||
// Attempt to soft then hard abort
|
||||
Computer computer = task.getOwner();
|
||||
if( computer != null )
|
||||
{
|
||||
computer.abort( false );
|
||||
|
||||
done = runner.await( 1500 );
|
||||
if( !done )
|
||||
{
|
||||
computer.abort( true );
|
||||
done = runner.await( 1500 );
|
||||
}
|
||||
}
|
||||
|
||||
// Interrupt the thread
|
||||
if( !done )
|
||||
{
|
||||
thread.interrupt();
|
||||
thread = null;
|
||||
runner = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
//System.out.println( "Event queue overflow" );
|
||||
// Re-add it back onto the queue or remove it
|
||||
synchronized( s_taskLock )
|
||||
{
|
||||
if( queue.isEmpty() )
|
||||
{
|
||||
s_computerTasksActiveSet.remove( queue );
|
||||
}
|
||||
else
|
||||
{
|
||||
s_computerTasksActive.add( queue );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (m_monitor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for the actual running of tasks. It waitin for the {@link TaskRunner#input} semaphore to be
|
||||
* triggered, consumes a task and then triggers {@link TaskRunner#finished}.
|
||||
*/
|
||||
private static final class TaskRunner implements Runnable
|
||||
{
|
||||
private final Semaphore input = new Semaphore();
|
||||
private final Semaphore finished = new Semaphore();
|
||||
private ITask task;
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
m_monitor.notify();
|
||||
try
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
input.await();
|
||||
try
|
||||
{
|
||||
task.execute();
|
||||
}
|
||||
catch( RuntimeException e )
|
||||
{
|
||||
ComputerCraft.log.error( "Error running task.", e );
|
||||
}
|
||||
task = null;
|
||||
finished.signal();
|
||||
}
|
||||
}
|
||||
catch( InterruptedException e )
|
||||
{
|
||||
ComputerCraft.log.error( "Error running task.", e );
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
void submit( ITask task )
|
||||
{
|
||||
this.task = task;
|
||||
input.signal();
|
||||
}
|
||||
|
||||
boolean await( long timeout ) throws InterruptedException
|
||||
{
|
||||
return finished.await( timeout );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple method to allow awaiting/providing a signal.
|
||||
*
|
||||
* Java does provide similar classes, but I only needed something simple.
|
||||
*/
|
||||
private static final class Semaphore
|
||||
{
|
||||
private volatile boolean state = false;
|
||||
|
||||
synchronized void signal()
|
||||
{
|
||||
state = true;
|
||||
notify();
|
||||
}
|
||||
|
||||
synchronized void await() throws InterruptedException
|
||||
{
|
||||
while( !state ) wait();
|
||||
state = false;
|
||||
}
|
||||
|
||||
synchronized boolean await( long timeout ) throws InterruptedException
|
||||
{
|
||||
if( !state )
|
||||
{
|
||||
wait( timeout );
|
||||
if( !state ) return false;
|
||||
}
|
||||
state = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,6 @@ package dan200.computercraft.core.filesystem;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
@@ -22,31 +21,31 @@ public class EmptyMount implements IMount
|
||||
// IMount implementation
|
||||
|
||||
@Override
|
||||
public boolean exists( @Nonnull String path ) throws IOException
|
||||
public boolean exists( @Nonnull String path )
|
||||
{
|
||||
return path.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory( @Nonnull String path ) throws IOException
|
||||
public boolean isDirectory( @Nonnull String path )
|
||||
{
|
||||
return path.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
|
||||
public void list( @Nonnull String path, @Nonnull List<String> contents )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize( @Nonnull String path ) throws IOException
|
||||
public long getSize( @Nonnull String path )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InputStream openForRead( @Nonnull String path ) throws IOException
|
||||
public InputStream openForRead( @Nonnull String path )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
@@ -95,7 +95,7 @@ public class FileMount implements IWritableMount
|
||||
// IMount implementation
|
||||
|
||||
@Override
|
||||
public boolean exists( @Nonnull String path ) throws IOException
|
||||
public boolean exists( @Nonnull String path )
|
||||
{
|
||||
if( !created() )
|
||||
{
|
||||
@@ -109,7 +109,7 @@ public class FileMount implements IWritableMount
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory( @Nonnull String path ) throws IOException
|
||||
public boolean isDirectory( @Nonnull String path )
|
||||
{
|
||||
if( !created() )
|
||||
{
|
||||
@@ -339,7 +339,7 @@ public class FileMount implements IWritableMount
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRemainingSpace() throws IOException
|
||||
public long getRemainingSpace()
|
||||
{
|
||||
return Math.max( m_capacity - m_usedSpace, 0 );
|
||||
}
|
||||
|
@@ -65,7 +65,7 @@ public class FileSystem
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isReadOnly( String path ) throws FileSystemException
|
||||
public boolean isReadOnly( String path )
|
||||
{
|
||||
return (m_writableMount == null);
|
||||
}
|
||||
@@ -347,7 +347,7 @@ public class FileSystem
|
||||
mount( new MountWrapper( label, location, mount ) );
|
||||
}
|
||||
|
||||
private synchronized void mount( MountWrapper wrapper ) throws FileSystemException
|
||||
private synchronized void mount( MountWrapper wrapper )
|
||||
{
|
||||
String location = wrapper.getLocation();
|
||||
if( m_mounts.containsKey( location ) )
|
||||
|
@@ -176,14 +176,14 @@ public class JarMount implements IMount
|
||||
// IMount implementation
|
||||
|
||||
@Override
|
||||
public boolean exists( @Nonnull String path ) throws IOException
|
||||
public boolean exists( @Nonnull String path )
|
||||
{
|
||||
FileInZip file = m_root.getFile( path );
|
||||
return file != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory( @Nonnull String path ) throws IOException
|
||||
public boolean isDirectory( @Nonnull String path )
|
||||
{
|
||||
FileInZip file = m_root.getFile( path );
|
||||
if( file != null )
|
||||
|
@@ -15,105 +15,131 @@ import dan200.computercraft.core.apis.ILuaAPI;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.ITask;
|
||||
import dan200.computercraft.core.computer.MainThread;
|
||||
|
||||
import org.luaj.vm2.*;
|
||||
import org.luaj.vm2.lib.OneArgFunction;
|
||||
import org.luaj.vm2.lib.VarArgFunction;
|
||||
import org.luaj.vm2.lib.ZeroArgFunction;
|
||||
import org.luaj.vm2.lib.jse.JsePlatform;
|
||||
import org.squiddev.cobalt.*;
|
||||
import org.squiddev.cobalt.compiler.CompileException;
|
||||
import org.squiddev.cobalt.compiler.LoadState;
|
||||
import org.squiddev.cobalt.debug.DebugFrame;
|
||||
import org.squiddev.cobalt.debug.DebugHandler;
|
||||
import org.squiddev.cobalt.debug.DebugState;
|
||||
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 javax.annotation.Nonnull;
|
||||
import java.io.*;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class LuaJLuaMachine implements ILuaMachine
|
||||
{
|
||||
private Computer m_computer;
|
||||
import static org.squiddev.cobalt.Constants.NONE;
|
||||
import static org.squiddev.cobalt.ValueFactory.valueOf;
|
||||
import static org.squiddev.cobalt.ValueFactory.varargsOf;
|
||||
|
||||
private LuaValue m_globals;
|
||||
private LuaValue m_loadString;
|
||||
private LuaValue m_assert;
|
||||
private LuaValue m_coroutine_create;
|
||||
private LuaValue m_coroutine_resume;
|
||||
private LuaValue m_coroutine_yield;
|
||||
|
||||
private LuaValue m_mainRoutine;
|
||||
public class CobaltLuaMachine implements ILuaMachine
|
||||
{
|
||||
private final Computer m_computer;
|
||||
|
||||
private final LuaState m_state;
|
||||
private final LuaTable m_globals;
|
||||
|
||||
private LuaThread m_mainRoutine;
|
||||
private String m_eventFilter;
|
||||
private String m_softAbortMessage;
|
||||
private String m_hardAbortMessage;
|
||||
|
||||
private Map<Object, LuaValue> m_valuesInProgress;
|
||||
private Map<LuaValue, Object> m_objectsInProgress;
|
||||
|
||||
public LuaJLuaMachine( Computer computer )
|
||||
public CobaltLuaMachine( Computer computer )
|
||||
{
|
||||
m_computer = computer;
|
||||
|
||||
// Create an environment to run in
|
||||
m_globals = JsePlatform.debugGlobals();
|
||||
m_loadString = m_globals.get("loadstring");
|
||||
m_assert = m_globals.get("assert");
|
||||
|
||||
LuaValue coroutine = m_globals.get("coroutine");
|
||||
final LuaValue native_coroutine_create = coroutine.get("create");
|
||||
|
||||
LuaValue debug = m_globals.get("debug");
|
||||
final LuaValue debug_sethook = debug.get("sethook");
|
||||
|
||||
coroutine.set("create", new OneArgFunction() {
|
||||
final LuaState state = this.m_state = new LuaState( new AbstractResourceManipulator()
|
||||
{
|
||||
@Override
|
||||
public LuaValue call( LuaValue value )
|
||||
public InputStream findResource( String filename )
|
||||
{
|
||||
final LuaThread thread = native_coroutine_create.call( value ).checkthread();
|
||||
debug_sethook.invoke( new LuaValue[] {
|
||||
thread,
|
||||
new ZeroArgFunction() {
|
||||
@Override
|
||||
public LuaValue call() {
|
||||
String hardAbortMessage = m_hardAbortMessage;
|
||||
if( hardAbortMessage != null )
|
||||
{
|
||||
LuaThread.yield(LuaValue.NIL);
|
||||
}
|
||||
return LuaValue.NIL;
|
||||
}
|
||||
},
|
||||
LuaValue.NIL,
|
||||
LuaValue.valueOf(100000)
|
||||
} );
|
||||
return thread;
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
m_coroutine_create = coroutine.get("create");
|
||||
m_coroutine_resume = coroutine.get("resume");
|
||||
m_coroutine_yield = coroutine.get("yield");
|
||||
|
||||
} );
|
||||
state.debug = new DebugHandler( state )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
if( m_hardAbortMessage != null ) LuaThread.yield( state, NONE );
|
||||
this.count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
handleSoftAbort();
|
||||
}
|
||||
|
||||
super.onInstruction( ds, di, pc, extras, top );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void poll() throws LuaError
|
||||
{
|
||||
if( m_hardAbortMessage != null ) LuaThread.yield( state, NONE );
|
||||
handleSoftAbort();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
m_globals = new LuaTable();
|
||||
state.setupThread( m_globals );
|
||||
|
||||
// Add basic libraries
|
||||
m_globals.load( state, new BaseLib() );
|
||||
m_globals.load( state, new TableLib() );
|
||||
m_globals.load( state, new StringLib() );
|
||||
m_globals.load( state, new MathLib() );
|
||||
m_globals.load( state, new CoroutineLib() );
|
||||
m_globals.load( state, new Bit32Lib() );
|
||||
if( ComputerCraft.debug_enable ) m_globals.load( state, new DebugLib() );
|
||||
|
||||
// Register custom load/loadstring provider which automatically adds prefixes.
|
||||
LibFunction.bind( state, m_globals, PrefixLoader.class, new String[]{ "load", "loadstring" } );
|
||||
|
||||
// Remove globals we don't want to expose
|
||||
m_globals.set( "collectgarbage", LuaValue.NIL );
|
||||
m_globals.set( "dofile", LuaValue.NIL );
|
||||
m_globals.set( "loadfile", LuaValue.NIL );
|
||||
m_globals.set( "module", LuaValue.NIL );
|
||||
m_globals.set( "require", LuaValue.NIL );
|
||||
m_globals.set( "package", LuaValue.NIL );
|
||||
m_globals.set( "io", LuaValue.NIL );
|
||||
m_globals.set( "os", LuaValue.NIL );
|
||||
m_globals.set( "print", LuaValue.NIL );
|
||||
m_globals.set( "luajava", LuaValue.NIL );
|
||||
m_globals.set( "debug", LuaValue.NIL );
|
||||
m_globals.set( "newproxy", LuaValue.NIL );
|
||||
m_globals.set( "__inext", LuaValue.NIL );
|
||||
m_globals.rawset( "collectgarbage", Constants.NIL );
|
||||
m_globals.rawset( "dofile", Constants.NIL );
|
||||
m_globals.rawset( "loadfile", Constants.NIL );
|
||||
m_globals.rawset( "print", Constants.NIL );
|
||||
|
||||
// Add version globals
|
||||
m_globals.set( "_VERSION", "Lua 5.1" );
|
||||
m_globals.set( "_HOST", computer.getAPIEnvironment().getComputerEnvironment().getHostString() );
|
||||
m_globals.set( "_CC_DEFAULT_SETTINGS", toValue( ComputerCraft.default_computer_settings ) );
|
||||
m_globals.rawset( "_VERSION", valueOf( "Lua 5.1" ) );
|
||||
m_globals.rawset( "_HOST", valueOf( computer.getAPIEnvironment().getComputerEnvironment().getHostString() ) );
|
||||
m_globals.rawset( "_CC_DEFAULT_SETTINGS", valueOf( ComputerCraft.default_computer_settings ) );
|
||||
if( ComputerCraft.disable_lua51_features )
|
||||
{
|
||||
m_globals.set( "_CC_DISABLE_LUA51_FEATURES", toValue( true ) );
|
||||
m_globals.rawset( "_CC_DISABLE_LUA51_FEATURES", Constants.TRUE );
|
||||
}
|
||||
|
||||
// Our main function will go here
|
||||
@@ -123,7 +149,7 @@ public class LuaJLuaMachine implements ILuaMachine
|
||||
m_softAbortMessage = null;
|
||||
m_hardAbortMessage = null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addAPI( ILuaAPI api )
|
||||
{
|
||||
@@ -132,10 +158,10 @@ public class LuaJLuaMachine implements ILuaMachine
|
||||
String[] names = api.getNames();
|
||||
for( String name : names )
|
||||
{
|
||||
m_globals.set( name, table );
|
||||
m_globals.rawset( name, table );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void loadBios( InputStream bios )
|
||||
{
|
||||
@@ -144,56 +170,31 @@ public class LuaJLuaMachine implements ILuaMachine
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
// Read the whole bios into a string
|
||||
String biosText;
|
||||
try
|
||||
{
|
||||
InputStreamReader isr;
|
||||
try
|
||||
{
|
||||
isr = new InputStreamReader( bios, "UTF-8" );
|
||||
}
|
||||
catch( UnsupportedEncodingException e )
|
||||
{
|
||||
isr = new InputStreamReader( bios );
|
||||
}
|
||||
BufferedReader reader = new BufferedReader( isr );
|
||||
StringBuilder fileText = new StringBuilder( "" );
|
||||
String line = reader.readLine();
|
||||
while( line != null ) {
|
||||
fileText.append( line );
|
||||
line = reader.readLine();
|
||||
if( line != null ) {
|
||||
fileText.append( "\n" );
|
||||
}
|
||||
}
|
||||
biosText = fileText.toString();
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
throw new LuaError( "Could not read file" );
|
||||
}
|
||||
|
||||
// Load it
|
||||
LuaValue program = m_assert.call( m_loadString.call(
|
||||
toValue( biosText ), toValue( "bios.lua" )
|
||||
));
|
||||
m_mainRoutine = m_coroutine_create.call( program );
|
||||
LuaFunction value = LoadState.load( m_state, bios, "@bios.lua", m_globals );
|
||||
m_mainRoutine = new LuaThread( m_state, value, m_globals );
|
||||
}
|
||||
catch( LuaError e )
|
||||
catch( CompileException e )
|
||||
{
|
||||
if( m_mainRoutine != null )
|
||||
{
|
||||
m_mainRoutine.abandon();
|
||||
m_mainRoutine = null;
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
ComputerCraft.log.warn( "Could not load bios.lua ", e );
|
||||
if( m_mainRoutine != null )
|
||||
{
|
||||
((LuaThread)m_mainRoutine).abandon();
|
||||
m_mainRoutine.abandon();
|
||||
m_mainRoutine = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleEvent( String eventName, Object[] arguments )
|
||||
{
|
||||
@@ -206,35 +207,28 @@ public class LuaJLuaMachine implements ILuaMachine
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
LuaValue[] resumeArgs;
|
||||
{
|
||||
Varargs resumeArgs = Constants.NONE;
|
||||
if( eventName != null )
|
||||
{
|
||||
resumeArgs = toValues( arguments, 2 );
|
||||
resumeArgs[0] = m_mainRoutine;
|
||||
resumeArgs[1] = toValue( eventName );
|
||||
resumeArgs = varargsOf( valueOf( eventName ), toValues( arguments ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
resumeArgs = new LuaValue[1];
|
||||
resumeArgs[0] = m_mainRoutine;
|
||||
}
|
||||
|
||||
Varargs results = m_coroutine_resume.invoke( LuaValue.varargsOf( resumeArgs ) );
|
||||
if( m_hardAbortMessage != null )
|
||||
|
||||
Varargs results = m_mainRoutine.resume( resumeArgs );
|
||||
if( m_hardAbortMessage != null )
|
||||
{
|
||||
throw new LuaError( m_hardAbortMessage );
|
||||
}
|
||||
else if( results.arg1().checkboolean() == false )
|
||||
else if( !results.first().checkBoolean() )
|
||||
{
|
||||
throw new LuaError( results.arg(2).checkstring().toString() );
|
||||
throw new LuaError( results.arg( 2 ).checkString() );
|
||||
}
|
||||
else
|
||||
{
|
||||
LuaValue filter = results.arg(2);
|
||||
if( filter.isstring() )
|
||||
LuaValue filter = results.arg( 2 );
|
||||
if( filter.isString() )
|
||||
{
|
||||
m_eventFilter = filter.toString();
|
||||
}
|
||||
@@ -243,16 +237,16 @@ public class LuaJLuaMachine implements ILuaMachine
|
||||
m_eventFilter = null;
|
||||
}
|
||||
}
|
||||
|
||||
LuaThread mainThread = (LuaThread)m_mainRoutine;
|
||||
if( mainThread.getStatus().equals("dead") )
|
||||
|
||||
LuaThread mainThread = m_mainRoutine;
|
||||
if( mainThread.getStatus().equals( "dead" ) )
|
||||
{
|
||||
m_mainRoutine = null;
|
||||
}
|
||||
}
|
||||
catch( LuaError e )
|
||||
{
|
||||
((LuaThread)m_mainRoutine).abandon();
|
||||
m_mainRoutine.abandon();
|
||||
m_mainRoutine = null;
|
||||
}
|
||||
finally
|
||||
@@ -262,13 +256,13 @@ public class LuaJLuaMachine implements ILuaMachine
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void softAbort( String abortMessage )
|
||||
{
|
||||
m_softAbortMessage = abortMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void hardAbort( String abortMessage )
|
||||
{
|
||||
m_softAbortMessage = abortMessage;
|
||||
@@ -280,100 +274,88 @@ public class LuaJLuaMachine implements ILuaMachine
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean restoreState( InputStream input )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isFinished()
|
||||
{
|
||||
return (m_mainRoutine == null);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void unload()
|
||||
{
|
||||
if( m_mainRoutine != null )
|
||||
{
|
||||
LuaThread mainThread = (LuaThread)m_mainRoutine;
|
||||
LuaThread mainThread = m_mainRoutine;
|
||||
mainThread.abandon();
|
||||
m_mainRoutine = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void tryAbort() throws LuaError
|
||||
{
|
||||
// while( m_stopped )
|
||||
// {
|
||||
// m_coroutine_yield.call();
|
||||
// }
|
||||
|
||||
String abortMessage = m_softAbortMessage;
|
||||
if( abortMessage != null )
|
||||
{
|
||||
m_softAbortMessage = null;
|
||||
m_hardAbortMessage = null;
|
||||
throw new LuaError( abortMessage );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private LuaTable wrapLuaObject( ILuaObject object )
|
||||
{
|
||||
LuaTable table = new LuaTable();
|
||||
String[] methods = object.getMethodNames();
|
||||
for(int i=0; i<methods.length; ++i )
|
||||
for( int i = 0; i < methods.length; ++i )
|
||||
{
|
||||
if( methods[i] != null )
|
||||
if( methods[ i ] != null )
|
||||
{
|
||||
final int method = i;
|
||||
final ILuaObject apiObject = object;
|
||||
final String methodName = methods[i];
|
||||
table.set( methodName, new VarArgFunction() {
|
||||
final String methodName = methods[ i ];
|
||||
table.rawset( methodName, new VarArgFunction()
|
||||
{
|
||||
@Override
|
||||
public Varargs invoke( Varargs _args )
|
||||
public Varargs invoke( final LuaState state, Varargs _args ) throws LuaError
|
||||
{
|
||||
tryAbort();
|
||||
Object[] arguments = toObjects( _args, 1 );
|
||||
Object[] results;
|
||||
try
|
||||
{
|
||||
results = apiObject.callMethod( new ILuaContext() {
|
||||
results = apiObject.callMethod( new ILuaContext()
|
||||
{
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] pullEvent( String filter ) throws LuaException, InterruptedException
|
||||
{
|
||||
Object[] results = pullEventRaw( filter );
|
||||
if( results.length >= 1 && results[0].equals( "terminate" ) )
|
||||
if( results.length >= 1 && results[ 0 ].equals( "terminate" ) )
|
||||
{
|
||||
throw new LuaException( "Terminated", 0 );
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] pullEventRaw( String filter ) throws InterruptedException
|
||||
{
|
||||
return yield( new Object[] { filter } );
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] yield( Object[] yieldArgs ) throws InterruptedException
|
||||
{
|
||||
try
|
||||
{
|
||||
LuaValue[] yieldValues = toValues( yieldArgs, 0 );
|
||||
Varargs results = m_coroutine_yield.invoke( LuaValue.varargsOf( yieldValues ) );
|
||||
Varargs results = LuaThread.yield( state, toValues( yieldArgs ) );
|
||||
return toObjects( results, 1 );
|
||||
}
|
||||
catch( OrphanedThread e )
|
||||
{
|
||||
throw new InterruptedException();
|
||||
}
|
||||
catch( Throwable e )
|
||||
{
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -448,10 +430,10 @@ public class LuaJLuaMachine implements ILuaMachine
|
||||
Object[] response = pullEvent( "task_complete" );
|
||||
if( response.length >= 3 && response[ 1 ] instanceof Number && response[ 2 ] instanceof Boolean )
|
||||
{
|
||||
if( ( (Number)response[ 1 ] ).intValue() == taskID )
|
||||
if( ((Number) response[ 1 ]).intValue() == taskID )
|
||||
{
|
||||
Object[] returnValues = new Object[ response.length - 3 ];
|
||||
if( (Boolean)response[ 2 ] )
|
||||
if( (Boolean) response[ 2 ] )
|
||||
{
|
||||
// Extract the return values from the event and return them
|
||||
System.arraycopy( response, 3, returnValues, 0, returnValues.length );
|
||||
@@ -460,9 +442,9 @@ public class LuaJLuaMachine implements ILuaMachine
|
||||
else
|
||||
{
|
||||
// Extract the error message from the event and raise it
|
||||
if( response.length >= 4 && response[3] instanceof String )
|
||||
if( response.length >= 4 && response[ 3 ] instanceof String )
|
||||
{
|
||||
throw new LuaException( (String)response[ 3 ] );
|
||||
throw new LuaException( (String) response[ 3 ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -479,7 +461,7 @@ public class LuaJLuaMachine implements ILuaMachine
|
||||
catch( InterruptedException e )
|
||||
{
|
||||
throw new OrphanedThread();
|
||||
}
|
||||
}
|
||||
catch( LuaException e )
|
||||
{
|
||||
throw new LuaError( e.getMessage(), e.getLevel() );
|
||||
@@ -492,7 +474,7 @@ public class LuaJLuaMachine implements ILuaMachine
|
||||
}
|
||||
throw new LuaError( "Java Exception Thrown: " + t.toString(), 0 );
|
||||
}
|
||||
return LuaValue.varargsOf( toValues( results, 0 ) );
|
||||
return toValues( results );
|
||||
}
|
||||
} );
|
||||
}
|
||||
@@ -500,192 +482,257 @@ public class LuaJLuaMachine implements ILuaMachine
|
||||
return table;
|
||||
}
|
||||
|
||||
private LuaValue toValue( Object object )
|
||||
private LuaValue toValue( Object object, Map<Object, LuaValue> values )
|
||||
{
|
||||
if( object == null )
|
||||
{
|
||||
return LuaValue.NIL;
|
||||
return Constants.NIL;
|
||||
}
|
||||
else if( object instanceof Number )
|
||||
{
|
||||
double d = ((Number)object).doubleValue();
|
||||
return LuaValue.valueOf( d );
|
||||
double d = ((Number) object).doubleValue();
|
||||
return valueOf( d );
|
||||
}
|
||||
else if( object instanceof Boolean )
|
||||
{
|
||||
boolean b = (Boolean) object;
|
||||
return LuaValue.valueOf( b );
|
||||
return valueOf( (Boolean) object );
|
||||
}
|
||||
else if( object instanceof String )
|
||||
{
|
||||
String s = object.toString();
|
||||
return LuaValue.valueOf( s );
|
||||
return valueOf( s );
|
||||
}
|
||||
else if( object instanceof byte[] )
|
||||
{
|
||||
byte[] b = (byte[]) object;
|
||||
return LuaValue.valueOf( Arrays.copyOf( b, b.length ) );
|
||||
return valueOf( Arrays.copyOf( b, b.length ) );
|
||||
}
|
||||
else if( object instanceof Map )
|
||||
{
|
||||
// Table:
|
||||
// Start remembering stuff
|
||||
boolean clearWhenDone = false;
|
||||
try
|
||||
if( values == null )
|
||||
{
|
||||
if( m_valuesInProgress == null )
|
||||
{
|
||||
m_valuesInProgress = new IdentityHashMap<>();
|
||||
clearWhenDone = true;
|
||||
}
|
||||
else if( m_valuesInProgress.containsKey( object ) )
|
||||
{
|
||||
return m_valuesInProgress.get( object );
|
||||
}
|
||||
LuaValue table = new LuaTable();
|
||||
m_valuesInProgress.put( object, table );
|
||||
values = new IdentityHashMap<>();
|
||||
}
|
||||
else if( values.containsKey( object ) )
|
||||
{
|
||||
return values.get( object );
|
||||
}
|
||||
LuaTable table = new LuaTable();
|
||||
values.put( object, table );
|
||||
|
||||
// Convert all keys
|
||||
for( Map.Entry<?, ?> pair : ((Map<?, ?>) object).entrySet() )
|
||||
{
|
||||
LuaValue key = toValue( pair.getKey() );
|
||||
LuaValue value = toValue( pair.getValue() );
|
||||
if( !key.isnil() && !value.isnil() )
|
||||
{
|
||||
table.set( key, value );
|
||||
}
|
||||
}
|
||||
return table;
|
||||
}
|
||||
finally
|
||||
// Convert all keys
|
||||
for( Map.Entry<?, ?> pair : ((Map<?, ?>) object).entrySet() )
|
||||
{
|
||||
// Clear (if exiting top level)
|
||||
if( clearWhenDone )
|
||||
LuaValue key = toValue( pair.getKey(), values );
|
||||
LuaValue value = toValue( pair.getValue(), values );
|
||||
if( !key.isNil() && !value.isNil() )
|
||||
{
|
||||
m_valuesInProgress = null;
|
||||
table.rawset( key, value );
|
||||
}
|
||||
}
|
||||
return table;
|
||||
}
|
||||
else if( object instanceof ILuaObject )
|
||||
{
|
||||
return wrapLuaObject( (ILuaObject)object );
|
||||
return wrapLuaObject( (ILuaObject) object );
|
||||
}
|
||||
else
|
||||
{
|
||||
return LuaValue.NIL;
|
||||
}
|
||||
return Constants.NIL;
|
||||
}
|
||||
}
|
||||
|
||||
private LuaValue[] toValues( Object[] objects, int leaveEmpty )
|
||||
private Varargs toValues( Object[] objects )
|
||||
{
|
||||
if( objects == null || objects.length == 0 )
|
||||
if( objects == null || objects.length == 0 )
|
||||
{
|
||||
return new LuaValue[ leaveEmpty ];
|
||||
return Constants.NONE;
|
||||
}
|
||||
|
||||
LuaValue[] values = new LuaValue[objects.length + leaveEmpty];
|
||||
for( int i=0; i<values.length; ++i )
|
||||
|
||||
LuaValue[] values = new LuaValue[ objects.length ];
|
||||
for( int i = 0; i < values.length; ++i )
|
||||
{
|
||||
if( i < leaveEmpty )
|
||||
{
|
||||
values[i] = null;
|
||||
continue;
|
||||
}
|
||||
Object object = objects[i - leaveEmpty];
|
||||
values[i] = toValue( object );
|
||||
Object object = objects[ i ];
|
||||
values[ i ] = toValue( object, null );
|
||||
}
|
||||
return values;
|
||||
return varargsOf( values );
|
||||
}
|
||||
|
||||
private Object toObject( LuaValue value )
|
||||
private static Object toObject( LuaValue value, Map<LuaValue, Object> objects )
|
||||
{
|
||||
switch( value.type() )
|
||||
{
|
||||
case LuaValue.TNIL:
|
||||
case LuaValue.TNONE:
|
||||
case Constants.TNIL:
|
||||
case Constants.TNONE:
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case LuaValue.TINT:
|
||||
case LuaValue.TNUMBER:
|
||||
case Constants.TINT:
|
||||
case Constants.TNUMBER:
|
||||
{
|
||||
return value.todouble();
|
||||
return value.toDouble();
|
||||
}
|
||||
case LuaValue.TBOOLEAN:
|
||||
case Constants.TBOOLEAN:
|
||||
{
|
||||
return value.toboolean();
|
||||
return value.toBoolean();
|
||||
}
|
||||
case LuaValue.TSTRING:
|
||||
case Constants.TSTRING:
|
||||
{
|
||||
LuaString str = value.checkstring();
|
||||
return str.tojstring();
|
||||
return value.toString();
|
||||
}
|
||||
case LuaValue.TTABLE:
|
||||
case Constants.TTABLE:
|
||||
{
|
||||
// Table:
|
||||
boolean clearWhenDone = false;
|
||||
try
|
||||
// Start remembering stuff
|
||||
if( objects == null )
|
||||
{
|
||||
// Start remembering stuff
|
||||
if( m_objectsInProgress == null )
|
||||
{
|
||||
m_objectsInProgress = new IdentityHashMap<>();
|
||||
clearWhenDone = true;
|
||||
}
|
||||
else if( m_objectsInProgress.containsKey( value ) )
|
||||
{
|
||||
return m_objectsInProgress.get( value );
|
||||
}
|
||||
Map<Object, Object> table = new HashMap<>();
|
||||
m_objectsInProgress.put( value, table );
|
||||
|
||||
// Convert all keys
|
||||
LuaValue k = LuaValue.NIL;
|
||||
while( true )
|
||||
{
|
||||
Varargs keyValue = value.next( k );
|
||||
k = keyValue.arg1();
|
||||
if( k.isnil() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
LuaValue v = keyValue.arg(2);
|
||||
Object keyObject = toObject(k);
|
||||
Object valueObject = toObject(v);
|
||||
if( keyObject != null && valueObject != null )
|
||||
{
|
||||
table.put( keyObject, valueObject );
|
||||
}
|
||||
}
|
||||
return table;
|
||||
objects = new IdentityHashMap<>();
|
||||
}
|
||||
finally
|
||||
else if( objects.containsKey( value ) )
|
||||
{
|
||||
// Clear (if exiting top level)
|
||||
if( clearWhenDone )
|
||||
return objects.get( value );
|
||||
}
|
||||
Map<Object, Object> table = new HashMap<>();
|
||||
objects.put( value, table );
|
||||
|
||||
LuaTable luaTable = (LuaTable) value;
|
||||
|
||||
// Convert all keys
|
||||
LuaValue k = Constants.NIL;
|
||||
while( true )
|
||||
{
|
||||
Varargs keyValue;
|
||||
try
|
||||
{
|
||||
m_objectsInProgress = null;
|
||||
keyValue = luaTable.next( k );
|
||||
}
|
||||
catch( LuaError luaError )
|
||||
{
|
||||
break;
|
||||
}
|
||||
k = keyValue.first();
|
||||
if( k.isNil() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
LuaValue v = keyValue.arg( 2 );
|
||||
Object keyObject = toObject( k, objects );
|
||||
Object valueObject = toObject( v, objects );
|
||||
if( keyObject != null && valueObject != null )
|
||||
{
|
||||
table.put( keyObject, valueObject );
|
||||
}
|
||||
}
|
||||
return table;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object[] toObjects( Varargs values, int startIdx )
|
||||
|
||||
private static Object[] toObjects( Varargs values, int startIdx )
|
||||
{
|
||||
int count = values.narg();
|
||||
int count = values.count();
|
||||
Object[] objects = new Object[ count - startIdx + 1 ];
|
||||
for( int n=startIdx; n<=count; ++n )
|
||||
for( int n = startIdx; n <= count; ++n )
|
||||
{
|
||||
int i = n - startIdx;
|
||||
LuaValue value = values.arg(n);
|
||||
objects[i] = toObject( value );
|
||||
LuaValue value = values.arg( n );
|
||||
objects[ i ] = toObject( value, null );
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
|
||||
private static class PrefixLoader extends VarArgFunction
|
||||
{
|
||||
private static final LuaString FUNCTION_STR = valueOf( "function" );
|
||||
private static final LuaString EQ_STR = valueOf( "=" );
|
||||
|
||||
@Override
|
||||
public Varargs invoke( LuaState state, Varargs args ) throws LuaError
|
||||
{
|
||||
switch (opcode)
|
||||
{
|
||||
case 0: // "load", // ( func [,chunkname] ) -> chunk | nil, msg
|
||||
{
|
||||
LuaValue func = args.arg( 1 ).checkFunction();
|
||||
LuaString chunkname = args.arg( 2 ).optLuaString( FUNCTION_STR );
|
||||
if( !chunkname.startsWith( '@' ) && !chunkname.startsWith( '=' ) )
|
||||
{
|
||||
chunkname = OperationHelper.concat( EQ_STR, chunkname );
|
||||
}
|
||||
return BaseLib.loadStream( state, new StringInputStream( state, func ), chunkname );
|
||||
}
|
||||
case 1: // "loadstring", // ( string [,chunkname] ) -> chunk | nil, msg
|
||||
{
|
||||
LuaString script = args.arg( 1 ).checkLuaString();
|
||||
LuaString chunkname = args.arg( 2 ).optLuaString( script );
|
||||
if( !chunkname.startsWith( '@' ) && !chunkname.startsWith( '=' ) )
|
||||
{
|
||||
chunkname = OperationHelper.concat( EQ_STR, chunkname );
|
||||
}
|
||||
return BaseLib.loadStream( state, script.toInputStream(), chunkname );
|
||||
}
|
||||
}
|
||||
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
|
||||
private static class StringInputStream extends InputStream
|
||||
{
|
||||
private final LuaState state;
|
||||
private final LuaValue func;
|
||||
private byte[] bytes;
|
||||
private int offset, remaining = 0;
|
||||
|
||||
public StringInputStream( LuaState state, LuaValue func )
|
||||
{
|
||||
this.state = state;
|
||||
this.func = func;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException
|
||||
{
|
||||
if( remaining <= 0 )
|
||||
{
|
||||
LuaValue s;
|
||||
try
|
||||
{
|
||||
s = OperationHelper.call( state, func );
|
||||
} catch (LuaError e)
|
||||
{
|
||||
throw new IOException( e );
|
||||
}
|
||||
|
||||
if( s.isNil() )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
LuaString ls;
|
||||
try
|
||||
{
|
||||
ls = s.strvalue();
|
||||
} catch (LuaError e)
|
||||
{
|
||||
throw new IOException( e );
|
||||
}
|
||||
bytes = ls.bytes;
|
||||
offset = ls.offset;
|
||||
remaining = ls.length;
|
||||
if( remaining <= 0 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
--remaining;
|
||||
return bytes[offset++];
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,7 +6,9 @@
|
||||
|
||||
package dan200.computercraft.server.proxy;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.computer.blocks.TileComputer;
|
||||
import dan200.computercraft.shared.network.ComputerCraftPacket;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
|
||||
import dan200.computercraft.shared.peripheral.printer.TilePrinter;
|
||||
import dan200.computercraft.shared.proxy.ComputerCraftProxyCommon;
|
||||
@@ -64,11 +66,6 @@ public class ComputerCraftProxyServer extends ComputerCraftProxyCommon
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playRecord( SoundEvent record, String recordInfo, World world, BlockPos pos )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDiskDriveGUI( InventoryPlayer inventory, TileDiskDrive drive )
|
||||
|
@@ -0,0 +1,60 @@
|
||||
package dan200.computercraft.shared.command;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
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.server.MinecraftServer;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class CommandComputer extends CommandBase
|
||||
{
|
||||
@Override
|
||||
@Nonnull
|
||||
public String getName()
|
||||
{
|
||||
return "computer";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public String getUsage( @Nonnull ICommandSender sender )
|
||||
{
|
||||
return "computer <id> <value1> [value2]...";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute( @Nonnull MinecraftServer server, @Nonnull ICommandSender sender, @Nonnull String[] args ) throws CommandException
|
||||
{
|
||||
if( args.length < 2 )
|
||||
{
|
||||
throw new CommandException( "Usage: /computer <id> <value1> [value2]..." );
|
||||
}
|
||||
try
|
||||
{
|
||||
ServerComputer computer = ComputerCraft.serverComputerRegistry.lookup( Integer.valueOf( args[ 0 ] ) );
|
||||
if( computer != null && computer.getFamily() == ComputerFamily.Command )
|
||||
{
|
||||
computer.queueEvent( "computer_command", ArrayUtils.remove( args, 0 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new CommandException( "Computer #" + args[ 0 ] + " is not a Command Computer" );
|
||||
}
|
||||
}
|
||||
catch( NumberFormatException e )
|
||||
{
|
||||
throw new CommandException( "Invalid ID" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRequiredPermissionLevel()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
@@ -68,7 +68,7 @@ public abstract class BlockGeneric extends Block implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean removedByPlayer( @Nonnull IBlockState state, World world, @Nonnull BlockPos pos, @Nonnull EntityPlayer player, boolean willHarvest )
|
||||
public boolean removedByPlayer( @Nonnull IBlockState state, World world, @Nonnull BlockPos pos, @Nonnull EntityPlayer player, boolean willHarvest )
|
||||
{
|
||||
if( !world.isRemote )
|
||||
{
|
||||
@@ -111,18 +111,18 @@ public abstract class BlockGeneric extends Block implements
|
||||
public final void breakBlock( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState newState )
|
||||
{
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
super.breakBlock( world, pos, newState );
|
||||
world.removeTileEntity( pos );
|
||||
if( tile != null && tile instanceof TileGeneric )
|
||||
{
|
||||
TileGeneric generic = (TileGeneric)tile;
|
||||
generic.destroy();
|
||||
}
|
||||
super.breakBlock( world, pos, newState );
|
||||
world.removeTileEntity( pos );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public final ItemStack getPickBlock( @Nonnull IBlockState state, RayTraceResult target, @Nonnull World world, @Nonnull BlockPos pos, EntityPlayer player )
|
||||
public ItemStack getPickBlock( @Nonnull IBlockState state, RayTraceResult target, @Nonnull World world, @Nonnull BlockPos pos, EntityPlayer player )
|
||||
{
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
if( tile != null && tile instanceof TileGeneric )
|
||||
@@ -337,22 +337,6 @@ public abstract class BlockGeneric extends Block implements
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean eventReceived( IBlockState state, World world, BlockPos pos, int eventID, int eventParameter )
|
||||
{
|
||||
if( world.isRemote )
|
||||
{
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
if( tile != null && tile instanceof TileGeneric )
|
||||
{
|
||||
TileGeneric generic = (TileGeneric)tile;
|
||||
generic.onBlockEvent( eventID, eventParameter );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public final TileEntity createTileEntity( @Nonnull World world, @Nonnull IBlockState state )
|
||||
|
@@ -159,7 +159,7 @@ public abstract class TileGeneric extends TileEntity
|
||||
double range = getInteractRange( player );
|
||||
BlockPos pos = getPos();
|
||||
return player.getEntityWorld() == getWorld() &&
|
||||
player.getDistanceSq( (double)pos.getX() + 0.5, (double)pos.getY() + 0.5, (double)pos.getZ() + 0.5 ) <= ( range * range );
|
||||
player.getDistanceSq( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ) <= ( range * range );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -175,20 +175,6 @@ public abstract class TileGeneric extends TileEntity
|
||||
{
|
||||
}
|
||||
|
||||
public final void sendBlockEvent( int eventID )
|
||||
{
|
||||
sendBlockEvent( eventID, 0 );
|
||||
}
|
||||
|
||||
public final void sendBlockEvent( int eventID, int eventParameter )
|
||||
{
|
||||
getWorld().addBlockEvent( getPos(), getWorld().getBlockState( getPos() ).getBlock(), eventID, eventParameter );
|
||||
}
|
||||
|
||||
public void onBlockEvent( int eventID, int eventParameter )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRefresh( World world, BlockPos pos, @Nonnull IBlockState oldState, @Nonnull IBlockState newState )
|
||||
{
|
||||
|
@@ -7,10 +7,8 @@
|
||||
package dan200.computercraft.shared.computer.blocks;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
@@ -18,9 +16,9 @@ public class ComputerPeripheral
|
||||
implements IPeripheral
|
||||
{
|
||||
private final String m_type;
|
||||
private final ServerComputer m_computer;
|
||||
private final ComputerProxy m_computer;
|
||||
|
||||
public ComputerPeripheral( String type, ServerComputer computer )
|
||||
public ComputerPeripheral( String type, ComputerProxy computer )
|
||||
{
|
||||
m_type = type;
|
||||
m_computer = computer;
|
||||
@@ -50,7 +48,7 @@ public class ComputerPeripheral
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException
|
||||
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments )
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
|
@@ -0,0 +1,87 @@
|
||||
package dan200.computercraft.shared.computer.blocks;
|
||||
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
|
||||
/**
|
||||
* A proxy object for computer objects, delegating to {@link ServerComputer} or {@link TileComputer} where appropriate.
|
||||
*/
|
||||
public abstract class ComputerProxy
|
||||
{
|
||||
protected abstract TileComputerBase getTile();
|
||||
|
||||
public void turnOn()
|
||||
{
|
||||
TileComputerBase tile = getTile();
|
||||
ServerComputer computer = tile.getServerComputer();
|
||||
if( computer == null )
|
||||
{
|
||||
tile.m_startOn = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
computer.turnOn();
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown()
|
||||
{
|
||||
TileComputerBase tile = getTile();
|
||||
ServerComputer computer = tile.getServerComputer();
|
||||
if( computer == null )
|
||||
{
|
||||
tile.m_startOn = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
computer.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void reboot()
|
||||
{
|
||||
TileComputerBase tile = getTile();
|
||||
ServerComputer computer = tile.getServerComputer();
|
||||
if( computer == null )
|
||||
{
|
||||
tile.m_startOn = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
computer.reboot();
|
||||
}
|
||||
}
|
||||
|
||||
public int assignID()
|
||||
{
|
||||
TileComputerBase tile = getTile();
|
||||
ServerComputer computer = tile.getServerComputer();
|
||||
if( computer == null )
|
||||
{
|
||||
return tile.m_computerID;
|
||||
}
|
||||
else
|
||||
{
|
||||
return computer.getID();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isOn()
|
||||
{
|
||||
ServerComputer computer = getTile().getServerComputer();
|
||||
return computer != null && computer.isOn();
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
TileComputerBase tile = getTile();
|
||||
ServerComputer computer = tile.getServerComputer();
|
||||
if( computer == null )
|
||||
{
|
||||
return tile.m_label;
|
||||
}
|
||||
else
|
||||
{
|
||||
return computer.getLabel();
|
||||
}
|
||||
}
|
||||
}
|
@@ -19,14 +19,14 @@ import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.NonNullList;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
public class TileComputer extends TileComputerBase
|
||||
{
|
||||
// Statics
|
||||
|
||||
// Members
|
||||
|
||||
private ComputerProxy m_proxy;
|
||||
|
||||
public TileComputer()
|
||||
{
|
||||
}
|
||||
@@ -48,6 +48,23 @@ public class TileComputer extends TileComputerBase
|
||||
return computer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputerProxy createProxy()
|
||||
{
|
||||
if( m_proxy == null )
|
||||
{
|
||||
m_proxy = new ComputerProxy()
|
||||
{
|
||||
@Override
|
||||
protected TileComputerBase getTile()
|
||||
{
|
||||
return TileComputer.this;
|
||||
}
|
||||
};
|
||||
}
|
||||
return m_proxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDroppedItems( @Nonnull NonNullList<ItemStack> drops, boolean creative )
|
||||
{
|
||||
|
@@ -37,6 +37,7 @@ public abstract class TileComputerBase extends TileGeneric
|
||||
protected String m_label;
|
||||
protected boolean m_on;
|
||||
protected boolean m_startOn;
|
||||
protected boolean m_fresh;
|
||||
|
||||
protected TileComputerBase()
|
||||
{
|
||||
@@ -45,6 +46,7 @@ public abstract class TileComputerBase extends TileGeneric
|
||||
m_label = null;
|
||||
m_on = false;
|
||||
m_startOn = false;
|
||||
m_fresh = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -213,7 +215,7 @@ public abstract class TileComputerBase extends TileGeneric
|
||||
ServerComputer computer = createServerComputer();
|
||||
if( computer != null )
|
||||
{
|
||||
if( m_startOn )
|
||||
if( m_startOn || (m_fresh && m_on) )
|
||||
{
|
||||
computer.turnOn();
|
||||
m_startOn = false;
|
||||
@@ -223,6 +225,7 @@ public abstract class TileComputerBase extends TileGeneric
|
||||
{
|
||||
updateOutput();
|
||||
}
|
||||
m_fresh = false;
|
||||
m_computerID = computer.getID();
|
||||
m_label = computer.getLabel();
|
||||
m_on = computer.isOn();
|
||||
@@ -386,6 +389,8 @@ public abstract class TileComputerBase extends TileGeneric
|
||||
|
||||
protected abstract ServerComputer createComputer( int instanceID, int id );
|
||||
|
||||
public abstract ComputerProxy createProxy();
|
||||
|
||||
// ITerminalTile
|
||||
|
||||
@Override
|
||||
@@ -471,6 +476,7 @@ public abstract class TileComputerBase extends TileGeneric
|
||||
{
|
||||
ServerComputer computer = createComputer( m_instanceID, m_computerID );
|
||||
ComputerCraft.serverComputerRegistry.add( m_instanceID, computer );
|
||||
m_fresh = true;
|
||||
changed = true;
|
||||
}
|
||||
if( changed )
|
||||
|
@@ -153,11 +153,8 @@ public class ClientComputer extends ClientTerminal
|
||||
ComputerCraft.sendToServer( packet );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readDescription( NBTTagCompound nbttagcompound )
|
||||
private void readComputerDescription( NBTTagCompound nbttagcompound )
|
||||
{
|
||||
super.readDescription( nbttagcompound );
|
||||
|
||||
int oldID = m_computerID;
|
||||
String oldLabel = m_label;
|
||||
boolean oldOn = m_on;
|
||||
@@ -189,10 +186,11 @@ public class ClientComputer extends ClientTerminal
|
||||
switch( packet.m_packetType )
|
||||
{
|
||||
case ComputerCraftPacket.ComputerChanged:
|
||||
{
|
||||
readComputerDescription( packet.m_dataNBT );
|
||||
break;
|
||||
case ComputerCraftPacket.ComputerTerminalChanged:
|
||||
readDescription( packet.m_dataNBT );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -20,10 +20,13 @@ import dan200.computercraft.shared.network.ComputerCraftPacket;
|
||||
import dan200.computercraft.shared.network.INetworkedThing;
|
||||
import dan200.computercraft.shared.util.NBTUtil;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.inventory.Container;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
|
||||
import java.io.InputStream;
|
||||
@@ -36,6 +39,7 @@ public class ServerComputer extends ServerTerminal
|
||||
private World m_world;
|
||||
private BlockPos m_position;
|
||||
|
||||
private final ComputerFamily m_family;
|
||||
private final Computer m_computer;
|
||||
private NBTTagCompound m_userData;
|
||||
private boolean m_changed;
|
||||
@@ -51,6 +55,7 @@ public class ServerComputer extends ServerTerminal
|
||||
m_world = world;
|
||||
m_position = null;
|
||||
|
||||
m_family = family;
|
||||
m_computer = new Computer( this, getTerminal(), computerID );
|
||||
m_computer.setLabel( label );
|
||||
m_userData = null;
|
||||
@@ -60,6 +65,10 @@ public class ServerComputer extends ServerTerminal
|
||||
m_ticksSincePing = 0;
|
||||
}
|
||||
|
||||
public ComputerFamily getFamily(){
|
||||
return m_family;
|
||||
}
|
||||
|
||||
public World getWorld()
|
||||
{
|
||||
return m_world;
|
||||
@@ -131,27 +140,62 @@ public class ServerComputer extends ServerTerminal
|
||||
{
|
||||
m_changed = true;
|
||||
}
|
||||
|
||||
public void broadcastState()
|
||||
{
|
||||
// Send state to client
|
||||
|
||||
private ComputerCraftPacket createComputerPacket() {
|
||||
ComputerCraftPacket packet = new ComputerCraftPacket();
|
||||
packet.m_packetType = ComputerCraftPacket.ComputerChanged;
|
||||
packet.m_dataInt = new int[] { getInstanceID() };
|
||||
packet.m_dataNBT = new NBTTagCompound();
|
||||
writeDescription( packet.m_dataNBT );
|
||||
ComputerCraft.sendToAllPlayers( packet );
|
||||
writeComputerDescription( packet.m_dataNBT );
|
||||
return packet;
|
||||
}
|
||||
|
||||
public void sendState( EntityPlayer player )
|
||||
{
|
||||
// Send state to client
|
||||
private ComputerCraftPacket createTerminalPacket() {
|
||||
ComputerCraftPacket packet = new ComputerCraftPacket();
|
||||
packet.m_packetType = ComputerCraftPacket.ComputerChanged;
|
||||
packet.m_packetType = ComputerCraftPacket.ComputerTerminalChanged;
|
||||
packet.m_dataInt = new int[] { getInstanceID() };
|
||||
packet.m_dataNBT = new NBTTagCompound();
|
||||
writeDescription( packet.m_dataNBT );
|
||||
ComputerCraft.sendToPlayer( player, packet );
|
||||
return packet;
|
||||
}
|
||||
|
||||
public void broadcastState(boolean force)
|
||||
{
|
||||
if(hasOutputChanged() || force)
|
||||
{
|
||||
// Send computer state to all clients
|
||||
ComputerCraft.sendToAllPlayers( createComputerPacket() );
|
||||
}
|
||||
|
||||
if( hasTerminalChanged() || force )
|
||||
{
|
||||
// Send terminal state to clients who are currently interacting with the computer.
|
||||
FMLCommonHandler handler = FMLCommonHandler.instance();
|
||||
if( handler != null )
|
||||
{
|
||||
ComputerCraftPacket packet = createTerminalPacket();
|
||||
MinecraftServer server = handler.getMinecraftServerInstance();
|
||||
for( EntityPlayerMP player : server.getPlayerList().getPlayers() )
|
||||
{
|
||||
if( isInteracting( player ) )
|
||||
{
|
||||
ComputerCraft.sendToPlayer( player, packet );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sendComputerState( EntityPlayer player )
|
||||
{
|
||||
// Send state to client
|
||||
ComputerCraft.sendToPlayer( player, createComputerPacket() );
|
||||
}
|
||||
|
||||
public void sendTerminalState( EntityPlayer player )
|
||||
{
|
||||
// Send terminal state to client
|
||||
ComputerCraft.sendToPlayer( player, createTerminalPacket() );
|
||||
}
|
||||
|
||||
public void broadcastDelete()
|
||||
@@ -290,7 +334,7 @@ public class ServerComputer extends ServerTerminal
|
||||
@Override
|
||||
public double getTimeOfDay()
|
||||
{
|
||||
return (double)((m_world.getWorldTime() + 6000) % 24000) / 1000.0;
|
||||
return (m_world.getWorldTime() + 6000) % 24000 / 1000.0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -336,12 +380,8 @@ public class ServerComputer extends ServerTerminal
|
||||
}
|
||||
|
||||
// Networking stuff
|
||||
|
||||
@Override
|
||||
public void writeDescription( NBTTagCompound nbttagcompound )
|
||||
public void writeComputerDescription( NBTTagCompound nbttagcompound )
|
||||
{
|
||||
super.writeDescription( nbttagcompound );
|
||||
|
||||
nbttagcompound.setInteger( "id", m_computer.getID() );
|
||||
String label = m_computer.getLabel();
|
||||
if( label != null )
|
||||
@@ -362,14 +402,9 @@ public class ServerComputer extends ServerTerminal
|
||||
public void handlePacket( ComputerCraftPacket packet, EntityPlayer sender )
|
||||
{
|
||||
// Allow Computer/Tile updates as they may happen at any time.
|
||||
if (packet.requiresContainer()) {
|
||||
if (sender == null) return;
|
||||
|
||||
Container container = sender.openContainer;
|
||||
if (!(container instanceof IContainerComputer)) return;
|
||||
|
||||
IComputer computer = ((IContainerComputer) container).getComputer();
|
||||
if (computer != this) return;
|
||||
if( packet.requiresContainer() && !isInteracting( sender ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Receive packets sent from the client to the server
|
||||
@@ -415,9 +450,20 @@ public class ServerComputer extends ServerTerminal
|
||||
case ComputerCraftPacket.RequestComputerUpdate:
|
||||
{
|
||||
// A player asked for an update on the state of the terminal
|
||||
sendState( sender );
|
||||
sendComputerState( sender );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInteracting( EntityPlayer player )
|
||||
{
|
||||
if( player == null ) return false;
|
||||
|
||||
Container container = player.openContainer;
|
||||
if( !(container instanceof IContainerComputer) ) return false;
|
||||
|
||||
IComputer computer = ((IContainerComputer) container).getComputer();
|
||||
return computer == this;
|
||||
}
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ public class ServerComputerRegistry extends ComputerRegistry<ServerComputer>
|
||||
computer.update();
|
||||
if( computer.hasTerminalChanged() || computer.hasOutputChanged() )
|
||||
{
|
||||
computer.broadcastState();
|
||||
computer.broadcastState(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ public class ServerComputerRegistry extends ComputerRegistry<ServerComputer>
|
||||
{
|
||||
//System.out.println( "ADD SERVER COMPUTER " + instanceID );
|
||||
super.add( instanceID, computer );
|
||||
computer.broadcastState();
|
||||
computer.broadcastState(true);
|
||||
//System.out.println( getComputers().size() + " SERVER COMPUTERS" );
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,99 @@
|
||||
package dan200.computercraft.shared.integration;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.shared.media.items.ItemDiskLegacy;
|
||||
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
||||
import dan200.computercraft.shared.turtle.items.ITurtleItem;
|
||||
import mezz.jei.api.IModPlugin;
|
||||
import mezz.jei.api.IModRegistry;
|
||||
import mezz.jei.api.ISubtypeRegistry;
|
||||
import mezz.jei.api.ISubtypeRegistry.ISubtypeInterpreter;
|
||||
import mezz.jei.api.JEIPlugin;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
@JEIPlugin
|
||||
public class JEIComputerCraft implements IModPlugin
|
||||
{
|
||||
@Override
|
||||
public void registerItemSubtypes( ISubtypeRegistry subtypeRegistry )
|
||||
{
|
||||
subtypeRegistry.registerSubtypeInterpreter( Item.getItemFromBlock( ComputerCraft.Blocks.turtle ), turtleSubtype );
|
||||
subtypeRegistry.registerSubtypeInterpreter( Item.getItemFromBlock( ComputerCraft.Blocks.turtleExpanded ), turtleSubtype );
|
||||
subtypeRegistry.registerSubtypeInterpreter( Item.getItemFromBlock( ComputerCraft.Blocks.turtleAdvanced ), turtleSubtype );
|
||||
|
||||
subtypeRegistry.registerSubtypeInterpreter( ComputerCraft.Items.pocketComputer, pocketSubtype );
|
||||
|
||||
subtypeRegistry.registerSubtypeInterpreter( ComputerCraft.Items.disk, diskSubtype );
|
||||
subtypeRegistry.registerSubtypeInterpreter( ComputerCraft.Items.diskExpanded, diskSubtype );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register( IModRegistry registry )
|
||||
{
|
||||
// Hide treasure disks from the ingredient list
|
||||
registry.getJeiHelpers().getIngredientBlacklist()
|
||||
.addIngredientToBlacklist( new ItemStack( ComputerCraft.Items.treasureDisk, OreDictionary.WILDCARD_VALUE ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Distinguishes turtles by upgrades and family
|
||||
*/
|
||||
private static final ISubtypeInterpreter turtleSubtype = stack -> {
|
||||
Item item = stack.getItem();
|
||||
if( !(item instanceof ITurtleItem) ) return "";
|
||||
|
||||
ITurtleItem turtle = (ITurtleItem) item;
|
||||
StringBuilder name = new StringBuilder();
|
||||
|
||||
name.append( turtle.getFamily( stack ).toString() );
|
||||
|
||||
// Add left and right upgrades to the identifier
|
||||
ITurtleUpgrade left = turtle.getUpgrade( stack, TurtleSide.Left );
|
||||
name.append( '|' );
|
||||
if( left != null ) name.append( left.getUpgradeID() );
|
||||
|
||||
ITurtleUpgrade right = turtle.getUpgrade( stack, TurtleSide.Right );
|
||||
name.append( '|' );
|
||||
if( right != null ) name.append( '|' ).append( right.getUpgradeID() );
|
||||
|
||||
return name.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Distinguishes pocket computers by upgrade and family
|
||||
*/
|
||||
private static final ISubtypeInterpreter pocketSubtype = stack -> {
|
||||
Item item = stack.getItem();
|
||||
if( !(item instanceof ItemPocketComputer) ) return "";
|
||||
|
||||
ItemPocketComputer pocket = (ItemPocketComputer) item;
|
||||
StringBuilder name = new StringBuilder();
|
||||
|
||||
name.append( pocket.getFamily( stack ).toString() );
|
||||
|
||||
// Add the upgrade to the identifier
|
||||
IPocketUpgrade upgrade = pocket.getUpgrade( stack );
|
||||
name.append( '|' );
|
||||
if( upgrade != null ) name.append( upgrade.getUpgradeID() );
|
||||
|
||||
return name.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Distinguishes disks by colour
|
||||
*/
|
||||
private static final ISubtypeInterpreter diskSubtype = stack -> {
|
||||
Item item = stack.getItem();
|
||||
if( !(item instanceof ItemDiskLegacy) ) return "";
|
||||
|
||||
ItemDiskLegacy disk = (ItemDiskLegacy) item;
|
||||
|
||||
int colour = disk.getColour( stack );
|
||||
return colour == -1 ? "" : String.format( "%06x", colour );
|
||||
};
|
||||
}
|
@@ -30,7 +30,9 @@ public class ComputerCraftPacket
|
||||
|
||||
// To client
|
||||
public static final byte ComputerChanged = 7;
|
||||
public static final byte ComputerDeleted = 8;
|
||||
public static final byte ComputerTerminalChanged = 8;
|
||||
public static final byte ComputerDeleted = 9;
|
||||
public static final byte PlayRecord = 10;
|
||||
|
||||
// Packet class
|
||||
public byte m_packetType;
|
||||
|
@@ -7,20 +7,33 @@
|
||||
package dan200.computercraft.shared.peripheral.common;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.common.TileGeneric;
|
||||
import dan200.computercraft.shared.peripheral.PeripheralType;
|
||||
import dan200.computercraft.shared.peripheral.modem.TileCable;
|
||||
import dan200.computercraft.shared.util.WorldUtil;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.properties.PropertyBool;
|
||||
import net.minecraft.block.properties.PropertyEnum;
|
||||
import net.minecraft.block.state.BlockFaceShape;
|
||||
import net.minecraft.block.state.BlockStateContainer;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BlockCable extends BlockPeripheralBase
|
||||
{
|
||||
@@ -29,7 +42,7 @@ public class BlockCable extends BlockPeripheralBase
|
||||
public static class Properties
|
||||
{
|
||||
public static final PropertyEnum<BlockCableModemVariant> MODEM = PropertyEnum.create( "modem", BlockCableModemVariant.class );
|
||||
public static final PropertyBool CABLE = PropertyBool.create( "cable" );
|
||||
public static final PropertyEnum<BlockCableCableVariant> CABLE = PropertyEnum.create( "cable", BlockCableCableVariant.class );
|
||||
public static final PropertyBool NORTH = PropertyBool.create( "north" );
|
||||
public static final PropertyBool SOUTH = PropertyBool.create( "south" );
|
||||
public static final PropertyBool EAST = PropertyBool.create( "east" );
|
||||
@@ -64,7 +77,7 @@ public class BlockCable extends BlockPeripheralBase
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
setDefaultState( this.blockState.getBaseState()
|
||||
.withProperty( Properties.MODEM, BlockCableModemVariant.None )
|
||||
.withProperty( Properties.CABLE, true )
|
||||
.withProperty( Properties.CABLE, BlockCableCableVariant.NONE )
|
||||
.withProperty( Properties.NORTH, false )
|
||||
.withProperty( Properties.SOUTH, false )
|
||||
.withProperty( Properties.EAST, false )
|
||||
@@ -98,17 +111,17 @@ public class BlockCable extends BlockPeripheralBase
|
||||
IBlockState state = getDefaultState();
|
||||
if( meta < 6 )
|
||||
{
|
||||
state = state.withProperty( Properties.CABLE, false );
|
||||
state = state.withProperty( Properties.CABLE, BlockCableCableVariant.NONE );
|
||||
state = state.withProperty( Properties.MODEM, BlockCableModemVariant.fromFacing( EnumFacing.getFront( meta ) ) );
|
||||
}
|
||||
else if( meta < 12 )
|
||||
{
|
||||
state = state.withProperty( Properties.CABLE, true );
|
||||
state = state.withProperty( Properties.CABLE, BlockCableCableVariant.ANY );
|
||||
state = state.withProperty( Properties.MODEM, BlockCableModemVariant.fromFacing( EnumFacing.getFront( meta - 6 ) ) );
|
||||
}
|
||||
else if( meta == 13 )
|
||||
{
|
||||
state = state.withProperty( Properties.CABLE, true );
|
||||
state = state.withProperty( Properties.CABLE, BlockCableCableVariant.ANY );
|
||||
state = state.withProperty( Properties.MODEM, BlockCableModemVariant.None );
|
||||
}
|
||||
return state;
|
||||
@@ -118,7 +131,7 @@ public class BlockCable extends BlockPeripheralBase
|
||||
public int getMetaFromState( IBlockState state )
|
||||
{
|
||||
int meta = 0;
|
||||
boolean cable = state.getValue( Properties.CABLE );
|
||||
boolean cable = state.getValue( Properties.CABLE ) != BlockCableCableVariant.NONE;
|
||||
BlockCableModemVariant modem = state.getValue( Properties.MODEM );
|
||||
if( cable && modem != BlockCableModemVariant.None )
|
||||
{
|
||||
@@ -143,20 +156,20 @@ public class BlockCable extends BlockPeripheralBase
|
||||
case Cable:
|
||||
{
|
||||
return getDefaultState()
|
||||
.withProperty( Properties.CABLE, true )
|
||||
.withProperty( Properties.CABLE, BlockCableCableVariant.ANY )
|
||||
.withProperty( Properties.MODEM, BlockCableModemVariant.None );
|
||||
}
|
||||
case WiredModem:
|
||||
default:
|
||||
{
|
||||
return getDefaultState()
|
||||
.withProperty( Properties.CABLE, false )
|
||||
.withProperty( Properties.CABLE, BlockCableCableVariant.NONE )
|
||||
.withProperty( Properties.MODEM, BlockCableModemVariant.fromFacing( placedSide.getOpposite() ) );
|
||||
}
|
||||
case WiredModemWithCable:
|
||||
{
|
||||
return getDefaultState()
|
||||
.withProperty( Properties.CABLE, true )
|
||||
.withProperty( Properties.CABLE, BlockCableCableVariant.ANY )
|
||||
.withProperty( Properties.MODEM, BlockCableModemVariant.fromFacing( placedSide.getOpposite() ) );
|
||||
}
|
||||
}
|
||||
@@ -164,7 +177,7 @@ public class BlockCable extends BlockPeripheralBase
|
||||
|
||||
private boolean doesConnect( IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing dir )
|
||||
{
|
||||
if( !state.getValue( Properties.CABLE ) )
|
||||
if( state.getValue( Properties.CABLE ) == BlockCableCableVariant.NONE )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -190,11 +203,30 @@ public class BlockCable extends BlockPeripheralBase
|
||||
state = state.withProperty( Properties.UP, doesConnect( state, world, pos, EnumFacing.UP ) );
|
||||
state = state.withProperty( Properties.DOWN, doesConnect( state, world, pos, EnumFacing.DOWN ) );
|
||||
|
||||
if( state.getValue( Properties.CABLE ) != BlockCableCableVariant.NONE )
|
||||
{
|
||||
BlockCableCableVariant direction = null;
|
||||
if( state.getValue( Properties.WEST ) || state.getValue( Properties.EAST ) )
|
||||
{
|
||||
direction = direction == null ? BlockCableCableVariant.X_AXIS : BlockCableCableVariant.ANY;
|
||||
}
|
||||
if( state.getValue( Properties.DOWN ) || state.getValue( Properties.UP ) )
|
||||
{
|
||||
direction = direction == null ? BlockCableCableVariant.Y_AXIS : BlockCableCableVariant.ANY;
|
||||
}
|
||||
if( state.getValue( Properties.NORTH ) || state.getValue( Properties.SOUTH ) )
|
||||
{
|
||||
direction = direction == null ? BlockCableCableVariant.Z_AXIS : BlockCableCableVariant.ANY;
|
||||
}
|
||||
|
||||
state = state.withProperty( Properties.CABLE, direction == null ? BlockCableCableVariant.Z_AXIS : direction );
|
||||
}
|
||||
|
||||
int anim;
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
if( tile != null && tile instanceof TilePeripheralBase )
|
||||
{
|
||||
TilePeripheralBase peripheral = (TilePeripheralBase)tile;
|
||||
TilePeripheralBase peripheral = (TilePeripheralBase) tile;
|
||||
anim = peripheral.getAnim();
|
||||
}
|
||||
else
|
||||
@@ -207,7 +239,7 @@ public class BlockCable extends BlockPeripheralBase
|
||||
{
|
||||
modem = BlockCableModemVariant.values()[
|
||||
1 + 6 * anim + modem.getFacing().getIndex()
|
||||
];
|
||||
];
|
||||
}
|
||||
state = state.withProperty( Properties.MODEM, modem );
|
||||
|
||||
@@ -230,7 +262,7 @@ public class BlockCable extends BlockPeripheralBase
|
||||
@Override
|
||||
public PeripheralType getPeripheralType( IBlockState state )
|
||||
{
|
||||
boolean cable = state.getValue( Properties.CABLE );
|
||||
boolean cable = state.getValue( Properties.CABLE ) != BlockCableCableVariant.NONE;
|
||||
BlockCableModemVariant modem = state.getValue( Properties.MODEM );
|
||||
if( cable && modem != BlockCableModemVariant.None )
|
||||
{
|
||||
@@ -251,4 +283,152 @@ public class BlockCable extends BlockPeripheralBase
|
||||
{
|
||||
return new TileCable();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Deprecated
|
||||
public RayTraceResult collisionRayTrace( IBlockState blockState, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Vec3d start, @Nonnull Vec3d end )
|
||||
{
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
if( tile != null && tile instanceof TileGeneric && tile.hasWorld() )
|
||||
{
|
||||
TileGeneric generic = (TileGeneric) tile;
|
||||
|
||||
double distance = Double.POSITIVE_INFINITY;
|
||||
RayTraceResult result = null;
|
||||
|
||||
List<AxisAlignedBB> bounds = new ArrayList<AxisAlignedBB>( 7 );
|
||||
generic.getCollisionBounds( bounds );
|
||||
|
||||
Vec3d startOff = start.subtract( pos.getX(), pos.getY(), pos.getZ() );
|
||||
Vec3d endOff = end.subtract( pos.getX(), pos.getY(), pos.getZ() );
|
||||
|
||||
for( AxisAlignedBB bb : bounds )
|
||||
{
|
||||
RayTraceResult hit = bb.calculateIntercept( startOff, endOff );
|
||||
if( hit != null )
|
||||
{
|
||||
double newDistance = hit.hitVec.squareDistanceTo( startOff );
|
||||
if( newDistance <= distance )
|
||||
{
|
||||
distance = newDistance;
|
||||
result = hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result == null ? null : new RayTraceResult( result.hitVec.addVector( pos.getX(), pos.getY(), pos.getZ() ), result.sideHit, pos );
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.collisionRayTrace( blockState, world, pos, start, end );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removedByPlayer( @Nonnull IBlockState state, World world, @Nonnull BlockPos pos, @Nonnull EntityPlayer player, boolean willHarvest )
|
||||
{
|
||||
PeripheralType type = getPeripheralType( world, pos );
|
||||
if( type == PeripheralType.WiredModemWithCable )
|
||||
{
|
||||
RayTraceResult hit = state.collisionRayTrace( world, pos, WorldUtil.getRayStart( player ), WorldUtil.getRayEnd( player ) );
|
||||
if( hit != null )
|
||||
{
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
if( tile != null && tile instanceof TileCable && tile.hasWorld() )
|
||||
{
|
||||
TileCable cable = (TileCable) tile;
|
||||
|
||||
ItemStack item;
|
||||
|
||||
AxisAlignedBB bb = cable.getModemBounds();
|
||||
if( WorldUtil.isVecInsideInclusive( bb, hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) ) )
|
||||
{
|
||||
world.setBlockState( pos, state.withProperty( Properties.MODEM, BlockCableModemVariant.None ), 3 );
|
||||
cable.modemChanged();
|
||||
item = PeripheralItemFactory.create( PeripheralType.WiredModem, null, 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
world.setBlockState( pos, state.withProperty( Properties.CABLE, BlockCableCableVariant.NONE ), 3 );
|
||||
item = PeripheralItemFactory.create( PeripheralType.Cable, null, 1 );
|
||||
}
|
||||
|
||||
cable.networkChanged();
|
||||
if( !world.isRemote && !player.capabilities.isCreativeMode ) dropItem( world, pos, item );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.removedByPlayer( state, world, pos, player, willHarvest );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getPickBlock( @Nonnull IBlockState state, RayTraceResult hit, @Nonnull World world, @Nonnull BlockPos pos, EntityPlayer player )
|
||||
{
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
if( tile != null && tile instanceof TileCable && tile.hasWorld() )
|
||||
{
|
||||
TileCable cable = (TileCable) tile;
|
||||
PeripheralType type = getPeripheralType( state );
|
||||
|
||||
if( type == PeripheralType.WiredModemWithCable )
|
||||
{
|
||||
if( hit == null || WorldUtil.isVecInsideInclusive( cable.getModemBounds(), hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) ) )
|
||||
{
|
||||
return PeripheralItemFactory.create( PeripheralType.WiredModem, null, 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
return PeripheralItemFactory.create( PeripheralType.Cable, null, 1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return PeripheralItemFactory.create( type, null, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
return PeripheralItemFactory.create( PeripheralType.Cable, null, 1 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockPlacedBy( World world, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack )
|
||||
{
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
if( tile != null && tile instanceof TileCable )
|
||||
{
|
||||
TileCable cable = (TileCable) tile;
|
||||
if( cable.getPeripheralType() != PeripheralType.WiredModem )
|
||||
{
|
||||
cable.networkChanged();
|
||||
}
|
||||
}
|
||||
|
||||
super.onBlockPlacedBy( world, pos, state, placer, stack );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final boolean isOpaqueCube( IBlockState state )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final boolean isFullCube( IBlockState state )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@Deprecated
|
||||
public BlockFaceShape getBlockFaceShape( IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing side )
|
||||
{
|
||||
return BlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,28 @@
|
||||
package dan200.computercraft.shared.peripheral.common;
|
||||
|
||||
import net.minecraft.util.IStringSerializable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public enum BlockCableCableVariant implements IStringSerializable
|
||||
{
|
||||
NONE( "none" ),
|
||||
ANY( "any" ),
|
||||
X_AXIS( "x" ),
|
||||
Y_AXIS( "y" ),
|
||||
Z_AXIS( "z" ),;
|
||||
|
||||
private final String m_name;
|
||||
|
||||
BlockCableCableVariant( String name )
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public String getName()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
}
|
@@ -16,6 +16,7 @@ import dan200.computercraft.shared.peripheral.speaker.TileSpeaker;
|
||||
import dan200.computercraft.shared.util.DirectionUtil;
|
||||
import net.minecraft.block.properties.PropertyDirection;
|
||||
import net.minecraft.block.properties.PropertyEnum;
|
||||
import net.minecraft.block.state.BlockFaceShape;
|
||||
import net.minecraft.block.state.BlockStateContainer;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
@@ -612,4 +613,29 @@ public class BlockPeripheral extends BlockPeripheralBase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final boolean isOpaqueCube( IBlockState state )
|
||||
{
|
||||
PeripheralType type = getPeripheralType( state );
|
||||
return type == PeripheralType.DiskDrive || type == PeripheralType.Printer
|
||||
|| type == PeripheralType.Monitor || type == PeripheralType.AdvancedMonitor
|
||||
|| type == PeripheralType.Speaker;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final boolean isFullCube( IBlockState state )
|
||||
{
|
||||
return isOpaqueCube( state );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@Deprecated
|
||||
public BlockFaceShape getBlockFaceShape( IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing side )
|
||||
{
|
||||
return isOpaqueCube( state ) ? BlockFaceShape.SOLID : BlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
@@ -31,20 +31,6 @@ public abstract class BlockPeripheralBase extends BlockDirectional
|
||||
protected abstract PeripheralType getPeripheralType( IBlockState state );
|
||||
protected abstract TilePeripheralBase createTile( PeripheralType type );
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final boolean isOpaqueCube( IBlockState state )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final boolean isFullCube( IBlockState state )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean canPlaceBlockOnSide( @Nonnull World world, @Nonnull BlockPos pos, EnumFacing side )
|
||||
{
|
||||
|
@@ -45,12 +45,12 @@ public class DefaultPeripheralProvider implements IPeripheralProvider
|
||||
{
|
||||
if( !((TileTurtle)tile).hasMoved() )
|
||||
{
|
||||
return new ComputerPeripheral( "turtle", computerTile.createServerComputer() );
|
||||
return new ComputerPeripheral( "turtle", computerTile.createProxy() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ComputerPeripheral( "computer", computerTile.createServerComputer() );
|
||||
return new ComputerPeripheral( "computer", computerTile.createProxy() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -89,12 +89,12 @@ public class ItemCable extends ItemPeripheralBase
|
||||
{
|
||||
if( !stack.isEmpty() )
|
||||
{
|
||||
IBlockState newState = existingState.withProperty( BlockCable.Properties.CABLE, true );
|
||||
IBlockState newState = existingState.withProperty( BlockCable.Properties.CABLE, BlockCableCableVariant.ANY );
|
||||
world.setBlockState( pos, newState, 3 );
|
||||
SoundType soundType = newState.getBlock().getSoundType( newState, world, pos, player );
|
||||
world.playSound( null, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, soundType.getPlaceSound(), SoundCategory.BLOCKS, (soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F );
|
||||
stack.shrink( 1 );
|
||||
|
||||
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
if( tile != null && tile instanceof TileCable )
|
||||
{
|
||||
@@ -143,7 +143,7 @@ public class ItemCable extends ItemPeripheralBase
|
||||
{
|
||||
if( !stack.isEmpty() )
|
||||
{
|
||||
IBlockState newState = offsetExistingState.withProperty( BlockCable.Properties.CABLE, true );
|
||||
IBlockState newState = offsetExistingState.withProperty( BlockCable.Properties.CABLE, BlockCableCableVariant.ANY );
|
||||
world.setBlockState( offset, newState, 3 );
|
||||
SoundType soundType = newState.getBlock().getSoundType( newState, world, offset, player );
|
||||
world.playSound( null, offset.getX() + 0.5, offset.getY() + 0.5, offset.getZ() + 0.5, soundType.getPlaceSound(), SoundCategory.BLOCKS, (soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F );
|
||||
|
@@ -7,7 +7,6 @@
|
||||
package dan200.computercraft.shared.peripheral.common;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.common.IDirectionalTile;
|
||||
import dan200.computercraft.shared.common.TileGeneric;
|
||||
import dan200.computercraft.shared.peripheral.PeripheralType;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -20,7 +19,7 @@ import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class TilePeripheralBase extends TileGeneric
|
||||
implements IPeripheralTile, IDirectionalTile, ITickable
|
||||
implements IPeripheralTile, ITickable
|
||||
{
|
||||
// Statics
|
||||
|
||||
|
@@ -24,7 +24,6 @@ import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.ITickable;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
@@ -44,13 +43,8 @@ import java.util.Set;
|
||||
import static net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY;
|
||||
|
||||
public class TileDiskDrive extends TilePeripheralBase
|
||||
implements IInventory, ITickable
|
||||
implements IInventory
|
||||
{
|
||||
// Statics
|
||||
|
||||
private static final int BLOCKEVENT_PLAY_RECORD = 0;
|
||||
private static final int BLOCKEVENT_STOP_RECORD = 1;
|
||||
|
||||
private static class MountInfo
|
||||
{
|
||||
public String mountPath;
|
||||
@@ -90,7 +84,7 @@ public class TileDiskDrive extends TilePeripheralBase
|
||||
{
|
||||
if( m_recordPlaying )
|
||||
{
|
||||
sendBlockEvent( BLOCKEVENT_STOP_RECORD );
|
||||
stopRecord();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,7 +182,7 @@ public class TileDiskDrive extends TilePeripheralBase
|
||||
// Music
|
||||
synchronized( this )
|
||||
{
|
||||
if( m_recordPlaying != m_recordQueued || m_restartRecord )
|
||||
if( !world.isRemote && m_recordPlaying != m_recordQueued || m_restartRecord )
|
||||
{
|
||||
m_restartRecord = false;
|
||||
if( m_recordQueued )
|
||||
@@ -198,7 +192,7 @@ public class TileDiskDrive extends TilePeripheralBase
|
||||
if( record != null )
|
||||
{
|
||||
m_recordPlaying = true;
|
||||
sendBlockEvent( BLOCKEVENT_PLAY_RECORD );
|
||||
playRecord();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -207,7 +201,7 @@ public class TileDiskDrive extends TilePeripheralBase
|
||||
}
|
||||
else
|
||||
{
|
||||
sendBlockEvent( BLOCKEVENT_STOP_RECORD );
|
||||
stopRecord();
|
||||
m_recordPlaying = false;
|
||||
}
|
||||
}
|
||||
@@ -306,7 +300,7 @@ public class TileDiskDrive extends TilePeripheralBase
|
||||
// Stop music
|
||||
if( m_recordPlaying )
|
||||
{
|
||||
sendBlockEvent( BLOCKEVENT_STOP_RECORD );
|
||||
stopRecord();
|
||||
m_recordPlaying = false;
|
||||
m_recordQueued = false;
|
||||
}
|
||||
@@ -615,13 +609,13 @@ public class TileDiskDrive extends TilePeripheralBase
|
||||
}
|
||||
|
||||
BlockPos pos = getPos();
|
||||
double x = (double)pos.getX() + 0.5 + ((double)xOff * 0.5);
|
||||
double y = (double)pos.getY() + 0.75;
|
||||
double z = (double)pos.getZ() + 0.5 + ((double)zOff * 0.5);
|
||||
double x = pos.getX() + 0.5 + (xOff * 0.5);
|
||||
double y = pos.getY() + 0.75;
|
||||
double z = pos.getZ() + 0.5 + (zOff * 0.5);
|
||||
EntityItem entityitem = new EntityItem( getWorld(), x, y, z, disks );
|
||||
entityitem.motionX = (double)xOff * 0.15;
|
||||
entityitem.motionX = xOff * 0.15;
|
||||
entityitem.motionY = 0.0;
|
||||
entityitem.motionZ = (double)zOff * 0.15;
|
||||
entityitem.motionZ = zOff * 0.15;
|
||||
|
||||
getWorld().spawnEntity(entityitem);
|
||||
if( !destroyed )
|
||||
@@ -658,25 +652,6 @@ public class TileDiskDrive extends TilePeripheralBase
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockEvent( int eventID, int eventParameter )
|
||||
{
|
||||
super.onBlockEvent( eventID, eventParameter );
|
||||
switch( eventID )
|
||||
{
|
||||
case BLOCKEVENT_PLAY_RECORD:
|
||||
{
|
||||
playRecord();
|
||||
break;
|
||||
}
|
||||
case BLOCKEVENT_STOP_RECORD:
|
||||
{
|
||||
stopRecord();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRefresh( World world, BlockPos pos, @Nonnull IBlockState oldState, @Nonnull IBlockState newState )
|
||||
{
|
||||
|
@@ -12,11 +12,12 @@ import dan200.computercraft.shared.peripheral.common.BlockPeripheralBase;
|
||||
import dan200.computercraft.shared.peripheral.common.TilePeripheralBase;
|
||||
import net.minecraft.block.properties.PropertyBool;
|
||||
import net.minecraft.block.properties.PropertyDirection;
|
||||
import net.minecraft.block.state.BlockFaceShape;
|
||||
import net.minecraft.block.state.BlockStateContainer;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -114,4 +115,26 @@ public class BlockAdvancedModem extends BlockPeripheralBase
|
||||
{
|
||||
return new TileAdvancedModem();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final boolean isOpaqueCube( IBlockState state )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final boolean isFullCube( IBlockState state )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@Deprecated
|
||||
public BlockFaceShape getBlockFaceShape( IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing side )
|
||||
{
|
||||
return BlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
@@ -41,7 +41,7 @@ public class TileAdvancedModem extends TileModemBase
|
||||
public Vec3d getPosition()
|
||||
{
|
||||
BlockPos pos = m_entity.getPos().offset( m_entity.getDirection() );
|
||||
return new Vec3d( (double)pos.getX(), (double)pos.getY(), (double)pos.getZ() );
|
||||
return new Vec3d( pos.getX(), pos.getY(), pos.getZ() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -46,8 +46,8 @@ import static dan200.computercraft.core.apis.ArgumentHelper.getString;
|
||||
public class TileCable extends TileModemBase
|
||||
implements IPacketNetwork
|
||||
{
|
||||
private static final double MIN = 0.375;
|
||||
private static final double MAX = 1 - MIN;
|
||||
public static final double MIN = 0.375;
|
||||
public static final double MAX = 1 - MIN;
|
||||
|
||||
private static final AxisAlignedBB BOX_CENTRE = new AxisAlignedBB( MIN, MIN, MIN, MAX, MAX, MAX );
|
||||
private static final AxisAlignedBB[] BOXES = new AxisAlignedBB[]{
|
||||
@@ -101,7 +101,7 @@ public class TileCable extends TileModemBase
|
||||
{
|
||||
EnumFacing direction = m_entity.getDirection();
|
||||
BlockPos pos = m_entity.getPos().offset( direction );
|
||||
return new Vec3d( (double)pos.getX() + 0.5, (double)pos.getY() + 0.5, (double)pos.getZ() + 0.5 );
|
||||
return new Vec3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -359,6 +359,7 @@ public class TileCable extends TileModemBase
|
||||
((BlockGeneric)getBlockType()).dropItem( getWorld(), getPos(), PeripheralItemFactory.create( PeripheralType.WiredModem, getLabel(), 1 ) );
|
||||
setLabel( null );
|
||||
setBlockState( getBlockState().withProperty( BlockCable.Properties.MODEM, BlockCableModemVariant.None ) );
|
||||
if( modemChanged() ) networkChanged();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -682,7 +683,7 @@ public class TileCable extends TileModemBase
|
||||
{
|
||||
if( !getWorld().isRemote )
|
||||
{
|
||||
if( !m_destroyed )
|
||||
if( !m_destroyed && getPeripheralType() != PeripheralType.WiredModem)
|
||||
{
|
||||
// If this modem is alive, rebuild the network
|
||||
searchNetwork( ( modem, distance ) ->
|
||||
@@ -712,6 +713,29 @@ public class TileCable extends TileModemBase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean modemChanged()
|
||||
{
|
||||
if( getWorld().isRemote ) return false;
|
||||
|
||||
boolean requiresUpdate = false;
|
||||
|
||||
PeripheralType type = getPeripheralType();
|
||||
if( type == PeripheralType.Cable )
|
||||
{
|
||||
m_attachedPeripheralID = -1;
|
||||
}
|
||||
|
||||
if( type != PeripheralType.WiredModemWithCable && m_peripheralAccessAllowed )
|
||||
{
|
||||
m_peripheralAccessAllowed = false;
|
||||
requiresUpdate = true;
|
||||
markDirty();
|
||||
updateAnim();
|
||||
}
|
||||
|
||||
return requiresUpdate;
|
||||
}
|
||||
|
||||
// private stuff
|
||||
|
||||
@@ -1032,13 +1056,19 @@ public class TileCable extends TileModemBase
|
||||
Queue<SearchLoc> queue = new LinkedList<>();
|
||||
enqueue( queue, getWorld(), getPos(), 1 );
|
||||
|
||||
int visited = 0;
|
||||
//int visited = 0;
|
||||
while( queue.peek() != null )
|
||||
{
|
||||
SearchLoc loc = queue.remove();
|
||||
visitBlock( queue, loc, searchID, visitor );
|
||||
visited++;
|
||||
//visited++;
|
||||
}
|
||||
//System.out.println( "Visited "+visited+" common" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRenderBreaking()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ public class TileWirelessModem extends TileModemBase
|
||||
public Vec3d getPosition()
|
||||
{
|
||||
BlockPos pos = m_entity.getPos().offset( m_entity.getDirection() );
|
||||
return new Vec3d( (double)pos.getX(), (double)pos.getY(), (double)pos.getZ() );
|
||||
return new Vec3d( pos.getX(), pos.getY(), pos.getZ() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -31,7 +31,7 @@ public abstract class WirelessModemPeripheral extends ModemPeripheral
|
||||
{
|
||||
if( m_advanced )
|
||||
{
|
||||
return (double)Integer.MAX_VALUE;
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -39,12 +39,12 @@ public abstract class WirelessModemPeripheral extends ModemPeripheral
|
||||
if( world != null )
|
||||
{
|
||||
Vec3d position = getPosition();
|
||||
double minRange = (double) ComputerCraft.modem_range;
|
||||
double maxRange = (double) ComputerCraft.modem_highAltitudeRange;
|
||||
double minRange = ComputerCraft.modem_range;
|
||||
double maxRange = ComputerCraft.modem_highAltitudeRange;
|
||||
if( world.isRaining() && world.isThundering() )
|
||||
{
|
||||
minRange = (double) ComputerCraft.modem_rangeDuringStorm;
|
||||
maxRange = (double) ComputerCraft.modem_highAltitudeRangeDuringStorm;
|
||||
minRange = ComputerCraft.modem_rangeDuringStorm;
|
||||
maxRange = ComputerCraft.modem_highAltitudeRangeDuringStorm;
|
||||
}
|
||||
if( position.y > 96.0 && maxRange > minRange )
|
||||
{
|
||||
|
@@ -65,7 +65,8 @@ public class MonitorPeripheral implements IPeripheral
|
||||
"setPaletteColour",
|
||||
"setPaletteColor",
|
||||
"getPaletteColour",
|
||||
"getPaletteColor"
|
||||
"getPaletteColor",
|
||||
"getTextScale"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -249,6 +250,11 @@ public class MonitorPeripheral implements IPeripheral
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 24:
|
||||
{
|
||||
// getTextScale
|
||||
return new Object[] { m_monitor.getTextScale() };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@@ -290,7 +290,7 @@ public class TileMonitor extends TilePeripheralBase
|
||||
|
||||
public double getTextScale()
|
||||
{
|
||||
return (double)m_textScale * 0.5;
|
||||
return m_textScale * 0.5;
|
||||
}
|
||||
|
||||
private void rebuildTerminal()
|
||||
@@ -301,11 +301,11 @@ public class TileMonitor extends TilePeripheralBase
|
||||
|
||||
double textScale = getTextScale();
|
||||
int termWidth = (int)Math.max(
|
||||
Math.round( ((double)m_width - 2.0 * ( TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN )) / (textScale * 6.0 * TileMonitor.RENDER_PIXEL_SCALE) ),
|
||||
Math.round( (m_width - 2.0 * ( TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN )) / (textScale * 6.0 * TileMonitor.RENDER_PIXEL_SCALE) ),
|
||||
1.0
|
||||
);
|
||||
int termHeight = (int)Math.max(
|
||||
Math.round( ((double)m_height - 2.0 * ( TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN )) / (textScale * 9.0 * TileMonitor.RENDER_PIXEL_SCALE) ),
|
||||
Math.round( (m_height - 2.0 * ( TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN )) / (textScale * 9.0 * TileMonitor.RENDER_PIXEL_SCALE) ),
|
||||
1.0
|
||||
);
|
||||
((ServerTerminal)getLocalTerminal()).resize( termWidth, termHeight );
|
||||
@@ -709,11 +709,11 @@ public class TileMonitor extends TilePeripheralBase
|
||||
return;
|
||||
}
|
||||
|
||||
double xCharWidth = ((double)m_width - ((RENDER_BORDER + RENDER_MARGIN) * 2.0)) / ((double)originTerminal.getWidth());
|
||||
double yCharHeight = ((double)m_height - ((RENDER_BORDER + RENDER_MARGIN) * 2.0)) / ((double)originTerminal.getHeight());
|
||||
double xCharWidth = (m_width - ((RENDER_BORDER + RENDER_MARGIN) * 2.0)) / (originTerminal.getWidth());
|
||||
double yCharHeight = (m_height - ((RENDER_BORDER + RENDER_MARGIN) * 2.0)) / (originTerminal.getHeight());
|
||||
|
||||
int xCharPos = (int)Math.min((double)originTerminal.getWidth(), Math.max(((pair.x - RENDER_BORDER - RENDER_MARGIN) / xCharWidth) + 1.0, 1.0));
|
||||
int yCharPos = (int)Math.min((double)originTerminal.getHeight(), Math.max(((pair.y - RENDER_BORDER - RENDER_MARGIN) / yCharHeight) + 1.0, 1.0));
|
||||
int xCharPos = (int)Math.min(originTerminal.getWidth(), Math.max(((pair.x - RENDER_BORDER - RENDER_MARGIN) / xCharWidth) + 1.0, 1.0));
|
||||
int yCharPos = (int)Math.min(originTerminal.getHeight(), Math.max(((pair.y - RENDER_BORDER - RENDER_MARGIN) / yCharHeight) + 1.0, 1.0));
|
||||
|
||||
for( int y=0; y<m_height; ++y )
|
||||
{
|
||||
|
@@ -17,7 +17,6 @@ import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.inventory.ISidedInventory;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -42,7 +41,7 @@ import javax.annotation.Nullable;
|
||||
import static net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY;
|
||||
|
||||
public class TilePrinter extends TilePeripheralBase
|
||||
implements IInventory, ISidedInventory
|
||||
implements ISidedInventory
|
||||
{
|
||||
// Statics
|
||||
|
||||
@@ -493,21 +492,6 @@ public class TilePrinter extends TilePeripheralBase
|
||||
ItemStack paperStack = m_inventory.get( i );
|
||||
if( !paperStack.isEmpty() && isPaper(paperStack) )
|
||||
{
|
||||
// Decrement ink
|
||||
inkStack.shrink( 1 );
|
||||
if( inkStack.isEmpty() )
|
||||
{
|
||||
m_inventory.set( 0, ItemStack.EMPTY );
|
||||
}
|
||||
|
||||
// Decrement paper
|
||||
paperStack.shrink( 1 );
|
||||
if( paperStack.isEmpty() )
|
||||
{
|
||||
m_inventory.set( i, ItemStack.EMPTY );
|
||||
updateAnim();
|
||||
}
|
||||
|
||||
// Setup the new page
|
||||
int colour = inkStack.getItemDamage();
|
||||
if( colour >= 0 && colour < 16 ) {
|
||||
@@ -532,6 +516,21 @@ public class TilePrinter extends TilePeripheralBase
|
||||
m_pageTitle = "";
|
||||
}
|
||||
m_page.setCursorPos( 0, 0 );
|
||||
|
||||
// Decrement ink
|
||||
inkStack.shrink( 1 );
|
||||
if( inkStack.isEmpty() )
|
||||
{
|
||||
m_inventory.set( 0, ItemStack.EMPTY );
|
||||
}
|
||||
|
||||
// Decrement paper
|
||||
paperStack.shrink( 1 );
|
||||
if( paperStack.isEmpty() )
|
||||
{
|
||||
m_inventory.set( i, ItemStack.EMPTY );
|
||||
updateAnim();
|
||||
}
|
||||
|
||||
markDirty();
|
||||
m_printing = true;
|
||||
@@ -583,9 +582,9 @@ public class TilePrinter extends TilePeripheralBase
|
||||
|
||||
// Spawn the item in the world
|
||||
BlockPos pos = getPos();
|
||||
double x = (double)pos.getX() + 0.5;
|
||||
double y = (double)pos.getY() + 0.75;
|
||||
double z = (double)pos.getZ() + 0.5;
|
||||
double x = pos.getX() + 0.5;
|
||||
double y = pos.getY() + 0.75;
|
||||
double z = pos.getZ() + 0.5;
|
||||
EntityItem entityitem = new EntityItem( getWorld(), x, y, z, stack );
|
||||
entityitem.motionX = getWorld().rand.nextFloat() * 0.2 - 0.1;
|
||||
entityitem.motionY = getWorld().rand.nextFloat() * 0.2 - 0.1;
|
||||
|
@@ -59,6 +59,11 @@ public class SpeakerPeripheral implements IPeripheral {
|
||||
return m_speaker.getPos();
|
||||
}
|
||||
|
||||
public synchronized boolean madeSound(long ticks)
|
||||
{
|
||||
return (m_clock - m_lastPlayTime <= ticks) ;
|
||||
}
|
||||
|
||||
/* IPeripheral implementation */
|
||||
|
||||
@Override
|
||||
@@ -170,7 +175,7 @@ public class SpeakerPeripheral implements IPeripheral {
|
||||
{
|
||||
@Nullable
|
||||
@Override
|
||||
public Object[] execute() throws LuaException
|
||||
public Object[] execute()
|
||||
{
|
||||
world.playSound( null, pos, SoundEvent.REGISTRY.getObject( resource ), SoundCategory.RECORDS, Math.min( vol, 3f ), soundPitch );
|
||||
return null;
|
||||
|
@@ -402,10 +402,10 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
@Override
|
||||
public IMount createDataMount( @Nonnull ItemStack stack, @Nonnull World world )
|
||||
{
|
||||
ServerComputer computer = createServerComputer( world, null, null, stack );
|
||||
if( computer != null )
|
||||
int id = getComputerID( stack );
|
||||
if( id >= 0 )
|
||||
{
|
||||
return computer.getRootMount();
|
||||
return ComputerCraft.createSaveDirMount( world, "computer/" + id, ComputerCraft.computerSpaceLimit );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@@ -11,6 +11,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.util.Colour;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -73,6 +74,7 @@ public class PocketSpeaker implements IPocketUpgrade
|
||||
speaker.setLocation( entity.getEntityWorld(), entity.posX, entity.posY, entity.posZ );
|
||||
}
|
||||
speaker.update();
|
||||
access.setLight( speaker.madeSound(20) ? 0x3320fc : -1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -18,6 +18,8 @@ import dan200.computercraft.shared.computer.blocks.BlockComputer;
|
||||
import dan200.computercraft.shared.computer.blocks.TileCommandComputer;
|
||||
import dan200.computercraft.shared.computer.blocks.TileComputer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
import dan200.computercraft.shared.computer.core.IContainerComputer;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
|
||||
import dan200.computercraft.shared.computer.items.ItemCommandComputer;
|
||||
@@ -58,6 +60,7 @@ import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.inventory.Container;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemRecord;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -70,6 +73,7 @@ import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
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.eventhandler.SubscribeEvent;
|
||||
@@ -145,7 +149,22 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void playRecord( SoundEvent record, String recordInfo, World world, BlockPos pos );
|
||||
public void playRecord( SoundEvent record, String recordInfo, World world, BlockPos pos )
|
||||
{
|
||||
ComputerCraftPacket packet = new ComputerCraftPacket();
|
||||
packet.m_packetType = ComputerCraftPacket.PlayRecord;
|
||||
if( record != null )
|
||||
{
|
||||
packet.m_dataInt = new int[] { pos.getX(), pos.getY(), pos.getZ(), SoundEvent.REGISTRY.getIDForObject( record ) };
|
||||
packet.m_dataString = new String[] { recordInfo };
|
||||
}
|
||||
else
|
||||
{
|
||||
packet.m_dataInt = new int[] { pos.getX(), pos.getY(), pos.getZ() };
|
||||
}
|
||||
|
||||
ComputerCraft.sendToAllPlayers( packet );
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract Object getDiskDriveGUI( InventoryPlayer inventory, TileDiskDrive drive );
|
||||
@@ -646,5 +665,20 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
||||
ComputerCraft.syncConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onContainerOpen( PlayerContainerEvent.Open event )
|
||||
{
|
||||
// If we're opening a computer container then broadcast the terminal state
|
||||
Container container = event.getContainer();
|
||||
if( container instanceof IContainerComputer )
|
||||
{
|
||||
IComputer computer = ((IContainerComputer) container).getComputer();
|
||||
if( computer instanceof ServerComputer )
|
||||
{
|
||||
((ServerComputer) computer).sendTerminalState( event.getEntityPlayer() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.util.DirectionUtil;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.properties.PropertyDirection;
|
||||
import net.minecraft.block.state.BlockFaceShape;
|
||||
import net.minecraft.block.state.BlockStateContainer;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
@@ -75,6 +76,14 @@ public class BlockTurtle extends BlockComputerBase
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@Deprecated
|
||||
public BlockFaceShape getBlockFaceShape( IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing side )
|
||||
{
|
||||
return BlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
protected BlockStateContainer createBlockState()
|
||||
|
@@ -10,6 +10,7 @@ import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.shared.computer.blocks.ComputerProxy;
|
||||
import dan200.computercraft.shared.computer.blocks.TileComputerBase;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
@@ -43,12 +44,11 @@ import net.minecraftforge.items.wrapper.InvWrapper;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
import static net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY;
|
||||
|
||||
public class TileTurtle extends TileComputerBase
|
||||
implements ITurtleTile, IInventory, ITickable
|
||||
implements ITurtleTile, IInventory
|
||||
{
|
||||
// Statics
|
||||
|
||||
@@ -114,6 +114,12 @@ public class TileTurtle extends TileComputerBase
|
||||
return createComputer( instanceID, id, ComputerCraft.terminalWidth_turtle, ComputerCraft.terminalHeight_turtle );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputerProxy createProxy()
|
||||
{
|
||||
return m_brain.getProxy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
|
@@ -12,6 +12,8 @@ import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.turtle.*;
|
||||
import dan200.computercraft.shared.computer.blocks.ComputerProxy;
|
||||
import dan200.computercraft.shared.computer.blocks.TileComputerBase;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
@@ -104,6 +106,7 @@ public class TurtleBrain implements ITurtleAccess
|
||||
private static final int ANIM_DURATION = 8;
|
||||
|
||||
private TileTurtle m_owner;
|
||||
private ComputerProxy m_proxy;
|
||||
|
||||
private LinkedList<TurtleCommandQueueEntry> m_commandQueue;
|
||||
private int m_commandsIssued;
|
||||
@@ -169,6 +172,21 @@ public class TurtleBrain implements ITurtleAccess
|
||||
return m_owner;
|
||||
}
|
||||
|
||||
public ComputerProxy getProxy()
|
||||
{
|
||||
if(m_proxy == null) {
|
||||
m_proxy = new ComputerProxy()
|
||||
{
|
||||
@Override
|
||||
protected TileComputerBase getTile()
|
||||
{
|
||||
return m_owner;
|
||||
}
|
||||
};
|
||||
}
|
||||
return m_proxy;
|
||||
}
|
||||
|
||||
public ComputerFamily getFamily()
|
||||
{
|
||||
return m_owner.getFamily();
|
||||
@@ -936,11 +954,11 @@ public class TurtleBrain implements ITurtleAccess
|
||||
}
|
||||
}
|
||||
|
||||
double distance = -1.0 + (double)getAnimationFraction( f );
|
||||
double distance = -1.0 + getAnimationFraction( f );
|
||||
return new Vec3d(
|
||||
distance * (double)dir.getFrontOffsetX(),
|
||||
distance * (double)dir.getFrontOffsetY(),
|
||||
distance * (double)dir.getFrontOffsetZ()
|
||||
distance * dir.getFrontOffsetX(),
|
||||
distance * dir.getFrontOffsetY(),
|
||||
distance * dir.getFrontOffsetZ()
|
||||
);
|
||||
}
|
||||
default:
|
||||
@@ -955,7 +973,7 @@ public class TurtleBrain implements ITurtleAccess
|
||||
if( (side == TurtleSide.Left && m_animation == TurtleAnimation.SwingLeftTool) ||
|
||||
(side == TurtleSide.Right && m_animation == TurtleAnimation.SwingRightTool) )
|
||||
{
|
||||
return 45.0f * (float)Math.sin( (double) getAnimationFraction( f ) * Math.PI );
|
||||
return 45.0f * (float)Math.sin( getAnimationFraction( f ) * Math.PI );
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
@@ -1126,39 +1144,39 @@ public class TurtleBrain implements ITurtleAccess
|
||||
float push = Math.max( pushFrac + 0.0125f, 0.0f );
|
||||
if (moveDir.getFrontOffsetX() < 0)
|
||||
{
|
||||
minX += (double)((float)moveDir.getFrontOffsetX() * push);
|
||||
minX += moveDir.getFrontOffsetX() * push;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxX -= (double)((float)moveDir.getFrontOffsetX() * push);
|
||||
maxX -= moveDir.getFrontOffsetX() * push;
|
||||
}
|
||||
|
||||
if (moveDir.getFrontOffsetY() < 0)
|
||||
{
|
||||
minY += (double)((float)moveDir.getFrontOffsetY() * push);
|
||||
minY += moveDir.getFrontOffsetY() * push;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxY -= (double)((float)moveDir.getFrontOffsetY() * push);
|
||||
maxY -= moveDir.getFrontOffsetY() * push;
|
||||
}
|
||||
|
||||
if (moveDir.getFrontOffsetZ() < 0)
|
||||
{
|
||||
minZ += (double)((float)moveDir.getFrontOffsetZ() * push);
|
||||
minZ += moveDir.getFrontOffsetZ() * push;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxZ -= (double)((float)moveDir.getFrontOffsetZ() * push);
|
||||
maxZ -= moveDir.getFrontOffsetZ() * push;
|
||||
}
|
||||
|
||||
AxisAlignedBB aabb = new AxisAlignedBB( minX, minY, minZ, maxX, maxY, maxZ );
|
||||
List<Entity> list = world.getEntitiesWithinAABBExcludingEntity( null, aabb );
|
||||
if( !list.isEmpty() )
|
||||
{
|
||||
double pushStep = 1.0f / (float) ANIM_DURATION;
|
||||
double pushStepX = (double) moveDir.getFrontOffsetX() * pushStep;
|
||||
double pushStepY = (double) moveDir.getFrontOffsetY() * pushStep;
|
||||
double pushStepZ = (double) moveDir.getFrontOffsetZ() * pushStep;
|
||||
double pushStep = 1.0f / ANIM_DURATION;
|
||||
double pushStepX = moveDir.getFrontOffsetX() * pushStep;
|
||||
double pushStepY = moveDir.getFrontOffsetY() * pushStep;
|
||||
double pushStepZ = moveDir.getFrontOffsetZ() * pushStep;
|
||||
for (Entity entity : list)
|
||||
{
|
||||
entity.move( MoverType.PISTON, pushStepX, pushStepY, pushStepZ );
|
||||
|
@@ -85,9 +85,9 @@ public class TurtleMoveCommand implements ITurtleCommand
|
||||
if( entityBB != null )
|
||||
{
|
||||
AxisAlignedBB pushedBB = entityBB.offset(
|
||||
(double) direction.getFrontOffsetX(),
|
||||
(double) direction.getFrontOffsetY(),
|
||||
(double) direction.getFrontOffsetZ()
|
||||
direction.getFrontOffsetX(),
|
||||
direction.getFrontOffsetY(),
|
||||
direction.getFrontOffsetZ()
|
||||
);
|
||||
if( !oldWorld.getCollisionBoxes( null, pushedBB ).isEmpty() )
|
||||
{
|
||||
|
@@ -9,7 +9,6 @@ package dan200.computercraft.shared.turtle.items;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.util.ColourUtils;
|
||||
import net.minecraft.block.Block;
|
||||
@@ -20,7 +19,7 @@ import net.minecraftforge.common.util.Constants;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class ItemTurtleNormal extends ItemTurtleBase implements IColouredItem
|
||||
public class ItemTurtleNormal extends ItemTurtleBase
|
||||
{
|
||||
public ItemTurtleNormal( Block block )
|
||||
{
|
||||
|
@@ -32,7 +32,6 @@ import javax.vecmath.Matrix4f;
|
||||
public class TurtleModem implements ITurtleUpgrade
|
||||
{
|
||||
private static class Peripheral extends WirelessModemPeripheral
|
||||
implements IPeripheral
|
||||
{
|
||||
private final ITurtleAccess m_turtle;
|
||||
|
||||
@@ -55,9 +54,9 @@ public class TurtleModem implements ITurtleUpgrade
|
||||
{
|
||||
BlockPos turtlePos = m_turtle.getPosition();
|
||||
return new Vec3d(
|
||||
(double)turtlePos.getX(),
|
||||
(double)turtlePos.getY(),
|
||||
(double)turtlePos.getZ()
|
||||
turtlePos.getX(),
|
||||
turtlePos.getY(),
|
||||
turtlePos.getZ()
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -52,9 +52,9 @@ public enum Colour
|
||||
{
|
||||
m_hex = hex;
|
||||
m_rgb = new float[] {
|
||||
(float)((hex >> 16) & 0xFF) / 255.0f,
|
||||
(float)((hex >> 8 ) & 0xFF) / 255.0f,
|
||||
(float)((hex ) & 0xFF) / 255.0f,
|
||||
((hex >> 16) & 0xFF) / 255.0f,
|
||||
((hex >> 8 ) & 0xFF) / 255.0f,
|
||||
((hex ) & 0xFF) / 255.0f,
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -38,7 +38,7 @@ public class ColourTracker
|
||||
int avgB = totalB / count;
|
||||
|
||||
float avgTotal = (float) total / (float) count;
|
||||
float avgMax = (float) Math.max( avgR, Math.max( avgG, avgB ) );
|
||||
float avgMax = Math.max( avgR, Math.max( avgG, avgB ) );
|
||||
avgR = (int) (avgR * avgTotal / avgMax);
|
||||
avgG = (int) (avgG * avgTotal / avgMax);
|
||||
avgB = (int) (avgB * avgTotal / avgMax);
|
||||
|
@@ -9,6 +9,8 @@ package dan200.computercraft.shared.util;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
@@ -16,18 +18,20 @@ public class CreativeTabMain extends CreativeTabs
|
||||
{
|
||||
public CreativeTabMain( int i )
|
||||
{
|
||||
super( i, "ComputerCraft" );
|
||||
super( i, "CC: Tweaked" );
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public ItemStack getTabIconItem()
|
||||
{
|
||||
return new ItemStack( ComputerCraft.Blocks.computer );
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public String getTranslatedTabLabel()
|
||||
{
|
||||
return getTabLabel();
|
||||
|
@@ -6,11 +6,18 @@
|
||||
|
||||
package dan200.computercraft.shared.util;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.*;
|
||||
import net.minecraft.util.math.*;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
@@ -47,9 +54,9 @@ public class WorldUtil
|
||||
}
|
||||
|
||||
// Check for entities
|
||||
float xStretch = (Math.abs(vecDir.x) > 0.25f) ? 0.0f : 1.0f;
|
||||
float yStretch = (Math.abs(vecDir.y) > 0.25f) ? 0.0f : 1.0f;
|
||||
float zStretch = (Math.abs(vecDir.z) > 0.25f) ? 0.0f : 1.0f;
|
||||
float xStretch = Math.abs(vecDir.x) > 0.25f ? 0.0f : 1.0f;
|
||||
float yStretch = Math.abs(vecDir.y) > 0.25f ? 0.0f : 1.0f;
|
||||
float zStretch = Math.abs(vecDir.z) > 0.25f ? 0.0f : 1.0f;
|
||||
AxisAlignedBB bigBox = new AxisAlignedBB(
|
||||
Math.min(vecStart.x, vecEnd.x) - 0.375f * xStretch,
|
||||
Math.min(vecStart.y, vecEnd.y) - 0.375f * yStretch,
|
||||
@@ -113,6 +120,35 @@ public class WorldUtil
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Vec3d getRayStart( EntityLivingBase entity )
|
||||
{
|
||||
return new Vec3d( entity.posX, entity.posY + entity.getEyeHeight(), entity.posZ );
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Vec3d look = player.getLookVec();
|
||||
|
||||
return getRayStart( player ).addVector( look.x * reach, look.y * reach, look.z * reach );
|
||||
}
|
||||
|
||||
public static boolean isVecInsideInclusive(AxisAlignedBB bb , Vec3d vec) {
|
||||
return vec.x >= bb.minX && vec.x <= bb.maxX && vec.y >= bb.minY && vec.y <= bb.maxY && vec.z >= bb.minZ && vec.z <= bb.maxZ;
|
||||
}
|
||||
|
||||
public static void dropItemStack( @Nonnull ItemStack stack, World world, BlockPos pos )
|
||||
{
|
||||
dropItemStack( stack, world, pos, null );
|
||||
@@ -125,9 +161,9 @@ public class WorldUtil
|
||||
double zDir;
|
||||
if( direction != null )
|
||||
{
|
||||
xDir = (double)direction.getFrontOffsetX();
|
||||
yDir = (double)direction.getFrontOffsetY();
|
||||
zDir = (double)direction.getFrontOffsetZ();
|
||||
xDir = direction.getFrontOffsetX();
|
||||
yDir = direction.getFrontOffsetY();
|
||||
zDir = direction.getFrontOffsetZ();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [ "computercraft:advanced_pocket_upgrade_computercraft_advanved_modem" ]
|
||||
},
|
||||
"criteria": {
|
||||
"has_normal": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [ { "item": "computercraft:pocket_computer", "data": 0 } ]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": { "recipe": "computercraft:advanced_pocket_upgrade_computercraft_advanved_modem" }
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_normal",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [ "computercraft:advanced_pocket_upgrade_computercraft_speaker" ]
|
||||
},
|
||||
"criteria": {
|
||||
"has_normal": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [ { "item": "computercraft:pocket_computer", "data": 0 } ]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": { "recipe": "computercraft:advanced_pocket_upgrade_computercraft_speaker" }
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_normal",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [ "computercraft:advanced_pocket_upgrade_computercraft_wireless_modem" ]
|
||||
},
|
||||
"criteria": {
|
||||
"has_normal": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [ { "item": "computercraft:pocket_computer", "data": 0 } ]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": { "recipe": "computercraft:advanced_pocket_upgrade_computercraft_wireless_modem" }
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_normal",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [ "computercraft:advanced_turtle_upgrade_computercraft_advanced_modem_1" ]
|
||||
},
|
||||
"criteria": {
|
||||
"has_normal": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_computercraft_advanced_modem_1" }
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_normal",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [ "computercraft:advanced_turtle_upgrade_computercraft_speaker_1" ]
|
||||
},
|
||||
"criteria": {
|
||||
"has_normal": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_computercraft_speaker_1" }
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_normal",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [ "computercraft:advanced_turtle_upgrade_computercraft_wireless_modem_1" ]
|
||||
},
|
||||
"criteria": {
|
||||
"has_normal": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_computercraft_wireless_modem_1" }
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_normal",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [ "computercraft:advanced_turtle_upgrade_minecraft_crafting_table_1" ]
|
||||
},
|
||||
"criteria": {
|
||||
"has_normal": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_minecraft_crafting_table_1" }
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_normal",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [ "computercraft:advanced_turtle_upgrade_minecraft_diamond_axe_1" ]
|
||||
},
|
||||
"criteria": {
|
||||
"has_normal": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_minecraft_diamond_axe_1" }
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_normal",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [ "computercraft:advanced_turtle_upgrade_minecraft_diamond_hoe_1" ]
|
||||
},
|
||||
"criteria": {
|
||||
"has_normal": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_minecraft_diamond_hoe_1" }
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_normal",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [ "computercraft:advanced_turtle_upgrade_minecraft_diamond_pickaxe_1" ]
|
||||
},
|
||||
"criteria": {
|
||||
"has_normal": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_minecraft_diamond_pickaxe_1" }
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_normal",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [ "computercraft:advanced_turtle_upgrade_minecraft_diamond_shovel_1" ]
|
||||
},
|
||||
"criteria": {
|
||||
"has_normal": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_minecraft_diamond_shovel_1" }
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_normal",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [ "computercraft:advanced_turtle_upgrade_minecraft_diamond_sword_1" ]
|
||||
},
|
||||
"criteria": {
|
||||
"has_normal": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_minecraft_diamond_sword_1" }
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_normal",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [ "computercraft:disk_imposter_1" ]
|
||||
},
|
||||
"criteria": {
|
||||
"has_normal": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [ { "item": "computercraft:peripheral", "data": 0 } ]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": { "recipe": "computercraft:disk_imposter_1" }
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_normal",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [ "computercraft:disk_imposter_10" ]
|
||||
},
|
||||
"criteria": {
|
||||
"has_normal": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [ { "item": "computercraft:peripheral", "data": 0 } ]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": { "recipe": "computercraft:disk_imposter_10" }
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_normal",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user