From 276956eed8520e11848e3b610d012805e3f62926 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Fri, 26 Nov 2021 20:58:58 +0000 Subject: [PATCH 01/10] Fix command block config not being read --- .../shared/peripheral/commandblock/CommandBlockPeripheral.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java index 350a60119..051938fbd 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java @@ -128,7 +128,7 @@ public class CommandBlockPeripheral implements IPeripheral, ICapabilityProvider public static void onCapability( AttachCapabilitiesEvent event ) { TileEntity tile = event.getObject(); - if( tile instanceof CommandBlockTileEntity ) + if( ComputerCraft.enableCommandBlock && tile instanceof CommandBlockTileEntity ) { CommandBlockPeripheral peripheral = new CommandBlockPeripheral( (CommandBlockTileEntity) tile ); event.addCapability( CAP_ID, peripheral ); From e247bd823e56f2970ef884031cc5cb769f78acc9 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Fri, 26 Nov 2021 21:12:20 +0000 Subject: [PATCH 02/10] Bump Gradle and Kotlin versions I think we need this for 1.18 --- build.gradle | 8 +- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 269 +++++++++++------- package-lock.json | 24 +- .../computercraft/core/apis/AsyncRunner.kt | 4 +- 5 files changed, 178 insertions(+), 129 deletions(-) diff --git a/build.gradle b/build.gradle index 5815efe51..7a4621507 100644 --- a/build.gradle +++ b/build.gradle @@ -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" } @@ -143,9 +143,9 @@ 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.kotlin:kotlin-reflect: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.kotlin:kotlin-reflect:1.6.0' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2' testModImplementation sourceSets.main.output diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a254..e750102e0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/gradlew b/gradlew index 744e882ed..1b6c78733 100755 --- a/gradlew +++ b/gradlew @@ -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" "$@" diff --git a/package-lock.json b/package-lock.json index 9a4786579..342b794ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -142,9 +142,9 @@ } }, "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" @@ -177,9 +177,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" @@ -353,9 +353,9 @@ "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==" }, "requirejs": { "version": "2.3.6", @@ -374,9 +374,9 @@ } }, "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" diff --git a/src/test/java/dan200/computercraft/core/apis/AsyncRunner.kt b/src/test/java/dan200/computercraft/core/apis/AsyncRunner.kt index 936fb6c62..51f8e90c7 100644 --- a/src/test/java/dan200/computercraft/core/apis/AsyncRunner.kt +++ b/src/test/java/dan200/computercraft/core/apis/AsyncRunner.kt @@ -15,8 +15,8 @@ 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 -import kotlin.time.seconds abstract class NullApiEnvironment : IAPIEnvironment { @@ -52,7 +52,7 @@ class AsyncRunner : NullApiEnvironment() { override fun queueEvent(event: String?, vararg args: Any?) { ComputerCraft.log.debug("Queue event $event ${args.contentToString()}") - if (!eventStream.offer(arrayOf(event, *args))) { + if (!eventStream.trySend(arrayOf(event, *args)).isSuccess) { throw IllegalStateException("Queue is full") } } From 8ffd45c66ee7cad5a52ad6b0703c9126d9797ba0 Mon Sep 17 00:00:00 2001 From: Lupus590 Date: Fri, 26 Nov 2021 21:13:15 +0000 Subject: [PATCH 03/10] "cc.pretty".pretty_print shortcut function (#965) --- .../lua/rom/modules/main/cc/pretty.lua | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua index f5cdeee6c..7a918df59 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua @@ -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, } From 7f3490591ddfd6ce691055e66cdbaffb0f00a4f4 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 27 Nov 2021 12:27:40 +0000 Subject: [PATCH 04/10] Some fixes to the web-based emulator - Bump copy-cat version to have support for initial files in directories and the blit fixes. - Add an example nft image and move example nfp into a data/ directory. - Fix nft parser not resetting colours on the start of each line. --- package-lock.json | 378 +++++++++++++++++- package.json | 2 +- rollup.config.js | 32 +- .../computercraft/lua/rom/apis/paintutils.lua | 2 +- .../lua/rom/modules/main/cc/image/nft.lua | 5 +- src/web/copy-cat.d.ts | 21 - src/web/index.tsx | 8 +- src/web/mount/example.nft | 15 + src/web/typings.ts | 45 +++ tsconfig.json | 5 - 10 files changed, 459 insertions(+), 54 deletions(-) delete mode 100644 src/web/copy-cat.d.ts create mode 100644 src/web/mount/example.nft create mode 100644 src/web/typings.ts diff --git a/package-lock.json b/package-lock.json index 342b794ba..eb8d3d0d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", @@ -150,6 +297,15 @@ "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", @@ -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", @@ -357,6 +688,15 @@ "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", "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz", @@ -382,6 +722,33 @@ "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", diff --git a/package.json b/package.json index 0af7fbfcf..84eaa5a43 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/rollup.config.js b/rollup.config.js index 725abb7ca..a4eebecbf 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -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(), ], }; diff --git a/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua b/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua index c3a6ce314..d0ed2ba24 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua @@ -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") diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/image/nft.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/image/nft.lua index 410934c5d..c188e7823 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/image/nft.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/image/nft.lua @@ -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 diff --git a/src/web/copy-cat.d.ts b/src/web/copy-cat.d.ts deleted file mode 100644 index b4a85d267..000000000 --- a/src/web/copy-cat.d.ts +++ /dev/null @@ -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 { - public render(props: MainProps, state: unknown): ComponentChild; -} - -export { Computer }; diff --git a/src/web/index.tsx b/src/web/index.tsx index f4e85033a..d795b3b2d 100644 --- a/src/web/index.tsx +++ b/src/web/index.tsx @@ -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 => { diff --git a/src/web/mount/example.nft b/src/web/mount/example.nft new file mode 100644 index 000000000..ce99422a3 --- /dev/null +++ b/src/web/mount/example.nft @@ -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 diff --git a/src/web/typings.ts b/src/web/typings.ts new file mode 100644 index 000000000..df7f62e12 --- /dev/null +++ b/src/web/typings.ts @@ -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 { + public render(props: MainProps, state: unknown): ComponentChild; + } + + export { Computer }; +} diff --git a/tsconfig.json b/tsconfig.json index b6dc1da3f..a1a8a0ed1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,11 +22,6 @@ "noFallthroughCasesInSwitch": true, "importsNotUsedAsValues": "error", "forceConsistentCasingInFileNames": true, - "paths": { - "copycat/embed": [ - "src/web/copy-cat.d.ts" - ], - } }, "include": [ "src/web", From 4f115491121f4b0f60afa59121c52893099ddece Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 27 Nov 2021 16:35:44 +0000 Subject: [PATCH 05/10] Remove space in fs API --- build.gradle | 2 +- src/main/java/dan200/computercraft/core/apis/FSAPI.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 7a4621507..d40ae19ce 100644 --- a/build.gradle +++ b/build.gradle @@ -149,7 +149,7 @@ dependencies { testModImplementation sourceSets.main.output - cctJavadoc 'cc.tweaked:cct-javadoc:1.4.1' + cctJavadoc 'cc.tweaked:cct-javadoc:1.4.2' } // Compile tasks diff --git a/src/main/java/dan200/computercraft/core/apis/FSAPI.java b/src/main/java/dan200/computercraft/core/apis/FSAPI.java index 9e1059545..6c3fd3522 100644 --- a/src/main/java/dan200/computercraft/core/apis/FSAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/FSAPI.java @@ -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, rom/*/command* will look for any path starting with * {@code command} inside any subdirectory of {@code /rom}. * * @param path The wildcard-qualified path to search for. From 306e06a79a172abf6a3cb880aa96929992a57a31 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 28 Nov 2021 12:26:39 +0000 Subject: [PATCH 06/10] Do not allow transferring into removed blocks See #893. --- .../shared/peripheral/generic/methods/FluidMethods.java | 3 +++ .../shared/peripheral/generic/methods/InventoryMethods.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java index 3a66f809a..e6878d651 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java @@ -13,6 +13,7 @@ import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.shared.peripheral.generic.data.FluidData; import net.minecraft.fluid.Fluid; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ResourceLocation; import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.common.util.LazyOptional; @@ -155,6 +156,8 @@ public class FluidMethods implements GenericSource @Nullable private static IFluidHandler extractHandler( @Nullable Object object ) { + if( (object instanceof TileEntity) && ((TileEntity) object).isRemoved() ) return null; + if( object instanceof ICapabilityProvider ) { LazyOptional cap = ((ICapabilityProvider) object).getCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY ); diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java index d247d7aee..c85a77d72 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java @@ -14,6 +14,7 @@ import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.shared.peripheral.generic.data.ItemData; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ResourceLocation; import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.common.util.LazyOptional; @@ -257,6 +258,8 @@ public class InventoryMethods implements GenericSource @Nullable private static IItemHandler extractHandler( @Nullable Object object ) { + if( (object instanceof TileEntity) && ((TileEntity) object).isRemoved() ) return null; + if( object instanceof ICapabilityProvider ) { LazyOptional cap = ((ICapabilityProvider) object).getCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY ); From 9d44f1ca661bf67845be572da35e2288bf031ae8 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 28 Nov 2021 12:47:08 +0000 Subject: [PATCH 07/10] Make capability invalidation callbacks less strict Forge!! *shakes fist*. --- .../dan200/computercraft/shared/Peripherals.java | 4 ++-- .../shared/computer/blocks/TileComputerBase.java | 5 ++--- .../generic/GenericPeripheralProvider.java | 11 +++-------- .../modem/wired/WiredModemLocalPeripheral.java | 3 +-- .../computercraft/shared/util/CapabilityUtil.java | 12 ++++++++++-- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/main/java/dan200/computercraft/shared/Peripherals.java b/src/main/java/dan200/computercraft/shared/Peripherals.java index 1f809ccb9..86978d3e5 100644 --- a/src/main/java/dan200/computercraft/shared/Peripherals.java +++ b/src/main/java/dan200/computercraft/shared/Peripherals.java @@ -38,13 +38,13 @@ public final class Peripherals } @Nullable - public static IPeripheral getPeripheral( World world, BlockPos pos, Direction side, NonNullConsumer> invalidate ) + public static IPeripheral getPeripheral( World world, BlockPos pos, Direction side, NonNullConsumer invalidate ) { return World.isInWorldBounds( pos ) && !world.isClientSide ? getPeripheralAt( world, pos, side, invalidate ) : null; } @Nullable - private static IPeripheral getPeripheralAt( World world, BlockPos pos, Direction side, NonNullConsumer> invalidate ) + private static IPeripheral getPeripheralAt( World world, BlockPos pos, Direction side, NonNullConsumer invalidate ) { TileEntity block = world.getBlockEntity( pos ); if( block != null ) diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java index e87d78a09..9afcebff5 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java @@ -39,7 +39,6 @@ import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.TranslationTextComponent; import net.minecraft.world.World; -import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.NonNullConsumer; import javax.annotation.Nonnull; @@ -58,7 +57,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT private boolean on = false; boolean startOn = false; private boolean fresh = false; - private final NonNullConsumer>[] invalidate; + private final NonNullConsumer[] invalidate; private final ComputerFamily family; @@ -69,7 +68,7 @@ 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>[] invalidate = this.invalidate = new NonNullConsumer[6]; + NonNullConsumer[] invalidate = this.invalidate = new NonNullConsumer[6]; for( Direction direction : Direction.values() ) { invalidate[direction.ordinal()] = o -> updateInput( direction ); diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java index d6918a1fd..54ab72c2f 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java @@ -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.tileentity.TileEntity; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; @@ -34,7 +35,7 @@ public class GenericPeripheralProvider } @Nullable - public static IPeripheral getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side, NonNullConsumer> invalidate ) + public static IPeripheral getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side, NonNullConsumer invalidate ) { TileEntity tile = world.getBlockEntity( pos ); if( tile == null ) return null; @@ -52,7 +53,7 @@ public class GenericPeripheralProvider if( capabilityMethods.isEmpty() ) return; saturated.addMethods( contents, capabilityMethods ); - wrapper.addListener( cast( invalidate ) ); + CapabilityUtil.addListener( wrapper, invalidate ); } ); } @@ -91,10 +92,4 @@ public class GenericPeripheralProvider } } } - - @SuppressWarnings( { "unchecked", "rawtypes" } ) - private static NonNullConsumer cast( NonNullConsumer consumer ) - { - return (NonNullConsumer) consumer; - } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java index 156bc72a1..80ddbad55 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java @@ -15,7 +15,6 @@ import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.common.util.Constants; -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> invalidate; + private final NonNullConsumer invalidate; public WiredModemLocalPeripheral( @Nonnull Runnable invalidate ) { diff --git a/src/main/java/dan200/computercraft/shared/util/CapabilityUtil.java b/src/main/java/dan200/computercraft/shared/util/CapabilityUtil.java index e542294d5..af5bf42e4 100644 --- a/src/main/java/dan200/computercraft/shared/util/CapabilityUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/CapabilityUtil.java @@ -35,12 +35,20 @@ public final class CapabilityUtil } } + public static void addListener( LazyOptional p, NonNullConsumer> invalidate ) + { + // We can make this safe with invalidate::accept, but then we're allocating it's just kind of absurd. + @SuppressWarnings( "unchecked" ) + NonNullConsumer> safeInvalidate = (NonNullConsumer>) invalidate; + p.addListener( safeInvalidate ); + } + @Nullable - public static T unwrap( LazyOptional p, NonNullConsumer> invalidate ) + public static T unwrap( LazyOptional p, NonNullConsumer> invalidate ) { if( !p.isPresent() ) return null; - p.addListener( invalidate ); + addListener( p, invalidate ); return p.orElseThrow( NullPointerException::new ); } From 298f3393762c25001c45a49e4209585553ce24fe Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 28 Nov 2021 20:03:27 +0000 Subject: [PATCH 08/10] Invalidate peripherals during the computer's tick instead - Capability invalidation and tile/block entity changes set a dirty bit instead of refetching the peripheral immediately. - Then on the block's tick we recompute the peripheral if the dirty bit is set. Fixes #696 and probably fixes #882. Some way towards #893, but not everything yet. This is probably going to break things horribly. Let's find out! --- .../computer/blocks/BlockComputerBase.java | 2 +- .../computer/blocks/TileComputerBase.java | 174 +++++++++--------- .../peripheral/modem/wired/TileCable.java | 15 +- .../modem/wired/TileWiredModemFull.java | 21 ++- .../shared/turtle/blocks/TileTurtle.java | 4 +- .../shared/turtle/core/TurtleBrain.java | 23 +-- .../shared/util/RedstoneUtil.java | 27 +++ 7 files changed, 162 insertions(+), 104 deletions(-) diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputerBase.java b/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputerBase.java index b9febd5e6..1904484a2 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputerBase.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputerBase.java @@ -54,7 +54,7 @@ public abstract class BlockComputerBase extends Bloc super.onPlace( state, world, pos, oldState, isMoving ); TileEntity tile = world.getBlockEntity( pos ); - if( tile instanceof TileComputerBase ) ((TileComputerBase) tile).updateInput(); + if( tile instanceof TileComputerBase ) ((TileComputerBase) tile).updateInputsImmediately( ); } @Override diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java index 9afcebff5..1a2a9f07f 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java @@ -19,9 +19,6 @@ import dan200.computercraft.shared.util.DirectionUtil; import dan200.computercraft.shared.util.RedstoneUtil; import joptsimple.internal.Strings; import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; -import net.minecraft.block.RedstoneDiodeBlock; -import net.minecraft.block.RedstoneWireBlock; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.container.INamedContainerProvider; import net.minecraft.item.ItemStack; @@ -38,7 +35,6 @@ import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.TranslationTextComponent; -import net.minecraft.world.World; import net.minecraftforge.common.util.NonNullConsumer; import javax.annotation.Nonnull; @@ -57,6 +53,8 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT private boolean on = false; boolean startOn = false; private boolean fresh = false; + + private int invalidSides = 0; private final NonNullConsumer[] invalidate; private final ComputerFamily family; @@ -71,7 +69,8 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT NonNullConsumer[] 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,45 +142,51 @@ 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 ); } @Override public void tick() { - if( !getLevel().isClientSide ) + if( getLevel().isClientSide ) return; + + ServerComputer computer = createServerComputer(); + + if( invalidSides != 0 ) { - ServerComputer computer = createServerComputer(); - if( computer == null ) return; - - // If the computer isn't on and should be, then turn it on - if( startOn || (fresh && on) ) + for( Direction direction : DirectionUtil.FACINGS ) { - computer.turnOn(); - startOn = false; + if( (invalidSides & (1 << direction.ordinal())) != 0 ) refreshPeripheral( computer, direction ); } - - computer.keepAlive(); - - fresh = false; - computerID = computer.getID(); - label = computer.getLabel(); - on = computer.isOn(); - - // Update the block state if needed. We don't fire a block update intentionally, - // as this only really is needed on the client side. - updateBlockState( computer.getState() ); - - // TODO: This should ideally be split up into label/id/on (which should save NBT and sync to client) and - // redstone (which should update outputs) - if( computer.hasOutputChanged() ) updateOutput(); } + + // If the computer isn't on and should be, then turn it on + if( startOn || (fresh && on) ) + { + computer.turnOn(); + startOn = false; + } + + computer.keepAlive(); + + fresh = false; + computerID = computer.getID(); + label = computer.getLabel(); + on = computer.isOn(); + + // Update the block state if needed. We don't fire a block update intentionally, + // as this only really is needed on the client side. + updateBlockState( computer.getState() ); + + // TODO: This should ideally be split up into label/id/on (which should save NBT and sync to client) and + // redstone (which should update outputs) + if( computer.hasOutputChanged() ) updateOutput(); } protected abstract void updateBlockState( ComputerState newState ); @@ -226,89 +231,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 ) ) - { - IPeripheral peripheral = Peripherals.getPeripheral( getLevel(), offset, offsetSide, invalidate[dir.ordinal()] ); - computer.setPeripheral( localDir, peripheral ); - } + computer.setRedstoneInput( localDir, RedstoneUtil.getRedstoneInput( level, targetPos, dir ) ); + computer.setBundledRedstoneInput( localDir, BundledRedstone.getOutput( getLevel(), targetPos, offsetSide ) ); + } + + private void refreshPeripheral( @Nonnull ServerComputer computer, Direction dir ) + { + 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 RedstoneDiodeBlock#calculateInputStrength(World, 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( World 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 ) { @@ -358,9 +354,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 ) @@ -368,18 +365,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 ); diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java index 3a0f03ddf..394e55f0d 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java @@ -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; @@ -239,12 +240,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(); @@ -324,6 +333,8 @@ public class TileCable extends TileGeneric elementCap = CapabilityUtil.invalidate( elementCap ); } + if( invalidPeripheral ) refreshPeripheral(); + if( modem.getModemState().pollChanged() ) updateBlockState(); if( !connectionsFormed ) diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java index e8a2c0728..1f2523fe8 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java @@ -108,13 +108,15 @@ public class TileWiredModemFull extends TileGeneric private final NonNullConsumer> connectedNodeChanged = x -> connectionsChanged(); + private int invalidSides = 0; + public TileWiredModemFull( TileEntityType type ) { super( type ); 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 ) diff --git a/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java b/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java index fb89ae33d..7b0ce1994 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java +++ b/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java @@ -322,8 +322,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(); } diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java index 7678f0663..1af843524 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java @@ -333,16 +333,17 @@ public class TurtleBrain implements ITurtleAccess TileTurtle newTurtle = (TileTurtle) newTile; newTurtle.setLevelAndPosition( world, pos ); newTurtle.transferStateFrom( oldOwner ); - newTurtle.createServerComputer().setWorld( world ); - newTurtle.createServerComputer().setPosition( pos ); + + ServerComputer computer = newTurtle.createServerComputer(); + computer.setWorld( 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; } } @@ -614,16 +615,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(); - } + owner.updateBlock(); + + // 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 ) @@ -645,7 +646,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() ); } diff --git a/src/main/java/dan200/computercraft/shared/util/RedstoneUtil.java b/src/main/java/dan200/computercraft/shared/util/RedstoneUtil.java index 41c632dc9..021032d87 100644 --- a/src/main/java/dan200/computercraft/shared/util/RedstoneUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/RedstoneUtil.java @@ -6,6 +6,9 @@ package dan200.computercraft.shared.util; import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneDiodeBlock; +import net.minecraft.block.RedstoneWireBlock; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -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 RedstoneDiodeBlock#getInputSignal(World, BlockPos, BlockState) + */ + public static int getRedstoneInput( World 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( World world, BlockPos pos, Direction side ) { // Propagate ordinary output. See BlockRedstoneDiode.notifyNeighbors From 53811f8169466601641a69aee8743a25d997b9e9 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Mon, 29 Nov 2021 17:37:30 +0000 Subject: [PATCH 09/10] Allow peripherals to have multiple types (#963) Peripherals can now have multiple types: - A single primary type. This is the same as the current idea of a type - some identifier which (mostly) uniquely identifies this kind of peripheral. For instance, "speaker" or "minecraft:chest". - 0 or more "additional" types. These are more like traits, and describe what other behaviour the peripheral has - is it an inventory? Does it supply additional peripherals (like a wired modem)?. This is mostly intended for the generic peripheral system, but it might prove useful elsewhere too - we'll have to see! - peripheral.getType (and modem.getTypeRemote) now returns 1 or more values, rather than exactly one. - Add a new peripheral.hasType (and modem.hasTypeRemote) function which determines if a peripheral has the given type (primary or additional). - Change peripheral.find and all internal peripheral methods to use peripheral.hasType instead. - Update the peripherals program to show all types This effectively allows you to do things like `peripheral.find("inventory")` to find all inventories. This also rewrites the introduction to the peripheral API, hopefully making it a little more useful. --- doc/events/redstone.md | 2 +- .../api/peripheral/IPeripheral.java | 14 ++ .../api/peripheral/PeripheralType.java | 75 +++++++- .../core/apis/PeripheralAPI.java | 26 ++- .../peripheral/generic/GenericPeripheral.java | 12 +- .../generic/GenericPeripheralProvider.java | 12 +- .../generic/methods/EnergyMethods.java | 12 +- .../generic/methods/FluidMethods.java | 12 +- .../generic/methods/InventoryMethods.java | 12 +- .../modem/wired/WiredModemPeripheral.java | 32 +++- .../computercraft/shared/util/LuaUtil.java | 23 +++ .../computercraft/lua/rom/apis/peripheral.lua | 181 ++++++++++++++---- .../lua/rom/programs/peripherals.lua | 2 +- .../test-rom/spec/apis/peripheral_spec.lua | 43 +++++ 14 files changed, 405 insertions(+), 53 deletions(-) create mode 100644 src/main/java/dan200/computercraft/shared/util/LuaUtil.java diff --git a/doc/events/redstone.md b/doc/events/redstone.md index 44eda304a..0c199ee5d 100644 --- a/doc/events/redstone.md +++ b/doc/events/redstone.md @@ -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: diff --git a/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java b/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java index 9c7f2bea5..201888e6f 100644 --- a/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java +++ b/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java @@ -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 getAdditionalTypes() + { + return Collections.emptySet(); + } + /** * Is called when when a computer is attaching to the peripheral. * diff --git a/src/main/java/dan200/computercraft/api/peripheral/PeripheralType.java b/src/main/java/dan200/computercraft/api/peripheral/PeripheralType.java index 2780534c2..c80526eda 100644 --- a/src/main/java/dan200/computercraft/api/peripheral/PeripheralType.java +++ b/src/main/java/dan200/computercraft/api/peripheral/PeripheralType.java @@ -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 additionalTypes; - public PeripheralType( String type ) + public PeripheralType( String type, Set 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 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 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 getAdditionalTypes() + { + return additionalTypes; + } } diff --git a/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java b/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java index 02e382090..35e20ca03 100644 --- a/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java @@ -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 additionalTypes; private final Map 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 getAdditionalTypes() + { + return additionalTypes; + } + public Collection 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; } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java index 8703f687e..3852468c3 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java @@ -18,18 +18,21 @@ import net.minecraft.util.ResourceLocation; 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 additionalTypes; private final TileEntity tile; private final List methods; - GenericPeripheral( TileEntity tile, String name, List methods ) + GenericPeripheral( TileEntity tile, String name, Set additionalTypes, List 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 getAdditionalTypes() + { + return additionalTypes; + } + @Nullable @Override public Object getTarget() diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java index 54ab72c2f..3832f0172 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java @@ -20,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 { @@ -62,15 +60,16 @@ public class GenericPeripheralProvider private static class GenericPeripheralBuilder { - String name; - final ArrayList methods = new ArrayList<>( 0 ); + private String name; + private final Set additionalTypes = new HashSet<>( 0 ); + private final ArrayList methods = new ArrayList<>( 0 ); IPeripheral toPeripheral( TileEntity 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> methods ) @@ -89,6 +88,7 @@ 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() ); } } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/EnergyMethods.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/EnergyMethods.java index 4bf894a24..186bcaad3 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/EnergyMethods.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/EnergyMethods.java @@ -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.util.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() diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java index e6878d651..5b034b3c1 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java @@ -6,11 +6,12 @@ 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.fluid.Fluid; import net.minecraft.tileentity.TileEntity; @@ -35,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() diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java index c85a77d72..08fa78c52 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java @@ -6,11 +6,12 @@ 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.ItemData; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; @@ -36,8 +37,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() diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemPeripheral.java index 554a4478d..b71d6ae60 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemPeripheral.java @@ -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.World; 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. + * + *
Important: This function only appears on wired modems. Check {@link #isWireless} + * returns false before calling it.
+ * + * @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() ) }; } /** @@ -308,6 +331,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW private final String name; private final String type; + private final Set additionalTypes; private final Map methodMap; private volatile boolean attached; @@ -321,6 +345,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 ); } @@ -354,6 +379,11 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW return type; } + public Set getAdditionalTypes() + { + return additionalTypes; + } + public Collection getMethodNames() { return methodMap.keySet(); diff --git a/src/main/java/dan200/computercraft/shared/util/LuaUtil.java b/src/main/java/dan200/computercraft/shared/util/LuaUtil.java new file mode 100644 index 000000000..79cf1a445 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/util/LuaUtil.java @@ -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; + } +} diff --git a/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua b/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua index 5266678e7..f3c74a1c2 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua @@ -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) diff --git a/src/main/resources/data/computercraft/lua/rom/programs/peripherals.lua b/src/main/resources/data/computercraft/lua/rom/programs/peripherals.lua index 290619614..17d4877e8 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/peripherals.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/peripherals.lua @@ -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") diff --git a/src/test/resources/test-rom/spec/apis/peripheral_spec.lua b/src/test/resources/test-rom/spec/apis/peripheral_spec.lua index 4fea6ddf0..c64c042ce 100644 --- a/src/test/resources/test-rom/spec/apis/peripheral_spec.lua +++ b/src/test/resources/test-rom/spec/apis/peripheral_spec.lua @@ -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() From 5927e9bb1002707d80754878c67e89119f68ebe3 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Mon, 29 Nov 2021 18:54:54 +0000 Subject: [PATCH 10/10] Bump CC:T version --- gradle.properties | 2 +- .../computercraft/lua/rom/help/changelog.md | 27 +++++++++++++++++ .../computercraft/lua/rom/help/whatsnew.md | 29 +++++++++++++++---- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/gradle.properties b/gradle.properties index 421a3fb44..300298aca 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # Mod properties -mod_version=1.98.2 +mod_version=1.99.0 # Minecraft properties (update mods.toml when changing) mc_version=1.16.5 diff --git a/src/main/resources/data/computercraft/lua/rom/help/changelog.md b/src/main/resources/data/computercraft/lua/rom/help/changelog.md index d7886c750..7aa09fcdf 100644 --- a/src/main/resources/data/computercraft/lua/rom/help/changelog.md +++ b/src/main/resources/data/computercraft/lua/rom/help/changelog.md @@ -1,3 +1,30 @@ +# 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). +* 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. + # New features in CC: Tweaked 1.98.2 * Add JP translation (MORIMORI0317) diff --git a/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md b/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md index cdffaca78..31d94fbfe 100644 --- a/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md +++ b/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md @@ -1,9 +1,28 @@ -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). +* Various translation updates (MORIMORI3017, Ale2Bit, mindy15963) -Several bug fixes: -* Fix volume speaker sounds are played at. +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. Type "help changelog" to see the full version history.