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

Compare commits

...

160 Commits

Author SHA1 Message Date
Jonathan Coates
bdd38fb061 Update changelog and whatsnew for release 2021-08-02 20:15:32 +01:00
DrHesperus
06f35e4997 Update and rename 2021-08-02 19:49:50 +01:00
Jonathan Coates
2d95c32892 Reformat language files 2021-08-02 19:48:16 +01:00
Jonathan Coates
6f4d4540b2 Update location of several langauge files
Looks like weblate was misconfigured here. These should be picked up -
fingers crossed!
2021-08-02 19:05:10 +01:00
Jonathan Coates
96316cddaa Fix monitor resize checks being entirely wrong
Fixes #879
2021-08-02 18:46:00 +01:00
Jonathan Coates
e84ddef877 Bump version to 1.98.0 2021-08-01 09:02:05 +01:00
Jonathan Coates
11b40bb6d5 Identify unknown upgrades as ComputerCraft instead
When the mod id is null, the namespace is given as "minecraft". This is
not what we want, so use CC there instead.
2021-08-01 08:48:02 +01:00
Jonathan Coates
686c6a4c44 Use cc.strings.wrap inside slowWrite
Means we don't end up re-wrapping text and producing incorrect results.
Fixes #865
2021-08-01 08:43:37 +01:00
Jonathan Coates
a1821035d3 Add a bit of version information to the bios
It's probably the lowest traffic module :p.

Also (somewhat) improve the deprecated messages in os.loadAPI. We really
need a proper article on require and converting from os.loadAPI.
2021-07-30 22:45:51 +01:00
Jonathan Coates
7b8650bbc8 Fix fs.getCapacity docs
Slightly embarassing the state this was in
2021-07-29 09:59:21 +00:00
Jonathan Coates
0285260e97 Merge branch 'mc-1.15.x' into mc-1.16.x 2021-07-28 16:18:27 +01:00
Jonathan Coates
10a3a223a0 Bump version to 1.97.1
Plan here is to release 1.98 for 1.16.x and 1.17.x and 1.97.1 for
1.15.x. However, will let this sit for a few days while I sort out 1.98
and the 1.17 port just in case any more bugs pop up.
2021-07-28 16:15:32 +01:00
Jonathan Coates
2dc970a8bb Don't error when given a malformed URL
Sometimes the pattern fails to match and so the file name ends up being
nil.
2021-07-28 16:00:31 +01:00
Jonathan Coates
f74c4cc83c Add config options for a global bandwidth limit
This uses Netty's global traffic shaping handlers to limit the rate at
which packets can be sent and received. If the bandwidth limit is hit,
we'll start dropping packets, which will mean remote servers send
traffic to us at a much slower pace.

This isn't perfect, as there is only a global limit, and not a
per-computer one. As a result, its possible for one computer to use
all/most bandwidth, and thus slow down other computers.

This would be something to improve on in the future. However, I've spent
a lot of time reading the netty source code and docs, and the
implementation for that is significantly more complex, and one I'm not
comfortable working on right now.

For the time being, this satisfies the issues in #33 and hopefully
alleviates server owner's concerns about the http API. Remaining
problems can either be solved by moderation (with help of the
//computercraft track` command) or future updates.

Closes #33
2021-07-28 15:53:22 +01:00
Jonathan Coates
7012ac7163 Merge branch 'mc-1.15.x' into mc-1.16.x 2021-07-25 16:42:22 +01:00
Jonathan Coates
227b444d81 Accept client_no_context_takeover in websockets
Doesn't fix #695, but Good Enough(TM).
2021-07-25 16:40:27 +01:00
Jonathan Coates
d50db8a6f3 Add a fancy test system for async methods
Written in order to ~avoid working on~ test #695. Sadly, no luck there.
2021-07-25 16:25:59 +01:00
Jonathan Coates
3a80b51a9f Ensure monitors are well-formed when placed
Closes #36
2021-07-25 14:18:07 +01:00
Jonathan Coates
03396cf07a Fix help crashing on terminal resize
Closes #870. Woops.
2021-07-24 22:58:23 +01:00
Jonathan Coates
5b57f7509d Add back executeMainThreadTask
Closes #868
2021-07-24 12:04:21 +01:00
Jonathan Coates
0568c86628 Hopefully fix flakiness in rednet test 2021-07-24 00:00:37 +01:00
Jonathan Coates
b31e66686d Make rednet.run a little more strict
Also add a test for rednet message sending. Hopefully gives some of the
modem and networking code a little bit of coverage (which is clearly the
same as being right :p).
2021-07-23 23:53:40 +01:00
Jonathan Coates
924b8ef30f Do not access the current server from the client
Just missing some guards in a few places.

Fixes #867
2021-07-23 23:35:13 +01:00
Jonathan Coates
7bcc16bb40 Some examples for rednet 2021-07-23 10:52:44 +00:00
Jonathan Coates
31e6746bdf Reword community links a bit 2021-07-21 23:20:27 +01:00
Jonathan Coates
c39bf3eb4d Hopefully block people creating blank issues 2021-07-21 23:18:32 +01:00
JackMacWindows
8b952e7e1e Fix parallel.* hanging when called with no args (#863) 2021-07-19 11:20:35 +01:00
SkyTheCodeMaster
04e97f7b86 Fix & enhance some docs (#857) 2021-07-18 17:36:09 +01:00
Jonathan Coates
74752c561c Correctly extract parse errors from build reports 2021-07-18 16:17:11 +01:00
Jonathan Coates
ee96458b56 Merge pull request #862 from Angalexik/typo
Fix typo in paintutils documentation
2021-07-18 12:16:19 +01:00
Angalexik
333410e4cd Fixed typo in paintutils documentation 2021-07-18 12:37:16 +02:00
Jonathan Coates
999a39a3e6 Merge branch 'mc-1.15.x' into mc-1.16.x 2021-07-15 13:05:31 +01:00
Jonathan Coates
82ca19c296 Add type check for http.checkURL 2021-07-15 13:04:44 +01:00
Jonathan Coates
56d8a5d585 Don't update block states when there is no world
Fixes #856
2021-07-15 12:13:38 +01:00
Jonathan Coates
aa5fbb2980 Use heap buffers rather than direct ones
Closes #855, even if it doesn't really fix it.
2021-07-15 11:55:52 +01:00
Jonathan Coates
db0bb071f5 Add motd for file uploading 2021-07-15 10:58:52 +01:00
Jonathan Coates
ab702e2ba1 Correctly render turtles' selected slot
We had the wrong texture bound, which meant nothing was being rendered!

Fixes #852
2021-07-15 10:58:40 +01:00
SkyTheCodeMaster
d4efacd40a Turn a nil to a string
Makes a `nil` a string inside of an expect in `define`, allowing `nil` to be passed.
2021-07-15 10:54:30 +01:00
Jonathan Coates
347affcc5c Merge pull request #859 from emiliskiskis/patch-1
Fix typo in turtle.craft documentation
2021-07-12 14:31:28 +01:00
Emilis Kiškis
8ebe34b8da Fix typo in turtle.craft documentation
Description wrote "to craft sticks, slots should contain sticks", whereas it should say "planks" in the second part.
2021-07-11 11:03:41 +03:00
Jonathan Coates
7086cb8a02 Merge pull request #848 from SkyTheCodeMaster/patch-3
Turn a nil to a string
2021-07-05 06:16:05 +01:00
SkyTheCodeMaster
8dbc930c2f Turn a nil to a string
Makes a `nil` a string inside of an expect in `define`, allowing `nil` to be passed.
2021-07-04 20:54:55 -04:00
Jonathan Coates
61eb67849d Some fs examples
See #566
2021-07-03 14:05:41 +01:00
Jonathan Coates
c2316ef256 Publish docs from 1.16 instead 2021-07-02 21:49:42 +01:00
Jonathan Coates
0d22270f8b Publish docs from 1.16 instead 2021-07-02 21:49:19 +01:00
Jonathan Coates
abb9c14256 Bump Forge Gradle version 2021-07-02 21:48:28 +01:00
Jonathan Coates
815e534dc6 Merge branch 'mc-1.15.x' into mc-1.16.x 2021-07-02 21:03:39 +01:00
Jonathan Coates
51dde077fe And there was me thinking I understood Gradle 2021-06-29 09:08:45 +00:00
Jonathan Coates
31d0b7afcd Some further fixes to previous commit
Did NOT mean to push that
2021-06-28 23:20:54 +01:00
Jonathan Coates
95b0d950aa Couple of gradle fixes 2021-06-28 23:07:36 +01:00
Jonathan Coates
efa2be2821 Update changelog for 1.16.x-only features 2021-06-28 22:58:13 +01:00
Jonathan Coates
670db97fc7 Merge branch 'mc-1.15.x' into mc-1.16.x 2021-06-28 22:52:05 +01:00
Jonathan Coates
1650b72edb Prepare for 1.97.0 release 2021-06-28 22:52:01 +01:00
Jonathan Coates
a5bca3f0df File uploading (#840) 2021-06-28 22:49:45 +01:00
Jonathan Coates
88f41314c7 Make io handles opaque to the docs
Yep, this is kinda gross. But also a nice refactor
2021-06-28 22:47:56 +01:00
Jonathan Coates
5ef8d52c13 Merge branch 'mc-1.15.x' into mc-1.16.x 2021-06-28 18:37:24 +01:00
Jonathan Coates
0b65d56ab0 Prevent some files from being eol converted
Closes #813

Well, hopefully. This hasn't been tested, as I don't have easy access to
Windows.
2021-06-28 18:16:30 +01:00
Jonathan Coates
a256b70685 Allow setting the subprotocol header
Fixes #828, closes #829
2021-06-24 20:48:28 +01:00
Jonathan Coates
f16d1499fe Woops 2021-06-24 09:07:22 +00:00
Jonathan Coates
79ca851e4f Like I said, 'there may be a few of these' 2021-06-24 08:34:38 +00:00
Jonathan Coates
d5c54d64a6 Hopefully fix the bug report template
There may be a few of these commits :D:.
2021-06-24 08:32:57 +00:00
Jonathan Coates
5cfdd2339f Try the new issue template system 2021-06-24 08:32:18 +00:00
Jonathan Coates
3ab3213290 Try the new issue template system 2021-06-24 08:31:41 +00:00
Jonathan Coates
46c9840d00 Merge pull request #842 from Lupus590-CC/fix-extra-,-in-expect
remove extra ,
2021-06-23 15:22:08 +01:00
Lupus590
b3f2f14e96 remove extra , 2021-06-23 15:14:01 +01:00
Jonathan Coates
3ace49d27f Remove config option for debug API
And also remove the "is present" guards within the various APIs. I'm
happy at this point that debug is safe, and think we can guarantee its
presence.
2021-06-22 21:01:05 +01:00
Jonathan Coates
9bd662d8dc Fix CraftTweaker actions being applied twice
By default CT applies them on the client and server. In a single player
world, this means we try to create two upgrades, which obviously fails!

Fixes #721
2021-06-22 19:39:46 +01:00
Jonathan Coates
df7a40354e Merge branch 'mc-1.15.x' into mc-1.16.x 2021-06-19 15:18:12 +01:00
Jonathan Coates
c489d4bc4f Fix exiting paint typing "e" in the shell
When exiting paint via the keyboard by typing "Ctrl" then "E"
separately, we consume the "key" event within paint, leaving the shell
to consume "read".

To avoid this, we run a sleep(0) to gobble any other left-over events.

Note, it's generally not enough to run a queueEvent/pullEvent here, as
the char event may not have ended up on the queue yet. Alas, as this
solution is pretty ugly.
2021-06-19 15:13:26 +01:00
Jonathan Coates
2b029bd506 Refactor some computer screen logic
- Move some shared Gui{Computer,Turtle} code into a new class. Using
   entirely different naming conventions because of course (they are
   consistent with MojMap, just not the rest of CC:T).
 - Fix some mouse scaling issues in the terminal.
2021-06-19 14:18:12 +01:00
Jonathan Coates
2227845658 Remove NetworkMessage.fromBytes
Always build packets using a PacketBuffer constructor instead.
2021-06-19 13:01:43 +01:00
Jonathan Coates
2d3e88ef59 Replace mixin with access transformers
Hadn't realised I could use these to touch final modifiers! More
importantly, Mixin doesn't work with FG 5.0, so we needed an alternative
:).
2021-06-19 12:52:07 +01:00
Jonathan Coates
0bfe960cbd Merge branch 'mc-1.15.x' into mc-1.16.x 2021-06-19 12:41:30 +01:00
Jonathan Coates
a735f23e1f Fix a couple of bugs with wget
- Provide a friendly message when writing to a file fails.
 - Correctly write empty files

Fixes #832, closes #833
2021-06-19 11:59:15 +01:00
Jonathan Coates
de6f27ceaf Rewrite speaker networking code
Speakers now play sounds using a custom set of packets.
 - When playing a sound, we send the resource id, position, volume,
   pitch and a UUID for the _speaker_ to all nearby clients.
 - This UUID is then used when we need to update the sound. When the
   speaker is moved or destroyed, we send a new packet to clients and
   update accordingly.

This does have one side effect, that speakers can now only play one
sound at a time. I think this is accceptable - otherwise it's possible
to spam ward in a loop.

Notes still use the old networking code, and so will not be affected.

Closes #823
2021-06-18 22:23:04 +01:00
Jonathan Coates
2fab1a3054 Minor code style fixups
- Add missing @Override annotations. I can't find a way to enforce this
   with checkstyle - maybe I need spotbugs too D:.
 - Remove superflous "this"es.
2021-06-12 22:18:35 +01:00
Jonathan Coates
d4745ae47e Don't override isMouseOver
Minecraft's Widget class implements this for us!
2021-06-12 22:02:03 +01:00
Jonathan Coates
dc21e2dbc9 Minor dockerfile tweaks
- Install npm
 - Use apt-get instead of apt
2021-06-12 21:38:01 +01:00
Merith
75dfa71275 Gitpod Support (#817) 2021-06-12 21:33:08 +01:00
Jonathan Coates
d71bf225cc Add very simple markdown support to the help viewer (#819)
- Allow help files to use the ".md" suffix, and move changelog/whatsnew
   to use them.

 - When files end with ".md", the "help" program attempts to highlight
   them. This involves:
   - Colour code blocks with a lightGrey background.
   - Replace lists to use bullet points instead of "-"/"*".
   - Colours headings yellow.
   The implementation of this is a bit janky because a) I wrote this and
   b) we need to run this step before text wrapping, but preserve
   colours and section positions over wrapping (thanks to Jack for
   getting this working).

 - Add section navigation to the help viewer, with left/right to move to
   the next/previous section.

Closes #569
2021-06-12 19:48:41 +01:00
Jonathan Coates
8644c4ebf6 More gradle tweaks
- Upgade to Gradle 7.0 and FG 5.0
 - Allow running with any Java version - this now correctly compiles
   Forge with the right version.
 - Upload to Modrinth too. This is entirely untested, so may need some
   tweaking.
2021-06-09 18:01:13 +01:00
Wojbie
b323db30ee Add cc.shell.completion programWithArgs completion. (#815)
And expand monitor, shell, fg and bg to use it
2021-06-07 22:02:45 +01:00
JackMacWindows
53efd6b303 Fixed error handling in scale subcommand (#816) 2021-06-07 20:47:15 +01:00
Jonathan Coates
97faa1b3bc Fix several sidebar textures
There was an off by one here!
2021-06-07 18:34:57 +01:00
Jonathan Coates
7404133d40 Fix alternative localhost domain
lvh.me is dead. Let's kick the can down the road!
2021-06-07 18:22:49 +01:00
Jonathan Coates
e18e24407e Add additional buttons to the computer GUI (#809)
Adds a sidebar to the computer and turtle GUI. This currently provides
 - A power indicator, which turns on/shuts down a computer.
 - Button to queue a "terminate" event
2021-06-06 19:32:52 +01:00
Jonathan Coates
026afa7f73 Put some limits on various external queues
Ideally turtle functions would error, but wrangling that is more pain
than it's worth.
2021-06-06 19:26:20 +01:00
Jonathan Coates
29cc5bb86b Shut down computers on errors
Previously we would attempt to resume them, which then caused confusing
behaviour if the Lua VM was in an inconsistent state.

Closes #811
2021-06-06 18:33:52 +01:00
Jonathan Coates
aa9d3c8269 Refactor LuaContext into a separate class
This isn't tied to Cobalt any more!
2021-06-06 18:33:52 +01:00
Matthew Wilbern
f8074636bc Allow craft program to craft unlimited items (#807) 2021-06-06 06:52:01 +01:00
Jonathan Coates
db2cde4a4c Add MoreRed support
I've written three or four integrations with bundled cables now over the
various versions, and I'm still not convinced this one is right. It
appears to work though, so we'll see.

We need depend on the main jar here (rather than the API jar), as
otherwise our javadoc tools fails - the API references some internal
classes which aren't on our classpath.

It might be nice to write tests for this (and CT) in the future -
goodness knows these features are fragile - but that'll require some
more gradle work which I don't want to do right now.

Closes #772
2021-06-05 14:48:38 +01:00
Jonathan Coates
5eec7d9172 Bump to 1.16.5
I don't think anyone still uses 1.16.4, so no point worrying about it.

Closes #808
2021-06-05 12:32:40 +01:00
Jonathan Coates
8b9735d72e Fix background not rendering on (pocket) computers 2021-06-05 11:46:10 +01:00
Jonathan Coates
1866916cb8 Fix coloured pocket computers using the wrong texture 2021-06-05 11:36:36 +01:00
Jonathan Coates
f38a6a9d43 Merge branch 'mc-1.15.x' into mc-1.16.x 2021-06-05 11:36:30 +01:00
Jonathan Coates
0f6db63020 Remove "*Proxy" classes
- Move registry code into the various *Registry classes.

   I'm not sure this is any more sensible, but things being registered
   in different places kinda irked me.

 - Everything else (i.e. event listeners) goes in a {Client,Common}Hooks
   class right now. It's not ideal, but I don't think we can split it up
   much.
2021-06-05 10:09:28 +01:00
Jonathan Coates
51fcd83b87 Clean up turtle model loading code
Looks like Forge has had hooks for this for years, I've just never
noticed.
2021-06-05 09:14:18 +01:00
Jonathan Coates
c2190e1318 Improve error message for SSL failures
Fixes #803
2021-06-01 22:10:11 +01:00
Ronan Hanley
c40a13558c Fixed side input consistency issue when a turtle upgrade is removed (#743) 2021-06-01 19:12:35 +01:00
Jonathan Coates
02695aea51 Reduce some block updates
- Fix double updateOutput() call in TileComputerBase - I guess a
   merge/rebase gone wrong in the past.
 - Don't call updateBlock() when creating a server computer. This used
   to be needed when we sent the computer to the client, but this is no
   longer the case.
 - Don't call updateBlock() on TileMonitors when updating from the
   client. We don't need to do a redraw here, as this is all stored in
   the block state now.
 - Don't update the block when reading turtle upgrades. See #643 for
   some background here.

See #658
2021-06-01 18:55:12 +01:00
Jonathan Coates
d5be1aca0e Always accept command output for command computers
Fixes #799
2021-05-31 19:36:43 +01:00
Jonathan Coates
8ff8b78ed8 Better error handling in treasure disks
- Return a more sensible string for empty treasure disks (i.e. those
   given by /give). This should help identify packs which are giving
   items in non-supported ways.
 - Fix NPE when the treasure mount doesn't exist.

Fixes #801
2021-05-31 19:24:38 +01:00
Matthew Wilbern
7fc55aa9a0 Add __eq metamethod and equals method to the vector api. (#800) 2021-05-31 13:58:46 +01:00
magiczocker10
38335ca187 Add some options for textutils.serialize (#664) 2021-05-29 17:46:58 +01:00
JackMacWindows
e0e194099c Add a scale subcommand to monitor.lua (#623) 2021-05-29 16:55:55 +01:00
Ronan Hanley
8063059764 Unit tests for Terminal (#740) 2021-05-29 16:24:42 +01:00
Jonathan Coates
f96d923b2a Allow cleaning dyed turtles in a cauldron
Fixes #771. This does not allow clearing pocket computers or disks right
now - neither normally responds very well to water!
2021-05-29 15:18:53 +01:00
Jonathan Coates
9142ccfc93 Rewrite turtle placing logic
- Simplify how the turtle's inventory is processed. We now copy all
   items into the player inventory, attempt to place, and then copy the
   items back.

   This also fixes the problem where turtle.place() wouldn't (always)
   update the item which was placed.

   I'm also hoping this is more "correct" in how we process drops from
   entities and whatnot. Though I've not had any reports of issues, so
   it's probably fine.

 - Replace the "error message" string array with an actual object. It'd
   be nicer all these functions returned a TurtleCommandResult, but
   merging error messages from that is a little harder.

Fun facts: the test suite was actually helpful here, and caught the fact
that hoeing was broken!
2021-05-29 15:18:26 +01:00
Jonathan Coates
9f7cc00fcb Add button to view computer's folder
Implementation is a little awkward, as we can't send OPEN_FILE links
from the server, so we ensure the client runs a
/computercraft open-computer ID command instead. We then intercept this
on the client side and use that to open the folder.
2021-05-28 22:19:04 +01:00
Jonathan Coates
b129ae627b Make cc.pretty internals more opaque
I wish I had an actual type system.
2021-05-28 21:28:56 +01:00
Jonathan Coates
f9fb0619fa Swap CSS over to use illuaminate's variables
Closes #797
2021-05-28 21:18:38 +01:00
Jonathan Coates
7f9b86a78e Add basic JMX monitoring on dedicated servers
Closes #482
2021-05-27 21:36:56 +01:00
Weblate
58ea7a275e Translations for French
Translations for French

Translations for German

Co-authored-by: Anavrins <xanavrins@gmail.com>
Co-authored-by: Jummit <jummit@web.de>
Co-authored-by: Naheulf <newheulf@gmail.com>
2021-05-24 01:01:57 +00:00
Jonathan Coates
8487a13764 Remove some funky buildscript code
- Strip out proguard
 - Drop the json compression code

I /think/ this may have had a bigger impact at the time, but Cobalt is
slimmer than it was.
2021-05-21 22:11:25 +01:00
Jonathan Coates
f0ba1108d5 Merge branch 'mc-1.15.x' into mc-1.16.x 2021-05-20 19:03:56 +01:00
Jonathan Coates
5d0daf9b2d Remove dependencies from gradle 2021-05-20 19:03:30 +01:00
Jonathan Coates
8b8692ba53 Merge branch 'mc-1.15.x' into mc-1.16.x 2021-05-20 18:30:10 +01:00
Jonathan Coates
f387730b88 Merge branch 'mc-1.15.x' into mc-1.16.x 2021-04-24 11:50:23 +01:00
Jonathan Coates
8c56b6a7be Merge branch 'mc-1.15.x' into mc-1.16.x 2021-03-12 09:26:15 +00:00
Jonathan Coates
7514cf7320 Mark as compatible with 1.16.{4,5}
Closes #694
2021-01-24 21:23:29 +00:00
FensieRenaud
417fda3019 Serialise sparse arrays into JSON (#685) 2021-01-18 16:44:39 +00:00
Jonathan Coates
23bf33c454 Use mixins to construct the TestFunctionInfo class
There's some funky things going on here, but thankfully they're limited
to test code.
2021-01-16 12:40:00 +00:00
Jonathan Coates
0be030c497 Merge branch 'mc-1.15.x' into mc-1.16.x 2021-01-16 11:38:59 +00:00
Jonathan Coates
a3a9684505 Widen version range to include 1.16.5
Building against 1.16.4 for now to ensure we don't break it. Hopefully
we can bump this too once most people have migrated.

Will push a release tomorrow - don't want to be sorting out merge
conflicts at 23:30.
2021-01-15 23:26:01 +00:00
Jonathan Coates
c5694ea966 Merge branch 'mc-1.15.x' into mc-1.16.x 2021-01-09 19:25:33 +00:00
Jonathan Coates
7b476cb24b Remove usage of deprecated event constructor
This requires a Forge bump, but probably no harm in doing so anyway.
We're on an ancient (2nd Nov) version. Fixes #665.
2021-01-09 18:49:40 +00:00
Jonathan Coates
7ca261d763 Merge branch 'mc-1.15.x' into mc-1.16.x 2021-01-09 18:32:40 +00:00
Jonathan Coates
1edb7288b9 Merge branch 'mc-1.15.x' into mc-1.16.x 2021-01-06 22:39:54 +00:00
Jonathan Coates
4af5bcc0b0 Fix serveral 1.15 -> 1.16 issues
Well, strictly speaking some mapping changes.
2020-12-25 16:59:09 +00:00
Jonathan Coates
96c577482d Merge branch 'mc-1.15.x' into mc-1.16.x 2020-12-25 16:43:32 +00:00
SquidDev
61f8e97f6b Force the monitor depth blocker to be flushed
As explained in the comment, "built-in" rendering types are now manually
rendered ("finish"ed) before calling finish() on the main renderer. This
means our depth blocker hasn't actually been drawn (if a monitor is the
last TE to be rendered), and so blocks pass the depth test when they
shouldn't.

Fixes #599
2020-12-11 13:09:16 +00:00
SquidDev
c92f06cfd9 Bump deps to 1.16.4 2020-12-11 13:08:24 +00:00
Stephen Gibson
c9f3d315c0 Fix epoch documentation to use milliseconds (#580) 2020-11-13 14:32:49 +00:00
SquidDev
83df64e520 Merge branch 'mc-1.15.x' into mc-1.16.x 2020-11-07 12:46:10 +00:00
SquidDev
ab232bd689 Update to MC 1.16.4
Just some mapping changes really
2020-11-03 15:33:10 +00:00
SquidDev
5bf367af9f Merge branch 'mc-1.15.x' into mc-1.16.x 2020-10-23 17:45:11 +01:00
SquidDev
4766833cf2 Bump JEI/crafttweaker versions
In my defence, they weren't out when I started the 1.15 update.
2020-10-20 17:38:59 +01:00
SquidDev
71563a52ff Let's make this a proper release 2020-10-04 11:31:06 +01:00
SquidDev
0c6e7b5db5 Merge branch 'mc-1.15.x' into mc-1.16.x 2020-10-04 11:24:42 +01:00
SquidDev
72c1d451fe Mark the release as beta-quality 2020-09-12 10:47:19 +01:00
SquidDev
8b4a01df27 Update to Minecraft 1.16.3
I hope the Fabric folks now realise this is gonna be a race of who can
update first :p. Either way, this was a very easy update - only changes
were due to unrelated Forge changes.
2020-09-12 10:45:59 +01:00
SquidDev
d0a973fa46 Merge branch 'mc-1.15.x' into mc-1.16.x 2020-09-12 09:29:21 +01:00
SquidDev
26c12ac1a9 Bump version to 1.91.1 2020-09-04 17:29:35 +01:00
SquidDev
2c67849b35 Fix NPE when turtles interact with an entity
Closes #531
2020-08-27 17:17:03 +01:00
SquidDev
7809a2eddd Merge branch 'mc-1.15.x' into mc-1.16.x 2020-08-23 15:46:17 +01:00
SquidDev
e8e9294fdf Correctly check for success or consume
No, I don't really know what the difference is either :). Closes #518.
2020-08-22 19:28:02 +01:00
SquidDev
99581e1f40 Initial update to 1.16.2
Seems to load fine, but not done any proper testing.
2020-08-14 22:00:03 +01:00
SquidDev
29646a7f61 Bump version to 1.90.3 2020-07-27 19:07:06 +01:00
SquidDev
50d2712581 Resolve CC's save location to the world dir
Fixes #509
2020-07-27 19:04:57 +01:00
SquidDev
3093f882d8 Fix selected slot now showing in the turtle GUI 2020-07-27 18:37:07 +01:00
SquidDev
e5cf0d1c61 Update mappings 2020-07-27 18:26:42 +01:00
SquidDev
6b102a8142 Merge branch 'mc-1.15.x' into mc-1.16.x 2020-07-25 12:08:33 +01:00
SquidDev
c8a6888a2f Fix styles not being saved
Styles have been changed to be immutable, meaning that we were never
updating them! Fixes #499.
2020-07-25 11:19:04 +01:00
SquidDev
9ce33f8a3f Add back missing override of getPositionVec
This was removed in the initial update (46595e73df)
because I got terribly confused over mappings and I forgot to add it
back.

Fixes #505
2020-07-25 10:56:50 +01:00
SquidDev
a1dcd59d95 Update to latest Forge
Fixes #498
2020-07-18 15:11:57 +01:00
SquidDev
2a17585702 Merge branch 'mc-1.15.x' into mc-1.16.x 2020-07-18 12:24:49 +01:00
SquidDev
087c305b0d Fix non-inventory GUIs rendering labels 2020-07-18 12:17:02 +01:00
SquidDev
31764f6d65 Register various gold items as piglin_loved 2020-07-18 11:14:55 +01:00
SquidDev
4efde2b294 Merge branch 'mc-1.15.x' into mc-1.16.x 2020-07-18 10:34:29 +01:00
SquidDev
46595e73df Initial update to Minecraft 1.16.1
A lot is broken, but at least we can get in game:
 - GUIs render a whole bunch of additional "inventory" text, which we
   really don't want.
 - Computers load from the wrong location.
 - There's some issues with using Forge's tags from outside of JSON
   recipes. We need to work out why.
2020-07-11 20:36:10 +01:00
273 changed files with 7686 additions and 2770 deletions

12
.gitattributes vendored
View File

@@ -1,3 +1,15 @@
# Ignore changes in generated files
src/generated/resources/data/** linguist-generated
src/test/server-files/structures linguist-generated
* text=auto
*.gradle eol=lf diff=java
*.java eol=lf diff=java
*.kt eol=lf diff=java
*.lua eol=lf
*.md eol=lf diff=markdown
*.txt eol=lf
*.png binary
*.jar binary

View File

@@ -1,16 +0,0 @@
---
name: Bug report
about: Report some misbehaviour in the mod
labels: bug
---
<!--
## Before reporting
- Search for the bug on the issue tracker. Make sure to look at closed issues too!
-->
## Useful information to include:
- Minecraft version
- CC: Tweaked version
- Logs: These will be located in the `logs/` directory of your Minecraft instance. Please upload them as a gist or directly into this editor.
- Detailed reproduction steps: sometimes I can spot a bug pretty easily, but often it's much more obscure. The more information I have to help reproduce it, the quicker it'll get fixed.

33
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Bug report
description: Report some misbehaviour in the mod
labels: [ bug ]
body:
- type: dropdown
id: mc-version
attributes:
label: Minecraft Version
description: What version of Minecraft are you using?
options:
- 1.15.x
- 1.16.x
validations:
required: true
- type: input
id: version
attributes:
label: Version
description: "What version of CC: Tweaked are you using?"
placeholder: "e.g. 1.96.0"
validations:
required: true
- type: textarea
id: details
attributes:
label: Details
description: |
Description of the bug. Please include the following:
- Logs: These will be located in the `logs/` directory of your Minecraft
instance. Please upload them as a gist or directly into this editor.
- Detailed reproduction steps: sometimes I can spot a bug pretty easily,
but often it's much more obscure. The more information I have to help
reproduce it, the quicker it'll get fixed.

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: ComputerCraft Discord
url: https://discord.computercraft.cc
about: Get help on the ComputerCraft Discord.
- name: GitHub Discussions
url: https://github.com/SquidDev-CC/CC-Tweaked/discussions
about: Or ask questions on GitHub Discussions.

View File

@@ -0,0 +1,4 @@
---
name: Something else
about: An issue about something else.
---

View File

@@ -3,7 +3,7 @@ name: Build documentation
on:
push:
branches:
- mc-1.15.x
- mc-1.16.x
jobs:
make_doc:

22
.gitpod.yml Normal file
View File

@@ -0,0 +1,22 @@
image:
file: config/gitpod/Dockerfile
ports:
- port: 25565
onOpen: notify
vscode:
extensions:
- eamodio.gitlens
- github.vscode-pull-request-github
- ms-azuretools.vscode-docker
- redhat.java
- richardwillis.vscode-gradle
- vscjava.vscode-java-debug
- vscode.github
tasks:
- name: Setup pre-commit hool
init: pre-commit install --config config/pre-commit/config.yml --allow-missing-config
- name: Install npm packages
init: npm ci

View File

@@ -19,6 +19,10 @@ process. When building on Windows, Use `gradlew.bat` instead of `./gradlew`.
- **Clone the repository:** `git clone https://github.com/SquidDev-CC/CC-Tweaked.git && cd CC-Tweaked`
- **Setup Forge:** `./gradlew build`
- **Run Minecraft:** `./gradlew runClient` (or run the `GradleStart` class from your IDE).
- **Optionally:** For small PRs (especially those only touching Lua code), it may be easier to use GitPod, which
provides a pre-configured environment: [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-2b2b2b?logo=gitpod)](https://gitpod.io/#https://github.com/SquidDev-CC/CC-Tweaked/)
Do note you will need to download the mod after compiling to test.
If you want to run CC:T in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from `build/libs`.
These commands may take a few minutes to run the first time, as the environment is set up, but should be much faster

View File

@@ -1,6 +1,5 @@
buildscript {
repositories {
jcenter()
mavenCentral()
maven {
name = "forge"
@@ -8,9 +7,7 @@ buildscript {
}
}
dependencies {
classpath 'com.google.code.gson:gson:2.8.1'
classpath 'net.minecraftforge.gradle:ForgeGradle:4.1.9'
classpath 'net.sf.proguard:proguard-gradle:6.1.0beta2'
classpath 'net.minecraftforge.gradle:ForgeGradle:5.0.24'
}
}
@@ -18,10 +15,11 @@ plugins {
id "checkstyle"
id "jacoco"
id "maven-publish"
id "com.github.hierynomus.license" version "0.15.0"
id "com.github.hierynomus.license" version "0.16.1"
id "com.matthewprenger.cursegradle" version "1.4.0"
id "com.github.breadmoirai.github-release" version "2.2.12"
id "org.jetbrains.kotlin.jvm" version "1.3.72"
id "com.modrinth.minotaur" version "1.2.1"
}
apply plugin: 'net.minecraftforge.gradle'
@@ -31,15 +29,22 @@ version = mod_version
group = "org.squiddev"
archivesBaseName = "cc-tweaked-${mc_version}"
def javaVersion = JavaLanguageVersion.of(8)
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
languageVersion = javaVersion
}
withSourcesJar()
withJavadocJar()
}
tasks.withType(JavaExec).configureEach {
javaLauncher = javaToolchains.launcherFor {
languageVersion = javaVersion
}
}
minecraft {
runs {
client {
@@ -83,6 +88,7 @@ minecraft {
testServer {
workingDirectory project.file('test-files/server')
parent runs.server
arg "--nogui"
mods {
cctest {
@@ -92,9 +98,10 @@ minecraft {
}
}
mappings channel: 'official', version: project.mc_version
mappings channel: 'official', version: mc_version
accessTransformer file('src/main/resources/META-INF/accesstransformer.cfg')
accessTransformer file('src/test/resources/META-INF/accesstransformer.cfg')
}
sourceSets {
@@ -122,10 +129,11 @@ dependencies {
minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}"
compileOnly fg.deobf("mezz.jei:jei-1.15.2:6.0.0.3:api")
compileOnly fg.deobf("com.blamejared.crafttweaker:CraftTweaker-1.15.2:6.0.0.9")
compileOnly fg.deobf("mezz.jei:jei-1.16.5:7.7.0.104:api")
compileOnly fg.deobf("com.blamejared.crafttweaker:CraftTweaker-1.16.5:7.1.0.313")
compileOnly fg.deobf("commoble.morered:morered-1.16.5:2.1.1.0")
runtimeOnly fg.deobf("mezz.jei:jei-1.15.2:6.0.0.3")
runtimeOnly fg.deobf("mezz.jei:jei-1.16.5:7.7.0.104")
shade 'org.squiddev:Cobalt:0.5.2-SNAPSHOT'
@@ -137,7 +145,7 @@ dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-reflect:1.3.72'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8'
cctJavadoc 'cc.tweaked:cct-javadoc:1.4.0'
cctJavadoc 'cc.tweaked:cct-javadoc:1.4.1'
}
// Compile tasks
@@ -165,12 +173,12 @@ task luaJavadoc(type: Javadoc) {
jar {
manifest {
attributes(["Specification-Title": "computercraft",
"Specification-Vendor": "SquidDev",
"Specification-Version": "1",
"Implementation-Title": "CC: Tweaked",
"Implementation-Version": "${mod_version}",
"Implementation-Vendor" :"SquidDev",
attributes(["Specification-Title" : "computercraft",
"Specification-Vendor" : "SquidDev",
"Specification-Version" : "1",
"Implementation-Title" : "CC: Tweaked",
"Implementation-Version" : "${mod_version}",
"Implementation-Vendor" : "SquidDev",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")])
}
@@ -183,59 +191,6 @@ jar {
}
}
import java.nio.charset.StandardCharsets
import java.nio.file.*
import java.util.zip.*
import com.google.gson.GsonBuilder
import com.google.gson.JsonElement
import com.hierynomus.gradle.license.tasks.LicenseCheck
import com.hierynomus.gradle.license.tasks.LicenseFormat
import proguard.gradle.ProGuardTask
task proguard(type: ProGuardTask, dependsOn: jar) {
description "Removes unused shadowed classes from the jar"
group "compact"
injars jar.archivePath
outjars "${jar.archivePath.absolutePath.replace(".jar", "")}-min.jar"
// Add the main runtime jar and all non-shadowed dependencies
libraryjars "${System.getProperty('java.home')}/lib/rt.jar"
libraryjars "${System.getProperty('java.home')}/lib/jce.jar"
doFirst {
sourceSets.main.compileClasspath
.filter { !it.name.contains("Cobalt") }
.each { libraryjars it }
}
// We want to avoid as much obfuscation as possible. We're only doing this to shrink code size.
dontobfuscate; dontoptimize; keepattributes; keepparameternames
// Proguard will remove directories by default, but that breaks JarMount.
keepdirectories 'data/computercraft/lua**'
// Preserve ComputerCraft classes - we only want to strip shadowed files.
keep 'class dan200.computercraft.** { *; }'
// LWJGL and Apache bundle Java 9 versions, which is great, but rather breaks Proguard
dontwarn 'module-info'
dontwarn 'org.apache.**,org.lwjgl.**'
}
task proguardMove(dependsOn: proguard) {
description "Replace the original jar with the minified version"
group "compact"
doLast {
Files.move(
file("${jar.archivePath.absolutePath.replace(".jar", "")}-min.jar").toPath(),
file(jar.archivePath).toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}
}
processResources {
inputs.property "version", mod_version
inputs.property "mcversion", mc_version
@@ -249,18 +204,19 @@ processResources {
["git", "-C", projectDir, "log", "--format=tformat:%an%n%cn"].execute().text.split('\n').each {
if (!blacklist.contains(it)) contributors.add(it)
}
} catch(Exception e) {
} catch (Exception e) {
e.printStackTrace()
}
inputs.property "commithash", hash
duplicatesStrategy = DuplicatesStrategy.INCLUDE
from(sourceSets.main.resources.srcDirs) {
include 'META-INF/mods.toml'
include 'data/computercraft/lua/rom/help/credits.txt'
expand 'version': mod_version,
'mcversion': mc_version,
'gitcontributors': contributors.sort(false, String.CASE_INSENSITIVE_ORDER).join('\n')
'mcversion': mc_version,
'gitcontributors': contributors.sort(false, String.CASE_INSENSITIVE_ORDER).join('\n')
}
from(sourceSets.main.resources.srcDirs) {
@@ -269,51 +225,15 @@ processResources {
}
}
task compressJson(dependsOn: jar) {
group "compact"
description "Minifies all JSON files, stripping whitespace"
def jarPath = file(jar.archivePath)
def tempPath = File.createTempFile("input", ".jar", temporaryDir)
tempPath.deleteOnExit()
def gson = new GsonBuilder().create()
doLast {
// Copy over all files in the current jar to the new one, running json files from GSON. As pretty printing
// is turned off, they should be minified.
new ZipFile(jarPath).withCloseable { inJar ->
tempPath.getParentFile().mkdirs()
new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tempPath))).withCloseable { outJar ->
inJar.entries().each { entry ->
if(entry.directory) {
outJar.putNextEntry(entry)
} else if(!entry.name.endsWith(".json")) {
outJar.putNextEntry(entry)
inJar.getInputStream(entry).withCloseable { outJar << it }
} else {
ZipEntry newEntry = new ZipEntry(entry.name)
newEntry.setTime(entry.time)
outJar.putNextEntry(newEntry)
def element = inJar.getInputStream(entry).withCloseable { gson.fromJson(it.newReader("UTF8"), JsonElement.class) }
outJar.write(gson.toJson(element).getBytes(StandardCharsets.UTF_8))
}
}
}
}
// And replace the original jar again
Files.move(tempPath.toPath(), jarPath.toPath(), StandardCopyOption.REPLACE_EXISTING)
}
sourcesJar {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
assemble.dependsOn compressJson
// Web tasks
import com.hierynomus.gradle.license.tasks.LicenseCheck
import com.hierynomus.gradle.license.tasks.LicenseFormat
import org.apache.tools.ant.taskdefs.condition.Os
List<String> mkCommand(String command) {
@@ -376,8 +296,8 @@ test {
jacocoTestReport {
dependsOn('test')
reports {
xml.enabled true
html.enabled true
xml.required = true
html.required = true
}
}
@@ -412,8 +332,8 @@ gradle.projectsEvaluated {
}
task licenseAPI(type: LicenseCheck);
task licenseFormatAPI(type: LicenseFormat);
task licenseAPI(type: LicenseCheck)
task licenseFormatAPI(type: LicenseFormat)
[licenseAPI, licenseFormatAPI].forEach {
it.configure {
source = sourceSets.main.java
@@ -436,19 +356,15 @@ task setupServer(type: Copy) {
tasks.register('testInGame', JavaExec.class).configure {
it.group('test server')
it.description("Runs tests on a temporary Minecraft server.")
it.dependsOn(setupServer, 'prepareRunTestServer')
it.dependsOn(setupServer, 'prepareRunTestServer', 'cleanTestInGame')
// Copy from runTestServer. We do it in this slightly odd way as runTestServer
// isn't created until the task is configured (which is no good for us).
JavaExec exec = tasks.getByName('runTestServer')
it.setWorkingDir(exec.getWorkingDir())
it.setSystemProperties(exec.getSystemProperties())
it.setBootstrapClasspath(exec.getBootstrapClasspath())
exec.copyTo(it)
it.setClasspath(exec.getClasspath())
it.setMain(exec.getMain())
it.setEnvironment(exec.getEnvironment())
it.mainClass = exec.mainClass
it.setArgs(exec.getArgs())
it.setJvmArgs(exec.getJvmArgs())
it.systemProperty('forge.logging.console.level', 'info')
it.systemProperty('cctest.run', 'true')
@@ -490,31 +406,31 @@ task checkRelease {
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")
inputs.file("src/main/resources/data/computercraft/lua/rom/help/changelog.md")
inputs.file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.md")
doLast {
def ok = true
// Check we're targetting the current version
def whatsnew = new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt").readLines()
def whatsnew = new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/whatsnew.md").readLines()
if (whatsnew[0] != "New features in CC: Tweaked $mod_version") {
ok = false
project.logger.error("Expected `whatsnew.txt' to target $mod_version.")
project.logger.error("Expected `whatsnew.md' 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")
project.logger.error("Must mention the changelog in whatsnew.md")
} else {
whatsnew = whatsnew.getAt(0 ..< idx)
whatsnew = whatsnew.getAt(0..<idx)
}
// Check whatsnew and changelog match.
def versionChangelog = "# " + whatsnew.join("\n")
def changelog = new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/changelog.txt").getText()
def changelog = new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/changelog.md").getText()
if (!changelog.startsWith(versionChangelog)) {
ok = false
project.logger.error("whatsnew and changelog are not in sync")
@@ -535,9 +451,32 @@ curseforge {
relations {
incompatible "computercraft"
}
addGameVersion "${mc_version}"
}
}
import com.modrinth.minotaur.TaskModrinthUpload
tasks.register('publishModrinth', TaskModrinthUpload.class).configure {
dependsOn('assemble', 'reobfJar')
onlyIf {
project.hasProperty('modrinthApiKey')
}
token = project.hasProperty('modrinthApiKey') ? project.getProperty('modrinthApiKey') : ''
projectId = 'gu7yAYhd'
versionNumber = "${project.mc_version}-${project.mod_version}"
uploadFile = jar
addGameVersion(project.mc_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})."
addLoader('forge')
}
tasks.withType(GenerateModuleMetadata) {
// We can't generate metadata as that includes Forge as a dependency.
enabled = false
}
publishing {
publications {
maven(MavenPublication) {
@@ -563,6 +502,8 @@ publishing {
url = 'https://github.com/SquidDev-CC/CC-Tweaked/blob/mc-1.15.x/LICENSE'
}
}
withXml { asNode().remove(asNode().get("dependencies")) }
}
}
}
@@ -597,7 +538,7 @@ githubRelease {
tagName "v${mc_version}-${mod_version}"
releaseName "[${mc_version}] ${mod_version}"
body.set(project.provider({
"## " + new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt")
"## " + new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/whatsnew.md")
.readLines()
.takeWhile { it != 'Type "help changelog" to see the full version history.' }
.join("\n").trim()
@@ -605,10 +546,10 @@ githubRelease {
prerelease false
}
def uploadTasks = ["publish", "curseforge", "githubRelease"]
def uploadTasks = ["publish", "curseforge", "publishModrinth", "githubRelease"]
uploadTasks.forEach { tasks.getByName(it).dependsOn checkRelease }
task uploadAll(dependsOn: uploadTasks) {
group "upload"
description "Uploads to all repositories (Maven, Curse, GitHub release)"
description "Uploads to all repositories (Maven, Curse, Modrinth, GitHub release)"
}

View File

@@ -7,9 +7,6 @@
<suppress checks="StaticVariableName" files=".*[\\/]ComputerCraft.java" />
<suppress checks="StaticVariableName" files=".*[\\/]ComputerCraftAPI.java" />
<!-- Do not check for missing package Javadoc. -->
<suppress checks="JavadocStyle" files=".*[\\/]package-info.java" />
<!-- The commands API is documented in Lua. -->
<suppress checks="SummaryJavadocCheck" files=".*[\\/]CommandAPI.java" />
</suppressions>

8
config/gitpod/Dockerfile Normal file
View File

@@ -0,0 +1,8 @@
FROM gitpod/workspace-base
USER gitpod
RUN sudo apt-get -q update \
&& sudo apt-get install -yq openjdk-8-jdk openjdk-16-jdk python3-pip npm \
&& sudo pip3 install pre-commit \
&& sudo update-java-alternatives --set java-1.8.0-openjdk-amd64

View File

@@ -77,7 +77,7 @@ the prompt.
@treturn string The text typed in.
@see cc.completion For functions to help with completion.
@usage Read an string and echo it back to the user
@usage Read a string and echo it back to the user
write("> ")
local msg = read()
@@ -101,6 +101,9 @@ the prompt.
write("> ")
local msg = read(nil, history, function(text) return completion.choice(text, choices) end, "app")
print(msg)
@changed 1.74 Added `completeFn` parameter.
@changed 1.80pr1 Added `default` parameter.
]]
function read(replaceChar, history, completeFn, default) end
@@ -108,6 +111,7 @@ function read(replaceChar, history, completeFn, default) end
--
-- For example, `ComputerCraft 1.93.0 (Minecraft 1.15.2)`.
-- @usage _HOST
-- @since 1.76
_HOST = _HOST
--[[- The default computer settings as defined in the ComputerCraft
@@ -121,5 +125,6 @@ An example value to disable autocompletion:
shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false
@usage _CC_DEFAULT_SETTINGS
@since 1.77
]]
_CC_DEFAULT_SETTINGS = _CC_DEFAULT_SETTINGS

View File

@@ -2,9 +2,6 @@
--[[- Loads the given API into the global environment.
**Warning** This function is deprecated. Use of this function will pollute the
global table, use @{require} instead.
This function loads and executes the file at the given path, and all global
variables and functions exported by it will by available through the use of
`myAPI.<function name>`, where `myAPI` is the base name of the API file.
@@ -12,7 +9,10 @@ variables and functions exported by it will by available through the use of
@tparam string path The path of the API to load.
@treturn boolean Whether or not the API was successfully loaded.
@deprecated Use @{require}.
@deprecated When possible it's best to avoid using this function. It pollutes
the global table and can mask errors.
@{require} should be used to load libraries instead.
]]
function loadAPI(path) end
@@ -21,7 +21,7 @@ function loadAPI(path) end
-- This effectively removes the specified table from `_G`.
--
-- @tparam string name The name of the API to unload.
-- @deprecated Use @{require}.
-- @deprecated See @{os.loadAPI} for why.
function unloadAPI(name) end
--[[- Pause execution of the current thread and waits for any events matching

View File

@@ -1,7 +1,7 @@
--[[- Craft a recipe based on the turtle's inventory.
The turtle's inventory should set up like a crafting grid. For instance, to
craft sticks, slots 1 and 5 should contain sticks. _All_ other slots should be
craft sticks, slots 1 and 5 should contain planks. _All_ other slots should be
empty, including those outside the crafting "grid".
@tparam[opt=64] number limit The maximum number of crafting steps to run.

View File

@@ -1,6 +1,7 @@
# Mod properties
mod_version=1.96.0
mod_version=1.98.1
# Minecraft properties (update mods.toml when changing)
mc_version=1.15.2
forge_version=31.1.41
mc_version=1.16.5
forge_version=36.1.0
# NO SERIOUSLY, UPDATE mods.toml WHEN CHANGING

View File

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

232
package-lock.json generated
View File

@@ -1,8 +1,238 @@
{
"name": "tweaked.cc",
"version": "1.0.0",
"lockfileVersion": 1,
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "tweaked.cc",
"version": "1.0.0",
"license": "BSD-3-Clause",
"dependencies": {
"preact": "^10.5.5",
"tslib": "^2.0.3"
},
"devDependencies": {
"@rollup/plugin-typescript": "^6.1.0",
"requirejs": "^2.3.6",
"rollup": "^2.33.1",
"terser": "^5.3.8",
"typescript": "^4.0.5"
}
},
"node_modules/@rollup/plugin-typescript": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-6.1.0.tgz",
"integrity": "sha512-hJxaiE6WyNOsK+fZpbFh9CUijZYqPQuAOWO5khaGTUkM8DYNNyA2TDlgamecE+qLOG1G1+CwbWMAx3rbqpp6xQ==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^3.1.0",
"resolve": "^1.17.0"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/@rollup/pluginutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
"integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
"dev": true,
"dependencies": {
"@types/estree": "0.0.39",
"estree-walker": "^1.0.1",
"picomatch": "^2.2.2"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/@types/estree": {
"version": "0.0.39",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
"dev": true
},
"node_modules/buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
"node_modules/estree-walker": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
"dev": true
},
"node_modules/fsevents": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/is-core-module": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz",
"integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==",
"dev": true,
"dependencies": {
"has": "^1.0.3"
}
},
"node_modules/path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"node_modules/picomatch": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
"dev": true,
"engines": {
"node": ">=8.6"
}
},
"node_modules/preact": {
"version": "10.5.5",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.5.5.tgz",
"integrity": "sha512-5ONLNH1SXMzzbQoExZX4TELemNt+TEDb622xXFNfZngjjM9qtrzseJt+EfiUu4TZ6EJ95X5sE1ES4yqHFSIdhg=="
},
"node_modules/requirejs": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
"integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==",
"dev": true,
"bin": {
"r_js": "bin/r.js",
"r.js": "bin/r.js"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/resolve": {
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz",
"integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==",
"dev": true,
"dependencies": {
"is-core-module": "^2.0.0",
"path-parse": "^1.0.6"
}
},
"node_modules/rollup": {
"version": "2.33.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.33.1.tgz",
"integrity": "sha512-uY4O/IoL9oNW8MMcbA5hcOaz6tZTMIh7qJHx/tzIJm+n1wLoY38BLn6fuy7DhR57oNFLMbDQtDeJoFURt5933w==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=10.0.0"
},
"optionalDependencies": {
"fsevents": "~2.1.2"
}
},
"node_modules/source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true,
"engines": {
"node": ">= 8"
}
},
"node_modules/source-map-support": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/source-map-support/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/terser": {
"version": "5.3.8",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.3.8.tgz",
"integrity": "sha512-zVotuHoIfnYjtlurOouTazciEfL7V38QMAOhGqpXDEg6yT13cF4+fEP9b0rrCEQTn+tT46uxgFsTZzhygk+CzQ==",
"dev": true,
"dependencies": {
"commander": "^2.20.0",
"source-map": "~0.7.2",
"source-map-support": "~0.5.19"
},
"bin": {
"terser": "bin/terser"
},
"engines": {
"node": ">=10"
}
},
"node_modules/tslib": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
},
"node_modules/typescript": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
"integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
}
},
"dependencies": {
"@rollup/plugin-typescript": {
"version": "6.1.0",

View File

@@ -0,0 +1,10 @@
{
"replace": false,
"values": [
"computercraft:computer_advanced",
"computercraft:turtle_advanced",
"computercraft:wireless_modem_advanced",
"computercraft:pocket_computer_advanced",
"computercraft:monitor_advanced"
]
}

View File

@@ -36,7 +36,6 @@ public final class ComputerCraft
public static int maximumFilesOpen = 128;
public static boolean disableLua51Features = false;
public static String defaultComputerSettings = "";
public static boolean debugEnable = true;
public static boolean logComputerErrors = true;
public static boolean commandRequireCreative = true;
@@ -53,6 +52,8 @@ public final class ComputerCraft
public static int httpMaxRequests = 16;
public static int httpMaxWebsockets = 4;
public static int httpDownloadBandwidth = 32 * 1024 * 1024;
public static int httpUploadBandwidth = 32 * 1024 * 1024;
public static boolean enableCommandBlock = false;
public static int modemRange = 64;

View File

@@ -58,7 +58,7 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
public static InputStream getResourceFile( String domain, String subPath )
{
IReloadableResourceManager manager = ServerLifecycleHooks.getCurrentServer().getResources();
IReloadableResourceManager manager = (IReloadableResourceManager) ServerLifecycleHooks.getCurrentServer().getDataPackRegistries().getResourceManager();
try
{
return manager.getResource( new ResourceLocation( domain, subPath ) ).getInputStream();
@@ -101,7 +101,7 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
@Override
public IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath )
{
IReloadableResourceManager manager = ServerLifecycleHooks.getCurrentServer().getResources();
IReloadableResourceManager manager = (IReloadableResourceManager) ServerLifecycleHooks.getCurrentServer().getDataPackRegistries().getResourceManager();
ResourceMount mount = ResourceMount.get( domain, subPath, manager );
return mount.exists( "" ) ? mount : null;
}

View File

@@ -6,11 +6,11 @@
package dan200.computercraft.api.client;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.TransformationMatrix;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ModelManager;
import net.minecraft.client.renderer.model.ModelResourceLocation;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.vector.TransformationMatrix;
import javax.annotation.Nonnull;
import java.util.Objects;
@@ -32,7 +32,7 @@ public final class TransformedModel
public TransformedModel( @Nonnull IBakedModel model )
{
this.model = Objects.requireNonNull( model );
this.matrix = TransformationMatrix.identity();
matrix = TransformationMatrix.identity();
}
public static TransformedModel of( @Nonnull ModelResourceLocation location )

View File

@@ -30,7 +30,7 @@ public class FileOperationException extends IOException
public FileOperationException( @Nonnull String message )
{
super( Objects.requireNonNull( message, "message cannot be null" ) );
this.filename = null;
filename = null;
}
@Nullable

View File

@@ -27,4 +27,18 @@ public interface ILuaContext
* @see LuaFunction#mainThread() To run functions on the main thread and return their results synchronously.
*/
long issueMainThreadTask( @Nonnull ILuaTask task ) throws LuaException;
/**
* Queue a task to be executed on the main server thread at the beginning of next tick, waiting for it to complete.
* This should be used when you need to interact with the world in a thread-safe manner.
*
* Note that the return values of your task are handled as events, meaning more complex objects such as maps or
* {@link IDynamicLuaObject} will not preserve their identities.
*
* @param task The task to execute on the main thread.
* @return The objects returned by {@code task}.
* @throws LuaException If the task could not be queued, or if the task threw an exception.
*/
@Nonnull
MethodResult executeMainThreadTask( @Nonnull ILuaTask task ) throws LuaException;
}

View File

@@ -19,14 +19,14 @@ public class LuaException extends Exception
public LuaException( @Nullable String message )
{
super( message );
this.hasLevel = false;
this.level = 1;
hasLevel = false;
level = 1;
}
public LuaException( @Nullable String message, int level )
{
super( message );
this.hasLevel = true;
hasLevel = true;
this.level = level;
}

View File

@@ -30,14 +30,14 @@ public final class MethodResult
private MethodResult( Object[] arguments, ILuaCallback callback )
{
this.result = arguments;
result = arguments;
this.callback = callback;
this.adjust = 0;
adjust = 0;
}
private MethodResult( Object[] arguments, ILuaCallback callback, int adjust )
{
this.result = arguments;
result = arguments;
this.callback = callback;
this.adjust = adjust;
}

View File

@@ -5,7 +5,7 @@
*/
package dan200.computercraft.api.network;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
@@ -29,7 +29,7 @@ public interface IPacketReceiver
* @return The receiver's position.
*/
@Nonnull
Vec3d getPosition();
Vector3d getPosition();
/**
* Get the maximum distance this receiver can send and receive messages.

View File

@@ -5,7 +5,7 @@
*/
package dan200.computercraft.api.network;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
@@ -29,7 +29,7 @@ public interface IPacketSender
* @return The sender's position.
*/
@Nonnull
Vec3d getPosition();
Vector3d getPosition();
/**
* Get some sort of identification string for this sender. This does not strictly need to be unique, but you

View File

@@ -13,7 +13,7 @@ import net.minecraft.inventory.IInventory;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import net.minecraftforge.items.IItemHandlerModifiable;
@@ -67,7 +67,7 @@ public interface ITurtleAccess
* @see #getVisualYaw(float)
*/
@Nonnull
Vec3d getVisualPosition( float f );
Vector3d getVisualPosition( float f );
/**
* Returns the yaw the turtle is facing when it is rendered.

View File

@@ -0,0 +1,40 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ClientPlayerNetworkEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public class ClientHooks
{
@SubscribeEvent
public static void onWorldUnload( WorldEvent.Unload event )
{
if( event.getWorld().isClientSide() )
{
ClientMonitor.destroyAll();
SoundManager.reset();
}
}
@SubscribeEvent
public static void onLogIn( ClientPlayerNetworkEvent.LoggedInEvent event )
{
ComputerCraft.clientComputerRegistry.reset();
}
@SubscribeEvent
public static void onLogOut( ClientPlayerNetworkEvent.LoggedOutEvent event )
{
ComputerCraft.clientComputerRegistry.reset();
}
}

View File

@@ -6,31 +6,39 @@
package dan200.computercraft.client;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.*;
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
import dan200.computercraft.client.render.TurtleModelLoader;
import dan200.computercraft.client.render.TurtlePlayerRenderer;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.media.items.ItemDisk;
import dan200.computercraft.shared.media.items.ItemTreasureDisk;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.util.Colour;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.IUnbakedModel;
import net.minecraft.client.gui.ScreenManager;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.RenderTypeLookup;
import net.minecraft.client.renderer.model.ModelResourceLocation;
import net.minecraft.inventory.container.PlayerContainer;
import net.minecraft.item.IItemPropertyGetter;
import net.minecraft.item.Item;
import net.minecraft.item.ItemModelsProperties;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ColorHandlerEvent;
import net.minecraftforge.client.event.ModelBakeEvent;
import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.client.model.SimpleModelTransform;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.client.registry.RenderingRegistry;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import java.util.HashSet;
import java.util.Map;
import java.util.function.Supplier;
/**
* Registers textures and models for items.
@@ -39,6 +47,7 @@ import java.util.Map;
public final class ClientRegistry
{
private static final String[] EXTRA_MODELS = new String[] {
// Turtle upgrades
"turtle_modem_normal_off_left",
"turtle_modem_normal_on_left",
"turtle_modem_normal_off_right",
@@ -54,56 +63,20 @@ public final class ClientRegistry
"turtle_speaker_upgrade_left",
"turtle_speaker_upgrade_right",
// Turtle block renderer
"turtle_colour",
"turtle_elf_overlay",
};
private static final String[] EXTRA_TEXTURES = new String[] {
// TODO: Gather these automatically from the model. Sadly the model loader isn't available
// when stitching textures.
"block/turtle_colour",
"block/turtle_elf_overlay",
"block/turtle_crafty_face",
"block/turtle_speaker_face",
};
private ClientRegistry() {}
@SubscribeEvent
public static void registerModels( ModelRegistryEvent event )
{
ModelLoaderRegistry.registerLoader( new ResourceLocation( ComputerCraft.MOD_ID, "turtle" ), TurtleModelLoader.INSTANCE );
}
@SubscribeEvent
public static void onTextureStitchEvent( TextureStitchEvent.Pre event )
{
if( !event.getMap().location().equals( PlayerContainer.BLOCK_ATLAS ) ) return;
for( String extra : EXTRA_TEXTURES )
for( String model : EXTRA_MODELS )
{
event.addSprite( new ResourceLocation( ComputerCraft.MOD_ID, extra ) );
}
}
@SubscribeEvent
public static void onModelBakeEvent( ModelBakeEvent event )
{
// Load all extra models
ModelLoader loader = event.getModelLoader();
Map<ResourceLocation, IBakedModel> registry = event.getModelRegistry();
for( String modelName : EXTRA_MODELS )
{
ResourceLocation location = new ResourceLocation( ComputerCraft.MOD_ID, "item/" + modelName );
IUnbakedModel model = loader.getModel( location );
model.getMaterials( loader::getModel, new HashSet<>() );
IBakedModel baked = model.bake( loader, ModelLoader.defaultTextureGetter(), SimpleModelTransform.IDENTITY, location );
if( baked != null )
{
registry.put( new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, modelName ), "inventory" ), baked );
}
ModelLoader.addSpecialModel( new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, model ), "inventory" ) );
}
}
@@ -148,4 +121,61 @@ public final class ClientRegistry
Registry.ModBlocks.TURTLE_NORMAL.get(), Registry.ModBlocks.TURTLE_ADVANCED.get()
);
}
@SubscribeEvent
public static void setupClient( FMLClientSetupEvent event )
{
registerContainers();
// While turtles themselves are not transparent, their upgrades may be.
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.TURTLE_NORMAL.get(), RenderType.translucent() );
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.TURTLE_ADVANCED.get(), RenderType.translucent() );
// Monitors' textures have transparent fronts and so count as cutouts.
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.MONITOR_NORMAL.get(), RenderType.cutout() );
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.MONITOR_ADVANCED.get(), RenderType.cutout() );
// Setup TESRs
net.minecraftforge.fml.client.registry.ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.MONITOR_NORMAL.get(), TileEntityMonitorRenderer::new );
net.minecraftforge.fml.client.registry.ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.MONITOR_ADVANCED.get(), TileEntityMonitorRenderer::new );
net.minecraftforge.fml.client.registry.ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_NORMAL.get(), TileEntityTurtleRenderer::new );
net.minecraftforge.fml.client.registry.ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_ADVANCED.get(), TileEntityTurtleRenderer::new );
RenderingRegistry.registerEntityRenderingHandler( Registry.ModEntities.TURTLE_PLAYER.get(), TurtlePlayerRenderer::new );
registerItemProperty( "state",
( stack, world, player ) -> ItemPocketComputer.getState( stack ).ordinal(),
Registry.ModItems.POCKET_COMPUTER_NORMAL, Registry.ModItems.POCKET_COMPUTER_ADVANCED
);
registerItemProperty( "coloured",
( stack, world, player ) -> IColouredItem.getColourBasic( stack ) != -1 ? 1 : 0,
Registry.ModItems.POCKET_COMPUTER_NORMAL, Registry.ModItems.POCKET_COMPUTER_ADVANCED
);
}
@SafeVarargs
private static void registerItemProperty( String name, IItemPropertyGetter getter, Supplier<? extends Item>... items )
{
ResourceLocation id = new ResourceLocation( ComputerCraft.MOD_ID, name );
for( Supplier<? extends Item> item : items )
{
ItemModelsProperties.register( item.get(), id, getter );
}
}
private static void registerContainers()
{
// My IDE doesn't think so, but we do actually need these generics.
ScreenManager.<ContainerComputer, GuiComputer<ContainerComputer>>register( Registry.ModContainers.COMPUTER.get(), GuiComputer::create );
ScreenManager.<ContainerPocketComputer, GuiComputer<ContainerPocketComputer>>register( Registry.ModContainers.POCKET_COMPUTER.get(), GuiComputer::createPocket );
ScreenManager.register( Registry.ModContainers.TURTLE.get(), GuiTurtle::new );
ScreenManager.register( Registry.ModContainers.PRINTER.get(), GuiPrinter::new );
ScreenManager.register( Registry.ModContainers.DISK_DRIVE.get(), GuiDiskDrive::new );
ScreenManager.register( Registry.ModContainers.PRINTOUT.get(), GuiPrintout::new );
ScreenManager.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register( Registry.ModContainers.VIEW_COMPUTER.get(), GuiComputer::createView );
}
}

View File

@@ -12,14 +12,12 @@ import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.NewChatGui;
import net.minecraft.client.gui.RenderComponentsUtil;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextFormatting;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.Nullable;
import java.util.List;
public class ClientTableFormatter implements TableFormatter
{
@@ -57,7 +55,7 @@ public class ClientTableFormatter implements TableFormatter
@Override
public int getWidth( ITextComponent component )
{
return renderer().width( component.getColoredString() );
return renderer().width( component );
}
@Override
@@ -66,10 +64,11 @@ public class ClientTableFormatter implements TableFormatter
Minecraft mc = Minecraft.getInstance();
NewChatGui chat = mc.gui.getChat();
// Trim the text if it goes over the allowed length
int maxWidth = MathHelper.floor( chat.getWidth() / chat.getScale() );
List<ITextComponent> list = RenderComponentsUtil.wrapComponents( component, maxWidth, mc.font, false, false );
if( !list.isEmpty() ) chat.addMessage( list.get( 0 ), id );
// TODO: Trim the text if it goes over the allowed length
// int maxWidth = MathHelper.floor( chat.getChatWidth() / chat.getScale() );
// List<ITextProperties> list = RenderComponentsUtil.wrapComponents( component, maxWidth, mc.fontRenderer );
// if( !list.isEmpty() ) chat.printChatMessageWithOptionalDeletion( list.get( 0 ), id );
chat.addMessage( component, id );
}
@Override

View File

@@ -0,0 +1,84 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.ISound;
import net.minecraft.client.audio.ITickableSound;
import net.minecraft.client.audio.LocatableSound;
import net.minecraft.client.audio.SoundHandler;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.vector.Vector3d;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class SoundManager
{
private static final Map<UUID, MoveableSound> sounds = new HashMap<>();
public static void playSound( UUID source, Vector3d position, SoundEvent event, float volume, float pitch )
{
SoundHandler soundManager = Minecraft.getInstance().getSoundManager();
MoveableSound oldSound = sounds.get( source );
if( oldSound != null ) soundManager.stop( oldSound );
MoveableSound newSound = new MoveableSound( event, position, volume, pitch );
sounds.put( source, newSound );
soundManager.play( newSound );
}
public static void stopSound( UUID source )
{
ISound sound = sounds.remove( source );
if( sound == null ) return;
Minecraft.getInstance().getSoundManager().stop( sound );
}
public static void moveSound( UUID source, Vector3d position )
{
MoveableSound sound = sounds.get( source );
if( sound != null ) sound.setPosition( position );
}
public static void reset()
{
sounds.clear();
}
private static class MoveableSound extends LocatableSound implements ITickableSound
{
protected MoveableSound( SoundEvent sound, Vector3d position, float volume, float pitch )
{
super( sound, SoundCategory.RECORDS );
setPosition( position );
this.volume = volume;
this.pitch = pitch;
}
void setPosition( Vector3d position )
{
x = (float) position.x();
y = (float) position.y();
z = (float) position.z();
}
@Override
public boolean isStopped()
{
return false;
}
@Override
public void tick()
{
}
}
}

View File

@@ -0,0 +1,208 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.matrix.MatrixStack;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.computer.upload.FileUpload;
import dan200.computercraft.shared.computer.upload.UploadResult;
import dan200.computercraft.shared.network.NetworkHandler;
import dan200.computercraft.shared.network.server.ContinueUploadMessage;
import dan200.computercraft.shared.network.server.UploadFileMessage;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public abstract class ComputerScreenBase<T extends ContainerComputerBase> extends ContainerScreen<T>
{
private static final ITextComponent OK = new TranslationTextComponent( "gui.ok" );
private static final ITextComponent CANCEL = new TranslationTextComponent( "gui.cancel" );
private static final ITextComponent OVERWRITE = new TranslationTextComponent( "gui.computercraft.upload.overwrite_button" );
protected WidgetTerminal terminal;
protected final ClientComputer computer;
protected final ComputerFamily family;
protected final int sidebarYOffset;
public ComputerScreenBase( T container, PlayerInventory player, ITextComponent title, int sidebarYOffset )
{
super( container, player, title );
computer = (ClientComputer) container.getComputer();
family = container.getFamily();
this.sidebarYOffset = sidebarYOffset;
}
protected abstract WidgetTerminal createTerminal();
@Override
protected final void init()
{
super.init();
minecraft.keyboardHandler.setSendRepeatsToGui( true );
terminal = addButton( createTerminal() );
ComputerSidebar.addButtons( this, computer, this::addButton, leftPos, topPos + sidebarYOffset );
setFocused( terminal );
}
@Override
public final void removed()
{
super.removed();
minecraft.keyboardHandler.setSendRepeatsToGui( false );
}
@Override
public final void tick()
{
super.tick();
terminal.update();
}
@Override
public final boolean keyPressed( int key, int scancode, int modifiers )
{
// Forward the tab key to the terminal, rather than moving between controls.
if( key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminal )
{
return getFocused().keyPressed( key, scancode, modifiers );
}
return super.keyPressed( key, scancode, modifiers );
}
@Override
public final void render( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks )
{
renderBackground( stack );
super.render( stack, mouseX, mouseY, partialTicks );
renderTooltip( stack, mouseX, mouseY );
}
@Override
public final boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
{
return (getFocused() != null && getFocused().mouseDragged( x, y, button, deltaX, deltaY ))
|| super.mouseDragged( x, y, button, deltaX, deltaY );
}
@Override
protected void renderLabels( @Nonnull MatrixStack transform, int mouseX, int mouseY )
{
// Skip rendering labels.
}
@Override
public void onFilesDrop( @Nonnull List<Path> files )
{
if( files.isEmpty() ) return;
if( computer == null || !computer.isOn() )
{
alert( UploadResult.FAILED_TITLE, UploadResult.COMPUTER_OFF_MSG );
return;
}
long size = 0;
List<FileUpload> toUpload = new ArrayList<>();
for( Path file : files )
{
// TODO: Recurse directories? If so, we probably want to shunt this off-thread.
if( !Files.isRegularFile( file ) ) continue;
try( SeekableByteChannel sbc = Files.newByteChannel( file ) )
{
long fileSize = sbc.size();
if( fileSize > UploadFileMessage.MAX_SIZE || (size += fileSize) >= UploadFileMessage.MAX_SIZE )
{
alert( UploadResult.FAILED_TITLE, UploadResult.TOO_MUCH_MSG );
return;
}
ByteBuffer buffer = ByteBuffer.allocateDirect( (int) fileSize );
sbc.read( buffer );
buffer.flip();
toUpload.add( new FileUpload( file.getFileName().toString(), buffer ) );
}
catch( IOException e )
{
ComputerCraft.log.error( "Failed uploading files", e );
alert( UploadResult.FAILED_TITLE, new TranslationTextComponent( "computercraft.gui.upload.failed.generic", e.getMessage() ) );
}
}
if( toUpload.size() > 0 )
{
NetworkHandler.sendToServer( new UploadFileMessage( computer.getInstanceID(), toUpload ) );
}
}
public void uploadResult( UploadResult result, ITextComponent message )
{
switch( result )
{
case SUCCESS:
alert( UploadResult.SUCCESS_TITLE, message );
break;
case ERROR:
alert( UploadResult.FAILED_TITLE, message );
break;
case CONFIRM_OVERWRITE:
OptionScreen.show(
minecraft, UploadResult.UPLOAD_OVERWRITE, message,
Arrays.asList(
OptionScreen.newButton( CANCEL, b -> cancelUpload() ),
OptionScreen.newButton( OVERWRITE, b -> continueUpload() )
),
this::cancelUpload
);
break;
}
}
private void continueUpload()
{
if( minecraft.screen instanceof OptionScreen ) ((OptionScreen) minecraft.screen).disable();
NetworkHandler.sendToServer( new ContinueUploadMessage( computer.getInstanceID(), true ) );
}
private void cancelUpload()
{
minecraft.setScreen( this );
NetworkHandler.sendToServer( new ContinueUploadMessage( computer.getInstanceID(), false ) );
}
private void alert( ITextComponent title, ITextComponent message )
{
OptionScreen.show( minecraft, title, message,
Collections.singletonList( OptionScreen.newButton( OK, b -> minecraft.setScreen( this ) ) ),
() -> minecraft.setScreen( this )
);
}
}

View File

@@ -13,10 +13,14 @@ import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.*;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.RenderState;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.TransformationMatrix;
import org.lwjgl.opengl.GL11;
import javax.annotation.Nonnull;

View File

@@ -5,45 +5,38 @@
*/
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
import dan200.computercraft.client.gui.widgets.WidgetWrapper;
import dan200.computercraft.client.render.ComputerBorderRenderer;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.text.ITextComponent;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nonnull;
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
public final class GuiComputer<T extends ContainerComputerBase> extends ContainerScreen<T>
public final class GuiComputer<T extends ContainerComputerBase> extends ComputerScreenBase<T>
{
private final ComputerFamily family;
private final ClientComputer computer;
private final int termWidth;
private final int termHeight;
private WidgetTerminal terminal;
private WidgetWrapper terminalWrapper;
private GuiComputer(
T container, PlayerInventory player, ITextComponent title, int termWidth, int termHeight
)
{
super( container, player, title );
family = container.getFamily();
computer = (ClientComputer) container.getComputer();
super( container, player, title, BORDER );
this.termWidth = termWidth;
this.termHeight = termHeight;
terminal = null;
imageWidth = WidgetTerminal.getWidth( termWidth ) + BORDER * 2 + ComputerSidebar.WIDTH;
imageHeight = WidgetTerminal.getHeight( termHeight ) + BORDER * 2;
}
public static GuiComputer<ContainerComputer> create( ContainerComputer container, PlayerInventory inventory, ITextComponent component )
@@ -70,82 +63,21 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Containe
);
}
@Override
protected void init()
protected WidgetTerminal createTerminal()
{
minecraft.keyboardHandler.setSendRepeatsToGui( true );
int termPxWidth = termWidth * FixedWidthFontRenderer.FONT_WIDTH;
int termPxHeight = termHeight * FixedWidthFontRenderer.FONT_HEIGHT;
imageWidth = termPxWidth + MARGIN * 2 + BORDER * 2;
imageHeight = termPxHeight + MARGIN * 2 + BORDER * 2;
super.init();
terminal = new WidgetTerminal( minecraft, () -> computer, termWidth, termHeight, MARGIN, MARGIN, MARGIN, MARGIN );
terminalWrapper = new WidgetWrapper( terminal, MARGIN + BORDER + leftPos, MARGIN + BORDER + topPos, termPxWidth, termPxHeight );
children.add( terminalWrapper );
setFocused( terminalWrapper );
}
@Override
public void removed()
{
super.removed();
children.remove( terminal );
terminal = null;
minecraft.keyboardHandler.setSendRepeatsToGui( false );
}
@Override
public void tick()
{
super.tick();
terminal.update();
}
@Override
public boolean keyPressed( int key, int scancode, int modifiers )
{
// Forward the tab key to the terminal, rather than moving between controls.
if( key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminalWrapper )
{
return getFocused().keyPressed( key, scancode, modifiers );
}
return super.keyPressed( key, scancode, modifiers );
}
@Override
public void renderBg( float partialTicks, int mouseX, int mouseY )
{
// Draw terminal
terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() );
// Draw a border around the terminal
RenderSystem.color4f( 1, 1, 1, 1 );
minecraft.getTextureManager().bind( ComputerBorderRenderer.getTexture( family ) );
ComputerBorderRenderer.render(
terminalWrapper.getX() - MARGIN, terminalWrapper.getY() - MARGIN, getBlitOffset(),
terminalWrapper.getWidth() + MARGIN * 2, terminalWrapper.getHeight() + MARGIN * 2
return new WidgetTerminal( computer,
leftPos + ComputerSidebar.WIDTH + BORDER, topPos + BORDER, termWidth, termHeight
);
}
@Override
public void render( int mouseX, int mouseY, float partialTicks )
public void renderBg( @Nonnull MatrixStack stack, float partialTicks, int mouseX, int mouseY )
{
renderBackground();
super.render( mouseX, mouseY, partialTicks );
renderTooltip( mouseX, mouseY );
}
@Override
public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
{
return (getFocused() != null && getFocused().mouseDragged( x, y, button, deltaX, deltaY ))
|| super.mouseDragged( x, y, button, deltaX, deltaY );
// Draw a border around the terminal
RenderSystem.color4f( 1, 1, 1, 1 );
minecraft.getTextureManager().bind( ComputerBorderRenderer.getTexture( family ) );
ComputerBorderRenderer.render( terminal.x, terminal.y, getBlitOffset(), terminal.getWidth(), terminal.getHeight() );
ComputerSidebar.renderBackground( stack, leftPos, topPos + sidebarYOffset );
}
}

View File

@@ -5,6 +5,7 @@
*/
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
@@ -12,6 +13,8 @@ import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import javax.annotation.Nonnull;
public class GuiDiskDrive extends ContainerScreen<ContainerDiskDrive>
{
private static final ResourceLocation BACKGROUND = new ResourceLocation( "computercraft", "textures/gui/disk_drive.png" );
@@ -22,26 +25,18 @@ public class GuiDiskDrive extends ContainerScreen<ContainerDiskDrive>
}
@Override
protected void renderLabels( int mouseX, int mouseY )
{
String title = this.title.getColoredString();
font.draw( title, (imageWidth - font.width( title )) / 2.0f, 6, 0x404040 );
font.draw( title, 8, imageHeight - 96 + 2, 0x404040 );
}
@Override
protected void renderBg( float partialTicks, int mouseX, int mouseY )
protected void renderBg( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY )
{
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
minecraft.getTextureManager().bind( BACKGROUND );
blit( leftPos, topPos, 0, 0, imageWidth, imageHeight );
blit( transform, leftPos, topPos, 0, 0, imageWidth, imageHeight );
}
@Override
public void render( int mouseX, int mouseY, float partialTicks )
public void render( @Nonnull MatrixStack transform, int mouseX, int mouseY, float partialTicks )
{
renderBackground();
super.render( mouseX, mouseY, partialTicks );
renderTooltip( mouseX, mouseY );
renderBackground( transform );
super.render( transform, mouseX, mouseY, partialTicks );
renderTooltip( transform, mouseX, mouseY );
}
}

View File

@@ -5,14 +5,16 @@
*/
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import javax.annotation.Nonnull;
public class GuiPrinter extends ContainerScreen<ContainerPrinter>
{
private static final ResourceLocation BACKGROUND = new ResourceLocation( "computercraft", "textures/gui/printer.png" );
@@ -23,28 +25,20 @@ public class GuiPrinter extends ContainerScreen<ContainerPrinter>
}
@Override
protected void renderLabels( int mouseX, int mouseY )
{
String title = getTitle().getColoredString();
font.draw( title, (imageWidth - font.width( title )) / 2.0f, 6, 0x404040 );
font.draw( I18n.get( "container.inventory" ), 8, imageHeight - 96 + 2, 0x404040 );
}
@Override
protected void renderBg( float partialTicks, int mouseX, int mouseY )
protected void renderBg( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY )
{
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
minecraft.getTextureManager().bind( BACKGROUND );
blit( leftPos, topPos, 0, 0, imageWidth, imageHeight );
blit( transform, leftPos, topPos, 0, 0, imageWidth, imageHeight );
if( getMenu().isPrinting() ) blit( leftPos + 34, topPos + 21, 176, 0, 25, 45 );
if( getMenu().isPrinting() ) blit( transform, leftPos + 34, topPos + 21, 176, 0, 25, 45 );
}
@Override
public void render( int mouseX, int mouseY, float partialTicks )
public void render( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks )
{
renderBackground();
super.render( mouseX, mouseY, partialTicks );
renderTooltip( mouseX, mouseY );
renderBackground( stack );
super.render( stack, mouseX, mouseY, partialTicks );
renderTooltip( stack, mouseX, mouseY );
}
}

View File

@@ -5,6 +5,7 @@
*/
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.common.ContainerHeldItem;
@@ -12,18 +13,17 @@ import dan200.computercraft.shared.media.items.ItemPrintout;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.client.renderer.TransformationMatrix;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.text.ITextComponent;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nonnull;
import static dan200.computercraft.client.render.PrintoutRenderer.*;
public class GuiPrintout extends ContainerScreen<ContainerHeldItem>
{
private static final Matrix4f IDENTITY = TransformationMatrix.identity().getMatrix();
private final boolean book;
private final int pages;
private final TextBuffer[] text;
@@ -91,27 +91,33 @@ public class GuiPrintout extends ContainerScreen<ContainerHeldItem>
}
@Override
public void renderBg( float partialTicks, int mouseX, int mouseY )
protected void renderBg( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY )
{
// Draw the printout
RenderSystem.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
RenderSystem.enableDepthTest();
IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().renderBuffers().bufferSource();
drawBorder( IDENTITY, renderer, leftPos, topPos, getBlitOffset(), page, pages, book );
drawText( IDENTITY, renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * page, text, colours );
Matrix4f matrix = transform.last().pose();
drawBorder( matrix, renderer, leftPos, topPos, getBlitOffset(), page, pages, book );
drawText( matrix, renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * page, text, colours );
renderer.endBatch();
}
@Override
public void render( int mouseX, int mouseY, float partialTicks )
public void render( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks )
{
// We must take the background further back in order to not overlap with our printed pages.
setBlitOffset( getBlitOffset() - 1 );
renderBackground();
renderBackground( stack );
setBlitOffset( getBlitOffset() + 1 );
super.render( mouseX, mouseY, partialTicks );
renderTooltip( mouseX, mouseY );
super.render( stack, mouseX, mouseY, partialTicks );
}
@Override
protected void renderLabels( @Nonnull MatrixStack transform, int mouseX, int mouseY )
{
// Skip rendering labels.
}
}

View File

@@ -5,134 +5,70 @@
*/
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
import dan200.computercraft.client.gui.widgets.WidgetWrapper;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.client.render.ComputerBorderRenderer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import org.lwjgl.glfw.GLFW;
public class GuiTurtle extends ContainerScreen<ContainerTurtle>
import javax.annotation.Nonnull;
import static dan200.computercraft.shared.turtle.inventory.ContainerTurtle.*;
public class GuiTurtle extends ComputerScreenBase<ContainerTurtle>
{
private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/turtle_normal.png" );
private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/turtle_advanced.png" );
private final ContainerTurtle container;
private static final int TEX_WIDTH = 254;
private static final int TEX_HEIGHT = 217;
private final ComputerFamily family;
private final ClientComputer computer;
private WidgetTerminal terminal;
private WidgetWrapper terminalWrapper;
public GuiTurtle( ContainerTurtle container, PlayerInventory player, ITextComponent title )
{
super( container, player, title );
this.container = container;
super( container, player, title, BORDER );
family = container.getFamily();
computer = (ClientComputer) container.getComputer();
imageWidth = 254;
imageHeight = 217;
imageWidth = TEX_WIDTH + ComputerSidebar.WIDTH;
imageHeight = TEX_HEIGHT;
}
@Override
protected void init()
protected WidgetTerminal createTerminal()
{
super.init();
minecraft.keyboardHandler.setSendRepeatsToGui( true );
int termPxWidth = ComputerCraft.turtleTermWidth * FixedWidthFontRenderer.FONT_WIDTH;
int termPxHeight = ComputerCraft.turtleTermHeight * FixedWidthFontRenderer.FONT_HEIGHT;
terminal = new WidgetTerminal(
minecraft, () -> computer,
ComputerCraft.turtleTermWidth,
ComputerCraft.turtleTermHeight,
2, 2, 2, 2
return new WidgetTerminal(
computer, leftPos + BORDER + ComputerSidebar.WIDTH, topPos + BORDER,
ComputerCraft.turtleTermWidth, ComputerCraft.turtleTermHeight
);
terminalWrapper = new WidgetWrapper( terminal, 2 + 8 + leftPos, 2 + 8 + topPos, termPxWidth, termPxHeight );
children.add( terminalWrapper );
setFocused( terminalWrapper );
}
@Override
public void removed()
protected void renderBg( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY )
{
super.removed();
children.remove( terminal );
terminal = null;
minecraft.keyboardHandler.setSendRepeatsToGui( false );
}
boolean advanced = family == ComputerFamily.ADVANCED;
minecraft.getTextureManager().bind( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL );
blit( transform, leftPos + ComputerSidebar.WIDTH, topPos, 0, 0, TEX_WIDTH, TEX_HEIGHT );
@Override
public void tick()
{
super.tick();
terminal.update();
}
@Override
public boolean keyPressed( int key, int scancode, int modifiers )
{
// Forward the tab key to the terminal, rather than moving between controls.
if( key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminalWrapper )
{
return getFocused().keyPressed( key, scancode, modifiers );
}
return super.keyPressed( key, scancode, modifiers );
}
private void drawSelectionSlot( boolean advanced )
{
// Draw selection slot
int slot = container.getSelectedSlot();
int slot = getMenu().getSelectedSlot();
if( slot >= 0 )
{
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
int slotX = slot % 4;
int slotY = slot / 4;
minecraft.getTextureManager().bind( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL );
blit( leftPos + ContainerTurtle.TURTLE_START_X - 2 + slotX * 18, topPos + ContainerTurtle.PLAYER_START_Y - 2 + slotY * 18, 0, 217, 24, 24 );
blit( transform,
leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18,
0, 217, 24, 24
);
}
}
@Override
protected void renderBg( float partialTicks, int mouseX, int mouseY )
{
// Draw term
boolean advanced = family == ComputerFamily.ADVANCED;
terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() );
// Draw border/inventory
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
minecraft.getTextureManager().bind( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL );
blit( leftPos, topPos, 0, 0, imageWidth, imageHeight );
drawSelectionSlot( advanced );
}
@Override
public void render( int mouseX, int mouseY, float partialTicks )
{
renderBackground();
super.render( mouseX, mouseY, partialTicks );
renderTooltip( mouseX, mouseY );
}
@Override
public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
{
return (getFocused() != null && getFocused().mouseDragged( x, y, button, deltaX, deltaY ))
|| super.mouseDragged( x, y, button, deltaX, deltaY );
minecraft.getTextureManager().bind( advanced ? ComputerBorderRenderer.BACKGROUND_ADVANCED : ComputerBorderRenderer.BACKGROUND_NORMAL );
ComputerSidebar.renderBackground( transform, leftPos, topPos + sidebarYOffset );
}
}

View File

@@ -0,0 +1,127 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.IBidiRenderer;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.Widget;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import javax.annotation.Nonnull;
import java.util.List;
public final class OptionScreen extends Screen
{
private static final ResourceLocation BACKGROUND = new ResourceLocation( "computercraft", "textures/gui/blank_screen.png" );
public static final int BUTTON_WIDTH = 100;
public static final int BUTTON_HEIGHT = 20;
private static final int PADDING = 16;
private static final int FONT_HEIGHT = 9;
private int x;
private int y;
private int innerWidth;
private int innerHeight;
private IBidiRenderer messageRenderer;
private final ITextComponent message;
private final List<Widget> buttons;
private final Runnable exit;
private final Screen originalScreen;
private OptionScreen( ITextComponent title, ITextComponent message, List<Widget> buttons, Runnable exit, Screen originalScreen )
{
super( title );
this.message = message;
this.buttons = buttons;
this.exit = exit;
this.originalScreen = originalScreen;
}
public static void show( Minecraft minecraft, ITextComponent title, ITextComponent message, List<Widget> buttons, Runnable exit )
{
minecraft.setScreen( new OptionScreen( title, message, buttons, exit, unwrap( minecraft.screen ) ) );
}
public static Screen unwrap( Screen screen )
{
return screen instanceof OptionScreen ? ((OptionScreen) screen).getOriginalScreen() : screen;
}
@Override
public void init()
{
super.init();
int buttonWidth = BUTTON_WIDTH * buttons.size() + PADDING * (buttons.size() - 1);
int innerWidth = this.innerWidth = Math.max( 256, buttonWidth + PADDING * 2 );
messageRenderer = IBidiRenderer.create( font, message, innerWidth - PADDING * 2 );
int textHeight = messageRenderer.getLineCount() * FONT_HEIGHT + PADDING * 2;
innerHeight = textHeight + (buttons.isEmpty() ? 0 : buttons.get( 0 ).getHeight()) + PADDING;
x = (width - innerWidth) / 2;
y = (height - innerHeight) / 2;
int x = (width - buttonWidth) / 2;
for( Widget button : buttons )
{
button.x = x;
button.y = y + textHeight;
addButton( button );
x += BUTTON_WIDTH + PADDING;
}
}
@Override
public void render( @Nonnull MatrixStack transform, int mouseX, int mouseY, float partialTicks )
{
renderBackground( transform );
// Render the actual texture.
minecraft.textureManager.bind( BACKGROUND );
blit( transform, x, y, 0, 0, innerWidth, PADDING );
blit( transform,
x, y + PADDING, 0, PADDING, innerWidth, innerHeight - PADDING * 2,
innerWidth, PADDING
);
blit( transform, x, y + innerHeight - PADDING, 0, 256 - PADDING, innerWidth, PADDING );
messageRenderer.renderLeftAlignedNoShadow( transform, x + PADDING, y + PADDING, FONT_HEIGHT, 0x404040 );
super.render( transform, mouseX, mouseY, partialTicks );
}
@Override
public void onClose()
{
exit.run();
}
public static Widget newButton( ITextComponent component, Button.IPressable clicked )
{
return new Button( 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, component, clicked );
}
public void disable()
{
for( Widget widget : buttons ) widget.active = false;
}
@Nonnull
public Screen getOriginalScreen()
{
return originalScreen;
}
}

View File

@@ -0,0 +1,106 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui.widgets;
import com.mojang.blaze3d.matrix.MatrixStack;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.render.ComputerBorderRenderer;
import dan200.computercraft.shared.computer.core.ClientComputer;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.Widget;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.util.text.TranslationTextComponent;
import java.util.Arrays;
import java.util.function.Consumer;
/**
* Registers buttons to interact with a computer.
*/
public final class ComputerSidebar
{
private static final ResourceLocation TEXTURE = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/buttons.png" );
private static final int TEX_SIZE = 64;
private static final int ICON_WIDTH = 12;
private static final int ICON_HEIGHT = 12;
private static final int ICON_MARGIN = 2;
private static final int ICON_TEX_Y_DIFF = 14;
private static final int CORNERS_BORDER = 3;
private static final int FULL_BORDER = CORNERS_BORDER + ICON_MARGIN;
private static final int BUTTONS = 2;
private static final int HEIGHT = (ICON_HEIGHT + ICON_MARGIN * 2) * BUTTONS + CORNERS_BORDER * 2;
public static final int WIDTH = 17;
private ComputerSidebar()
{
}
public static void addButtons( Screen screen, ClientComputer computer, Consumer<Widget> add, int x, int y )
{
x += CORNERS_BORDER + 1;
y += CORNERS_BORDER + ICON_MARGIN;
add.accept( new DynamicImageButton(
screen, x, y, ICON_WIDTH, ICON_HEIGHT, () -> computer.isOn() ? 15 : 1, 1, ICON_TEX_Y_DIFF,
TEXTURE, TEX_SIZE, TEX_SIZE, b -> toggleComputer( computer ),
() -> computer.isOn() ? Arrays.asList(
new TranslationTextComponent( "gui.computercraft.tooltip.turn_off" ),
new TranslationTextComponent( "gui.computercraft.tooltip.turn_off.key" ).withStyle( TextFormatting.GRAY )
) : Arrays.asList(
new TranslationTextComponent( "gui.computercraft.tooltip.turn_on" ),
new TranslationTextComponent( "gui.computercraft.tooltip.turn_off.key" ).withStyle( TextFormatting.GRAY )
)
) );
y += ICON_HEIGHT + ICON_MARGIN * 2;
add.accept( new DynamicImageButton(
screen, x, y, ICON_WIDTH, ICON_HEIGHT, 29, 1, ICON_TEX_Y_DIFF,
TEXTURE, TEX_SIZE, TEX_SIZE, b -> computer.queueEvent( "terminate" ),
Arrays.asList(
new TranslationTextComponent( "gui.computercraft.tooltip.terminate" ),
new TranslationTextComponent( "gui.computercraft.tooltip.terminate.key" ).withStyle( TextFormatting.GRAY )
)
) );
}
public static void renderBackground( MatrixStack transform, int x, int y )
{
Screen.blit( transform,
x, y, 0, 102, WIDTH, FULL_BORDER,
ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE
);
Screen.blit( transform,
x, y + FULL_BORDER, WIDTH, HEIGHT - FULL_BORDER * 2,
0, 107, WIDTH, 4,
ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE
);
Screen.blit( transform,
x, y + HEIGHT - FULL_BORDER, 0, 111, WIDTH, FULL_BORDER,
ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE
);
}
private static void toggleComputer( ClientComputer computer )
{
if( computer.isOn() )
{
computer.shutdown();
}
else
{
computer.turnOn();
}
}
}

View File

@@ -0,0 +1,101 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui.widgets;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraftforge.common.util.NonNullSupplier;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.function.IntSupplier;
/**
* Version of {@link net.minecraft.client.gui.widget.button.ImageButton} which allows changing some properties
* dynamically.
*/
public class DynamicImageButton extends Button
{
private final Screen screen;
private final ResourceLocation texture;
private final IntSupplier xTexStart;
private final int yTexStart;
private final int yDiffTex;
private final int textureWidth;
private final int textureHeight;
private final NonNullSupplier<List<ITextComponent>> tooltip;
public DynamicImageButton(
Screen screen, int x, int y, int width, int height, int xTexStart, int yTexStart, int yDiffTex,
ResourceLocation texture, int textureWidth, int textureHeight,
IPressable onPress, List<ITextComponent> tooltip
)
{
this(
screen, x, y, width, height, () -> xTexStart, yTexStart, yDiffTex,
texture, textureWidth, textureHeight,
onPress, () -> tooltip
);
}
public DynamicImageButton(
Screen screen, int x, int y, int width, int height, IntSupplier xTexStart, int yTexStart, int yDiffTex,
ResourceLocation texture, int textureWidth, int textureHeight,
IPressable onPress, NonNullSupplier<List<ITextComponent>> tooltip
)
{
super( x, y, width, height, StringTextComponent.EMPTY, onPress );
this.screen = screen;
this.textureWidth = textureWidth;
this.textureHeight = textureHeight;
this.xTexStart = xTexStart;
this.yTexStart = yTexStart;
this.yDiffTex = yDiffTex;
this.texture = texture;
this.tooltip = tooltip;
}
@Override
public void renderButton( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks )
{
Minecraft minecraft = Minecraft.getInstance();
minecraft.getTextureManager().bind( texture );
RenderSystem.disableDepthTest();
int yTex = yTexStart;
if( isHovered() ) yTex += yDiffTex;
blit( stack, x, y, xTexStart.getAsInt(), yTex, width, height, textureWidth, textureHeight );
RenderSystem.enableDepthTest();
if( isHovered() ) renderToolTip( stack, mouseX, mouseY );
}
@Nonnull
@Override
public ITextComponent getMessage()
{
List<ITextComponent> tooltip = this.tooltip.get();
return tooltip.isEmpty() ? StringTextComponent.EMPTY : tooltip.get( 0 );
}
@Override
public void renderToolTip( @Nonnull MatrixStack stack, int mouseX, int mouseY )
{
List<ITextComponent> tooltip = this.tooltip.get();
if( !tooltip.isEmpty() )
{
screen.renderWrappedToolTip( stack, tooltip, mouseX, mouseY, screen.getMinecraft().font );
}
}
}

View File

@@ -5,32 +5,35 @@
*/
package dan200.computercraft.client.gui.widgets;
import com.mojang.blaze3d.matrix.MatrixStack;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.IComputer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.IGuiEventListener;
import net.minecraft.client.gui.widget.Widget;
import net.minecraft.util.SharedConstants;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.text.StringTextComponent;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nonnull;
import java.util.BitSet;
import java.util.function.Supplier;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
public class WidgetTerminal implements IGuiEventListener
public class WidgetTerminal extends Widget
{
private static final float TERMINATE_TIME = 0.5f;
private final Minecraft client;
private final ClientComputer computer;
private boolean focused;
private final Supplier<ClientComputer> computer;
private final int termWidth;
private final int termHeight;
// The positions of the actual terminal
private final int innerX;
private final int innerY;
private final int innerWidth;
private final int innerHeight;
private float terminateTimer = -1;
private float rebootTimer = -1;
@@ -40,23 +43,18 @@ public class WidgetTerminal implements IGuiEventListener
private int lastMouseX = -1;
private int lastMouseY = -1;
private final int leftMargin;
private final int rightMargin;
private final int topMargin;
private final int bottomMargin;
private final BitSet keysDown = new BitSet( 256 );
public WidgetTerminal( Minecraft client, Supplier<ClientComputer> computer, int termWidth, int termHeight, int leftMargin, int rightMargin, int topMargin, int bottomMargin )
public WidgetTerminal( @Nonnull ClientComputer computer, int x, int y, int termWidth, int termHeight )
{
this.client = client;
super( x, y, termWidth * FONT_WIDTH + MARGIN * 2, termHeight * FONT_HEIGHT + MARGIN * 2, StringTextComponent.EMPTY );
this.computer = computer;
this.termWidth = termWidth;
this.termHeight = termHeight;
this.leftMargin = leftMargin;
this.rightMargin = rightMargin;
this.topMargin = topMargin;
this.bottomMargin = bottomMargin;
innerX = x + MARGIN;
innerY = y + MARGIN;
innerWidth = termWidth * FONT_WIDTH;
innerHeight = termHeight * FONT_HEIGHT;
}
@Override
@@ -65,7 +63,7 @@ public class WidgetTerminal implements IGuiEventListener
if( ch >= 32 && ch <= 126 || ch >= 160 && ch <= 255 ) // printable chars in byte range
{
// Queue the "char" event
queueEvent( "char", Character.toString( ch ) );
computer.queueEvent( "char", new Object[] { Character.toString( ch ) } );
}
return true;
@@ -91,7 +89,7 @@ public class WidgetTerminal implements IGuiEventListener
case GLFW.GLFW_KEY_V:
// Ctrl+V for paste
String clipboard = client.keyboardHandler.getClipboard();
String clipboard = Minecraft.getInstance().keyboardHandler.getClipboard();
if( clipboard != null )
{
// Clip to the first occurrence of \r or \n
@@ -116,7 +114,7 @@ public class WidgetTerminal implements IGuiEventListener
{
// Clip to 512 characters and queue the event
if( clipboard.length() > 512 ) clipboard = clipboard.substring( 0, 512 );
queueEvent( "paste", clipboard );
computer.queueEvent( "paste", new Object[] { clipboard } );
}
return true;
@@ -129,8 +127,7 @@ public class WidgetTerminal implements IGuiEventListener
// Queue the "key" event and add to the down set
boolean repeat = keysDown.get( key );
keysDown.set( key );
IComputer computer = this.computer.get();
if( computer != null ) computer.keyDown( key, repeat );
computer.keyDown( key, repeat );
}
return true;
@@ -143,8 +140,7 @@ public class WidgetTerminal implements IGuiEventListener
if( key >= 0 && keysDown.get( key ) )
{
keysDown.set( key, false );
IComputer computer = this.computer.get();
if( computer != null ) computer.keyUp( key );
computer.keyUp( key );
}
switch( key )
@@ -170,14 +166,14 @@ public class WidgetTerminal implements IGuiEventListener
@Override
public boolean mouseClicked( double mouseX, double mouseY, int button )
{
ClientComputer computer = this.computer.get();
if( computer == null || !computer.isColour() || button < 0 || button > 2 ) return false;
if( !inTermRegion( mouseX, mouseY ) ) return false;
if( !computer.isColour() || button < 0 || button > 2 ) return false;
Terminal term = computer.getTerminal();
if( term != null )
{
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
int charX = (int) ((mouseX - innerX) / FONT_WIDTH);
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
@@ -194,14 +190,14 @@ public class WidgetTerminal implements IGuiEventListener
@Override
public boolean mouseReleased( double mouseX, double mouseY, int button )
{
ClientComputer computer = this.computer.get();
if( computer == null || !computer.isColour() || button < 0 || button > 2 ) return false;
if( !inTermRegion( mouseX, mouseY ) ) return false;
if( !computer.isColour() || button < 0 || button > 2 ) return false;
Terminal term = computer.getTerminal();
if( term != null )
{
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
int charX = (int) ((mouseX - innerX) / FONT_WIDTH);
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
@@ -221,14 +217,14 @@ public class WidgetTerminal implements IGuiEventListener
@Override
public boolean mouseDragged( double mouseX, double mouseY, int button, double v2, double v3 )
{
ClientComputer computer = this.computer.get();
if( computer == null || !computer.isColour() || button < 0 || button > 2 ) return false;
if( !inTermRegion( mouseX, mouseY ) ) return false;
if( !computer.isColour() || button < 0 || button > 2 ) return false;
Terminal term = computer.getTerminal();
if( term != null )
{
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
int charX = (int) ((mouseX - innerX) / FONT_WIDTH);
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
@@ -246,14 +242,14 @@ public class WidgetTerminal implements IGuiEventListener
@Override
public boolean mouseScrolled( double mouseX, double mouseY, double delta )
{
ClientComputer computer = this.computer.get();
if( computer == null || !computer.isColour() || delta == 0 ) return false;
if( !inTermRegion( mouseX, mouseY ) ) return false;
if( !computer.isColour() || delta == 0 ) return false;
Terminal term = computer.getTerminal();
if( term != null )
{
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
int charX = (int) ((mouseX - innerX) / FONT_WIDTH);
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
@@ -266,89 +262,74 @@ public class WidgetTerminal implements IGuiEventListener
return true;
}
private boolean inTermRegion( double mouseX, double mouseY )
{
return active && visible && mouseX >= innerX && mouseY >= innerY && mouseX < innerX + innerWidth && mouseY < innerY + innerHeight;
}
public void update()
{
if( terminateTimer >= 0 && terminateTimer < TERMINATE_TIME && (terminateTimer += 0.05f) > TERMINATE_TIME )
{
queueEvent( "terminate" );
computer.queueEvent( "terminate" );
}
if( shutdownTimer >= 0 && shutdownTimer < TERMINATE_TIME && (shutdownTimer += 0.05f) > TERMINATE_TIME )
{
ClientComputer computer = this.computer.get();
if( computer != null ) computer.shutdown();
computer.shutdown();
}
if( rebootTimer >= 0 && rebootTimer < TERMINATE_TIME && (rebootTimer += 0.05f) > TERMINATE_TIME )
{
ClientComputer computer = this.computer.get();
if( computer != null ) computer.reboot();
computer.reboot();
}
}
@Override
public boolean changeFocus( boolean reversed )
public void onFocusedChanged( boolean focused )
{
if( focused )
if( !focused )
{
// When blurring, we should make all keys go up
for( int key = 0; key < keysDown.size(); key++ )
{
if( keysDown.get( key ) ) queueEvent( "key_up", key );
if( keysDown.get( key ) ) computer.keyUp( key );
}
keysDown.clear();
// When blurring, we should make the last mouse button go up
if( lastMouseButton > 0 )
{
IComputer computer = this.computer.get();
if( computer != null ) computer.mouseUp( lastMouseButton + 1, lastMouseX + 1, lastMouseY + 1 );
computer.mouseUp( lastMouseButton + 1, lastMouseX + 1, lastMouseY + 1 );
lastMouseButton = -1;
}
shutdownTimer = terminateTimer = rebootTimer = -1;
}
focused = !focused;
return true;
}
public void draw( int originX, int originY )
{
synchronized( computer )
{
// Draw the screen contents
ClientComputer computer = this.computer.get();
Terminal terminal = computer != null ? computer.getTerminal() : null;
if( terminal != null )
{
FixedWidthFontRenderer.drawTerminal( originX, originY, terminal, !computer.isColour(), topMargin, bottomMargin, leftMargin, rightMargin );
}
else
{
FixedWidthFontRenderer.drawEmptyTerminal(
originX - leftMargin, originY - rightMargin,
termWidth * FONT_WIDTH + leftMargin + rightMargin,
termHeight * FONT_HEIGHT + topMargin + bottomMargin
);
}
}
}
private void queueEvent( String event )
{
ClientComputer computer = this.computer.get();
if( computer != null ) computer.queueEvent( event );
}
private void queueEvent( String event, Object... args )
{
ClientComputer computer = this.computer.get();
if( computer != null ) computer.queueEvent( event, args );
}
@Override
public boolean isMouseOver( double x, double y )
public void render( @Nonnull MatrixStack transform, int mouseX, int mouseY, float partialTicks )
{
return true;
Matrix4f matrix = transform.last().pose();
Terminal terminal = computer.getTerminal();
if( terminal != null )
{
FixedWidthFontRenderer.drawTerminal( matrix, innerX, innerY, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN );
}
else
{
FixedWidthFontRenderer.drawEmptyTerminal( matrix, x, y, width, height );
}
}
public static int getWidth( int termWidth )
{
return termWidth * FONT_WIDTH + MARGIN * 2;
}
public static int getHeight( int termHeight )
{
return termHeight * FONT_HEIGHT + MARGIN * 2;
}
}

View File

@@ -1,105 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui.widgets;
import net.minecraft.client.gui.IGuiEventListener;
public class WidgetWrapper implements IGuiEventListener
{
private final IGuiEventListener listener;
private final int x;
private final int y;
private final int width;
private final int height;
public WidgetWrapper( IGuiEventListener listener, int x, int y, int width, int height )
{
this.listener = listener;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
@Override
public boolean changeFocus( boolean b )
{
return listener.changeFocus( b );
}
@Override
public boolean mouseClicked( double x, double y, int button )
{
double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseClicked( dx, dy, button );
}
@Override
public boolean mouseReleased( double x, double y, int button )
{
double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseReleased( dx, dy, button );
}
@Override
public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
{
double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseDragged( dx, dy, button, deltaX, deltaY );
}
@Override
public boolean mouseScrolled( double x, double y, double delta )
{
double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseScrolled( dx, dy, delta );
}
@Override
public boolean keyPressed( int key, int scancode, int modifiers )
{
return listener.keyPressed( key, scancode, modifiers );
}
@Override
public boolean keyReleased( int key, int scancode, int modifiers )
{
return listener.keyReleased( key, scancode, modifiers );
}
@Override
public boolean charTyped( char character, int modifiers )
{
return listener.charTyped( character, modifiers );
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
public int getWidth()
{
return width;
}
public int getHeight()
{
return height;
}
@Override
public boolean isMouseOver( double x, double y )
{
double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < width && dy >= 0 && dy < height;
}
}

View File

@@ -1,81 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.proxy;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.*;
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
import dan200.computercraft.client.render.TurtlePlayerRenderer;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import net.minecraft.client.gui.ScreenManager;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.RenderTypeLookup;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.client.registry.RenderingRegistry;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD )
public final class ComputerCraftProxyClient
{
@SubscribeEvent
public static void setupClient( FMLClientSetupEvent event )
{
registerContainers();
// While turtles themselves are not transparent, their upgrades may be.
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.TURTLE_NORMAL.get(), RenderType.translucent() );
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.TURTLE_ADVANCED.get(), RenderType.translucent() );
// Monitors' textures have transparent fronts and so count as cutouts.
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.MONITOR_NORMAL.get(), RenderType.cutout() );
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.MONITOR_ADVANCED.get(), RenderType.cutout() );
// Setup TESRs
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.MONITOR_NORMAL.get(), TileEntityMonitorRenderer::new );
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.MONITOR_ADVANCED.get(), TileEntityMonitorRenderer::new );
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_NORMAL.get(), TileEntityTurtleRenderer::new );
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_ADVANCED.get(), TileEntityTurtleRenderer::new );
RenderingRegistry.registerEntityRenderingHandler( Registry.ModEntities.TURTLE_PLAYER.get(), TurtlePlayerRenderer::new );
}
private static void registerContainers()
{
// My IDE doesn't think so, but we do actually need these generics.
ScreenManager.<ContainerComputer, GuiComputer<ContainerComputer>>register( Registry.ModContainers.COMPUTER.get(), GuiComputer::create );
ScreenManager.<ContainerPocketComputer, GuiComputer<ContainerPocketComputer>>register( Registry.ModContainers.POCKET_COMPUTER.get(), GuiComputer::createPocket );
ScreenManager.register( Registry.ModContainers.TURTLE.get(), GuiTurtle::new );
ScreenManager.register( Registry.ModContainers.PRINTER.get(), GuiPrinter::new );
ScreenManager.register( Registry.ModContainers.DISK_DRIVE.get(), GuiDiskDrive::new );
ScreenManager.register( Registry.ModContainers.PRINTOUT.get(), GuiPrintout::new );
ScreenManager.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register( Registry.ModContainers.VIEW_COMPUTER.get(), GuiComputer::createView );
}
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public static final class ForgeHandlers
{
@SubscribeEvent
public static void onWorldUnload( WorldEvent.Unload event )
{
if( event.getWorld().isClientSide() )
{
ClientMonitor.destroyAll();
}
}
}
}

View File

@@ -14,14 +14,14 @@ import dan200.computercraft.shared.peripheral.modem.wired.CableShapes;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.DrawHighlightEvent;
@@ -63,7 +63,7 @@ public final class CableHighlightRenderer
? CableShapes.getModemShape( state )
: CableShapes.getCableShape( state );
Vec3d cameraPos = info.getPosition();
Vector3d cameraPos = info.getPosition();
double xOffset = pos.getX() - cameraPos.x();
double yOffset = pos.getY() - cameraPos.y();
double zOffset = pos.getZ() - cameraPos.z();

View File

@@ -10,10 +10,10 @@ import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Matrix4f;
import org.lwjgl.opengl.GL11;
import javax.annotation.Nonnull;
@@ -52,7 +52,8 @@ public class ComputerBorderRenderer
public static final int LIGHT_HEIGHT = 8;
private static final float TEX_SCALE = 1 / 256.0f;
public static final int TEX_SIZE = 256;
private static final float TEX_SCALE = 1 / (float) TEX_SIZE;
private final Matrix4f transform;
private final IVertexBuilder builder;

View File

@@ -10,12 +10,12 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.AbstractClientPlayerEntity;
import net.minecraft.client.renderer.FirstPersonRenderer;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.Vector3f;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
import net.minecraft.util.HandSide;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector3f;
public abstract class ItemMapLikeRenderer
{

View File

@@ -15,9 +15,13 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.util.Colour;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.*;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3f;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;

View File

@@ -9,9 +9,9 @@ import com.mojang.blaze3d.matrix.MatrixStack;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.media.items.ItemPrintout;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.client.renderer.Vector3f;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3f;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.client.event.RenderItemInFrameEvent;

View File

@@ -9,12 +9,12 @@ import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.DrawHighlightEvent;
@@ -61,7 +61,7 @@ public final class MonitorHighlightRenderer
if( monitor.getYIndex() != monitor.getHeight() - 1 ) faces.remove( monitor.getDown() );
MatrixStack transformStack = event.getMatrix();
Vec3d cameraPos = event.getInfo().getPosition();
Vector3d cameraPos = event.getInfo().getPosition();
transformStack.pushPose();
transformStack.translate( pos.getX() - cameraPos.x(), pos.getY() - cameraPos.y(), pos.getZ() - cameraPos.z() );

View File

@@ -11,8 +11,8 @@ import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.client.renderer.texture.TextureUtil;
import net.minecraft.util.math.vector.Matrix4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;

View File

@@ -10,11 +10,11 @@ import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.client.renderer.RenderState;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Matrix4f;
import org.lwjgl.opengl.GL11;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;

View File

@@ -24,6 +24,9 @@ import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.TransformationMatrix;
import net.minecraft.util.math.vector.Vector3f;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
@@ -141,6 +144,10 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
(float) (xSize + 2 * TileMonitor.RENDER_MARGIN), (float) -(ySize + TileMonitor.RENDER_MARGIN * 2)
);
// Force a flush of the blocker. WorldRenderer.updateCameraAndRender will "finish" all the built-in
// buffers before calling renderer.finish, which means the blocker isn't actually rendered at that point!
renderer.getBuffer( RenderType.solid() );
transform.popPose();
}

View File

@@ -19,8 +19,6 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.renderer.Atlases;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.client.renderer.Vector3f;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ModelManager;
@@ -31,7 +29,9 @@ import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.math.vector.Vector3f;
import net.minecraftforge.client.model.data.EmptyModelData;
import javax.annotation.Nonnull;
@@ -99,7 +99,7 @@ public class TileEntityTurtleRenderer extends TileEntityRenderer<TileTurtle>
transform.pushPose();
// Setup the transform.
Vec3d offset = turtle.getRenderOffset( partialTicks );
Vector3d offset = turtle.getRenderOffset( partialTicks );
float yaw = turtle.getRenderYaw( partialTicks );
transform.translate( offset.x, offset.y, offset.z );

View File

@@ -57,16 +57,16 @@ public final class TurtleModelLoader implements IModelLoader<TurtleModelLoader.T
}
@Override
public Collection<Material> getTextures( IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<Pair<String, String>> missingTextureErrors )
public Collection<RenderMaterial> getTextures( IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<Pair<String, String>> missingTextureErrors )
{
Set<Material> materials = new HashSet<>();
Set<RenderMaterial> materials = new HashSet<>();
materials.addAll( modelGetter.apply( family ).getMaterials( modelGetter, missingTextureErrors ) );
materials.addAll( modelGetter.apply( COLOUR_TURTLE_MODEL ).getMaterials( modelGetter, missingTextureErrors ) );
return materials;
}
@Override
public IBakedModel bake( IModelConfiguration owner, ModelBakery bakery, Function<Material, TextureAtlasSprite> spriteGetter, IModelTransform transform, ItemOverrideList overrides, ResourceLocation modelLocation )
public IBakedModel bake( IModelConfiguration owner, ModelBakery bakery, Function<RenderMaterial, TextureAtlasSprite> spriteGetter, IModelTransform transform, ItemOverrideList overrides, ResourceLocation modelLocation )
{
return new TurtleSmartItemModel(
bakery.getBakedModel( family, transform, spriteGetter ),

View File

@@ -7,12 +7,12 @@ package dan200.computercraft.client.render;
import dan200.computercraft.api.client.TransformedModel;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.TransformationMatrix;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemOverrideList;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.Direction;
import net.minecraft.util.math.vector.TransformationMatrix;
import net.minecraftforge.client.model.data.EmptyModelData;
import net.minecraftforge.client.model.data.IModelData;
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder;

View File

@@ -15,14 +15,14 @@ import dan200.computercraft.shared.util.Holiday;
import dan200.computercraft.shared.util.HolidayUtil;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.TransformationMatrix;
import net.minecraft.client.renderer.model.*;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraft.util.math.vector.TransformationMatrix;
import net.minecraftforge.client.model.data.IModelData;
import javax.annotation.Nonnull;
@@ -109,7 +109,7 @@ public class TurtleSmartItemModel implements IBakedModel
{
@Nonnull
@Override
public IBakedModel resolve( @Nonnull IBakedModel originalModel, @Nonnull ItemStack stack, @Nullable World world, @Nullable LivingEntity entity )
public IBakedModel resolve( @Nonnull IBakedModel originalModel, @Nonnull ItemStack stack, @Nullable ClientWorld world, @Nullable LivingEntity entity )
{
ItemTurtle turtle = (ItemTurtle) stack.getItem();
int colour = turtle.getColour( stack );

View File

@@ -68,6 +68,13 @@ public class FSAPI implements ILuaAPI
* @param path The path to list.
* @return A table with a list of files in the directory.
* @throws LuaException If the path doesn't exist.
* @cc.usage List all files under {@code /rom/}
* <pre>{@code
* local files = fs.list("/rom/")
* for i = 1, #files do
* print(files[i])
* end
* }</pre>
*/
@LuaFunction
public final String[] list( String path ) throws LuaException
@@ -92,6 +99,11 @@ public class FSAPI implements ILuaAPI
* @throws LuaException On argument errors.
* @cc.tparam string path The first part of the path. For example, a parent directory path.
* @cc.tparam string ... Additional parts of the path to combine.
* @cc.usage Combine several file paths together
* <pre>{@code
* fs.combine("/rom/programs", "../apis", "parallel.lua")
* -- => rom/apis/parallel.lua
* }</pre>
*/
@LuaFunction
public final String combine( IArguments arguments ) throws LuaException
@@ -114,6 +126,11 @@ public class FSAPI implements ILuaAPI
*
* @param path The path to get the name from.
* @return The final part of the path (the file name).
* @cc.usage Get the file name of {@code rom/startup.lua}
* <pre>{@code
* fs.getName("rom/startup.lua")
* -- => startup.lua
* }</pre>
*/
@LuaFunction
public final String getName( String path )
@@ -126,6 +143,11 @@ public class FSAPI implements ILuaAPI
*
* @param path The path to get the directory from.
* @return The path with the final part removed (the parent directory).
* @cc.usage Get the directory name of {@code rom/startup.lua}
* <pre>{@code
* fs.getDir("rom/startup.lua")
* -- => rom
* }</pre>
*/
@LuaFunction
public final String getDir( String path )
@@ -304,10 +326,15 @@ public class FSAPI implements ILuaAPI
/**
* Opens a file for reading or writing at a path.
*
* The mode parameter can be {@code r} to read, {@code w} to write (deleting
* all contents), or {@code a} to append (keeping contents). If {@code b} is
* added to the end, the file will be opened in binary mode; otherwise, it's
* opened in text mode.
* The {@code mode} string can be any of the following:
* <ul>
* <li><strong>"r"</strong>: Read mode</li>
* <li><strong>"w"</strong>: Write mode</li>
* <li><strong>"a"</strong>: Append mode</li>
* </ul>
*
* The mode may also have a "b" at the end, which opens the file in "binary
* mode". This allows you to read binary files, as well as seek within a file.
*
* @param path The path to the file to open.
* @param mode The mode to open the file with.
@@ -316,6 +343,37 @@ public class FSAPI implements ILuaAPI
* @cc.treturn [1] table A file handle object for the file.
* @cc.treturn [2] nil If the file does not exist, or cannot be opened.
* @cc.treturn string|nil A message explaining why the file cannot be opened.
* @cc.usage Read the contents of a file.
* <pre>{@code
* local file = fs.open("/rom/help/intro.txt", "r")
* local contents = file.readAll()
* file.close()
*
* print(contents)
* }</pre>
* @cc.usage Open a file and read all lines into a table. @{io.lines} offers an alternative way to do this.
* <pre>{@code
* local file = fs.open("/rom/motd.txt", "r")
* local lines = {}
* while true do
* local line = file.readLine()
*
* -- If line is nil then we've reached the end of the file and should stop
* if not line then break end
*
* lines[#lines + 1] = line
* end
*
* file.close()
*
* print(lines[math.random(#lines)]) -- Pick a random line and print it.
* }</pre>
* @cc.usage Open a file and write some text to it. You can run {@code edit out.txt} to see the written text.
* <pre>{@code
* local file = fs.open("out.txt", "w")
* file.write("Just testing some code")
* file.close() -- Remember to call close, otherwise changes may not be written!
* }</pre>
*/
@LuaFunction
public final Object[] open( String path, String mode ) throws LuaException
@@ -399,6 +457,7 @@ public class FSAPI implements ILuaAPI
* @param path The path to check the free space for.
* @return The amount of free space available, in bytes.
* @throws LuaException If the path doesn't exist.
* @see #getCapacity To get the capacity of this drive.
* @cc.treturn number|"unlimited" The amount of free space available, in bytes, or "unlimited".
*/
@LuaFunction
@@ -442,15 +501,12 @@ public class FSAPI implements ILuaAPI
}
/**
* Returns true if a path is mounted to the parent filesystem.
*
* The root filesystem "/" is considered a mount, along with disk folders and the rom folder. Other programs
* (such as network shares) can extend this to make other mount types by correctly assigning their return value for
* getDrive.
* Returns the capacity of the drive the path is located on.
*
* @param path The path of the drive to get.
* @return The drive's capacity.
* @throws LuaException If the capacity cannot be determined.
* @see #getFreeSpace To get the free space available on this drive.
* @cc.treturn number|nil This drive's capacity. This will be nil for "read-only" drives, such as the ROM or
* treasure disks.
*/

View File

@@ -37,7 +37,7 @@ public class HTTPAPI implements ILuaAPI
{
private final IAPIEnvironment apiEnvironment;
private final ResourceGroup<CheckUrl> checkUrls = new ResourceGroup<>();
private final ResourceGroup<CheckUrl> checkUrls = new ResourceGroup<>( ResourceGroup.DEFAULT );
private final ResourceGroup<HttpRequest> requests = new ResourceQueue<>( () -> ComputerCraft.httpMaxRequests );
private final ResourceGroup<Websocket> websockets = new ResourceGroup<>( () -> ComputerCraft.httpMaxWebsockets );
@@ -127,7 +127,10 @@ public class HTTPAPI implements ILuaAPI
HttpRequest request = new HttpRequest( requests, apiEnvironment, address, postString, headers, binary, redirect );
// Make the request
request.queue( r -> r.request( uri, httpMethod ) );
if( !request.queue( r -> r.request( uri, httpMethod ) ) )
{
throw new LuaException( "Too many ongoing HTTP requests" );
}
return new Object[] { true };
}
@@ -138,12 +141,15 @@ public class HTTPAPI implements ILuaAPI
}
@LuaFunction
public final Object[] checkURL( String address )
public final Object[] checkURL( String address ) throws LuaException
{
try
{
URI uri = HttpRequest.checkUri( address );
new CheckUrl( checkUrls, apiEnvironment, address, uri ).queue( CheckUrl::run );
if( !new CheckUrl( checkUrls, apiEnvironment, address, uri ).queue( CheckUrl::run ) )
{
throw new LuaException( "Too many ongoing checkUrl calls" );
}
return new Object[] { true };
}

View File

@@ -62,6 +62,7 @@ public interface IAPIEnvironment
@Nullable
IPeripheral getPeripheral( ComputerSide side );
@Nullable
String getLabel();
void setLabel( @Nullable String label );

View File

@@ -11,19 +11,26 @@ import dan200.computercraft.core.apis.http.options.AddressRule;
import dan200.computercraft.core.apis.http.options.Options;
import dan200.computercraft.shared.util.ThreadUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ConnectTimeoutException;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.TooLongFrameException;
import io.netty.handler.codec.http.websocketx.WebSocketHandshakeException;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.timeout.ReadTimeoutException;
import io.netty.handler.traffic.AbstractTrafficShapingHandler;
import io.netty.handler.traffic.GlobalTrafficShapingHandler;
import javax.annotation.Nonnull;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManagerFactory;
import java.net.InetSocketAddress;
import java.net.URI;
import java.security.KeyStore;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
@@ -31,10 +38,8 @@ import java.util.concurrent.TimeUnit;
*/
public final class NetworkUtils
{
public static final ExecutorService EXECUTOR = new ThreadPoolExecutor(
4, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<>(),
public static final ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(
4,
ThreadUtils.builder( "Network" )
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
.build()
@@ -45,6 +50,15 @@ public final class NetworkUtils
.build()
);
public static final AbstractTrafficShapingHandler SHAPING_HANDLER = new GlobalTrafficShapingHandler(
EXECUTOR, ComputerCraft.httpUploadBandwidth, ComputerCraft.httpDownloadBandwidth
);
static
{
EXECUTOR.setKeepAliveTime( 60, TimeUnit.SECONDS );
}
private NetworkUtils()
{
}
@@ -100,6 +114,16 @@ public final class NetworkUtils
}
}
public static void reloadConfig()
{
SHAPING_HANDLER.configure( ComputerCraft.httpUploadBandwidth, ComputerCraft.httpDownloadBandwidth );
}
public static void reset()
{
SHAPING_HANDLER.trafficCounter().resetCumulativeTime();
}
/**
* Create a {@link InetSocketAddress} from a {@link java.net.URI}.
*
@@ -161,4 +185,29 @@ public final class NetworkUtils
buffer.readBytes( bytes );
return bytes;
}
@Nonnull
public static String toFriendlyError( @Nonnull Throwable cause )
{
if( cause instanceof WebSocketHandshakeException || cause instanceof HTTPRequestException )
{
return cause.getMessage();
}
else if( cause instanceof TooLongFrameException )
{
return "Message is too large";
}
else if( cause instanceof ReadTimeoutException || cause instanceof ConnectTimeoutException )
{
return "Timed out";
}
else if( cause instanceof SSLHandshakeException || (cause instanceof DecoderException && cause.getCause() instanceof SSLHandshakeException) )
{
return "Could not create a secure connection";
}
else
{
return "Could not connect";
}
}
}

View File

@@ -97,7 +97,7 @@ public abstract class Resource<T extends Resource<T>> implements Closeable
tryClose();
}
public boolean queue( Consumer<T> task )
public final boolean queue( Consumer<T> task )
{
@SuppressWarnings( "unchecked" )
T thisT = (T) this;

View File

@@ -18,6 +18,9 @@ import java.util.function.Supplier;
*/
public class ResourceGroup<T extends Resource<T>>
{
public static final int DEFAULT_LIMIT = 512;
public static final IntSupplier DEFAULT = () -> DEFAULT_LIMIT;
private static final IntSupplier ZERO = () -> 0;
final IntSupplier limit;

View File

@@ -38,8 +38,10 @@ public class ResourceQueue<T extends Resource<T>> extends ResourceGroup<T>
public synchronized boolean queue( Supplier<T> resource )
{
if( !active ) return false;
if( super.queue( resource ) ) return true;
if( pending.size() > DEFAULT_LIMIT ) return false;
if( !super.queue( resource ) ) pending.add( resource );
pending.add( resource );
return true;
}

View File

@@ -19,13 +19,10 @@ import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ConnectTimeoutException;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.TooLongFrameException;
import io.netty.handler.codec.http.*;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.timeout.ReadTimeoutException;
import io.netty.handler.timeout.ReadTimeoutHandler;
import java.net.InetSocketAddress;
@@ -170,6 +167,7 @@ public class HttpRequest extends Resource<HttpRequest>
}
ChannelPipeline p = ch.pipeline();
p.addLast( NetworkUtils.SHAPING_HANDLER );
if( sslContext != null )
{
p.addLast( sslContext.newHandler( ch.alloc(), uri.getHost(), socketAddress.getPort() ) );
@@ -190,7 +188,7 @@ public class HttpRequest extends Resource<HttpRequest>
.remoteAddress( socketAddress )
.connect()
.addListener( c -> {
if( !c.isSuccess() ) failure( c.cause() );
if( !c.isSuccess() ) failure( NetworkUtils.toFriendlyError( c.cause() ) );
} );
// Do an additional check for cancellation
@@ -202,7 +200,7 @@ public class HttpRequest extends Resource<HttpRequest>
}
catch( Exception e )
{
failure( "Could not connect" );
failure( NetworkUtils.toFriendlyError( e ) );
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error in HTTP request", e );
}
}
@@ -212,29 +210,6 @@ public class HttpRequest extends Resource<HttpRequest>
if( tryClose() ) environment.queueEvent( FAILURE_EVENT, address, message );
}
void failure( Throwable cause )
{
String message;
if( cause instanceof HTTPRequestException )
{
message = cause.getMessage();
}
else if( cause instanceof TooLongFrameException )
{
message = "Response is too large";
}
else if( cause instanceof ReadTimeoutException || cause instanceof ConnectTimeoutException )
{
message = "Timed out";
}
else
{
message = "Could not connect";
}
failure( message );
}
void failure( String message, HttpResponseHandle object )
{
if( tryClose() ) environment.queueEvent( FAILURE_EVENT, address, message, object );

View File

@@ -183,7 +183,7 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<HttpOb
public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause )
{
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error handling HTTP response", cause );
request.failure( cause );
request.failure( NetworkUtils.toFriendlyError( cause ) );
}
private void sendResponse()

View File

@@ -22,12 +22,12 @@ import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
import io.netty.handler.ssl.SslContext;
import java.lang.ref.WeakReference;
@@ -145,20 +145,22 @@ public class Websocket extends Resource<Websocket>
protected void initChannel( SocketChannel ch )
{
ChannelPipeline p = ch.pipeline();
p.addLast( NetworkUtils.SHAPING_HANDLER );
if( sslContext != null )
{
p.addLast( sslContext.newHandler( ch.alloc(), uri.getHost(), socketAddress.getPort() ) );
}
String subprotocol = headers.get( HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL );
WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(
uri, WebSocketVersion.V13, null, true, headers,
uri, WebSocketVersion.V13, subprotocol, true, headers,
options.websocketMessage <= 0 ? MAX_MESSAGE_SIZE : options.websocketMessage
);
p.addLast(
new HttpClientCodec(),
new HttpObjectAggregator( 8192 ),
WebSocketClientCompressionHandler.INSTANCE,
WebsocketCompressionHandler.INSTANCE,
new WebsocketHandler( Websocket.this, handshaker, options )
);
}
@@ -166,7 +168,7 @@ public class Websocket extends Resource<Websocket>
.remoteAddress( socketAddress )
.connect()
.addListener( c -> {
if( !c.isSuccess() ) failure( c.cause().getMessage() );
if( !c.isSuccess() ) failure( NetworkUtils.toFriendlyError( c.cause() ) );
} );
// Do an additional check for cancellation
@@ -178,7 +180,7 @@ public class Websocket extends Resource<Websocket>
}
catch( Exception e )
{
failure( "Could not connect" );
failure( NetworkUtils.toFriendlyError( e ) );
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error in websocket", e );
}
}

View File

@@ -0,0 +1,38 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis.http.websocket;
import io.netty.channel.ChannelHandler;
import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.DeflateFrameClientExtensionHandshaker;
import io.netty.handler.codec.http.websocketx.extensions.compression.PerMessageDeflateClientExtensionHandshaker;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
import static io.netty.handler.codec.http.websocketx.extensions.compression.PerMessageDeflateServerExtensionHandshaker.MAX_WINDOW_SIZE;
/**
* An alternative to {@link WebSocketClientCompressionHandler} which supports the {@literal client_no_context_takeover}
* extension. Makes CC <em>slightly</em> more flexible.
*/
@ChannelHandler.Sharable
final class WebsocketCompressionHandler extends WebSocketClientExtensionHandler
{
public static final WebsocketCompressionHandler INSTANCE = new WebsocketCompressionHandler();
private WebsocketCompressionHandler()
{
super(
new PerMessageDeflateClientExtensionHandshaker(
6, ZlibCodecFactory.isSupportingWindowSizeAndMemLevel(), MAX_WINDOW_SIZE,
true, false
),
new DeflateFrameClientExtensionHandshaker( false ),
new DeflateFrameClientExtensionHandshaker( true )
);
}
}

View File

@@ -5,17 +5,13 @@
*/
package dan200.computercraft.core.apis.http.websocket;
import dan200.computercraft.core.apis.http.HTTPRequestException;
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.apis.http.options.Options;
import dan200.computercraft.core.tracking.TrackingField;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ConnectTimeoutException;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.TooLongFrameException;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.ReadTimeoutException;
import io.netty.util.CharsetUtil;
import static dan200.computercraft.core.apis.http.websocket.Websocket.MESSAGE_EVENT;
@@ -97,24 +93,7 @@ public class WebsocketHandler extends SimpleChannelInboundHandler<Object>
{
ctx.close();
String message;
if( cause instanceof WebSocketHandshakeException || cause instanceof HTTPRequestException )
{
message = cause.getMessage();
}
else if( cause instanceof TooLongFrameException )
{
message = "Message is too large";
}
else if( cause instanceof ReadTimeoutException || cause instanceof ConnectTimeoutException )
{
message = "Timed out";
}
else
{
message = "Could not connect";
}
String message = NetworkUtils.toFriendlyError( cause );
if( handshaker.isHandshakeComplete() )
{
websocket.close( -1, message );

View File

@@ -11,7 +11,10 @@ import com.google.common.cache.LoadingCache;
import com.google.common.primitives.Primitives;
import com.google.common.reflect.TypeToken;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.*;
import dan200.computercraft.api.lua.IArguments;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.lua.MethodResult;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
@@ -63,7 +66,7 @@ public final class Generator<T>
{
this.base = base;
this.context = context;
this.interfaces = new String[] { Type.getInternalName( base ) };
interfaces = new String[] { Type.getInternalName( base ) };
this.wrap = wrap;
StringBuilder methodDesc = new StringBuilder().append( "(Ljava/lang/Object;" );

View File

@@ -251,6 +251,20 @@ final class ComputerExecutor
* and then schedule a shutdown.
*/
void abort()
{
immediateFail( StateCommand.ABORT );
}
/**
* Abort this whole computer due to an internal error. This will immediately destroy the Lua machine,
* and then schedule a shutdown.
*/
void fastFail()
{
immediateFail( StateCommand.ERROR );
}
private void immediateFail( StateCommand command )
{
ILuaMachine machine = this.machine;
if( machine != null ) machine.close();
@@ -258,7 +272,7 @@ final class ComputerExecutor
synchronized( queueLock )
{
if( closed ) return;
command = StateCommand.ABORT;
this.command = command;
if( isOn ) enqueue();
}
}
@@ -596,6 +610,12 @@ final class ComputerExecutor
displayFailure( "Error running computer", TimeoutState.ABORT_MESSAGE );
shutdown();
break;
case ERROR:
if( !isOn ) return;
displayFailure( "Error running computer", "An internal error occurred, see logs." );
shutdown();
break;
}
}
else if( event != null )
@@ -644,6 +664,7 @@ final class ComputerExecutor
SHUTDOWN,
REBOOT,
ABORT,
ERROR,
}
private static final class Event

View File

@@ -506,6 +506,8 @@ public final class ComputerThread
catch( Exception | LinkageError | VirtualMachineError e )
{
ComputerCraft.log.error( "Error running task on computer #" + executor.getComputer().getID(), e );
// Tear down the computer immediately. There's no guarantee it's well behaved from now on.
executor.fastFail();
}
finally
{

View File

@@ -10,7 +10,6 @@ import dan200.computercraft.api.lua.*;
import dan200.computercraft.core.asm.LuaMethod;
import dan200.computercraft.core.asm.ObjectSource;
import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.MainThread;
import dan200.computercraft.core.computer.TimeoutState;
import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.core.tracking.TrackingField;
@@ -53,7 +52,7 @@ public class CobaltLuaMachine implements ILuaMachine
private final Computer computer;
private final TimeoutState timeout;
private final TimeoutDebugHandler debug;
private final ILuaContext context = new CobaltLuaContext();
private final ILuaContext context;
private LuaState state;
private LuaTable globals;
@@ -65,6 +64,7 @@ public class CobaltLuaMachine implements ILuaMachine
{
this.computer = computer;
this.timeout = timeout;
context = new LuaContext( computer );
debug = new TimeoutDebugHandler();
// Create an environment to run in
@@ -97,7 +97,7 @@ public class CobaltLuaMachine implements ILuaMachine
globals.load( state, new CoroutineLib() );
globals.load( state, new Bit32Lib() );
globals.load( state, new Utf8Lib() );
if( ComputerCraft.debugEnable ) globals.load( state, new DebugLib() );
globals.load( state, new DebugLib() );
// Remove globals we don't want to expose
globals.rawset( "collectgarbage", Constants.NIL );
@@ -509,53 +509,6 @@ public class CobaltLuaMachine implements ILuaMachine
}
}
private class CobaltLuaContext implements ILuaContext
{
@Override
public long issueMainThreadTask( @Nonnull final ILuaTask task ) throws LuaException
{
// Issue command
final long taskID = MainThread.getUniqueTaskID();
final Runnable iTask = () -> {
try
{
Object[] results = task.execute();
if( results != null )
{
Object[] eventArguments = new Object[results.length + 2];
eventArguments[0] = taskID;
eventArguments[1] = true;
System.arraycopy( results, 0, eventArguments, 2, results.length );
computer.queueEvent( "task_complete", eventArguments );
}
else
{
computer.queueEvent( "task_complete", new Object[] { taskID, true } );
}
}
catch( LuaException e )
{
computer.queueEvent( "task_complete", new Object[] { taskID, false, e.getMessage() } );
}
catch( Throwable t )
{
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error running task", t );
computer.queueEvent( "task_complete", new Object[] {
taskID, false, "Java Exception Thrown: " + t,
} );
}
};
if( computer.queueMainThread( iTask ) )
{
return taskID;
}
else
{
throw new LuaException( "Task limit exceeded" );
}
}
}
private static final class HardAbortError extends Error
{
private static final long serialVersionUID = 7954092008586367501L;

View File

@@ -0,0 +1,78 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.lua;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaTask;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.MethodResult;
import dan200.computercraft.core.asm.TaskCallback;
import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.MainThread;
import javax.annotation.Nonnull;
class LuaContext implements ILuaContext
{
private final Computer computer;
LuaContext( Computer computer )
{
this.computer = computer;
}
@Override
public long issueMainThreadTask( @Nonnull final ILuaTask task ) throws LuaException
{
// Issue command
final long taskID = MainThread.getUniqueTaskID();
final Runnable iTask = () -> {
try
{
Object[] results = task.execute();
if( results != null )
{
Object[] eventArguments = new Object[results.length + 2];
eventArguments[0] = taskID;
eventArguments[1] = true;
System.arraycopy( results, 0, eventArguments, 2, results.length );
computer.queueEvent( "task_complete", eventArguments );
}
else
{
computer.queueEvent( "task_complete", new Object[] { taskID, true } );
}
}
catch( LuaException e )
{
computer.queueEvent( "task_complete", new Object[] { taskID, false, e.getMessage() } );
}
catch( Exception t )
{
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error running task", t );
computer.queueEvent( "task_complete", new Object[] {
taskID, false, "Java Exception Thrown: " + t,
} );
}
};
if( computer.queueMainThread( iTask ) )
{
return taskID;
}
else
{
throw new LuaException( "Task limit exceeded" );
}
}
@Nonnull
@Override
public MethodResult executeMainThreadTask( @Nonnull ILuaTask task ) throws LuaException
{
return TaskCallback.make( this, task );
}
}

View File

@@ -44,9 +44,9 @@ public class Terminal
this.height = height;
onChanged = changedCallback;
text = new TextBuffer[this.height];
textColour = new TextBuffer[this.height];
backgroundColour = new TextBuffer[this.height];
text = new TextBuffer[height];
textColour = new TextBuffer[height];
backgroundColour = new TextBuffer[height];
for( int i = 0; i < this.height; i++ )
{
text[i] = new TextBuffer( ' ', this.width );
@@ -93,9 +93,9 @@ public class Terminal
this.width = width;
this.height = height;
text = new TextBuffer[this.height];
textColour = new TextBuffer[this.height];
backgroundColour = new TextBuffer[this.height];
text = new TextBuffer[height];
textColour = new TextBuffer[height];
backgroundColour = new TextBuffer[height];
for( int i = 0; i < this.height; i++ )
{
if( i >= oldHeight )

View File

@@ -12,7 +12,7 @@ public class TextBuffer
public TextBuffer( char c, int length )
{
text = new char[length];
this.fill( c );
fill( c );
}
public TextBuffer( String text )
@@ -79,6 +79,7 @@ public class TextBuffer
}
}
@Override
public String toString()
{
return new String( text );

View File

@@ -0,0 +1,155 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.tracking;
import com.google.common.base.CaseFormat;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.core.computer.Computer;
import net.minecraft.util.text.LanguageMap;
import javax.annotation.Nonnull;
import javax.management.*;
import java.lang.management.ManagementFactory;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.LongSupplier;
public final class ComputerMBean implements DynamicMBean, Tracker
{
private static final Set<TrackingField> SKIP = new HashSet<>( Arrays.asList(
TrackingField.TASKS, TrackingField.TOTAL_TIME, TrackingField.AVERAGE_TIME, TrackingField.MAX_TIME,
TrackingField.SERVER_COUNT, TrackingField.SERVER_TIME
) );
private static ComputerMBean instance;
private final Map<String, LongSupplier> attributes = new HashMap<>();
private final Map<TrackingField, Counter> values = new HashMap<>();
private final MBeanInfo info;
private ComputerMBean()
{
List<MBeanAttributeInfo> attributes = new ArrayList<>();
for( Map.Entry<String, TrackingField> field : TrackingField.fields().entrySet() )
{
if( SKIP.contains( field.getValue() ) ) continue;
String name = CaseFormat.LOWER_UNDERSCORE.to( CaseFormat.LOWER_CAMEL, field.getKey() );
add( name, field.getValue(), attributes, null );
}
add( "task", TrackingField.TOTAL_TIME, attributes, TrackingField.TASKS );
add( "serverTask", TrackingField.SERVER_TIME, attributes, TrackingField.SERVER_COUNT );
this.info = new MBeanInfo(
ComputerMBean.class.getSimpleName(),
"metrics about all computers on the server",
attributes.toArray( new MBeanAttributeInfo[0] ), null, null, null
);
}
public static void register()
{
try
{
ManagementFactory.getPlatformMBeanServer().registerMBean( instance = new ComputerMBean(), new ObjectName( "dan200.computercraft:type=Computers" ) );
}
catch( InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException | MalformedObjectNameException e )
{
ComputerCraft.log.warn( "Failed to register JMX bean", e );
}
}
public static void registerTracker()
{
if( instance != null ) Tracking.add( instance );
}
@Override
public Object getAttribute( String attribute ) throws AttributeNotFoundException, MBeanException, ReflectionException
{
LongSupplier value = attributes.get( attribute );
if( value == null ) throw new AttributeNotFoundException();
return value.getAsLong();
}
@Override
public void setAttribute( Attribute attribute ) throws InvalidAttributeValueException
{
throw new InvalidAttributeValueException( "Cannot set attribute" );
}
@Override
public AttributeList getAttributes( String[] attributes )
{
return null;
}
@Override
public AttributeList setAttributes( AttributeList attributes )
{
return new AttributeList();
}
@Override
public Object invoke( String actionName, Object[] params, String[] signature ) throws MBeanException, ReflectionException
{
return null;
}
@Override
@Nonnull
public MBeanInfo getMBeanInfo()
{
return info;
}
@Override
public void addTaskTiming( Computer computer, long time )
{
addValue( computer, TrackingField.TOTAL_TIME, time );
}
@Override
public void addServerTiming( Computer computer, long time )
{
addValue( computer, TrackingField.SERVER_TIME, time );
}
@Override
public void addValue( Computer computer, TrackingField field, long change )
{
Counter counter = values.get( field );
counter.value.addAndGet( change );
counter.count.incrementAndGet();
}
private MBeanAttributeInfo addAttribute( String name, String description, LongSupplier value )
{
attributes.put( name, value );
return new MBeanAttributeInfo( name, "long", description, true, false, false );
}
private void add( String name, TrackingField field, List<MBeanAttributeInfo> attributes, TrackingField count )
{
Counter counter = new Counter();
values.put( field, counter );
String prettyName = LanguageMap.getInstance().getOrDefault( field.translationKey() );
attributes.add( addAttribute( name, prettyName, counter.value::longValue ) );
if( count != null )
{
String countName = LanguageMap.getInstance().getOrDefault( count.translationKey() );
attributes.add( addAttribute( name + "Count", countName, counter.count::longValue ) );
}
}
private static class Counter
{
AtomicLong value = new AtomicLong();
AtomicLong count = new AtomicLong();
}
}

View File

@@ -16,6 +16,7 @@ import net.minecraft.data.DataGenerator;
import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.generators.*;
import net.minecraftforge.common.data.ExistingFileHelper;
import javax.annotation.Nonnull;

View File

@@ -5,7 +5,7 @@
*/
package dan200.computercraft.data;
import dan200.computercraft.shared.proxy.ComputerCraftProxyCommon;
import dan200.computercraft.shared.Registry;
import net.minecraft.data.DataGenerator;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
@@ -17,12 +17,12 @@ public class Generators
@SubscribeEvent
public static void gather( GatherDataEvent event )
{
ComputerCraftProxyCommon.registerLoot();
Registry.registerLoot();
DataGenerator generator = event.getGenerator();
generator.addProvider( new Recipes( generator ) );
generator.addProvider( new LootTables( generator ) );
generator.addProvider( new Tags( generator ) );
generator.addProvider( new Tags( generator, event.getExistingFileHelper() ) );
generator.addProvider( new BlockModelProvider( generator, event.getExistingFileHelper() ) );
}
}

View File

@@ -12,11 +12,11 @@ import dan200.computercraft.ComputerCraft;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.DirectoryCache;
import net.minecraft.data.IDataProvider;
import net.minecraft.loot.LootParameterSets;
import net.minecraft.loot.LootTable;
import net.minecraft.loot.LootTableManager;
import net.minecraft.loot.ValidationTracker;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.storage.loot.LootParameterSets;
import net.minecraft.world.storage.loot.LootTable;
import net.minecraft.world.storage.loot.LootTableManager;
import net.minecraft.world.storage.loot.ValidationTracker;
import javax.annotation.Nonnull;
import java.io.IOException;

View File

@@ -6,17 +6,17 @@
package dan200.computercraft.data;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.CommonHooks;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
import dan200.computercraft.shared.data.HasComputerIdLootCondition;
import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
import dan200.computercraft.shared.proxy.ComputerCraftProxyCommon;
import net.minecraft.block.Block;
import net.minecraft.data.DataGenerator;
import net.minecraft.loot.*;
import net.minecraft.loot.conditions.Alternative;
import net.minecraft.loot.conditions.SurvivesExplosion;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.storage.loot.*;
import net.minecraft.world.storage.loot.conditions.Alternative;
import net.minecraft.world.storage.loot.conditions.SurvivesExplosion;
import net.minecraftforge.fml.RegistryObject;
import java.util.function.BiConsumer;
@@ -46,7 +46,7 @@ public class LootTables extends LootTableProvider
computerDrop( add, Registry.ModBlocks.TURTLE_NORMAL );
computerDrop( add, Registry.ModBlocks.TURTLE_ADVANCED );
add.accept( ComputerCraftProxyCommon.ForgeHandlers.LOOT_TREASURE_DISK, LootTable
add.accept( CommonHooks.LOOT_TREASURE_DISK, LootTable
.lootTable()
.setParamSet( LootParameterSets.ALL_PARAMS )
.build() );
@@ -77,9 +77,9 @@ public class LootTables extends LootTableProvider
.setRolls( ConstantRange.exactly( 1 ) )
.add( DynamicLootEntry.dynamicEntry( new ResourceLocation( ComputerCraft.MOD_ID, "computer" ) ) )
.when( Alternative.alternative(
BlockNamedEntityLootCondition.builder(),
HasComputerIdLootCondition.builder(),
PlayerCreativeLootCondition.builder().invert()
BlockNamedEntityLootCondition.BUILDER,
HasComputerIdLootCondition.BUILDER,
PlayerCreativeLootCondition.BUILDER.invert()
) )
).build() );
}

View File

@@ -22,7 +22,7 @@ import net.minecraft.advancements.criterion.ItemPredicate;
import net.minecraft.block.Blocks;
import net.minecraft.data.*;
import net.minecraft.item.*;
import net.minecraft.tags.Tag;
import net.minecraft.tags.ITag;
import net.minecraft.util.IItemProvider;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.Tags;
@@ -62,7 +62,7 @@ public class Recipes extends RecipeProvider
.requires( Items.PAPER )
.requires( DyeItem.byColor( ofColour( colour ) ) )
.group( "computercraft:disk" )
.unlocks( "has_drive", inventoryChange( Registry.ModBlocks.DISK_DRIVE.get() ) )
.unlockedBy( "has_drive", inventoryChange( Registry.ModBlocks.DISK_DRIVE.get() ) )
.save( RecipeWrapper.wrap(
ImpostorShapelessRecipe.SERIALIZER, add,
x -> x.putInt( IColouredItem.NBT_COLOUR, colour.getHex() )
@@ -92,7 +92,7 @@ public class Recipes extends RecipeProvider
.pattern( "#T" )
.define( '#', base.getItem() )
.define( 'T', upgrade.getCraftingItem().getItem() )
.unlocks( "has_items",
.unlockedBy( "has_items",
inventoryChange( base.getItem(), upgrade.getCraftingItem().getItem() ) )
.save(
RecipeWrapper.wrap( ImpostorRecipe.SERIALIZER, add, result.getTag() ),
@@ -127,7 +127,7 @@ public class Recipes extends RecipeProvider
.pattern( "P" )
.define( '#', base.getItem() )
.define( 'P', upgrade.getCraftingItem().getItem() )
.unlocks( "has_items",
.unlockedBy( "has_items",
inventoryChange( base.getItem(), upgrade.getCraftingItem().getItem() ) )
.save(
RecipeWrapper.wrap( ImpostorRecipe.SERIALIZER, add, result.getTag() ),
@@ -148,8 +148,8 @@ public class Recipes extends RecipeProvider
.pattern( " # " )
.define( '#', Tags.Items.STONE )
.define( 'R', Tags.Items.DUSTS_REDSTONE )
.unlocks( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlocks( "has_modem", inventoryChange( CCTags.COMPUTER ) )
.unlockedBy( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlockedBy( "has_modem", inventoryChange( CCTags.COMPUTER ) )
.save( add );
ShapedRecipeBuilder
@@ -160,7 +160,7 @@ public class Recipes extends RecipeProvider
.define( '#', Tags.Items.STONE )
.define( 'R', Tags.Items.DUSTS_REDSTONE )
.define( 'G', Tags.Items.GLASS_PANES )
.unlocks( "has_redstone", inventoryChange( Tags.Items.DUSTS_REDSTONE ) )
.unlockedBy( "has_redstone", inventoryChange( Tags.Items.DUSTS_REDSTONE ) )
.save( add );
ShapedRecipeBuilder
@@ -171,7 +171,7 @@ public class Recipes extends RecipeProvider
.define( '#', Tags.Items.INGOTS_GOLD )
.define( 'R', Tags.Items.DUSTS_REDSTONE )
.define( 'G', Tags.Items.GLASS_PANES )
.unlocks( "has_components", inventoryChange( Items.REDSTONE, Items.GOLD_INGOT ) )
.unlockedBy( "has_components", inventoryChange( Items.REDSTONE, Items.GOLD_INGOT ) )
.save( add );
ShapedRecipeBuilder
@@ -182,7 +182,7 @@ public class Recipes extends RecipeProvider
.define( '#', Tags.Items.INGOTS_GOLD )
.define( 'R', Blocks.COMMAND_BLOCK )
.define( 'G', Tags.Items.GLASS_PANES )
.unlocks( "has_components", inventoryChange( Blocks.COMMAND_BLOCK ) )
.unlockedBy( "has_components", inventoryChange( Blocks.COMMAND_BLOCK ) )
.save( add );
ShapedRecipeBuilder
@@ -192,7 +192,7 @@ public class Recipes extends RecipeProvider
.pattern( "#R#" )
.define( '#', Tags.Items.STONE )
.define( 'R', Tags.Items.DUSTS_REDSTONE )
.unlocks( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlockedBy( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.save( add );
ShapedRecipeBuilder
@@ -202,7 +202,7 @@ public class Recipes extends RecipeProvider
.pattern( "###" )
.define( '#', Tags.Items.STONE )
.define( 'G', Tags.Items.GLASS_PANES )
.unlocks( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlockedBy( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.save( add );
ShapedRecipeBuilder
@@ -212,7 +212,7 @@ public class Recipes extends RecipeProvider
.pattern( "###" )
.define( '#', Tags.Items.INGOTS_GOLD )
.define( 'G', Tags.Items.GLASS_PANES )
.unlocks( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlockedBy( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.save( add );
ShapedRecipeBuilder
@@ -223,8 +223,8 @@ public class Recipes extends RecipeProvider
.define( '#', Tags.Items.STONE )
.define( 'A', Items.GOLDEN_APPLE )
.define( 'G', Tags.Items.GLASS_PANES )
.unlocks( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlocks( "has_apple", inventoryChange( Items.GOLDEN_APPLE ) )
.unlockedBy( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlockedBy( "has_apple", inventoryChange( Items.GOLDEN_APPLE ) )
.save( add );
ShapedRecipeBuilder
@@ -235,8 +235,8 @@ public class Recipes extends RecipeProvider
.define( '#', Tags.Items.INGOTS_GOLD )
.define( 'A', Items.GOLDEN_APPLE )
.define( 'G', Tags.Items.GLASS_PANES )
.unlocks( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlocks( "has_apple", inventoryChange( Items.GOLDEN_APPLE ) )
.unlockedBy( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlockedBy( "has_apple", inventoryChange( Items.GOLDEN_APPLE ) )
.save( add );
ShapedRecipeBuilder
@@ -247,7 +247,7 @@ public class Recipes extends RecipeProvider
.define( '#', Tags.Items.STONE )
.define( 'R', Tags.Items.DUSTS_REDSTONE )
.define( 'D', Tags.Items.DYES )
.unlocks( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlockedBy( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.save( add );
ShapedRecipeBuilder
@@ -258,7 +258,7 @@ public class Recipes extends RecipeProvider
.define( '#', Tags.Items.STONE )
.define( 'N', Blocks.NOTE_BLOCK )
.define( 'R', Tags.Items.DUSTS_REDSTONE )
.unlocks( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlockedBy( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.save( add );
ShapedRecipeBuilder
@@ -268,19 +268,19 @@ public class Recipes extends RecipeProvider
.pattern( "###" )
.define( '#', Tags.Items.STONE )
.define( 'R', Tags.Items.DUSTS_REDSTONE )
.unlocks( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlocks( "has_cable", inventoryChange( Registry.ModItems.CABLE.get() ) )
.unlockedBy( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlockedBy( "has_cable", inventoryChange( Registry.ModItems.CABLE.get() ) )
.save( add );
ShapelessRecipeBuilder
.shapeless( Registry.ModBlocks.WIRED_MODEM_FULL.get() )
.requires( Registry.ModItems.WIRED_MODEM.get() )
.unlocks( "has_modem", inventoryChange( CCTags.WIRED_MODEM ) )
.unlockedBy( "has_modem", inventoryChange( CCTags.WIRED_MODEM ) )
.save( add, new ResourceLocation( ComputerCraft.MOD_ID, "wired_modem_full_from" ) );
ShapelessRecipeBuilder
.shapeless( Registry.ModItems.WIRED_MODEM.get() )
.requires( Registry.ModBlocks.WIRED_MODEM_FULL.get() )
.unlocks( "has_modem", inventoryChange( CCTags.WIRED_MODEM ) )
.unlockedBy( "has_modem", inventoryChange( CCTags.WIRED_MODEM ) )
.save( add, new ResourceLocation( ComputerCraft.MOD_ID, "wired_modem_full_to" ) );
ShapedRecipeBuilder
@@ -290,7 +290,7 @@ public class Recipes extends RecipeProvider
.pattern( "###" )
.define( '#', Tags.Items.STONE )
.define( 'E', Tags.Items.ENDER_PEARLS )
.unlocks( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlockedBy( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.save( add );
ShapedRecipeBuilder
@@ -300,8 +300,8 @@ public class Recipes extends RecipeProvider
.pattern( "###" )
.define( '#', Tags.Items.INGOTS_GOLD )
.define( 'E', Items.ENDER_EYE )
.unlocks( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlocks( "has_wireless", inventoryChange( Registry.ModBlocks.WIRELESS_MODEM_NORMAL.get() ) )
.unlockedBy( "has_computer", inventoryChange( CCTags.COMPUTER ) )
.unlockedBy( "has_wireless", inventoryChange( Registry.ModBlocks.WIRELESS_MODEM_NORMAL.get() ) )
.save( add );
}
@@ -310,13 +310,13 @@ public class Recipes extends RecipeProvider
return DyeColor.byId( 15 - colour.ordinal() );
}
private static InventoryChangeTrigger.Instance inventoryChange( Tag<Item> stack )
private static InventoryChangeTrigger.Instance inventoryChange( ITag<Item> stack )
{
return InventoryChangeTrigger.Instance.hasItem( ItemPredicate.Builder.item().of( stack ).build() );
return InventoryChangeTrigger.Instance.hasItems( ItemPredicate.Builder.item().of( stack ).build() );
}
private static InventoryChangeTrigger.Instance inventoryChange( IItemProvider... stack )
{
return InventoryChangeTrigger.Instance.hasItem( stack );
return InventoryChangeTrigger.Instance.hasItems( stack );
}
}

View File

@@ -7,46 +7,55 @@ package dan200.computercraft.data;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.Registry;
import net.minecraft.data.BlockTagsProvider;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.ItemTagsProvider;
import net.minecraft.item.Item;
import net.minecraft.tags.ITag;
import net.minecraft.tags.ItemTags;
import net.minecraft.tags.Tag;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.data.ExistingFileHelper;
import static dan200.computercraft.data.Tags.CCTags.*;
public class Tags extends ItemTagsProvider
{
private static final ITag.INamedTag<Item> PIGLIN_LOVED = ItemTags.PIGLIN_LOVED;
public static class CCTags
{
public static final Tag<Item> COMPUTER = item( "computer" );
public static final Tag<Item> TURTLE = item( "turtle" );
public static final Tag<Item> WIRED_MODEM = item( "wired_modem" );
public static final Tag<Item> MONITOR = item( "monitor" );
public static final ITag.INamedTag<Item> COMPUTER = item( "computer" );
public static final ITag.INamedTag<Item> TURTLE = item( "turtle" );
public static final ITag.INamedTag<Item> WIRED_MODEM = item( "wired_modem" );
public static final ITag.INamedTag<Item> MONITOR = item( "monitor" );
}
public Tags( DataGenerator generator )
public Tags( DataGenerator generator, ExistingFileHelper helper )
{
super( generator );
super( generator, new BlockTagsProvider( generator, ComputerCraft.MOD_ID, helper ), ComputerCraft.MOD_ID, helper );
}
@Override
protected void addTags()
{
tag( COMPUTER )
.add( Registry.ModItems.COMPUTER_NORMAL.get() )
.add( Registry.ModItems.COMPUTER_ADVANCED.get() )
.add( Registry.ModItems.COMPUTER_COMMAND.get() );
tag( COMPUTER ).add(
Registry.ModItems.COMPUTER_NORMAL.get(),
Registry.ModItems.COMPUTER_ADVANCED.get(),
Registry.ModItems.COMPUTER_COMMAND.get()
);
tag( TURTLE ).add( Registry.ModItems.TURTLE_NORMAL.get(), Registry.ModItems.TURTLE_ADVANCED.get() );
tag( WIRED_MODEM ).add( Registry.ModItems.WIRED_MODEM.get(), Registry.ModItems.WIRED_MODEM_FULL.get() );
tag( MONITOR )
.add( Registry.ModItems.MONITOR_NORMAL.get() )
.add( Registry.ModItems.MONITOR_ADVANCED.get() );
tag( MONITOR ).add( Registry.ModItems.MONITOR_NORMAL.get(), Registry.ModItems.MONITOR_ADVANCED.get() );
tag( PIGLIN_LOVED ).add(
Registry.ModItems.COMPUTER_ADVANCED.get(), Registry.ModItems.TURTLE_ADVANCED.get(),
Registry.ModItems.WIRELESS_MODEM_ADVANCED.get(), Registry.ModItems.POCKET_COMPUTER_ADVANCED.get(),
Registry.ModItems.MONITOR_ADVANCED.get()
);
}
private static Tag<Item> item( String name )
private static ITag.INamedTag<Item> item( String name )
{
return new ItemTags.Wrapper( new ResourceLocation( ComputerCraft.MOD_ID, name ) );
return ItemTags.bind( new ResourceLocation( ComputerCraft.MOD_ID, name ).toString() );
}
}

View File

@@ -13,20 +13,19 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import java.util.LinkedHashSet;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Set;
public final class BundledRedstone
{
private static final Set<IBundledRedstoneProvider> providers = new LinkedHashSet<>();
private static final ArrayList<IBundledRedstoneProvider> providers = new ArrayList<>();
private BundledRedstone() {}
public static synchronized void register( @Nonnull IBundledRedstoneProvider provider )
{
Objects.requireNonNull( provider, "provider cannot be null" );
providers.add( provider );
if( !providers.contains( provider ) ) providers.add( provider );
}
public static int getDefaultOutput( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side )

View File

@@ -0,0 +1,141 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.computer.MainThread;
import dan200.computercraft.core.tracking.ComputerMBean;
import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.shared.command.CommandComputerCraft;
import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
import net.minecraft.inventory.container.Container;
import net.minecraft.loot.ConstantRange;
import net.minecraft.loot.LootPool;
import net.minecraft.loot.LootTables;
import net.minecraft.loot.TableLootEntry;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.LootTableLoadEvent;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.server.FMLServerStartedEvent;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.event.server.FMLServerStoppedEvent;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* Miscellaneous hooks which are present on the client and server.
*
* These should possibly be refactored into separate classes at some point, but are fine here for now.
*
* @see dan200.computercraft.client.ClientHooks For client-specific ones.
*/
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID )
public final class CommonHooks
{
private CommonHooks()
{
}
@SubscribeEvent
public static void onServerTick( TickEvent.ServerTickEvent event )
{
if( event.phase == TickEvent.Phase.START )
{
MainThread.executePendingTasks();
ComputerCraft.serverComputerRegistry.update();
}
}
@SubscribeEvent
public static void onContainerOpen( PlayerContainerEvent.Open event )
{
// If we're opening a computer container then broadcast the terminal state
Container container = event.getContainer();
if( container instanceof IContainerComputer )
{
IComputer computer = ((IContainerComputer) container).getComputer();
if( computer instanceof ServerComputer )
{
((ServerComputer) computer).sendTerminalState( event.getPlayer() );
}
}
}
@SubscribeEvent
public static void onRegisterCommand( RegisterCommandsEvent event )
{
CommandComputerCraft.register( event.getDispatcher() );
}
@SubscribeEvent
public static void onServerStarting( FMLServerStartingEvent event )
{
MinecraftServer server = event.getServer();
if( server instanceof DedicatedServer && ((DedicatedServer) server).getProperties().enableJmxMonitoring )
{
ComputerMBean.register();
}
}
@SubscribeEvent
public static void onServerStarted( FMLServerStartedEvent event )
{
ComputerCraft.serverComputerRegistry.reset();
WirelessNetwork.resetNetworks();
Tracking.reset();
ComputerMBean.registerTracker();
NetworkUtils.reset();
}
@SubscribeEvent
public static void onServerStopped( FMLServerStoppedEvent event )
{
ComputerCraft.serverComputerRegistry.reset();
WirelessNetwork.resetNetworks();
Tracking.reset();
NetworkUtils.reset();
}
public static final ResourceLocation LOOT_TREASURE_DISK = new ResourceLocation( ComputerCraft.MOD_ID, "treasure_disk" );
private static final Set<ResourceLocation> TABLES = new HashSet<>( Arrays.asList(
LootTables.SIMPLE_DUNGEON,
LootTables.ABANDONED_MINESHAFT,
LootTables.STRONGHOLD_CORRIDOR,
LootTables.STRONGHOLD_CROSSING,
LootTables.STRONGHOLD_LIBRARY,
LootTables.DESERT_PYRAMID,
LootTables.JUNGLE_TEMPLE,
LootTables.IGLOO_CHEST,
LootTables.WOODLAND_MANSION,
LootTables.VILLAGE_CARTOGRAPHER
) );
@SubscribeEvent
public static void lootLoad( LootTableLoadEvent event )
{
ResourceLocation name = event.getName();
if( !name.getNamespace().equals( "minecraft" ) || !TABLES.contains( name ) ) return;
event.getTable().addPool( LootPool.lootPool()
.add( TableLootEntry.lootTableReference( LOOT_TREASURE_DISK ) )
.setRolls( ConstantRange.exactly( 1 ) )
.name( "computercraft_treasure" )
.build() );
}
}

View File

@@ -12,6 +12,7 @@ import com.google.common.base.CaseFormat;
import com.google.common.base.Converter;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.apis.http.options.Action;
import dan200.computercraft.core.apis.http.options.AddressRuleConfig;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
@@ -42,7 +43,6 @@ public final class Config
private static final ConfigValue<Integer> maximumFilesOpen;
private static final ConfigValue<Boolean> disableLua51Features;
private static final ConfigValue<String> defaultComputerSettings;
private static final ConfigValue<Boolean> debugEnabled;
private static final ConfigValue<Boolean> logComputerErrors;
private static final ConfigValue<Boolean> commandRequireCreative;
@@ -57,6 +57,9 @@ public final class Config
private static final ConfigValue<Integer> httpMaxRequests;
private static final ConfigValue<Integer> httpMaxWebsockets;
private static final ConfigValue<Integer> httpDownloadBandwidth;
private static final ConfigValue<Integer> httpUploadBandwidth;
private static final ConfigValue<Boolean> commandBlockEnabled;
private static final ConfigValue<Integer> modemRange;
private static final ConfigValue<Integer> modemHighAltitudeRange;
@@ -120,10 +123,6 @@ public final class Config
"autocompletion" )
.define( "default_computer_settings", ComputerCraft.defaultComputerSettings );
debugEnabled = builder
.comment( "Enable Lua's debug library. This is sandboxed to each computer, so is generally safe to be used by players." )
.define( "debug_enabled", ComputerCraft.debugEnable );
logComputerErrors = builder
.comment( "Log exceptions thrown by peripherals and other Lua objects.\n" +
"This makes it easier for mod authors to debug problems, but may result in log spam should people use buggy methods." )
@@ -192,6 +191,20 @@ public final class Config
.comment( "The number of websockets a computer can have open at one time. Set to 0 for unlimited." )
.defineInRange( "max_websockets", ComputerCraft.httpMaxWebsockets, 1, Integer.MAX_VALUE );
builder
.comment( "Limits bandwidth used by computers" )
.push( "bandwidth" );
httpDownloadBandwidth = builder
.comment( "The number of bytes which can be downloaded in a second. This is shared across all computers. (bytes/s)" )
.defineInRange( "global_download", ComputerCraft.httpDownloadBandwidth, 1, Integer.MAX_VALUE );
httpUploadBandwidth = builder
.comment( "The number of bytes which can be uploaded in a second. This is shared across all computers. (bytes/s)" )
.defineInRange( "global_upload", ComputerCraft.httpUploadBandwidth, 1, Integer.MAX_VALUE );
builder.pop();
builder.pop();
}
@@ -317,7 +330,6 @@ public final class Config
ComputerCraft.maximumFilesOpen = maximumFilesOpen.get();
ComputerCraft.disableLua51Features = disableLua51Features.get();
ComputerCraft.defaultComputerSettings = defaultComputerSettings.get();
ComputerCraft.debugEnable = debugEnabled.get();
ComputerCraft.computerThreads = computerThreads.get();
ComputerCraft.logComputerErrors = logComputerErrors.get();
ComputerCraft.commandRequireCreative = commandRequireCreative.get();
@@ -335,6 +347,8 @@ public final class Config
ComputerCraft.httpMaxRequests = httpMaxRequests.get();
ComputerCraft.httpMaxWebsockets = httpMaxWebsockets.get();
ComputerCraft.httpDownloadBandwidth = httpDownloadBandwidth.get();
NetworkUtils.reloadConfig();
// Peripheral
ComputerCraft.enableCommandBlock = commandBlockEnabled.get();

View File

@@ -7,8 +7,13 @@ package dan200.computercraft.shared;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.api.network.wired.IWiredElement;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.shared.command.arguments.ArgumentSerializers;
import dan200.computercraft.shared.common.ColourableRecipe;
import dan200.computercraft.shared.common.ContainerHeldItem;
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
import dan200.computercraft.shared.computer.blocks.BlockComputer;
import dan200.computercraft.shared.computer.blocks.TileCommandComputer;
import dan200.computercraft.shared.computer.blocks.TileComputer;
@@ -17,11 +22,17 @@ import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.computer.items.ItemComputer;
import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe;
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
import dan200.computercraft.shared.data.HasComputerIdLootCondition;
import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
import dan200.computercraft.shared.integration.morered.MoreRedIntegration;
import dan200.computercraft.shared.media.items.ItemDisk;
import dan200.computercraft.shared.media.items.ItemPrintout;
import dan200.computercraft.shared.media.items.ItemTreasureDisk;
import dan200.computercraft.shared.media.items.RecordMedia;
import dan200.computercraft.shared.media.recipes.DiskRecipe;
import dan200.computercraft.shared.media.recipes.PrintoutRecipe;
import dan200.computercraft.shared.network.NetworkHandler;
import dan200.computercraft.shared.network.container.ComputerContainerData;
import dan200.computercraft.shared.network.container.ContainerData;
import dan200.computercraft.shared.network.container.HeldItemContainerData;
@@ -29,6 +40,9 @@ import dan200.computercraft.shared.network.container.ViewComputerContainerData;
import dan200.computercraft.shared.peripheral.diskdrive.BlockDiskDrive;
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
import dan200.computercraft.shared.peripheral.generic.methods.EnergyMethods;
import dan200.computercraft.shared.peripheral.generic.methods.FluidMethods;
import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods;
import dan200.computercraft.shared.peripheral.modem.wired.*;
import dan200.computercraft.shared.peripheral.modem.wireless.BlockWirelessModem;
import dan200.computercraft.shared.peripheral.modem.wireless.TileWirelessModem;
@@ -52,29 +66,32 @@ import dan200.computercraft.shared.turtle.items.ItemTurtle;
import dan200.computercraft.shared.turtle.recipes.TurtleRecipe;
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
import dan200.computercraft.shared.turtle.upgrades.*;
import dan200.computercraft.shared.util.CreativeTabMain;
import dan200.computercraft.shared.util.FixedPointTileEntityType;
import dan200.computercraft.shared.util.ImpostorRecipe;
import dan200.computercraft.shared.util.ImpostorShapelessRecipe;
import dan200.computercraft.shared.util.*;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.EntityClassification;
import net.minecraft.entity.EntityType;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.Items;
import net.minecraft.item.*;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.loot.LootConditionType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.capabilities.CapabilityManager;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fml.DeferredWorkQueue;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
@@ -92,21 +109,21 @@ public final class Registry
public static final class ModBlocks
{
static final DeferredRegister<Block> BLOCKS = new DeferredRegister<>( ForgeRegistries.BLOCKS, ComputerCraft.MOD_ID );
static final DeferredRegister<Block> BLOCKS = DeferredRegister.create( ForgeRegistries.BLOCKS, ComputerCraft.MOD_ID );
private static Block.Properties properties()
private static AbstractBlock.Properties properties()
{
return Block.Properties.of( Material.STONE ).strength( 2 );
return AbstractBlock.Properties.of( Material.STONE ).strength( 2 );
}
private static Block.Properties turtleProperties()
private static AbstractBlock.Properties turtleProperties()
{
return Block.Properties.of( Material.STONE ).strength( 2.5f );
return AbstractBlock.Properties.of( Material.STONE ).strength( 2.5f );
}
private static Block.Properties modemProperties()
private static AbstractBlock.Properties modemProperties()
{
return Block.Properties.of( Material.STONE ).strength( 1.5f );
return AbstractBlock.Properties.of( Material.STONE ).strength( 1.5f );
}
public static final RegistryObject<BlockComputer> COMPUTER_NORMAL = BLOCKS.register( "computer_normal",
@@ -115,7 +132,7 @@ public final class Registry
() -> new BlockComputer( properties(), ComputerFamily.ADVANCED, ModTiles.COMPUTER_ADVANCED ) );
public static final RegistryObject<BlockComputer> COMPUTER_COMMAND = BLOCKS.register( "computer_command", () -> new BlockComputer(
Block.Properties.of( Material.STONE ).strength( -1, 6000000.0F ),
AbstractBlock.Properties.of( Material.STONE ).strength( -1, 6000000.0F ),
ComputerFamily.COMMAND, ModTiles.COMPUTER_COMMAND
) );
@@ -145,7 +162,7 @@ public final class Registry
public static class ModTiles
{
static final DeferredRegister<TileEntityType<?>> TILES = new DeferredRegister<>( ForgeRegistries.TILE_ENTITIES, ComputerCraft.MOD_ID );
static final DeferredRegister<TileEntityType<?>> TILES = DeferredRegister.create( ForgeRegistries.TILE_ENTITIES, ComputerCraft.MOD_ID );
private static <T extends TileEntity> RegistryObject<TileEntityType<T>> ofBlock( RegistryObject<? extends Block> block, Function<TileEntityType<T>, T> factory )
{
@@ -183,7 +200,7 @@ public final class Registry
public static final class ModItems
{
static final DeferredRegister<Item> ITEMS = new DeferredRegister<>( ForgeRegistries.ITEMS, ComputerCraft.MOD_ID );
static final DeferredRegister<Item> ITEMS = DeferredRegister.create( ForgeRegistries.ITEMS, ComputerCraft.MOD_ID );
private static Item.Properties properties()
{
@@ -281,7 +298,7 @@ public final class Registry
public static class ModEntities
{
static final DeferredRegister<EntityType<?>> ENTITIES = new DeferredRegister<>( ForgeRegistries.ENTITIES, ComputerCraft.MOD_ID );
static final DeferredRegister<EntityType<?>> ENTITIES = DeferredRegister.create( ForgeRegistries.ENTITIES, ComputerCraft.MOD_ID );
public static final RegistryObject<EntityType<TurtlePlayer>> TURTLE_PLAYER = ENTITIES.register( "turtle_player", () ->
EntityType.Builder.<TurtlePlayer>createNothing( EntityClassification.MISC )
@@ -293,7 +310,7 @@ public final class Registry
public static class ModContainers
{
static final DeferredRegister<ContainerType<?>> CONTAINERS = new DeferredRegister<>( ForgeRegistries.CONTAINERS, ComputerCraft.MOD_ID );
static final DeferredRegister<ContainerType<?>> CONTAINERS = DeferredRegister.create( ForgeRegistries.CONTAINERS, ComputerCraft.MOD_ID );
public static final RegistryObject<ContainerType<ContainerComputer>> COMPUTER = CONTAINERS.register( "computer",
() -> ContainerData.toType( ComputerContainerData::new, ContainerComputer::new ) );
@@ -333,6 +350,65 @@ public final class Registry
);
}
@SubscribeEvent
@SuppressWarnings( "deprecation" )
public static void init( FMLCommonSetupEvent event )
{
NetworkHandler.setup();
DeferredWorkQueue.runLater( () -> {
registerProviders();
ArgumentSerializers.register();
registerLoot();
} );
ComputerCraftAPI.registerGenericSource( new InventoryMethods() );
ComputerCraftAPI.registerGenericSource( new FluidMethods() );
ComputerCraftAPI.registerGenericSource( new EnergyMethods() );
}
private static void registerProviders()
{
// Register bundled power providers
ComputerCraftAPI.registerBundledRedstoneProvider( new DefaultBundledRedstoneProvider() );
// Register media providers
ComputerCraftAPI.registerMediaProvider( stack -> {
Item item = stack.getItem();
if( item instanceof IMedia ) return (IMedia) item;
if( item instanceof MusicDiscItem ) return RecordMedia.INSTANCE;
return null;
} );
// Register capabilities
CapabilityManager.INSTANCE.register( IWiredElement.class, new NullStorage<>(), () -> null );
CapabilityManager.INSTANCE.register( IPeripheral.class, new NullStorage<>(), () -> null );
// Register generic capabilities. This can technically be done off-thread, but we need it to happen
// after Forge's common setup, so this is easiest.
ComputerCraftAPI.registerGenericCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY );
ComputerCraftAPI.registerGenericCapability( CapabilityEnergy.ENERGY );
ComputerCraftAPI.registerGenericCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY );
// Mod integration code.
if( ModList.get().isLoaded( MoreRedIntegration.MOD_ID ) ) MoreRedIntegration.initialise();
}
public static void registerLoot()
{
registerCondition( "block_named", BlockNamedEntityLootCondition.TYPE );
registerCondition( "player_creative", PlayerCreativeLootCondition.TYPE );
registerCondition( "has_id", HasComputerIdLootCondition.TYPE );
}
private static void registerCondition( String name, LootConditionType serializer )
{
net.minecraft.util.registry.Registry.register(
net.minecraft.util.registry.Registry.LOOT_CONDITION_TYPE,
new ResourceLocation( ComputerCraft.MOD_ID, name ), serializer
);
}
public static void setup()
{
IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();

View File

@@ -11,6 +11,7 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
@@ -20,13 +21,12 @@ public final class TurtlePermissions
public static boolean isBlockEnterable( World world, BlockPos pos, PlayerEntity player )
{
MinecraftServer server = world.getServer();
return server == null || world.isClientSide || !server.isUnderSpawnProtection( world, pos, player );
return server == null || world.isClientSide || (world instanceof ServerWorld && !server.isUnderSpawnProtection( (ServerWorld) world, pos, player ));
}
public static boolean isBlockEditable( World world, BlockPos pos, PlayerEntity player )
{
MinecraftServer server = world.getServer();
return server == null || world.isClientSide || !server.isUnderSpawnProtection( world, pos, player );
return isBlockEnterable( world, pos, player );
}
@SubscribeEvent

View File

@@ -11,6 +11,7 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenCustomHashMap;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Util;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModLoadingContext;
import javax.annotation.Nonnull;
@@ -33,9 +34,10 @@ public final class TurtleUpgrades
Wrapper( ITurtleUpgrade upgrade )
{
this.upgrade = upgrade;
this.id = upgrade.getUpgradeID().toString();
this.modId = ModLoadingContext.get().getActiveNamespace();
this.enabled = true;
id = upgrade.getUpgradeID().toString();
ModContainer mc = ModLoadingContext.get().getActiveContainer();
modId = mc != null && mc.getModId() != null ? mc.getModId() : ComputerCraft.MOD_ID;
enabled = true;
}
}

View File

@@ -0,0 +1,63 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.command;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.util.IDAssigner;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.Util;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ClientChatEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.server.ServerLifecycleHooks;
import java.io.File;
/**
* Basic client-side commands.
*
* Simply hooks into client chat messages and intercepts matching strings.
*/
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public final class ClientCommands
{
public static final String OPEN_COMPUTER = "/computercraft open-computer ";
private ClientCommands()
{
}
@SubscribeEvent
public static void onClientSendMessage( ClientChatEvent event )
{
// Emulate the command on the client side
if( event.getMessage().startsWith( OPEN_COMPUTER ) )
{
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
if( server == null || server.isDedicatedServer() ) return;
event.setCanceled( true );
String idStr = event.getMessage().substring( OPEN_COMPUTER.length() ).trim();
int id;
try
{
id = Integer.parseInt( idStr );
}
catch( NumberFormatException ignore )
{
return;
}
File file = new File( IDAssigner.getDir(), "computer/" + id );
if( !file.isDirectory() ) return;
Util.getPlatform().openFile( file );
}
}
}

View File

@@ -21,6 +21,7 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.network.container.ViewComputerContainerData;
import dan200.computercraft.shared.util.IDAssigner;
import net.minecraft.command.CommandSource;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
@@ -30,6 +31,7 @@ import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.INamedContainerProvider;
import net.minecraft.network.play.server.SPlayerPositionLookPacket;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.IFormattableTextComponent;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TranslationTextComponent;
@@ -37,6 +39,7 @@ import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import javax.annotation.Nonnull;
import java.io.File;
import java.util.*;
import static dan200.computercraft.shared.command.CommandUtils.isPlayer;
@@ -283,7 +286,7 @@ public final class CommandComputerCraft
private static ITextComponent linkComputer( CommandSource source, ServerComputer serverComputer, int computerId )
{
ITextComponent out = new StringTextComponent( "" );
IFormattableTextComponent out = new StringTextComponent( "" );
// Append the computer instance
if( serverComputer == null )
@@ -320,6 +323,12 @@ public final class CommandComputerCraft
) );
}
if( UserLevel.OWNER.test( source ) && isPlayer( source ) )
{
ITextComponent linkPath = linkStorage( computerId );
if( linkPath != null ) out.append( " " ).append( linkPath );
}
return out;
}
@@ -339,6 +348,18 @@ public final class CommandComputerCraft
}
}
private static ITextComponent linkStorage( int id )
{
File file = new File( IDAssigner.getDir(), "computer/" + id );
if( !file.isDirectory() ) return null;
return link(
text( "\u270E" ),
ClientCommands.OPEN_COMPUTER + id,
translate( "commands.computercraft.dump.open_path" )
);
}
@Nonnull
private static TrackingContext getTimingContext( CommandSource source )
{

View File

@@ -1,66 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import dan200.computercraft.ComputerCraft;
import net.minecraft.client.Minecraft;
import net.minecraft.command.CommandSource;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.util.text.event.ClickEvent;
import net.minecraft.util.text.event.HoverEvent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ClientChatEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import static net.minecraft.command.Commands.argument;
import static net.minecraft.command.Commands.literal;
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public final class CommandCopy
{
private static final String PREFIX = "/computercraft copy ";
private CommandCopy()
{
}
public static void register( CommandDispatcher<CommandSource> registry )
{
registry.register( literal( "computercraft" )
.then( literal( "copy" ) )
.then( argument( "message", StringArgumentType.greedyString() ) )
.executes( context -> {
Minecraft.getInstance().keyboardHandler.setClipboard( context.getArgument( "message", String.class ) );
return 1;
} )
);
}
@SubscribeEvent
public static void onClientSendMessage( ClientChatEvent event )
{
// Emulate the command on the client side
if( event.getMessage().startsWith( PREFIX ) )
{
Minecraft.getInstance().keyboardHandler.setClipboard( event.getMessage().substring( PREFIX.length() ) );
event.setCanceled( true );
}
}
public static ITextComponent createCopyText( String text )
{
StringTextComponent name = new StringTextComponent( text );
name.getStyle()
.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, PREFIX + text ) )
.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new TranslationTextComponent( "gui.computercraft.tooltip.copy" ) ) );
return name;
}
}

View File

@@ -57,14 +57,15 @@ public enum UserLevel implements Predicate<CommandSource>
{
if( this == ANYONE ) return true;
// We *always* allow level 0 stuff, even if the
MinecraftServer server = source.getServer();
Entity sender = source.getEntity();
if( server.isSingleplayer() && sender instanceof PlayerEntity &&
((PlayerEntity) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ) )
if( this == OWNER || this == OWNER_OP )
{
if( this == OWNER || this == OWNER_OP ) return true;
MinecraftServer server = source.getServer();
Entity sender = source.getEntity();
if( server.isSingleplayer() && sender instanceof PlayerEntity &&
((PlayerEntity) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ) )
{
return true;
}
}
return source.hasPermission( toLevel() );

View File

@@ -13,6 +13,7 @@ import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import net.minecraft.command.CommandSource;
import net.minecraft.util.text.IFormattableTextComponent;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextFormatting;
@@ -173,7 +174,7 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<Command
temp.addChild( node );
String usage = dispatcher.getSmartUsage( temp, context.getSource() ).get( node ).substring( node.getName().length() );
ITextComponent output = new StringTextComponent( "" )
IFormattableTextComponent output = new StringTextComponent( "" )
.append( coloured( "/" + command + usage, HEADER ) )
.append( " " )
.append( coloured( translate( "commands." + id + ".synopsis" ), SYNOPSIS ) )
@@ -189,8 +190,8 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<Command
output.append( "\n" );
ITextComponent component = coloured( child.getName(), NAME );
component.getStyle().setClickEvent( new ClickEvent(
IFormattableTextComponent component = coloured( child.getName(), NAME );
component.getStyle().withClickEvent( new ClickEvent(
ClickEvent.Action.SUGGEST_COMMAND,
"/" + command + " " + child.getName()
) );

View File

@@ -19,37 +19,35 @@ public final class ChatHelpers
private ChatHelpers() {}
public static ITextComponent coloured( String text, TextFormatting colour )
public static IFormattableTextComponent coloured( String text, TextFormatting colour )
{
ITextComponent component = new StringTextComponent( text == null ? "" : text );
component.getStyle().setColor( colour );
return new StringTextComponent( text == null ? "" : text ).withStyle( colour );
}
public static <T extends IFormattableTextComponent> T coloured( T component, TextFormatting colour )
{
component.withStyle( colour );
return component;
}
public static <T extends ITextComponent> T coloured( T component, TextFormatting colour )
{
component.getStyle().setColor( colour );
return component;
}
public static ITextComponent text( String text )
public static IFormattableTextComponent text( String text )
{
return new StringTextComponent( text == null ? "" : text );
}
public static ITextComponent translate( String text )
public static IFormattableTextComponent translate( String text )
{
return new TranslationTextComponent( text == null ? "" : text );
}
public static ITextComponent translate( String text, Object... args )
public static IFormattableTextComponent translate( String text, Object... args )
{
return new TranslationTextComponent( text == null ? "" : text, args );
}
public static ITextComponent list( ITextComponent... children )
public static IFormattableTextComponent list( ITextComponent... children )
{
ITextComponent component = new StringTextComponent( "" );
IFormattableTextComponent component = new StringTextComponent( "" );
for( ITextComponent child : children )
{
component.append( child );
@@ -57,32 +55,46 @@ public final class ChatHelpers
return component;
}
public static ITextComponent position( BlockPos pos )
public static IFormattableTextComponent position( BlockPos pos )
{
if( pos == null ) return translate( "commands.computercraft.generic.no_position" );
return translate( "commands.computercraft.generic.position", pos.getX(), pos.getY(), pos.getZ() );
}
public static ITextComponent bool( boolean value )
public static IFormattableTextComponent bool( boolean value )
{
return value
? coloured( translate( "commands.computercraft.generic.yes" ), TextFormatting.GREEN )
: coloured( translate( "commands.computercraft.generic.no" ), TextFormatting.RED );
}
public static ITextComponent link( ITextComponent component, String command, ITextComponent toolTip )
public static ITextComponent link( IFormattableTextComponent component, String command, ITextComponent toolTip )
{
return link( component, new ClickEvent( ClickEvent.Action.RUN_COMMAND, command ), toolTip );
}
public static ITextComponent link( ITextComponent component, ClickEvent click, ITextComponent toolTip )
{
Style style = component.getStyle();
if( style.getColor() == null ) style.setColor( TextFormatting.YELLOW );
style.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, command ) );
style.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, toolTip ) );
if( style.getColor() == null ) style = style.withColor( TextFormatting.YELLOW );
style = style.withClickEvent( click );
style = style.withHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, toolTip ) );
return component;
return component.copy().withStyle( style );
}
public static ITextComponent header( String text )
public static IFormattableTextComponent header( String text )
{
return coloured( text, HEADER );
}
public static IFormattableTextComponent copy( String text )
{
StringTextComponent name = new StringTextComponent( text );
Style style = name.getStyle()
.withClickEvent( new ClickEvent( ClickEvent.Action.COPY_TO_CLIPBOARD, text ) )
.withHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new TranslationTextComponent( "gui.computercraft.tooltip.copy" ) ) );
return name.withStyle( style );
}
}

View File

@@ -61,7 +61,7 @@ public class ContainerHeldItem extends Container
public Factory( ContainerType<ContainerHeldItem> type, ItemStack stack, Hand hand )
{
this.type = type;
this.name = stack.getHoverName();
name = stack.getHoverName();
this.hand = hand;
}

View File

@@ -105,9 +105,9 @@ public abstract class TileGeneric extends TileEntity
}
@Override
public void handleUpdateTag( @Nonnull CompoundNBT tag )
public void handleUpdateTag( @Nonnull BlockState state, @Nonnull CompoundNBT tag )
{
super.handleUpdateTag( tag );
super.handleUpdateTag( state, tag );
readDescription( tag );
}
}

View File

@@ -16,6 +16,8 @@ import net.minecraft.block.BlockState;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.loot.LootContext;
import net.minecraft.loot.LootParameters;
import net.minecraft.stats.Stats;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
@@ -23,12 +25,11 @@ import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorldReader;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.storage.loot.LootContext;
import net.minecraft.world.storage.loot.LootParameters;
import net.minecraftforge.fml.RegistryObject;
import javax.annotation.Nonnull;
@@ -139,6 +140,7 @@ public abstract class BlockComputerBase<T extends TileComputerBase> extends Bloc
public void playerWillDestroy( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nonnull PlayerEntity player )
{
if( !(world instanceof ServerWorld) ) return;
ServerWorld serverWorld = (ServerWorld) world;
// We drop the item here instead of doing it in the harvest method, as we should
// drop computers for creative players too.
@@ -147,19 +149,19 @@ public abstract class BlockComputerBase<T extends TileComputerBase> extends Bloc
if( tile instanceof TileComputerBase )
{
TileComputerBase computer = (TileComputerBase) tile;
LootContext.Builder context = new LootContext.Builder( (ServerWorld) world )
LootContext.Builder context = new LootContext.Builder( serverWorld )
.withRandom( world.random )
.withParameter( LootParameters.BLOCK_POS, pos )
.withParameter( LootParameters.ORIGIN, Vector3d.atCenterOf( pos ) )
.withParameter( LootParameters.TOOL, player.getMainHandItem() )
.withParameter( LootParameters.THIS_ENTITY, player )
.withOptionalParameter( LootParameters.BLOCK_ENTITY, tile )
.withParameter( LootParameters.BLOCK_ENTITY, tile )
.withDynamicDrop( DROP, ( ctx, out ) -> out.accept( getItem( computer ) ) );
for( ItemStack item : state.getDrops( context ) )
{
popResource( world, pos, item );
}
state.spawnAfterBreak( world, pos, player.getMainHandItem() );
state.spawnAfterBreak( serverWorld, pos, player.getMainHandItem() );
}
}

View File

@@ -14,8 +14,8 @@ import net.minecraft.command.ICommandSource;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.math.Vec2f;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.vector.Vector2f;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TranslationTextComponent;
@@ -25,6 +25,7 @@ import net.minecraft.world.server.ServerWorld;
import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class TileCommandComputer extends TileComputer
{
@@ -48,7 +49,7 @@ public class TileCommandComputer extends TileComputer
}
@Override
public void sendMessage( @Nonnull ITextComponent textComponent )
public void sendMessage( @Nonnull ITextComponent textComponent, @Nonnull UUID id )
{
output.put( output.size() + 1, textComponent.getString() );
}
@@ -56,7 +57,7 @@ public class TileCommandComputer extends TileComputer
@Override
public boolean acceptsSuccess()
{
return getLevel().getGameRules().getBoolean( GameRules.RULE_SENDCOMMANDFEEDBACK );
return true;
}
@Override
@@ -96,7 +97,7 @@ public class TileCommandComputer extends TileComputer
}
return new CommandSource( receiver,
new Vec3d( worldPosition.getX() + 0.5, worldPosition.getY() + 0.5, worldPosition.getZ() + 0.5 ), Vec2f.ZERO,
new Vector3d( worldPosition.getX() + 0.5, worldPosition.getY() + 0.5, worldPosition.getZ() + 0.5 ), Vector2f.ZERO,
(ServerWorld) getLevel(), 2,
name, new StringTextComponent( name ),
getLevel().getServer(), null

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