1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-11-04 07:32:59 +00:00

Compare commits

..

84 Commits

Author SHA1 Message Date
SquidDev
46d78af068 Fix changelog being out-of-sync 2019-08-04 11:05:44 +01:00
SquidDev
eb5cff1045 Alright, let's do this one last time 2019-08-04 09:27:48 +01:00
SquidDev
35c7792aa2 Limit the titles of printed pages
Just enforce the same restrictions as we do for computer/disk labels.
2019-08-04 08:59:44 +01:00
Jonathan Coates
521688d630 Merge pull request #183 from SquidDev-CC/feature/thread-safe-inventories 2019-08-01 14:30:32 +01:00
SquidDev
75e2845c01 Remove synchronized from turtle inventory code
These should never be called off the server thread, so this doesn't make
much difference.
2019-08-01 14:09:57 +01:00
SquidDev
2f96283286 Make disk drives thread-safe 2019-08-01 13:48:03 +01:00
SquidDev
cbe6e9b5f5 Make printers thread-safe 2019-08-01 13:48:03 +01:00
SquidDev
2ab79cf474 Version bumps 'n stuff 2019-07-30 15:48:05 +01:00
SquidDev
6ce34aba79 A quick attempt at fixing Travis
Oracle JDK 8 is EOL (I think at least).
2019-07-30 15:25:14 +01:00
SquidDev
5eeb320b60 Include all mods within a resource mount
This is the behaviour on 1.14 already, so it makes sense to backport to
1.12.

Any mod may now insert files into assets/computercraft/lua/rom, and
they'll be automatically added to the default ROM mount. This allows
other mods to easily register new programs or autorun files.

See #242
2019-07-30 15:20:08 +01:00
SquidDev
93310850d2 Use the "cc" module namespace instead of "craftos"
This is what we actually discussed in the issue, and I failed to
remember.
2019-07-27 11:34:59 +01:00
powerboat9
a2880b12ca Do not refuel beyond the turtle limit (#274) 2019-07-24 08:15:02 +01:00
powerboat9
303b57779a Fix turtles harvesting blocks when they shouldn't (#276)
harvestBlock should only be called when removedByPlayer and canHarvestBlock
return true, otherwise we run the risk of causing dupe bugs.

See #273.
2019-07-17 09:23:14 +01:00
SquidDev
6279816ecc Try using the HTTP one instead 2019-07-15 08:45:22 +01:00
SquidDev
4ae77261fa Petty changes because I'm petty 2019-07-13 08:29:28 +01:00
liquid
4b7d843b78 Removed term.getLine 2019-07-13 01:45:16 -05:00
liquid
1c28df65c3 Fixed style errors 2019-07-12 23:56:49 -05:00
liquid
85b740f484 Added term.getLine and window.getLine 2019-07-12 22:54:37 -05:00
SquidDev
f9929cb27d Fix the signature of loadfile
Lua 5.2+ uses loadfile(filename, mode, env), not loadfile(filename,
env). While this is a minor incompatibility, it'd be nice to be
consistent as much as possible.

We try to handle the incorrect case too, as obviously we don't want to
break existing programs.
2019-07-12 22:04:28 +01:00
SquidDev
bafab1ac07 Expose expect as a module (#267)
This moves expect from the bios into a new craftos.expect module,
removing the internal _G["~expect"] definition. Apparently people were
using this irrespective of the "don't use this" comment, so we need to
find another solution.

While this does introduce some ugliness (having to load the module in
weird ways for programs, duplicating the expect function in memory), it
does allow people to use the function in a supported way, and removes
the global ugliness.
2019-07-09 08:04:49 +01:00
JakobDev
e05c262468 Add more tests (#253)
I'm not entirely sure how useful all of these will be yet - still
trying to work out what/when to test things, but hopefully this'll
be a useful datapoint.
2019-07-08 09:24:05 +01:00
SquidDev
7a3f7d3bba I'm a muppet
Even worse, I enabled branch protection for some reason, and so can't
force push and pretend this never happened.
2019-06-29 15:49:14 +01:00
SquidDev
95aa48c456 Allow running expectations against stubbed functions
Co-authored-by: hydraz <urn@semi.works>
2019-06-29 15:37:41 +01:00
SquidDev
904a168d5c Fix incorrect explosion check
We should block explosions if the turtle is advanced /or/ if it's from
a fireball or entity, not if both.

Fixes #257
2019-06-21 18:53:28 +01:00
JakobDev
724441eddc Change URLs in build.gradle to https (#259) 2019-06-21 16:51:55 +01:00
SquidDev
f68ab3edd1 Minor tweaks to build script
Mostly just rearranging. Bump JUnit version in an attempt to fix test
outputs, but it appears this is a mix of gradle/gradle#5975 and
gradle/gradle#4438.
2019-06-15 11:05:45 +01:00
SquidDev
29dce26bf6 Clean up checkstyle warning
Fixes #251, closes #252
2019-06-14 20:55:32 +01:00
JakobDev
717ab69093 Add a few Checks (#248)
- Add some basic tests for several built-in programs.
 - Preserve the state of the shell between tests
2019-06-14 08:15:12 +01:00
SquidDev
138a2cf08f Merge branches 'copycheck' and 'renamefix' 2019-06-13 08:03:35 +01:00
JakobDev
81daf82647 Add Checks to copy.lua 2019-06-13 08:03:01 +01:00
JakobDev
f3798bfb63 Improve rename.lua's argument validation 2019-06-13 08:00:06 +01:00
SquidDev
bc07dfad2e Make sure all writeDescription methods are pure
We don't want to be firing block updates or anything from here! That
runs the ricks of causing CMEs and the like.

Fixes #245
2019-06-12 21:03:11 +01:00
JakobDev
309cbdb8be Add wget run (#218)
Equivalent to `pastebin run`, but allows running arbitrary URLs
instead.

Is this a little questionable? Yes - people shouldn't be downloading
and running code from the internet. But hey, people do that already,
so we might as well make it convenient.
2019-06-08 14:49:42 +01:00
SquidDev
a0e7c4a74c Add a little bit of source code checking to Gradle
- Adds a CheckStyle configuration which is pretty similar to CC's
   existing one.
 - Add the Gradle license plugin.
 - Ensure the existing source code is compatible with these additional
   checks.

See #239
2019-06-08 00:28:03 +01:00
Lignum
7d428030df Fix some warnings (#235)
Remove some unused code and fix a bunch of warnings
2019-06-07 14:35:17 +01:00
JakobDev
00c395f689 Add Check to delete.lua (#236) 2019-06-06 16:43:48 +01:00
SquidDev
d8e1c73d26 Ensure files cannot be closed multiple times
Also fix an NPE if we try to close them twice.

Fixes #230
2019-06-04 21:34:19 +01:00
SquidDev
ffa4cc241b Add a test utility for capturing program output
- Make mcfly's stubbing system a little more fault-tolerant.
 - Add a small utility function which redirects print, printError and
   write to capture their output, rather than printing to the terminal.
   This can then be matched against in order to determine a program's
   output.
   It's a little flakey - you can't use it multiple times in an it
   block, etc... but it's a nice feature.
 - Add a small couple of tests to delete as a proof-of-concept.
2019-06-03 20:33:09 +01:00
SquidDev
6f1b740c8f Un-localise paths in mount error messages
This is probably a little more complex than it needs to be, as we try to
handle the mounts as well (which generally don't error).

Fixes #229
2019-06-03 19:58:16 +01:00
SquidDev
3406ba3ebf Fix a build from a clean state failing 2019-06-02 18:48:36 +01:00
SquidDev
6b81bcf334 Bump version 2019-06-02 16:20:15 +01:00
SquidDev
acac70675d Add link to CF on the README
Not sure how useful this is - people are far more likely to come across
the CurseForge page than the GH one, but there's no harm I guess.

Closes #227
2019-06-01 15:15:39 +01:00
JakobDev
56434259c1 Add Checks for Turtle and Pocket programs (#226)
Error if turtle and pocket computers programs are not run on
their respective devices.
2019-06-01 12:48:33 +01:00
JakobDev
da7e4b9016 Add more MOTD messages. (#225) 2019-06-01 12:41:01 +01:00
SquidDev
d4b8650d21 Use select('#', ...) instead of ipairs on varargs
This means we don't discard any value after nil, meaning that
calls such as colors.combine(nil, 1, 2, 3) will error, rather
than returning 0.
2019-06-01 10:12:10 +01:00
SquidDev
17645a79f0 Fix type check on rednet.lookup
Fixes #224
2019-06-01 09:23:18 +01:00
SquidDev
ce1f14a010 Fix a couple of problems in the release buildscript
Otherwise that went pretty smoothly!
2019-05-31 13:56:48 +01:00
SquidDev
43050426de Bump version
First time using the new changelog tooling, let's see how this goes.
2019-05-31 13:53:15 +01:00
SquidDev
b05f60c98b Add small task to verify changelog is correct
Also use the changelog contents as the GH release notes
2019-05-31 10:19:24 +01:00
SquidDev
c44c560f96 Update changelog and whatsnew
- Convert existing changelog over to use Markdown. This mostly involves
   wrapping code in backticks, and marking things as headers where
   appropriate.
 - Copy all of CC:T's release notes over to the changelog. This is
   somewhat more verbose than Dan's notes, but keeping them in sync
   seems reasonable (and allows for automation!).
2019-05-31 09:35:45 +01:00
SquidDev
e839ef54af Forbid mutating empty_json_array
It's definitely still possible (rawset), but should prevent people
accidentally trying to modify it when they shouldn't.
2019-05-30 20:01:09 +01:00
SquidDev
0cb659d78c Ensure require is usable within the REPL
As 'require' operates relative to the current program's directory,
rather than the current directory, it meant we were trying to load files
from /rom/programs.

This is never a good idea, so we add the current directory to the
package path, allowing you to use require as one'd expect.
2019-05-30 19:52:55 +01:00
hydraz
9048deeb95 Add a function for type-checking arguments (#207)
- Define an expect(index, actual_value, types...) helper function which
   takes an argument index, value and list of permissable types and
   ensures the value is of one of those types.
   If not, it will produce an error message with the expected and actual
   type, as well as the argument number and (if available) the function
   name.
 - Expose expect in the global scope as _G["~expect"], hopefully making
   it clear it is internal.
 - Replace most manual type checks with this helper method.
 - Write tests to ensure this argument validation works as expected

Also fix a couple of bugs exposed by this refactor and the subsequent
tests:
 - Make rednet checks a little more strict - rednet.close(false) is no
   longer valid.
 - Error when attempting to redirect the terminal to itself 
   (term.redirect(term)).
2019-05-30 19:36:28 +01:00
SquidDev
5592ebae7d A small bit of housekeeping
- Add a link to discord and the forums. Oh goodness, I hope this
   doesn't count as making things official.
   I've had a couple of people email me with support requests, which is
   /fine/, but there's better channels for it!
 - Add a couple of PR templates
   - Ask people to write tests for CraftOS changes. This is a standard
     I'm trying to impose on myself, so seems reasonable to impose on
     everyone. Sorry!
   - Require same levels of explanation for PRs as we do for issues.
 - Try to expand on the feature request "rationale" section a little.
   Just trying to explan my process a little bit more.
2019-05-30 08:44:15 +01:00
SquidDev
b076c32fd1 Merge remote-tracking branch 'origin' 2019-05-30 07:57:30 +01:00
SquidDev
a48f1e310f Add a custom renderer for monitor block highlights (#220)
This only renders the bounding box on non-screen edges of the monitor,
meaning you have an uninterrupted view of the screen when hovering
hover.

Closes #219
2019-05-30 07:51:35 +01:00
SquidDev
19aca001d7 A couple of stability improvements to ComputerThread
- Be more generous in what errors we catch, handling some of the more
   "recoverable" ones.
 - Should a runner crash, attempt to restart it.
2019-05-29 09:03:31 +01:00
SquidDev
114f913bf8 Rework rendering of in-hand pocket computers (#215)
Rendering an item worked in principle, but had several caveats:
 - The terminal did not fit well within the item's texture, so we had a
   rather large border.
 - The "correctness" of this was very tied to Minecraft's item rendering
   code. This changed a little in 1.13, causing problems like #208.

Instead we effectively reuse the computer GUI rendering code, though
also handling coloured pocket computers and rendering the modem light.

This fixes #208, and hopefully fixes #212.
2019-05-26 15:24:37 +01:00
Daniel Ratcliffe
1c9810890a Merge pull request #577 from SquidDev-CC/feature/get-blink
Add .getCursorBlink to monitors and terminals
2019-05-26 00:14:02 +01:00
SquidDev
b11beb508b Convert mkdir motd note to plural
*shrug*
2019-05-25 19:17:10 +01:00
SquidDev
af8d4da594 Ensure the test name is ASCII
Gradle really doesn't like unicode apparently
2019-05-25 18:11:14 +01:00
JakobDev
a81db2cda6 Allow multiple arguments for mkdir (#216) 2019-05-25 18:04:56 +01:00
SquidDev
99bdff0f92 Automatically label issues created with templates 2019-05-25 14:53:48 +01:00
hydraz
5b0ce7410d Make delete delete many files (#210)
Actually, many *globs*. It additionally prints the glob if no files
matched it, since that's clearer.

Also move the ComputerTestDelegate's filesystem to be disk-based. This
is what actual computers use, and the MemoryMount is a little broken.
2019-05-25 09:02:42 +01:00
SquidDev
d5ea22d1a0 Jolly good job 2019-05-24 22:43:59 +01:00
SquidDev
210f3fa9e2 Add mostly standard compliant os.time/os.date
- os.time, when given a table, will act the same as PUC Lua - returning
   the seconds since the epoch. We preserve the previous string/nil
   behaviour though - os.epoch("local") is equivalent to PUC's
   os.time().

 - os.date will now act accept a string and (optional) time, returning
   an appropriate table.

Somewhat resolves the madness which was dan200/ComputerCraft#183, and
hopefully (though probably not) makes @Vexatos happy.
2019-05-24 22:33:56 +01:00
SquidDev
d661cfa88b Set the arg table within shell.run
This makes us a little more compatible with Lua
2019-05-24 18:42:19 +01:00
SquidDev
68bf3a71dc Lazilly instantiate the terminal packet
This means we don't create an NBT tag every tick if the screen is
updating, unless we actually need to.
2019-05-24 18:32:56 +01:00
SquidDev
3cdb12d293 Stop shipping jars with GH releases
The build appears to be a little bit broken, I haven't got willpower to
look into it, and it's not really used anyway.
2019-05-14 07:44:19 +01:00
Wilma456 (Jakob0815)
ad33acd7d1 Add MOTD (#175) 2019-05-13 16:54:19 +01:00
SquidDev
0ec3884e98 Handle websockets which close while receiving
This changes the previous behaviour a little, but hopefully is more
sane:

 - Only require the socket to be open when first calling receive. This
   means if it closes while receving, you won't get an error.

   This behaviour is still not perfect - the socket could have closed,
   but the event not reached the user yet, but it's better.

 - Listen to websocket_close events while receiving, and return null
   should it match ours.

See #201
2019-05-08 08:16:21 +01:00
SquidDev
7f2471d6b2 Fix list-like config options not reloading
Closes #199
2019-05-08 08:00:07 +01:00
SquidDev
8fafec4915 Fix budget growing too much
We were using += instead of =, meaning the budget always grew,
rather than growing while there was still space. As a result, computers
were never correctly rate limited.

Further more, if a computer went into a deficit, we would continue to
increase the budget by a negative amount, exponentially decreasing until
overflowing!

Yes, this is a very embarrassing mistake. I'd been aware that rate
limiting wasn't working as expected for a while, I hadn't realised
the problem would be this stupid.
2019-05-01 17:24:30 +01:00
SquidDev
b9fd690ecb Mark CC:T as incompatible with ComputerCraft
Just so people using the Twitch client don't try to use both.
2019-05-01 17:24:30 +01:00
xuyu0v0
2f2ada4416 Create zh_cn.lang (#186) 2019-04-24 15:41:13 +01:00
SquidDev
9c951c58d9 Revert "Switch over to Curse maven for now"
This reverts commit 4b4b47e231.

Didn't need it for that long. Woops.
2019-04-24 15:17:13 +01:00
SquidDev
4b4b47e231 Switch over to Curse maven for now
It stinks, but it's a suitable workaround.

Closes #184
2019-04-24 14:48:11 +01:00
SquidDev
978c28a686 Bump version 2019-04-24 09:48:28 +01:00
SquidDev
b867ada5e5 Merge pull request #182 from SquidDev-CC/hotfix/redstone-checks
Redstone behaviour tweaks
2019-04-24 09:45:28 +01:00
SquidDev
7071cc972b Make redstone behaviour consistent with repeaters
This uses the same behaviour that repeaters and comparators do for
determining their input, meaning that redstone directly connected to the
computer is read (as you would expect).

It's worth noting that this is a shift from previous behaviour.
Therefore, it runs the (small) risk of breaking existing builds.
However, I believe it is more consistent with the expected behaviour.
2019-04-22 11:52:53 +01:00
SquidDev
6898f932a0 Remove redstone blocked checks
This has been returning false for almost 2 years (since
8abff95441). At this point, it's probably
worth just dropping it entirely.
2019-04-22 11:40:19 +01:00
SquidDev
2e0ef6385d Fix TurtleCompareCommand throwing exceptions
Originally introduced in 173ea72001, but
the underlying cause has been happening for much longer.

 - Fix order of method name parameters. Looks like this has been broken
   since 1.11. Woops.
 - Catch RuntimeExceptions too, as Forge converts checked into unchecked
   ones.

Fixes #179
2019-04-19 18:57:12 +01:00
SquidDev
f93da7ea51 Bump Cobalt version
I promise! The joys of using -SNAPSHOT I guess...

This will now correctly cause orphaned threads to be cleaned up,
reducing the risk of thread saturation.
2019-04-18 09:49:52 +01:00
SquidDev
1210bb8a4d Fix Gradle dependencies
I'm not entirely sure why it didn't like the previous version, but there
we go.
2019-04-16 10:43:11 +01:00
208 changed files with 5273 additions and 1534 deletions

View File

@@ -1,7 +1,7 @@
---
name: Bug report
about: Report some misbehaviour in the mod
labels: bug
---
<!--

View File

@@ -1,7 +1,7 @@
---
name: Feature request
about: Suggest an idea or improvement
labels: enhancement
---
<!--
@@ -11,4 +11,4 @@ about: Suggest an idea or improvement
## Useful information to include:
- Explanation of how the feature/change should work.
- Some rationale/use case for a feature. I'd like to keep CC:T as minimal as possible, so I like have a solid justification for each feature.
- Some rationale/use case for a feature. My general approach to designing new features is to ask yourself "what issue are we trying to solve" and _then_ "is this the best way to solve this issue?".

3
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,3 @@
## A quick checklist
- If there's a existing issue, please link to it. If not, provide fill out the same information you would in a normal issue - reproduction steps for bugs, rationale for use-case.
- If you're working on CraftOS, try to write a few test cases so we can ensure everything continues to work in the future. Tests live in `src/test/resources/test-rom/spec` and can be run with `./gradlew check`.

6
.gitignore vendored
View File

@@ -15,3 +15,9 @@
.idea
.gradle
*.DS_Store
.classpath
.project
.settings/
bin/
*.launch

View File

@@ -17,7 +17,8 @@ ignore = {
-- are largely unsupported.
include_files = {
'src/main/resources/assets/computercraft/lua/rom',
'src/main/resources/assets/computercraft/lua/bios.lua'
'src/main/resources/assets/computercraft/lua/bios.lua',
'src/test/resources/test-rom',
}
files['src/main/resources/assets/computercraft/lua/bios.lua'] = {

View File

@@ -11,4 +11,4 @@ cache:
- $HOME/.gradle/wrapper/s
jdk:
- oraclejdk8
- openjdk8

View File

@@ -1,5 +1,5 @@
# ![CC: Tweaked](logo.png)
[![Build Status](https://travis-ci.org/SquidDev-CC/CC-Tweaked.svg?branch=master)](https://travis-ci.org/SquidDev-CC/CC-Tweaked)
[![Current build status](https://travis-ci.org/SquidDev-CC/CC-Tweaked.svg?branch=master)](https://travis-ci.org/SquidDev-CC/CC-Tweaked "Current build status") [![Download CC: Tweaked on CurseForge](http://cf.way2muchnoise.eu/title/cc-tweaked.svg)](https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked on CurseForge")
CC: Tweaked is a fork of [ComputerCraft](https://github.com/dan200/ComputerCraft), adding programmable computers,
turtles and more to Minecraft.
@@ -9,7 +9,7 @@ ComputerCraft has always held a fond place in my heart: it's the mod which reall
mod which has kept me playing it for many years. However, development of the original mod has slowed, as the original
developers have had less time to work on the mod, and moved onto other projects and commitments.
CC:Tweaked (or CC:T for short) is an attempt to continue ComputerCraft's legacy. It's not intended to be a competitor
CC: Tweaked (or CC:T for short) is an attempt to continue ComputerCraft's legacy. It's not intended to be a competitor
to CC, nor do I want to take it in a vastly different direction to the original mod. Instead, CC:T focuses on making the
ComputerCraft experience as _solid_ as possible, ironing out any wrinkles that may have developed over time.
@@ -46,8 +46,17 @@ develop CC:T, you'll need to follow these steps:
If you want to run CC:T in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from `build/libs`.
## Community
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
ComputerCraft we have a [forum](https://forums.computercraft.cc/) and [Discord guild](https://discord.gg/H2UyJXe)!
There's also a fairly populated, albeit quiet [IRC channel](http://webchat.esper.net/?channels=#computercraft), if
that's more your cup of tea.
I'd generally recommend you don't contact me directly (email, DM, etc...) unless absolutely necessary (i.e. in order to
report exploits). You'll get a far quicker response if you ask the whole community!
## Using
If you want to depend on CC:Tweaked, we have a maven repo. However, you should be wary that some functionality is only
If you want to depend on CC: Tweaked, we have a maven repo. However, you should be wary that some functionality is only
exposed by CC:T's API and not vanilla ComputerCraft. If you wish to support all variations of ComputerCraft, I recommend
using [cc.crzd.me's maven](https://cc.crzd.me/maven/) instead.

View File

@@ -5,7 +5,7 @@ buildscript {
jcenter()
maven {
name = "forge"
url = "http://files.minecraftforge.net/maven"
url = "https://files.minecraftforge.net/maven"
}
}
dependencies {
@@ -17,7 +17,9 @@ buildscript {
}
plugins {
id 'com.matthewprenger.cursegradle' version '1.0.10'
id "checkstyle"
id "com.github.hierynomus.license" version "0.15.0"
id "com.matthewprenger.cursegradle" version "1.3.0"
id "com.github.breadmoirai.github-release" version "2.2.4"
}
@@ -43,7 +45,7 @@ minecraft {
repositories {
maven {
name "JEI"
url "http://dvs1.progwml6.com/files/maven"
url "https://dvs1.progwml6.com/files/maven"
}
maven {
name "SquidDev"
@@ -55,7 +57,7 @@ repositories {
}
maven {
name "Amadornes"
url "http://maven.amadornes.com/"
url "https://maven.amadornes.com/"
}
}
@@ -66,6 +68,8 @@ configurations {
}
dependencies {
checkstyle "com.puppycrawl.tools:checkstyle:8.21"
deobfProvided "mezz.jei:jei_1.12.2:4.15.0.269:api"
deobfProvided "pl.asie:Charset-Lib:0.5.4.6"
deobfProvided "MCMultiPart2:MCMultiPart:2.5.3"
@@ -74,12 +78,14 @@ dependencies {
shade 'org.squiddev:Cobalt:0.5.0-SNAPSHOT'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.1.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.1.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.4.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.4.2'
deployerJars "org.apache.maven.wagon:wagon-ssh:3.0.0"
}
// Compile tasks
javadoc {
include "dan200/computercraft/api/**/*.java"
}
@@ -98,12 +104,22 @@ jar {
from configurations.shade.collect { it.isDirectory() ? it : zipTree(it) }
}
[compileJava, compileTestJava].forEach {
it.configure {
options.compilerArgs << "-Xlint" << "-Xlint:-processing" << "-Werror"
}
}
import java.nio.charset.StandardCharsets
import java.nio.file.*
import java.util.zip.*
import com.google.gson.GsonBuilder
import com.google.gson.JsonElement
import com.hierynomus.gradle.license.tasks.LicenseCheck
import com.hierynomus.gradle.license.tasks.LicenseFormat
import org.ajoberstar.grgit.Grgit
import proguard.gradle.ProGuardTask
@@ -199,6 +215,7 @@ task compressJson(dependsOn: extractAnnotationsJar) {
// Copy over all files in the current jar to the new one, running json files from GSON. As pretty printing
// is turned off, they should be minified.
new ZipFile(jarPath).withCloseable { inJar ->
tempPath.getParentFile().mkdirs()
new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tempPath))).withCloseable { outJar ->
inJar.entries().each { entry ->
if(entry.directory) {
@@ -226,12 +243,105 @@ task compressJson(dependsOn: extractAnnotationsJar) {
assemble.dependsOn compressJson
// Check tasks
test {
useJUnitPlatform()
testLogging {
events "skipped", "failed"
}
}
license {
mapping("java", "SLASHSTAR_STYLE")
strictCheck true
ext.year = Calendar.getInstance().get(Calendar.YEAR)
}
[licenseMain, licenseFormatMain].forEach {
it.configure {
include("**/*.java")
exclude("dan200/computercraft/api/**")
header rootProject.file('config/license/main.txt')
}
}
[licenseTest, licenseFormatTest].forEach {
it.configure {
include("**/*.java")
header rootProject.file('config/license/main.txt')
}
}
gradle.projectsEvaluated {
tasks.withType(LicenseFormat) {
outputs.upToDateWhen { false }
}
}
task licenseAPI(type: LicenseCheck);
task licenseFormatAPI(type: LicenseFormat);
[licenseAPI, licenseFormatAPI].forEach {
it.configure {
source = sourceSets.main.java
include("dan200/computercraft/api/**")
header rootProject.file('config/license/api.txt')
}
}
// Upload tasks
task checkRelease {
group "upload"
description "Verifies that everything is ready for a release"
inputs.property "version", mod_version
inputs.file("src/main/resources/assets/computercraft/lua/rom/help/changelog.txt")
inputs.file("src/main/resources/assets/computercraft/lua/rom/help/whatsnew.txt")
doLast {
def ok = true
// Check we're targetting the current version
def whatsnew = new File("src/main/resources/assets/computercraft/lua/rom/help/whatsnew.txt").readLines()
if (whatsnew[0] != "New features in CC: Tweaked $mod_version") {
ok = false
project.logger.error("Expected `whatsnew.txt' to target $mod_version.")
}
// Check "read more" exists and trim it
def idx = whatsnew.findIndexOf { it == 'Type "help changelog" to see the full version history.' }
if (idx == -1) {
ok = false
project.logger.error("Must mention the changelog in whatsnew.txt")
} else {
whatsnew = whatsnew.getAt(0 ..< idx)
}
// Check whatsnew and changelog match.
def versionChangelog = "# " + whatsnew.join("\n")
def changelog = new File("src/main/resources/assets/computercraft/lua/rom/help/changelog.txt").getText()
if (!changelog.startsWith(versionChangelog)) {
ok = false
project.logger.error("whatsnew and changelog are not in sync")
}
if (!ok) throw new IllegalStateException("Could not check release")
}
}
curseforge {
apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : ''
project {
id = '282001'
releaseType = 'release'
changelog = "Release notes can be found on the GitHub repository (https://github.com/SquidDev-CC/CC-Tweaked/releases/tag/v${mc_version}-${mod_version})."
relations {
incompatible "computercraft"
}
}
}
@@ -292,33 +402,25 @@ githubRelease {
token project.hasProperty('githubApiKey') ? project.githubApiKey : ''
owner 'SquidDev-CC'
repo 'CC-Tweaked'
targetCommitish (mc_version == "1.12.2" ? "master" : mc_version)
targetCommitish { Grgit.open(dir: '.').branch.current().name }
tagName "v${mc_version}-${mod_version}"
releaseName "[${mc_version}] ${mod_version}"
body ''
body {
"## " + new File("src/main/resources/assets/computercraft/lua/rom/help/whatsnew.txt")
.readLines()
.takeWhile { it != 'Type "help changelog" to see the full version history.' }
.join("\n").trim()
}
prerelease false
releaseAssets.from(jar.archivePath)
}
task uploadAll(dependsOn: proguard) {
def uploadTasks = ["uploadArchives", "curseforge", "githubRelease"]
uploadTasks.forEach { tasks.getByName(it).dependsOn checkRelease }
task uploadAll(dependsOn: uploadTasks) {
group "upload"
description "Uploads to all repositories (Maven, Curse, GitHub release)"
dependsOn uploadArchives, curseforge, githubRelease
}
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint" << "-Xlint:-processing" << "-Werror"
}
}
runClient.outputs.upToDateWhen { false }

View File

@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<property name="tabWidth" value="4"/>
<property name="charset" value="UTF-8" />
<module name="SuppressionFilter">
<property name="file" value="config/checkstyle/suppressions.xml" />
</module>
<module name="TreeWalker">
<!-- Annotations -->
<module name="AnnotationLocation" />
<module name="AnnotationUseStyle" />
<module name="MissingDeprecated">
<property name="skipNoJavadoc" value="true" />
</module>
<module name="MissingOverride" />
<!-- Blocks -->
<module name="EmptyBlock" />
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="ignored" />
</module>
<module name="LeftCurly">
<property name="option" value="nl" />
<!-- The defaults, minus lambdas. -->
<property name="tokens" value="ANNOTATION_DEF,CLASS_DEF,CTOR_DEF,ENUM_CONSTANT_DEF,ENUM_DEF,INTERFACE_DEF,LITERAL_CASE,LITERAL_CATCH,LITERAL_DEFAULT,LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_FOR,LITERAL_IF,LITERAL_SWITCH,LITERAL_SYNCHRONIZED,LITERAL_TRY,LITERAL_WHILE,METHOD_DEF,OBJBLOCK,STATIC_INIT" />
</module>
<module name="NeedBraces">
<property name="allowSingleLineStatement" value="true"/>
</module>
<module name="RightCurly">
<property name="option" value="alone" />
</module>
<!-- Class design. As if we've ever followed good practice here. -->
<module name="FinalClass" />
<module name="InterfaceIsType" />
<module name="MutableException" />
<module name="OneTopLevelClass" />
<!-- Coding -->
<module name="ArrayTrailingComma" />
<module name="EqualsHashCode" />
<!-- FallThrough does not handle unreachable code well -->
<module name="IllegalInstantiation" />
<module name="IllegalThrows" />
<module name="ModifiedControlVariable" />
<module name="NoClone" />
<module name="NoFinalizer" />
<module name="OneStatementPerLine" />
<module name="PackageDeclaration" />
<module name="SimplifyBooleanExpression" />
<module name="SimplifyBooleanReturn" />
<module name="StringLiteralEquality" />
<module name="UnnecessaryParentheses" />
<!-- Imports -->
<module name="CustomImportOrder" />
<module name="IllegalImport" />
<module name="RedundantImport" />
<module name="UnusedImports" />
<!-- Javadoc -->
<module name="AtclauseOrder" />
<!-- TODO: Cleanup our documentation before enabling JavadocMethod, JavadocStyle, JavadocType and SummaryJavadoc. -->
<module name="NonEmptyAtclauseDescription" />
<module name="SingleLineJavadoc" />
<!-- Misc -->
<module name="ArrayTypeStyle" />
<module name="CommentsIndentation" />
<module name="Indentation" />
<module name="OuterTypeFilename" />
<!-- Modifiers -->
<module name="ModifierOrder" />
<module name="RedundantModifier" />
<!-- Naming -->
<module name="ClassTypeParameterName" />
<module name="InterfaceTypeParameterName" />
<module name="LambdaParameterName" />
<module name="LocalFinalVariableName" />
<module name="LocalVariableName" />
<!-- Allow an optional m_ on private members -->
<module name="MemberName">
<property name="applyToPrivate" value="false" />
<property name="applyToPackage" value="false" />
</module>
<module name="MemberName">
<property name="format" value="^(m_)?[a-z][a-zA-Z0-9]*$" />
<property name="applyToPrivate" value="true" />
<property name="applyToPackage" value="true" />
</module>
<module name="MethodName" />
<module name="MethodTypeParameterName" />
<module name="PackageName">
<property name="format" value="^dan200\.computercraf(\.[a-z][a-z0-9]*)*" />
</module>
<module name="ParameterName" />
<module name="StaticVariableName">
<property name="format" value="^[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z]+)?$" />
<property name="applyToPrivate" value="false" />
</module>
<module name="StaticVariableName">
<property name="format" value="^(s_)?[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z]+)?$" />
<property name="applyToPrivate" value="true" />
</module>
<module name="TypeName" />
<!-- Whitespace -->
<module name="EmptyForInitializerPad"/>
<module name="EmptyForIteratorPad">
<property name="option" value="space"/>
</module>
<module name="GenericWhitespace" />
<module name="MethodParamPad" />
<module name="NoLineWrap" />
<module name="NoWhitespaceAfter">
<property name="tokens" value="AT,INC,DEC,UNARY_MINUS,UNARY_PLUS,BNOT,LNOT,DOT,ARRAY_DECLARATOR,INDEX_OP" />
</module>
<module name="NoWhitespaceBefore" />
<!-- TODO: Decide on an OperatorWrap style. -->
<module name="ParenPad">
<property name="option" value="space" />
<property name="tokens" value="ANNOTATION,ANNOTATION_FIELD_DEF,CTOR_CALL,CTOR_DEF,ENUM_CONSTANT_DEF,LITERAL_CATCH,LITERAL_DO,LITERAL_FOR,LITERAL_IF,LITERAL_NEW,LITERAL_SWITCH,LITERAL_SYNCHRONIZED,LITERAL_WHILE,METHOD_CALL,METHOD_DEF,RESOURCE_SPECIFICATION,SUPER_CTOR_CALL,LAMBDA" />
</module>
<module name="ParenPad">
<property name="option" value="nospace" />
<property name="tokens" value="DOT,EXPR,QUESTION" />
</module>
<module name="SeparatorWrap">
<property name="option" value="eol" />
<property name="tokens" value="COMMA,SEMI,ELLIPSIS,ARRAY_DECLARATOR,RBRACK,METHOD_REF" />
</module>
<module name="SeparatorWrap">
<property name="option" value="nl" />
<property name="tokens" value="DOT,AT" />
</module>
<module name="SingleSpaceSeparator" />
<module name="TypecastParenPad" />
<module name="WhitespaceAfter">
<property name="tokens" value="COMMA" />
</module>
<module name="WhitespaceAround">
<property name="allowEmptyConstructors" value="true" />
<property name="ignoreEnhancedForColon" value="false" />
<property name="tokens" value="ASSIGN,BAND,BAND_ASSIGN,BOR,BOR_ASSIGN,BSR,BSR_ASSIGN,BXOR,BXOR_ASSIGN,COLON,DIV,DIV_ASSIGN,DO_WHILE,EQUAL,GE,GT,LAMBDA,LAND,LCURLY,LE,LITERAL_RETURN,LOR,LT,MINUS,MINUS_ASSIGN,MOD,MOD_ASSIGN,NOT_EQUAL,PLUS,PLUS_ASSIGN,QUESTION,RCURLY,SL,SLIST,SL_ASSIGN,SR,SR_ASSIGN,STAR,STAR_ASSIGN,LITERAL_ASSERT,TYPE_EXTENSION_AND" />
</module>
</module>
<module name="FileTabCharacter" />
<module name="NewlineAtEndOfFile" />
</module>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suppressions PUBLIC
"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
"https://checkstyle.org/dtds/suppressions_1_2.dtd">
<suppressions>
<!-- All the config options and method fields. -->
<suppress checks="StaticVariableName" files=".*[\\/]ComputerCraft.java" />
<suppress checks="StaticVariableName" files=".*[\\/]ComputerCraftAPI.java" />
</suppressions>

3
config/license/api.txt Normal file
View File

@@ -0,0 +1,3 @@
This file is part of the public ComputerCraft API - http://www.computercraft.info
Copyright Daniel Ratcliffe, 2011-${year}. This API may be redistributed unmodified and in full only.
For help using the API, and posting your mods, visit the forums at computercraft.info.

3
config/license/main.txt Normal file
View File

@@ -0,0 +1,3 @@
This file is part of ComputerCraft - http://www.computercraft.info
Copyright Daniel Ratcliffe, 2011-${year}. Do not distribute without permission.
Send enquiries to dratcliffe@gmail.com

View File

@@ -1,5 +1,5 @@
# Mod properties
mod_version=1.82.2
mod_version=1.84.0
# Minecraft properties
mc_version=1.12.2

View File

@@ -72,9 +72,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.common.*;
import net.minecraftforge.fml.common.event.*;
import net.minecraftforge.fml.relauncher.Side;
import org.apache.logging.log4j.Logger;
@@ -83,9 +81,7 @@ import java.io.*;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -394,6 +390,27 @@ public class ComputerCraft
}
}
private static void loadFromFile( List<IMount> mounts, File file, String path, boolean allowMissing )
{
try
{
if( file.isFile() )
{
mounts.add( new JarMount( file, path ) );
}
else
{
File subResource = new File( file, path );
if( subResource.exists() ) mounts.add( new FileMount( subResource, 0 ) );
}
}
catch( IOException | RuntimeException e )
{
if( allowMissing && e instanceof FileNotFoundException ) return;
ComputerCraft.log.error( "Could not load mount '" + path + " 'from '" + file.getName() + "'", e );
}
}
@Deprecated
public static IMount createResourceMount( Class<?> modClass, String domain, String subPath )
{
@@ -413,18 +430,26 @@ public class ComputerCraft
}
}
// Mount from mod jar
// Mount from mod jars, preferring the specified one.
File modJar = getContainingJar( modClass );
Set<File> otherMods = new HashSet<>();
for( ModContainer container : Loader.instance().getActiveModList() )
{
File modFile = container.getSource();
if( modFile != null && !modFile.equals( modJar ) && modFile.exists() )
{
otherMods.add( container.getSource() );
}
}
for( File file : otherMods )
{
loadFromFile( mounts, file, subPath, true );
}
if( modJar != null )
{
try
{
mounts.add( new JarMount( modJar, subPath ) );
}
catch( IOException | RuntimeException e )
{
ComputerCraft.log.error( "Could not load mount from mod jar", e );
}
loadFromFile( mounts, modJar, subPath, false );
}
// Mount from resource packs
@@ -433,29 +458,9 @@ public class ComputerCraft
{
String[] resourcePacks = resourcePackDir.list();
for( String resourcePackName : resourcePacks )
{
try
{
File resourcePack = new File( resourcePackDir, resourcePackName );
if( !resourcePack.isDirectory() )
{
// Mount a resource pack from a jar
mounts.add( new JarMount( resourcePack, subPath ) );
}
else
{
// Mount a resource pack from a folder
File subResource = new File( resourcePack, subPath );
if( subResource.exists() ) mounts.add( new FileMount( subResource, 0 ) );
}
}
catch( FileNotFoundException ignored )
{
}
catch( IOException | RuntimeException e )
{
ComputerCraft.log.error( "Could not load resource pack '" + resourcePackName + "'", e );
}
loadFromFile( mounts, resourcePack, subPath, true );
}
}

View File

@@ -1,9 +1,8 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api;
import dan200.computercraft.api.turtle.ITurtleUpgrade;

View File

@@ -438,45 +438,45 @@ public final class ComputerCraftAPI
computerCraft_getVersion = findCCMethod( "getVersion", new Class<?>[] {
} );
computerCraft_createUniqueNumberedSaveDir = findCCMethod( "createUniqueNumberedSaveDir", new Class<?>[] {
World.class, String.class
World.class, String.class,
} );
computerCraft_createSaveDirMount = findCCMethod( "createSaveDirMount", new Class<?>[] {
World.class, String.class, Long.TYPE
World.class, String.class, Long.TYPE,
} );
computerCraft_createResourceMount = findCCMethod( "createResourceMount", new Class<?>[] {
Class.class, String.class, String.class
Class.class, String.class, String.class,
} );
computerCraft_registerPeripheralProvider = findCCMethod( "registerPeripheralProvider", new Class<?>[] {
IPeripheralProvider.class
IPeripheralProvider.class,
} );
computerCraft_registerTurtleUpgrade = findCCMethod( "registerTurtleUpgrade", new Class<?>[] {
ITurtleUpgrade.class
ITurtleUpgrade.class,
} );
computerCraft_registerBundledRedstoneProvider = findCCMethod( "registerBundledRedstoneProvider", new Class<?>[] {
IBundledRedstoneProvider.class
IBundledRedstoneProvider.class,
} );
computerCraft_getDefaultBundledRedstoneOutput = findCCMethod( "getDefaultBundledRedstoneOutput", new Class<?>[] {
World.class, BlockPos.class, EnumFacing.class
World.class, BlockPos.class, EnumFacing.class,
} );
computerCraft_registerMediaProvider = findCCMethod( "registerMediaProvider", new Class<?>[] {
IMediaProvider.class
IMediaProvider.class,
} );
computerCraft_registerPermissionProvider = findCCMethod( "registerPermissionProvider", new Class<?>[] {
ITurtlePermissionProvider.class
ITurtlePermissionProvider.class,
} );
computerCraft_registerPocketUpgrade = findCCMethod( "registerPocketUpgrade", new Class<?>[] {
IPocketUpgrade.class
IPocketUpgrade.class,
} );
computerCraft_getWirelessNetwork = findCCMethod( "getWirelessNetwork", new Class<?>[] {
} );
computerCraft_registerAPIFactory = findCCMethod( "registerAPIFactory", new Class<?>[] {
ILuaAPIFactory.class
ILuaAPIFactory.class,
} );
computerCraft_createWiredNodeForElement = findCCMethod( "createWiredNodeForElement", new Class<?>[] {
IWiredElement.class
IWiredElement.class,
} );
computerCraft_getWiredElementAt = findCCMethod( "getWiredElementAt", new Class<?>[] {
IBlockAccess.class, BlockPos.class, EnumFacing.class
IBlockAccess.class, BlockPos.class, EnumFacing.class,
} );
}
catch( Exception e )

View File

@@ -0,0 +1,41 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.filesystem;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Objects;
/**
* An {@link IOException} which occurred on a specific file.
*
* This may be thrown from a {@link IMount} or {@link IWritableMount} to give more information about a failure.
*/
public class FileOperationException extends IOException
{
private static final long serialVersionUID = -8809108200853029849L;
private final String filename;
public FileOperationException( @Nullable String filename, @Nonnull String message )
{
super( Objects.requireNonNull( message, "message cannot be null" ) );
this.filename = filename;
}
public FileOperationException( String message )
{
super( Objects.requireNonNull( message, "message cannot be null" ) );
this.filename = null;
}
@Nullable
public String getFilename()
{
return filename;
}
}

View File

@@ -90,7 +90,6 @@ public interface IMount
* @throws IOException If the file does not exist, or could not be opened.
*/
@Nonnull
@SuppressWarnings( "deprecation" )
default ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
{
return Channels.newChannel( openForRead( path ) );

View File

@@ -67,7 +67,6 @@ public interface IWritableMount extends IMount
* @throws IOException If the file could not be opened for writing.
*/
@Nonnull
@SuppressWarnings( "deprecation" )
default WritableByteChannel openChannelForWrite( @Nonnull String path ) throws IOException
{
return Channels.newChannel( openForWrite( path ) );
@@ -94,7 +93,6 @@ public interface IWritableMount extends IMount
* @throws IOException If the file could not be opened for writing.
*/
@Nonnull
@SuppressWarnings( "deprecation" )
default WritableByteChannel openChannelForAppend( @Nonnull String path ) throws IOException
{
return Channels.newChannel( openForAppend( path ) );

View File

@@ -1,9 +1,8 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.peripheral;
import javax.annotation.Nonnull;

View File

@@ -1,9 +1,8 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.pocket;
import net.minecraft.item.ItemStack;

View File

@@ -145,7 +145,9 @@ public interface ITurtleAccess
GameProfile getOwningPlayer();
/**
* Get the inventory of this turtle
* Get the inventory of this turtle.
*
* Note: this inventory should only be accessed and modified on the server thread.
*
* @return This turtle's inventory
* @see #getItemHandler()
@@ -156,6 +158,8 @@ public interface ITurtleAccess
/**
* Get the inventory of this turtle as an {@link IItemHandlerModifiable}.
*
* Note: this inventory should only be accessed and modified on the server thread.
*
* @return This turtle's inventory
* @see #getInventory()
* @see IItemHandlerModifiable

View File

@@ -109,8 +109,8 @@ public interface ITurtleUpgrade
* Will only be called for Tool turtle. Called when turtle.dig() or turtle.attack() is called
* by the turtle, and the tool is required to do some work.
*
* Conforming implementations should fire {@link BlockEvent.BreakEvent} and {@link TurtleBlockEvent.Dig}for digging,
* {@link AttackEntityEvent} and {@link TurtleAttackEvent} for attacking.
* Conforming implementations should fire {@link BlockEvent.BreakEvent} and {@link TurtleBlockEvent.Dig} for
* digging, {@link AttackEntityEvent} and {@link TurtleAttackEvent} for attacking.
*
* @param turtle Access to the turtle that the tool resides on.
* @param side Which side of the turtle (left or right) the tool resides on.

View File

@@ -1,9 +1,8 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.turtle.event;
import dan200.computercraft.api.lua.ILuaContext;

View File

@@ -1,9 +1,8 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.turtle.event;
import dan200.computercraft.api.turtle.ITurtleAccess;

View File

@@ -24,9 +24,10 @@ import java.io.IOException;
public class GuiComputer extends GuiContainer
{
private static final ResourceLocation BACKGROUND = new ResourceLocation( "computercraft", "textures/gui/corners.png" );
private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( "computercraft", "textures/gui/corners_advanced.png" );
private static final ResourceLocation BACKGROUND_COMMAND = new ResourceLocation( "computercraft", "textures/gui/corners_command.png" );
public static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners.png" );
public static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_advanced.png" );
public static final ResourceLocation BACKGROUND_COMMAND = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_command.png" );
public static final ResourceLocation BACKGROUND_COLOUR = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_colour.png" );
private final ComputerFamily m_family;
private final ClientComputer m_computer;
@@ -158,7 +159,7 @@ public class GuiComputer extends GuiContainer
{
case Normal:
default:
mc.getTextureManager().bindTexture( BACKGROUND );
mc.getTextureManager().bindTexture( BACKGROUND_NORMAL );
break;
case Advanced:
mc.getTextureManager().bindTexture( BACKGROUND_ADVANCED );
@@ -169,12 +170,12 @@ public class GuiComputer extends GuiContainer
}
drawTexturedModalRect( startX - 12, startY - 12, 12, 28, 12, 12 );
drawTexturedModalRect( startX - 12, endY, 12, 40, 12, 16 );
drawTexturedModalRect( startX - 12, endY, 12, 40, 12, 12 );
drawTexturedModalRect( endX, startY - 12, 24, 28, 12, 12 );
drawTexturedModalRect( endX, endY, 24, 40, 12, 16 );
drawTexturedModalRect( endX, endY, 24, 40, 12, 12 );
drawTexturedModalRect( startX, startY - 12, 0, 0, endX - startX, 12 );
drawTexturedModalRect( startX, endY, 0, 12, endX - startX, 16 );
drawTexturedModalRect( startX, endY, 0, 12, endX - startX, 12 );
drawTexturedModalRect( startX - 12, startY, 0, 28, 12, endY - startY );
drawTexturedModalRect( endX, startY, 36, 28, 12, endY - startY );

View File

@@ -120,7 +120,7 @@ public class GuiTurtle extends GuiContainer
int slotX = slot % 4;
int slotY = slot / 4;
mc.getTextureManager().bindTexture( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL );
drawTexturedModalRect( guiLeft + m_container.m_turtleInvStartX - 2 + slotX * 18, guiTop + m_container.m_playerInvStartY - 2 + slotY * 18, 0, 217, 24, 24 );
drawTexturedModalRect( guiLeft + m_container.turtleInvStartX - 2 + slotX * 18, guiTop + m_container.playerInvStartY - 2 + slotY * 18, 0, 217, 24, 24 );
}
}

View File

@@ -29,13 +29,13 @@ import net.minecraftforge.fml.relauncher.Side;
import org.lwjgl.opengl.GL11;
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Side.CLIENT )
public final class RenderOverlayCable
public final class CableHighlightRenderer
{
private static final float EXPAND = 0.002f;
private static final double MIN = CableBounds.MIN - EXPAND;
private static final double MAX = CableBounds.MAX + EXPAND;
private RenderOverlayCable()
private CableHighlightRenderer()
{
}

View File

@@ -12,25 +12,24 @@ import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.RenderItem;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.item.ItemStack;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.event.RenderSpecificHandEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import org.lwjgl.opengl.GL11;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.*;
import static dan200.computercraft.client.gui.GuiComputer.*;
/**
* Emulates map rendering for pocket computers
@@ -38,6 +37,10 @@ import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Side.CLIENT )
public final class ItemPocketRenderer extends ItemMapLikeRenderer
{
private static final int MARGIN = 2;
private static final int FRAME = 12;
private static final int LIGHT_HEIGHT = 8;
private static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer();
private ItemPocketRenderer()
@@ -57,115 +60,194 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
@Override
protected void renderItem( ItemStack stack )
{
ClientComputer computer = ItemPocketComputer.createClientComputer( stack );
Terminal terminal = computer == null ? null : computer.getTerminal();
int termWidth, termHeight;
if( terminal == null )
{
termWidth = ComputerCraft.terminalWidth_pocketComputer;
termHeight = ComputerCraft.terminalHeight_pocketComputer;
}
else
{
termWidth = terminal.getWidth();
termHeight = terminal.getHeight();
}
int width = termWidth * FONT_WIDTH + MARGIN * 2;
int height = termHeight * FONT_HEIGHT + MARGIN * 2;
// Setup various transformations. Note that these are partially adapted from the corresponding method
// in ItemRenderer
GlStateManager.pushMatrix();
GlStateManager.disableLighting();
GlStateManager.disableDepth();
GlStateManager.rotate( 180f, 0f, 1f, 0f );
GlStateManager.rotate( 180f, 0f, 0f, 1f );
GlStateManager.scale( 0.5, 0.5, 0.5 );
ClientComputer computer = ItemPocketComputer.createClientComputer( stack );
double scale = 0.75 / Math.max( width + FRAME * 2, height + FRAME * 2 + LIGHT_HEIGHT );
GlStateManager.scale( scale, scale, 0 );
GlStateManager.translate( -0.5 * width, -0.5 * height, 0 );
// Render the main frame
ComputerFamily family = ComputerCraft.Items.pocketComputer.getFamily( stack );
int frameColour = ComputerCraft.Items.pocketComputer.getColour( stack );
renderFrame( family, frameColour, width, height );
// Render the light
int lightColour = ItemPocketComputer.getLightState( stack );
if( lightColour == -1 ) lightColour = Colour.Black.getHex();
renderLight( lightColour, width, height );
if( computer != null && terminal != null )
{
// First render the background item. We use the item's model rather than a direct texture as this ensures
// we display the pocket light and other such decorations.
GlStateManager.pushMatrix();
// If we've a computer and terminal then attempt to render it.
renderTerminal( terminal, !computer.isColour(), width, height );
}
else
{
// Otherwise render a plain background
Minecraft.getMinecraft().getTextureManager().bindTexture( BACKGROUND );
GlStateManager.scale( 1.0f, -1.0f, 1.0f );
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
Minecraft minecraft = Minecraft.getMinecraft();
TextureManager textureManager = minecraft.getTextureManager();
RenderItem renderItem = minecraft.getRenderItem();
// Copy of RenderItem#renderItemModelIntoGUI but without the translation or scaling
textureManager.bindTexture( TextureMap.LOCATION_BLOCKS_TEXTURE );
textureManager.getTexture( TextureMap.LOCATION_BLOCKS_TEXTURE ).setBlurMipmap( false, false );
GlStateManager.enableRescaleNormal();
GlStateManager.enableAlpha();
GlStateManager.alphaFunc( GL11.GL_GREATER, 0.1F );
GlStateManager.enableBlend();
GlStateManager.blendFunc( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA );
GlStateManager.color( 1.0F, 1.0F, 1.0F, 1.0F );
IBakedModel baked = renderItem.getItemModelWithOverrides( stack, null, null );
baked = ForgeHooksClient.handleCameraTransforms( baked, ItemCameraTransforms.TransformType.GUI, false );
renderItem.renderItem( stack, baked );
GlStateManager.disableAlpha();
GlStateManager.disableRescaleNormal();
Colour black = Colour.Black;
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION );
renderTexture( buffer, 0, 0, 0, 0, width, height, black.getR(), black.getG(), black.getB() );
tessellator.draw();
}
GlStateManager.enableDepth();
GlStateManager.enableLighting();
GlStateManager.popMatrix();
}
// If we've a computer and terminal then attempt to render it.
if( computer != null )
private static void renderFrame( ComputerFamily family, int colour, int width, int height )
{
Terminal terminal = computer.getTerminal();
if( terminal != null )
Minecraft.getMinecraft().getTextureManager().bindTexture( colour != -1
? BACKGROUND_COLOUR
: family == ComputerFamily.Normal ? BACKGROUND_NORMAL : BACKGROUND_ADVANCED
);
float r = ((colour >>> 16) & 0xFF) / 255.0f;
float g = ((colour >>> 8) & 0xFF) / 255.0f;
float b = (colour & 0xFF) / 255.0f;
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR );
// Top left, middle, right
renderTexture( buffer, -FRAME, -FRAME, 12, 28, FRAME, FRAME, r, g, b );
renderTexture( buffer, 0, -FRAME, 0, 0, width, FRAME, r, g, b );
renderTexture( buffer, width, -FRAME, 24, 28, FRAME, FRAME, r, g, b );
// Left and bright border
renderTexture( buffer, -FRAME, 0, 0, 28, FRAME, height, r, g, b );
renderTexture( buffer, width, 0, 36, 28, FRAME, height, r, g, b );
// Bottom left, middle, right. We do this in three portions: the top inner corners, an extended region for
// lights, and then the bottom outer corners.
renderTexture( buffer, -FRAME, height, 12, 40, FRAME, FRAME / 2, r, g, b );
renderTexture( buffer, 0, height, 0, 12, width, FRAME / 2, r, g, b );
renderTexture( buffer, width, height, 24, 40, FRAME, FRAME / 2, r, g, b );
renderTexture( buffer, -FRAME, height + FRAME / 2, 12, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
renderTexture( buffer, 0, height + FRAME / 2, 0, 16, width, LIGHT_HEIGHT, FRAME, 4, r, g, b );
renderTexture( buffer, width, height + FRAME / 2, 24, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
renderTexture( buffer, -FRAME, height + LIGHT_HEIGHT + FRAME / 2, 12, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
renderTexture( buffer, 0, height + LIGHT_HEIGHT + FRAME / 2, 0, 12 + FRAME / 2, width, FRAME / 2, r, g, b );
renderTexture( buffer, width, height + LIGHT_HEIGHT + FRAME / 2, 24, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
tessellator.draw();
}
private static void renderLight( int colour, int width, int height )
{
GlStateManager.enableBlend();
GlStateManager.disableTexture2D();
float r = ((colour >>> 16) & 0xFF) / 255.0f;
float g = ((colour >>> 8) & 0xFF) / 255.0f;
float b = (colour & 0xFF) / 255.0f;
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR );
buffer.pos( width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
buffer.pos( width, height + LIGHT_HEIGHT + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
buffer.pos( width, height + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
buffer.pos( width - LIGHT_HEIGHT * 2, height + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
tessellator.draw();
GlStateManager.enableTexture2D();
}
private static void renderTerminal( Terminal terminal, boolean greyscale, int width, int height )
{
synchronized( terminal )
{
GlStateManager.pushMatrix();
GlStateManager.disableDepth();
// Reset the position to be at the top left corner of the pocket computer
// Note we translate towards the screen slightly too.
GlStateManager.translate( -8 / 16.0, -8 / 16.0, 0.5 / 16.0 );
// Translate to the top left of the screen.
GlStateManager.translate( 4 / 16.0, 3 / 16.0, 0 );
// Work out the scaling required to resize the terminal in order to fit on the computer
final int margin = 2;
int tw = terminal.getWidth();
int th = terminal.getHeight();
int width = tw * FONT_WIDTH + margin * 2;
int height = th * FONT_HEIGHT + margin * 2;
int max = Math.max( height, width );
// The grid is 8 * 8 wide, so we start with a base of 1/2 (8 / 16).
double scale = 1.0 / 2.0 / max;
GlStateManager.scale( scale, scale, scale );
// The margin/start positions are determined in order for the terminal to be centred.
int startX = (max - width) / 2 + margin;
int startY = (max - height) / 2 + margin;
int termWidth = terminal.getWidth();
int termHeight = terminal.getHeight();
FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance();
boolean greyscale = !computer.isColour();
Palette palette = terminal.getPalette();
// Render top/bottom borders
TextBuffer emptyLine = new TextBuffer( ' ', termWidth );
fontRenderer.drawString(
emptyLine, MARGIN, 0,
terminal.getTextColourLine( 0 ), terminal.getBackgroundColourLine( 0 ), MARGIN, MARGIN, greyscale, palette
);
fontRenderer.drawString(
emptyLine, MARGIN, 2 * MARGIN + (termHeight - 1) * FixedWidthFontRenderer.FONT_HEIGHT,
terminal.getTextColourLine( termHeight - 1 ), terminal.getBackgroundColourLine( termHeight - 1 ), MARGIN, MARGIN, greyscale, palette
);
// Render the actual text
for( int line = 0; line < th; line++ )
for( int line = 0; line < termWidth; line++ )
{
TextBuffer text = terminal.getLine( line );
TextBuffer colour = terminal.getTextColourLine( line );
TextBuffer backgroundColour = terminal.getBackgroundColourLine( line );
fontRenderer.drawString(
text, startX, startY + line * FONT_HEIGHT,
colour, backgroundColour, margin, margin, greyscale, palette
text, MARGIN, MARGIN + line * FONT_HEIGHT,
colour, backgroundColour, MARGIN, MARGIN, greyscale, palette
);
}
// And render the cursor;
int tx = terminal.getCursorX(), ty = terminal.getCursorY();
if( terminal.getCursorBlink() && FrameInfo.getGlobalCursorBlink() &&
tx >= 0 && ty >= 0 && tx < tw && ty < th )
tx >= 0 && ty >= 0 && tx < termWidth && ty < termHeight )
{
TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 );
fontRenderer.drawString(
new TextBuffer( '_', 1 ), startX + FONT_WIDTH * tx, startY + FONT_HEIGHT * ty,
new TextBuffer( '_', 1 ), MARGIN + FONT_WIDTH * tx, MARGIN + FONT_HEIGHT * ty,
cursorColour, null, 0, 0, greyscale, palette
);
}
GlStateManager.enableDepth();
GlStateManager.popMatrix();
}
}
}
GlStateManager.enableLighting();
private static void renderTexture( BufferBuilder builder, int x, int y, int textureX, int textureY, int width, int height, float r, float g, float b )
{
renderTexture( builder, x, y, textureX, textureY, width, height, width, height, r, g, b );
}
private static void renderTexture( BufferBuilder builder, int x, int y, int textureX, int textureY, int width, int height, int textureWidth, int textureHeight, float r, float g, float b )
{
float scale = 1 / 255.0f;
builder.pos( x, y + height, 0 ).tex( textureX * scale, (textureY + textureHeight) * scale ).color( r, g, b, 1.0f ).endVertex();
builder.pos( x + width, y + height, 0 ).tex( (textureX + textureWidth) * scale, (textureY + textureHeight) * scale ).color( r, g, b, 1.0f ).endVertex();
builder.pos( x + width, y, 0 ).tex( (textureX + textureWidth) * scale, textureY * scale ).color( r, g, b, 1.0f ).endVertex();
builder.pos( x, y, 0 ).tex( textureX * scale, textureY * scale ).color( r, g, b, 1.0f ).endVertex();
}
}

View File

@@ -94,7 +94,7 @@ public final class ModelTransformer
private final Point3f[] before = new Point3f[4];
private final Point3f[] after = new Point3f[4];
public NormalAwareTransformer( IVertexConsumer parent, Matrix4f positionMatrix, Matrix4f normalMatrix )
NormalAwareTransformer( IVertexConsumer parent, Matrix4f positionMatrix, Matrix4f normalMatrix )
{
super( parent );
this.positionMatrix = positionMatrix;

View File

@@ -0,0 +1,118 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import org.lwjgl.opengl.GL11;
import java.util.EnumSet;
import static net.minecraft.util.EnumFacing.*;
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Side.CLIENT )
public final class MonitorHighlightRenderer
{
private static final float EXPAND = 0.002f;
private MonitorHighlightRenderer()
{
}
@SubscribeEvent
public static void drawHighlight( DrawBlockHighlightEvent event )
{
if( event.getTarget().typeOfHit != RayTraceResult.Type.BLOCK || event.getPlayer().isSneaking() ) return;
World world = event.getPlayer().getEntityWorld();
BlockPos pos = event.getTarget().getBlockPos();
if( world.getBlockState( pos ).getBlock() != ComputerCraft.Blocks.peripheral ) return;
TileEntity tile = world.getTileEntity( pos );
if( !(tile instanceof TileMonitor) ) return;
TileMonitor monitor = (TileMonitor) tile;
event.setCanceled( true );
// Determine which sides are part of the external faces of the monitor, and so which need to be rendered.
EnumSet<EnumFacing> faces = EnumSet.allOf( EnumFacing.class );
EnumFacing front = monitor.getFront();
faces.remove( front );
if( monitor.getXIndex() != 0 ) faces.remove( monitor.getRight().getOpposite() );
if( monitor.getXIndex() != monitor.getWidth() - 1 ) faces.remove( monitor.getRight() );
if( monitor.getYIndex() != 0 ) faces.remove( monitor.getDown().getOpposite() );
if( monitor.getYIndex() != monitor.getHeight() - 1 ) faces.remove( monitor.getDown() );
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO );
GL11.glLineWidth( 2.0F );
GlStateManager.disableTexture2D();
GlStateManager.depthMask( false );
GlStateManager.pushMatrix();
EntityPlayer player = event.getPlayer();
double x = player.lastTickPosX + (player.posX - player.lastTickPosX) * event.getPartialTicks();
double y = player.lastTickPosY + (player.posY - player.lastTickPosY) * event.getPartialTicks();
double z = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * event.getPartialTicks();
GlStateManager.translate( -x + pos.getX(), -y + pos.getY(), -z + pos.getZ() );
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR );
// I wish I could think of a better way to do this
if( faces.contains( NORTH ) || faces.contains( WEST ) ) line( buffer, 0, 0, 0, UP );
if( faces.contains( SOUTH ) || faces.contains( WEST ) ) line( buffer, 0, 0, 1, UP );
if( faces.contains( NORTH ) || faces.contains( EAST ) ) line( buffer, 1, 0, 0, UP );
if( faces.contains( SOUTH ) || faces.contains( EAST ) ) line( buffer, 1, 0, 1, UP );
if( faces.contains( NORTH ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 0, EAST );
if( faces.contains( SOUTH ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 1, EAST );
if( faces.contains( NORTH ) || faces.contains( UP ) ) line( buffer, 0, 1, 0, EAST );
if( faces.contains( SOUTH ) || faces.contains( UP ) ) line( buffer, 0, 1, 1, EAST );
if( faces.contains( WEST ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 0, SOUTH );
if( faces.contains( EAST ) || faces.contains( DOWN ) ) line( buffer, 1, 0, 0, SOUTH );
if( faces.contains( WEST ) || faces.contains( UP ) ) line( buffer, 0, 1, 0, SOUTH );
if( faces.contains( EAST ) || faces.contains( UP ) ) line( buffer, 1, 1, 0, SOUTH );
tessellator.draw();
GlStateManager.popMatrix();
GlStateManager.depthMask( true );
GlStateManager.enableTexture2D();
GlStateManager.disableBlend();
}
private static void line( BufferBuilder buffer, int x, int y, int z, EnumFacing direction )
{
double minX = x == 0 ? -EXPAND : 1 + EXPAND;
double minY = y == 0 ? -EXPAND : 1 + EXPAND;
double minZ = z == 0 ? -EXPAND : 1 + EXPAND;
buffer.pos( minX, minY, minZ ).color( 0, 0, 0, 0.4f ).endVertex();
buffer.pos(
minX + direction.getXOffset() * (1 + EXPAND * 2),
minY + direction.getYOffset() * (1 + EXPAND * 2),
minZ + direction.getZOffset() * (1 + EXPAND * 2)
).color( 0, 0, 0, 0.4f ).endVertex();
}
}

View File

@@ -43,9 +43,7 @@ public class FSAPI implements ILuaAPI
@Override
public String[] getNames()
{
return new String[] {
"fs"
};
return new String[] { "fs" };
}
@Override

View File

@@ -42,9 +42,7 @@ public class HTTPAPI implements ILuaAPI
@Override
public String[] getNames()
{
return new String[] {
"http"
};
return new String[] { "http" };
}
@Override
@@ -83,6 +81,7 @@ public class HTTPAPI implements ILuaAPI
}
@Override
@SuppressWarnings( "resource" )
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
{
switch( method )
@@ -95,7 +94,7 @@ public class HTTPAPI implements ILuaAPI
if( args.length >= 1 && args[0] instanceof Map )
{
Map<?, ?> options = (Map) args[0];
Map<?, ?> options = (Map<?, ?>) args[0];
address = getStringField( options, "url" );
postString = optStringField( options, "body", null );
headerTable = optTableField( options, "headers", Collections.emptyMap() );
@@ -135,7 +134,6 @@ public class HTTPAPI implements ILuaAPI
try
{
URI uri = HttpRequest.checkUri( address );
HttpRequest request = new HttpRequest( requests, m_apiEnvironment, address, postString, headers, binary, redirect );
long requestBody = request.body().readableBytes() + HttpRequest.getHeaderSize( headers );

View File

@@ -10,6 +10,7 @@ package dan200.computercraft.core.apis;
* This exists purely to ensure binary compatibility.
*
* @see dan200.computercraft.api.lua.ILuaAPI
* @deprecated Use the version in the public API. Only exists for compatibility with CCEmuX.
*/
@Deprecated
public interface ILuaAPI extends dan200.computercraft.api.lua.ILuaAPI

View File

@@ -0,0 +1,281 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.LuaException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
import java.time.temporal.*;
import java.util.HashMap;
import java.util.Map;
import java.util.function.LongUnaryOperator;
final class LuaDateTime
{
private LuaDateTime()
{
}
static void format( DateTimeFormatterBuilder formatter, String format, ZoneOffset offset ) throws LuaException
{
for( int i = 0; i < format.length(); )
{
char c;
switch( c = format.charAt( i++ ) )
{
case '\n':
formatter.appendLiteral( '\n' );
break;
default:
formatter.appendLiteral( c );
break;
case '%':
if( i >= format.length() ) break;
switch( c = format.charAt( i++ ) )
{
default:
throw new LuaException( "bad argument #1: invalid conversion specifier '%" + c + "'" );
case '%':
formatter.appendLiteral( '%' );
break;
case 'a':
formatter.appendText( ChronoField.DAY_OF_WEEK, TextStyle.SHORT );
break;
case 'A':
formatter.appendText( ChronoField.DAY_OF_WEEK, TextStyle.FULL );
break;
case 'b':
case 'h':
formatter.appendText( ChronoField.MONTH_OF_YEAR, TextStyle.SHORT );
break;
case 'B':
formatter.appendText( ChronoField.MONTH_OF_YEAR, TextStyle.FULL );
break;
case 'c':
format( formatter, "%a %b %e %H:%M:%S %Y", offset );
break;
case 'C':
formatter.appendValueReduced( CENTURY, 2, 2, 0 );
break;
case 'd':
formatter.appendValue( ChronoField.DAY_OF_MONTH, 2 );
break;
case 'D':
case 'x':
format( formatter, "%m/%d/%y", offset );
break;
case 'e':
formatter.padNext( 2 ).appendValue( ChronoField.DAY_OF_MONTH );
break;
case 'F':
format( formatter, "%Y-%m-%d", offset );
break;
case 'g':
formatter.appendValueReduced( IsoFields.WEEK_BASED_YEAR, 2, 2, 0 );
break;
case 'G':
formatter.appendValue( IsoFields.WEEK_BASED_YEAR );
break;
case 'H':
formatter.appendValue( ChronoField.HOUR_OF_DAY, 2 );
break;
case 'I':
formatter.appendValue( ChronoField.HOUR_OF_AMPM );
break;
case 'j':
formatter.appendValue( ChronoField.DAY_OF_YEAR, 3 );
break;
case 'm':
formatter.appendValue( ChronoField.MONTH_OF_YEAR, 2 );
break;
case 'M':
formatter.appendValue( ChronoField.MINUTE_OF_HOUR, 2 );
break;
case 'n':
formatter.appendLiteral( '\n' );
break;
case 'p':
formatter.appendText( ChronoField.AMPM_OF_DAY );
break;
case 'r':
format( formatter, "%I:%M:%S %p", offset );
break;
case 'R':
format( formatter, "%H:%M", offset );
break;
case 'S':
formatter.appendValue( ChronoField.SECOND_OF_MINUTE, 2 );
break;
case 't':
formatter.appendLiteral( '\t' );
break;
case 'T':
case 'X':
format( formatter, "%H:%M:%S", offset );
break;
case 'u':
formatter.appendValue( ChronoField.DAY_OF_WEEK );
break;
case 'U':
formatter.appendValue( ChronoField.ALIGNED_WEEK_OF_YEAR, 2 );
break;
case 'V':
formatter.appendValue( IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2 );
break;
case 'w':
formatter.appendValue( ZERO_WEEK );
break;
case 'W':
formatter.appendValue( WeekFields.ISO.weekOfYear(), 2 );
break;
case 'y':
formatter.appendValueReduced( ChronoField.YEAR, 2, 2, 0 );
break;
case 'Y':
formatter.appendValue( ChronoField.YEAR );
break;
case 'z':
formatter.appendOffset( "+HHMM", "+0000" );
break;
case 'Z':
formatter.appendChronologyId();
break;
}
}
}
}
static long fromTable( Map<?, ?> table ) throws LuaException
{
int year = getField( table, "year", -1 );
int month = getField( table, "month", -1 );
int day = getField( table, "day", -1 );
int hour = getField( table, "hour", 12 );
int minute = getField( table, "min", 12 );
int second = getField( table, "sec", 12 );
LocalDateTime time = LocalDateTime.of( year, month, day, hour, minute, second );
Boolean isDst = getBoolField( table, "isdst" );
if( isDst != null )
{
boolean requireDst = isDst;
for( ZoneOffset possibleOffset : ZoneOffset.systemDefault().getRules().getValidOffsets( time ) )
{
Instant instant = time.toInstant( possibleOffset );
if( possibleOffset.getRules().getDaylightSavings( instant ).isZero() == requireDst )
{
return instant.getEpochSecond();
}
}
}
ZoneOffset offset = ZoneOffset.systemDefault().getRules().getOffset( time );
return time.toInstant( offset ).getEpochSecond();
}
static Map<String, ?> toTable( TemporalAccessor date, ZoneId offset, Instant instant )
{
HashMap<String, Object> table = new HashMap<>( 9 );
table.put( "year", date.getLong( ChronoField.YEAR ) );
table.put( "month", date.getLong( ChronoField.MONTH_OF_YEAR ) );
table.put( "day", date.getLong( ChronoField.DAY_OF_MONTH ) );
table.put( "hour", date.getLong( ChronoField.HOUR_OF_DAY ) );
table.put( "min", date.getLong( ChronoField.MINUTE_OF_HOUR ) );
table.put( "sec", date.getLong( ChronoField.SECOND_OF_MINUTE ) );
table.put( "wday", date.getLong( WeekFields.SUNDAY_START.dayOfWeek() ) );
table.put( "yday", date.getLong( ChronoField.DAY_OF_YEAR ) );
table.put( "isdst", offset.getRules().isDaylightSavings( instant ) );
return table;
}
private static int getField( Map<?, ?> table, String field, int def ) throws LuaException
{
Object value = table.get( field );
if( value instanceof Number ) return ((Number) value).intValue();
if( def < 0 ) throw new LuaException( "field \"" + field + "\" missing in date table" );
return def;
}
private static Boolean getBoolField( Map<?, ?> table, String field ) throws LuaException
{
Object value = table.get( field );
if( value instanceof Boolean || value == null ) return (Boolean) value;
throw new LuaException( "field \"" + field + "\" missing in date table" );
}
private static final TemporalField CENTURY = map( ChronoField.YEAR, ValueRange.of( 0, 6 ), x -> (x / 100) % 100 );
private static final TemporalField ZERO_WEEK = map( WeekFields.SUNDAY_START.dayOfWeek(), ValueRange.of( 0, 6 ), x -> x - 1 );
private static TemporalField map( TemporalField field, ValueRange range, LongUnaryOperator convert )
{
return new TemporalField()
{
private final ValueRange range = ValueRange.of( 0, 99 );
@Override
public TemporalUnit getBaseUnit()
{
return field.getBaseUnit();
}
@Override
public TemporalUnit getRangeUnit()
{
return field.getRangeUnit();
}
@Override
public ValueRange range()
{
return range;
}
@Override
public boolean isDateBased()
{
return field.isDateBased();
}
@Override
public boolean isTimeBased()
{
return field.isTimeBased();
}
@Override
public boolean isSupportedBy( TemporalAccessor temporal )
{
return field.isSupportedBy( temporal );
}
@Override
public ValueRange rangeRefinedBy( TemporalAccessor temporal )
{
return range;
}
@Override
public long getFrom( TemporalAccessor temporal )
{
return convert.applyAsLong( temporal.getLong( field ) );
}
@Override
@SuppressWarnings( "unchecked" )
public <R extends Temporal> R adjustInto( R temporal, long newValue )
{
return (R) temporal.with( field, newValue );
}
};
}
}

View File

@@ -12,6 +12,11 @@ import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.shared.util.StringUtil;
import javax.annotation.Nonnull;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatterBuilder;
import java.util.*;
import static dan200.computercraft.core.apis.ArgumentHelper.*;
@@ -31,9 +36,9 @@ public class OSAPI implements ILuaAPI
private static class Timer
{
public int m_ticksLeft;
int m_ticksLeft;
public Timer( int ticksLeft )
Timer( int ticksLeft )
{
m_ticksLeft = ticksLeft;
}
@@ -41,10 +46,10 @@ public class OSAPI implements ILuaAPI
private static class Alarm implements Comparable<Alarm>
{
public final double m_time;
public final int m_day;
final double m_time;
final int m_day;
public Alarm( double time, int day )
Alarm( double time, int day )
{
m_time = time;
m_day = day;
@@ -73,9 +78,7 @@ public class OSAPI implements ILuaAPI
@Override
public String[] getNames()
{
return new String[] {
"os"
};
return new String[] { "os" };
}
@Override
@@ -184,11 +187,12 @@ public class OSAPI implements ILuaAPI
"day",
"cancelTimer",
"cancelAlarm",
"epoch"
"epoch",
"date",
};
}
private float getTimeForCalendar( Calendar c )
private static float getTimeForCalendar( Calendar c )
{
float time = c.get( Calendar.HOUR_OF_DAY );
time += c.get( Calendar.MINUTE ) / 60.0f;
@@ -196,7 +200,7 @@ public class OSAPI implements ILuaAPI
return time;
}
private int getDayForCalendar( Calendar c )
private static int getDayForCalendar( Calendar c )
{
GregorianCalendar g = c instanceof GregorianCalendar ? (GregorianCalendar) c : new GregorianCalendar();
int year = c.get( Calendar.YEAR );
@@ -209,7 +213,7 @@ public class OSAPI implements ILuaAPI
return day;
}
private long getEpochForCalendar( Calendar c )
private static long getEpochForCalendar( Calendar c )
{
return c.getTime().getTime();
}
@@ -282,6 +286,9 @@ public class OSAPI implements ILuaAPI
case 11:
{
// time
Object value = args.length > 0 ? args[0] : null;
if( value instanceof Map ) return new Object[] { LuaDateTime.fromTable( (Map<?, ?>) value ) };
String param = optString( args, 0, "ingame" );
switch( param.toLowerCase( Locale.ROOT ) )
{
@@ -355,9 +362,8 @@ public class OSAPI implements ILuaAPI
}
return null;
}
case 15:
case 15: // epoch
{
// epoch
String param = optString( args, 0, "ingame" );
switch( param.toLowerCase( Locale.ROOT ) )
{
@@ -377,14 +383,39 @@ public class OSAPI implements ILuaAPI
// Get in-game epoch
synchronized( m_alarms )
{
return new Object[] {
m_day * 86400000 + (int) (m_time * 3600000.0f)
};
return new Object[] { m_day * 86400000 + (int) (m_time * 3600000.0f) };
}
default:
throw new LuaException( "Unsupported operation" );
}
}
case 16: // date
{
String format = optString( args, 0, "%c" );
long time = optLong( args, 1, Instant.now().getEpochSecond() );
Instant instant = Instant.ofEpochSecond( time );
ZonedDateTime date;
ZoneOffset offset;
if( format.startsWith( "!" ) )
{
offset = ZoneOffset.UTC;
date = ZonedDateTime.ofInstant( instant, offset );
format = format.substring( 1 );
}
else
{
ZoneId id = ZoneId.systemDefault();
offset = id.getRules().getOffset( instant );
date = ZonedDateTime.ofInstant( instant, id );
}
if( format.equals( "*t" ) ) return new Object[] { LuaDateTime.toTable( date, offset, instant ) };
DateTimeFormatterBuilder formatter = new DateTimeFormatterBuilder();
LuaDateTime.format( formatter, format, offset );
return new Object[] { formatter.toFormatter( Locale.ROOT ).format( date ) };
}
default:
return null;
}

View File

@@ -36,7 +36,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
private Map<String, Integer> m_methodMap;
private boolean m_attached;
public PeripheralWrapper( IPeripheral peripheral, String side )
PeripheralWrapper( IPeripheral peripheral, String side )
{
super( m_environment );
m_side = side;
@@ -282,9 +282,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
@Override
public String[] getNames()
{
return new String[] {
"peripheral"
};
return new String[] { "peripheral" };
}
@Override
@@ -326,7 +324,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
"isPresent",
"getType",
"getMethods",
"call"
"call",
};
}
@@ -356,7 +354,6 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
ComputerSide side = ComputerSide.valueOfInsensitive( getString( args, 0 ) );
if( side != null )
{
String type = null;
synchronized( m_peripherals )
{
PeripheralWrapper p = m_peripherals[side.ordinal()];

View File

@@ -29,9 +29,7 @@ public class RedstoneAPI implements ILuaAPI
@Override
public String[] getNames()
{
return new String[] {
"rs", "redstone"
};
return new String[] { "rs", "redstone" };
}
@Nonnull

View File

@@ -33,9 +33,7 @@ public class TermAPI implements ILuaAPI
@Override
public String[] getNames()
{
return new String[] {
"term"
};
return new String[] { "term" };
}
@Nonnull
@@ -89,9 +87,7 @@ public class TermAPI implements ILuaAPI
public static Object[] encodeColour( int colour ) throws LuaException
{
return new Object[] {
1 << colour
};
return new Object[] { 1 << colour };
}
public static void setColour( Terminal terminal, int colour, double r, double g, double b )

View File

@@ -212,6 +212,7 @@ public class BinaryReadableHandle extends HandleGeneric
}
}
case 3: // close
checkOpen();
close();
return null;
case 4: // seek

View File

@@ -95,6 +95,7 @@ public class BinaryWritableHandle extends HandleGeneric
return null;
}
case 2: // close
checkOpen();
close();
return null;
case 3: // seek

View File

@@ -152,6 +152,7 @@ public class EncodedReadableHandle extends HandleGeneric
return null;
}
case 3: // close
checkOpen();
close();
return null;
default:

View File

@@ -93,6 +93,7 @@ public class EncodedWritableHandle extends HandleGeneric
return null;
}
case 3: // close
checkOpen();
close();
return null;
default:

View File

@@ -37,9 +37,14 @@ public abstract class HandleGeneric implements ILuaObject
protected final void close()
{
m_open = false;
IoUtil.closeQuietly( m_closable );
Closeable closeable = m_closable;
if( closeable != null )
{
IoUtil.closeQuietly( closeable );
m_closable = null;
}
}
/**
* Shared implementation for various file handle types

View File

@@ -72,7 +72,8 @@ public abstract class Resource<T extends Resource<T>> implements Closeable
*/
protected void dispose()
{
@SuppressWarnings( "unchecked" ) T thisT = (T) this;
@SuppressWarnings( "unchecked" )
T thisT = (T) this;
limiter.release( thisT );
}
@@ -95,7 +96,8 @@ public abstract class Resource<T extends Resource<T>> implements Closeable
public boolean queue( Consumer<T> task )
{
@SuppressWarnings( "unchecked" ) T thisT = (T) this;
@SuppressWarnings( "unchecked" )
T thisT = (T) this;
return limiter.queue( thisT, () -> task.accept( thisT ) );
}

View File

@@ -24,6 +24,7 @@ import java.io.Closeable;
import java.util.Arrays;
import static dan200.computercraft.core.apis.ArgumentHelper.optBoolean;
import static dan200.computercraft.core.apis.http.websocket.Websocket.CLOSE_EVENT;
import static dan200.computercraft.core.apis.http.websocket.Websocket.MESSAGE_EVENT;
public class WebsocketHandle implements ILuaObject, Closeable
@@ -53,15 +54,18 @@ public class WebsocketHandle implements ILuaObject, Closeable
switch( method )
{
case 0: // receive
checkOpen();
while( true )
{
checkOpen();
Object[] event = context.pullEvent( MESSAGE_EVENT );
if( event.length >= 3 && Objects.equal( event[1], websocket.address() ) )
Object[] event = context.pullEvent( null );
if( event.length >= 3 && Objects.equal( event[0], MESSAGE_EVENT ) && Objects.equal( event[1], websocket.address() ) )
{
return Arrays.copyOfRange( event, 2, event.length );
}
else if( event.length >= 2 && Objects.equal( event[0], CLOSE_EVENT ) && Objects.equal( event[1], websocket.address() ) && closed )
{
return null;
}
}
case 1: // send

View File

@@ -239,7 +239,6 @@ public class Computer
}
@Deprecated
@SuppressWarnings( "unused" )
public void advance( double dt )
{
tick();

View File

@@ -30,7 +30,10 @@ public enum ComputerSide
private final String name;
ComputerSide( String name ) {this.name = name;}
ComputerSide( String name )
{
this.name = name;
}
@Nonnull
public static ComputerSide valueOf( int side )

View File

@@ -133,7 +133,6 @@ public final class ComputerThread
synchronized( threadLock )
{
running = true;
if( monitor == null || !monitor.isAlive() ) (monitor = monitorFactory.newThread( new Monitor() )).start();
if( runners == null )
{
@@ -158,6 +157,8 @@ public final class ComputerThread
runnerFactory.newThread( runners[i] = new TaskRunner() ).start();
}
}
if( monitor == null || !monitor.isAlive() ) (monitor = monitorFactory.newThread( new Monitor() )).start();
}
}
@@ -368,7 +369,16 @@ public final class ComputerThread
{
TaskRunner runner = currentRunners[i];
// If we've no runner, skip.
if( runner == null ) continue;
if( runner == null || runner.owner == null || !runner.owner.isAlive() )
{
if( !running ) continue;
// Mark the old runner as dead and start a new one.
ComputerCraft.log.warn( "Previous runner ({}) has crashed, restarting!",
runner != null && runner.owner != null ? runner.owner.getName() : runner );
if( runner != null ) runner.running = false;
runnerFactory.newThread( runners[i] = new TaskRunner() ).start();
}
// If the runner has no work, skip
ComputerExecutor executor = runner.currentExecutor.get();
@@ -492,7 +502,7 @@ public final class ComputerThread
{
executor.work();
}
catch( Exception e )
catch( Exception | LinkageError | VirtualMachineError e )
{
ComputerCraft.log.error( "Error running task on computer #" + executor.getComputer().getID(), e );
}

View File

@@ -134,7 +134,7 @@ public final class MainThread
// Of course, we'll go over the MAX_TICK_TIME most of the time, but eventually that overrun will accumulate
// and we'll skip a whole tick - bringing the average back down again.
currentTick++;
budget += Math.min( budget + ComputerCraft.maxMainGlobalTime, ComputerCraft.maxMainGlobalTime );
budget = Math.min( budget + ComputerCraft.maxMainGlobalTime, ComputerCraft.maxMainGlobalTime );
canExecute = budget > 0;
// Cool down any warm computers.

View File

@@ -224,7 +224,7 @@ final class MainThreadExecutor implements IWorkMonitor
{
state = State.COOLING;
currentTick = MainThread.currentTick();
budget += Math.min( budget + ComputerCraft.maxMainComputerTime, ComputerCraft.maxMainComputerTime );
budget = Math.min( budget + ComputerCraft.maxMainComputerTime, ComputerCraft.maxMainComputerTime );
if( budget < ComputerCraft.maxMainComputerTime ) return false;
state = State.COOL;

View File

@@ -6,6 +6,7 @@
package dan200.computercraft.core.filesystem;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IMount;
import javax.annotation.Nonnull;
@@ -95,7 +96,7 @@ public class ComboMount implements IMount
}
else
{
throw new IOException( "/" + path + ": Not a directory" );
throw new FileOperationException( path, "Not a directory" );
}
}
@@ -110,7 +111,7 @@ public class ComboMount implements IMount
return part.getSize( path );
}
}
throw new IOException( "/" + path + ": No such file" );
throw new FileOperationException( path, "No such file" );
}
@Nonnull
@@ -126,7 +127,7 @@ public class ComboMount implements IMount
return part.openForRead( path );
}
}
throw new IOException( "/" + path + ": No such file" );
throw new FileOperationException( path, "No such file" );
}
@Nonnull
@@ -141,6 +142,6 @@ public class ComboMount implements IMount
return part.openChannelForRead( path );
}
}
throw new IOException( "/" + path + ": No such file" );
throw new FileOperationException( path, "No such file" );
}
}

View File

@@ -6,6 +6,7 @@
package dan200.computercraft.core.filesystem;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IMount;
import javax.annotation.Nonnull;
@@ -44,7 +45,7 @@ public class EmptyMount implements IMount
@Deprecated
public InputStream openForRead( @Nonnull String path ) throws IOException
{
throw new IOException( "/" + path + ": No such file" );
throw new FileOperationException( path, "No such file" );
}
@Nonnull
@@ -52,6 +53,6 @@ public class EmptyMount implements IMount
@Deprecated
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
{
throw new IOException( "/" + path + ": No such file" );
throw new FileOperationException( path, "No such file" );
}
}

View File

@@ -7,6 +7,7 @@
package dan200.computercraft.core.filesystem;
import com.google.common.collect.Sets;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IWritableMount;
import javax.annotation.Nonnull;
@@ -167,12 +168,12 @@ public class FileMount implements IWritableMount
{
if( !created() )
{
if( !path.isEmpty() ) throw new IOException( "/" + path + ": Not a directory" );
if( !path.isEmpty() ) throw new FileOperationException( path, "Not a directory" );
return;
}
File file = getRealPath( path );
if( !file.exists() || !file.isDirectory() ) throw new IOException( "/" + path + ": Not a directory" );
if( !file.exists() || !file.isDirectory() ) throw new FileOperationException( path, "Not a directory" );
String[] paths = file.list();
for( String subPath : paths )
@@ -194,7 +195,7 @@ public class FileMount implements IWritableMount
if( file.exists() ) return file.isDirectory() ? 0 : file.length();
}
throw new IOException( "/" + path + ": No such file" );
throw new FileOperationException( path, "No such file" );
}
@Nonnull
@@ -208,7 +209,7 @@ public class FileMount implements IWritableMount
if( file.exists() && !file.isDirectory() ) return new FileInputStream( file );
}
throw new IOException( "/" + path + ": No such file" );
throw new FileOperationException( path, "No such file" );
}
@Nonnull
@@ -221,7 +222,7 @@ public class FileMount implements IWritableMount
if( file.exists() && !file.isDirectory() ) return FileChannel.open( file.toPath(), READ_OPTIONS );
}
throw new IOException( "/" + path + ": No such file" );
throw new FileOperationException( path, "No such file" );
}
// IWritableMount implementation
@@ -233,7 +234,7 @@ public class FileMount implements IWritableMount
File file = getRealPath( path );
if( file.exists() )
{
if( !file.isDirectory() ) throw new IOException( "/" + path + ": File exists" );
if( !file.isDirectory() ) throw new FileOperationException( path, "File exists" );
return;
}
@@ -247,7 +248,7 @@ public class FileMount implements IWritableMount
if( getRemainingSpace() < dirsToCreate * MINIMUM_FILE_SIZE )
{
throw new IOException( "/" + path + ": Out of space" );
throw new FileOperationException( path, "Out of space" );
}
if( file.mkdirs() )
@@ -256,14 +257,14 @@ public class FileMount implements IWritableMount
}
else
{
throw new IOException( "/" + path + ": Access denied" );
throw new FileOperationException( path, "Access denied" );
}
}
@Override
public void delete( @Nonnull String path ) throws IOException
{
if( path.isEmpty() ) throw new IOException( "/" + path + ": Access denied" );
if( path.isEmpty() ) throw new FileOperationException( path, "Access denied" );
if( created() )
{
@@ -319,7 +320,7 @@ public class FileMount implements IWritableMount
{
create();
File file = getRealPath( path );
if( file.exists() && file.isDirectory() ) throw new IOException( "/" + path + ": Cannot write to directory" );
if( file.exists() && file.isDirectory() ) throw new FileOperationException( path, "Cannot write to directory" );
if( file.exists() )
{
@@ -327,7 +328,7 @@ public class FileMount implements IWritableMount
}
else if( getRemainingSpace() < MINIMUM_FILE_SIZE )
{
throw new IOException( "/" + path + ": Out of space" );
throw new FileOperationException( path, "Out of space" );
}
m_usedSpace += MINIMUM_FILE_SIZE;
@@ -340,12 +341,12 @@ public class FileMount implements IWritableMount
{
if( !created() )
{
throw new IOException( "/" + path + ": No such file" );
throw new FileOperationException( path, "No such file" );
}
File file = getRealPath( path );
if( !file.exists() ) throw new IOException( "/" + path + ": No such file" );
if( file.isDirectory() ) throw new IOException( "/" + path + ": Cannot write to directory" );
if( !file.exists() ) throw new FileOperationException( path, "No such file" );
if( file.isDirectory() ) throw new FileOperationException( path, "Cannot write to directory" );
// Allowing seeking when appending is not recommended, so we use a separate channel.
return new WritableCountingChannel(

View File

@@ -8,6 +8,7 @@ package dan200.computercraft.core.filesystem;
import com.google.common.io.ByteStreams;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IFileSystem;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
@@ -45,7 +46,7 @@ public class FileSystem
m_writableMount = null;
}
public MountWrapper( String label, String location, IWritableMount mount )
MountWrapper( String label, String location, IWritableMount mount )
{
this( label, location, (IMount) mount );
m_writableMount = mount;
@@ -107,7 +108,7 @@ public class FileSystem
}
catch( IOException e )
{
throw new FileSystemException( e.getMessage() );
throw localExceptionOf( e );
}
}
@@ -122,12 +123,12 @@ public class FileSystem
}
else
{
throw new FileSystemException( "/" + path + ": Not a directory" );
throw localExceptionOf( path, "Not a directory" );
}
}
catch( IOException e )
{
throw new FileSystemException( e.getMessage() );
throw localExceptionOf( e );
}
}
@@ -149,12 +150,12 @@ public class FileSystem
}
else
{
throw new FileSystemException( "/" + path + ": No such file" );
throw localExceptionOf( path, "No such file" );
}
}
catch( IOException e )
{
throw new FileSystemException( e.getMessage() );
throw localExceptionOf( e );
}
}
@@ -169,12 +170,12 @@ public class FileSystem
}
else
{
throw new FileSystemException( "/" + path + ": No such file" );
throw localExceptionOf( path, "No such file" );
}
}
catch( IOException e )
{
throw new FileSystemException( e.getMessage() );
throw localExceptionOf( e );
}
}
@@ -182,19 +183,14 @@ public class FileSystem
public void makeDirectory( String path ) throws FileSystemException
{
if( m_writableMount == null )
{
throw new FileSystemException( "/" + path + ": Access denied" );
}
if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
path = toLocal( path );
try
{
path = toLocal( path );
if( m_mount.exists( path ) )
{
if( !m_mount.isDirectory( path ) )
{
throw new FileSystemException( "/" + path + ": File exists" );
}
if( !m_mount.isDirectory( path ) ) throw localExceptionOf( path, "File exists" );
}
else
{
@@ -203,16 +199,14 @@ public class FileSystem
}
catch( IOException e )
{
throw new FileSystemException( e.getMessage() );
throw localExceptionOf( e );
}
}
public void delete( String path ) throws FileSystemException
{
if( m_writableMount == null )
{
throw new FileSystemException( "/" + path + ": Access denied" );
}
if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
try
{
path = toLocal( path );
@@ -227,22 +221,20 @@ public class FileSystem
}
catch( IOException e )
{
throw new FileSystemException( e.getMessage() );
throw localExceptionOf( e );
}
}
public WritableByteChannel openForWrite( String path ) throws FileSystemException
{
if( m_writableMount == null )
{
throw new FileSystemException( "/" + path + ": Access denied" );
}
if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
path = toLocal( path );
try
{
path = toLocal( path );
if( m_mount.exists( path ) && m_mount.isDirectory( path ) )
{
throw new FileSystemException( "/" + path + ": Cannot write to directory" );
throw localExceptionOf( path, "Cannot write to directory" );
}
else
{
@@ -263,19 +255,17 @@ public class FileSystem
}
catch( IOException e )
{
throw new FileSystemException( e.getMessage() );
throw localExceptionOf( e );
}
}
public WritableByteChannel openForAppend( String path ) throws FileSystemException
{
if( m_writableMount == null )
{
throw new FileSystemException( "/" + path + ": Access denied" );
}
if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
path = toLocal( path );
try
{
path = toLocal( path );
if( !m_mount.exists( path ) )
{
if( !path.isEmpty() )
@@ -290,7 +280,7 @@ public class FileSystem
}
else if( m_mount.isDirectory( path ) )
{
throw new FileSystemException( "/" + path + ": Cannot write to directory" );
throw localExceptionOf( path, "Cannot write to directory" );
}
else
{
@@ -303,16 +293,36 @@ public class FileSystem
}
catch( IOException e )
{
throw new FileSystemException( e.getMessage() );
throw localExceptionOf( e );
}
}
// private members
private String toLocal( String path )
{
return FileSystem.toLocal( path, m_location );
}
private FileSystemException localExceptionOf( IOException e )
{
if( !m_location.isEmpty() && e instanceof FileOperationException )
{
FileOperationException ex = (FileOperationException) e;
if( ex.getFilename() != null ) return localExceptionOf( ex.getFilename(), ex.getMessage() );
}
return new FileSystemException( e.getMessage() );
}
private FileSystemException localExceptionOf( String path, String message )
{
if( !m_location.isEmpty() ) path = path.isEmpty() ? m_location : m_location + "/" + path;
return exceptionOf( path, message );
}
private static FileSystemException exceptionOf( String path, String message )
{
return new FileSystemException( "/" + path + ": " + message );
}
}
private final FileSystemWrapperMount m_wrapper = new FileSystemWrapperMount( this );
@@ -769,7 +779,7 @@ public class FileSystem
// Clean the path or illegal characters.
final char[] specialChars = new char[] {
'"', ':', '<', '>', '?', '|' // Sorted by ascii value (important)
'"', ':', '<', '>', '?', '|', // Sorted by ascii value (important)
};
StringBuilder cleanName = new StringBuilder();

View File

@@ -9,6 +9,7 @@ package dan200.computercraft.core.filesystem;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.io.ByteStreams;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.core.apis.handles.ArrayByteChannel;
import dan200.computercraft.shared.util.IoUtil;
@@ -92,7 +93,7 @@ public class JarMount implements IMount
new MountReference( this );
// Read in all the entries
root = new FileEntry( "" );
root = new FileEntry();
Enumeration<? extends ZipEntry> zipEntries = zip.entries();
while( zipEntries.hasMoreElements() )
{
@@ -139,7 +140,7 @@ public class JarMount implements IMount
FileEntry nextEntry = lastEntry.children.get( part );
if( nextEntry == null || !nextEntry.isDirectory() )
{
lastEntry.children.put( part, nextEntry = new FileEntry( part ) );
lastEntry.children.put( part, nextEntry = new FileEntry() );
}
lastEntry = nextEntry;
@@ -166,7 +167,7 @@ public class JarMount implements IMount
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
{
FileEntry file = get( path );
if( file == null || !file.isDirectory() ) throw new IOException( "/" + path + ": Not a directory" );
if( file == null || !file.isDirectory() ) throw new FileOperationException( path, "Not a directory" );
file.list( contents );
}
@@ -176,7 +177,7 @@ public class JarMount implements IMount
{
FileEntry file = get( path );
if( file != null ) return file.size;
throw new IOException( "/" + path + ": No such file" );
throw new FileOperationException( path, "No such file" );
}
@Nonnull
@@ -218,22 +219,15 @@ public class JarMount implements IMount
}
}
throw new IOException( "/" + path + ": No such file" );
throw new FileOperationException( path, "No such file" );
}
private static class FileEntry
{
final String name;
String path;
long size;
Map<String, FileEntry> children;
FileEntry( String name )
{
this.name = name;
}
void setup( ZipEntry entry )
{
path = entry.getName();

View File

@@ -242,7 +242,7 @@ public class CobaltLuaMachine implements ILuaMachine
}
catch( InterruptedException e )
{
throw new OrphanedThread();
throw new InterruptedError( e );
}
catch( LuaException e )
{
@@ -550,7 +550,7 @@ public class CobaltLuaMachine implements ILuaMachine
{
if( ComputerCraft.logPeripheralErrors ) ComputerCraft.log.error( "Error running task", t );
m_computer.queueEvent( "task_complete", new Object[] {
taskID, false, "Java Exception Thrown: " + t
taskID, false, "Java Exception Thrown: " + t,
} );
}
};

View File

@@ -8,7 +8,7 @@ package dan200.computercraft.core.terminal;
public class TextBuffer
{
public char[] m_text;
private final char[] m_text;
public TextBuffer( char c, int length )
{

View File

@@ -402,7 +402,7 @@ public final class Config
if( oldProperty.isList() )
{
oldProperty.setValues( oldProperty.getStringList() );
oldProperty.setValues( newProperty.getStringList() );
}
else
{

View File

@@ -51,7 +51,6 @@ public abstract class BlockGeneric extends Block implements ITileEntityProvider
@Override
@Deprecated
@SuppressWarnings( "deprecation" )
public final void neighborChanged( IBlockState state, World world, BlockPos pos, Block neighbourBlock, BlockPos neighbourPos )
{
TileEntity tile = world.getTileEntity( pos );

View File

@@ -63,7 +63,6 @@ public abstract class TileGeneric extends TileEntity
{
}
@SuppressWarnings( "deprecation" )
public void onNeighbourChange( @Nonnull BlockPos neighbour )
{
onNeighbourChange();

View File

@@ -47,9 +47,7 @@ public class CommandAPI implements ILuaAPI
@Override
public String[] getNames()
{
return new String[] {
"commands"
};
return new String[] { "commands" };
}
@Nonnull
@@ -62,7 +60,7 @@ public class CommandAPI implements ILuaAPI
"list",
"getBlockPosition",
"getBlockInfos",
"getBlockInfo"
"getBlockInfo",
};
}

View File

@@ -32,7 +32,7 @@ public class TileComputer extends TileComputerBase
ServerComputer computer = new ServerComputer(
getWorld(),
id,
m_label,
label,
instanceID,
family,
ComputerCraft.terminalWidth_computer,

View File

@@ -22,7 +22,10 @@ import dan200.computercraft.shared.util.DirectionUtil;
import dan200.computercraft.shared.util.RedstoneUtil;
import joptsimple.internal.Strings;
import net.minecraft.block.Block;
import net.minecraft.block.BlockRedstoneWire;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
@@ -34,6 +37,7 @@ import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.IWorldNameable;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -43,7 +47,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
{
private int m_instanceID = -1;
private int m_computerID = -1;
protected String m_label = null;
protected String label = null;
private boolean m_on = false;
boolean m_startOn = false;
private boolean m_fresh = false;
@@ -124,16 +128,14 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
@Override
public boolean getRedstoneConnectivity( EnumFacing side )
{
if( side == null ) return false;
ComputerSide localDir = remapLocalSide( DirectionUtil.toLocal( this, side.getOpposite() ) );
return !isRedstoneBlockedOnSide( localDir );
return true;
}
@Override
public int getRedstoneOutput( EnumFacing side )
{
ComputerSide localDir = remapLocalSide( DirectionUtil.toLocal( this, side ) );
if( !isRedstoneBlockedOnSide( localDir ) && world != null && !world.isRemote )
if( world != null && !world.isRemote )
{
ServerComputer computer = getServerComputer();
if( computer != null ) return computer.getRedstoneOutput( localDir );
@@ -144,15 +146,14 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
@Override
public boolean getBundledRedstoneConnectivity( @Nonnull EnumFacing side )
{
ComputerSide localDir = remapLocalSide( DirectionUtil.toLocal( this, side ) );
return !isRedstoneBlockedOnSide( localDir );
return true;
}
@Override
public int getBundledRedstoneOutput( @Nonnull EnumFacing side )
{
ComputerSide localDir = remapLocalSide( DirectionUtil.toLocal( this, side ) );
if( !isRedstoneBlockedOnSide( localDir ) && !world.isRemote )
if( !world.isRemote )
{
ServerComputer computer = getServerComputer();
if( computer != null ) return computer.getBundledRedstoneOutput( localDir );
@@ -189,7 +190,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
m_fresh = false;
m_computerID = computer.getID();
m_label = computer.getLabel();
label = computer.getLabel();
m_on = computer.isOn();
if( computer.hasOutputChanged() ) updateOutput();
@@ -211,9 +212,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
{
nbt.setInteger( "computerID", m_computerID );
}
if( m_label != null )
if( label != null )
{
nbt.setString( "label", m_label );
nbt.setString( "label", label );
}
nbt.setBoolean( "on", m_on );
return super.writeToNBT( nbt );
@@ -247,7 +248,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
m_computerID = id;
// Load label
m_label = nbt.hasKey( "label" ) ? nbt.getString( "label" ) : null;
label = nbt.hasKey( "label" ) ? nbt.getString( "label" ) : null;
// Load power state
m_startOn = nbt.getBoolean( "on" );
@@ -259,11 +260,6 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
return false;
}
protected boolean isRedstoneBlockedOnSide( ComputerSide localSide )
{
return false;
}
protected ComputerSide remapLocalSide( ComputerSide localSide )
{
return localSide;
@@ -273,17 +269,36 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
{
EnumFacing offsetSide = dir.getOpposite();
ComputerSide localDir = remapLocalSide( DirectionUtil.toLocal( this, dir ) );
if( !isRedstoneBlockedOnSide( localDir ) )
{
computer.setRedstoneInput( localDir, getWorld().getRedstonePower( offset, dir ) );
computer.setRedstoneInput( localDir, getRedstoneInput( world, offset, dir ) );
computer.setBundledRedstoneInput( localDir, BundledRedstone.getOutput( getWorld(), offset, offsetSide ) );
}
if( !isPeripheralBlockedOnSide( localDir ) )
{
computer.setPeripheral( localDir, Peripherals.getPeripheral( getWorld(), offset, offsetSide ) );
}
}
/**
* 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 net.minecraft.block.BlockRedstoneDiode#calculateInputStrength(World, BlockPos, IBlockState)
*/
protected static int getRedstoneInput( World world, BlockPos pos, EnumFacing side )
{
int power = world.getRedstonePower( pos, side );
if( power >= 15 ) return power;
IBlockState neighbour = world.getBlockState( pos );
return neighbour.getBlock() == Blocks.REDSTONE_WIRE
? Math.max( power, neighbour.getValue( BlockRedstoneWire.POWER ) )
: power;
}
public void updateInput()
{
if( getWorld() == null || getWorld().isRemote ) return;
@@ -346,7 +361,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
@Override
public final String getLabel()
{
return m_label;
return label;
}
@Override
@@ -363,9 +378,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
@Override
public final void setLabel( String label )
{
if( getWorld().isRemote || Objects.equals( m_label, label ) ) return;
if( getWorld().isRemote || Objects.equals( this.label, label ) ) return;
m_label = label;
this.label = label;
ServerComputer computer = getServerComputer();
if( computer != null ) computer.setLabel( label );
markDirty();
@@ -438,8 +453,8 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
protected void writeDescription( @Nonnull NBTTagCompound nbt )
{
super.writeDescription( nbt );
nbt.setInteger( "instanceID", createServerComputer().getInstanceID() );
if( m_label != null ) nbt.setString( "label", m_label );
if( m_instanceID >= 0 ) nbt.setInteger( "instanceID", m_instanceID );
if( label != null ) nbt.setString( "label", label );
if( m_computerID >= 0 ) nbt.setInteger( "computerID", m_computerID );
}
@@ -448,7 +463,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
{
super.readDescription( nbt );
m_instanceID = nbt.getInteger( "instanceID" );
m_label = nbt.hasKey( "label" ) ? nbt.getString( "label" ) : null;
label = nbt.hasKey( "label" ) ? nbt.getString( "label" ) : null;
m_computerID = nbt.hasKey( "computerID" ) ? nbt.getInteger( "computerID" ) : -1;
}
@@ -459,7 +474,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
unload();
m_instanceID = copy.m_instanceID;
m_computerID = copy.m_computerID;
m_label = copy.m_label;
label = copy.label;
m_on = copy.m_on;
m_startOn = copy.m_startOn;
updateBlock();
@@ -478,13 +493,13 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
@Override
public String getName()
{
return hasCustomName() ? m_label : getBlockType().getTranslationKey();
return hasCustomName() ? label : getBlockType().getTranslationKey();
}
@Override
public boolean hasCustomName()
{
return !Strings.isNullOrEmpty( m_label );
return !Strings.isNullOrEmpty( label );
}
@Nonnull

View File

@@ -11,9 +11,9 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class ComputerRegistry<TComputer extends IComputer>
public class ComputerRegistry<T extends IComputer>
{
private Map<Integer, TComputer> m_computers;
private Map<Integer, T> m_computers;
private int m_nextUnusedInstanceID;
private int m_sessionID;
@@ -33,12 +33,12 @@ public class ComputerRegistry<TComputer extends IComputer>
return m_nextUnusedInstanceID++;
}
public Collection<TComputer> getComputers()
public Collection<T> getComputers()
{
return m_computers.values();
}
public TComputer get( int instanceID )
public T get( int instanceID )
{
if( instanceID >= 0 )
{
@@ -55,7 +55,7 @@ public class ComputerRegistry<TComputer extends IComputer>
return m_computers.containsKey( instanceID );
}
public void add( int instanceID, TComputer computer )
public void add( int instanceID, T computer )
{
if( m_computers.containsKey( instanceID ) )
{

View File

@@ -175,12 +175,13 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
FMLCommonHandler handler = FMLCommonHandler.instance();
if( handler != null )
{
IMessage packet = createTerminalPacket();
IMessage packet = null;
MinecraftServer server = handler.getMinecraftServerInstance();
for( EntityPlayerMP player : server.getPlayerList().getPlayers() )
{
if( isInteracting( player ) )
{
if( packet == null ) packet = createTerminalPacket();
NetworkHandler.sendToPlayer( player, packet );
}
}
@@ -219,6 +220,13 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
return m_instanceID;
}
/*
* getID and getLabel are deprecated on IComputer, as they do not make sense in ClientComputer.
* However, for compatibility reasons, we still need these here, and have no choice but to override the IComputer methods.
*
* Hence, we suppress the deprecation warning.
*/
@Override
@SuppressWarnings( "deprecation" )
public int getID()

View File

@@ -1,9 +1,8 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.integration.charset;
import dan200.computercraft.shared.common.TileGeneric;

View File

@@ -1,9 +1,8 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.integration.charset;
import dan200.computercraft.api.redstone.IBundledRedstoneProvider;

View File

@@ -1,9 +1,8 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.integration.charset;
import dan200.computercraft.ComputerCraft;

View File

@@ -90,6 +90,7 @@ public class JEIComputerCraft implements IModPlugin
ingredients.addIngredientsAtRuntime( VanillaTypes.ITEM, upgradeItems );
// Hide all upgrade recipes
@SuppressWarnings( "unchecked" )
IRecipeCategory<? extends IRecipeWrapper> category = (IRecipeCategory<? extends IRecipeWrapper>) registry.getRecipeCategory( VanillaRecipeCategoryUid.CRAFTING );
if( category != null )
{

View File

@@ -39,5 +39,8 @@ public enum PeripheralType implements IStringSerializable
}
@Override
public String toString() { return name; }
public String toString()
{
return name;
}
}

View File

@@ -52,7 +52,7 @@ public class CommandBlockPeripheral implements IPeripheral
{
case 0: // getCommand
return context.executeMainThreadTask( () -> new Object[] {
m_commandBlock.getCommandBlockLogic().getCommand()
m_commandBlock.getCommandBlockLogic().getCommand(),
} );
case 1:
{

View File

@@ -11,6 +11,7 @@ import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.shared.MediaProviders;
import dan200.computercraft.shared.media.items.ItemDiskLegacy;
import dan200.computercraft.shared.util.StringUtil;
import net.minecraft.item.Item;
@@ -20,11 +21,11 @@ import javax.annotation.Nonnull;
import static dan200.computercraft.core.apis.ArgumentHelper.optString;
public class DiskDrivePeripheral implements IPeripheral
class DiskDrivePeripheral implements IPeripheral
{
private final TileDiskDrive m_diskDrive;
public DiskDrivePeripheral( TileDiskDrive diskDrive )
DiskDrivePeripheral( TileDiskDrive diskDrive )
{
m_diskDrive = diskDrive;
}
@@ -51,12 +52,12 @@ public class DiskDrivePeripheral implements IPeripheral
"playAudio",
"stopAudio",
"ejectDisk",
"getDiskID"
"getDiskID",
};
}
@Override
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
{
switch( method )
{
@@ -64,21 +65,26 @@ public class DiskDrivePeripheral implements IPeripheral
return new Object[] { !m_diskDrive.getDiskStack().isEmpty() };
case 1: // getDiskLabel
{
IMedia media = m_diskDrive.getDiskMedia();
return media == null ? null : new Object[] { media.getLabel( m_diskDrive.getDiskStack() ) };
ItemStack stack = m_diskDrive.getDiskStack();
IMedia media = MediaProviders.get( stack );
return media == null ? null : new Object[] { media.getLabel( stack ) };
}
case 2: // setDiskLabel
{
String label = optString( arguments, 0, null );
IMedia media = m_diskDrive.getDiskMedia();
return context.executeMainThreadTask( () -> {
ItemStack stack = m_diskDrive.getDiskStack();
IMedia media = MediaProviders.get( stack );
if( media == null ) return null;
ItemStack disk = m_diskDrive.getDiskStack();
label = StringUtil.normaliseLabel( label );
if( !media.setLabel( disk, label ) ) throw new LuaException( "Disk label cannot be changed" );
m_diskDrive.setDiskStack( disk );
if( !media.setLabel( stack, StringUtil.normaliseLabel( label ) ) )
{
throw new LuaException( "Disk label cannot be changed" );
}
m_diskDrive.setDiskStack( stack );
return null;
} );
}
case 3: // hasData
return new Object[] { m_diskDrive.getDiskMountPath( computer ) != null };
@@ -87,14 +93,16 @@ public class DiskDrivePeripheral implements IPeripheral
case 5:
{
// hasAudio
IMedia media = m_diskDrive.getDiskMedia();
return new Object[] { media != null && media.getAudio( m_diskDrive.getDiskStack() ) != null };
ItemStack stack = m_diskDrive.getDiskStack();
IMedia media = MediaProviders.get( stack );
return new Object[] { media != null && media.getAudio( stack ) != null };
}
case 6:
{
// getAudioTitle
IMedia media = m_diskDrive.getDiskMedia();
return new Object[] { media != null ? media.getAudioTitle( m_diskDrive.getDiskStack() ) : false };
ItemStack stack = m_diskDrive.getDiskStack();
IMedia media = MediaProviders.get( stack );
return new Object[] { media != null ? media.getAudioTitle( stack ) : false };
}
case 7: // playAudio
m_diskDrive.playDiskAudio();
@@ -131,8 +139,7 @@ public class DiskDrivePeripheral implements IPeripheral
@Override
public boolean equals( IPeripheral other )
{
if( this == other ) return true;
return other instanceof DiskDrivePeripheral && ((DiskDrivePeripheral) other).m_diskDrive == m_diskDrive;
return this == other || other instanceof DiskDrivePeripheral && ((DiskDrivePeripheral) other).m_diskDrive == m_diskDrive;
}
@Nonnull

View File

@@ -318,35 +318,31 @@ public class TileDiskDrive extends TilePeripheralBase implements DefaultInventor
}
@Nonnull
public ItemStack getDiskStack()
ItemStack getDiskStack()
{
return getStackInSlot( 0 );
}
public void setDiskStack( @Nonnull ItemStack stack )
void setDiskStack( @Nonnull ItemStack stack )
{
setInventorySlotContents( 0, stack );
}
public IMedia getDiskMedia()
private IMedia getDiskMedia()
{
return MediaProviders.get( getDiskStack() );
}
public String getDiskMountPath( IComputerAccess computer )
String getDiskMountPath( IComputerAccess computer )
{
synchronized( this )
{
if( m_computers.containsKey( computer ) )
{
MountInfo info = m_computers.get( computer );
return info.mountPath;
return info != null ? info.mountPath : null;
}
}
return null;
}
public void mount( IComputerAccess computer )
void mount( IComputerAccess computer )
{
synchronized( this )
{
@@ -355,7 +351,7 @@ public class TileDiskDrive extends TilePeripheralBase implements DefaultInventor
}
}
public void unmount( IComputerAccess computer )
void unmount( IComputerAccess computer )
{
synchronized( this )
{
@@ -364,7 +360,7 @@ public class TileDiskDrive extends TilePeripheralBase implements DefaultInventor
}
}
public void playDiskAudio()
void playDiskAudio()
{
synchronized( this )
{
@@ -377,7 +373,7 @@ public class TileDiskDrive extends TilePeripheralBase implements DefaultInventor
}
}
public void stopDiskAudio()
void stopDiskAudio()
{
synchronized( this )
{
@@ -386,7 +382,7 @@ public class TileDiskDrive extends TilePeripheralBase implements DefaultInventor
}
}
public void ejectDisk()
void ejectDisk()
{
synchronized( this )
{

View File

@@ -87,7 +87,7 @@ public abstract class ModemPeripheral implements IPeripheral, IPacketSender, IPa
for( IComputerAccess computer : m_computers )
{
computer.queueEvent( "modem_message", new Object[] {
computer.getAttachmentName(), packet.getChannel(), packet.getReplyChannel(), packet.getPayload(), distance
computer.getAttachmentName(), packet.getChannel(), packet.getReplyChannel(), packet.getPayload(), distance,
} );
}
}
@@ -103,7 +103,7 @@ public abstract class ModemPeripheral implements IPeripheral, IPacketSender, IPa
for( IComputerAccess computer : m_computers )
{
computer.queueEvent( "modem_message", new Object[] {
computer.getAttachmentName(), packet.getChannel(), packet.getReplyChannel(), packet.getPayload()
computer.getAttachmentName(), packet.getChannel(), packet.getReplyChannel(), packet.getPayload(),
} );
}
}

View File

@@ -277,7 +277,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
private final String[] m_methods;
private final Map<String, Integer> m_methodMap;
public RemotePeripheralWrapper( WiredModemElement element, IPeripheral peripheral, IComputerAccess computer, String name )
RemotePeripheralWrapper( WiredModemElement element, IPeripheral peripheral, IComputerAccess computer, String name )
{
m_element = element;
m_peripheral = peripheral;

View File

@@ -142,7 +142,7 @@ public class TileMonitor extends TileGeneric implements ITilePeripheral, IPeriph
for( IComputerAccess computer : monitor.m_computers )
{
computer.queueEvent( "monitor_resize", new Object[] {
computer.getAttachmentName()
computer.getAttachmentName(),
} );
}
}
@@ -339,7 +339,7 @@ public class TileMonitor extends TileGeneric implements ITilePeripheral, IPeriph
return EnumFacing.WEST;
}
private EnumFacing getDown()
public EnumFacing getDown()
{
int dir = getDir();
if( dir <= 5 ) return EnumFacing.UP;
@@ -660,7 +660,7 @@ public class TileMonitor extends TileGeneric implements ITilePeripheral, IPeriph
for( IComputerAccess computer : monitor.m_computers )
{
computer.queueEvent( "monitor_touch", new Object[] {
computer.getAttachmentName(), xCharPos, yCharPos
computer.getAttachmentName(), xCharPos, yCharPos,
} );
}
}

View File

@@ -11,6 +11,7 @@ import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.util.StringUtil;
import javax.annotation.Nonnull;
@@ -51,8 +52,13 @@ public class PrinterPeripheral implements IPeripheral
}
@Override
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException, InterruptedException
{
// FIXME: There's a theoretical race condition here between getCurrentPage and then using the page. Ideally
// we'd lock on the page, consume it, and unlock.
// FIXME: None of our page modification functions actually mark the tile as dirty, so the page may not be
// persisted correctly.
switch( method )
{
case 0: // write
@@ -89,10 +95,13 @@ public class PrinterPeripheral implements IPeripheral
return new Object[] { width, height };
}
case 4: // newPage
return new Object[] { m_printer.startNewPage() };
return context.executeMainThreadTask( () -> new Object[] { m_printer.startNewPage() } );
case 5: // endPage
getCurrentPage();
return context.executeMainThreadTask( () -> {
getCurrentPage();
return new Object[] { m_printer.endCurrentPage() };
} );
case 6: // getInkLevel
return new Object[] { m_printer.getInkLevel() };
case 7:
@@ -100,7 +109,7 @@ public class PrinterPeripheral implements IPeripheral
// setPageTitle
String title = optString( args, 0, "" );
getCurrentPage();
m_printer.setPageTitle( title );
m_printer.setPageTitle( StringUtil.normaliseLabel( title ) );
return null;
}
case 8: // getPaperLevel
@@ -123,13 +132,11 @@ public class PrinterPeripheral implements IPeripheral
return m_printer;
}
@Nonnull
private Terminal getCurrentPage() throws LuaException
{
Terminal currentPage = m_printer.getCurrentPage();
if( currentPage == null )
{
throw new LuaException( "Page not started" );
}
if( currentPage == null ) throw new LuaException( "Page not started" );
return currentPage;
}
}

View File

@@ -45,9 +45,9 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
{
// Statics
private static final int[] bottomSlots = new int[] { 7, 8, 9, 10, 11, 12 };
private static final int[] topSlots = new int[] { 1, 2, 3, 4, 5, 6 };
private static final int[] sideSlots = new int[] { 0 };
private static final int[] BOTTOM_SLOTS = new int[] { 7, 8, 9, 10, 11, 12 };
private static final int[] TOP_SLOTS = new int[] { 1, 2, 3, 4, 5, 6 };
private static final int[] SIDE_SLOTS = new int[] { 0 };
// Members
@@ -88,18 +88,12 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
}
// Read inventory
synchronized( m_inventory )
NBTTagList itemList = nbt.getTagList( "Items", Constants.NBT.TAG_COMPOUND );
for( int i = 0; i < itemList.tagCount(); i++ )
{
NBTTagList nbttaglist = nbt.getTagList( "Items", Constants.NBT.TAG_COMPOUND );
for( int i = 0; i < nbttaglist.tagCount(); i++ )
{
NBTTagCompound itemTag = nbttaglist.getCompoundTagAt( i );
int j = itemTag.getByte( "Slot" ) & 0xff;
if( j < m_inventory.size() )
{
m_inventory.set( j, new ItemStack( itemTag ) );
}
}
NBTTagCompound itemTag = itemList.getCompoundTagAt( i );
int slot = itemTag.getByte( "Slot" ) & 0xff;
if( slot < m_inventory.size() ) m_inventory.set( slot, new ItemStack( itemTag ) );
}
}
@@ -116,21 +110,18 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
}
// Write inventory
synchronized( m_inventory )
{
NBTTagList nbttaglist = new NBTTagList();
NBTTagList itemList = new NBTTagList();
for( int i = 0; i < m_inventory.size(); i++ )
{
if( !m_inventory.get( i ).isEmpty() )
{
ItemStack stack = m_inventory.get( i );
if( stack.isEmpty() ) continue;
NBTTagCompound tag = new NBTTagCompound();
tag.setByte( "Slot", (byte) i );
m_inventory.get( i ).writeToNBT( tag );
nbttaglist.appendTag( tag );
}
}
nbt.setTag( "Items", nbttaglist );
stack.writeToNBT( tag );
itemList.appendTag( tag );
}
nbt.setTag( "Items", itemList );
return super.writeToNBT( nbt );
}
@@ -148,7 +139,7 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
return super.shouldRefresh( world, pos, oldState, newState ) || BlockPeripheral.getPeripheralType( newState ) != PeripheralType.Printer;
}
public boolean isPrinting()
boolean isPrinting()
{
return m_printing;
}
@@ -173,74 +164,60 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
@Nonnull
@Override
public ItemStack getStackInSlot( int i )
public ItemStack getStackInSlot( int slot )
{
return m_inventory.get( i );
return m_inventory.get( slot );
}
@Nonnull
@Override
public ItemStack removeStackFromSlot( int i )
public ItemStack removeStackFromSlot( int slot )
{
synchronized( m_inventory )
{
ItemStack result = m_inventory.get( i );
m_inventory.set( i, ItemStack.EMPTY );
ItemStack result = m_inventory.get( slot );
m_inventory.set( slot, ItemStack.EMPTY );
markDirty();
updateAnim();
return result;
}
}
@Nonnull
@Override
public ItemStack decrStackSize( int i, int j )
public ItemStack decrStackSize( int slot, int count )
{
synchronized( m_inventory )
{
if( m_inventory.get( i ).isEmpty() ) return ItemStack.EMPTY;
ItemStack stack = m_inventory.get( slot );
if( stack.isEmpty() ) return ItemStack.EMPTY;
if( m_inventory.get( i ).getCount() <= j )
if( stack.getCount() <= count )
{
ItemStack itemstack = m_inventory.get( i );
m_inventory.set( i, ItemStack.EMPTY );
markDirty();
updateAnim();
return itemstack;
setInventorySlotContents( slot, ItemStack.EMPTY );
return stack;
}
ItemStack part = m_inventory.get( i ).splitStack( j );
if( m_inventory.get( i ).isEmpty() )
ItemStack part = stack.splitStack( count );
if( m_inventory.get( slot ).isEmpty() )
{
m_inventory.set( i, ItemStack.EMPTY );
m_inventory.set( slot, ItemStack.EMPTY );
updateAnim();
}
markDirty();
return part;
}
}
@Override
public void setInventorySlotContents( int i, @Nonnull ItemStack stack )
public void setInventorySlotContents( int slot, @Nonnull ItemStack stack )
{
synchronized( m_inventory )
{
m_inventory.set( i, stack );
m_inventory.set( slot, stack );
markDirty();
updateAnim();
}
}
@Override
public void clear()
{
synchronized( m_inventory )
{
for( int i = 0; i < m_inventory.size(); i++ ) m_inventory.set( i, ItemStack.EMPTY );
markDirty();
updateAnim();
}
}
@Override
public boolean isItemValidForSlot( int slot, @Nonnull ItemStack stack )
@@ -249,7 +226,7 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
{
return isInk( stack );
}
else if( slot >= topSlots[0] && slot <= topSlots[topSlots.length - 1] )
else if( slot >= TOP_SLOTS[0] && slot <= TOP_SLOTS[TOP_SLOTS.length - 1] )
{
return isPaper( stack );
}
@@ -295,11 +272,11 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
switch( side )
{
case DOWN: // Bottom (Out tray)
return bottomSlots;
return BOTTOM_SLOTS;
case UP: // Top (In tray)
return topSlots;
return TOP_SLOTS;
default: // Sides (Ink)
return sideSlots;
return SIDE_SLOTS;
}
}
@@ -311,14 +288,18 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
return new PrinterPeripheral( this );
}
public Terminal getCurrentPage()
@Nullable
Terminal getCurrentPage()
{
synchronized( m_page )
{
return m_printing ? m_page : null;
}
}
public boolean startNewPage()
boolean startNewPage()
{
synchronized( m_inventory )
synchronized( m_page )
{
if( !canInputPage() ) return false;
if( m_printing && !outputPage() ) return false;
@@ -326,49 +307,36 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
}
}
public boolean endCurrentPage()
boolean endCurrentPage()
{
synchronized( m_inventory )
synchronized( m_page )
{
if( m_printing && outputPage() )
{
return true;
return m_printing && outputPage();
}
}
return false;
}
public int getInkLevel()
{
synchronized( m_inventory )
int getInkLevel()
{
ItemStack inkStack = m_inventory.get( 0 );
return isInk( inkStack ) ? inkStack.getCount() : 0;
}
}
public int getPaperLevel()
int getPaperLevel()
{
int count = 0;
synchronized( m_inventory )
{
for( int i = 1; i < 7; i++ )
{
ItemStack paperStack = m_inventory.get( i );
if( !paperStack.isEmpty() && isPaper( paperStack ) )
{
count += paperStack.getCount();
}
}
if( isPaper( paperStack ) ) count += paperStack.getCount();
}
return count;
}
public void setPageTitle( String title )
void setPageTitle( String title )
{
if( m_printing )
synchronized( m_page )
{
m_pageTitle = title;
if( m_printing ) m_pageTitle = title;
}
}
@@ -384,17 +352,12 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
}
private boolean canInputPage()
{
synchronized( m_inventory )
{
ItemStack inkStack = m_inventory.get( 0 );
return !inkStack.isEmpty() && isInk( inkStack ) && getPaperLevel() > 0;
}
}
private boolean inputPage()
{
synchronized( m_inventory )
{
ItemStack inkStack = m_inventory.get( 0 );
if( !isInk( inkStack ) ) return false;
@@ -402,8 +365,8 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
for( int i = 1; i < 7; i++ )
{
ItemStack paperStack = m_inventory.get( i );
if( !paperStack.isEmpty() && isPaper( paperStack ) )
{
if( paperStack.isEmpty() || !isPaper( paperStack ) ) continue;
// Setup the new page
int colour = inkStack.getItemDamage();
m_page.setTextColour( colour >= 0 && colour < 16 ? 15 - colour : 15 );
@@ -441,14 +404,10 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
m_printing = true;
return true;
}
}
return false;
}
}
private boolean outputPage()
{
synchronized( m_page )
{
int height = m_page.getHeight();
String[] lines = new String[height];
@@ -460,9 +419,7 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
}
ItemStack stack = ItemPrintout.createSingleFromTitleAndText( m_pageTitle, lines, colours );
synchronized( m_inventory )
{
for( int slot : bottomSlots )
for( int slot : BOTTOM_SLOTS )
{
if( m_inventory.get( slot ).isEmpty() )
{
@@ -471,14 +428,10 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
return true;
}
}
}
return false;
}
}
private void ejectContents()
{
synchronized( m_inventory )
{
for( int i = 0; i < 13; i++ )
{
@@ -497,11 +450,8 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
}
}
}
}
private void updateAnim()
{
synchronized( m_inventory )
{
int anim = 0;
for( int i = 1; i < 7; i++ )
@@ -524,7 +474,6 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
}
setAnim( anim );
}
}
@Override
public boolean hasCapability( @Nonnull Capability<?> capability, @Nullable EnumFacing facing )

View File

@@ -44,7 +44,7 @@ public abstract class SpeakerPeripheral implements IPeripheral
{
// FIXME: Should be abstract, but we need this for Plethora compat. We'll
// be able to change this in a few versions as we implement both there.
@SuppressWarnings( "deprecation" ) BlockPos pos = getPos();
BlockPos pos = getPos();
return new Vec3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
}
@@ -71,8 +71,8 @@ public abstract class SpeakerPeripheral implements IPeripheral
public String[] getMethodNames()
{
return new String[] {
"playSound", // Plays sound at resourceLocator
"playNote" // Plays note
"playSound",
"playNote",
};
}

View File

@@ -35,9 +35,7 @@ public class PocketAPI implements ILuaAPI
@Override
public String[] getNames()
{
return new String[] {
"pocket"
};
return new String[] { "pocket" };
}
@Nonnull
@@ -46,7 +44,7 @@ public class PocketAPI implements ILuaAPI
{
return new String[] {
"equipBack",
"unequipBack"
"unequipBack",
};
}

View File

@@ -30,9 +30,13 @@ public final class FurnaceRefuelHandler implements TurtleRefuelEvent.Handler
@Override
public int refuel( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack currentStack, int slot, int limit )
{
ItemStack stack = turtle.getItemHandler().extractItem( slot, limit, false );
int fuelToGive = getFuelPerItem( stack ) * stack.getCount();
int fuelSpaceLeft = turtle.getFuelLimit() - turtle.getFuelLevel();
int fuelPerItem = getFuelPerItem( turtle.getItemHandler().getStackInSlot( slot ) );
int fuelItemLimit = (int) Math.ceil( fuelSpaceLeft / (double) fuelPerItem );
if( limit > fuelItemLimit ) limit = fuelItemLimit;
ItemStack stack = turtle.getItemHandler().extractItem( slot, limit, false );
int fuelToGive = fuelPerItem * stack.getCount();
// Store the replacement item in the inventory
ItemStack replacementStack = stack.getItem().getContainerItem( stack );
if( !replacementStack.isEmpty() )

View File

@@ -45,9 +45,7 @@ public class TurtleAPI implements ILuaAPI
@Override
public String[] getNames()
{
return new String[] {
"turtle"
};
return new String[] { "turtle" };
}
@Nonnull
@@ -336,9 +334,11 @@ public class TurtleAPI implements ILuaAPI
return tryCommand( context, new TurtleInspectCommand( InteractDirection.Up ) );
case 40: // inspectDown
return tryCommand( context, new TurtleInspectCommand( InteractDirection.Down ) );
case 41:
case 41: // getItemDetail
{
// getItemDetail
// FIXME: There's a race condition here if the stack is being modified (mutating NBT, etc...)
// on another thread. The obvious solution is to move this into a command, but some programs rely
// on this having a 0-tick delay.
int slot = parseOptionalSlotNumber( args, 0, m_turtle.getSelectedSlot() );
ItemStack stack = m_turtle.getInventory().getStackInSlot( slot );
if( stack.isEmpty() ) return new Object[] { null };

View File

@@ -218,7 +218,7 @@ public class BlockTurtle extends BlockComputerBase
@Deprecated
public float getExplosionResistance( Entity exploder )
{
if( getFamily() == ComputerFamily.Advanced && (exploder instanceof EntityLivingBase || exploder instanceof EntityFireball) )
if( getFamily() == ComputerFamily.Advanced || exploder instanceof EntityLivingBase || exploder instanceof EntityFireball )
{
return 2000;
}

View File

@@ -43,19 +43,16 @@ import net.minecraftforge.items.wrapper.InvWrapper;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collections;
import static net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY;
public class TileTurtle extends TileComputerBase implements ITurtleTile, DefaultInventory
{
// Statics
public static final int INVENTORY_SIZE = 16;
public static final int INVENTORY_WIDTH = 4;
public static final int INVENTORY_HEIGHT = 4;
// Members
enum MoveState
{
NOT_MOVED,
@@ -63,12 +60,12 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
MOVED
}
private NonNullList<ItemStack> m_inventory;
private NonNullList<ItemStack> m_previousInventory;
private final NonNullList<ItemStack> m_inventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY );
private final NonNullList<ItemStack> m_previousInventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY );
private final IItemHandlerModifiable m_itemHandler = new InvWrapper( this );
private boolean m_inventoryChanged;
private TurtleBrain m_brain;
private MoveState m_moveState;
private boolean m_inventoryChanged = false;
private TurtleBrain m_brain = new TurtleBrain( this );
private MoveState m_moveState = MoveState.NOT_MOVED;
private ComputerFamily m_family;
public TileTurtle()
@@ -78,15 +75,10 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
public TileTurtle( ComputerFamily family )
{
m_inventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY );
m_previousInventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY );
m_inventoryChanged = false;
m_brain = new TurtleBrain( this );
m_moveState = MoveState.NOT_MOVED;
m_family = family;
}
public boolean hasMoved()
private boolean hasMoved()
{
return m_moveState == MoveState.MOVED;
}
@@ -95,7 +87,7 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
protected ServerComputer createComputer( int instanceID, int id )
{
ServerComputer computer = new ServerComputer(
getWorld(), id, m_label, instanceID, getFamily(),
getWorld(), id, label, instanceID, getFamily(),
ComputerCraft.terminalWidth_turtle, ComputerCraft.terminalHeight_turtle
);
computer.setPosition( getPos() );
@@ -224,8 +216,6 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
{
super.update();
m_brain.update();
synchronized( m_inventory )
{
if( !getWorld().isRemote && m_inventoryChanged )
{
ServerComputer computer = getServerComputer();
@@ -238,7 +228,6 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
}
}
}
}
@Override
public void onNeighbourChange( @Nonnull BlockPos neighbour )
@@ -270,8 +259,8 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
// Read inventory
NBTTagList nbttaglist = nbt.getTagList( "Items", Constants.NBT.TAG_COMPOUND );
m_inventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY );
m_previousInventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY );
m_inventory.clear();
m_previousInventory.clear();
for( int i = 0; i < nbttaglist.tagCount(); i++ )
{
NBTTagCompound tag = nbttaglist.getCompoundTagAt( i );
@@ -317,12 +306,6 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
return hasPeripheralUpgradeOnSide( localSide );
}
@Override
protected boolean isRedstoneBlockedOnSide( ComputerSide localSide )
{
return false;
}
// IDirectionalTile
@Override
@@ -388,7 +371,7 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
return m_family;
}
public void setOwningPlayer( GameProfile player )
void setOwningPlayer( GameProfile player )
{
m_brain.setOwningPlayer( player );
markDirty();
@@ -416,44 +399,26 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
@Override
public ItemStack getStackInSlot( int slot )
{
if( slot >= 0 && slot < INVENTORY_SIZE )
{
synchronized( m_inventory )
{
return m_inventory.get( slot );
}
}
return ItemStack.EMPTY;
return slot >= 0 && slot < INVENTORY_SIZE ? m_inventory.get( slot ) : ItemStack.EMPTY;
}
@Nonnull
@Override
public ItemStack removeStackFromSlot( int slot )
{
synchronized( m_inventory )
{
ItemStack result = getStackInSlot( slot );
setInventorySlotContents( slot, ItemStack.EMPTY );
return result;
}
}
@Nonnull
@Override
public ItemStack decrStackSize( int slot, int count )
{
if( count == 0 )
{
return ItemStack.EMPTY;
}
if( count == 0 ) return ItemStack.EMPTY;
synchronized( m_inventory )
{
ItemStack stack = getStackInSlot( slot );
if( stack.isEmpty() )
{
return ItemStack.EMPTY;
}
if( stack.isEmpty() ) return ItemStack.EMPTY;
if( stack.getCount() <= count )
{
@@ -465,28 +430,19 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
onInventoryDefinitelyChanged();
return part;
}
}
@Override
public void setInventorySlotContents( int i, @Nonnull ItemStack stack )
{
if( i >= 0 && i < INVENTORY_SIZE )
{
synchronized( m_inventory )
{
if( !InventoryUtil.areItemsEqual( stack, m_inventory.get( i ) ) )
if( i >= 0 && i < INVENTORY_SIZE && !InventoryUtil.areItemsEqual( stack, m_inventory.get( i ) ) )
{
m_inventory.set( i, stack );
onInventoryDefinitelyChanged();
}
}
}
}
@Override
public void clear()
{
synchronized( m_inventory )
{
boolean changed = false;
for( int i = 0; i < INVENTORY_SIZE; i++ )
@@ -497,19 +453,14 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
changed = true;
}
}
if( changed )
{
onInventoryDefinitelyChanged();
}
}
if( changed ) onInventoryDefinitelyChanged();
}
@Override
public void markDirty()
{
super.markDirty();
synchronized( m_inventory )
{
if( !m_inventoryChanged )
{
for( int n = 0; n < getSizeInventory(); n++ )
@@ -522,7 +473,6 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
}
}
}
}
@Override
public boolean isUsableByPlayer( @Nonnull EntityPlayer player )
@@ -580,8 +530,8 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
public void transferStateFrom( TileTurtle copy )
{
super.transferStateFrom( copy );
m_inventory = copy.m_inventory;
m_previousInventory = copy.m_previousInventory;
Collections.copy( m_inventory, copy.m_inventory );
Collections.copy( m_previousInventory, copy.m_previousInventory );
m_inventoryChanged = copy.m_inventoryChanged;
m_brain = copy.m_brain;
m_brain.setOwner( this );

View File

@@ -908,14 +908,14 @@ public class TurtleBrain implements ITurtleAccess
else
{
computer.queueEvent( "turtle_response", new Object[] {
callbackID, true
callbackID, true,
} );
}
}
else
{
computer.queueEvent( "turtle_response", new Object[] {
callbackID, false, result != null ? result.getErrorMessage() : null
callbackID, false, result != null ? result.getErrorMessage() : null,
} );
}
}

View File

@@ -53,19 +53,15 @@ public class TurtleCompareCommand implements ITurtleCommand
Block lookAtBlock = lookAtState.getBlock();
if( !lookAtBlock.isAir( lookAtState, world, newPosition ) )
{
// Try createStackedBlock first
// Try getSilkTouchDrop first
if( !lookAtBlock.hasTileEntity( lookAtState ) )
{
try
{
Method method = ReflectionHelper.findMethod(
Block.class,
"func_180643_i", "getSilkTouchDrop",
IBlockState.class
);
Method method = ReflectionHelper.findMethod( Block.class, "getSilkTouchDrop", "func_180643_i", IBlockState.class );
lookAtStack = (ItemStack) method.invoke( lookAtBlock, lookAtState );
}
catch( ReflectiveOperationException ignored )
catch( ReflectiveOperationException | RuntimeException ignored )
{
}
}

View File

@@ -65,7 +65,6 @@ public class TurtlePlaceCommand implements ITurtleCommand
// Remember old block
EnumFacing direction = m_direction.toWorldDir( turtle );
World world = turtle.getWorld();
BlockPos coordinates = turtle.getPosition().offset( direction );
// Create a fake player, and orient it appropriately

View File

@@ -26,8 +26,8 @@ public class ContainerTurtle extends Container implements IContainerComputer
{
private static final int PROGRESS_ID_SELECTED_SLOT = 0;
public final int m_playerInvStartY;
public final int m_turtleInvStartX;
public final int playerInvStartY;
public final int turtleInvStartX;
private final ITurtleAccess m_turtle;
private IComputer m_computer;
@@ -36,8 +36,8 @@ public class ContainerTurtle extends Container implements IContainerComputer
protected ContainerTurtle( IInventory playerInventory, ITurtleAccess turtle, int playerInvStartY, int turtleInvStartX )
{
m_playerInvStartY = playerInvStartY;
m_turtleInvStartX = turtleInvStartX;
this.playerInvStartY = playerInvStartY;
this.turtleInvStartX = turtleInvStartX;
m_turtle = turtle;
m_selectedSlot = m_turtle.getWorld().isRemote ? 0 : m_turtle.getSelectedSlot();

View File

@@ -73,7 +73,10 @@ public class ItemTurtleLegacy extends ItemTurtleBase
}
@Override
public ResourceLocation getOverlay( @Nonnull ItemStack stack ) { return null; }
public ResourceLocation getOverlay( @Nonnull ItemStack stack )
{
return null;
}
@Override
public int getFuelLevel( @Nonnull ItemStack stack )

View File

@@ -54,7 +54,7 @@ public class TurtleHoe extends TurtleTool
{
if( verb == TurtleVerb.Dig )
{
ItemStack hoe = m_item.copy();
ItemStack hoe = item.copy();
ItemStack remainder = TurtlePlaceCommand.deploy( hoe, turtle, direction, null, null );
if( remainder != hoe )
{

View File

@@ -60,7 +60,7 @@ public class TurtleShovel extends TurtleTool
{
if( verb == TurtleVerb.Dig )
{
ItemStack shovel = m_item.copy();
ItemStack shovel = item.copy();
ItemStack remainder = TurtlePlaceCommand.deploy( shovel, turtle, direction, null, null );
if( remainder != shovel )
{

View File

@@ -48,18 +48,18 @@ import java.util.function.Function;
public class TurtleTool extends AbstractTurtleUpgrade
{
protected ItemStack m_item;
protected final ItemStack item;
public TurtleTool( ResourceLocation id, int legacyID, String adjective, Item item )
{
super( id, legacyID, TurtleUpgradeType.Tool, adjective, item );
m_item = new ItemStack( item, 1, 0 );
this.item = new ItemStack( item, 1, 0 );
}
public TurtleTool( ResourceLocation id, int legacyID, Item item )
{
super( id, legacyID, TurtleUpgradeType.Tool, item );
m_item = new ItemStack( item, 1, 0 );
this.item = new ItemStack( item, 1, 0 );
}
@Nonnull
@@ -76,7 +76,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
);
Minecraft mc = Minecraft.getMinecraft();
return Pair.of(
mc.getRenderItem().getItemModelMesher().getItemModel( m_item ),
mc.getRenderItem().getItemModelMesher().getItemModel( item ),
transform
);
}
@@ -124,7 +124,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
if( hit != null )
{
// Load up the turtle's inventory
ItemStack stackCopy = m_item.copy();
ItemStack stackCopy = item.copy();
turtlePlayer.loadInventory( stackCopy );
Entity hitEntity = hit.getKey();
@@ -202,7 +202,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
IBlockState state = world.getBlockState( blockPosition );
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, turtlePosition, direction );
turtlePlayer.loadInventory( m_item.copy() );
turtlePlayer.loadInventory( item.copy() );
if( ComputerCraft.turtlesObeyBlockProtection )
{
@@ -246,7 +246,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
boolean canHarvest = state.getBlock().canHarvestBlock( world, blockPosition, turtlePlayer );
boolean canBreak = state.getBlock().removedByPlayer( state, world, blockPosition, turtlePlayer, canHarvest );
if( canBreak ) state.getBlock().onPlayerDestroy( world, blockPosition, state );
if( canHarvest )
if( canHarvest && canBreak )
{
state.getBlock().harvestBlock( world, turtlePlayer, blockPosition, state, tile, turtlePlayer.getHeldItemMainhand() );
}

View File

@@ -20,7 +20,7 @@ public final class ColourUtils
"dyeBlack", "dyeRed", "dyeGreen", "dyeBrown",
"dyeBlue", "dyePurple", "dyeCyan", "dyeLightGray",
"dyeGray", "dyePink", "dyeLime", "dyeYellow",
"dyeLightBlue", "dyeMagenta", "dyeOrange", "dyeWhite"
"dyeLightBlue", "dyeMagenta", "dyeOrange", "dyeWhite",
};
private static int[] ids;

View File

@@ -72,11 +72,10 @@ public class Palette
public static double[] decodeRGB8( int rgb )
{
return new double[]
{
return new double[] {
((rgb >> 16) & 0xFF) / 255.0f,
((rgb >> 8) & 0xFF) / 255.0f,
(rgb & 0xFF) / 255.0f
(rgb & 0xFF) / 255.0f,
};
}

View File

@@ -0,0 +1,195 @@
itemGroup.computercraft=CC: Tweaked
tile.computercraft:computer.name=计算机
tile.computercraft:advanced_computer.name=高级计算机
tile.computercraft:drive.name=磁盘驱动器
tile.computercraft:printer.name=打印机
tile.computercraft:monitor.name=显示器
tile.computercraft:advanced_monitor.name=高级显示器
tile.computercraft:wireless_modem.name=无线调制解调器
tile.computercraft:wired_modem.name=有线调制解调器
tile.computercraft:cable.name=网络电缆
tile.computercraft:command_computer.name=命令电脑
tile.computercraft:advanced_modem.name=末影调制解调器
tile.computercraft:speaker.name=扬声器
tile.computercraft:turtle.name=海龟
tile.computercraft:turtle.upgraded.name=%s海龟
tile.computercraft:turtle.upgraded_twice.name=%s%s海龟
tile.computercraft:advanced_turtle.name=高级海龟
tile.computercraft:advanced_turtle.upgraded.name=高级%s海龟
tile.computercraft:advanced_turtle.upgraded_twice.name=高级%s%s海龟
item.computercraft:disk.name=软盘
item.computercraft:treasure_disk.name=软盘
item.computercraft:page.name=打印纸
item.computercraft:pages.name=打印纸
item.computercraft:book.name=打印书
item.computercraft:pocket_computer.name=手提计算机
item.computercraft:pocket_computer.upgraded.name=%s手提计算机
item.computercraft:advanced_pocket_computer.name=高级手提计算机
item.computercraft:advanced_pocket_computer.upgraded.name=高级%s手提计算机
upgrade.minecraft:diamond_sword.adjective=战斗
upgrade.minecraft:diamond_shovel.adjective=挖掘
upgrade.minecraft:diamond_pickaxe.adjective=采掘
upgrade.minecraft:diamond_axe.adjective=伐木
upgrade.minecraft:diamond_hoe.adjective=耕种
upgrade.computercraft:wireless_modem.adjective=无线
upgrade.minecraft:crafting_table.adjective=合成
upgrade.computercraft:advanced_modem.adjective=末影
upgrade.computercraft:speaker.adjective=喧闹
chat.computercraft.wired_modem.peripheral_connected=Peripheral "%s"连接到网络
chat.computercraft.wired_modem.peripheral_disconnected=Peripheral "%s"与网络断开连接
# Command descriptions, usage and any additional messages.
commands.computercraft.synopsis=各种控制计算机的命令.
commands.computercraft.desc=/computercraft命令提供各种调试和管理工具用于控制和与计算机交互.
commands.computercraft.help.synopsis=为特定的命令提供帮助
commands.computercraft.help.desc=
commands.computercraft.help.usage=[command]
commands.computercraft.help.no_children=%s没有子命令
commands.computercraft.help.no_command=没有这样的命令'%s'
commands.computercraft.dump.synopsis=显示计算机的状态.
commands.computercraft.dump.desc=显示所有计算机的状态或某台计算机的特定信息. 你可以指定计算机的实例id (例如. 123), 计算机id (例如. #123)或标签(例如. "@My Computer").
commands.computercraft.dump.usage=[id]
commands.computercraft.dump.action=查看有关此计算机的更多信息
commands.computercraft.shutdown.synopsis=远程关闭计算机.
commands.computercraft.shutdown.desc=关闭列出的计算机或全部计算机(如果未指定). 你可以指定计算机的实例id (例如. 123), 计算机id (例如. #123)或标签(例如. "@My Computer").
commands.computercraft.shutdown.usage=[ids...]
commands.computercraft.shutdown.done=关闭%s/%s计算机
commands.computercraft.turn_on.synopsis=远程打开计算机.
commands.computercraft.turn_on.desc=打开列出的计算机. 你可以指定计算机的实例id (例如. 123), 计算机id (例如. #123)或标签(例如. "@My Computer").
commands.computercraft.turn_on.usage=[ids...]
commands.computercraft.turn_on.done=打开%s/%s计算机
commands.computercraft.tp.synopsis=传送到特定的计算机.
commands.computercraft.tp.desc=传送到计算机的位置. 你可以指定计算机的实例id (例如. 123)或计算机id (例如. #123).
commands.computercraft.tp.usage=<id>
commands.computercraft.tp.action=传送到这台电脑
commands.computercraft.tp.not_entity=无法为非玩家打开终端
commands.computercraft.tp.not_there=无法在世界上定位电脑
commands.computercraft.view.synopsis=查看计算机的终端.
commands.computercraft.view.desc=打开计算机的终端,允许远程控制计算机. 这不提供对海龟库存的访问. 你可以指定计算机的实例id (例如. 123)或计算机id (例如. #123).
commands.computercraft.view.usage=<id>
commands.computercraft.view.action=查看此计算机
commands.computercraft.view.not_player=无法为非玩家打开终端
commands.computercraft.track.synopsis=跟踪计算机的执行时间.
commands.computercraft.track.desc=跟踪计算机执行的时间以及它们处理的事件数. 这以/forge track类似的方式呈现信息可用于诊断滞后.
commands.computercraft.track.start.synopsis=开始跟踪所有计算机
commands.computercraft.track.start.desc=开始跟踪所有计算机的执行时间和事件计数. 这将放弃先前运行的结果.
commands.computercraft.track.start.usage=
commands.computercraft.track.start.stop=运行%s以停止跟踪并查看结果
commands.computercraft.track.stop.synopsis=停止跟踪所有计算机
commands.computercraft.track.stop.desc=停止跟踪所有计算机的事件和执行时间
commands.computercraft.track.stop.usage=
commands.computercraft.track.stop.action=点击停止跟踪
commands.computercraft.track.stop.not_enabled=目前没有跟踪计算机
commands.computercraft.track.dump.synopsis=输出最新的跟踪结果
commands.computercraft.track.dump.desc=输出计算机跟踪的最新结果.
commands.computercraft.track.dump.usage=[kind]
commands.computercraft.track.dump.no_timings=没有时间可用
commands.computercraft.track.dump.no_field=未知字节'%s'
commands.computercraft.track.dump.computer=计算器
commands.computercraft.reload.synopsis=重新加载ComputerCraft配置文件
commands.computercraft.reload.desc=重新加载ComputerCraft配置文件
commands.computercraft.reload.usage=
commands.computercraft.reload.done=重新加载配置
commands.computercraft.queue.synopsis=将computer_command事件发送到命令计算机
commands.computercraft.queue.desc=发送computer_command事件到命令计算机,并传递其他参数. 这主要是为地图制作者设计的, 作为/trigger更加计算机友好的版本. 任何玩家都可以运行命令, 这很可能是通过文本组件的点击事件完成的.
commands.computercraft.queue.usage=<id> [args...]
commands.computercraft.generic.no_position=<无位置>
commands.computercraft.generic.position=%s, %s, %s
commands.computercraft.generic.yes=Y
commands.computercraft.generic.no=N
commands.computercraft.generic.exception=未处理的异常(%s)
commands.computercraft.generic.additional_rows=%d额外的行…
commands.computercraft.argument.no_matching=没有计算机匹配'%s'
commands.computercraft.argument.many_matching=多台计算机匹配'%s' (实例%s)
commands.computercraft.argument.not_number='%s'不是一个数字
# Names for the various tracking fields.
tracking_field.computercraft.tasks.name=任务
tracking_field.computercraft.total.name=总计时间
tracking_field.computercraft.average.name=平均时间
tracking_field.computercraft.max.name=最大时间
tracking_field.computercraft.server_count.name=服务器任务计数
tracking_field.computercraft.server_time.name=服务器任务时间
tracking_field.computercraft.peripheral.name=外围设备呼叫
tracking_field.computercraft.fs.name=文件系统操作
tracking_field.computercraft.turtle.name=海龟行动
tracking_field.computercraft.http.name=HTTP需求
tracking_field.computercraft.http_upload.name=HTTP上传
tracking_field.computercraft.http_download.name=HTTP下载
tracking_field.computercraft.websocket_incoming.name=Websocket传入
tracking_field.computercraft.websocket_outgoing.name=Websocket传出
tracking_field.computercraft.coroutines_created.name=协同创建
tracking_field.computercraft.coroutines_dead.name=协同处理
# Misc tooltips
gui.computercraft.tooltip.copy=复制到剪贴板
gui.computercraft.tooltip.computer_id=(计算机ID: %s)
gui.computercraft.tooltip.disk_id=(磁盘ID: %s)
# Config options
gui.computercraft:config.computer_space_limit=计算机空间限制(字节)
gui.computercraft:config.floppy_space_limit=软盘空间限制(字节)
gui.computercraft:config.maximum_open_files=每台计算机打开的最大文件数
gui.computercraft:config.disable_lua51_features=禁用Lua 5.1功能
gui.computercraft:config.default_computer_settings=默认计算机设置
gui.computercraft:config.debug_enabled=启用debug库
gui.computercraft:config.log_computer_errors=记录计算机错误
gui.computercraft:config.execution=执行
gui.computercraft:config.execution.computer_threads=计算机线程数
gui.computercraft:config.execution.max_main_global_time=服务器全局tick时间限制
gui.computercraft:config.execution.max_main_computer_time=服务器计算机tick时间限制
gui.computercraft:config.http=HTTP
gui.computercraft:config.http.enabled=启用HTTP API
gui.computercraft:config.http.websocket_enabled=启用websockets
gui.computercraft:config.http.whitelist=HTTP白名单
gui.computercraft:config.http.blacklist=HTTP黑名单
gui.computercraft:config.http.timeout=Timeout
gui.computercraft:config.http.max_requests=最大并发请求数
gui.computercraft:config.http.max_download=最大响应数据大小
gui.computercraft:config.http.max_upload=最大请求数据大小
gui.computercraft:config.http.max_websockets=最大并发websockets数
gui.computercraft:config.http.max_websocket_message=最大websockets消息大小
gui.computercraft:config.peripheral=外围设备
gui.computercraft:config.peripheral.command_block_enabled=启用命令方块外设
gui.computercraft:config.peripheral.modem_range=调制解调器范围(默认)
gui.computercraft:config.peripheral.modem_high_altitude_range=调制解调器范围(高海拔)
gui.computercraft:config.peripheral.modem_range_during_storm=调制解调器范围(恶劣天气)
gui.computercraft:config.peripheral.modem_high_altitude_range_during_storm=调制解调器范围(高海拔, 恶劣天气)
gui.computercraft:config.peripheral.max_notes_per_tick=计算机一次可以播放的最大音符数量
gui.computercraft:config.turtle=海龟
gui.computercraft:config.turtle.need_fuel=启用燃料
gui.computercraft:config.turtle.normal_fuel_limit=海龟燃料限制
gui.computercraft:config.turtle.advanced_fuel_limit=高级海龟燃料限制
gui.computercraft:config.turtle.obey_block_protection=海龟服从方块保护
gui.computercraft:config.turtle.can_push=海龟可以推动实体
gui.computercraft:config.turtle.disabled_actions=禁用海龟动作

View File

@@ -1,5 +1,19 @@
-- Load in expect from the module path.
--
-- Ideally we'd use require, but that is part of the shell, and so is not
-- available to the BIOS or any APIs. All APIs load this using dofile, but that
-- has not been defined at this point.
local expect
do
local h = fs.open("rom/modules/main/cc/expect.lua", "r")
local f, err = loadstring(h.readAll(), "@expect.lua")
h.close()
if not f then error(err) end
expect = f().expect
end
local nativegetfenv = getfenv
if _VERSION == "Lua 5.1" then
-- If we're on Lua 5.1, install parts of the Lua 5.2/5.3 API so that programs can be written against it
local type = type
@@ -20,18 +34,11 @@ if _VERSION == "Lua 5.1" then
end
function load( x, name, mode, env )
if type( x ) ~= "string" and type( x ) ~= "function" then
error( "bad argument #1 (expected string or function, got " .. type( x ) .. ")", 2 )
end
if name ~= nil and type( name ) ~= "string" then
error( "bad argument #2 (expected string, got " .. type( name ) .. ")", 2 )
end
if mode ~= nil and type( mode ) ~= "string" then
error( "bad argument #3 (expected string, got " .. type( mode ) .. ")", 2 )
end
if env ~= nil and type( env) ~= "table" then
error( "bad argument #4 (expected table, got " .. type( env ) .. ")", 2 )
end
expect(1, x, "function", "string")
expect(2, name, "string", "nil")
expect(3, mode, "string", "nil")
expect(4, env, "table", "nil")
local ok, p1, p2 = pcall( function()
if type(x) == "string" then
local result, err = nativeloadstring( x, name )
@@ -76,10 +83,9 @@ if _VERSION == "Lua 5.1" then
math.log10 = nil
table.maxn = nil
else
loadstring = function(string, chunkname) return nativeloadstring(string, prefix( chunkname ))
loadstring = function(string, chunkname) return nativeloadstring(string, prefix( chunkname )) end
-- Inject a stub for the old bit library
end
_G.bit = {
bnot = bit32.bnot,
band = bit32.band,
@@ -175,9 +181,7 @@ end
-- Install globals
function sleep( nTime )
if nTime ~= nil and type( nTime ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( nTime ) .. ")", 2 )
end
expect(1, nTime, "number", "nil")
local timer = os.startTimer( nTime or 0 )
repeat
local sEvent, param = os.pullEvent( "timer" )
@@ -185,9 +189,7 @@ function sleep( nTime )
end
function write( sText )
if type( sText ) ~= "string" and type( sText ) ~= "number" then
error( "bad argument #1 (expected string or number, got " .. type( sText ) .. ")", 2 )
end
expect(1, sText, "string", "number")
local w,h = term.getSize()
local x,y = term.getCursorPos()
@@ -275,18 +277,11 @@ function printError( ... )
end
function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
if _sReplaceChar ~= nil and type( _sReplaceChar ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sReplaceChar ) .. ")", 2 )
end
if _tHistory ~= nil and type( _tHistory ) ~= "table" then
error( "bad argument #2 (expected table, got " .. type( _tHistory ) .. ")", 2 )
end
if _fnComplete ~= nil and type( _fnComplete ) ~= "function" then
error( "bad argument #3 (expected function, got " .. type( _fnComplete ) .. ")", 2 )
end
if _sDefault ~= nil and type( _sDefault ) ~= "string" then
error( "bad argument #4 (expected string, got " .. type( _sDefault ) .. ")", 2 )
end
expect(1, _sReplaceChar, "string", "nil")
expect(2, _tHistory, "table", "nil")
expect(3, _fnComplete, "function", "nil")
expect(4, _sDefault, "string", "nil")
term.setCursorBlink( true )
local sLine
@@ -544,27 +539,28 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
return sLine
end
loadfile = function( _sFile, _tEnv )
if type( _sFile ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sFile ) .. ")", 2 )
function loadfile( filename, mode, env )
-- Support the previous `loadfile(filename, env)` form instead.
if type(mode) == "table" and env == nil then
mode, env = nil, mode
end
if _tEnv ~= nil and type( _tEnv ) ~= "table" then
error( "bad argument #2 (expected table, got " .. type( _tEnv ) .. ")", 2 )
end
local file = fs.open( _sFile, "r" )
if file then
local func, err = load( file.readAll(), "@" .. fs.getName( _sFile ), "t", _tEnv )
expect(1, filename, "string")
expect(2, mode, "string", "nil")
expect(3, env, "table", "nil")
local file = fs.open( filename, "r" )
if not file then return nil, "File not found" end
local func, err = load( file.readAll(), "@" .. fs.getName( filename ), mode, env )
file.close()
return func, err
end
return nil, "File not found"
end
dofile = function( _sFile )
if type( _sFile ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sFile ) .. ")", 2 )
end
local fnFile, e = loadfile( _sFile, _G )
function dofile( _sFile )
expect(1, _sFile, "string")
local fnFile, e = loadfile( _sFile, nil, _G )
if fnFile then
return fnFile()
else
@@ -574,16 +570,13 @@ end
-- Install the rest of the OS api
function os.run( _tEnv, _sPath, ... )
if type( _tEnv ) ~= "table" then
error( "bad argument #1 (expected table, got " .. type( _tEnv ) .. ")", 2 )
end
if type( _sPath ) ~= "string" then
error( "bad argument #2 (expected string, got " .. type( _sPath ) .. ")", 2 )
end
expect(1, _tEnv, "table")
expect(2, _sPath, "string")
local tArgs = table.pack( ... )
local tEnv = _tEnv
setmetatable( tEnv, { __index = _G } )
local fnFile, err = loadfile( _sPath, tEnv )
local fnFile, err = loadfile( _sPath, nil, tEnv )
if fnFile then
local ok, err = pcall( function()
fnFile( table.unpack( tArgs, 1, tArgs.n ) )
@@ -604,9 +597,7 @@ end
local tAPIsLoading = {}
function os.loadAPI( _sPath )
if type( _sPath ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sPath ) .. ")", 2 )
end
expect(1, _sPath, "string")
local sName = fs.getName( _sPath )
if sName:sub(-4) == ".lua" then
sName = sName:sub(1,-5)
@@ -619,7 +610,7 @@ function os.loadAPI( _sPath )
local tEnv = {}
setmetatable( tEnv, { __index = _G } )
local fnAPI, err = loadfile( _sPath, tEnv )
local fnAPI, err = loadfile( _sPath, nil, tEnv )
if fnAPI then
local ok, err = pcall( fnAPI )
if not ok then
@@ -644,9 +635,7 @@ function os.loadAPI( _sPath )
end
function os.unloadAPI( _sName )
if type( _sName ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sName ) .. ")", 2 )
end
expect(1, _sName, "string")
if _sName ~= "_G" and type(_G[_sName]) == "table" then
_G[_sName] = nil
end
@@ -692,9 +681,11 @@ if http then
local function checkOptions( options, body )
checkKey( options, "url", "string")
if body == false
then checkKey( options, "body", "nil" )
else checkKey( options, "body", "string", not body ) end
if body == false then
checkKey( options, "body", "nil" )
else
checkKey( options, "body", "string", not body )
end
checkKey( options, "headers", "table", true )
checkKey( options, "method", "string", true )
checkKey( options, "redirect", "boolean", true )
@@ -725,15 +716,9 @@ if http then
return wrapRequest( _url.url, _url )
end
if type( _url ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
end
if _headers ~= nil and type( _headers ) ~= "table" then
error( "bad argument #2 (expected table, got " .. type( _headers ) .. ")", 2 )
end
if _binary ~= nil and type( _binary ) ~= "boolean" then
error( "bad argument #3 (expected boolean, got " .. type( _binary ) .. ")", 2 )
end
expect(1, _url, "string")
expect(2, _headers, "table", "nil")
expect(3, _binary, "boolean", "nil")
return wrapRequest( _url, _url, nil, _headers, _binary )
end
@@ -743,18 +728,10 @@ if http then
return wrapRequest( _url.url, _url )
end
if type( _url ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
end
if type( _post ) ~= "string" then
error( "bad argument #2 (expected string, got " .. type( _post ) .. ")", 2 )
end
if _headers ~= nil and type( _headers ) ~= "table" then
error( "bad argument #3 (expected table, got " .. type( _headers ) .. ")", 2 )
end
if _binary ~= nil and type( _binary ) ~= "boolean" then
error( "bad argument #4 (expected boolean, got " .. type( _binary ) .. ")", 2 )
end
expect(1, _url, "string")
expect(2, _post, "string")
expect(3, _headers, "table", "nil")
expect(4, _binary, "boolean", "nil")
return wrapRequest( _url, _url, _post, _headers, _binary )
end
@@ -764,19 +741,10 @@ if http then
checkOptions( _url )
url = _url.url
else
if type( _url ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
end
if _post ~= nil and type( _post ) ~= "string" then
error( "bad argument #2 (expected string, got " .. type( _post ) .. ")", 2 )
end
if _headers ~= nil and type( _headers ) ~= "table" then
error( "bad argument #3 (expected table, got " .. type( _headers ) .. ")", 2 )
end
if _binary ~= nil and type( _binary ) ~= "boolean" then
error( "bad argument #4 (expected boolean, got " .. type( _binary ) .. ")", 2 )
end
expect(1, _url, "string")
expect(2, _post, "string", "nil")
expect(3, _headers, "table", "nil")
expect(4, _binary, "boolean", "nil")
url = _url.url
end
@@ -802,12 +770,9 @@ if http then
local nativeWebsocket = http.websocket
http.websocketAsync = nativeWebsocket
http.websocket = function( _url, _headers )
if type( _url ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
end
if _headers ~= nil and type( _headers ) ~= "table" then
error( "bad argument #2 (expected table, got " .. type( _headers ) .. ")", 2 )
end
expect(1, _url, "string")
expect(2, _headers, "table", "nil")
local ok, err = nativeWebsocket( _url, _headers )
if not ok then return ok, err end
@@ -825,18 +790,11 @@ end
-- Install the lua part of the FS api
local tEmpty = {}
function fs.complete( sPath, sLocation, bIncludeFiles, bIncludeDirs )
if type( sPath ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( sPath ) .. ")", 2 )
end
if type( sLocation ) ~= "string" then
error( "bad argument #2 (expected string, got " .. type( sLocation ) .. ")", 2 )
end
if bIncludeFiles ~= nil and type( bIncludeFiles ) ~= "boolean" then
error( "bad argument #3 (expected boolean, got " .. type( bIncludeFiles ) .. ")", 2 )
end
if bIncludeDirs ~= nil and type( bIncludeDirs ) ~= "boolean" then
error( "bad argument #4 (expected boolean, got " .. type( bIncludeDirs ) .. ")", 2 )
end
expect(1, sPath, "string")
expect(2, sLocation, "string")
expect(3, bIncludeFiles, "boolean", "nil")
expect(4, bIncludeDirs, "boolean", "nil")
bIncludeFiles = (bIncludeFiles ~= false)
bIncludeDirs = (bIncludeDirs ~= false)
local sDir = sLocation
@@ -982,6 +940,8 @@ settings.set( "edit.default_extension", "lua" )
settings.set( "paint.default_extension", "nfp" )
settings.set( "lua.autocomplete", true )
settings.set( "list.show_hidden", false )
settings.set( "motd.enable", false )
settings.set( "motd.path", "/rom/motd.txt:/motd.txt" )
if term.isColour() then
settings.set( "bios.use_multishell", true )
end

View File

@@ -1,3 +1,5 @@
local expect = dofile("rom/modules/main/cc/expect.lua").expect
-- Colors
white = 1
orange = 2
@@ -18,49 +20,35 @@ black = 32768
function combine( ... )
local r = 0
for n,c in ipairs( { ... } ) do
if type( c ) ~= "number" then
error( "bad argument #"..n.." (expected number, got " .. type( c ) .. ")", 2 )
end
for i = 1, select('#', ...) do
local c = select(i, ...)
expect(i, c, "number")
r = bit32.bor(r,c)
end
return r
end
function subtract( colors, ... )
if type( colors ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( colors ) .. ")", 2 )
end
expect(1, colors, "number")
local r = colors
for n,c in ipairs( { ... } ) do
if type( c ) ~= "number" then
error( "bad argument #"..tostring( n+1 ).." (expected number, got " .. type( c ) .. ")", 2 )
end
for i = 1, select('#', ...) do
local c = select(i, ...)
expect(i + 1, c, "number")
r = bit32.band(r, bit32.bnot(c))
end
return r
end
function test( colors, color )
if type( colors ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( colors ) .. ")", 2 )
end
if type( color ) ~= "number" then
error( "bad argument #2 (expected number, got " .. type( color ) .. ")", 2 )
end
expect(1, colors, "number")
expect(2, color, "number")
return bit32.band(colors, color) == color
end
function packRGB( r, g, b )
if type( r ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( r ) .. ")", 2 )
end
if type( g ) ~= "number" then
error( "bad argument #2 (expected number, got " .. type( g ) .. ")", 2 )
end
if type( b ) ~= "number" then
error( "bad argument #3 (expected number, got " .. type( b ) .. ")", 2 )
end
expect(1, r, "number")
expect(2, g, "number")
expect(3, b, "number")
return
bit32.band( r * 255, 0xFF ) * 2^16 +
bit32.band( g * 255, 0xFF ) * 2^8 +
@@ -68,9 +56,7 @@ function packRGB( r, g, b )
end
function unpackRGB( rgb )
if type( rgb ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( rgb ) .. ")", 2 )
end
expect(1, rgb, "number")
return
bit32.band( bit32.rshift( rgb, 16 ), 0xFF ) / 255,
bit32.band( bit32.rshift( rgb, 8 ), 0xFF ) / 255,

Some files were not shown because too many files have changed in this diff Show More