1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-14 20:20:30 +00:00

Merge branch 'mc-1.16.x' into mc-1.17.x

This commit is contained in:
Jonathan Coates 2021-11-29 19:40:05 +00:00
commit af966179ce
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
44 changed files with 1306 additions and 353 deletions

View File

@ -17,7 +17,7 @@ plugins {
id "com.github.hierynomus.license" version "0.16.1"
id "com.matthewprenger.cursegradle" version "1.4.0"
id "com.github.breadmoirai.github-release" version "2.2.12"
id "org.jetbrains.kotlin.jvm" version "1.5.21"
id "org.jetbrains.kotlin.jvm" version "1.6.0"
id "com.modrinth.minotaur" version "1.2.1"
}
@ -162,13 +162,13 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
testImplementation 'org.hamcrest:hamcrest:2.2'
testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.21'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1'
testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.0'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
testModImplementation sourceSets.main.output
testModExtra 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.21'
cctJavadoc 'cc.tweaked:cct-javadoc:1.4.1'
cctJavadoc 'cc.tweaked:cct-javadoc:1.4.2'
}
// Compile tasks

View File

@ -2,7 +2,7 @@
module: [kind=event] redstone
---
The @{redstone} event is fired whenever any redstone inputs on the computer change.
The @{event!redstone} event is fired whenever any redstone inputs on the computer change.
## Example
Prints a message when a redstone input changes:

View File

@ -1,7 +1,7 @@
org.gradle.jvmargs=-Xmx3G
# Mod properties
mod_version=1.98.2
mod_version=1.99.0
# Minecraft properties (update mods.toml when changing)
mc_version=1.17.1

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

269
gradlew vendored
View File

@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -17,67 +17,101 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${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='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# 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
;;
MSYS* | MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@ -106,80 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
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
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# 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"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

402
package-lock.json generated
View File

@ -16,10 +16,45 @@
"@rollup/plugin-typescript": "^8.2.5",
"requirejs": "^2.3.6",
"rollup": "^2.33.1",
"terser": "^5.3.8",
"rollup-plugin-terser": "^7.0.2",
"typescript": "^4.0.5"
}
},
"node_modules/@babel/code-frame": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz",
"integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==",
"dev": true,
"dependencies": {
"@babel/highlight": "^7.16.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.15.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
"integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/highlight": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz",
"integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==",
"dev": true,
"dependencies": {
"@babel/helper-validator-identifier": "^7.15.7",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@rollup/plugin-typescript": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.0.tgz",
@ -61,18 +96,74 @@
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
"dev": true
},
"node_modules/@types/node": {
"version": "16.11.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz",
"integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==",
"dev": true
},
"node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true,
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/estree-walker": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
@ -111,6 +202,15 @@
"node": ">= 0.4.0"
}
},
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/is-core-module": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz",
@ -123,6 +223,53 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/jest-worker": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
"integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
"dev": true,
"dependencies": {
"@types/node": "*",
"merge-stream": "^2.0.0",
"supports-color": "^7.0.0"
},
"engines": {
"node": ">= 10.13.0"
}
},
"node_modules/jest-worker/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/jest-worker/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
},
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
"dev": true
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
@ -142,14 +289,23 @@
}
},
"node_modules/preact": {
"version": "10.5.15",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.5.15.tgz",
"integrity": "sha512-5chK29n6QcJc3m1lVrKQSQ+V7K1Gb8HeQY6FViQ5AxCAEGu3DaHffWNDkC9+miZgsLvbvU9rxbV1qinGHMHzqA==",
"version": "10.6.1",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.6.1.tgz",
"integrity": "sha512-ydCg+ISIq70vqiThvNWStZWLRjR9U2awP/JAmGdWUKm9+Tyuy+MqVdAIyEByeIspAVtD4GWC/SJtxO8XD4knVA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
"dependencies": {
"safe-buffer": "^5.1.0"
}
},
"node_modules/requirejs": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
@ -177,9 +333,9 @@
}
},
"node_modules/rollup": {
"version": "2.60.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.0.tgz",
"integrity": "sha512-cHdv9GWd58v58rdseC8e8XIaPUo8a9cgZpnCMMDGZFDZKEODOiPPEQFXLriWr/TjXzhPPmG5bkAztPsOARIcGQ==",
"version": "2.60.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.1.tgz",
"integrity": "sha512-akwfnpjY0rXEDSn1UTVfKXJhPsEBu+imi1gqBA1ZkHGydUnkV/fWCC90P7rDaLEW8KTwBcS1G3N4893Ndz+jwg==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
@ -191,6 +347,50 @@
"fsevents": "~2.3.2"
}
},
"node_modules/rollup-plugin-terser": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz",
"integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.10.4",
"jest-worker": "^26.2.1",
"serialize-javascript": "^4.0.0",
"terser": "^5.0.0"
},
"peerDependencies": {
"rollup": "^2.0.0"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/serialize-javascript": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
"integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
"dev": true,
"dependencies": {
"randombytes": "^2.1.0"
}
},
"node_modules/source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
@ -219,6 +419,18 @@
"node": ">=0.10.0"
}
},
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/terser": {
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",
@ -264,6 +476,32 @@
}
},
"dependencies": {
"@babel/code-frame": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz",
"integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==",
"dev": true,
"requires": {
"@babel/highlight": "^7.16.0"
}
},
"@babel/helper-validator-identifier": {
"version": "7.15.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
"integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==",
"dev": true
},
"@babel/highlight": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz",
"integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.15.7",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
}
},
"@rollup/plugin-typescript": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.0.tgz",
@ -291,18 +529,65 @@
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
"dev": true
},
"@types/node": {
"version": "16.11.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz",
"integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"estree-walker": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
@ -331,6 +616,12 @@
"function-bind": "^1.1.1"
}
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"is-core-module": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz",
@ -340,6 +631,46 @@
"has": "^1.0.3"
}
},
"jest-worker": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
"integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
"dev": true,
"requires": {
"@types/node": "*",
"merge-stream": "^2.0.0",
"supports-color": "^7.0.0"
},
"dependencies": {
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
},
"merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
"dev": true
},
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
@ -353,9 +684,18 @@
"dev": true
},
"preact": {
"version": "10.5.15",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.5.15.tgz",
"integrity": "sha512-5chK29n6QcJc3m1lVrKQSQ+V7K1Gb8HeQY6FViQ5AxCAEGu3DaHffWNDkC9+miZgsLvbvU9rxbV1qinGHMHzqA=="
"version": "10.6.1",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.6.1.tgz",
"integrity": "sha512-ydCg+ISIq70vqiThvNWStZWLRjR9U2awP/JAmGdWUKm9+Tyuy+MqVdAIyEByeIspAVtD4GWC/SJtxO8XD4knVA=="
},
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
"requires": {
"safe-buffer": "^5.1.0"
}
},
"requirejs": {
"version": "2.3.6",
@ -374,14 +714,41 @@
}
},
"rollup": {
"version": "2.60.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.0.tgz",
"integrity": "sha512-cHdv9GWd58v58rdseC8e8XIaPUo8a9cgZpnCMMDGZFDZKEODOiPPEQFXLriWr/TjXzhPPmG5bkAztPsOARIcGQ==",
"version": "2.60.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.1.tgz",
"integrity": "sha512-akwfnpjY0rXEDSn1UTVfKXJhPsEBu+imi1gqBA1ZkHGydUnkV/fWCC90P7rDaLEW8KTwBcS1G3N4893Ndz+jwg==",
"dev": true,
"requires": {
"fsevents": "~2.3.2"
}
},
"rollup-plugin-terser": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz",
"integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.10.4",
"jest-worker": "^26.2.1",
"serialize-javascript": "^4.0.0",
"terser": "^5.0.0"
}
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true
},
"serialize-javascript": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
"integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
"dev": true,
"requires": {
"randombytes": "^2.1.0"
}
},
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
@ -406,6 +773,15 @@
}
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
},
"terser": {
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",

View File

@ -12,7 +12,7 @@
"@rollup/plugin-typescript": "^8.2.5",
"requirejs": "^2.3.6",
"rollup": "^2.33.1",
"terser": "^5.3.8",
"rollup-plugin-terser": "^7.0.2",
"typescript": "^4.0.5"
}
}

View File

@ -1,7 +1,8 @@
import { readFileSync, promises as fs } from "fs";
import { readFileSync } from "fs";
import path from "path";
import typescript from "@rollup/plugin-typescript";
import { terser } from "rollup-plugin-terser";
const input = "src/web";
const requirejs = readFileSync("node_modules/requirejs/require.js");
@ -12,7 +13,14 @@ export default {
file: "build/rollup/index.js",
// We bundle requirejs (and config) into the header. It's rather gross
// but also works reasonably well.
banner: `${requirejs}\nrequire.config({ paths: { copycat: "https://copy-cat.squiddev.cc" } });`,
// Also suffix a ?v=${date} onto the end in the event we need to require a specific copy-cat version.
banner: `
${requirejs}
require.config({
paths: { copycat: "https://copy-cat.squiddev.cc" },
urlArgs: function(id) { return id == "copycat/embed" ? "?v=20211127" : ""; }
});
`,
format: "amd",
preferConst: true,
amd: {
@ -27,22 +35,6 @@ export default {
{
name: "cc-tweaked",
async options(options) {
// Generate .d.ts files for all /mount files. This is the worst way to do it,
// but we need to run before the TS pass.
const template = "declare const contents : string;\nexport default contents;\n";
const files = await fs.readdir(`${input}/mount`);
await Promise.all(files
.filter(x => path.extname(x) !== ".ts")
.map(async file => {
const path = `${input}/mount/${file}.d.ts`;
const contents = await fs.readFile(path, { encoding: "utf-8" }).catch(() => "");
if (contents !== template) await fs.writeFile(path, template);
})
);
return options;
},
async transform(code, file) {
// Allow loading files in /mount.
const ext = path.extname(file);
@ -50,6 +42,8 @@ export default {
? `export default ${JSON.stringify(code)};\n`
: null;
},
}
},
terser(),
],
};

View File

@ -10,6 +10,8 @@ import net.minecraftforge.common.capabilities.Capability;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Set;
/**
* The interface that defines a peripheral.
@ -31,6 +33,18 @@ public interface IPeripheral
@Nonnull
String getType();
/**
* Return additional types/traits associated with this object.
*
* @return A collection of additional object traits.
* @see PeripheralType#getAdditionalTypes()
*/
@Nonnull
default Set<String> getAdditionalTypes()
{
return Collections.emptySet();
}
/**
* Is called when when a computer is attaching to the peripheral.
*

View File

@ -6,9 +6,13 @@
package dan200.computercraft.api.peripheral;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
/**
* The type of a {@link GenericPeripheral}.
@ -18,13 +22,19 @@ import javax.annotation.Nullable;
*/
public final class PeripheralType
{
private static final PeripheralType UNTYPED = new PeripheralType( null );
private static final PeripheralType UNTYPED = new PeripheralType( null, Collections.emptySet() );
private final String type;
private final Set<String> additionalTypes;
public PeripheralType( String type )
public PeripheralType( String type, Set<String> additionalTypes )
{
this.type = type;
this.additionalTypes = additionalTypes;
if( additionalTypes.contains( null ) )
{
throw new IllegalArgumentException( "All additional types must be non-null" );
}
}
/**
@ -46,7 +56,55 @@ public final class PeripheralType
public static PeripheralType ofType( @Nonnull String type )
{
if( Strings.isNullOrEmpty( type ) ) throw new IllegalArgumentException( "type cannot be null or empty" );
return new PeripheralType( type );
return new PeripheralType( type, Collections.emptySet() );
}
/**
* Create a new non-empty peripheral type with additional traits.
*
* @param type The name of the type.
* @param additionalTypes Additional types, or "traits" of this peripheral. For instance, {@literal "inventory"}.
* @return The constructed peripheral type.
*/
public static PeripheralType ofType( @Nonnull String type, Collection<String> additionalTypes )
{
if( Strings.isNullOrEmpty( type ) ) throw new IllegalArgumentException( "type cannot be null or empty" );
return new PeripheralType( type, ImmutableSet.copyOf( additionalTypes ) );
}
/**
* Create a new non-empty peripheral type with additional traits.
*
* @param type The name of the type.
* @param additionalTypes Additional types, or "traits" of this peripheral. For instance, {@literal "inventory"}.
* @return The constructed peripheral type.
*/
public static PeripheralType ofType( @Nonnull String type, @Nonnull String... additionalTypes )
{
if( Strings.isNullOrEmpty( type ) ) throw new IllegalArgumentException( "type cannot be null or empty" );
return new PeripheralType( type, ImmutableSet.copyOf( additionalTypes ) );
}
/**
* Create a new peripheral type with no primary type but additional traits.
*
* @param additionalTypes Additional types, or "traits" of this peripheral. For instance, {@literal "inventory"}.
* @return The constructed peripheral type.
*/
public static PeripheralType ofAdditional( Collection<String> additionalTypes )
{
return new PeripheralType( null, ImmutableSet.copyOf( additionalTypes ) );
}
/**
* Create a new peripheral type with no primary type but additional traits.
*
* @param additionalTypes Additional types, or "traits" of this peripheral. For instance, {@literal "inventory"}.
* @return The constructed peripheral type.
*/
public static PeripheralType ofAdditional( @Nonnull String... additionalTypes )
{
return new PeripheralType( null, ImmutableSet.copyOf( additionalTypes ) );
}
/**
@ -59,4 +117,15 @@ public final class PeripheralType
{
return type;
}
/**
* Get any additional types or "traits" of this peripheral. These effectively act as a standard set of interfaces
* a peripheral might have.
*
* @return All additional types.
*/
public Set<String> getAdditionalTypes()
{
return additionalTypes;
}
}

View File

@ -484,7 +484,7 @@ public class FSAPI implements ILuaAPI
*
* This string is formatted like a normal path string, but can include any
* number of wildcards ({@code *}) to look for files matching anything.
* For example, {@code rom/* /command*} will look for any path starting with
* For example, <code>rom/&#42;/command*</code> will look for any path starting with
* {@code command} inside any subdirectory of {@code /rom}.
*
* @param path The wildcard-qualified path to search for.

View File

@ -17,6 +17,7 @@ import dan200.computercraft.core.asm.NamedMethod;
import dan200.computercraft.core.asm.PeripheralMethod;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.core.tracking.TrackingField;
import dan200.computercraft.shared.util.LuaUtil;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -36,6 +37,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
private final IPeripheral peripheral;
private final String type;
private final Set<String> additionalTypes;
private final Map<String, PeripheralMethod> methodMap;
private boolean attached;
@ -47,6 +49,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
attached = false;
type = Objects.requireNonNull( peripheral.getType(), "Peripheral type cannot be null" );
additionalTypes = peripheral.getAdditionalTypes();
methodMap = PeripheralAPI.getMethods( peripheral );
}
@ -61,6 +64,11 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
return type;
}
public Set<String> getAdditionalTypes()
{
return additionalTypes;
}
public Collection<String> getMethods()
{
return methodMap.keySet();
@ -298,7 +306,23 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
synchronized( peripherals )
{
PeripheralWrapper p = peripherals[side.ordinal()];
if( p != null ) return new Object[] { p.getType() };
return p == null ? null : LuaUtil.consArray( p.getType(), p.getAdditionalTypes() );
}
}
@LuaFunction
public final Object[] hasType( String sideName, String type )
{
ComputerSide side = ComputerSide.valueOfInsensitive( sideName );
if( side == null ) return null;
synchronized( peripherals )
{
PeripheralWrapper p = peripherals[side.ordinal()];
if( p != null )
{
return new Object[] { p.getType().equals( type ) || p.getAdditionalTypes().contains( type ) };
}
}
return null;
}

View File

@ -38,13 +38,13 @@ public final class Peripherals
}
@Nullable
public static IPeripheral getPeripheral( Level world, BlockPos pos, Direction side, NonNullConsumer<LazyOptional<IPeripheral>> invalidate )
public static IPeripheral getPeripheral( Level world, BlockPos pos, Direction side, NonNullConsumer<Object> invalidate )
{
return world.isInWorldBounds( pos ) && !world.isClientSide ? getPeripheralAt( world, pos, side, invalidate ) : null;
}
@Nullable
private static IPeripheral getPeripheralAt( Level world, BlockPos pos, Direction side, NonNullConsumer<LazyOptional<IPeripheral>> invalidate )
private static IPeripheral getPeripheralAt( Level world, BlockPos pos, Direction side, NonNullConsumer<? super Object> invalidate )
{
BlockEntity block = world.getBlockEntity( pos );
if( block != null )

View File

@ -59,7 +59,7 @@ public abstract class BlockComputerBase<T extends TileComputerBase> extends Bloc
super.onPlace( state, world, pos, oldState, isMoving );
BlockEntity tile = world.getBlockEntity( pos );
if( tile instanceof TileComputerBase computer ) computer.updateInput();
if( tile instanceof TileComputerBase computer ) computer.updateInputsImmediately( );
}
@Override

View File

@ -31,14 +31,9 @@ import net.minecraft.world.Nameable;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.DiodeBlock;
import net.minecraft.world.level.block.RedStoneWireBlock;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.NonNullConsumer;
import javax.annotation.Nonnull;
@ -57,7 +52,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
private boolean on = false;
boolean startOn = false;
private boolean fresh = false;
private final NonNullConsumer<LazyOptional<IPeripheral>>[] invalidate;
private int invalidSides = 0;
private final NonNullConsumer<Object>[] invalidate;
private final ComputerFamily family;
@ -68,10 +65,11 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
// We cache these so we can guarantee we only ever register one listener for adjacent capabilities.
@SuppressWarnings( { "unchecked", "rawtypes" } )
NonNullConsumer<LazyOptional<IPeripheral>>[] invalidate = this.invalidate = new NonNullConsumer[6];
NonNullConsumer<Object>[] invalidate = this.invalidate = new NonNullConsumer[6];
for( Direction direction : Direction.values() )
{
invalidate[direction.ordinal()] = o -> updateInput( direction );
int mask = 1 << direction.ordinal();
invalidate[direction.ordinal()] = o -> invalidSides |= mask;
}
}
@ -143,19 +141,26 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
@Override
public void onNeighbourChange( @Nonnull BlockPos neighbour )
{
updateInput( neighbour );
updateInputAt( neighbour );
}
@Override
public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour )
{
updateInput( neighbour );
updateInputAt( neighbour );
}
protected void serverTick()
{
ServerComputer computer = createServerComputer();
if( computer == null ) return;
if( invalidSides != 0 )
{
for( Direction direction : DirectionUtil.FACINGS )
{
if( (invalidSides & (1 << direction.ordinal())) != 0 ) refreshPeripheral( computer, direction );
}
}
// If the computer isn't on and should be, then turn it on
if( startOn || (fresh && on) )
@ -222,89 +227,80 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
return localSide;
}
private void updateSideInput( ServerComputer computer, Direction dir, BlockPos offset )
private void updateRedstoneInput( @Nonnull ServerComputer computer, Direction dir, BlockPos targetPos )
{
Direction offsetSide = dir.getOpposite();
ComputerSide localDir = remapToLocalSide( dir );
computer.setRedstoneInput( localDir, getRedstoneInput( level, offset, dir ) );
computer.setBundledRedstoneInput( localDir, BundledRedstone.getOutput( getLevel(), offset, offsetSide ) );
if( !isPeripheralBlockedOnSide( localDir ) )
computer.setRedstoneInput( localDir, RedstoneUtil.getRedstoneInput( level, targetPos, dir ) );
computer.setBundledRedstoneInput( localDir, BundledRedstone.getOutput( getLevel(), targetPos, offsetSide ) );
}
private void refreshPeripheral( @Nonnull ServerComputer computer, Direction dir )
{
IPeripheral peripheral = Peripherals.getPeripheral( getLevel(), offset, offsetSide, invalidate[dir.ordinal()] );
invalidSides &= ~(1 << dir.ordinal());
ComputerSide localDir = remapToLocalSide( dir );
if( isPeripheralBlockedOnSide( localDir ) ) return;
Direction offsetSide = dir.getOpposite();
IPeripheral peripheral = Peripherals.getPeripheral( getLevel(), getBlockPos().relative( dir ), offsetSide, invalidate[dir.ordinal()] );
computer.setPeripheral( localDir, peripheral );
}
public void updateInputsImmediately()
{
ServerComputer computer = getServerComputer();
if( computer != null ) updateInputsImmediately( computer );
}
/**
* Gets the redstone input for an adjacent block.
* Update all redstone and peripherals.
*
* @param world The world we exist in
* @param pos The position of the neighbour
* @param side The side we are reading from
* @return The effective redstone power
* @see DiodeBlock#getInputSignal(Level, BlockPos, BlockState)
* This should only be really be called when the computer is being ticked (though there are some cases where it
* won't be), as peripheral scanning requires adjacent tiles to be in a "correct" state - which may not be the case
* if they're still updating!
*
* @param computer The current computer instance.
*/
protected static int getRedstoneInput( Level world, BlockPos pos, Direction side )
private void updateInputsImmediately( @Nonnull ServerComputer computer )
{
int power = world.getSignal( pos, side );
if( power >= 15 ) return power;
BlockState neighbour = world.getBlockState( pos );
return neighbour.getBlock() == Blocks.REDSTONE_WIRE
? Math.max( power, neighbour.getValue( RedStoneWireBlock.POWER ) )
: power;
}
public void updateInput()
{
if( getLevel() == null || getLevel().isClientSide ) return;
// Update all sides
ServerComputer computer = getServerComputer();
if( computer == null ) return;
BlockPos pos = computer.getPosition();
BlockPos pos = getBlockPos();
for( Direction dir : DirectionUtil.FACINGS )
{
updateSideInput( computer, dir, pos.relative( dir ) );
updateRedstoneInput( computer, dir, pos.relative( dir ) );
refreshPeripheral( computer, dir );
}
}
private void updateInput( BlockPos neighbour )
private void updateInputAt( @Nonnull BlockPos neighbour )
{
if( getLevel() == null || getLevel().isClientSide ) return;
ServerComputer computer = getServerComputer();
if( computer == null ) return;
for( Direction dir : DirectionUtil.FACINGS )
{
BlockPos offset = worldPosition.relative( dir );
BlockPos offset = getBlockPos().relative( dir );
if( offset.equals( neighbour ) )
{
updateSideInput( computer, dir, offset );
updateRedstoneInput( computer, dir, offset );
invalidSides |= 1 << dir.ordinal();
return;
}
}
// If the position is not any adjacent one, update all inputs.
updateInput();
}
private void updateInput( Direction dir )
{
if( getLevel() == null || getLevel().isClientSide ) return;
ServerComputer computer = getServerComputer();
if( computer == null ) return;
updateSideInput( computer, dir, worldPosition.relative( dir ) );
// If the position is not any adjacent one, update all inputs. This is pretty terrible, but some redstone mods
// handle this incorrectly.
BlockPos pos = getBlockPos();
for( Direction dir : DirectionUtil.FACINGS ) updateRedstoneInput( computer, dir, pos.relative( dir ) );
invalidSides = (1 << 6) - 1; // Mark all peripherals as dirty.
}
/**
* Update the block's state and propagate redstone output.
*/
public void updateOutput()
{
// Update redstone
updateBlock();
for( Direction dir : DirectionUtil.FACINGS )
{
@ -354,9 +350,10 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
return family;
}
@Nonnull
public ServerComputer createServerComputer()
{
if( getLevel().isClientSide ) return null;
if( getLevel().isClientSide ) throw new IllegalStateException( "Cannot access server computer on the client." );
boolean changed = false;
if( instanceID < 0 )
@ -364,18 +361,21 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
instanceID = ComputerCraft.serverComputerRegistry.getUnusedInstanceID();
changed = true;
}
if( !ComputerCraft.serverComputerRegistry.contains( instanceID ) )
ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instanceID );
if( computer == null )
{
ServerComputer computer = createComputer( instanceID, computerID );
computer = createComputer( instanceID, computerID );
ComputerCraft.serverComputerRegistry.add( instanceID, computer );
fresh = true;
changed = true;
}
if( changed ) updateInput();
return ComputerCraft.serverComputerRegistry.get( instanceID );
if( changed ) updateInputsImmediately( computer );
return computer;
}
@Nullable
public ServerComputer getServerComputer()
{
return getLevel().isClientSide ? null : ComputerCraft.serverComputerRegistry.get( instanceID );

View File

@ -128,7 +128,7 @@ public class CommandBlockPeripheral implements IPeripheral, ICapabilityProvider
public static void onCapability( AttachCapabilitiesEvent<BlockEntity> event )
{
BlockEntity tile = event.getObject();
if( tile instanceof CommandBlockEntity )
if( ComputerCraft.enableCommandBlock && tile instanceof CommandBlockEntity )
{
CommandBlockPeripheral peripheral = new CommandBlockPeripheral( (CommandBlockEntity) tile );
event.addCapability( CAP_ID, peripheral );

View File

@ -18,18 +18,21 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Set;
class GenericPeripheral implements IDynamicPeripheral
{
private final String type;
private final Set<String> additionalTypes;
private final BlockEntity tile;
private final List<SaturatedMethod> methods;
GenericPeripheral( BlockEntity tile, String name, List<SaturatedMethod> methods )
GenericPeripheral( BlockEntity tile, String name, Set<String> additionalTypes, List<SaturatedMethod> methods )
{
ResourceLocation type = tile.getType().getRegistryName();
this.tile = tile;
this.type = name != null ? name : (type != null ? type.toString() : "unknown");
this.additionalTypes = additionalTypes;
this.methods = methods;
}
@ -56,6 +59,13 @@ class GenericPeripheral implements IDynamicPeripheral
return type;
}
@Nonnull
@Override
public Set<String> getAdditionalTypes()
{
return additionalTypes;
}
@Nullable
@Override
public Object getTarget()

View File

@ -9,6 +9,7 @@ import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.PeripheralType;
import dan200.computercraft.core.asm.NamedMethod;
import dan200.computercraft.core.asm.PeripheralMethod;
import dan200.computercraft.shared.util.CapabilityUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
@ -19,9 +20,7 @@ import net.minecraftforge.common.util.NonNullConsumer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.*;
public class GenericPeripheralProvider
{
@ -34,7 +33,7 @@ public class GenericPeripheralProvider
}
@Nullable
public static IPeripheral getPeripheral( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side, NonNullConsumer<LazyOptional<IPeripheral>> invalidate )
public static IPeripheral getPeripheral( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side, NonNullConsumer<Object> invalidate )
{
BlockEntity tile = world.getBlockEntity( pos );
if( tile == null ) return null;
@ -52,7 +51,7 @@ public class GenericPeripheralProvider
if( capabilityMethods.isEmpty() ) return;
saturated.addMethods( contents, capabilityMethods );
wrapper.addListener( cast( invalidate ) );
CapabilityUtil.addListener( wrapper, invalidate );
} );
}
@ -61,15 +60,16 @@ public class GenericPeripheralProvider
private static class GenericPeripheralBuilder
{
String name;
final ArrayList<SaturatedMethod> methods = new ArrayList<>( 0 );
private String name;
private final Set<String> additionalTypes = new HashSet<>( 0 );
private final ArrayList<SaturatedMethod> methods = new ArrayList<>( 0 );
IPeripheral toPeripheral( BlockEntity tile )
{
if( methods.isEmpty() ) return null;
methods.trimToSize();
return new GenericPeripheral( tile, name, methods );
return new GenericPeripheral( tile, name, additionalTypes, methods );
}
void addMethods( Object target, List<NamedMethod<PeripheralMethod>> methods )
@ -88,13 +88,8 @@ public class GenericPeripheralProvider
String name = type.getPrimaryType();
if( this.name == null || this.name.compareTo( name ) > 0 ) this.name = name;
}
if( type != null ) additionalTypes.addAll( type.getAdditionalTypes() );
}
}
}
@SuppressWarnings( { "unchecked", "rawtypes" } )
private static <T> NonNullConsumer<T> cast( NonNullConsumer<?> consumer )
{
return (NonNullConsumer) consumer;
}
}

View File

@ -6,8 +6,9 @@
package dan200.computercraft.shared.peripheral.generic.methods;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.GenericSource;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.GenericPeripheral;
import dan200.computercraft.api.peripheral.PeripheralType;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.energy.IEnergyStorage;
@ -25,8 +26,15 @@ import javax.annotation.Nonnull;
*
* @cc.module energy_storage
*/
public class EnergyMethods implements GenericSource
public class EnergyMethods implements GenericPeripheral
{
@Nonnull
@Override
public PeripheralType getType()
{
return PeripheralType.ofAdditional( "energy_storage" );
}
@Nonnull
@Override
public ResourceLocation id()

View File

@ -6,13 +6,15 @@
package dan200.computercraft.shared.peripheral.generic.methods;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.GenericSource;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.GenericPeripheral;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.PeripheralType;
import dan200.computercraft.shared.peripheral.generic.data.FluidData;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.material.Fluid;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
@ -34,8 +36,15 @@ import static dan200.computercraft.shared.peripheral.generic.methods.ArgumentHel
*
* @cc.module fluid_storage
*/
public class FluidMethods implements GenericSource
public class FluidMethods implements GenericPeripheral
{
@Nonnull
@Override
public PeripheralType getType()
{
return PeripheralType.ofAdditional( "fluid_storage" );
}
@Nonnull
@Override
public ResourceLocation id()
@ -155,13 +164,15 @@ public class FluidMethods implements GenericSource
@Nullable
private static IFluidHandler extractHandler( @Nullable Object object )
{
if( object instanceof ICapabilityProvider )
if( object instanceof BlockEntity blockEntity && blockEntity.isRemoved() ) return null;
if( object instanceof ICapabilityProvider provider )
{
LazyOptional<IFluidHandler> cap = ((ICapabilityProvider) object).getCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY );
LazyOptional<IFluidHandler> cap = provider.getCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY );
if( cap.isPresent() ) return cap.orElseThrow( NullPointerException::new );
}
if( object instanceof IFluidHandler ) return (IFluidHandler) object;
if( object instanceof IFluidHandler handler ) return handler;
return null;
}

View File

@ -6,16 +6,18 @@
package dan200.computercraft.shared.peripheral.generic.methods;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.GenericSource;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.GenericPeripheral;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.PeripheralType;
import dan200.computercraft.shared.peripheral.generic.data.ItemData;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.Container;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
@ -36,8 +38,15 @@ import static dan200.computercraft.shared.peripheral.generic.methods.ArgumentHel
*
* @cc.module inventory
*/
public class InventoryMethods implements GenericSource
public class InventoryMethods implements GenericPeripheral
{
@Nonnull
@Override
public PeripheralType getType()
{
return PeripheralType.ofAdditional( "inventory" );
}
@Nonnull
@Override
public ResourceLocation id()
@ -259,14 +268,16 @@ public class InventoryMethods implements GenericSource
@Nullable
private static IItemHandler extractHandler( @Nullable Object object )
{
if( object instanceof ICapabilityProvider )
if( object instanceof BlockEntity blockEntity && blockEntity.isRemoved() ) return null;
if( object instanceof ICapabilityProvider provider )
{
LazyOptional<IItemHandler> cap = ((ICapabilityProvider) object).getCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY );
LazyOptional<IItemHandler> cap = provider.getCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY );
if( cap.isPresent() ) return cap.orElseThrow( NullPointerException::new );
}
if( object instanceof IItemHandler ) return (IItemHandler) object;
if( object instanceof Container ) return new InvWrapper( (Container) object );
if( object instanceof IItemHandler handler ) return handler;
if( object instanceof Container container ) return new InvWrapper( container );
return null;
}

View File

@ -77,8 +77,9 @@ public class TileCable extends TileGeneric
}
}
private boolean invalidPeripheral;
private boolean peripheralAccessAllowed;
private final WiredModemLocalPeripheral peripheral = new WiredModemLocalPeripheral( this::refreshPeripheral );
private final WiredModemLocalPeripheral peripheral = new WiredModemLocalPeripheral( this::queueRefreshPeripheral );
private boolean destroyed = false;
@ -234,12 +235,20 @@ public class TileCable extends TileGeneric
if( !level.isClientSide && peripheralAccessAllowed )
{
Direction facing = getDirection();
if( getBlockPos().relative( facing ).equals( neighbour ) ) refreshPeripheral();
if( getBlockPos().relative( facing ).equals( neighbour ) ) queueRefreshPeripheral();
}
}
private void queueRefreshPeripheral()
{
if( invalidPeripheral ) return;
invalidPeripheral = true;
TickScheduler.schedule( this );
}
private void refreshPeripheral()
{
invalidPeripheral = false;
if( level != null && !isRemoved() && peripheral.attach( level, getBlockPos(), getDirection() ) )
{
updateConnectedPeripherals();
@ -310,6 +319,8 @@ public class TileCable extends TileGeneric
{
if( getLevel().isClientSide ) return;
if( invalidPeripheral ) refreshPeripheral();
if( modem.getModemState().pollChanged() ) updateBlockState();
if( !connectionsFormed )

View File

@ -108,13 +108,15 @@ public class TileWiredModemFull extends TileGeneric
private final NonNullConsumer<LazyOptional<IWiredElement>> connectedNodeChanged = x -> connectionsChanged();
private int invalidSides = 0;
public TileWiredModemFull( BlockEntityType<TileWiredModemFull> type, BlockPos pos, BlockState state )
{
super( type, pos, state );
for( int i = 0; i < peripherals.length; i++ )
{
Direction facing = Direction.from3DDataValue( i );
peripherals[i] = new WiredModemLocalPeripheral( () -> refreshPeripheral( facing ) );
peripherals[i] = new WiredModemLocalPeripheral( () -> queueRefreshPeripheral( facing ) );
}
}
@ -173,13 +175,20 @@ public class TileWiredModemFull extends TileGeneric
{
for( Direction facing : DirectionUtil.FACINGS )
{
if( getBlockPos().relative( facing ).equals( neighbour ) ) refreshPeripheral( facing );
if( getBlockPos().relative( facing ).equals( neighbour ) ) queueRefreshPeripheral( facing );
}
}
}
private void queueRefreshPeripheral( @Nonnull Direction facing )
{
if( invalidSides == 0 ) TickScheduler.schedule( this );
invalidSides |= 1 << facing.ordinal();
}
private void refreshPeripheral( @Nonnull Direction facing )
{
invalidSides &= ~(1 << facing.ordinal());
WiredModemLocalPeripheral peripheral = peripherals[facing.ordinal()];
if( level != null && !isRemoved() && peripheral.attach( level, getBlockPos(), facing ) )
{
@ -262,6 +271,14 @@ public class TileWiredModemFull extends TileGeneric
{
if( getLevel().isClientSide ) return;
if( invalidSides != 0 )
{
for( Direction direction : DirectionUtil.FACINGS )
{
if( (invalidSides & (1 << direction.ordinal())) != 0 ) refreshPeripheral( direction );
}
}
if( modemState.pollChanged() ) updateBlockState();
if( !connectionsFormed )

View File

@ -15,7 +15,6 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.NonNullConsumer;
import javax.annotation.Nonnull;
@ -38,7 +37,7 @@ public final class WiredModemLocalPeripheral
private String type;
private IPeripheral peripheral;
private final NonNullConsumer<LazyOptional<IPeripheral>> invalidate;
private final NonNullConsumer<Object> invalidate;
public WiredModemLocalPeripheral( @Nonnull Runnable invalidate )
{

View File

@ -21,6 +21,7 @@ import dan200.computercraft.core.apis.PeripheralAPI;
import dan200.computercraft.core.asm.PeripheralMethod;
import dan200.computercraft.shared.peripheral.modem.ModemPeripheral;
import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.util.LuaUtil;
import net.minecraft.world.level.Level;
import javax.annotation.Nonnull;
@ -118,13 +119,35 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
* @param name The peripheral's name.
* @return The peripheral's name.
* @cc.treturn string|nil The peripheral's type, or {@code nil} if it is not present.
* @cc.changed 1.99 Peripherals can have multiple types - this function returns multiple values.
* @see PeripheralAPI#getType
*/
@LuaFunction
public final Object[] getTypeRemote( IComputerAccess computer, String name )
{
RemotePeripheralWrapper wrapper = getWrapper( computer, name );
return wrapper != null ? new Object[] { wrapper.getType() } : null;
return wrapper == null ? null : LuaUtil.consArray( wrapper.getType(), wrapper.getAdditionalTypes() );
}
/**
* Check a peripheral is of a particular type.
*
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
* returns false before calling it.</blockquote>
*
* @param computer The calling computer.
* @param name The peripheral's name.
* @param type The type to check.
* @return The peripheral's name.
* @cc.treturn boolean|nil If a peripheral has a particular type, or {@literal nil} if it is not present.
* @cc.since 1.99
* @see PeripheralAPI#getType
*/
@LuaFunction
public final Object[] hasTypeRemote( IComputerAccess computer, String name, String type )
{
RemotePeripheralWrapper wrapper = getWrapper( computer, name );
return wrapper == null ? null : new Object[] { wrapper.getType().equals( type ) || wrapper.getAdditionalTypes().contains( getType() ) };
}
/**
@ -307,6 +330,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
private final String name;
private final String type;
private final Set<String> additionalTypes;
private final Map<String, PeripheralMethod> methodMap;
private volatile boolean attached;
@ -320,6 +344,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
this.name = name;
type = Objects.requireNonNull( peripheral.getType(), "Peripheral type cannot be null" );
additionalTypes = peripheral.getAdditionalTypes();
methodMap = PeripheralAPI.getMethods( peripheral );
}
@ -353,6 +378,11 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
return type;
}
public Set<String> getAdditionalTypes()
{
return additionalTypes;
}
public Collection<String> getMethodNames()
{
return methodMap.keySet();

View File

@ -331,8 +331,10 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
{
if( dir.getAxis() == Direction.Axis.Y ) dir = Direction.NORTH;
level.setBlockAndUpdate( worldPosition, getBlockState().setValue( BlockTurtle.FACING, dir ) );
updateOutput();
updateInput();
updateInputsImmediately();
onTileEntityChange();
}

View File

@ -332,16 +332,17 @@ public class TurtleBrain implements ITurtleAccess
// Copy the old turtle state into the new turtle
newTurtle.setLevel( world );
newTurtle.transferStateFrom( oldOwner );
newTurtle.createServerComputer().setLevel( world );
newTurtle.createServerComputer().setPosition( pos );
ServerComputer computer = newTurtle.createServerComputer();
computer.setLevel( world );
computer.setPosition( pos );
// Remove the old turtle
oldWorld.removeBlock( oldPos, false );
// Make sure everybody knows about it
newTurtle.updateBlock();
newTurtle.updateInput();
newTurtle.updateOutput();
newTurtle.updateInputsImmediately();
return true;
}
}
@ -613,16 +614,16 @@ public class TurtleBrain implements ITurtleAccess
@Override
public void setUpgrade( @Nonnull TurtleSide side, ITurtleUpgrade upgrade )
{
if( !setUpgradeDirect( side, upgrade ) ) return;
if( !setUpgradeDirect( side, upgrade ) || owner.getLevel() == null ) return;
// This is a separate function to avoid updating the block when reading the NBT. We don't need to do this as
// either the block is newly placed (and so won't have changed) or is being updated with /data, which calls
// updateBlock for us.
if( owner.getLevel() != null )
{
owner.updateBlock();
owner.updateInput();
}
// Recompute peripherals in case an upgrade being removed has exposed a new peripheral.
// TODO: Only update peripherals, or even only two sides?
owner.updateInputsImmediately();
}
private boolean setUpgradeDirect( @Nonnull TurtleSide side, ITurtleUpgrade upgrade )
@ -644,7 +645,7 @@ public class TurtleBrain implements ITurtleAccess
if( upgrade != null ) upgrades.put( side, upgrade );
// Notify clients and create peripherals
if( owner.getLevel() != null )
if( owner.getLevel() != null && !owner.getLevel().isClientSide )
{
updatePeripherals( owner.createServerComputer() );
}

View File

@ -35,12 +35,20 @@ public final class CapabilityUtil
}
}
public static <T> void addListener( LazyOptional<T> p, NonNullConsumer<? super LazyOptional<T>> invalidate )
{
// We can make this safe with invalidate::accept, but then we're allocating it's just kind of absurd.
@SuppressWarnings( "unchecked" )
NonNullConsumer<LazyOptional<T>> safeInvalidate = (NonNullConsumer<LazyOptional<T>>) invalidate;
p.addListener( safeInvalidate );
}
@Nullable
public static <T> T unwrap( LazyOptional<T> p, NonNullConsumer<LazyOptional<T>> invalidate )
public static <T> T unwrap( LazyOptional<T> p, NonNullConsumer<? super LazyOptional<T>> invalidate )
{
if( !p.isPresent() ) return null;
p.addListener( invalidate );
addListener( p, invalidate );
return p.orElseThrow( NullPointerException::new );
}

View File

@ -0,0 +1,23 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.util;
import java.util.Collection;
public class LuaUtil
{
public static Object[] consArray( Object value, Collection<?> rest )
{
if( rest.isEmpty() ) return new Object[] { value };
// I'm not proud of this code.
Object[] out = new Object[rest.size() + 1];
out[0] = value;
int i = 1;
for( Object additionalType : rest ) out[i++] = additionalType;
return out;
}
}

View File

@ -8,6 +8,9 @@ package dan200.computercraft.shared.util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.DiodeBlock;
import net.minecraft.world.level.block.RedStoneWireBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.event.ForgeEventFactory;
@ -15,6 +18,30 @@ import java.util.EnumSet;
public final class RedstoneUtil
{
private RedstoneUtil()
{
}
/**
* Gets the redstone input for an adjacent block.
*
* @param world The world we exist in
* @param pos The position of the neighbour
* @param side The side we are reading from
* @return The effective redstone power
* @see DiodeBlock#getInputSignal(Level, BlockPos, BlockState)
*/
public static int getRedstoneInput( Level world, BlockPos pos, Direction side )
{
int power = world.getSignal( pos, side );
if( power >= 15 ) return power;
BlockState neighbour = world.getBlockState( pos );
return neighbour.getBlock() == Blocks.REDSTONE_WIRE
? Math.max( power, neighbour.getValue( RedStoneWireBlock.POWER ) )
: power;
}
public static void propagateRedstoneOutput( Level world, BlockPos pos, Direction side )
{
// Propagate ordinary output. See BlockRedstoneDiode.notifyNeighbors

View File

@ -68,7 +68,7 @@ end
-- @{paintutils.drawImage}, or `nil` if the file does not exist.
-- @usage Load an image and draw it.
--
-- local image = paintutils.loadImage("test-image.nfp")
-- local image = paintutils.loadImage("data/example.nfp")
-- paintutils.drawImage(image, term.getCursorPos())
function loadImage(path)
expect(1, path, "string")

View File

@ -1,18 +1,90 @@
--- The Peripheral API is for interacting with peripherals connected to the
-- computer, such as the Disk Drive, the Advanced Monitor and Monitor.
--
-- Each peripheral block has a name, either referring to the side the peripheral
-- can be found on, or a name on an adjacent wired network.
--
-- If the peripheral is next to the computer, its side is either `front`,
-- `back`, `left`, `right`, `top` or `bottom`. If the peripheral is attached by
-- a cable, its side will follow the format `type_id`, for example `printer_0`.
--
-- Peripheral functions are called *methods*, a term borrowed from Java.
--
-- @module peripheral
-- @since 1.3
-- @changed 1.51 Add support for wired modems.
--[[- Peripherals are blocks (or turtle and pocket computer upgrades) which can
be controlled by a computer. For instance, the @{speaker} peripheral allows a
computer to play music and the @{monitor} peripheral allows you to display text
in the world.
## Referencing peripherals
Computers can interact with adjacent peripherals. Each peripheral is given a
name based on which direction it is in. For instance, a disk drive below your
computer will be called `"bottom"` in your Lua code, one to the left called
`"left"` , and so on for all 6 directions (`"bottom"`, `"top"`, `"left"`,
`"right"`, `"front"`, `"back"`).
You can list the names of all peripherals with the `peripherals` program, or the
@{peripheral.getNames} function.
It's also possible to use peripherals which are further away from your computer
through the use of @{modem|Wired Modems}. Place one modem against your computer,
run Networking Cable to your peripheral, and then place another modem against
that block. You can then right click the modem to use (or *attach*) the
peripheral. This will print a peripheral name to chat, which can then be used
just like a direction name to access the peripheral. You can click on the message
to copy the name to your clipboard.
## Using peripherals
Once you have the name of a peripheral, you can call functions on it using the
@{peripheral.call} function. This takes the name of our peripheral, the name of
the function we want to call, and then its arguments.
> Some bits of the peripheral API call peripheral functions *methods* instead
> (for example, the @{peripheral.getMethods} function). Don't worry, they're the
> same thing!
Let's say we have a monitor above our computer (and so "top") and want to
@{monitor.write|write some text to it}. We'd write the following:
```lua
peripheral.call("top", "write", "This is displayed on a monitor!")
```
Once you start calling making a couple of peripheral calls this can get very
repetitive, and so we can @{peripheral.wrap|wrap} a peripheral. This builds a
table of all the peripheral's functions so you can use it like an API or module.
For instance, we could have written the above example as follows:
```lua
local my_monitor = peripheral.wrap("top")
my_monitor.write("This is displayed on a monitor!")
```
## Finding peripherals
Sometimes when you're writing a program you don't care what a peripheral is
called, you just need to know it's there. For instance, if you're writing a
music player, you just need a speaker - it doesn't matter if it's above or below
the computer.
Thankfully there's a quick way to do this: @{peripheral.find}. This takes a
*peripheral type* and returns all the attached peripherals which are of this
type.
What is a peripheral type though? This is a string which describes what a
peripheral is, and so what functions are available on it. For instance, speakers
are just called `"speaker"`, and monitors `"monitor"`. Some peripherals might
have more than one type; a Minecraft chest is both a `"minecraft:chest"` and
`"inventory"`.
You can get all the types a peripheral has with @{peripheral.getType}, and check
a peripheral is a specific type with @{peripheral.hasType}.
To return to our original example, let's use @{peripheral.find} to find an
attached speaker:
```lua
local speaker = peripheral.find("speaker")
speaker.playNote("harp")
```
@module peripheral
@see event!peripheral This event is fired whenever a new peripheral is attached.
@see event!peripheral_detach This event is fired whenever a peripheral is detached.
@since 1.3
@changed 1.51 Add support for wired modems.
@changed 1.99 Peripherals can have multiple types.
]]
local expect = dofile("rom/modules/main/cc/expect.lua").expect
@ -33,7 +105,7 @@ function getNames()
local side = sides[n]
if native.isPresent(side) then
table.insert(results, side)
if native.getType(side) == "modem" and not native.call(side, "isWireless") then
if native.hasType(side, "modem") and not native.call(side, "isWireless") then
local remote = native.call(side, "getNamesRemote")
for _, name in ipairs(remote) do
table.insert(results, name)
@ -58,7 +130,7 @@ function isPresent(name)
for n = 1, #sides do
local side = sides[n]
if native.getType(side) == "modem" and not native.call(side, "isWireless") and
if native.hasType(side, "modem") and not native.call(side, "isWireless") and
native.call(side, "isPresentRemote", name)
then
return true
@ -67,12 +139,17 @@ function isPresent(name)
return false
end
--- Get the type of a wrapped peripheral, or a peripheral with the given name.
--
-- @tparam string|table peripheral The name of the peripheral to find, or a
-- wrapped peripheral instance.
-- @treturn string|nil The peripheral's type, or `nil` if it is not present.
-- @changed 1.88.0 Accepts a wrapped peripheral as an argument.
--[[- Get the types of a named or wrapped peripheral.
@tparam string|table peripheral The name of the peripheral to find, or a
wrapped peripheral instance.
@treturn string... The peripheral's types, or `nil` if it is not present.
@changed 1.88.0 Accepts a wrapped peripheral as an argument.
@changed 1.99 Now returns multiple types.
@usage Get the type of a peripheral above this computer.
peripheral.getType("top")
]]
function getType(peripheral)
expect(1, peripheral, "string", "table")
if type(peripheral) == "string" then -- Peripheral name passed
@ -81,7 +158,7 @@ function getType(peripheral)
end
for n = 1, #sides do
local side = sides[n]
if native.getType(side) == "modem" and not native.call(side, "isWireless") and
if native.hasType(side, "modem") and not native.call(side, "isWireless") and
native.call(side, "isPresentRemote", peripheral)
then
return native.call(side, "getTypeRemote", peripheral)
@ -90,10 +167,43 @@ function getType(peripheral)
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.type) ~= "string" then
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.type
return table.unpack(mt.types)
end
end
--[[- Check if a peripheral is of a particular type.
@tparam string|table peripheral The name of the peripheral or a wrapped peripheral instance.
@tparam string peripheral_type The type to check.
@treturn boolean|nil If a peripheral has a particular type, or `nil` if it is not present.
@since 1.99
]]
function hasType(peripheral, peripheral_type)
expect(1, peripheral, "string", "table")
expect(2, peripheral_type, "string")
if type(peripheral) == "string" then -- Peripheral name passed
if native.isPresent(peripheral) then
return native.hasType(peripheral, peripheral_type)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "modem") and not native.call(side, "isWireless") and
native.call(side, "isPresentRemote", peripheral)
then
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.types[peripheral_type] ~= nil
end
end
@ -109,7 +219,7 @@ function getMethods(name)
end
for n = 1, #sides do
local side = sides[n]
if native.getType(side) == "modem" and not native.call(side, "isWireless") and
if native.hasType(side, "modem") and not native.call(side, "isWireless") and
native.call(side, "isPresentRemote", name)
then
return native.call(side, "getMethodsRemote", name)
@ -151,7 +261,7 @@ function call(name, method, ...)
for n = 1, #sides do
local side = sides[n]
if native.getType(side) == "modem" and not native.call(side, "isWireless") and
if native.hasType(side, "modem") and not native.call(side, "isWireless") and
native.call(side, "isPresentRemote", name)
then
return native.call(side, "callRemote", name, method, ...)
@ -160,15 +270,16 @@ function call(name, method, ...)
return nil
end
--- Get a table containing functions pointing to the peripheral's methods, which
-- can then be called as if using @{peripheral.call}.
--- Get a table containing all functions available on a peripheral. These can
-- then be called instead of using @{peripheral.call} every time.
--
-- @tparam string name The name of the peripheral to wrap.
-- @treturn table|nil The table containing the peripheral's methods, or `nil` if
-- there is no peripheral present with the given name.
-- @usage Open the modem on the top of this computer.
--
-- peripheral.wrap("top").open(1)
-- local modem = peripheral.wrap("top")
-- modem.open(1)
function wrap(name)
expect(1, name, "string")
@ -177,10 +288,14 @@ function wrap(name)
return nil
end
-- We store our types array as a list (for getType) and a lookup table (for hasType).
local types = { peripheral.getType(name) }
for i = 1, #types do types[types[i]] = true end
local result = setmetatable({}, {
__name = "peripheral",
name = name,
type = peripheral.getType(name),
type = types[1],
types = types,
})
for _, method in ipairs(methods) do
result[method] = function(...)
@ -222,7 +337,7 @@ function find(ty, filter)
local results = {}
for _, name in ipairs(peripheral.getNames()) do
if peripheral.getType(name) == ty then
if peripheral.hasType(name, ty) then
local wrapped = peripheral.wrap(name)
if filter == nil or filter(name, wrapped) then
table.insert(results, wrapped)

View File

@ -1,3 +1,34 @@
# New features in CC: Tweaked 1.99.0
* Pocket computers in their offhand will open without showing a terminal. You can look around and interact with the world, but your keyboard will be forwarded to the computer. (Wojbie, MagGen-hub).
* Peripherals can now have multiple types. `peripheral.getType` now returns multiple values, and `peripheral.hasType` checks if a peripheral has a specific type.
* Add several missing keys to the `keys` table. (ralphgod3)
* Add feature introduction/changed version information to the documentation. (MCJack123)
* Increase the file upload limit to 512KiB.
* Rednet can now handle computer IDs larger than 65535. (Ale32bit)
* Optimise deduplication of rednet messages (MCJack123)
* Make `term.blit` colours case insensitive. (Ocawesome101)
* Add a new `about` program for easier version identification. (MCJack123)
* Optimise peripheral calls in `rednet.run`. (xAnavrins)
* Add dimension parameter to `commands.getBlockInfo`.
* Add `cc.pretty.pretty_print` helper function (Lupus590).
* Add back JEI integration.
* Turtle and pocket computer upgrades can now be added and modified with data packs.
* Various translation updates (MORIMORI3017, Ale2Bit, mindy15963)
And several bug fixes:
* Fix various computer commands failing when OP level was 4.
* Various documentation fixes. (xXTurnerLP, MCJack123)
* Fix `textutils.serialize` not serialising infinity and nan values. (Wojbie)
* Wired modems now correctly clean up mounts when a peripheral is detached.
* Fix incorrect turtle and pocket computer upgrade recipes in the recipe book.
* Fix speakers not playing sounds added via resource packs which are not registered in-game.
* Fix speaker upgrades sending packets after the server has stopped.
* Monitor sizing has been rewritten, hopefully making it more stable.
* Peripherals are now invalidated when the computer ticks, rather than when the peripheral changes.
* Fix printouts and pocket computers rendering at fullbright when in item frames.
* All mod blocks now have an effective tool (pickaxe).
# New features in CC: Tweaked 1.98.2
* Add JP translation (MORIMORI0317)

View File

@ -1,13 +1,32 @@
New features in CC: Tweaked 1.98.2
New features in CC: Tweaked 1.99.0
* Add JP translation (MORIMORI0317)
* Migrate several recipes to data generators.
* Pocket computers in their offhand will open without showing a terminal. You can look around and interact with the world, but your keyboard will be forwarded to the computer. (Wojbie, MagGen-hub).
* Peripherals can now have multiple types. `peripheral.getType` now returns multiple values, and `peripheral.hasType` checks if a peripheral has a specific type.
* Add several missing keys to the `keys` table. (ralphgod3)
* Add feature introduction/changed version information to the documentation. (MCJack123)
* Increase the file upload limit to 512KiB.
* Rednet can now handle computer IDs larger than 65535. (Ale32bit)
* Optimise deduplication of rednet messages (MCJack123)
* Make `term.blit` colours case insensitive. (Ocawesome101)
* Add a new `about` program for easier version identification. (MCJack123)
* Optimise peripheral calls in `rednet.run`. (xAnavrins)
* Add dimension parameter to `commands.getBlockInfo`.
* Add `cc.pretty.pretty_print` helper function (Lupus590).
* Add back JEI integration.
* Turtle and pocket computer upgrades can now be added and modified with data packs.
* Various translation updates (MORIMORI3017, Ale2Bit, mindy15963)
Several bug fixes:
* Fix volume speaker sounds are played at.
* Fix several rendering issues when holding pocket computers and printouts in
hand.
* Ensure wired modems and cables join the wired network on chunk load.
* Fix stack overflow when using wired networks.
And several bug fixes:
* Fix various computer commands failing when OP level was 4.
* Various documentation fixes. (xXTurnerLP, MCJack123)
* Fix `textutils.serialize` not serialising infinity and nan values. (Wojbie)
* Wired modems now correctly clean up mounts when a peripheral is detached.
* Fix incorrect turtle and pocket computer upgrade recipes in the recipe book.
* Fix speakers not playing sounds added via resource packs which are not registered in-game.
* Fix speaker upgrades sending packets after the server has stopped.
* Monitor sizing has been rewritten, hopefully making it more stable.
* Peripherals are now invalidated when the computer ticks, rather than when the peripheral changes.
* Fix printouts and pocket computers rendering at fullbright when in item frames.
* All mod blocks now have an effective tool (pickaxe).
Type "help changelog" to see the full version history.

View File

@ -9,8 +9,8 @@
-- @usage Load an image from `example.nft` and draw it.
--
-- local nft = require "cc.image.nft"
-- local image = assert(nft.load("example.nft"))
-- nft.draw(image)
-- local image = assert(nft.load("data/example.nft"))
-- nft.draw(image, term.getCursorPos())
local expect = require "cc.expect".expect
@ -41,6 +41,7 @@ local function parse(image)
end
line = line + 1
foreground, background = "0", "f"
else
local next = image:find("[\n\30\31]", i) or #image + 1
local seg_len = next - i

View File

@ -19,7 +19,7 @@ The structure of this module is based on [A Prettier Printer][prettier].
@usage Print a table to the terminal
local pretty = require "cc.pretty"
pretty.print(pretty.pretty({ 1, 2, 3 }))
pretty.pretty_print({ 1, 2, 3 })
@usage Build a custom document and display it
@ -463,6 +463,7 @@ end
--
-- local pretty = require "cc.pretty"
-- pretty.print(pretty.pretty({ 1, 2, 3 }))
-- @see pretty_print for a shorthand to prettify and print an object.
local function pretty(obj, options)
expect(2, options, "table", "nil")
options = options or {}
@ -474,6 +475,33 @@ local function pretty(obj, options)
return pretty_impl(obj, actual_options, {})
end
--[[- A shortcut for calling @{pretty} and @{print} together.
@param obj The object to pretty-print.
@tparam[opt] { function_args = boolean, function_source = boolean } options
Controls how various properties are displayed.
- `function_args`: Show the arguments to a function if known (`false` by default).
- `function_source`: Show where the function was defined, instead of
`function: xxxxxxxx` (`false` by default).
@tparam[opt] number ribbon_frac The maximum fraction of the width that we should write in.
@usage Display a table on the screen.
local pretty = require "cc.pretty"
pretty.pretty_print({ 1, 2, 3 })
@see pretty
@see print
@since 1.99
]]
local function pretty_print(obj, options, ribbon_frac)
expect(2, options, "table", "nil")
options = options or {}
expect(3, ribbon_frac, "number", "nil")
return print(pretty(obj, options), ribbon_frac)
end
return {
empty = empty,
space = space,
@ -489,4 +517,6 @@ return {
render = render,
pretty = pretty,
pretty_print = pretty_print,
}

View File

@ -3,7 +3,7 @@ print("Attached Peripherals:")
if #tPeripherals > 0 then
for n = 1, #tPeripherals do
local sPeripheral = tPeripherals[n]
print(sPeripheral .. " (" .. peripheral.getType(sPeripheral) .. ")")
print(sPeripheral .. " (" .. table.concat({ peripheral.getType(sPeripheral) }, ", ") .. ")")
end
else
print("None")

View File

@ -15,6 +15,7 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlin.time.ExperimentalTime

View File

@ -1,6 +1,13 @@
describe("The peripheral library", function()
local it_modem = peripheral.getType("top") == "modem" and it or pending
local multitype_peripheral = setmetatable({}, {
__name = "peripheral",
name = "top",
type = "modem",
types = { "modem", "inventory", modem = true, inventory = true },
})
describe("peripheral.isPresent", function()
it("validates arguments", function()
peripheral.isPresent("")
@ -26,6 +33,10 @@ describe("The peripheral library", function()
expect.error(peripheral.getType, {}):eq("bad argument #1 (table is not a peripheral)")
end)
it("returns nil when no peripheral is present", function()
expect(peripheral.getType("bottom")):eq(nil)
end)
it_modem("can get the type of a peripheral by side", function()
expect(peripheral.getType("top")):eq("modem")
end)
@ -33,6 +44,38 @@ describe("The peripheral library", function()
it_modem("can get the type of a wrapped peripheral", function()
expect(peripheral.getType(peripheral.wrap("top"))):eq("modem")
end)
it("can return multiple types", function()
expect({ peripheral.getType(multitype_peripheral) }):same { "modem", "inventory" }
end)
end)
describe("peripheral.hasType", function()
it("validates arguments", function()
peripheral.getType("")
expect.error(peripheral.hasType, nil):eq("bad argument #1 (expected string or table, got nil)")
expect.error(peripheral.hasType, {}, ""):eq("bad argument #1 (table is not a peripheral)")
expect.error(peripheral.hasType, ""):eq("bad argument #2 (expected string, got nil)")
end)
it("returns nil when no peripherals are present", function()
expect(peripheral.hasType("bottom", "modem")):eq(nil)
end)
it_modem("can check type of a peripheral by side", function()
expect(peripheral.hasType("top", "modem")):eq(true)
expect(peripheral.hasType("top", "not_a_modem")):eq(false)
end)
it_modem("can check the type of a wrapped peripheral (true)", function()
expect(peripheral.hasType(peripheral.wrap("top"), "modem")):eq(true)
end)
it("can check the type of a wrapped peripheral (fake)", function()
expect(peripheral.hasType(multitype_peripheral, "modem")):eq(true)
expect(peripheral.hasType(multitype_peripheral, "inventory")):eq(true)
expect(peripheral.hasType(multitype_peripheral, "something else")):eq(false)
end)
end)
describe("peripheral.getMethods", function()

21
src/web/copy-cat.d.ts vendored
View File

@ -1,21 +0,0 @@
import { h, Component, render, ComponentChild } from "preact";
export { h, Component, render };
export type ComputerAccess = unknown;
export type MainProps = {
hdFont?: boolean | string,
persistId?: number,
files?: { [filename: string]: string | ArrayBuffer },
label?: string,
width?: number,
height?: number,
resolve?: (computer: ComputerAccess) => void,
}
declare class Computer extends Component<MainProps, unknown> {
public render(props: MainProps, state: unknown): ComponentChild;
}
export { Computer };

View File

@ -4,15 +4,15 @@ import type { ComponentChild } from "preact";
import settingsFile from "./mount/.settings";
import startupFile from "./mount/startup.lua";
import exprTemplate from "./mount/expr_template.lua";
import exampleImage from "./mount/example.nfp";
import exampleNfp from "./mount/example.nfp";
import exampleNft from "./mount/example.nft";
const defaultFiles: { [filename: string]: string } = {
".settings": settingsFile,
"startup.lua": startupFile,
// TODO: Ideally this'd be in data/image.nfp or something, but copy-cat's
// dir bootstrapping doesn't cope with that right now.
"test-image.nfp": exampleImage
"data/example.nfp": exampleNfp,
"data/example.nft": exampleNft,
};
const clamp = (value: number, min: number, max: number): number => {

15
src/web/mount/example.nft Normal file
View File

@ -0,0 +1,15 @@
4
4
4  4
0 4  4>0 ls 4
0 4  drom/ 4 0
0 4  startup.lua 4
0 4  4> 0hello 4
0 4  aHello World! 0 4
0 4  4
0 4  4
0 4  4
0 4
0 4  4
0 4

45
src/web/typings.ts Normal file
View File

@ -0,0 +1,45 @@
declare module "*.lua" {
const contents: string;
export default contents;
}
declare module "*.nfp" {
const contents: string;
export default contents;
}
declare module "*.nft" {
const contents: string;
export default contents;
}
declare module "*.settings" {
const contents: string;
export default contents;
}
declare module "copycat/embed" {
import { h, Component, render, ComponentChild } from "preact";
export { h, Component, render };
export type ComputerAccess = unknown;
export type MainProps = {
hdFont?: boolean | string,
persistId?: number,
files?: { [filename: string]: string | ArrayBuffer },
label?: string,
width?: number,
height?: number,
resolve?: (computer: ComputerAccess) => void,
}
class Computer extends Component<MainProps, unknown> {
public render(props: MainProps, state: unknown): ComponentChild;
}
export { Computer };
}

View File

@ -22,11 +22,6 @@
"noFallthroughCasesInSwitch": true,
"importsNotUsedAsValues": "error",
"forceConsistentCasingInFileNames": true,
"paths": {
"copycat/embed": [
"src/web/copy-cat.d.ts"
],
}
},
"include": [
"src/web",