1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-11-09 18:03:08 +00:00

Compare commits

...

85 Commits

Author SHA1 Message Date
SquidDev
0f3c44c926 Merge branch 'master' into mc-1.13.x 2019-06-08 08:21:08 +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
b6715bd812 Fix a couple of build failures 2019-06-02 18:36:34 +01:00
SquidDev
18aee02221 Mark CurseForge builds as release
They're almost definitely not, but I've had no bug reports so they must
work!
2019-06-02 17:28:13 +01:00
SquidDev
401bbf2e6a Fix turtle block placing failing 2019-06-02 17:25:49 +01:00
SquidDev
7467b7f88a Fix several deprecated warnings 2019-06-02 17:23:33 +01:00
SquidDev
c82d8a7c2a Merge branch 'master' into mc-1.13.x 2019-06-02 16:46:45 +01:00
SquidDev
6b81bcf334 Bump version 2019-06-02 16:20:15 +01:00
SquidDev
3d67421d98 Bump Forge version 2019-06-02 16:18:44 +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
SquidDev
bb138326df Use correct model for blinking pocket computers
Fixes #211211211211211211211211211211211211211211211211211211211211211
2019-05-25 09:28:15 +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
xuyu0v0
e0660b1dab Create zh_cn.json 2019-05-06 21:47:43 +01:00
SquidDev
2182cfbeb7 Merge branch 'master' into mc-1.13.x 2019-05-06 21:32:56 +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
2c027adb68 Fix advancements
Looks like these were never properly updated. Woops.
2019-04-24 12:15:03 +01:00
SquidDev
4a25e7a178 Fix GH release uploading 2019-04-24 10:40:44 +01:00
SquidDev
55d54fec63 Bump versions 2019-04-24 10:33:50 +01:00
SquidDev
220e4bd660 Merge branch 'master' into mc-1.13.x 2019-04-24 10:15:33 +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
SquidDev
48a71e96eb Bump version 2019-04-16 10:35:15 +01:00
SquidDev
3bf47b5290 Make refuelling a little more flexible
This removes the link between item size and refuel limit, meaning
working with other items (such as FE items) is a little easier.
2019-04-16 10:28:10 +01:00
SquidDev
9e9f199e55 Remove a couple of over-eager error logs 2019-04-16 10:08:12 +01:00
SquidDev
5a8a111857 You didn't see nothing 2019-04-15 18:33:59 +01:00
SquidDev
48ba247ab4 Create an IMultipartTile for all BlockPeripheral tiles
Fixes #176
2019-04-15 09:21:10 +01:00
SquidDev
362dbd97ac Correctly handle capability creation & invalidation
Also make cable part detection more consistent.
2019-04-09 17:29:23 +01:00
SquidDev
aa0e1883d1 Add check for if item/block registration has failed
If mod loading fails, we'll continue to load colour handlers. As
blocks/items have not been registered, then we'll throw an NPE.

See MinecraftForge/MinecraftForge#5682. Somewhat fixes #168.
2019-04-09 16:32:20 +01:00
SquidDev
9cdbcb4332 Use int instead of long for configs
Forge's config system will read the default values as integers, meaning
it fails to validate against the config spec. Ideally this'd be fixed in
forge, but this is a suitable work around.
2019-04-09 16:31:00 +01:00
SquidDev
23ddd4feb5 Fix several deprecation warnings 2019-04-09 16:30:44 +01:00
SquidDev
fcaa777c95 Merge branch 'master' into mc-1.13.x 2019-04-09 11:11:12 +01:00
SquidDev
b195cab6a7 Add a couple of colours unit tests 2019-04-09 10:35:23 +01:00
SquidDev
63dc0daa09 Convert computer sides to an enum
Previously we just relied on magic int values, which was confusing and
potentially error-prone. We could use EnumFacing, but that's a)
dependent on MC and b) incorrect, as we're referring to local
coordinates.
2019-04-09 10:02:54 +01:00
SquidDev
34602ec4be Move null checks from Preconditions to Objects 2019-04-08 14:54:57 +01:00
SquidDev
f3ce44042f Update all inputs if the block is not a neighbour
Fixes #172, and acts as a workaround for MrTJP/ProjectRed#1472.
2019-04-08 14:52:24 +01:00
SquidDev
4205f18f0c Publish jar files to GH too
I'm not sure if I'll keep this, as it's a little redundant. We're on an
older version of the plugin, as 2.2.7 seems to fail for me.

Closes #165.
2019-04-07 15:30:27 +01:00
SquidDev
6be330ae8d Expose Computer.getRootMount again
It's a little evil, but we need it for CCEmuX. There's other ways of
achieving this, but not with supporting CC and CC:T.
2019-04-07 14:58:25 +01:00
SquidDev
4569af2130 Bump version 2019-04-06 22:42:44 +01:00
SquidDev
765c31315a Make redstone updates identical to how MC does it
Also fixes rather embarassing bug with redstone not updating.
2019-04-06 22:42:03 +01:00
Wilma456 (Jakob0815)
0e191e42a0 Update de_de.lang (#166) 2019-04-03 14:58:04 +01:00
SquidDev
ca34b2a1b8 Update the dependency info a little 2019-04-02 21:37:19 +01:00
206 changed files with 4436 additions and 1489 deletions

View File

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

View File

@@ -1,7 +1,7 @@
--- ---
name: Feature request name: Feature request
about: Suggest an idea or improvement about: Suggest an idea or improvement
labels: enhancement
--- ---
<!-- <!--
@@ -11,4 +11,4 @@ about: Suggest an idea or improvement
## Useful information to include: ## Useful information to include:
- Explanation of how the feature/change should work. - 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 .idea
.gradle .gradle
*.DS_Store *.DS_Store
.classpath
.project
.settings/
bin/
*.launch

View File

@@ -1,5 +1,5 @@
# ![CC: Tweaked](logo.png) # ![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](https://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, CC: Tweaked is a fork of [ComputerCraft](https://github.com/dan200/ComputerCraft), adding programmable computers,
turtles and more to Minecraft. 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 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. 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 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. 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`. 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 ## 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 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. using [cc.crzd.me's maven](https://cc.crzd.me/maven/) instead.
@@ -57,7 +66,7 @@ dependencies {
} }
dependencies { dependencies {
implementation "org.squiddev:cc-tweaked:${mc_version}-${cct_version}" implementation "org.squiddev:cc-tweaked-${mc_version}:${cct_version}"
} }
``` ```

View File

@@ -9,14 +9,17 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.google.code.gson:gson:2.8.1' classpath 'com.google.code.gson:gson:2.8.1'
classpath 'net.minecraftforge.gradle:ForgeGradle:3.0.115' classpath 'net.minecraftforge.gradle:ForgeGradle:3.0.117'
classpath 'net.sf.proguard:proguard-gradle:6.1.0beta2' classpath 'net.sf.proguard:proguard-gradle:6.1.0beta2'
classpath 'org.ajoberstar.grgit:grgit-gradle:3.0.0' classpath 'org.ajoberstar.grgit:grgit-gradle:3.0.0'
} }
} }
plugins { plugins {
id 'com.matthewprenger.cursegradle' version '1.2.0' 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"
} }
apply plugin: 'net.minecraftforge.gradle' apply plugin: 'net.minecraftforge.gradle'
@@ -87,13 +90,15 @@ configurations {
} }
dependencies { dependencies {
checkstyle "com.puppycrawl.tools:checkstyle:8.21"
minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}" minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}"
compileOnly "mezz.jei:jei-1.13.2:5.0.0.8:api" compileOnly fg.deobf("mezz.jei:jei-1.13.2:5.0.0.20:api")
// deobfProvided "pl.asie:Charset-Lib:0.5.4.6" // deobfProvided "pl.asie:Charset-Lib:0.5.4.6"
// deobfProvided "MCMultiPart2:MCMultiPart:2.5.3" // deobfProvided "MCMultiPart2:MCMultiPart:2.5.3"
deobf "mezz.jei:jei-1.13.2:5.0.0.8" runtimeOnly fg.deobf("mezz.jei:jei-1.13.2:5.0.0.20")
shade 'org.squiddev:Cobalt:0.5.0-SNAPSHOT' shade 'org.squiddev:Cobalt:0.5.0-SNAPSHOT'
@@ -142,6 +147,8 @@ import java.util.zip.*
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import com.google.gson.JsonElement 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 org.ajoberstar.grgit.Grgit
import proguard.gradle.ProGuardTask import proguard.gradle.ProGuardTask
@@ -164,7 +171,7 @@ task proguard(type: ProGuardTask, dependsOn: jar) {
dontobfuscate; dontoptimize; keepattributes; keepparameternames dontobfuscate; dontoptimize; keepattributes; keepparameternames
// Proguard will remove directories by default, but that breaks JarMount. // Proguard will remove directories by default, but that breaks JarMount.
keepdirectories 'assets/computercraft/lua**' keepdirectories 'data/computercraft/lua**'
// Preserve ComputerCraft classes - we only want to strip shadowed files. // Preserve ComputerCraft classes - we only want to strip shadowed files.
keep 'class dan200.computercraft.** { *; }' keep 'class dan200.computercraft.** { *; }'
@@ -241,6 +248,7 @@ task compressJson(dependsOn: jar) {
// Copy over all files in the current jar to the new one, running json files from GSON. As pretty printing // 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. // is turned off, they should be minified.
new ZipFile(jarPath).withCloseable { inJar -> new ZipFile(jarPath).withCloseable { inJar ->
tempPath.getParentFile().mkdirs()
new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tempPath))).withCloseable { outJar -> new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tempPath))).withCloseable { outJar ->
inJar.entries().each { entry -> inJar.entries().each { entry ->
if(entry.directory) { if(entry.directory) {
@@ -268,12 +276,91 @@ task compressJson(dependsOn: jar) {
assemble.dependsOn compressJson assemble.dependsOn compressJson
/* Check tasks */
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')
}
}
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/data/computercraft/lua/rom/help/changelog.txt")
inputs.file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt")
doLast {
def ok = true
// Check we're targetting the current version
def whatsnew = new File("src/main/resources/data/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/data/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 { curseforge {
apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : '' apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : ''
project { project {
id = '282001' id = '282001'
releaseType = 'beta' 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})." 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"
}
} }
} }
@@ -330,6 +417,31 @@ uploadArchives {
} }
} }
githubRelease {
token project.hasProperty('githubApiKey') ? project.githubApiKey : ''
owner 'SquidDev-CC'
repo 'CC-Tweaked'
targetCommitish { Grgit.open(dir: '.').branch.current().name }
tagName "v${mc_version}-${mod_version}"
releaseName "[${mc_version}] ${mod_version}"
body {
"## " + new File("src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt")
.readLines()
.takeWhile { it != 'Type "help changelog" to see the full version history.' }
.join("\n").trim()
}
prerelease false
}
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)"
}
test { test {
useJUnitPlatform() useJUnitPlatform()
testLogging { testLogging {
@@ -343,5 +455,9 @@ gradle.projectsEvaluated {
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint" << "-Xlint:-processing" // Causes Forge build to fail << "-Werror" options.compilerArgs << "-Xlint" << "-Xlint:-processing" // Causes Forge build to fail << "-Werror"
} }
tasks.withType(LicenseFormat) {
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,13 @@
<?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>
<!-- Has a public m_label field. We need to check if this is used in other projects before renaming it. -->
<suppress checks="MemberName" files=".*[\\/]TileComputerBase.java"
message="Name 'm_label' must match pattern .*" />
<!-- 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,7 +1,7 @@
# Mod properties # Mod properties
mod_version=1.82.0 mod_version=1.83.1
# Minecraft properties # Minecraft properties
mc_version=1.13.2 mc_version=1.13.2
forge_version=25.0.100 forge_version=25.0.219
mappings_version=20190327-1.13.2 mappings_version=20190530-1.13.2

View File

@@ -43,6 +43,7 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
{ {
} }
@Nonnull
@Override @Override
public String getInstalledVersion() public String getInstalledVersion()
{ {
@@ -110,6 +111,7 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
PocketUpgrades.register( upgrade ); PocketUpgrades.register( upgrade );
} }
@Nonnull
@Override @Override
public IPacketNetwork getWirelessNetwork() public IPacketNetwork getWirelessNetwork()
{ {
@@ -122,19 +124,18 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
ApiFactories.register( factory ); ApiFactories.register( factory );
} }
@Nonnull
@Override @Override
public IWiredNode createWiredNodeForElement( @Nonnull IWiredElement element ) public IWiredNode createWiredNodeForElement( @Nonnull IWiredElement element )
{ {
return new WiredNode( element ); return new WiredNode( element );
} }
@Nonnull
@Override @Override
public IWiredElement getWiredElementAt( @Nonnull IBlockReader world, @Nonnull BlockPos pos, @Nonnull EnumFacing side ) public LazyOptional<IWiredElement> getWiredElementAt( @Nonnull IBlockReader world, @Nonnull BlockPos pos, @Nonnull EnumFacing side )
{ {
TileEntity tile = world.getTileEntity( pos ); TileEntity tile = world.getTileEntity( pos );
if( tile == null ) return null; return tile == null ? LazyOptional.empty() : tile.getCapability( CapabilityWiredElement.CAPABILITY, side );
LazyOptional<IWiredElement> element = tile.getCapability( CapabilityWiredElement.CAPABILITY, side );
return CapabilityWiredElement.unwrap( element );
} }
} }

View File

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

View File

@@ -24,6 +24,7 @@ import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader; import net.minecraft.world.IBlockReader;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -240,8 +241,8 @@ public final class ComputerCraftAPI
* @return The element's node * @return The element's node
* @see IWiredElement#getNode() * @see IWiredElement#getNode()
*/ */
@Nullable @Nonnull
public static IWiredElement getWiredElementAt( @Nonnull IBlockReader world, @Nonnull BlockPos pos, @Nonnull EnumFacing side ) public static LazyOptional<IWiredElement> getWiredElementAt( @Nonnull IBlockReader world, @Nonnull BlockPos pos, @Nonnull EnumFacing side )
{ {
return getInstance().getWiredElementAt( world, pos, side ); return getInstance().getWiredElementAt( world, pos, side );
} }
@@ -266,12 +267,15 @@ public final class ComputerCraftAPI
public interface IComputerCraftAPI public interface IComputerCraftAPI
{ {
@Nonnull
String getInstalledVersion(); String getInstalledVersion();
int createUniqueNumberedSaveDir( @Nonnull World world, @Nonnull String parentSubPath ); int createUniqueNumberedSaveDir( @Nonnull World world, @Nonnull String parentSubPath );
@Nullable
IWritableMount createSaveDirMount( @Nonnull World world, @Nonnull String subPath, long capacity ); IWritableMount createSaveDirMount( @Nonnull World world, @Nonnull String subPath, long capacity );
@Nullable
IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath ); IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath );
void registerPeripheralProvider( @Nonnull IPeripheralProvider provider ); void registerPeripheralProvider( @Nonnull IPeripheralProvider provider );
@@ -286,12 +290,15 @@ public final class ComputerCraftAPI
void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade ); void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade );
@Nonnull
IPacketNetwork getWirelessNetwork(); IPacketNetwork getWirelessNetwork();
void registerAPIFactory( @Nonnull ILuaAPIFactory factory ); void registerAPIFactory( @Nonnull ILuaAPIFactory factory );
@Nonnull
IWiredNode createWiredNodeForElement( @Nonnull IWiredElement element ); IWiredNode createWiredNodeForElement( @Nonnull IWiredElement element );
IWiredElement getWiredElementAt( @Nonnull IBlockReader world, @Nonnull BlockPos pos, @Nonnull EnumFacing side ); @Nonnull
LazyOptional<IWiredElement> getWiredElementAt( @Nonnull IBlockReader world, @Nonnull BlockPos pos, @Nonnull EnumFacing side );
} }
} }

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. * @throws IOException If the file does not exist, or could not be opened.
*/ */
@Nonnull @Nonnull
@SuppressWarnings( "deprecation" )
default ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException default ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
{ {
return Channels.newChannel( openForRead( path ) ); 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. * @throws IOException If the file could not be opened for writing.
*/ */
@Nonnull @Nonnull
@SuppressWarnings( "deprecation" )
default WritableByteChannel openChannelForWrite( @Nonnull String path ) throws IOException default WritableByteChannel openChannelForWrite( @Nonnull String path ) throws IOException
{ {
return Channels.newChannel( openForWrite( path ) ); 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. * @throws IOException If the file could not be opened for writing.
*/ */
@Nonnull @Nonnull
@SuppressWarnings( "deprecation" )
default WritableByteChannel openChannelForAppend( @Nonnull String path ) throws IOException default WritableByteChannel openChannelForAppend( @Nonnull String path ) throws IOException
{ {
return Channels.newChannel( openForAppend( path ) ); return Channels.newChannel( openForAppend( path ) );

View File

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

View File

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

View File

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

View File

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

View File

@@ -121,6 +121,12 @@ public final class ClientRegistry
@SubscribeEvent @SubscribeEvent
public static void onItemColours( ColorHandlerEvent.Item event ) public static void onItemColours( ColorHandlerEvent.Item event )
{ {
if( ComputerCraft.Items.disk == null || ComputerCraft.Blocks.turtleNormal == null )
{
ComputerCraft.log.warn( "Block/item registration has failed. Skipping registration of item colours." );
return;
}
event.getItemColors().register( event.getItemColors().register(
( stack, layer ) -> layer == 1 ? ((ItemDisk) stack.getItem()).getColour( stack ) : 0xFFFFFF, ( stack, layer ) -> layer == 1 ? ((ItemDisk) stack.getItem()).getColour( stack ) : 0xFFFFFF,
ComputerCraft.Items.disk ComputerCraft.Items.disk

View File

@@ -20,9 +20,10 @@ import net.minecraft.util.ResourceLocation;
public class GuiComputer extends GuiContainer public class GuiComputer extends GuiContainer
{ {
private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation( "computercraft", "textures/gui/corners_normal.png" ); public static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_normal.png" );
private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( "computercraft", "textures/gui/corners_advanced.png" ); public static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_advanced.png" );
private static final ResourceLocation BACKGROUND_COMMAND = new ResourceLocation( "computercraft", "textures/gui/corners_command.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 ComputerFamily m_family;
private final ClientComputer m_computer; private final ClientComputer m_computer;
@@ -118,12 +119,12 @@ public class GuiComputer extends GuiContainer
} }
drawTexturedModalRect( startX - 12, startY - 12, 12, 28, 12, 12 ); 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, 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, 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( startX - 12, startY, 0, 28, 12, endY - startY );
drawTexturedModalRect( endX, startY, 36, 28, 12, endY - startY ); drawTexturedModalRect( endX, startY, 36, 28, 12, endY - startY );

View File

@@ -88,7 +88,7 @@ public class GuiTurtle extends GuiContainer
int slotX = slot % 4; int slotX = slot % 4;
int slotY = slot / 4; int slotY = slot / 4;
mc.getTextureManager().bindTexture( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL ); 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

@@ -75,8 +75,9 @@ public final class ComputerCraftProxyClient
ModLoadingContext.get().registerExtensionPoint( ExtensionPoint.GUIFACTORY, () -> packet -> { ModLoadingContext.get().registerExtensionPoint( ExtensionPoint.GUIFACTORY, () -> packet -> {
ContainerType<?> type = ContainerType.factories.get( packet.getId() ).get(); ContainerType<?> type = ContainerType.factories.get( packet.getId() ).get();
if( packet.getAdditionalData() != null ) type.fromBytes( packet.getAdditionalData() ); if( packet.getAdditionalData() != null ) type.fromBytes( packet.getAdditionalData() );
return ((BiFunction<ContainerType<?>, EntityPlayer, GuiContainer>) ContainerType.guiFactories.get( packet.getId() )) @SuppressWarnings( "unchecked" )
.apply( type, Minecraft.getInstance().player ); BiFunction<ContainerType<?>, EntityPlayer, GuiContainer> factory = (BiFunction<ContainerType<?>, EntityPlayer, GuiContainer>) ContainerType.guiFactories.get( packet.getId() );
return factory.apply( type, Minecraft.getInstance().player );
} ); } );
} }

View File

@@ -26,9 +26,9 @@ import net.minecraftforge.fml.common.Mod;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT ) @Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public final class RenderOverlayCable public final class CableHighlightRenderer
{ {
private RenderOverlayCable() private CableHighlightRenderer()
{ {
} }
@@ -74,7 +74,8 @@ public final class RenderOverlayCable
double z = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * partialTicks; double z = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * partialTicks;
VoxelShape shape = WorldUtil.isVecInside( CableShapes.getModemShape( state ), event.getTarget().hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) ) VoxelShape shape = WorldUtil.isVecInside( CableShapes.getModemShape( state ), event.getTarget().hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) )
? CableShapes.getModemShape( state ) : CableShapes.getCableShape( state ); ? CableShapes.getModemShape( state )
: CableShapes.getCableShape( state );
WorldRenderer.drawShape( shape, pos.getX() - x, pos.getY() - y, pos.getZ() - z, 0.0F, 0.0F, 0.0F, 0.4F ); WorldRenderer.drawShape( shape, pos.getX() - x, pos.getY() - y, pos.getZ() - z, 0.0F, 0.0F, 0.0F, 0.4F );

View File

@@ -12,25 +12,24 @@ import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.computer.core.ClientComputer; 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.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.Palette; import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.ItemRenderer; import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.event.RenderSpecificHandEvent; import net.minecraftforge.client.event.RenderSpecificHandEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT; import static dan200.computercraft.client.gui.FixedWidthFontRenderer.*;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH; import static dan200.computercraft.client.gui.GuiComputer.*;
/** /**
* Emulates map rendering for pocket computers * 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 = Dist.CLIENT ) @Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public final class ItemPocketRenderer extends ItemMapLikeRenderer 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 static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer();
private ItemPocketRenderer() private ItemPocketRenderer()
@@ -57,115 +60,195 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
@Override @Override
protected void renderItem( ItemStack stack ) 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 // Setup various transformations. Note that these are partially adapted from the corresponding method
// in ItemRenderer // in ItemRenderer
GlStateManager.pushMatrix();
GlStateManager.disableLighting(); GlStateManager.disableLighting();
GlStateManager.disableDepthTest();
GlStateManager.rotatef( 180f, 0f, 1f, 0f ); GlStateManager.rotatef( 180f, 0f, 1f, 0f );
GlStateManager.rotatef( 180f, 0f, 0f, 1f ); GlStateManager.rotatef( 180f, 0f, 0f, 1f );
GlStateManager.scalef( 0.5f, 0.5f, 0.5f ); GlStateManager.scalef( 0.5f, 0.5f, 0.5f );
ClientComputer computer = ItemPocketComputer.createClientComputer( stack ); double scale = 0.75 / Math.max( width + FRAME * 2, height + FRAME * 2 + LIGHT_HEIGHT );
GlStateManager.scaled( scale, scale, 0 );
GlStateManager.translated( -0.5 * width, -0.5 * height, 0 );
// Render the main frame
ItemPocketComputer item = (ItemPocketComputer) stack.getItem();
ComputerFamily family = item.getFamily();
int frameColour = item.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 // If we've a computer and terminal then attempt to render it.
// we display the pocket light and other such decorations. renderTerminal( terminal, !computer.isColour(), width, height );
GlStateManager.pushMatrix(); }
else
{
// Otherwise render a plain background
Minecraft.getInstance().getTextureManager().bindTexture( BACKGROUND );
GlStateManager.scalef( 1.0f, -1.0f, 1.0f ); Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
Minecraft minecraft = Minecraft.getInstance(); Colour black = Colour.Black;
TextureManager textureManager = minecraft.getTextureManager(); buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION );
ItemRenderer renderItem = minecraft.getItemRenderer(); renderTexture( buffer, 0, 0, 0, 0, width, height, black.getR(), black.getG(), black.getB() );
tessellator.draw();
// 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.enableAlphaTest();
GlStateManager.alphaFunc( GL11.GL_GREATER, 0.1F );
GlStateManager.enableBlend();
GlStateManager.blendFunc( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA );
GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
IBakedModel baked = renderItem.getItemModelWithOverrides( stack, null, null );
baked = ForgeHooksClient.handleCameraTransforms( baked, TransformType.GUI, false );
renderItem.renderItem( stack, baked );
GlStateManager.disableAlphaTest();
GlStateManager.disableRescaleNormal();
GlStateManager.popMatrix();
} }
// If we've a computer and terminal then attempt to render it. GlStateManager.enableDepthTest();
if( computer != null ) GlStateManager.enableLighting();
GlStateManager.popMatrix();
}
private static void renderFrame( ComputerFamily family, int colour, int width, int height )
{
Minecraft.getInstance().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 )
{ {
Terminal terminal = computer.getTerminal(); int termWidth = terminal.getWidth();
if( terminal != null ) int termHeight = terminal.getHeight();
FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance();
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 < termWidth; line++ )
{ {
synchronized( terminal ) TextBuffer text = terminal.getLine( line );
{ TextBuffer colour = terminal.getTextColourLine( line );
GlStateManager.pushMatrix(); TextBuffer backgroundColour = terminal.getBackgroundColourLine( line );
GlStateManager.disableDepthTest(); fontRenderer.drawString(
text, MARGIN, MARGIN + line * FONT_HEIGHT,
colour, backgroundColour, MARGIN, MARGIN, greyscale, palette
);
}
// Reset the position to be at the top left corner of the pocket computer // And render the cursor;
// Note we translate towards the screen slightly too. int tx = terminal.getCursorX(), ty = terminal.getCursorY();
GlStateManager.translated( -8 / 16.0, -8 / 16.0, 0.5 / 16.0 ); if( terminal.getCursorBlink() && FrameInfo.getGlobalCursorBlink() &&
// Translate to the top left of the screen. tx >= 0 && ty >= 0 && tx < termWidth && ty < termHeight )
GlStateManager.translated( 4 / 16.0, 3 / 16.0, 0 ); {
TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 );
// Work out the scaling required to resize the terminal in order to fit on the computer fontRenderer.drawString(
final int margin = 2; new TextBuffer( '_', 1 ), MARGIN + FONT_WIDTH * tx, MARGIN + FONT_HEIGHT * ty,
int tw = terminal.getWidth(); cursorColour, null, 0, 0, greyscale, palette
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.scaled( 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;
FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance();
boolean greyscale = !computer.isColour();
Palette palette = terminal.getPalette();
// Render the actual text
for( int line = 0; line < th; 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
);
}
// And render the cursor;
int tx = terminal.getCursorX(), ty = terminal.getCursorY();
if( terminal.getCursorBlink() && FrameInfo.getGlobalCursorBlink() &&
tx >= 0 && ty >= 0 && tx < tw && ty < th )
{
TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 );
fontRenderer.drawString(
new TextBuffer( '_', 1 ), startX + FONT_WIDTH * tx, startY + FONT_HEIGHT * ty,
cursorColour, null, 0, 0, greyscale, palette
);
}
GlStateManager.enableDepthTest();
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[] before = new Point3f[4];
private final Point3f[] after = 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 ); super( parent );
this.positionMatrix = positionMatrix; this.positionMatrix = positionMatrix;

View File

@@ -0,0 +1,117 @@
/*
* 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.Minecraft;
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.api.distmarker.Dist;
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import org.lwjgl.opengl.GL11;
import java.util.EnumSet;
import static net.minecraft.util.EnumFacing.*;
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public final class MonitorHighlightRenderer
{
private static final float EXPAND = 0.002f;
private MonitorHighlightRenderer()
{
}
@SubscribeEvent
public static void drawHighlight( DrawBlockHighlightEvent event )
{
if( event.getTarget().type != RayTraceResult.Type.BLOCK || event.getPlayer().isSneaking() ) return;
World world = event.getPlayer().getEntityWorld();
BlockPos pos = event.getTarget().getBlockPos();
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.blendFuncSeparate( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO );
GlStateManager.lineWidth( Math.max( 2.5F, (float) Minecraft.getInstance().mainWindow.getFramebufferWidth() / 1920.0F * 2.5F ) );
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.translated( -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

@@ -11,6 +11,7 @@ import dan200.computercraft.shared.peripheral.modem.wired.BlockCable;
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant; import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
import dan200.computercraft.shared.peripheral.modem.wired.CableShapes; import dan200.computercraft.shared.peripheral.modem.wired.CableShapes;
import dan200.computercraft.shared.peripheral.modem.wired.TileCable; import dan200.computercraft.shared.peripheral.modem.wired.TileCable;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@@ -25,10 +26,10 @@ import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.BlockRenderLayer; import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.client.ForgeHooksClient; import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.MinecraftForgeClient; import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.client.model.data.EmptyModelData;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@@ -60,8 +61,7 @@ public class TileEntityCableRenderer extends TileEntityRenderer<TileCable>
Block block = state.getBlock(); Block block = state.getBlock();
if( block != ComputerCraft.Blocks.cable ) return; if( block != ComputerCraft.Blocks.cable ) return;
VoxelShape shape = CableShapes.getModemShape( state ); state = WorldUtil.isVecInside( CableShapes.getModemShape( state ), hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) )
state = te.hasModem() && shape.getBoundingBox().grow( 0.02, 0.02, 0.02 ).contains( hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) )
? block.getDefaultState().with( BlockCable.MODEM, state.get( BlockCable.MODEM ) ) ? block.getDefaultState().with( BlockCable.MODEM, state.get( BlockCable.MODEM ) )
: state.with( BlockCable.MODEM, CableModemVariant.None ); : state.with( BlockCable.MODEM, CableModemVariant.None );
@@ -81,7 +81,7 @@ public class TileEntityCableRenderer extends TileEntityRenderer<TileCable>
mc.getBlockRendererDispatcher().getBlockModelRenderer().renderModel( mc.getBlockRendererDispatcher().getBlockModelRenderer().renderModel(
world, world,
ForgeHooksClient.getDamageModel( model, breakingTexture, state, world, pos ), ForgeHooksClient.getDamageModel( model, breakingTexture, state, world, pos ),
state, pos, buffer, true, random, state.getPositionRandom( pos ) state, pos, buffer, true, random, state.getPositionRandom( pos ), EmptyModelData.INSTANCE
); );
ForgeHooksClient.setRenderLayer( BlockRenderLayer.SOLID ); ForgeHooksClient.setRenderLayer( BlockRenderLayer.SOLID );

View File

@@ -31,6 +31,7 @@ import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.ForgeHooksClient; import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.data.EmptyModelData;
import net.minecraftforge.client.model.pipeline.LightUtil; import net.minecraftforge.client.model.pipeline.LightUtil;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
@@ -197,10 +198,10 @@ public class TileEntityTurtleRenderer extends TileEntityRenderer<TileTurtle>
Random random = new Random( 0 ); Random random = new Random( 0 );
Tessellator tessellator = Tessellator.getInstance(); Tessellator tessellator = Tessellator.getInstance();
rendererDispatcher.textureManager.bindTexture( TextureMap.LOCATION_BLOCKS_TEXTURE ); rendererDispatcher.textureManager.bindTexture( TextureMap.LOCATION_BLOCKS_TEXTURE );
renderQuads( tessellator, model.getQuads( state, null, random ), tints ); renderQuads( tessellator, model.getQuads( state, null, random, EmptyModelData.INSTANCE ), tints );
for( EnumFacing facing : DirectionUtil.FACINGS ) for( EnumFacing facing : DirectionUtil.FACINGS )
{ {
renderQuads( tessellator, model.getQuads( state, facing, random ), tints ); renderQuads( tessellator, model.getQuads( state, facing, random, EmptyModelData.INSTANCE ), tints );
} }
} }

View File

@@ -70,7 +70,10 @@ public final class TurtleModelLoader implements ICustomModelLoader
{ {
private final ResourceLocation family; private final ResourceLocation family;
private TurtleModel( ResourceLocation family ) {this.family = family;} private TurtleModel( ResourceLocation family )
{
this.family = family;
}
@Nonnull @Nonnull
@Override @Override

View File

@@ -9,10 +9,11 @@ package dan200.computercraft.client.render;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.model.BakedQuad; import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemCameraTransforms;
import net.minecraft.client.renderer.model.ItemOverrideList; import net.minecraft.client.renderer.model.ItemOverrideList;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraftforge.client.model.data.EmptyModelData;
import net.minecraftforge.client.model.data.IModelData;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.vecmath.Matrix4f; import javax.vecmath.Matrix4f;
@@ -44,7 +45,15 @@ public class TurtleMultiModel implements IBakedModel
@Nonnull @Nonnull
@Override @Override
@Deprecated
public List<BakedQuad> getQuads( IBlockState state, EnumFacing side, @Nonnull Random rand ) public List<BakedQuad> getQuads( IBlockState state, EnumFacing side, @Nonnull Random rand )
{
return getQuads( state, side, rand, EmptyModelData.INSTANCE );
}
@Nonnull
@Override
public List<BakedQuad> getQuads( IBlockState state, EnumFacing side, @Nonnull Random rand, @Nonnull IModelData data )
{ {
if( side != null ) if( side != null )
{ {
@@ -61,14 +70,10 @@ public class TurtleMultiModel implements IBakedModel
private List<BakedQuad> buildQuads( IBlockState state, EnumFacing side, Random rand ) private List<BakedQuad> buildQuads( IBlockState state, EnumFacing side, Random rand )
{ {
ArrayList<BakedQuad> quads = new ArrayList<>(); ArrayList<BakedQuad> quads = new ArrayList<>();
ModelTransformer.transformQuadsTo( quads, m_baseModel.getQuads( state, side, rand ), m_generalTransform ); ModelTransformer.transformQuadsTo( quads, m_baseModel.getQuads( state, side, rand, EmptyModelData.INSTANCE ), m_generalTransform );
if( m_overlayModel != null ) if( m_overlayModel != null )
{ {
ModelTransformer.transformQuadsTo( quads, m_overlayModel.getQuads( state, side, rand ), m_generalTransform ); ModelTransformer.transformQuadsTo( quads, m_overlayModel.getQuads( state, side, rand, EmptyModelData.INSTANCE ), m_generalTransform );
}
if( m_overlayModel != null )
{
ModelTransformer.transformQuadsTo( quads, m_overlayModel.getQuads( state, side, rand ), m_generalTransform );
} }
if( m_leftUpgradeModel != null ) if( m_leftUpgradeModel != null )
{ {
@@ -78,7 +83,7 @@ public class TurtleMultiModel implements IBakedModel
upgradeTransform = new Matrix4f( m_generalTransform ); upgradeTransform = new Matrix4f( m_generalTransform );
upgradeTransform.mul( m_leftUpgradeTransform ); upgradeTransform.mul( m_leftUpgradeTransform );
} }
ModelTransformer.transformQuadsTo( quads, m_leftUpgradeModel.getQuads( state, side, rand ), upgradeTransform ); ModelTransformer.transformQuadsTo( quads, m_leftUpgradeModel.getQuads( state, side, rand, EmptyModelData.INSTANCE ), upgradeTransform );
} }
if( m_rightUpgradeModel != null ) if( m_rightUpgradeModel != null )
{ {
@@ -88,7 +93,7 @@ public class TurtleMultiModel implements IBakedModel
upgradeTransform = new Matrix4f( m_generalTransform ); upgradeTransform = new Matrix4f( m_generalTransform );
upgradeTransform.mul( m_rightUpgradeTransform ); upgradeTransform.mul( m_rightUpgradeTransform );
} }
ModelTransformer.transformQuadsTo( quads, m_rightUpgradeModel.getQuads( state, side, rand ), upgradeTransform ); ModelTransformer.transformQuadsTo( quads, m_rightUpgradeModel.getQuads( state, side, rand, EmptyModelData.INSTANCE ), upgradeTransform );
} }
quads.trimToSize(); quads.trimToSize();
return quads; return quads;
@@ -122,7 +127,7 @@ public class TurtleMultiModel implements IBakedModel
@Nonnull @Nonnull
@Override @Override
@Deprecated @Deprecated
public ItemCameraTransforms getItemCameraTransforms() public net.minecraft.client.renderer.model.ItemCameraTransforms getItemCameraTransforms()
{ {
return m_baseModel.getItemCameraTransforms(); return m_baseModel.getItemCameraTransforms();
} }

View File

@@ -21,6 +21,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.client.model.data.IModelData;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@@ -167,11 +168,20 @@ public class TurtleSmartItemModel implements IBakedModel
@Nonnull @Nonnull
@Override @Override
@Deprecated
public List<BakedQuad> getQuads( IBlockState state, EnumFacing facing, @Nonnull Random rand ) public List<BakedQuad> getQuads( IBlockState state, EnumFacing facing, @Nonnull Random rand )
{ {
return familyModel.getQuads( state, facing, rand ); return familyModel.getQuads( state, facing, rand );
} }
@Nonnull
@Override
@Deprecated
public List<BakedQuad> getQuads( IBlockState state, EnumFacing facing, @Nonnull Random rand, @Nonnull IModelData data )
{
return familyModel.getQuads( state, facing, rand, data );
}
@Override @Override
public boolean isAmbientOcclusion() public boolean isAmbientOcclusion()
{ {

View File

@@ -6,13 +6,13 @@
package dan200.computercraft.core.apis; package dan200.computercraft.core.apis;
import com.google.common.base.Preconditions;
import dan200.computercraft.api.lua.ILuaAPIFactory; import dan200.computercraft.api.lua.ILuaAPIFactory;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Objects;
public final class ApiFactories public final class ApiFactories
{ {
@@ -25,7 +25,7 @@ public final class ApiFactories
public static synchronized void register( @Nonnull ILuaAPIFactory factory ) public static synchronized void register( @Nonnull ILuaAPIFactory factory )
{ {
Preconditions.checkNotNull( factory, "provider cannot be null" ); Objects.requireNonNull( factory, "provider cannot be null" );
factories.add( factory ); factories.add( factory );
} }

View File

@@ -6,7 +6,6 @@
package dan200.computercraft.core.apis; package dan200.computercraft.core.apis;
import com.google.common.base.Preconditions;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount; import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IComputerAccess;
@@ -121,7 +120,7 @@ public abstract class ComputerAccess implements IComputerAccess
@Override @Override
public void queueEvent( @Nonnull final String event, final Object[] arguments ) public void queueEvent( @Nonnull final String event, final Object[] arguments )
{ {
Preconditions.checkNotNull( event, "event cannot be null" ); Objects.requireNonNull( event, "event cannot be null" );
m_environment.queueEvent( event, arguments ); m_environment.queueEvent( event, arguments );
} }

View File

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

View File

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

View File

@@ -8,6 +8,7 @@ package dan200.computercraft.core.apis;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IWorkMonitor; import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.core.computer.IComputerEnvironment; import dan200.computercraft.core.computer.IComputerEnvironment;
import dan200.computercraft.core.filesystem.FileSystem; import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
@@ -18,16 +19,10 @@ import javax.annotation.Nullable;
public interface IAPIEnvironment public interface IAPIEnvironment
{ {
String[] SIDE_NAMES = new String[] {
"bottom", "top", "back", "front", "right", "left",
};
int SIDE_COUNT = 6;
@FunctionalInterface @FunctionalInterface
interface IPeripheralChangeListener interface IPeripheralChangeListener
{ {
void onPeripheralChanged( int side, @Nullable IPeripheral newPeripheral ); void onPeripheralChanged( ComputerSide side, @Nullable IPeripheral newPeripheral );
} }
int getComputerID(); int getComputerID();
@@ -49,22 +44,22 @@ public interface IAPIEnvironment
void queueEvent( String event, Object[] args ); void queueEvent( String event, Object[] args );
void setOutput( int side, int output ); void setOutput( ComputerSide side, int output );
int getOutput( int side ); int getOutput( ComputerSide side );
int getInput( int side ); int getInput( ComputerSide side );
void setBundledOutput( int side, int output ); void setBundledOutput( ComputerSide side, int output );
int getBundledOutput( int side ); int getBundledOutput( ComputerSide side );
int getBundledInput( int side ); int getBundledInput( ComputerSide side );
void setPeripheralChangeListener( @Nullable IPeripheralChangeListener listener ); void setPeripheralChangeListener( @Nullable IPeripheralChangeListener listener );
@Nullable @Nullable
IPeripheral getPeripheral( int side ); IPeripheral getPeripheral( ComputerSide side );
String getLabel(); String getLabel();

View File

@@ -10,6 +10,7 @@ package dan200.computercraft.core.apis;
* This exists purely to ensure binary compatibility. * This exists purely to ensure binary compatibility.
* *
* @see dan200.computercraft.api.lua.ILuaAPI * @see dan200.computercraft.api.lua.ILuaAPI
* @deprecated Use the version in the public API. Only exists for compatibility with CCEmuX.
*/ */
@Deprecated @Deprecated
public interface ILuaAPI extends dan200.computercraft.api.lua.ILuaAPI 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 dan200.computercraft.shared.util.StringUtil;
import javax.annotation.Nonnull; 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 java.util.*;
import static dan200.computercraft.core.apis.ArgumentHelper.*; import static dan200.computercraft.core.apis.ArgumentHelper.*;
@@ -31,9 +36,9 @@ public class OSAPI implements ILuaAPI
private static class Timer private static class Timer
{ {
public int m_ticksLeft; int m_ticksLeft;
public Timer( int ticksLeft ) Timer( int ticksLeft )
{ {
m_ticksLeft = ticksLeft; m_ticksLeft = ticksLeft;
} }
@@ -41,10 +46,10 @@ public class OSAPI implements ILuaAPI
private static class Alarm implements Comparable<Alarm> private static class Alarm implements Comparable<Alarm>
{ {
public final double m_time; final double m_time;
public final int m_day; final int m_day;
public Alarm( double time, int day ) Alarm( double time, int day )
{ {
m_time = time; m_time = time;
m_day = day; m_day = day;
@@ -73,9 +78,7 @@ public class OSAPI implements ILuaAPI
@Override @Override
public String[] getNames() public String[] getNames()
{ {
return new String[] { return new String[] { "os" };
"os"
};
} }
@Override @Override
@@ -184,11 +187,12 @@ public class OSAPI implements ILuaAPI
"day", "day",
"cancelTimer", "cancelTimer",
"cancelAlarm", "cancelAlarm",
"epoch" "epoch",
"date",
}; };
} }
private float getTimeForCalendar( Calendar c ) private static float getTimeForCalendar( Calendar c )
{ {
float time = c.get( Calendar.HOUR_OF_DAY ); float time = c.get( Calendar.HOUR_OF_DAY );
time += c.get( Calendar.MINUTE ) / 60.0f; time += c.get( Calendar.MINUTE ) / 60.0f;
@@ -196,7 +200,7 @@ public class OSAPI implements ILuaAPI
return time; return time;
} }
private int getDayForCalendar( Calendar c ) private static int getDayForCalendar( Calendar c )
{ {
GregorianCalendar g = c instanceof GregorianCalendar ? (GregorianCalendar) c : new GregorianCalendar(); GregorianCalendar g = c instanceof GregorianCalendar ? (GregorianCalendar) c : new GregorianCalendar();
int year = c.get( Calendar.YEAR ); int year = c.get( Calendar.YEAR );
@@ -209,7 +213,7 @@ public class OSAPI implements ILuaAPI
return day; return day;
} }
private long getEpochForCalendar( Calendar c ) private static long getEpochForCalendar( Calendar c )
{ {
return c.getTime().getTime(); return c.getTime().getTime();
} }
@@ -282,6 +286,9 @@ public class OSAPI implements ILuaAPI
case 11: case 11:
{ {
// time // 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" ); String param = optString( args, 0, "ingame" );
switch( param.toLowerCase( Locale.ROOT ) ) switch( param.toLowerCase( Locale.ROOT ) )
{ {
@@ -355,9 +362,8 @@ public class OSAPI implements ILuaAPI
} }
return null; return null;
} }
case 15: case 15: // epoch
{ {
// epoch
String param = optString( args, 0, "ingame" ); String param = optString( args, 0, "ingame" );
switch( param.toLowerCase( Locale.ROOT ) ) switch( param.toLowerCase( Locale.ROOT ) )
{ {
@@ -377,14 +383,39 @@ public class OSAPI implements ILuaAPI
// Get in-game epoch // Get in-game epoch
synchronized( m_alarms ) synchronized( m_alarms )
{ {
return new Object[] { return new Object[] { m_day * 86400000 + (int) (m_time * 3600000.0f) };
m_day * 86400000 + (int) (m_time * 3600000.0f)
};
} }
default: default:
throw new LuaException( "Unsupported operation" ); 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: default:
return null; return null;
} }

View File

@@ -12,6 +12,7 @@ import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.core.tracking.TrackingField; import dan200.computercraft.core.tracking.TrackingField;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@@ -35,7 +36,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
private Map<String, Integer> m_methodMap; private Map<String, Integer> m_methodMap;
private boolean m_attached; private boolean m_attached;
public PeripheralWrapper( IPeripheral peripheral, String side ) PeripheralWrapper( IPeripheral peripheral, String side )
{ {
super( m_environment ); super( m_environment );
m_side = side; m_side = side;
@@ -245,32 +246,33 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
// IPeripheralChangeListener // IPeripheralChangeListener
@Override @Override
public void onPeripheralChanged( int side, IPeripheral newPeripheral ) public void onPeripheralChanged( ComputerSide side, IPeripheral newPeripheral )
{ {
synchronized( m_peripherals ) synchronized( m_peripherals )
{ {
if( m_peripherals[side] != null ) int index = side.ordinal();
if( m_peripherals[index] != null )
{ {
// Queue a detachment // Queue a detachment
final PeripheralWrapper wrapper = m_peripherals[side]; final PeripheralWrapper wrapper = m_peripherals[index];
if( wrapper.isAttached() ) wrapper.detach(); if( wrapper.isAttached() ) wrapper.detach();
// Queue a detachment event // Queue a detachment event
m_environment.queueEvent( "peripheral_detach", new Object[] { IAPIEnvironment.SIDE_NAMES[side] } ); m_environment.queueEvent( "peripheral_detach", new Object[] { side.getName() } );
} }
// Assign the new peripheral // Assign the new peripheral
m_peripherals[side] = newPeripheral == null ? null m_peripherals[index] = newPeripheral == null ? null
: new PeripheralWrapper( newPeripheral, IAPIEnvironment.SIDE_NAMES[side] ); : new PeripheralWrapper( newPeripheral, side.getName() );
if( m_peripherals[side] != null ) if( m_peripherals[index] != null )
{ {
// Queue an attachment // Queue an attachment
final PeripheralWrapper wrapper = m_peripherals[side]; final PeripheralWrapper wrapper = m_peripherals[index];
if( m_running && !wrapper.isAttached() ) wrapper.attach(); if( m_running && !wrapper.isAttached() ) wrapper.attach();
// Queue an attachment event // Queue an attachment event
m_environment.queueEvent( "peripheral", new Object[] { IAPIEnvironment.SIDE_NAMES[side] } ); m_environment.queueEvent( "peripheral", new Object[] { side.getName() } );
} }
} }
} }
@@ -280,9 +282,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
@Override @Override
public String[] getNames() public String[] getNames()
{ {
return new String[] { return new String[] { "peripheral" };
"peripheral"
};
} }
@Override @Override
@@ -324,7 +324,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
"isPresent", "isPresent",
"getType", "getType",
"getMethods", "getMethods",
"call" "call",
}; };
} }
@@ -337,16 +337,13 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
{ {
// isPresent // isPresent
boolean present = false; boolean present = false;
int side = parseSide( args ); ComputerSide side = ComputerSide.valueOfInsensitive( getString( args, 0 ) );
if( side >= 0 ) if( side != null )
{ {
synchronized( m_peripherals ) synchronized( m_peripherals )
{ {
PeripheralWrapper p = m_peripherals[side]; PeripheralWrapper p = m_peripherals[side.ordinal()];
if( p != null ) if( p != null ) present = true;
{
present = true;
}
} }
} }
return new Object[] { present }; return new Object[] { present };
@@ -354,21 +351,13 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
case 1: case 1:
{ {
// getType // getType
String type = null; ComputerSide side = ComputerSide.valueOfInsensitive( getString( args, 0 ) );
int side = parseSide( args ); if( side != null )
if( side >= 0 )
{ {
synchronized( m_peripherals ) synchronized( m_peripherals )
{ {
PeripheralWrapper p = m_peripherals[side]; PeripheralWrapper p = m_peripherals[side.ordinal()];
if( p != null ) if( p != null ) return new Object[] { p.getType() };
{
type = p.getType();
}
}
if( type != null )
{
return new Object[] { type };
} }
} }
return null; return null;
@@ -377,12 +366,12 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
{ {
// getMethods // getMethods
String[] methods = null; String[] methods = null;
int side = parseSide( args ); ComputerSide side = ComputerSide.valueOfInsensitive( getString( args, 0 ) );
if( side >= 0 ) if( side != null )
{ {
synchronized( m_peripherals ) synchronized( m_peripherals )
{ {
PeripheralWrapper p = m_peripherals[side]; PeripheralWrapper p = m_peripherals[side.ordinal()];
if( p != null ) if( p != null )
{ {
methods = p.getMethods(); methods = p.getMethods();
@@ -403,16 +392,16 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
case 3: case 3:
{ {
// call // call
int side = parseSide( args ); ComputerSide side = ComputerSide.valueOfInsensitive( getString( args, 0 ) );
String methodName = getString( args, 1 ); String methodName = getString( args, 1 );
Object[] methodArgs = Arrays.copyOfRange( args, 2, args.length ); Object[] methodArgs = Arrays.copyOfRange( args, 2, args.length );
if( side >= 0 ) if( side != null )
{ {
PeripheralWrapper p; PeripheralWrapper p;
synchronized( m_peripherals ) synchronized( m_peripherals )
{ {
p = m_peripherals[side]; p = m_peripherals[side.ordinal()];
} }
if( p != null ) if( p != null )
{ {
@@ -425,19 +414,4 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
return null; return null;
} }
} }
// Privates
private int parseSide( Object[] args ) throws LuaException
{
String side = getString( args, 0 );
for( int n = 0; n < IAPIEnvironment.SIDE_NAMES.length; n++ )
{
if( side.equals( IAPIEnvironment.SIDE_NAMES[n] ) )
{
return n;
}
}
return -1;
}
} }

View File

@@ -9,6 +9,7 @@ package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.ILuaAPI; import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.computer.ComputerSide;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.HashMap; import java.util.HashMap;
@@ -28,9 +29,7 @@ public class RedstoneAPI implements ILuaAPI
@Override @Override
public String[] getNames() public String[] getNames()
{ {
return new String[] { return new String[] { "rs", "redstone" };
"rs", "redstone"
};
} }
@Nonnull @Nonnull
@@ -64,56 +63,40 @@ public class RedstoneAPI implements ILuaAPI
{ {
// getSides // getSides
Map<Object, Object> table = new HashMap<>(); Map<Object, Object> table = new HashMap<>();
for( int i = 0; i < IAPIEnvironment.SIDE_NAMES.length; i++ ) for( int i = 0; i < ComputerSide.NAMES.length; i++ )
{ {
table.put( i + 1, IAPIEnvironment.SIDE_NAMES[i] ); table.put( i + 1, ComputerSide.NAMES[i] );
} }
return new Object[] { table }; return new Object[] { table };
} }
case 1: case 1:
{ {
// setOutput // setOutput
int side = parseSide( args ); ComputerSide side = parseSide( args );
boolean output = getBoolean( args, 1 ); boolean output = getBoolean( args, 1 );
m_environment.setOutput( side, output ? 15 : 0 ); m_environment.setOutput( side, output ? 15 : 0 );
return null; return null;
} }
case 2: case 2: // getOutput
{ return new Object[] { m_environment.getOutput( parseSide( args ) ) > 0 };
// getOutput case 3: // getInput
int side = parseSide( args ); return new Object[] { m_environment.getInput( parseSide( args ) ) > 0 };
return new Object[] { m_environment.getOutput( side ) > 0 };
}
case 3:
{
// getInput
int side = parseSide( args );
return new Object[] { m_environment.getInput( side ) > 0 };
}
case 4: case 4:
{ {
// setBundledOutput // setBundledOutput
int side = parseSide( args ); ComputerSide side = parseSide( args );
int output = getInt( args, 1 ); int output = getInt( args, 1 );
m_environment.setBundledOutput( side, output ); m_environment.setBundledOutput( side, output );
return null; return null;
} }
case 5: case 5: // getBundledOutput
{ return new Object[] { m_environment.getBundledOutput( parseSide( args ) ) };
// getBundledOutput case 6: // getBundledInput
int side = parseSide( args ); return new Object[] { m_environment.getBundledInput( parseSide( args ) ) };
return new Object[] { m_environment.getBundledOutput( side ) };
}
case 6:
{
// getBundledInput
int side = parseSide( args );
return new Object[] { m_environment.getBundledInput( side ) };
}
case 7: case 7:
{ {
// testBundledInput // testBundledInput
int side = parseSide( args ); ComputerSide side = parseSide( args );
int mask = getInt( args, 1 ); int mask = getInt( args, 1 );
int input = m_environment.getBundledInput( side ); int input = m_environment.getBundledInput( side );
return new Object[] { (input & mask) == mask }; return new Object[] { (input & mask) == mask };
@@ -122,7 +105,7 @@ public class RedstoneAPI implements ILuaAPI
case 9: case 9:
{ {
// setAnalogOutput/setAnalogueOutput // setAnalogOutput/setAnalogueOutput
int side = parseSide( args ); ComputerSide side = parseSide( args );
int output = getInt( args, 1 ); int output = getInt( args, 1 );
if( output < 0 || output > 15 ) if( output < 0 || output > 15 )
{ {
@@ -132,34 +115,20 @@ public class RedstoneAPI implements ILuaAPI
return null; return null;
} }
case 10: case 10:
case 11: case 11: // getAnalogOutput/getAnalogueOutput
{ return new Object[] { m_environment.getOutput( parseSide( args ) ) };
// getAnalogOutput/getAnalogueOutput
int side = parseSide( args );
return new Object[] { m_environment.getOutput( side ) };
}
case 12: case 12:
case 13: case 13: // getAnalogInput/getAnalogueInput
{ return new Object[] { m_environment.getInput( parseSide( args ) ) };
// getAnalogInput/getAnalogueInput
int side = parseSide( args );
return new Object[] { m_environment.getInput( side ) };
}
default: default:
return null; return null;
} }
} }
private static int parseSide( Object[] args ) throws LuaException private static ComputerSide parseSide( Object[] args ) throws LuaException
{ {
String side = getString( args, 0 ); ComputerSide side = ComputerSide.valueOfInsensitive( getString( args, 0 ) );
for( int n = 0; n < IAPIEnvironment.SIDE_NAMES.length; n++ ) if( side == null ) throw new LuaException( "Invalid side." );
{ return side;
if( side.equals( IAPIEnvironment.SIDE_NAMES[n] ) )
{
return n;
}
}
throw new LuaException( "Invalid side." );
} }
} }

View File

@@ -33,9 +33,7 @@ public class TermAPI implements ILuaAPI
@Override @Override
public String[] getNames() public String[] getNames()
{ {
return new String[] { return new String[] { "term" };
"term"
};
} }
@Nonnull @Nonnull
@@ -89,9 +87,7 @@ public class TermAPI implements ILuaAPI
public static Object[] encodeColour( int colour ) throws LuaException public static Object[] encodeColour( int colour ) throws LuaException
{ {
return new Object[] { return new Object[] { 1 << colour };
1 << colour
};
} }
public static void setColour( Terminal terminal, int colour, double r, double g, double b ) 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 case 3: // close
checkOpen();
close(); close();
return null; return null;
case 4: // seek case 4: // seek

View File

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

View File

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

View File

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

View File

@@ -37,8 +37,13 @@ public abstract class HandleGeneric implements ILuaObject
protected final void close() protected final void close()
{ {
m_open = false; m_open = false;
IoUtil.closeQuietly( m_closable );
m_closable = null; Closeable closeable = m_closable;
if( closeable != null )
{
IoUtil.closeQuietly( closeable );
m_closable = null;
}
} }
/** /**

View File

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

View File

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

View File

@@ -338,7 +338,7 @@ final class ComputerExecutor
} }
} }
private FileSystem createFileSystem() IWritableMount getRootMount()
{ {
if( rootMount == null ) if( rootMount == null )
{ {
@@ -347,11 +347,15 @@ final class ComputerExecutor
computer.getComputerEnvironment().getComputerSpaceLimit() computer.getComputerEnvironment().getComputerSpaceLimit()
); );
} }
return rootMount;
}
private FileSystem createFileSystem()
{
FileSystem filesystem = null; FileSystem filesystem = null;
try try
{ {
filesystem = new FileSystem( "hdd", rootMount ); filesystem = new FileSystem( "hdd", getRootMount() );
IMount romMount = getRomMount(); IMount romMount = getRomMount();
if( romMount == null ) if( romMount == null )

View File

@@ -0,0 +1,59 @@
/*
* 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.computer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* A side on a computer. Unlike {@link net.minecraft.util.EnumFacing}, this is relative to the direction the computer is
* facing..
*/
public enum ComputerSide
{
BOTTOM( "bottom" ),
TOP( "top" ),
BACK( "back" ),
FRONT( "front" ),
RIGHT( "right" ),
LEFT( "left" );
public static final String[] NAMES = new String[] { "bottom", "top", "back", "front", "right", "left" };
public static final int COUNT = 6;
private static final ComputerSide[] VALUES = values();
private final String name;
ComputerSide( String name )
{
this.name = name;
}
@Nonnull
public static ComputerSide valueOf( int side )
{
return VALUES[side];
}
@Nullable
public static ComputerSide valueOfInsensitive( @Nonnull String name )
{
for( ComputerSide side : VALUES )
{
if( side.name.equalsIgnoreCase( name ) ) return side;
}
return null;
}
public String getName()
{
return name;
}
}

View File

@@ -26,12 +26,12 @@ import javax.annotation.Nullable;
*/ */
public class ComputerSystem extends ComputerAccess implements IComputerSystem public class ComputerSystem extends ComputerAccess implements IComputerSystem
{ {
private final IAPIEnvironment m_environment; private final IAPIEnvironment environment;
ComputerSystem( IAPIEnvironment environment ) ComputerSystem( IAPIEnvironment environment )
{ {
super( environment ); super( environment );
this.m_environment = environment; this.environment = environment;
} }
@Nonnull @Nonnull
@@ -45,7 +45,7 @@ public class ComputerSystem extends ComputerAccess implements IComputerSystem
@Override @Override
public IFileSystem getFileSystem() public IFileSystem getFileSystem()
{ {
FileSystem fs = m_environment.getFileSystem(); FileSystem fs = environment.getFileSystem();
return fs == null ? null : fs.getMountWrapper(); return fs == null ? null : fs.getMountWrapper();
} }
@@ -53,6 +53,6 @@ public class ComputerSystem extends ComputerAccess implements IComputerSystem
@Override @Override
public String getLabel() public String getLabel()
{ {
return m_environment.getLabel(); return environment.getLabel();
} }
} }

View File

@@ -133,7 +133,6 @@ public final class ComputerThread
synchronized( threadLock ) synchronized( threadLock )
{ {
running = true; running = true;
if( monitor == null || !monitor.isAlive() ) (monitor = monitorFactory.newThread( new Monitor() )).start();
if( runners == null ) if( runners == null )
{ {
@@ -158,6 +157,8 @@ public final class ComputerThread
runnerFactory.newThread( runners[i] = new TaskRunner() ).start(); 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]; TaskRunner runner = currentRunners[i];
// If we've no runner, skip. // 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 // If the runner has no work, skip
ComputerExecutor executor = runner.currentExecutor.get(); ComputerExecutor executor = runner.currentExecutor.get();
@@ -492,7 +502,7 @@ public final class ComputerThread
{ {
executor.work(); executor.work();
} }
catch( Exception e ) catch( Exception | LinkageError | VirtualMachineError e )
{ {
ComputerCraft.log.error( "Error running task on computer #" + executor.getComputer().getID(), e ); ComputerCraft.log.error( "Error running task on computer #" + executor.getComputer().getID(), e );
} }

View File

@@ -41,17 +41,17 @@ public final class Environment implements IAPIEnvironment
private final Computer computer; private final Computer computer;
private boolean internalOutputChanged = false; private boolean internalOutputChanged = false;
private final int[] internalOutput = new int[SIDE_COUNT]; private final int[] internalOutput = new int[ComputerSide.COUNT];
private final int[] internalBundledOutput = new int[SIDE_COUNT]; private final int[] internalBundledOutput = new int[ComputerSide.COUNT];
private final int[] externalOutput = new int[SIDE_COUNT]; private final int[] externalOutput = new int[ComputerSide.COUNT];
private final int[] externalBundledOutput = new int[SIDE_COUNT]; private final int[] externalBundledOutput = new int[ComputerSide.COUNT];
private boolean inputChanged = false; private boolean inputChanged = false;
private final int[] input = new int[SIDE_COUNT]; private final int[] input = new int[ComputerSide.COUNT];
private final int[] bundledInput = new int[SIDE_COUNT]; private final int[] bundledInput = new int[ComputerSide.COUNT];
private final IPeripheral[] peripherals = new IPeripheral[SIDE_COUNT]; private final IPeripheral[] peripherals = new IPeripheral[ComputerSide.COUNT];
private IPeripheralChangeListener peripheralListener = null; private IPeripheralChangeListener peripheralListener = null;
Environment( Computer computer ) Environment( Computer computer )
@@ -111,85 +111,89 @@ public final class Environment implements IAPIEnvironment
} }
@Override @Override
public int getInput( int side ) public int getInput( ComputerSide side )
{ {
return input[side]; return input[side.ordinal()];
} }
@Override @Override
public int getBundledInput( int side ) public int getBundledInput( ComputerSide side )
{ {
return bundledInput[side]; return bundledInput[side.ordinal()];
} }
@Override @Override
public void setOutput( int side, int output ) public void setOutput( ComputerSide side, int output )
{ {
int index = side.ordinal();
synchronized( internalOutput ) synchronized( internalOutput )
{ {
if( internalOutput[side] != output ) if( internalOutput[index] != output )
{ {
internalOutput[side] = output; internalOutput[index] = output;
internalOutputChanged = true; internalOutputChanged = true;
} }
} }
} }
@Override @Override
public int getOutput( int side ) public int getOutput( ComputerSide side )
{ {
synchronized( internalOutput ) synchronized( internalOutput )
{ {
return computer.isOn() ? internalOutput[side] : 0; return computer.isOn() ? internalOutput[side.ordinal()] : 0;
} }
} }
@Override @Override
public void setBundledOutput( int side, int output ) public void setBundledOutput( ComputerSide side, int output )
{ {
int index = side.ordinal();
synchronized( internalOutput ) synchronized( internalOutput )
{ {
if( internalBundledOutput[side] != output ) if( internalBundledOutput[index] != output )
{ {
internalBundledOutput[side] = output; internalBundledOutput[index] = output;
internalOutputChanged = true; internalOutputChanged = true;
} }
} }
} }
@Override @Override
public int getBundledOutput( int side ) public int getBundledOutput( ComputerSide side )
{ {
synchronized( internalOutput ) synchronized( internalOutput )
{ {
return computer.isOn() ? internalBundledOutput[side] : 0; return computer.isOn() ? internalBundledOutput[side.ordinal()] : 0;
} }
} }
public int getExternalRedstoneOutput( int side ) public int getExternalRedstoneOutput( ComputerSide side )
{ {
return computer.isOn() ? externalOutput[side] : 0; return computer.isOn() ? externalOutput[side.ordinal()] : 0;
} }
public int getExternalBundledRedstoneOutput( int side ) public int getExternalBundledRedstoneOutput( ComputerSide side )
{ {
return computer.isOn() ? externalBundledOutput[side] : 0; return computer.isOn() ? externalBundledOutput[side.ordinal()] : 0;
} }
public void setRedstoneInput( int side, int level ) public void setRedstoneInput( ComputerSide side, int level )
{ {
if( input[side] != level ) int index = side.ordinal();
if( input[index] != level )
{ {
input[side] = level; input[index] = level;
inputChanged = true; inputChanged = true;
} }
} }
public void setBundledRedstoneInput( int side, int combination ) public void setBundledRedstoneInput( ComputerSide side, int combination )
{ {
if( bundledInput[side] != combination ) int index = side.ordinal();
if( bundledInput[index] != combination )
{ {
bundledInput[side] = combination; bundledInput[index] = combination;
inputChanged = true; inputChanged = true;
} }
} }
@@ -222,7 +226,7 @@ public final class Environment implements IAPIEnvironment
boolean changed = false; boolean changed = false;
for( int i = 0; i < SIDE_COUNT; i++ ) for( int i = 0; i < ComputerSide.COUNT; i++ )
{ {
if( externalOutput[i] != internalOutput[i] ) if( externalOutput[i] != internalOutput[i] )
{ {
@@ -255,24 +259,25 @@ public final class Environment implements IAPIEnvironment
} }
@Override @Override
public IPeripheral getPeripheral( int side ) public IPeripheral getPeripheral( ComputerSide side )
{ {
synchronized( peripherals ) synchronized( peripherals )
{ {
return peripherals[side]; return peripherals[side.ordinal()];
} }
} }
public void setPeripheral( int side, IPeripheral peripheral ) public void setPeripheral( ComputerSide side, IPeripheral peripheral )
{ {
synchronized( peripherals ) synchronized( peripherals )
{ {
IPeripheral existing = peripherals[side]; int index = side.ordinal();
IPeripheral existing = peripherals[index];
if( (existing == null && peripheral != null) || if( (existing == null && peripheral != null) ||
(existing != null && peripheral == null) || (existing != null && peripheral == null) ||
(existing != null && !existing.equals( peripheral )) ) (existing != null && !existing.equals( peripheral )) )
{ {
peripherals[side] = peripheral; peripherals[index] = peripheral;
if( peripheralListener != null ) peripheralListener.onPeripheralChanged( side, peripheral ); if( peripheralListener != null ) peripheralListener.onPeripheralChanged( side, peripheral );
} }
} }

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 // 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. // and we'll skip a whole tick - bringing the average back down again.
currentTick++; currentTick++;
budget += Math.min( budget + ComputerCraft.maxMainGlobalTime, ComputerCraft.maxMainGlobalTime ); budget = Math.min( budget + ComputerCraft.maxMainGlobalTime, ComputerCraft.maxMainGlobalTime );
canExecute = budget > 0; canExecute = budget > 0;
// Cool down any warm computers. // Cool down any warm computers.

View File

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

View File

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

View File

@@ -8,6 +8,7 @@ package dan200.computercraft.core.filesystem;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IFileSystem; import dan200.computercraft.api.filesystem.IFileSystem;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount; import dan200.computercraft.api.filesystem.IWritableMount;
@@ -45,7 +46,7 @@ public class FileSystem
m_writableMount = null; m_writableMount = null;
} }
public MountWrapper( String label, String location, IWritableMount mount ) MountWrapper( String label, String location, IWritableMount mount )
{ {
this( label, location, (IMount) mount ); this( label, location, (IMount) mount );
m_writableMount = mount; m_writableMount = mount;
@@ -107,7 +108,7 @@ public class FileSystem
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
@@ -122,12 +123,12 @@ public class FileSystem
} }
else else
{ {
throw new FileSystemException( "/" + path + ": Not a directory" ); throw localExceptionOf( path, "Not a directory" );
} }
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
@@ -149,12 +150,12 @@ public class FileSystem
} }
else else
{ {
throw new FileSystemException( "/" + path + ": No such file" ); throw localExceptionOf( path, "No such file" );
} }
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
@@ -169,12 +170,12 @@ public class FileSystem
} }
else else
{ {
throw new FileSystemException( "/" + path + ": No such file" ); throw localExceptionOf( path, "No such file" );
} }
} }
catch( IOException e ) 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 public void makeDirectory( String path ) throws FileSystemException
{ {
if( m_writableMount == null ) if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
{
throw new FileSystemException( "/" + path + ": Access denied" ); path = toLocal( path );
}
try try
{ {
path = toLocal( path );
if( m_mount.exists( path ) ) if( m_mount.exists( path ) )
{ {
if( !m_mount.isDirectory( path ) ) if( !m_mount.isDirectory( path ) ) throw localExceptionOf( path, "File exists" );
{
throw new FileSystemException( "/" + path + ": File exists" );
}
} }
else else
{ {
@@ -203,16 +199,14 @@ public class FileSystem
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
public void delete( String path ) throws FileSystemException public void delete( String path ) throws FileSystemException
{ {
if( m_writableMount == null ) if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
{
throw new FileSystemException( "/" + path + ": Access denied" );
}
try try
{ {
path = toLocal( path ); path = toLocal( path );
@@ -227,22 +221,20 @@ public class FileSystem
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
public WritableByteChannel openForWrite( String path ) throws FileSystemException public WritableByteChannel openForWrite( String path ) throws FileSystemException
{ {
if( m_writableMount == null ) if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
{
throw new FileSystemException( "/" + path + ": Access denied" ); path = toLocal( path );
}
try try
{ {
path = toLocal( path );
if( m_mount.exists( path ) && m_mount.isDirectory( 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 else
{ {
@@ -263,19 +255,17 @@ public class FileSystem
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
public WritableByteChannel openForAppend( String path ) throws FileSystemException public WritableByteChannel openForAppend( String path ) throws FileSystemException
{ {
if( m_writableMount == null ) if( m_writableMount == null ) throw exceptionOf( path, "Access denied" );
{
throw new FileSystemException( "/" + path + ": Access denied" ); path = toLocal( path );
}
try try
{ {
path = toLocal( path );
if( !m_mount.exists( path ) ) if( !m_mount.exists( path ) )
{ {
if( !path.isEmpty() ) if( !path.isEmpty() )
@@ -290,7 +280,7 @@ public class FileSystem
} }
else if( m_mount.isDirectory( path ) ) else if( m_mount.isDirectory( path ) )
{ {
throw new FileSystemException( "/" + path + ": Cannot write to directory" ); throw localExceptionOf( path, "Cannot write to directory" );
} }
else else
{ {
@@ -303,16 +293,36 @@ public class FileSystem
} }
catch( IOException e ) catch( IOException e )
{ {
throw new FileSystemException( e.getMessage() ); throw localExceptionOf( e );
} }
} }
// private members
private String toLocal( String path ) private String toLocal( String path )
{ {
return FileSystem.toLocal( path, m_location ); 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 ); private final FileSystemWrapperMount m_wrapper = new FileSystemWrapperMount( this );
@@ -769,7 +779,7 @@ public class FileSystem
// Clean the path or illegal characters. // Clean the path or illegal characters.
final char[] specialChars = new char[] { final char[] specialChars = new char[] {
'"', ':', '<', '>', '?', '|' // Sorted by ascii value (important) '"', ':', '<', '>', '?', '|', // Sorted by ascii value (important)
}; };
StringBuilder cleanName = new StringBuilder(); 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.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.core.apis.handles.ArrayByteChannel; import dan200.computercraft.core.apis.handles.ArrayByteChannel;
import dan200.computercraft.shared.util.IoUtil; import dan200.computercraft.shared.util.IoUtil;
@@ -69,7 +70,7 @@ public class JarMount implements IMount
// Cleanup any old mounts. It's unlikely that there will be any, but it's best to be safe. // Cleanup any old mounts. It's unlikely that there will be any, but it's best to be safe.
cleanup(); cleanup();
if( !jarFile.exists() || jarFile.isDirectory() ) throw new FileNotFoundException(); if( !jarFile.exists() || jarFile.isDirectory() ) throw new FileNotFoundException( "Cannot find " + jarFile );
// Open the zip file // Open the zip file
try try
@@ -85,14 +86,14 @@ public class JarMount implements IMount
if( zip.getEntry( subPath ) == null ) if( zip.getEntry( subPath ) == null )
{ {
zip.close(); zip.close();
throw new IOException( "Zip does not contain path" ); throw new FileNotFoundException( "Zip does not contain path" );
} }
// We now create a weak reference to this mount. This is automatically added to the appropriate queue. // We now create a weak reference to this mount. This is automatically added to the appropriate queue.
new MountReference( this ); new MountReference( this );
// Read in all the entries // Read in all the entries
root = new FileEntry( "" ); root = new FileEntry();
Enumeration<? extends ZipEntry> zipEntries = zip.entries(); Enumeration<? extends ZipEntry> zipEntries = zip.entries();
while( zipEntries.hasMoreElements() ) while( zipEntries.hasMoreElements() )
{ {
@@ -139,7 +140,7 @@ public class JarMount implements IMount
FileEntry nextEntry = lastEntry.children.get( part ); FileEntry nextEntry = lastEntry.children.get( part );
if( nextEntry == null || !nextEntry.isDirectory() ) if( nextEntry == null || !nextEntry.isDirectory() )
{ {
lastEntry.children.put( part, nextEntry = new FileEntry( part ) ); lastEntry.children.put( part, nextEntry = new FileEntry() );
} }
lastEntry = nextEntry; lastEntry = nextEntry;
@@ -166,7 +167,7 @@ public class JarMount implements IMount
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
{ {
FileEntry file = get( path ); 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 ); file.list( contents );
} }
@@ -176,7 +177,7 @@ public class JarMount implements IMount
{ {
FileEntry file = get( path ); FileEntry file = get( path );
if( file != null ) return file.size; if( file != null ) return file.size;
throw new IOException( "/" + path + ": No such file" ); throw new FileOperationException( path, "No such file" );
} }
@Nonnull @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 private static class FileEntry
{ {
final String name;
String path; String path;
long size; long size;
Map<String, FileEntry> children; Map<String, FileEntry> children;
FileEntry( String name )
{
this.name = name;
}
void setup( ZipEntry entry ) void setup( ZipEntry entry )
{ {
path = entry.getName(); path = entry.getName();

View File

@@ -14,7 +14,6 @@ import dan200.computercraft.core.apis.handles.ArrayByteChannel;
import net.minecraft.resources.IReloadableResourceManager; import net.minecraft.resources.IReloadableResourceManager;
import net.minecraft.resources.IResource; import net.minecraft.resources.IResource;
import net.minecraft.resources.IResourceManager; import net.minecraft.resources.IResourceManager;
import net.minecraft.resources.IResourceManagerReloadListener;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraftforge.resource.IResourceType; import net.minecraftforge.resource.IResourceType;
import net.minecraftforge.resource.ISelectiveResourceReloadListener; import net.minecraftforge.resource.ISelectiveResourceReloadListener;
@@ -171,7 +170,7 @@ public class ResourceMount implements IMount
{ {
total += read; total += read;
read = s.read( TEMP_BUFFER ); read = s.read( TEMP_BUFFER );
} while( read > 0 ); } while ( read > 0 );
return file.size = total; return file.size = total;
} }
@@ -241,7 +240,7 @@ public class ResourceMount implements IMount
} }
/** /**
* A {@link IResourceManagerReloadListener} which reloads any associated mounts. * A {@link ISelectiveResourceReloadListener} which reloads any associated mounts.
* *
* While people should really be keeping a permanent reference to this, some people construct it every * While people should really be keeping a permanent reference to this, some people construct it every
* method call, so let's make this as small as possible. * method call, so let's make this as small as possible.

View File

@@ -242,7 +242,7 @@ public class CobaltLuaMachine implements ILuaMachine
} }
catch( InterruptedException e ) catch( InterruptedException e )
{ {
throw new OrphanedThread(); throw new InterruptedError( e );
} }
catch( LuaException e ) catch( LuaException e )
{ {
@@ -550,7 +550,7 @@ public class CobaltLuaMachine implements ILuaMachine
{ {
if( ComputerCraft.logPeripheralErrors ) ComputerCraft.log.error( "Error running task", t ); if( ComputerCraft.logPeripheralErrors ) ComputerCraft.log.error( "Error running task", t );
m_computer.queueEvent( "task_complete", new Object[] { 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 class TextBuffer
{ {
public char[] m_text; private final char[] m_text;
public TextBuffer( char c, int length ) public TextBuffer( char c, int length )
{ {

View File

@@ -6,7 +6,6 @@
package dan200.computercraft.shared; package dan200.computercraft.shared;
import com.google.common.base.Preconditions;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.redstone.IBundledRedstoneProvider; import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider; import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
@@ -16,6 +15,7 @@ import net.minecraft.world.World;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set; import java.util.Set;
public final class BundledRedstone public final class BundledRedstone
@@ -26,7 +26,7 @@ public final class BundledRedstone
public static synchronized void register( @Nonnull IBundledRedstoneProvider provider ) public static synchronized void register( @Nonnull IBundledRedstoneProvider provider )
{ {
Preconditions.checkNotNull( provider, "provider cannot be null" ); Objects.requireNonNull( provider, "provider cannot be null" );
providers.add( provider ); providers.add( provider );
} }

View File

@@ -44,8 +44,8 @@ public final class Config
private static ConfigValue<Boolean> logComputerErrors; private static ConfigValue<Boolean> logComputerErrors;
private static ConfigValue<Integer> computerThreads; private static ConfigValue<Integer> computerThreads;
private static ConfigValue<Long> maxMainGlobalTime; private static ConfigValue<Integer> maxMainGlobalTime;
private static ConfigValue<Long> maxMainComputerTime; private static ConfigValue<Integer> maxMainComputerTime;
private static ConfigValue<Boolean> httpEnabled; private static ConfigValue<Boolean> httpEnabled;
private static ConfigValue<Boolean> httpWebsocketEnabled; private static ConfigValue<Boolean> httpWebsocketEnabled;
@@ -54,8 +54,8 @@ public final class Config
private static ConfigValue<Integer> httpTimeout; private static ConfigValue<Integer> httpTimeout;
private static ConfigValue<Integer> httpMaxRequests; private static ConfigValue<Integer> httpMaxRequests;
private static ConfigValue<Long> httpMaxDownload; private static ConfigValue<Integer> httpMaxDownload;
private static ConfigValue<Long> httpMaxUpload; private static ConfigValue<Integer> httpMaxUpload;
private static ConfigValue<Integer> httpMaxWebsockets; private static ConfigValue<Integer> httpMaxWebsockets;
private static ConfigValue<Integer> httpMaxWebsocketMessage; private static ConfigValue<Integer> httpMaxWebsocketMessage;
@@ -134,13 +134,13 @@ public final class Config
.comment( "The maximum time that can be spent executing tasks in a single tick, in milliseconds.\n" + .comment( "The maximum time that can be spent executing tasks in a single tick, in milliseconds.\n" +
"Note, we will quite possibly go over this limit, as there's no way to tell how long a will take " + "Note, we will quite possibly go over this limit, as there's no way to tell how long a will take " +
"- this aims to be the upper bound of the average time." ) "- this aims to be the upper bound of the average time." )
.defineInRange( "max_main_global_time", TimeUnit.NANOSECONDS.toMillis( ComputerCraft.maxMainGlobalTime ), 1, Long.MAX_VALUE ); .defineInRange( "max_main_global_time", (int) TimeUnit.NANOSECONDS.toMillis( ComputerCraft.maxMainGlobalTime ), 1, Integer.MAX_VALUE );
maxMainComputerTime = builder maxMainComputerTime = builder
.comment( "The ideal maximum time a computer can execute for in a tick, in milliseconds.\n" + .comment( "The ideal maximum time a computer can execute for in a tick, in milliseconds.\n" +
"Note, we will quite possibly go over this limit, as there's no way to tell how long a will take " + "Note, we will quite possibly go over this limit, as there's no way to tell how long a will take " +
"- this aims to be the upper bound of the average time." ) "- this aims to be the upper bound of the average time." )
.defineInRange( "max_main_computer_time", TimeUnit.NANOSECONDS.toMillis( ComputerCraft.maxMainComputerTime ), 1, Long.MAX_VALUE ); .defineInRange( "max_main_computer_time", (int) TimeUnit.NANOSECONDS.toMillis( ComputerCraft.maxMainComputerTime ), 1, Integer.MAX_VALUE );
builder.pop(); builder.pop();
} }
@@ -180,11 +180,11 @@ public final class Config
httpMaxDownload = builder httpMaxDownload = builder
.comment( "The maximum size (in bytes) that a computer can download in a single request. Note that responses may receive more data than allowed, but this data will not be returned to the client." ) .comment( "The maximum size (in bytes) that a computer can download in a single request. Note that responses may receive more data than allowed, but this data will not be returned to the client." )
.defineInRange( "max_download", ComputerCraft.httpMaxDownload, 0, Long.MAX_VALUE ); .defineInRange( "max_download", (int) ComputerCraft.httpMaxDownload, 0, Integer.MAX_VALUE );
httpMaxUpload = builder httpMaxUpload = builder
.comment( "The maximum size (in bytes) that a computer can upload in a single request. This includes headers and POST text." ) .comment( "The maximum size (in bytes) that a computer can upload in a single request. This includes headers and POST text." )
.defineInRange( "max_upload", ComputerCraft.httpMaxUpload, 0, Long.MAX_VALUE ); .defineInRange( "max_upload", (int) ComputerCraft.httpMaxUpload, 0, Integer.MAX_VALUE );
httpMaxWebsockets = builder httpMaxWebsockets = builder
.comment( "The number of websockets a computer can have open at one time. Set to 0 for unlimited." ) .comment( "The number of websockets a computer can have open at one time. Set to 0 for unlimited." )

View File

@@ -11,8 +11,8 @@ import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.core.tracking.ComputerTracker; import dan200.computercraft.core.tracking.ComputerTracker;
import dan200.computercraft.core.tracking.Tracking; import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.core.tracking.TrackingContext; import dan200.computercraft.core.tracking.TrackingContext;
@@ -118,12 +118,12 @@ public final class CommandComputerCraft
table.row( header( "Position" ), linkPosition( context.getSource(), computer ) ); table.row( header( "Position" ), linkPosition( context.getSource(), computer ) );
table.row( header( "Family" ), text( computer.getFamily().toString() ) ); table.row( header( "Family" ), text( computer.getFamily().toString() ) );
for( int i = 0; i < 6; i++ ) for( ComputerSide side : ComputerSide.values() )
{ {
IPeripheral peripheral = computer.getPeripheral( i ); IPeripheral peripheral = computer.getPeripheral( side );
if( peripheral != null ) if( peripheral != null )
{ {
table.row( header( "Peripheral " + IAPIEnvironment.SIDE_NAMES[i] ), text( peripheral.getType() ) ); table.row( header( "Peripheral " + side.getName() ), text( peripheral.getType() ) );
} }
} }

View File

@@ -16,7 +16,7 @@ public final class Exceptions
public static final DynamicCommandExceptionType COMPUTER_ARG_NONE = translated1( "argument.computercraft.computer.no_matching" ); public static final DynamicCommandExceptionType COMPUTER_ARG_NONE = translated1( "argument.computercraft.computer.no_matching" );
public static final Dynamic2CommandExceptionType COMPUTER_ARG_MANY = translated2( "argument.computercraft.computer.many_matching" ); public static final Dynamic2CommandExceptionType COMPUTER_ARG_MANY = translated2( "argument.computercraft.computer.many_matching" );
public static final DynamicCommandExceptionType TRACKING_FIELD_ARG_NONE = translated1( "argument.computercraft.tacking_field.no_field" ); public static final DynamicCommandExceptionType TRACKING_FIELD_ARG_NONE = translated1( "argument.computercraft.tracking_field.no_field" );
static final SimpleCommandExceptionType NOT_TRACKING_EXCEPTION = translated( "commands.computercraft.track.stop.not_enabled" ); static final SimpleCommandExceptionType NOT_TRACKING_EXCEPTION = translated( "commands.computercraft.track.stop.not_enabled" );
static final SimpleCommandExceptionType NO_TIMINGS_EXCEPTION = translated( "commands.computercraft.track.dump.no_timings" ); static final SimpleCommandExceptionType NO_TIMINGS_EXCEPTION = translated( "commands.computercraft.track.dump.no_timings" );

View File

@@ -7,7 +7,6 @@
package dan200.computercraft.shared.common; package dan200.computercraft.shared.common;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
@@ -23,7 +22,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Random; import java.util.Random;
public abstract class BlockGeneric extends Block implements ITileEntityProvider public abstract class BlockGeneric extends Block
{ {
private final TileEntityType<? extends TileGeneric> type; private final TileEntityType<? extends TileGeneric> type;
@@ -55,7 +54,6 @@ public abstract class BlockGeneric extends Block implements ITileEntityProvider
@Override @Override
@Deprecated @Deprecated
@SuppressWarnings( "deprecation" )
public final void neighborChanged( IBlockState state, World world, BlockPos pos, Block neighbourBlock, BlockPos neighbourPos ) public final void neighborChanged( IBlockState state, World world, BlockPos pos, Block neighbourBlock, BlockPos neighbourPos )
{ {
TileEntity tile = world.getTileEntity( pos ); TileEntity tile = world.getTileEntity( pos );
@@ -77,9 +75,15 @@ public abstract class BlockGeneric extends Block implements ITileEntityProvider
if( te instanceof TileGeneric ) ((TileGeneric) te).blockTick(); if( te instanceof TileGeneric ) ((TileGeneric) te).blockTick();
} }
@Override
public boolean hasTileEntity( IBlockState state )
{
return true;
}
@Nullable @Nullable
@Override @Override
public TileEntity createNewTileEntity( @Nonnull IBlockReader world ) public TileEntity createTileEntity( @Nonnull IBlockState state, @Nonnull IBlockReader world )
{ {
return type.create(); return type.create();
} }

View File

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

View File

@@ -6,6 +6,7 @@
package dan200.computercraft.shared.computer.blocks; package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.shared.common.BlockGeneric; import dan200.computercraft.shared.common.BlockGeneric;
import dan200.computercraft.shared.common.IBundledRedstoneBlock; import dan200.computercraft.shared.common.IBundledRedstoneBlock;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
@@ -65,9 +66,8 @@ public abstract class BlockComputerBase<T extends TileComputerBase> extends Bloc
ServerComputer computer = computerEntity.getServerComputer(); ServerComputer computer = computerEntity.getServerComputer();
if( computer == null ) return 0; if( computer == null ) return 0;
EnumFacing localSide = computerEntity.remapToLocalSide( incomingSide.getOpposite() ); ComputerSide localSide = computerEntity.remapToLocalSide( incomingSide.getOpposite() );
return computerEntity.isRedstoneBlockedOnSide( localSide ) ? 0 : return computer.getRedstoneOutput( localSide );
computer.getRedstoneOutput( localSide.getIndex() );
} }
@Nonnull @Nonnull
@@ -88,11 +88,7 @@ public abstract class BlockComputerBase<T extends TileComputerBase> extends Bloc
@Override @Override
public boolean getBundledRedstoneConnectivity( World world, BlockPos pos, EnumFacing side ) public boolean getBundledRedstoneConnectivity( World world, BlockPos pos, EnumFacing side )
{ {
TileEntity entity = world.getTileEntity( pos ); return true;
if( !(entity instanceof TileComputerBase) ) return false;
TileComputerBase computerEntity = (TileComputerBase) entity;
return !computerEntity.isRedstoneBlockedOnSide( computerEntity.remapToLocalSide( side ) );
} }
@Override @Override
@@ -105,9 +101,8 @@ public abstract class BlockComputerBase<T extends TileComputerBase> extends Bloc
ServerComputer computer = computerEntity.getServerComputer(); ServerComputer computer = computerEntity.getServerComputer();
if( computer == null ) return 0; if( computer == null ) return 0;
EnumFacing localSide = computerEntity.remapToLocalSide( side ); ComputerSide localSide = computerEntity.remapToLocalSide( side );
return computerEntity.isRedstoneBlockedOnSide( localSide ) ? 0 : return computer.getBundledRedstoneOutput( localSide );
computer.getBundledRedstoneOutput( localSide.getIndex() );
} }
@Nonnull @Nonnull

View File

@@ -7,6 +7,7 @@
package dan200.computercraft.shared.computer.blocks; package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ComputerState; import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.core.ServerComputer;
@@ -95,8 +96,12 @@ public class TileComputer extends TileComputerBase
} }
@Override @Override
protected EnumFacing remapLocalSide( EnumFacing localSide ) protected ComputerSide remapLocalSide( ComputerSide localSide )
{ {
return localSide.getAxis() == EnumFacing.Axis.X ? localSide.getOpposite() : localSide; // For legacy reasons, computers invert the meaning of "left" and "right". A computer's front is facing
// towards you, but a turtle's front is facing the other way.
if( localSide == ComputerSide.RIGHT ) return ComputerSide.LEFT;
if( localSide == ComputerSide.LEFT ) return ComputerSide.RIGHT;
return localSide;
} }
} }

View File

@@ -9,6 +9,7 @@ package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralTile; import dan200.computercraft.api.peripheral.IPeripheralTile;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.shared.BundledRedstone; import dan200.computercraft.shared.BundledRedstone;
import dan200.computercraft.shared.Peripherals; import dan200.computercraft.shared.Peripherals;
import dan200.computercraft.shared.common.TileGeneric; import dan200.computercraft.shared.common.TileGeneric;
@@ -19,7 +20,10 @@ import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.util.DirectionUtil; import dan200.computercraft.shared.util.DirectionUtil;
import dan200.computercraft.shared.util.RedstoneUtil; import dan200.computercraft.shared.util.RedstoneUtil;
import joptsimple.internal.Strings; import joptsimple.internal.Strings;
import net.minecraft.block.BlockRedstoneWire;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items; import net.minecraft.init.Items;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
@@ -31,6 +35,7 @@ import net.minecraft.util.ITickable;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextComponentString;
import net.minecraft.world.World;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -198,24 +203,19 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
m_on = m_startOn = nbt.getBoolean( NBT_ON ); m_on = m_startOn = nbt.getBoolean( NBT_ON );
} }
protected boolean isPeripheralBlockedOnSide( EnumFacing localSide ) protected boolean isPeripheralBlockedOnSide( ComputerSide localSide )
{
return false;
}
protected boolean isRedstoneBlockedOnSide( EnumFacing localSide )
{ {
return false; return false;
} }
protected abstract EnumFacing getDirection(); protected abstract EnumFacing getDirection();
protected EnumFacing remapToLocalSide( EnumFacing globalSide ) protected ComputerSide remapToLocalSide( EnumFacing globalSide )
{ {
return remapLocalSide( DirectionUtil.toLocal( getDirection(), globalSide ) ); return remapLocalSide( DirectionUtil.toLocal( getDirection(), globalSide ) );
} }
protected EnumFacing remapLocalSide( EnumFacing localSide ) protected ComputerSide remapLocalSide( ComputerSide localSide )
{ {
return localSide; return localSide;
} }
@@ -223,18 +223,36 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
private void updateSideInput( ServerComputer computer, EnumFacing dir, BlockPos offset ) private void updateSideInput( ServerComputer computer, EnumFacing dir, BlockPos offset )
{ {
EnumFacing offsetSide = dir.getOpposite(); EnumFacing offsetSide = dir.getOpposite();
EnumFacing localDir = remapToLocalSide( dir ); ComputerSide localDir = remapToLocalSide( dir );
if( !isRedstoneBlockedOnSide( localDir ) )
{ computer.setRedstoneInput( localDir, getRedstoneInput( world, offset, dir ) );
computer.setRedstoneInput( localDir.getIndex(), getWorld().getRedstonePower( offset, dir ) ); computer.setBundledRedstoneInput( localDir, BundledRedstone.getOutput( getWorld(), offset, offsetSide ) );
computer.setBundledRedstoneInput( localDir.getIndex(), BundledRedstone.getOutput( getWorld(), offset, offsetSide ) );
}
if( !isPeripheralBlockedOnSide( localDir ) ) if( !isPeripheralBlockedOnSide( localDir ) )
{ {
computer.setPeripheral( localDir.getIndex(), Peripherals.getPeripheral( getWorld(), offset, offsetSide ) ); 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.get( BlockRedstoneWire.POWER ) )
: power;
}
public void updateInput() public void updateInput()
{ {
if( getWorld() == null || getWorld().isRemote ) return; if( getWorld() == null || getWorld().isRemote ) return;
@@ -257,7 +275,6 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
ServerComputer computer = getServerComputer(); ServerComputer computer = getServerComputer();
if( computer == null ) return; if( computer == null ) return;
// Find the appropriate side and update.
BlockPos pos = computer.getPosition(); BlockPos pos = computer.getPosition();
for( EnumFacing dir : DirectionUtil.FACINGS ) for( EnumFacing dir : DirectionUtil.FACINGS )
{ {
@@ -268,6 +285,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
break; break;
} }
} }
// If the position is not any adjacent one, update all inputs.
updateInput();
} }
public void updateOutput() public void updateOutput()

View File

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

View File

@@ -14,6 +14,7 @@ import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.apis.IAPIEnvironment; import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.core.computer.IComputerEnvironment; import dan200.computercraft.core.computer.IComputerEnvironment;
import dan200.computercraft.shared.common.ServerTerminal; import dan200.computercraft.shared.common.ServerTerminal;
import dan200.computercraft.shared.network.NetworkHandler; import dan200.computercraft.shared.network.NetworkHandler;
@@ -172,10 +173,14 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
// Send terminal state to clients who are currently interacting with the computer. // Send terminal state to clients who are currently interacting with the computer.
MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
NetworkMessage packet = createTerminalPacket(); NetworkMessage packet = null;
for( EntityPlayer player : server.getPlayerList().getPlayers() ) for( EntityPlayer player : server.getPlayerList().getPlayers() )
{ {
if( isInteracting( player ) ) NetworkHandler.sendToPlayer( player, packet ); if( isInteracting( player ) )
{
if( packet == null ) packet = createTerminalPacket();
NetworkHandler.sendToPlayer( player, packet );
}
} }
} }
} }
@@ -261,22 +266,22 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
m_computer.queueEvent( event, arguments ); m_computer.queueEvent( event, arguments );
} }
public int getRedstoneOutput( int side ) public int getRedstoneOutput( ComputerSide side )
{ {
return m_computer.getEnvironment().getExternalRedstoneOutput( side ); return m_computer.getEnvironment().getExternalRedstoneOutput( side );
} }
public void setRedstoneInput( int side, int level ) public void setRedstoneInput( ComputerSide side, int level )
{ {
m_computer.getEnvironment().setRedstoneInput( side, level ); m_computer.getEnvironment().setRedstoneInput( side, level );
} }
public int getBundledRedstoneOutput( int side ) public int getBundledRedstoneOutput( ComputerSide side )
{ {
return m_computer.getEnvironment().getExternalBundledRedstoneOutput( side ); return m_computer.getEnvironment().getExternalBundledRedstoneOutput( side );
} }
public void setBundledRedstoneInput( int side, int combination ) public void setBundledRedstoneInput( ComputerSide side, int combination )
{ {
m_computer.getEnvironment().setBundledRedstoneInput( side, combination ); m_computer.getEnvironment().setBundledRedstoneInput( side, combination );
} }
@@ -286,12 +291,12 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
m_computer.addApi( api ); m_computer.addApi( api );
} }
public void setPeripheral( int side, IPeripheral peripheral ) public void setPeripheral( ComputerSide side, IPeripheral peripheral )
{ {
m_computer.getEnvironment().setPeripheral( side, peripheral ); m_computer.getEnvironment().setPeripheral( side, peripheral );
} }
public IPeripheral getPeripheral( int side ) public IPeripheral getPeripheral( ComputerSide side )
{ {
return m_computer.getEnvironment().getPeripheral( side ); return m_computer.getEnvironment().getPeripheral( side );
} }

View File

@@ -1,9 +1,8 @@
/* /*
* This file is part of ComputerCraft - http://www.computercraft.info * 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 * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.shared.integration.charset; package dan200.computercraft.shared.integration.charset;
import dan200.computercraft.shared.common.TileGeneric; import dan200.computercraft.shared.common.TileGeneric;

View File

@@ -1,9 +1,8 @@
/* /*
* This file is part of ComputerCraft - http://www.computercraft.info * 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 * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.shared.integration.charset; package dan200.computercraft.shared.integration.charset;
import dan200.computercraft.api.redstone.IBundledRedstoneProvider; import dan200.computercraft.api.redstone.IBundledRedstoneProvider;

View File

@@ -1,9 +1,8 @@
/* /*
* This file is part of ComputerCraft - http://www.computercraft.info * 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 * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.shared.integration.charset; package dan200.computercraft.shared.integration.charset;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;

View File

@@ -10,8 +10,10 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralTile; import dan200.computercraft.api.peripheral.IPeripheralTile;
import dan200.computercraft.shared.peripheral.common.TilePeripheralBase;
import dan200.computercraft.shared.peripheral.modem.wireless.TileAdvancedModem; import dan200.computercraft.shared.peripheral.modem.wireless.TileAdvancedModem;
import dan200.computercraft.shared.peripheral.modem.wireless.TileWirelessModem; import dan200.computercraft.shared.peripheral.modem.wireless.TileWirelessModem;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import mcmultipart.api.addon.IMCMPAddon; import mcmultipart.api.addon.IMCMPAddon;
import mcmultipart.api.addon.MCMPAddon; import mcmultipart.api.addon.MCMPAddon;
import mcmultipart.api.container.IMultipartContainer; import mcmultipart.api.container.IMultipartContainer;
@@ -52,7 +54,7 @@ public class MCMPIntegration implements IMCMPAddon
public void registerParts( IMultipartRegistry registry ) public void registerParts( IMultipartRegistry registry )
{ {
// Setup all parts // Setup all parts
register( registry, ComputerCraft.Blocks.peripheral, new PartNormalModem() ); register( registry, ComputerCraft.Blocks.peripheral, new PartPeripheral() );
register( registry, ComputerCraft.Blocks.advancedModem, new PartAdvancedModem() ); register( registry, ComputerCraft.Blocks.advancedModem, new PartAdvancedModem() );
// Subscribe to capability events // Subscribe to capability events
@@ -83,8 +85,11 @@ public class MCMPIntegration implements IMCMPAddon
public static void attach( AttachCapabilitiesEvent<TileEntity> event ) public static void attach( AttachCapabilitiesEvent<TileEntity> event )
{ {
TileEntity tile = event.getObject(); TileEntity tile = event.getObject();
if( tile instanceof TileAdvancedModem || tile instanceof TileWirelessModem ) if( tile instanceof TileAdvancedModem || tile instanceof TileWirelessModem
|| tile instanceof TilePeripheralBase || tile instanceof TileMonitor )
{ {
// We need to attach to modems (obviously), but also any other tile created by BlockPeripheral. Otherwise
// IMultipart.convertToMultipartTile will error.
event.addCapability( CAPABILITY_KEY, new BasicMultipart( tile ) ); event.addCapability( CAPABILITY_KEY, new BasicMultipart( tile ) );
} }
} }
@@ -94,7 +99,10 @@ public class MCMPIntegration implements IMCMPAddon
private final TileEntity tile; private final TileEntity tile;
private IMultipartTile wrapped; private IMultipartTile wrapped;
private BasicMultipart( TileEntity tile ) {this.tile = tile;} private BasicMultipart( TileEntity tile )
{
this.tile = tile;
}
@Override @Override
public boolean hasCapability( @Nonnull Capability<?> capability, @Nullable EnumFacing facing ) public boolean hasCapability( @Nonnull Capability<?> capability, @Nullable EnumFacing facing )

View File

@@ -10,6 +10,7 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.peripheral.common.BlockPeripheral; import dan200.computercraft.shared.peripheral.common.BlockPeripheral;
import dan200.computercraft.shared.peripheral.common.BlockPeripheralVariant; import dan200.computercraft.shared.peripheral.common.BlockPeripheralVariant;
import mcmultipart.api.multipart.IMultipart; import mcmultipart.api.multipart.IMultipart;
import mcmultipart.api.slot.EnumCenterSlot;
import mcmultipart.api.slot.EnumFaceSlot; import mcmultipart.api.slot.EnumFaceSlot;
import mcmultipart.api.slot.IPartSlot; import mcmultipart.api.slot.IPartSlot;
import net.minecraft.block.Block; import net.minecraft.block.Block;
@@ -20,34 +21,41 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess; import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World; import net.minecraft.world.World;
public class PartNormalModem implements IMultipart import javax.annotation.Nonnull;
public class PartPeripheral implements IMultipart
{ {
@Override @Override
public IPartSlot getSlotForPlacement( World world, BlockPos pos, IBlockState state, EnumFacing facing, float hitX, float hitY, float hitZ, EntityLivingBase placer ) public IPartSlot getSlotForPlacement( World world, BlockPos pos, IBlockState state, EnumFacing facing, float hitX, float hitY, float hitZ, EntityLivingBase placer )
{ {
return EnumFaceSlot.fromFace( getFacing( state ) ); return getSlot( state );
} }
@Override @Override
public IPartSlot getSlotFromWorld( IBlockAccess world, BlockPos pos, IBlockState state ) public IPartSlot getSlotFromWorld( IBlockAccess world, BlockPos pos, IBlockState state )
{ {
return EnumFaceSlot.fromFace( getFacing( state ) ); return getSlot( state );
} }
private EnumFacing getFacing( IBlockState state ) @Nonnull
private static IPartSlot getSlot( IBlockState state )
{ {
BlockPeripheralVariant type = state.getValue( BlockPeripheral.VARIANT ); BlockPeripheralVariant type = state.getValue( BlockPeripheral.VARIANT );
if( type == BlockPeripheralVariant.WirelessModemUpOn || type == BlockPeripheralVariant.WirelessModemUpOff ) if( type == BlockPeripheralVariant.WirelessModemUpOn || type == BlockPeripheralVariant.WirelessModemUpOff )
{ {
return EnumFacing.UP; return EnumFaceSlot.UP;
} }
else if( type == BlockPeripheralVariant.WirelessModemDownOn || type == BlockPeripheralVariant.WirelessModemDownOff ) else if( type == BlockPeripheralVariant.WirelessModemDownOn || type == BlockPeripheralVariant.WirelessModemDownOff )
{ {
return EnumFacing.UP; return EnumFaceSlot.DOWN;
}
else if( type == BlockPeripheralVariant.WirelessModemOff || type == BlockPeripheralVariant.WirelessModemOn )
{
return EnumFaceSlot.fromFace( state.getValue( BlockPeripheral.FACING ) );
} }
else else
{ {
return state.getValue( BlockPeripheral.FACING ); return EnumCenterSlot.CENTER;
} }
} }

View File

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

View File

@@ -50,7 +50,7 @@ public class DiskDrivePeripheral implements IPeripheral
"playAudio", "playAudio",
"stopAudio", "stopAudio",
"ejectDisk", "ejectDisk",
"getDiskID" "getDiskID",
}; };
} }

View File

@@ -62,7 +62,7 @@ public final class TileDiskDrive extends TileGeneric implements DefaultInventory
@Nonnull @Nonnull
private ItemStack m_diskStack = ItemStack.EMPTY; private ItemStack m_diskStack = ItemStack.EMPTY;
private final LazyOptional<IItemHandlerModifiable> m_itemCap = LazyOptional.of( () -> new InvWrapper( this ) ); private LazyOptional<IItemHandlerModifiable> itemHandlerCap;
private IMount m_diskMount = null; private IMount m_diskMount = null;
private boolean m_recordQueued = false; private boolean m_recordQueued = false;
@@ -82,6 +82,17 @@ public final class TileDiskDrive extends TileGeneric implements DefaultInventory
if( m_recordPlaying ) stopRecord(); if( m_recordPlaying ) stopRecord();
} }
@Override
protected void invalidateCaps()
{
super.invalidateCaps();
if( itemHandlerCap != null )
{
itemHandlerCap.invalidate();
itemHandlerCap = null;
}
}
@Override @Override
public boolean onActivate( EntityPlayer player, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ ) public boolean onActivate( EntityPlayer player, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ )
{ {
@@ -540,7 +551,11 @@ public final class TileDiskDrive extends TileGeneric implements DefaultInventory
@Override @Override
public <T> LazyOptional<T> getCapability( @Nonnull Capability<T> cap, @Nullable final EnumFacing side ) public <T> LazyOptional<T> getCapability( @Nonnull Capability<T> cap, @Nullable final EnumFacing side )
{ {
if( cap == ITEM_HANDLER_CAPABILITY ) return m_itemCap.cast(); if( cap == ITEM_HANDLER_CAPABILITY )
{
if( itemHandlerCap == null ) itemHandlerCap = LazyOptional.of( () -> new InvWrapper( this ) );
return itemHandlerCap.cast();
}
return super.getCapability( cap, side ); return super.getCapability( cap, side );
} }

View File

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

View File

@@ -86,7 +86,7 @@ public class BlockCable extends BlockGeneric implements WaterloggableBlock
{ {
if( !state.get( CABLE ) ) return false; if( !state.get( CABLE ) ) return false;
if( state.get( MODEM ).getFacing() == direction ) return true; if( state.get( MODEM ).getFacing() == direction ) return true;
return ComputerCraftAPI.getWiredElementAt( world, pos.offset( direction ), direction.getOpposite() ) != null; return ComputerCraftAPI.getWiredElementAt( world, pos.offset( direction ), direction.getOpposite() ).isPresent();
} }
@Nonnull @Nonnull
@@ -113,8 +113,7 @@ public class BlockCable extends BlockGeneric implements WaterloggableBlock
ItemStack item; ItemStack item;
IBlockState newState; IBlockState newState;
VoxelShape bb = CableShapes.getModemShape( state ); if( WorldUtil.isVecInside( CableShapes.getModemShape( state ), hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) ) )
if( WorldUtil.isVecInside( bb, hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) ) )
{ {
newState = state.with( MODEM, CableModemVariant.None ); newState = state.with( MODEM, CableModemVariant.None );
item = new ItemStack( ComputerCraft.Items.wiredModem ); item = new ItemStack( ComputerCraft.Items.wiredModem );
@@ -161,9 +160,7 @@ public class BlockCable extends BlockGeneric implements WaterloggableBlock
if( modem == null ) return new ItemStack( ComputerCraft.Items.cable ); if( modem == null ) return new ItemStack( ComputerCraft.Items.cable );
// We've a modem and cable, so try to work out which one we're interacting with // We've a modem and cable, so try to work out which one we're interacting with
TileEntity tile = world.getTileEntity( pos ); return hit != null && WorldUtil.isVecInside( CableShapes.getModemShape( state ), hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) )
return tile instanceof TileCable && hit != null &&
CableShapes.getModemShape( state ).getBoundingBox().contains( hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) )
? new ItemStack( ComputerCraft.Items.wiredModem ) ? new ItemStack( ComputerCraft.Items.wiredModem )
: new ItemStack( ComputerCraft.Items.cable ); : new ItemStack( ComputerCraft.Items.cable );
@@ -202,21 +199,6 @@ public class BlockCable extends BlockGeneric implements WaterloggableBlock
return getFluidState( state ).getBlockState(); return getFluidState( state ).getBlockState();
} }
if( side == state.get( MODEM ).getFacing() && !state.isValidPosition( world, pos ) )
{
if( !state.get( CABLE ) ) return getFluidState( state ).getBlockState();
/* TODO:
TileEntity entity = world.getTileEntity( pos );
if( entity instanceof TileCable )
{
entity.modemChanged();
entity.connectionsChanged();
}
*/
state = state.with( MODEM, CableModemVariant.None );
}
return state.with( CONNECTIONS.get( side ), doesConnectVisually( state, world, pos, side ) ); return state.with( CONNECTIONS.get( side ), doesConnectVisually( state, world, pos, side ) );
} }

View File

@@ -35,6 +35,7 @@ import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.NonNullConsumer;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -90,7 +91,7 @@ public class TileCable extends TileGeneric implements IPeripheralTile
private boolean m_connectionsFormed = false; private boolean m_connectionsFormed = false;
private final WiredModemElement m_cable = new CableElement(); private final WiredModemElement m_cable = new CableElement();
private LazyOptional<IWiredElement> m_cableCapability = LazyOptional.of( () -> m_cable ); private LazyOptional<IWiredElement> elementCap;
private final IWiredNode m_node = m_cable.getNode(); private final IWiredNode m_node = m_cable.getNode();
private final WiredModemPeripheral m_modem = new WiredModemPeripheral( private final WiredModemPeripheral m_modem = new WiredModemPeripheral(
new ModemState( () -> TickScheduler.schedule( this ) ), new ModemState( () -> TickScheduler.schedule( this ) ),
@@ -113,6 +114,8 @@ public class TileCable extends TileGeneric implements IPeripheralTile
} }
}; };
private final NonNullConsumer<LazyOptional<IWiredElement>> connectedNodeChanged = x -> connectionsChanged();
public TileCable() public TileCable()
{ {
super( FACTORY ); super( FACTORY );
@@ -152,6 +155,17 @@ public class TileCable extends TileGeneric implements IPeripheralTile
onRemove(); onRemove();
} }
@Override
protected void invalidateCaps()
{
super.invalidateCaps();
if( elementCap != null )
{
elementCap.invalidate();
elementCap = null;
}
}
@Override @Override
public void onLoad() public void onLoad()
{ {
@@ -322,18 +336,20 @@ public class TileCable extends TileGeneric implements IPeripheralTile
BlockPos offset = current.offset( facing ); BlockPos offset = current.offset( facing );
if( !world.isBlockLoaded( offset ) ) continue; if( !world.isBlockLoaded( offset ) ) continue;
IWiredElement element = ComputerCraftAPI.getWiredElementAt( world, offset, facing.getOpposite() ); LazyOptional<IWiredElement> element = ComputerCraftAPI.getWiredElementAt( world, offset, facing.getOpposite() );
if( element == null ) continue; if( !element.isPresent() ) continue;
element.addListener( connectedNodeChanged );
IWiredNode node = element.orElseThrow( NullPointerException::new ).getNode();
if( BlockCable.canConnectIn( state, facing ) ) if( BlockCable.canConnectIn( state, facing ) )
{ {
// If we can connect to it then do so // If we can connect to it then do so
m_node.connectTo( element.getNode() ); m_node.connectTo( node );
} }
else if( m_node.getNetwork() == element.getNode().getNetwork() ) else if( m_node.getNetwork() == node.getNetwork() )
{ {
// Otherwise if we're on the same network then attempt to void it. // Otherwise if we're on the same network then attempt to void it.
m_node.disconnectFrom( element.getNode() ); m_node.disconnectFrom( node );
} }
} }
} }
@@ -341,9 +357,11 @@ public class TileCable extends TileGeneric implements IPeripheralTile
void modemChanged() void modemChanged()
{ {
// Tell anyone who cares that the connection state has changed // Tell anyone who cares that the connection state has changed
// TODO: Be more restrictive about this. if( elementCap != null )
m_cableCapability.invalidate(); {
m_cableCapability = LazyOptional.of( () -> m_cable ); elementCap.invalidate();
elementCap = null;
}
if( getWorld().isRemote ) return; if( getWorld().isRemote ) return;
@@ -405,8 +423,9 @@ public class TileCable extends TileGeneric implements IPeripheralTile
{ {
if( capability == CapabilityWiredElement.CAPABILITY ) if( capability == CapabilityWiredElement.CAPABILITY )
{ {
return !m_destroyed && BlockCable.canConnectIn( getBlockState(), facing ) if( m_destroyed || !BlockCable.canConnectIn( getBlockState(), facing ) ) return LazyOptional.empty();
? m_cableCapability.cast() : LazyOptional.empty(); if( elementCap == null ) elementCap = LazyOptional.of( () -> m_cable );
return elementCap.cast();
} }
return super.getCapability( capability, facing ); return super.getCapability( capability, facing );
@@ -418,7 +437,7 @@ public class TileCable extends TileGeneric implements IPeripheralTile
return !m_destroyed && hasModem() && side == getDirection() ? m_modem : null; return !m_destroyed && hasModem() && side == getDirection() ? m_modem : null;
} }
public boolean hasCable() boolean hasCable()
{ {
return getBlockState().get( BlockCable.CABLE ); return getBlockState().get( BlockCable.CABLE );
} }
@@ -428,7 +447,7 @@ public class TileCable extends TileGeneric implements IPeripheralTile
return getBlockState().get( BlockCable.MODEM ) != CableModemVariant.None; return getBlockState().get( BlockCable.MODEM ) != CableModemVariant.None;
} }
boolean canAttachPeripheral() private boolean canAttachPeripheral()
{ {
return hasCable() && hasModem(); return hasCable() && hasModem();
} }

View File

@@ -8,7 +8,7 @@ package dan200.computercraft.shared.peripheral.modem.wired;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.ComputerCraftAPIImpl; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.network.wired.IWiredElement; import dan200.computercraft.api.network.wired.IWiredElement;
import dan200.computercraft.api.network.wired.IWiredNode; import dan200.computercraft.api.network.wired.IWiredNode;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
@@ -33,6 +33,7 @@ import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.NonNullConsumer;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -105,10 +106,10 @@ public class TileWiredModemFull extends TileGeneric implements IPeripheralTile
private final ModemState m_modemState = new ModemState( () -> TickScheduler.schedule( this ) ); private final ModemState m_modemState = new ModemState( () -> TickScheduler.schedule( this ) );
private final WiredModemElement m_element = new FullElement( this ); private final WiredModemElement m_element = new FullElement( this );
private final LazyOptional<WiredModemElement> m_elementCap = LazyOptional.of( () -> m_element ); private LazyOptional<IWiredElement> elementCap;
private final IWiredNode m_node = m_element.getNode(); private final IWiredNode m_node = m_element.getNode();
private int m_state = 0; private final NonNullConsumer<LazyOptional<IWiredElement>> connectedNodeChanged = x -> connectionsChanged();
public TileWiredModemFull() public TileWiredModemFull()
{ {
@@ -143,6 +144,17 @@ public class TileWiredModemFull extends TileGeneric implements IPeripheralTile
doRemove(); doRemove();
} }
@Override
protected void invalidateCaps()
{
super.invalidateCaps();
if( elementCap != null )
{
elementCap.invalidate();
elementCap = null;
}
}
@Override @Override
public void remove() public void remove()
{ {
@@ -275,11 +287,11 @@ public class TileWiredModemFull extends TileGeneric implements IPeripheralTile
BlockPos offset = current.offset( facing ); BlockPos offset = current.offset( facing );
if( !world.isBlockLoaded( offset ) ) continue; if( !world.isBlockLoaded( offset ) ) continue;
IWiredElement element = ComputerCraftAPIImpl.INSTANCE.getWiredElementAt( world, offset, facing.getOpposite() ); LazyOptional<IWiredElement> element = ComputerCraftAPI.getWiredElementAt( world, offset, facing.getOpposite() );
if( element == null ) continue; if( !element.isPresent() ) continue;
// If we can connect to it then do so element.addListener( connectedNodeChanged );
m_node.connectTo( element.getNode() ); m_node.connectTo( element.orElseThrow( NullPointerException::new ).getNode() );
} }
} }
@@ -350,7 +362,11 @@ public class TileWiredModemFull extends TileGeneric implements IPeripheralTile
@Override @Override
public <T> LazyOptional<T> getCapability( @Nonnull Capability<T> capability, @Nullable EnumFacing facing ) public <T> LazyOptional<T> getCapability( @Nonnull Capability<T> capability, @Nullable EnumFacing facing )
{ {
if( capability == CapabilityWiredElement.CAPABILITY ) return m_elementCap.cast(); if( capability == CapabilityWiredElement.CAPABILITY )
{
if( elementCap == null ) elementCap = LazyOptional.of( () -> m_element );
return elementCap.cast();
}
return super.getCapability( capability, facing ); return super.getCapability( capability, facing );
} }

View File

@@ -277,7 +277,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
private final String[] m_methods; private final String[] m_methods;
private final Map<String, Integer> m_methodMap; 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_element = element;
m_peripheral = peripheral; m_peripheral = peripheral;

View File

@@ -8,6 +8,7 @@ package dan200.computercraft.shared.peripheral.modem.wireless;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralTile;
import dan200.computercraft.shared.common.TileGeneric; import dan200.computercraft.shared.common.TileGeneric;
import dan200.computercraft.shared.peripheral.modem.ModemPeripheral; import dan200.computercraft.shared.peripheral.modem.ModemPeripheral;
import dan200.computercraft.shared.peripheral.modem.ModemState; import dan200.computercraft.shared.peripheral.modem.ModemState;
@@ -22,8 +23,9 @@ import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class TileWirelessModem extends TileGeneric public class TileWirelessModem extends TileGeneric implements IPeripheralTile
{ {
public static final NamedBlockEntityType<TileWirelessModem> FACTORY_NORMAL = NamedBlockEntityType.create( public static final NamedBlockEntityType<TileWirelessModem> FACTORY_NORMAL = NamedBlockEntityType.create(
new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_normal" ), new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_normal" ),
@@ -146,4 +148,13 @@ public class TileWirelessModem extends TileGeneric
getWorld().setBlockState( getPos(), state.with( BlockWirelessModem.ON, on ) ); getWorld().setBlockState( getPos(), state.with( BlockWirelessModem.ON, on ) );
} }
} }
@Nullable
@Override
public IPeripheral getPeripheral( @Nonnull EnumFacing side )
{
updateDirection();
return side == modemDirection ? modem : null;
}
} }

View File

@@ -155,7 +155,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
for( IComputerAccess computer : monitor.m_computers ) for( IComputerAccess computer : monitor.m_computers )
{ {
computer.queueEvent( "monitor_resize", new Object[] { computer.queueEvent( "monitor_resize", new Object[] {
computer.getAttachmentName() computer.getAttachmentName(),
} ); } );
} }
} }
@@ -321,7 +321,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
return getDirection().rotateYCCW(); return getDirection().rotateYCCW();
} }
private EnumFacing getDown() public EnumFacing getDown()
{ {
EnumFacing orientation = getOrientation(); EnumFacing orientation = getOrientation();
if( orientation == EnumFacing.NORTH ) return EnumFacing.UP; if( orientation == EnumFacing.NORTH ) return EnumFacing.UP;
@@ -625,7 +625,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
for( IComputerAccess computer : monitor.m_computers ) for( IComputerAccess computer : monitor.m_computers )
{ {
computer.queueEvent( "monitor_touch", new Object[] { computer.queueEvent( "monitor_touch", new Object[] {
computer.getAttachmentName(), xCharPos, yCharPos computer.getAttachmentName(), xCharPos, yCharPos,
} ); } );
} }
} }

View File

@@ -61,8 +61,7 @@ public final class TilePrinter extends TileGeneric implements DefaultSidedInvent
ITextComponent customName; ITextComponent customName;
private final NonNullList<ItemStack> m_inventory = NonNullList.withSize( 13, ItemStack.EMPTY ); private final NonNullList<ItemStack> m_inventory = NonNullList.withSize( 13, ItemStack.EMPTY );
private IItemHandlerModifiable m_itemHandlerAll = new InvWrapper( this ); private LazyOptional<IItemHandlerModifiable>[] itemHandlerCaps;
private LazyOptional<IItemHandlerModifiable>[] m_itemHandlerSides;
private final Terminal m_page = new Terminal( ItemPrintout.LINE_MAX_LENGTH, ItemPrintout.LINES_PER_PAGE ); private final Terminal m_page = new Terminal( ItemPrintout.LINE_MAX_LENGTH, ItemPrintout.LINES_PER_PAGE );
private String m_pageTitle = ""; private String m_pageTitle = "";
@@ -79,6 +78,22 @@ public final class TilePrinter extends TileGeneric implements DefaultSidedInvent
ejectContents(); ejectContents();
} }
@Override
protected void invalidateCaps()
{
super.invalidateCaps();
if( itemHandlerCaps != null )
{
for( int i = 0; i < itemHandlerCaps.length; i++ )
{
if( itemHandlerCaps[i] == null ) continue;
itemHandlerCaps[i].invalidate();
itemHandlerCaps[i] = null;
}
}
}
@Override @Override
public boolean onActivate( EntityPlayer player, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ ) public boolean onActivate( EntityPlayer player, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ )
{ {
@@ -516,7 +531,6 @@ public final class TilePrinter extends TileGeneric implements DefaultSidedInvent
getWorld().setBlockState( getPos(), state.with( BlockPrinter.TOP, top ).with( BlockPrinter.BOTTOM, bottom ) ); getWorld().setBlockState( getPos(), state.with( BlockPrinter.TOP, top ).with( BlockPrinter.BOTTOM, bottom ) );
} }
@SuppressWarnings( { "unchecked", "rawtypes" } ) @SuppressWarnings( { "unchecked", "rawtypes" } )
@Nonnull @Nonnull
@Override @Override
@@ -524,28 +538,16 @@ public final class TilePrinter extends TileGeneric implements DefaultSidedInvent
{ {
if( capability == ITEM_HANDLER_CAPABILITY ) if( capability == ITEM_HANDLER_CAPABILITY )
{ {
LazyOptional<IItemHandlerModifiable>[] handlers = m_itemHandlerSides; LazyOptional<IItemHandlerModifiable>[] handlers = itemHandlerCaps;
if( handlers == null ) handlers = m_itemHandlerSides = new LazyOptional[6]; if( handlers == null ) handlers = itemHandlerCaps = new LazyOptional[7];
LazyOptional<IItemHandlerModifiable> handler; int index = facing == null ? 0 : 1 + facing.getIndex();
if( facing == null ) LazyOptional<IItemHandlerModifiable> handler = handlers[index];
if( handler == null )
{ {
int i = 6; handler = handlers[index] = facing == null
handler = handlers[i]; ? LazyOptional.of( () -> new InvWrapper( this ) )
if( handler == null ) : LazyOptional.of( () -> new SidedInvWrapper( this, facing ) );
{
handler = handlers[i] = LazyOptional.of( () -> m_itemHandlerAll );
}
}
else
{
int i = facing.ordinal();
handler = handlers[i];
if( handler == null )
{
handler = handlers[i] = LazyOptional.of( () -> new SidedInvWrapper( this, facing ) );
}
} }
return handler.cast(); return handler.cast();

View File

@@ -59,8 +59,8 @@ public abstract class SpeakerPeripheral implements IPeripheral
public String[] getMethodNames() public String[] getMethodNames()
{ {
return new String[] { return new String[] {
"playSound", // Plays sound at resourceLocator "playSound",
"playNote" // Plays note "playNote",
}; };
} }

View File

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

View File

@@ -10,6 +10,7 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.pocket.IPocketAccess; import dan200.computercraft.api.pocket.IPocketAccess;
import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.shared.common.IColouredItem; import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.core.ServerComputer;
@@ -123,14 +124,14 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
public void invalidatePeripheral() public void invalidatePeripheral()
{ {
IPeripheral peripheral = m_upgrade == null ? null : m_upgrade.createPeripheral( this ); IPeripheral peripheral = m_upgrade == null ? null : m_upgrade.createPeripheral( this );
setPeripheral( 2, peripheral ); setPeripheral( ComputerSide.BACK, peripheral );
} }
@Nonnull @Nonnull
@Override @Override
public Map<ResourceLocation, IPeripheral> getUpgrades() public Map<ResourceLocation, IPeripheral> getUpgrades()
{ {
return m_upgrade == null ? Collections.emptyMap() : Collections.singletonMap( m_upgrade.getUpgradeID(), getPeripheral( 2 ) ); return m_upgrade == null ? Collections.emptyMap() : Collections.singletonMap( m_upgrade.getUpgradeID(), getPeripheral( ComputerSide.BACK ) );
} }
public IPocketUpgrade getUpgrade() public IPocketUpgrade getUpgrade()

View File

@@ -12,6 +12,7 @@ import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.media.IMedia; import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.shared.PocketUpgrades; import dan200.computercraft.shared.PocketUpgrades;
import dan200.computercraft.shared.common.IColouredItem; import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.core.ClientComputer; import dan200.computercraft.shared.computer.core.ClientComputer;
@@ -120,7 +121,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
// Update pocket upgrade // Update pocket upgrade
if( upgrade != null ) if( upgrade != null )
{ {
upgrade.update( computer, computer.getPeripheral( 2 ) ); upgrade.update( computer, computer.getPeripheral( ComputerSide.BACK ) );
} }
} }
} }
@@ -149,7 +150,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
if( upgrade != null ) if( upgrade != null )
{ {
computer.updateValues( player, stack, upgrade ); computer.updateValues( player, stack, upgrade );
stop = upgrade.onRightClick( world, computer, computer.getPeripheral( 2 ) ); stop = upgrade.onRightClick( world, computer, computer.getPeripheral( ComputerSide.BACK ) );
} }
} }

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