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

Compare commits

..

45 Commits

Author SHA1 Message Date
SquidDev
029374e9aa Merge branch 'master' into mc-1.14.x 2020-06-21 12:08:30 +01:00
SquidDev
2a8efb3fd5 Fix crashes when rendering monitors of varying sizes
When calling .flip(), we limit the size of the buffer. However, this
limit is not reset when writing the next time, which means we get
out-of-bounds errors, even if the buffer is /technically/ big enough.

Clearing the buffer before drawing (rather than just resetting the
position) is enough to fix this.

Fixes #476 (and closes #477, which is a duplicate)
2020-06-21 12:03:24 +01:00
SquidDev
9134f243c1 Merge branch 'master' into mc-1.14.x 2020-06-15 22:05:02 +01:00
SquidDev
c0f3ca81fb Bump to 1.89.0 2020-06-15 21:37:08 +01:00
SquidDev
b9b8121be9 Expose tags for turtle.{inspect,getItemDetail}
This is simply exposed as a table from tag -> true. While this is less
natural than an array, it allows for easy esting of whether a tag is
present.

Closes #461
2020-06-01 11:17:05 +01:00
Lignum
014bf55cd4 Cherry pick several improvements from #455
- Use texture over texture2D - the latter was deprecated in GLSL 1.30.
 - Cache the tbo buffer - this saves an allocation when monitors update.

Closes #455. While the rest of the PR has some nice changes, it
performs signlificantly worse on my system.
2020-05-31 17:23:49 +01:00
Lignum
085ae2e74a Use an older version of GLSL (#459)
This ensures that the MVP matrix is available within the monitor
fragment shader, without requiring the ARB_compatibility extension.
2020-05-28 11:06:14 +01:00
Lignum
4ff33f165d Fetch MVP matrix in monitor shader instead (#454) 2020-05-25 11:19:03 +01:00
SquidDev
d929c02d2a Fix settings loading failing for defined settings
Yes, this was the only piece of code which wasn't tested :/. Fixes #457.
2020-05-24 12:16:51 +01:00
Jonathan Coates
d50a08a549 Rewrite monitor networking (#453)
This moves monitor networking into its own packet, rather than serialising
using NBT. This allows us to be more flexible with how monitors are
serialised.

We now compress terminal data using gzip. This reduces the packet size
of a max-sized-monitor from ~25kb to as little as 100b.

On my test set of images (what I would consider to be the extreme end of
the "reasonable" case), we have packets from 1.4kb bytes up to 12kb,
with a mean of 6kb. Even in the worst case, this is a 2x reduction in
packet size.

While this is a fantastic win for the common case, it is not abuse-proof.
One can create a terminal with high entropy (and so uncompressible). This
will still be close to the original packet size.

In order to prevent any other abuse, we also limit the amount of monitor
data a client can possibly receive to 1MB (configurable).
2020-05-20 08:44:44 +01:00
SquidDev
13de2c4dd0 Fix location of cc.require
Yay for 1.12->1.13 changes!
2020-05-15 10:28:35 +01:00
SquidDev
906280225e Merge branch 'master' into mc-1.14.x 2020-05-15 10:19:57 +01:00
SquidDev
161a5b4707 Document and test the redstone library
The tests may be a little agressive, but I wanted some sanity checks for
the 1.15 API rewrite.
2020-05-15 10:03:47 +01:00
SquidDev
c6b6b4479c Update changelog and fix doc typo 2020-05-14 19:23:57 +01:00
SquidDev
96e7b60285 Display function arguments and positions in the REPL
- cc.pretty.pretty now accepts two additional options:
   - function_args: Show function arguments
   - function_source: Show where functions are defined.
 - Expose the two options as lua.* settings (defaulting function_args to
   true, and function_source to false).
   These are then used in the Lua REPL.

Closes #361
2020-05-14 19:11:57 +01:00
SquidDev
086fccd997 Move the package library into a separate module
Hopefully this makes it a little easier for people to use in custom
shells (and anything else where it might be useful).
2020-05-14 17:27:50 +01:00
Jonathan Coates
e251dd066c Fix test failures 2020-05-13 15:27:50 +01:00
SquidDev
c60dcb4f5a Fix deprecated usage 2020-05-13 14:04:32 +01:00
SquidDev
4be0b15afa Merge branch 'master' into mc-1.14.x 2020-05-13 14:04:28 +01:00
SquidDev
a4ae36b6b3 Bump version to 1.88.0
There's probably some other stuff I'll get in before release, but let's
do this just in case.
2020-05-13 13:43:40 +01:00
SquidDev
ac075d9f53 Allow using command computers in survival mode
I'm really not a fan of this change, but it's gated behind a config
option and there's apparently sufficient demand that it's worthwhile.
Closes #442.
2020-05-13 10:26:59 +01:00
Lupus590
05d7be0362 Improvements to the various file system programs (rm, mv, rename) (#440)
This enforces several sanity checks before actually attempting
the move, allowing us to produce friendlier error messages.
2020-05-12 11:32:48 +01:00
SquidDev
9a71dc1a26 Copy across a bunch of 5.1/5.3 io tests
I've been meaning to do this for ages. Woops.
2020-05-11 18:05:40 +01:00
SquidDev
156023b154 Create more work for myself
This ensures no lines start with an empty line, and all files finish
with exactly one "\n".
2020-05-11 16:08:25 +01:00
SquidDev
6b3773a862 Run tests with coverage
- Use jacoco for Java-side coverage. Our Java coverage is /terrible
   (~10%), as we only really test the core libraries. Still a good thing
   to track for regressions though.

 - mcfly now tracks Lua side coverage. This works in several stages:
   - Replace loadfile to include the whole path
   - Add a debug hook which just tracks filename->(lines->count). This
     is then submitted to the Java test runner.
   - On test completion, we emit a luacov.report.out file.

   As the debug hook is inserted by mcfly, this does not include any
   computer startup (such as loading apis, or the root of bios.lua),
   despite they're executed.

   This would be possible to do (for instance, inject a custom header
   into bios.lua). However, we're not actually testing any of the
   behaviour of startup (aside from "does it not crash"), so I'm not
   sure whether to include it or not. Something I'll most likely
   re-evaluate.
2020-05-11 15:47:30 +01:00
Jonathan Coates
376d628cf0 Make the local Lua message a little shorter
Co-authored-by: exerro <benedict.allen2514@gmail.com>
2020-05-09 08:30:55 +01:00
Lupus590
44062ebd52 Allow lua REPL to warn about using local variables (#367)
`local varname = value` results in `varname` being inaccessible in
the next REPL input. This is often unintended and can lead to confusing
behaviour. We produce a warning when this occurs.
2020-05-08 16:07:33 +01:00
Jonathan Coates
5739285fc2 Finish off documentation for the commands API 2020-05-05 21:17:52 +01:00
Jonathan Coates
70b457ed18 Add a monitor renderer using TBOs (#443)
This uses the system described in #409, to render monitors in a more
efficient manner.

Each monitor is backed by a texture buffer object (TBO) which contains
a relatively compact encoding of the terminal state. This is then
rendered using a shader, which consumes the TBO and uses it to index
into main font texture.

As we're transmitting significantly less data to the GPU (only 3 bytes
per character), this effectively reduces any update lag to 0. FPS appears
to be up by a small fraction (10-15fps on my machine, to ~110), possibly
as we're now only drawing a single quad (though doing much more work in
the shader).

On my laptop, with its Intel integrated graphics card, I'm able to draw
120 full-sized monitors (with an effective resolution of 3972 x 2330) at
a consistent 60fps. Updates still cause a slight spike, but we always
remain above 30fps - a significant improvement over VBOs, where updates
would go off the chart.

Many thanks to @Lignum and @Lemmmy for devising this scheme, and helping
test and review it! ♥
2020-05-05 13:05:23 +01:00
SquidDev
6816931659 Merge branch 'master' into mc-1.14.x 2020-05-04 10:28:15 +01:00
SquidDev
1547ecbeb3 Fix incorrect palette serialisation 2020-05-04 10:26:33 +01:00
SquidDev
c28b468844 Update languages and language script 2020-05-04 09:57:38 +01:00
SquidDev
052cf8ee7d Merge branch 'master' into mc-1.14.x 2020-05-04 09:39:54 +01:00
SquidDev
550ada2f9e Restore previous behaviour for unknown colours
Unknown blit colours, such as " " will be translated to black for the
background or white for the foreground. This restores the behaviour from
before #412.
2020-05-04 09:15:23 +01:00
SquidDev
17b7727262 Improve serialisation of terminals
- Write to a PacketBuffer instead of generating an NBT tag. This is
   then converted to an NBT byte array when we send across the network.
 - Pack background/foreground colours into a single byte.

This derives from some work I did back in 2017, and some of the changes
made/planned in #409. However, this patch does not change how terminals
are represented, it simply makes the transfer more compact.

This makes the patch incredibly small (100 lines!), but also limited in
what improvements it can make compared with #409. We send 26626 bytes
for a full-sized monitor. While a 2x improvement over the previous 58558
bytes, there's a lot of room for improvement.
2020-05-03 10:38:31 +01:00
Jonathan Coates
4553e404b2 Merge pull request #437 from SquidDev-CC/feature/keep-ids
Preserve computer ids on unlabelled computers
2020-05-03 06:54:04 +01:00
Drew Lemmy
a565a571f9 Return the peripheral name when wrapping (#436) 2020-05-03 06:53:42 +01:00
SquidDev
fb64b6017b Add shell.execute
This functions the same as shell.run, but does not tokenise the
arguments. This allows us to pass command line arguments through to
another program without having to re-quote them.

Closes #417
2020-05-02 11:05:09 +01:00
SquidDev
ed4229ab70 Keep ids of unlabelled computers and turtles 2020-05-02 10:38:18 +01:00
SquidDev
3fb906ef6c Show computer/disk ids in the tool tip more often
- Remove the parenthesis around the text (so it's now
   "Computer ID: 12"), rather than "(Computer ID: 12").
 - Show the tooltip if the computer has an ID and no label (as well as
   when in advanced mode).
2020-05-02 10:38:17 +01:00
Jonathan Coates
e1663f3df0 Fix malformed doc comments 2020-05-01 08:50:44 +01:00
SquidDev
358289b5f9 Merge branch 'master' into mc-1.14.x 2020-04-29 17:37:36 +01:00
SquidDev
5eec24676f Prevent computers scanning peripherals twice 2020-04-29 17:37:02 +01:00
Jonathan Coates
447c3ab125 Clean up dance.lua
Not sure what keys.escape was doing there. That's very old.
2020-04-28 09:51:06 +01:00
Jonathan Coates
8fac68386e Fix usages of global variables
- Lint references to unknown fields of modules, excluding the keys and
   colours modules. This caught several silly errors in our stub files,
   but nothing else.
 - Lint on using unknown globals. This highlighted a couple of really
   silly mistakes. Fixes #427.
 - Add documentation for fs.attributes, fs.getCapacity and pocket, as
   they were not defined before.

Co-authored-by: JackMacWindows <jackmacwindowslinux@gmail.com>
2020-04-28 09:42:34 +01:00
253 changed files with 5291 additions and 2434 deletions

View File

@@ -32,6 +32,9 @@ jobs:
name: CC-Tweaked name: CC-Tweaked
path: build/libs path: build/libs
- name: Upload Coverage
run: bash <(curl -s https://codecov.io/bash)
lint-lua: lint-lua:
name: Lint Lua name: Lint Lua
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@@ -9,7 +9,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.google.code.gson:gson:2.8.1' classpath 'com.google.code.gson:gson:2.8.1'
classpath 'net.minecraftforge.gradle:ForgeGradle:3.0.169' classpath 'net.minecraftforge.gradle:ForgeGradle:3.0.154'
classpath 'net.sf.proguard:proguard-gradle:6.1.0beta2' classpath 'net.sf.proguard:proguard-gradle:6.1.0beta2'
classpath 'org.ajoberstar.grgit:grgit-gradle:3.0.0' classpath 'org.ajoberstar.grgit:grgit-gradle:3.0.0'
} }
@@ -17,6 +17,7 @@ buildscript {
plugins { plugins {
id "checkstyle" id "checkstyle"
id "jacoco"
id "com.github.hierynomus.license" version "0.15.0" id "com.github.hierynomus.license" version "0.15.0"
id "com.matthewprenger.cursegradle" version "1.3.0" id "com.matthewprenger.cursegradle" version "1.3.0"
id "com.github.breadmoirai.github-release" version "2.2.4" id "com.github.breadmoirai.github-release" version "2.2.4"
@@ -47,7 +48,7 @@ minecraft {
} }
server { server {
workingDirectory project.file("run/server-${mc_version}") workingDirectory project.file('run')
property 'forge.logging.markers', 'REGISTRIES,REGISTRYDUMP' property 'forge.logging.markers', 'REGISTRIES,REGISTRYDUMP'
property 'forge.logging.console.level', 'debug' property 'forge.logging.console.level', 'debug'
@@ -99,10 +100,10 @@ dependencies {
minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}" 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("mezz.jei:jei-1.14.4:6.0.0.27:api")
compileOnly fg.deobf("com.blamejared.crafttweaker:CraftTweaker-1.15.2:6.0.0.9") compileOnly fg.deobf("com.blamejared.crafttweaker:CraftTweaker-1.14.4:5.0.1.162")
runtimeOnly fg.deobf("mezz.jei:jei-1.15.2:6.0.0.3") runtimeOnly fg.deobf("mezz.jei:jei-1.14.4:6.0.0.27")
shade 'org.squiddev:Cobalt:0.5.1-SNAPSHOT' shade 'org.squiddev:Cobalt:0.5.1-SNAPSHOT'
@@ -288,6 +289,15 @@ test {
} }
} }
jacocoTestReport {
reports {
xml.enabled true
html.enabled true
}
}
check.dependsOn jacocoTestReport
license { license {
mapping("java", "SLASHSTAR_STYLE") mapping("java", "SLASHSTAR_STYLE")
strictCheck true strictCheck true
@@ -373,7 +383,7 @@ curseforge {
apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : '' apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : ''
project { project {
id = '282001' id = '282001'
releaseType = 'alpha' releaseType = 'release'
changelog = "Release notes can be found on the GitHub repository (https://github.com/SquidDev-CC/CC-Tweaked/releases/tag/v${mc_version}-${mod_version})." changelog = "Release notes can be found on the GitHub repository (https://github.com/SquidDev-CC/CC-Tweaked/releases/tag/v${mc_version}-${mod_version})."
relations { relations {
@@ -451,7 +461,7 @@ githubRelease {
.takeWhile { it != 'Type "help changelog" to see the full version history.' } .takeWhile { it != 'Type "help changelog" to see the full version history.' }
.join("\n").trim() .join("\n").trim()
} }
prerelease true prerelease false
} }
def uploadTasks = ["uploadArchives", "curseforge", "githubRelease"] def uploadTasks = ["uploadArchives", "curseforge", "githubRelease"]

View File

@@ -10,10 +10,6 @@
<property name="file" value="config/checkstyle/suppressions.xml" /> <property name="file" value="config/checkstyle/suppressions.xml" />
</module> </module>
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="render_old"/>
</module>
<module name="TreeWalker"> <module name="TreeWalker">
<!-- Annotations --> <!-- Annotations -->
<module name="AnnotationLocation" /> <module name="AnnotationLocation" />

View File

@@ -1,6 +1,77 @@
--- Execute a specific command.
--
-- @tparam string command The command to execute.
-- @treturn boolean Whether the command executed successfully.
-- @treturn { string... } The output of this command, as a list of lines.
-- @treturn number|nil The number of "affected" objects, or `nil` if the command
-- failed. The definition of this varies from command to command.
-- @usage Set the block above the command computer to stone.
--
-- commands.exec("setblock ~ ~1 ~ minecraft:stone")
function exec(command) end function exec(command) end
--- Asynchronously execute a command.
--
-- Unlike @{exec}, this will immediately return, instead of waiting for the
-- command to execute. This allows you to run multiple commands at the same
-- time.
--
-- When this command has finished executing, it will queue a `task_complete`
-- event containing the result of executing this command (what @{exec} would
-- return).
--
-- @tparam string command The command to execute.
-- @treturn number The "task id". When this command has been executed, it will
-- queue a `task_complete` event with a matching id.
-- @usage Asynchronously sets the block above the computer to stone.
--
-- commands.execAsync("~ ~1 ~ minecraft:stone")
-- @see parallel One may also use the parallel API to run multiple commands at
-- once.
function execAsync(commad) end function execAsync(commad) end
--- List all available commands which the computer has permission to execute.
--
-- @treturn { string... } A list of all available commands
function list() end function list() end
--- Get the position of the current command computer.
--
-- @treturn number This computer's x position.
-- @treturn number This computer's y position.
-- @treturn number This computer's z position.
-- @see gps.locate To get the position of a non-command computer.
function getBlockPosition() end function getBlockPosition() end
function getBlockInfos(min_x, min_y, min_z, max_x, max_y, max_z) end
--- Get some basic information about a block.
--
-- The returned table contains the current name, metadata and block state (as
-- with @{turtle.inspect}). If there is a tile entity for that block, its NBT
-- will also be returned.
--
-- @tparam number x The x position of the block to query.
-- @tparam number y The y position of the block to query.
-- @tparam number z The z position of the block to query.
-- @treturn table The given block's information.
-- @throws If the coordinates are not within the world, or are not currently
-- loaded.
function getBlockInfo(x, y, z) end function getBlockInfo(x, y, z) end
--- Get information about a range of blocks.
--
-- This returns the same information as @{getBlockInfo}, just for multiple
-- blocks at once.
--
-- Blocks are traversed by ascending y level, followed by z and x - the returned
-- table may be indexed using `x + z*width + y*depth*depth`.
--
-- @tparam number min_x The start x coordinate of the range to query.
-- @tparam number min_y The start y coordinate of the range to query.
-- @tparam number min_z The start z coordinate of the range to query.
-- @tparam number max_x The end x coordinate of the range to query.
-- @tparam number max_y The end y coordinate of the range to query.
-- @tparam number max_z The end z coordinate of the range to query.
-- @treturn { table... } A list of information about each block.
-- @throws If the coordinates are not within the world.
-- @throws If trying to get information about more than 4096 blocks.
function getBlockInfos(min_x, min_y, min_z, max_x, max_y, max_z) end

View File

@@ -19,6 +19,48 @@ function getFreeSpace(path) end
function find(pattern) end function find(pattern) end
function getDir(path) end function getDir(path) end
--- 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 exstend this to
-- make other mount types by correctly assigning their return value for getDrive.
--
-- @tparam string path The path to check.
-- @treturn boolean If the path is mounted, rather than a normal file/folder.
-- @throws If the path does not exist.
-- @see getDrive
function isDriveRoot(path) end
--- Get the capacity of the drive at the given path.
--
-- This may be used in conjunction with @{getFreeSpace} to determine what
-- percentage of this drive has been used.
--
-- @tparam string path The path of the drive to get.
-- @treturn number This drive's capacity. This will be 0 for "read-only" drives,
-- such as the ROM or treasure disks.
function getCapacity(path) end
--- Get attributes about a specific file or folder.
--
-- The returned attributes table contains information about the size of the
-- file, whether it is a directory, and when it was created and last modified.
--
-- The creation and modification times are given as the number of milliseconds
-- since the UNIX epoch. This may be given to @{os.date} in order to convert it
-- to more usable form.
--
-- @tparam string path The path to get attributes for.
-- @treturn { size = number, isDir = boolean, created = number, modified = number }
-- The resulting attributes.
-- @throws If the path does not exist.
-- @see getSize If you only care about the file's size.
-- @see isDir If you only care whether a path is a directory or not.
function attributes(path) end
-- Defined in bios.lua
function complete(sPath, sLocation, bIncludeFiles, bIncludeDirs) end
--- A file handle which can be read from. --- A file handle which can be read from.
-- --
-- @type ReadHandle -- @type ReadHandle

View File

@@ -15,3 +15,10 @@ function cancelTimer(id) end
function cancelAlarm(id) end function cancelAlarm(id) end
function epoch(timezone) end function epoch(timezone) end
function date(format, time) end function date(format, time) end
-- Defined in bios.lua
function loadAPI(path) end
function pullEvent(filter) end
function pullEventRaw(filter) end
function version() end
function run(env, path, ...) end

28
doc/stub/pocket.lua Normal file
View File

@@ -0,0 +1,28 @@
--[[-
Control the current pocket computer, adding or removing upgrades.
This API is only available on pocket computers. As such, you may use its
presence to determine what kind of computer you are using:
```lua
if pocket then
print("On a pocket computer")
else
print("On something else")
end
```
]]
--- Search the player's inventory for another upgrade, replacing the existing
-- one with that item if found.
--
-- This inventory search starts from the player's currently selected slot,
-- allowing you to prioritise upgrades.
--
-- @throws If an upgrade cannot be found.
function equipBack() end
--- Remove the pocket computer's current upgrade.
--
-- @throws If this pocket computer does not currently have an upgrade.
function unequipBack() end

View File

@@ -1,14 +1,120 @@
--[[- Interact with redstone attached to this computer.
The @{redstone} library exposes three "types" of redstone control:
- Binary input/output (@{setOutput}/@{getInput}): These simply check if a
redstone wire has any input or output. A signal strength of 1 and 15 are
treated the same.
- Analogue input/output (@{setAnalogueOutput}/@{getAnalogueInput}): These
work with the actual signal strength of the redstone wired, from 0 to 15.
- Bundled cables (@{setBundledOutput}/@{getBundledInput}): These interact with
"bundled" cables, such as those from Project:Red. These allow you to send
16 separate on/off signals. Each channel corresponds to a colour, with the
first being @{colors.white} and the last @{colors.black}.
Whenever a redstone input changes, a `redstone` event will be fired. This may
be used in or
This module may also be referred to as `rs`. For example, one may call
`rs.getSides()` instead of @{redstone.getSides}.
@module redstone
@usage Toggle the redstone signal above the computer every 0.5 seconds.
while true do
redstone.setOutput("top", not redstone.getOutput("top"))
sleep(0.5)
end
@usage Mimic a redstone comparator in [subtraction mode][comparator].
while true do
local rear = rs.getAnalogueInput("back")
local sides = math.max(rs.getAnalogueInput("left"), rs.getAnalogueInput("right"))
rs.setAnalogueOutput("front", math.max(rear - sides, 0))
os.pullEvent("redstone") -- Wait for a change to inputs.
end
[comparator]: https://minecraft.gamepedia.com/Redstone_Comparator#Subtract_signal_strength "Redstone Comparator on the Minecraft wiki."
]]
--- Returns a table containing the six sides of the computer. Namely, "top",
-- "bottom", "left", "right", "front" and "back".
--
-- @treturn { string... } A table of valid sides.
function getSides() end function getSides() end
--- Turn the redstone signal of a specific side on or off.
--
-- @tparam string side The side to set.
-- @tparam boolean on Whether the redstone signal should be on or off. When on,
-- a signal strength of 15 is emitted.
function setOutput(side, on) end function setOutput(side, on) end
--- Get the current redstone output of a specific side.
--
-- @tparam string side The side to get.
-- @treturn boolean Whether the redstone output is on or off.
-- @see setOutput
function getOutput(side) end function getOutput(side) end
--- Get the current redstone input of a specific side.
--
-- @tparam string side The side to get.
-- @treturn boolean Whether the redstone input is on or off.
function getInput(side) end function getInput(side) end
function setBundledOutput(side, output) end
function getBundledOutput(side) end --- Set the redstone signal strength for a specific side.
function getBundledInput(side) end --
function testBundledInput(side, mask) end -- @tparam string side The side to set.
-- @tparam number value The signal strength, between 0 and 15.
-- @throws If `value` is not between 0 and 15.
function setAnalogOutput(side, value) end function setAnalogOutput(side, value) end
setAnalogueOutput = setAnalogOutput setAnalogueOutput = setAnalogOutput
--- Get the redstone output signal strength for a specific side.
--
-- @tparam string side The side to get.
-- @treturn number The output signal strength, between 0 and 15.
-- @see setAnalogueOutput
function getAnalogOutput(sid) end function getAnalogOutput(sid) end
getAnalogueOutput = getAnalogOutput getAnalogueOutput = getAnalogOutput
--- Get the redstone input signal strength for a specific side.
--
-- @tparam string side The side to get.
-- @treturn number The input signal strength, between 0 and 15.
function getAnalogInput(side) end function getAnalogInput(side) end
getAnalogueInput = getAnaloguInput getAnalogueInput = getAnalogInput
--- Set the bundled cable output for a specific side.
--
-- @tparam string side The side to set.
-- @tparam number The colour bitmask to set.
-- @see colors.subtract For removing a colour from the bitmask.
-- @see colors.combine For adding a colour to the bitmask.
function setBundledOutput(side, output) end
--- Get the bundled cable output for a specific side.
--
-- @tparam string side The side to get.
-- @treturn number The bundled cable's output.
function getBundledOutput(side) end
--- Get the bundled cable input for a specific side.
--
-- @tparam string side The side to get.
-- @treturn number The bundled cable's input.
-- @see testBundledInput To determine if a specific colour is set.
function getBundledInput(side) end
--- Determine if a specific combination of colours are on for the given side.
--
-- @tparam string side The side to test.
-- @tparam number mask The mask to test.
-- @see getBundledInput
-- @see colors.combine For adding a colour to the bitmask.
-- @usage Check if @{colors.white} and @{colors.black} are on for above the
-- computer.
--
-- print(redstone.testBundledInput("top", colors.combine(colors.white, colors.black)))
function testBundledInput(side, mask) end

View File

@@ -15,14 +15,14 @@ isColor = isColour
function getTextColour() end function getTextColour() end
getTextColor = getTextColor getTextColor = getTextColor
function getBackgroundColour() end function getBackgroundColour() end
getBackgroundColour = getBackgroundColour getBackgroundColor = getBackgroundColour
function blit(text, text_colours, background_colours) end function blit(text, text_colours, background_colours) end
function setPaletteColour(colour, ...) end function setPaletteColour(colour, ...) end
setPaletteColour = setPaletteColour setPaletteColor = setPaletteColour
function getPaletteColour(colour, ...) end function getPaletteColour(colour, ...) end
getPaletteColour = getPaletteColour getPaletteColor = getPaletteColour
function nativePaletteColour(colour) end function nativePaletteColour(colour) end
nativePaletteColour = nativePaletteColour nativePaletteColor = nativePaletteColour
--- @type Redirect --- @type Redirect
local Redirect = {} local Redirect = {}

View File

@@ -1,7 +1,7 @@
# Mod properties # Mod properties
mod_version=1.87.1 mod_version=1.89.1
# Minecraft properties (update mods.toml when changing) # Minecraft properties (update mods.toml when changing)
mc_version=1.15.2 mc_version=1.14.4
forge_version=31.1.41 forge_version=28.1.71
mappings_version=20200410-1.15.1 mappings_version=20191123-1.14.3

View File

@@ -33,17 +33,27 @@
;; It's useful to name arguments for documentation, so we allow this. It'd ;; It's useful to name arguments for documentation, so we allow this. It'd
;; be good to find a compromise in the future, but this works for now. ;; be good to find a compromise in the future, but this works for now.
-var:unused-arg -var:unused-arg)
;; Some APIS (keys, colour and os mainly) are incomplete right now.
-var:unresolved-member)
(lint (lint
(bracket-spaces (bracket-spaces
(call no-space) (call no-space)
(function-args no-space) (function-args no-space)
(parens no-space) (parens no-space)
(table space) (table space)
(index no-space)))) (index no-space))
;; colours imports from colors, and we don't handle that right now.
;; keys is entirely dynamic, so we skip it.
(dynamic-modules colours keys)
(globals
:max
_CC_DEFAULT_SETTINGS
_CC_DISABLE_LUA51_FEATURES
;; Ideally we'd pick these up from bios.lua, but illuaminate currently
;; isn't smart enough.
sleep write printError read rs)))
;; We disable the unused global linter in bios.lua and the APIs. In the future ;; We disable the unused global linter in bios.lua and the APIs. In the future
;; hopefully we'll get illuaminate to handle this. ;; hopefully we'll get illuaminate to handle this.
@@ -60,17 +70,13 @@
;; Suppress warnings for currently undocumented modules. ;; Suppress warnings for currently undocumented modules.
(at (at
(/doc/stub/commands.lua (/doc/stub/fs.lua
/doc/stub/fs.lua
/doc/stub/http.lua /doc/stub/http.lua
/doc/stub/os.lua /doc/stub/os.lua
/doc/stub/redstone.lua
/doc/stub/term.lua /doc/stub/term.lua
/doc/stub/turtle.lua /doc/stub/turtle.lua
/src/main/resources/*/computercraft/lua/rom/apis/command/commands.lua
/src/main/resources/*/computercraft/lua/rom/apis/io.lua /src/main/resources/*/computercraft/lua/rom/apis/io.lua
/src/main/resources/*/computercraft/lua/rom/apis/window.lua /src/main/resources/*/computercraft/lua/rom/apis/window.lua)
/src/main/resources/*/computercraft/lua/rom/modules/main/cc/shell/completion.lua)
(linters -doc:undocumented -doc:undocumented-arg)) (linters -doc:undocumented -doc:undocumented-arg))
@@ -79,6 +85,11 @@
(/src/main/resources/*/computercraft/lua/rom/apis/textutils.lua (/src/main/resources/*/computercraft/lua/rom/apis/textutils.lua
/src/main/resources/*/computercraft/lua/rom/modules/main/cc/completion.lua /src/main/resources/*/computercraft/lua/rom/modules/main/cc/completion.lua
/src/main/resources/*/computercraft/lua/rom/modules/main/cc/shell/completion.lua /src/main/resources/*/computercraft/lua/rom/modules/main/cc/shell/completion.lua
/src/main/resources/*/computercraft/lua/rom/programs/advanced/multishell.lua
/src/main/resources/*/computercraft/lua/rom/programs/shell.lua) /src/main/resources/*/computercraft/lua/rom/programs/shell.lua)
(linters -doc:unresolved-reference)) (linters -doc:unresolved-reference))
(at /src/test/resources/test-rom
(lint
(globals
:max sleep write
cct_test describe expect howlci fail it pending stub)))

View File

@@ -17,6 +17,9 @@
{ {
"condition": "computercraft:block_named" "condition": "computercraft:block_named"
}, },
{
"condition": "computercraft:has_id"
},
{ {
"condition": "minecraft:inverted", "condition": "minecraft:inverted",
"term": { "term": {

View File

@@ -17,6 +17,9 @@
{ {
"condition": "computercraft:block_named" "condition": "computercraft:block_named"
}, },
{
"condition": "computercraft:has_id"
},
{ {
"condition": "minecraft:inverted", "condition": "minecraft:inverted",
"term": { "term": {

View File

@@ -17,6 +17,9 @@
{ {
"condition": "computercraft:block_named" "condition": "computercraft:block_named"
}, },
{
"condition": "computercraft:has_id"
},
{ {
"condition": "minecraft:inverted", "condition": "minecraft:inverted",
"term": { "term": {

View File

@@ -17,6 +17,9 @@
{ {
"condition": "computercraft:block_named" "condition": "computercraft:block_named"
}, },
{
"condition": "computercraft:has_id"
},
{ {
"condition": "minecraft:inverted", "condition": "minecraft:inverted",
"term": { "term": {

View File

@@ -6,7 +6,7 @@
package dan200.computercraft; package dan200.computercraft;
import dan200.computercraft.api.turtle.event.TurtleAction; import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.core.apis.http.AddressRule; import dan200.computercraft.core.apis.AddressPredicate;
import dan200.computercraft.shared.Config; import dan200.computercraft.shared.Config;
import dan200.computercraft.shared.computer.blocks.BlockComputer; import dan200.computercraft.shared.computer.blocks.BlockComputer;
import dan200.computercraft.shared.computer.core.ClientComputerRegistry; import dan200.computercraft.shared.computer.core.ClientComputerRegistry;
@@ -39,13 +39,8 @@ import org.apache.logging.log4j.Logger;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Mod( ComputerCraft.MOD_ID ) @Mod( ComputerCraft.MOD_ID )
public final class ComputerCraft public final class ComputerCraft
@@ -55,8 +50,8 @@ public final class ComputerCraft
public static final int DATAFIXER_VERSION = 0; public static final int DATAFIXER_VERSION = 0;
// Configuration options // Configuration options
public static final String[] DEFAULT_HTTP_ALLOW = new String[] { "*" }; public static final String[] DEFAULT_HTTP_WHITELIST = new String[] { "*" };
public static final String[] DEFAULT_HTTP_DENY = new String[] { public static final String[] DEFAULT_HTTP_BLACKLIST = new String[] {
"127.0.0.0/8", "127.0.0.0/8",
"10.0.0.0/8", "10.0.0.0/8",
"172.16.0.0/12", "172.16.0.0/12",
@@ -70,18 +65,17 @@ public final class ComputerCraft
public static boolean disable_lua51_features = false; public static boolean disable_lua51_features = false;
public static String default_computer_settings = ""; public static String default_computer_settings = "";
public static boolean debug_enable = true; public static boolean debug_enable = true;
public static boolean logPeripheralErrors = false; public static boolean logPeripheralErrors = true;
public static boolean commandRequireCreative = true;
public static int computer_threads = 1; public static int computer_threads = 1;
public static long maxMainGlobalTime = TimeUnit.MILLISECONDS.toNanos( 10 ); public static long maxMainGlobalTime = TimeUnit.MILLISECONDS.toNanos( 10 );
public static long maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos( 5 ); public static long maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos( 5 );
public static boolean httpEnabled = true; public static boolean http_enable = true;
public static boolean httpWebsocketEnabled = true; public static boolean http_websocket_enable = true;
public static List<AddressRule> httpRules = Collections.unmodifiableList( Stream.concat( public static AddressPredicate http_whitelist = new AddressPredicate( DEFAULT_HTTP_WHITELIST );
Stream.of( DEFAULT_HTTP_DENY ).map( x -> AddressRule.parse( x, AddressRule.Action.DENY ) ).filter( Objects::nonNull ), public static AddressPredicate http_blacklist = new AddressPredicate( DEFAULT_HTTP_BLACKLIST );
Stream.of( DEFAULT_HTTP_ALLOW ).map( x -> AddressRule.parse( x, AddressRule.Action.ALLOW ) ).filter( Objects::nonNull )
).collect( Collectors.toList() ) );
public static int httpTimeout = 30000; public static int httpTimeout = 30000;
public static int httpMaxRequests = 16; public static int httpMaxRequests = 16;
@@ -97,6 +91,7 @@ public final class ComputerCraft
public static int modem_highAltitudeRangeDuringStorm = 384; public static int modem_highAltitudeRangeDuringStorm = 384;
public static int maxNotesPerTick = 8; public static int maxNotesPerTick = 8;
public static MonitorRenderer monitorRenderer = MonitorRenderer.BEST; public static MonitorRenderer monitorRenderer = MonitorRenderer.BEST;
public static long monitorBandwidth = 1_000_000;
public static boolean turtlesNeedFuel = true; public static boolean turtlesNeedFuel = true;
public static int turtleFuelLimit = 20000; public static int turtleFuelLimit = 20000;

View File

@@ -3,8 +3,10 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle; package dan200.computercraft.api;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeType;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.IItemProvider; import net.minecraft.util.IItemProvider;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;

View File

@@ -112,6 +112,31 @@ public final class ComputerCraftAPI
return getInstance().createResourceMount( domain, subPath ); return getInstance().createResourceMount( domain, subPath );
} }
/**
* Creates a file system mount to a resource folder, and returns it.
*
* Use in conjunction with {@link IComputerAccess#mount} or {@link IComputerAccess#mountWritable} to mount a
* resource folder onto a computer's file system.
*
* The files in this mount will be a combination of files in all mod jar, and data packs that contain
* resources with the same domain and path.
*
* @param klass The mod class to which the files belong.
* @param domain The domain under which to look for resources. eg: "mymod".
* @param subPath The subPath under which to look for resources. eg: "lua/myfiles".
* @return The mount, or {@code null} if it could be created for some reason.
* @see IComputerAccess#mount(String, IMount)
* @see IComputerAccess#mountWritable(String, IWritableMount)
* @see IMount
* @deprecated Use {@link #createResourceMount(String, String)} instead.
*/
@Nullable
@Deprecated
public static IMount createResourceMount( Class<?> klass, @Nonnull String domain, @Nonnull String subPath )
{
return getInstance().createResourceMount( domain, subPath );
}
/** /**
* Registers a peripheral provider to convert blocks into {@link IPeripheral} implementations. * Registers a peripheral provider to convert blocks into {@link IPeripheral} implementations.
* *

View File

@@ -1,61 +0,0 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.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 javax.annotation.Nonnull;
import java.util.Objects;
/**
* A model to render, combined with a transformation matrix to apply.
*/
public final class TransformedModel
{
private final IBakedModel model;
private final TransformationMatrix matrix;
public TransformedModel( @Nonnull IBakedModel model, @Nonnull TransformationMatrix matrix )
{
this.model = Objects.requireNonNull( model );
this.matrix = Objects.requireNonNull( matrix );
}
public TransformedModel( @Nonnull IBakedModel model )
{
this.model = Objects.requireNonNull( model );
this.matrix = TransformationMatrix.identity();
}
public static TransformedModel of( @Nonnull ModelResourceLocation location )
{
ModelManager modelManager = Minecraft.getInstance().getModelManager();
return new TransformedModel( modelManager.getModel( location ) );
}
public static TransformedModel of( @Nonnull ItemStack item, @Nonnull TransformationMatrix transform )
{
IBakedModel model = Minecraft.getInstance().getItemRenderer().getItemModelMesher().getItemModel( item );
return new TransformedModel( model, transform );
}
@Nonnull
public IBakedModel getModel()
{
return model;
}
@Nonnull
public TransformationMatrix getMatrix()
{
return matrix;
}
}

View File

@@ -11,6 +11,8 @@ import net.minecraft.world.World;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.List; import java.util.List;
@@ -21,10 +23,10 @@ import java.util.List;
* *
* Ready made implementations of this interface can be created using * Ready made implementations of this interface can be created using
* {@link ComputerCraftAPI#createSaveDirMount(World, String, long)} or * {@link ComputerCraftAPI#createSaveDirMount(World, String, long)} or
* {@link ComputerCraftAPI#createResourceMount(String, String)}, or you're free to implement it yourselves! * {@link ComputerCraftAPI#createResourceMount(Class, String, String)}, or you're free to implement it yourselves!
* *
* @see ComputerCraftAPI#createSaveDirMount(World, String, long) * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(String, String) * @see ComputerCraftAPI#createResourceMount(Class, String, String)
* @see IComputerAccess#mount(String, IMount) * @see IComputerAccess#mount(String, IMount)
* @see IWritableMount * @see IWritableMount
*/ */
@@ -66,6 +68,18 @@ public interface IMount
*/ */
long getSize( @Nonnull String path ) throws IOException; long getSize( @Nonnull String path ) throws IOException;
/**
* Opens a file with a given path, and returns an {@link InputStream} representing its contents.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return A stream representing the contents of the file.
* @throws IOException If the file does not exist, or could not be opened.
* @deprecated Use {@link #openChannelForRead(String)} instead
*/
@Nonnull
@Deprecated
InputStream openForRead( @Nonnull String path ) throws IOException;
/** /**
* Opens a file with a given path, and returns an {@link ReadableByteChannel} representing its contents. * Opens a file with a given path, and returns an {@link ReadableByteChannel} representing its contents.
* *
@@ -76,7 +90,10 @@ public interface IMount
* @throws IOException If the file does not exist, or could not be opened. * @throws IOException If the file does not exist, or could not be opened.
*/ */
@Nonnull @Nonnull
ReadableByteChannel openForRead( @Nonnull String path ) throws IOException; default ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
{
return Channels.newChannel( openForRead( path ) );
}
/** /**
* Get attributes about the given file. * Get attributes about the given file.

View File

@@ -12,6 +12,7 @@ import net.minecraft.world.World;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel; import java.nio.channels.WritableByteChannel;
import java.util.OptionalLong; import java.util.OptionalLong;
@@ -45,6 +46,18 @@ public interface IWritableMount extends IMount
*/ */
void delete( @Nonnull String path ) throws IOException; void delete( @Nonnull String path ) throws IOException;
/**
* Opens a file with a given path, and returns an {@link OutputStream} for writing to it.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return A stream for writing to
* @throws IOException If the file could not be opened for writing.
* @deprecated Use {@link #openChannelForWrite(String)} instead.
*/
@Nonnull
@Deprecated
OutputStream openForWrite( @Nonnull String path ) throws IOException;
/** /**
* Opens a file with a given path, and returns an {@link OutputStream} for writing to it. * Opens a file with a given path, and returns an {@link OutputStream} for writing to it.
* *
@@ -54,7 +67,22 @@ public interface IWritableMount extends IMount
* @throws IOException If the file could not be opened for writing. * @throws IOException If the file could not be opened for writing.
*/ */
@Nonnull @Nonnull
WritableByteChannel openForWrite( @Nonnull String path ) throws IOException; default WritableByteChannel openChannelForWrite( @Nonnull String path ) throws IOException
{
return Channels.newChannel( openForWrite( path ) );
}
/**
* Opens a file with a given path, and returns an {@link OutputStream} for appending to it.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return A stream for writing to.
* @throws IOException If the file could not be opened for writing.
* @deprecated Use {@link #openChannelForAppend(String)} instead.
*/
@Nonnull
@Deprecated
OutputStream openForAppend( @Nonnull String path ) throws IOException;
/** /**
* Opens a file with a given path, and returns an {@link OutputStream} for appending to it. * Opens a file with a given path, and returns an {@link OutputStream} for appending to it.
@@ -65,7 +93,10 @@ public interface IWritableMount extends IMount
* @throws IOException If the file could not be opened for writing. * @throws IOException If the file could not be opened for writing.
*/ */
@Nonnull @Nonnull
WritableByteChannel openForAppend( @Nonnull String path ) throws IOException; default WritableByteChannel openChannelForAppend( @Nonnull String path ) throws IOException
{
return Channels.newChannel( openForAppend( path ) );
}
/** /**
* Get the amount of free space on the mount, in bytes. You should decrease this value as the user writes to the * Get the amount of free space on the mount, in bytes. You should decrease this value as the user writes to the

View File

@@ -79,7 +79,7 @@ public interface IMedia
* @see IMount * @see IMount
* @see dan200.computercraft.api.filesystem.IWritableMount * @see dan200.computercraft.api.filesystem.IWritableMount
* @see dan200.computercraft.api.ComputerCraftAPI#createSaveDirMount(World, String, long) * @see dan200.computercraft.api.ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see dan200.computercraft.api.ComputerCraftAPI#createResourceMount(String, String) * @see dan200.computercraft.api.ComputerCraftAPI#createResourceMount(Class, String, String)
*/ */
@Nullable @Nullable
default IMount createDataMount( @Nonnull ItemStack stack, @Nonnull World world ) default IMount createDataMount( @Nonnull ItemStack stack, @Nonnull World world )

View File

@@ -14,6 +14,7 @@ import net.minecraft.world.World;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Map; import java.util.Map;
/** /**
@@ -30,9 +31,9 @@ public interface IComputerAccess
* @param mount The mount object to mount on the computer. * @param mount The mount object to mount on the computer.
* @return The location on the computer's file system where you the mount mounted, or {@code null} if there was already a * @return The location on the computer's file system where you the mount mounted, or {@code null} if there was already a
* file in the desired location. Store this value if you wish to unmount the mount later. * file in the desired location. Store this value if you wish to unmount the mount later.
* @throws NotAttachedException If the peripheral has been detached. * @throws RuntimeException If the peripheral has been detached.
* @see ComputerCraftAPI#createSaveDirMount(World, String, long) * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(String, String) * @see ComputerCraftAPI#createResourceMount(Class, String, String)
* @see #mount(String, IMount, String) * @see #mount(String, IMount, String)
* @see #mountWritable(String, IWritableMount) * @see #mountWritable(String, IWritableMount)
* @see #unmount(String) * @see #unmount(String)
@@ -52,9 +53,9 @@ public interface IComputerAccess
* @param driveName A custom name to give for this mount location, as returned by {@code fs.getDrive()}. * @param driveName A custom name to give for this mount location, as returned by {@code fs.getDrive()}.
* @return The location on the computer's file system where you the mount mounted, or {@code null} if there was already a * @return The location on the computer's file system where you the mount mounted, or {@code null} if there was already a
* file in the desired location. Store this value if you wish to unmount the mount later. * file in the desired location. Store this value if you wish to unmount the mount later.
* @throws NotAttachedException If the peripheral has been detached. * @throws RuntimeException If the peripheral has been detached.
* @see ComputerCraftAPI#createSaveDirMount(World, String, long) * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(String, String) * @see ComputerCraftAPI#createResourceMount(Class, String, String)
* @see #mount(String, IMount) * @see #mount(String, IMount)
* @see #mountWritable(String, IWritableMount) * @see #mountWritable(String, IWritableMount)
* @see #unmount(String) * @see #unmount(String)
@@ -70,9 +71,9 @@ public interface IComputerAccess
* @param mount The mount object to mount on the computer. * @param mount The mount object to mount on the computer.
* @return The location on the computer's file system where you the mount mounted, or null if there was already a * @return The location on the computer's file system where you the mount mounted, or null if there was already a
* file in the desired location. Store this value if you wish to unmount the mount later. * file in the desired location. Store this value if you wish to unmount the mount later.
* @throws NotAttachedException If the peripheral has been detached. * @throws RuntimeException If the peripheral has been detached.
* @see ComputerCraftAPI#createSaveDirMount(World, String, long) * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(String, String) * @see ComputerCraftAPI#createResourceMount(Class, String, String)
* @see #mount(String, IMount) * @see #mount(String, IMount)
* @see #unmount(String) * @see #unmount(String)
* @see IMount * @see IMount
@@ -91,9 +92,9 @@ public interface IComputerAccess
* @param driveName A custom name to give for this mount location, as returned by {@code fs.getDrive()}. * @param driveName A custom name to give for this mount location, as returned by {@code fs.getDrive()}.
* @return The location on the computer's file system where you the mount mounted, or null if there was already a * @return The location on the computer's file system where you the mount mounted, or null if there was already a
* file in the desired location. Store this value if you wish to unmount the mount later. * file in the desired location. Store this value if you wish to unmount the mount later.
* @throws NotAttachedException If the peripheral has been detached. * @throws RuntimeException If the peripheral has been detached.
* @see ComputerCraftAPI#createSaveDirMount(World, String, long) * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(String, String) * @see ComputerCraftAPI#createResourceMount(Class, String, String)
* @see #mount(String, IMount) * @see #mount(String, IMount)
* @see #unmount(String) * @see #unmount(String)
* @see IMount * @see IMount
@@ -113,8 +114,8 @@ public interface IComputerAccess
* @param location The desired location in the computers file system of the directory to unmount. * @param location The desired location in the computers file system of the directory to unmount.
* This must be the location of a directory previously mounted by {@link #mount(String, IMount)} or * This must be the location of a directory previously mounted by {@link #mount(String, IMount)} or
* {@link #mountWritable(String, IWritableMount)}, as indicated by their return value. * {@link #mountWritable(String, IWritableMount)}, as indicated by their return value.
* @throws NotAttachedException If the peripheral has been detached. * @throws RuntimeException If the peripheral has been detached.
* @throws IllegalStateException If the mount does not exist, or was mounted by another peripheral. * @throws RuntimeException If the mount does not exist, or was mounted by another peripheral.
* @see #mount(String, IMount) * @see #mount(String, IMount)
* @see #mountWritable(String, IWritableMount) * @see #mountWritable(String, IWritableMount)
*/ */
@@ -145,7 +146,7 @@ public interface IComputerAccess
* to lua data types in the same fashion as the return values of IPeripheral.callMethod(). * to lua data types in the same fashion as the return values of IPeripheral.callMethod().
* *
* You may supply {@code null} to indicate that no arguments are to be supplied. * You may supply {@code null} to indicate that no arguments are to be supplied.
* @throws NotAttachedException If the peripheral has been detached. * @throws RuntimeException If the peripheral has been detached.
* @see IPeripheral#callMethod * @see IPeripheral#callMethod
*/ */
void queueEvent( @Nonnull String event, @Nullable Object[] arguments ); void queueEvent( @Nonnull String event, @Nullable Object[] arguments );
@@ -158,7 +159,7 @@ public interface IComputerAccess
* which peripheral the event came. * which peripheral the event came.
* *
* @return A string unique to the computer, but not globally. * @return A string unique to the computer, but not globally.
* @throws NotAttachedException If the peripheral has been detached. * @throws RuntimeException If the peripheral has been detached.
*/ */
@Nonnull @Nonnull
String getAttachmentName(); String getAttachmentName();
@@ -169,12 +170,14 @@ public interface IComputerAccess
* This may include other peripherals on the wired network or peripherals on other sides of the computer. * This may include other peripherals on the wired network or peripherals on other sides of the computer.
* *
* @return All reachable peripherals * @return All reachable peripherals
* @throws NotAttachedException If the peripheral has been detached.
* @see #getAttachmentName() * @see #getAttachmentName()
* @see #getAvailablePeripheral(String) * @see #getAvailablePeripheral(String)
*/ */
@Nonnull @Nonnull
Map<String, IPeripheral> getAvailablePeripherals(); default Map<String, IPeripheral> getAvailablePeripherals()
{
return Collections.emptyMap();
}
/** /**
* Get a reachable peripheral with the given attachment name. This is a equivalent to * Get a reachable peripheral with the given attachment name. This is a equivalent to
@@ -185,7 +188,10 @@ public interface IComputerAccess
* @see #getAvailablePeripherals() * @see #getAvailablePeripherals()
*/ */
@Nullable @Nullable
IPeripheral getAvailablePeripheral( @Nonnull String name ); default IPeripheral getAvailablePeripheral( @Nonnull String name )
{
return null;
}
/** /**
* Get a {@link IWorkMonitor} for tasks your peripheral might execute on the main (server) thread. * Get a {@link IWorkMonitor} for tasks your peripheral might execute on the main (server) thread.
@@ -199,8 +205,10 @@ public interface IComputerAccess
* thread. * thread.
* *
* @return The work monitor for the main thread, or {@code null} if this computer does not have one. * @return The work monitor for the main thread, or {@code null} if this computer does not have one.
* @throws NotAttachedException If the peripheral has been detached.
*/ */
@Nonnull @Nullable
IWorkMonitor getMainThreadMonitor(); default IWorkMonitor getMainThreadMonitor()
{
return null;
}
} }

View File

@@ -1,25 +0,0 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.peripheral;
/**
* Thrown when performing operations on {@link IComputerAccess} when the current peripheral is no longer attached to
* the computer.
*/
public class NotAttachedException extends IllegalStateException
{
private static final long serialVersionUID = 1221244785535553536L;
public NotAttachedException()
{
super( "You are not attached to this Computer" );
}
public NotAttachedException( String s )
{
super( s );
}
}

View File

@@ -6,10 +6,10 @@
package dan200.computercraft.api.turtle; package dan200.computercraft.api.turtle;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.event.TurtleAttackEvent; import dan200.computercraft.api.turtle.event.TurtleAttackEvent;
import dan200.computercraft.api.turtle.event.TurtleBlockEvent; import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ModelResourceLocation; import net.minecraft.client.renderer.model.ModelResourceLocation;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
@@ -18,9 +18,11 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.event.entity.player.AttackEntityEvent; import net.minecraftforge.event.entity.player.AttackEntityEvent;
import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.event.world.BlockEvent;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.vecmath.Matrix4f;
/** /**
* The primary interface for defining an update for Turtles. A turtle update * The primary interface for defining an update for Turtles. A turtle update
@@ -124,11 +126,12 @@ public interface ITurtleUpgrade
* *
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models! * @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models!
* @param side Which side of the turtle (left or right) the upgrade resides on. * @param side Which side of the turtle (left or right) the upgrade resides on.
* @return The model that you wish to be used to render your upgrade. * @return The model that you wish to be used to render your upgrade, and a transformation to apply to it. Returning
* a transformation of {@code null} has the same effect as the identify matrix.
*/ */
@Nonnull @Nonnull
@OnlyIn( Dist.CLIENT ) @OnlyIn( Dist.CLIENT )
TransformedModel getModel( @Nullable ITurtleAccess turtle, @Nonnull TurtleSide side ); Pair<IBakedModel, Matrix4f> getModel( @Nullable ITurtleAccess turtle, @Nonnull TurtleSide side );
/** /**
* Called once per tick for each turtle which has the upgrade equipped. * Called once per tick for each turtle which has the upgrade equipped.

View File

@@ -17,70 +17,70 @@ public enum TurtleAnimation
/** /**
* An animation which does nothing. This takes no time to complete. * An animation which does nothing. This takes no time to complete.
* *
* @see #WAIT * @see #Wait
* @see #SHORT_WAIT * @see #ShortWait
*/ */
NONE, None,
/** /**
* Make the turtle move forward. Note that the animation starts from the block <em>behind</em> it, and * Make the turtle move forward. Note that the animation starts from the block <em>behind</em> it, and
* moves into this one. * moves into this one.
*/ */
MOVE_FORWARD, MoveForward,
/** /**
* Make the turtle move backwards. Note that the animation starts from the block <em>in front</em> it, and * Make the turtle move backwards. Note that the animation starts from the block <em>in front</em> it, and
* moves into this one. * moves into this one.
*/ */
MOVE_BACK, MoveBack,
/** /**
* Make the turtle move backwards. Note that the animation starts from the block <em>above</em> it, and * Make the turtle move backwards. Note that the animation starts from the block <em>above</em> it, and
* moves into this one. * moves into this one.
*/ */
MOVE_UP, MoveUp,
/** /**
* Make the turtle move backwards. Note that the animation starts from the block <em>below</em> it, and * Make the turtle move backwards. Note that the animation starts from the block <em>below</em> it, and
* moves into this one. * moves into this one.
*/ */
MOVE_DOWN, MoveDown,
/** /**
* Turn the turtle to the left. Note that the animation starts with the turtle facing <em>right</em>, and * Turn the turtle to the left. Note that the animation starts with the turtle facing <em>right</em>, and
* the turtle turns to face in the current direction. * the turtle turns to face in the current direction.
*/ */
TURN_LEFT, TurnLeft,
/** /**
* Turn the turtle to the left. Note that the animation starts with the turtle facing <em>right</em>, and * Turn the turtle to the left. Note that the animation starts with the turtle facing <em>right</em>, and
* the turtle turns to face in the current direction. * the turtle turns to face in the current direction.
*/ */
TURN_RIGHT, TurnRight,
/** /**
* Swing the tool on the left. * Swing the tool on the left.
*/ */
SWING_LEFT_TOOL, SwingLeftTool,
/** /**
* Swing the tool on the right. * Swing the tool on the right.
*/ */
SWING_RIGHT_TOOL, SwingRightTool,
/** /**
* Wait until the animation has finished, performing no movement. * Wait until the animation has finished, performing no movement.
* *
* @see #SHORT_WAIT * @see #ShortWait
* @see #NONE * @see #None
*/ */
WAIT, Wait,
/** /**
* Wait until the animation has finished, performing no movement. This takes 4 ticks to complete. * Wait until the animation has finished, performing no movement. This takes 4 ticks to complete.
* *
* @see #WAIT * @see #Wait
* @see #NONE * @see #None
*/ */
SHORT_WAIT, ShortWait,
} }

View File

@@ -13,10 +13,10 @@ public enum TurtleSide
/** /**
* The turtle's left side (where the pickaxe usually is on a Wireless Mining Turtle). * The turtle's left side (where the pickaxe usually is on a Wireless Mining Turtle).
*/ */
LEFT, Left,
/** /**
* The turtle's right side (where the modem usually is on a Wireless Mining Turtle). * The turtle's right side (where the modem usually is on a Wireless Mining Turtle).
*/ */
RIGHT, Right,
} }

View File

@@ -16,28 +16,28 @@ public enum TurtleUpgradeType
* A tool is rendered as an item on the side of the turtle, and responds to the {@code turtle.dig()} * A tool is rendered as an item on the side of the turtle, and responds to the {@code turtle.dig()}
* and {@code turtle.attack()} methods (Such as pickaxe or sword on Mining and Melee turtles). * and {@code turtle.attack()} methods (Such as pickaxe or sword on Mining and Melee turtles).
*/ */
TOOL, Tool,
/** /**
* A peripheral adds a special peripheral which is attached to the side of the turtle, * A peripheral adds a special peripheral which is attached to the side of the turtle,
* and can be interacted with the {@code peripheral} API (Such as the modem on Wireless Turtles). * and can be interacted with the {@code peripheral} API (Such as the modem on Wireless Turtles).
*/ */
PERIPHERAL, Peripheral,
/** /**
* An upgrade which provides both a tool and a peripheral. This can be used when you wish * An upgrade which provides both a tool and a peripheral. This can be used when you wish
* your upgrade to also provide methods. For example, a pickaxe could provide methods * your upgrade to also provide methods. For example, a pickaxe could provide methods
* determining whether it can break the given block or not. * determining whether it can break the given block or not.
*/ */
BOTH; Both;
public boolean isTool() public boolean isTool()
{ {
return this == TOOL || this == BOTH; return this == Tool || this == Both;
} }
public boolean isPeripheral() public boolean isPeripheral()
{ {
return this == PERIPHERAL || this == BOTH; return this == Peripheral || this == Both;
} }
} }

View File

@@ -19,10 +19,10 @@ public enum TurtleVerb
/** /**
* The turtle called {@code turtle.dig()}, {@code turtle.digUp()} or {@code turtle.digDown()}. * The turtle called {@code turtle.dig()}, {@code turtle.digUp()} or {@code turtle.digDown()}.
*/ */
DIG, Dig,
/** /**
* The turtle called {@code turtle.attack()}, {@code turtle.attackUp()} or {@code turtle.attackDown()}. * The turtle called {@code turtle.attack()}, {@code turtle.attackUp()} or {@code turtle.attackDown()}.
*/ */
ATTACK, Attack,
} }

View File

@@ -11,19 +11,20 @@ import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.media.items.ItemDisk; import dan200.computercraft.shared.media.items.ItemDisk;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.util.Colour; import dan200.computercraft.shared.util.Colour;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.IUnbakedModel; import net.minecraft.client.renderer.model.IUnbakedModel;
import net.minecraft.client.renderer.model.ModelResourceLocation; import net.minecraft.client.renderer.model.ModelResourceLocation;
import net.minecraft.inventory.container.PlayerContainer; import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ColorHandlerEvent; import net.minecraftforge.client.event.ColorHandlerEvent;
import net.minecraftforge.client.event.ModelBakeEvent; import net.minecraftforge.client.event.ModelBakeEvent;
import net.minecraftforge.client.event.ModelRegistryEvent; import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.client.model.BasicState;
import net.minecraftforge.client.model.ModelLoader; import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.ModelLoaderRegistry; import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.client.model.SimpleModelTransform;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
@@ -70,13 +71,13 @@ public final class ClientRegistry
@SubscribeEvent @SubscribeEvent
public static void registerModels( ModelRegistryEvent event ) public static void registerModels( ModelRegistryEvent event )
{ {
ModelLoaderRegistry.registerLoader( new ResourceLocation( ComputerCraft.MOD_ID, "turtle" ), TurtleModelLoader.INSTANCE ); ModelLoaderRegistry.registerLoader( TurtleModelLoader.INSTANCE );
} }
@SubscribeEvent @SubscribeEvent
public static void onTextureStitchEvent( TextureStitchEvent.Pre event ) public static void onTextureStitchEvent( TextureStitchEvent.Pre event )
{ {
if( !event.getMap().getTextureLocation().equals( PlayerContainer.LOCATION_BLOCKS_TEXTURE ) ) return; if( event.getMap() != Minecraft.getInstance().getTextureMap() ) return;
for( String extra : EXTRA_TEXTURES ) for( String extra : EXTRA_TEXTURES )
{ {
@@ -91,18 +92,29 @@ public final class ClientRegistry
ModelLoader loader = event.getModelLoader(); ModelLoader loader = event.getModelLoader();
Map<ResourceLocation, IBakedModel> registry = event.getModelRegistry(); Map<ResourceLocation, IBakedModel> registry = event.getModelRegistry();
for( String modelName : EXTRA_MODELS ) for( String model : EXTRA_MODELS )
{ {
ResourceLocation location = new ResourceLocation( ComputerCraft.MOD_ID, "item/" + modelName ); IBakedModel bakedModel = bake( loader, loader.getUnbakedModel( new ResourceLocation( ComputerCraft.MOD_ID, "item/" + model ) ) );
IUnbakedModel model = loader.getUnbakedModel( location );
model.getTextures( loader::getUnbakedModel, new HashSet<>() );
IBakedModel baked = model.bakeModel( loader, ModelLoader.defaultTextureGetter(), SimpleModelTransform.IDENTITY, location ); if( bakedModel != null )
if( baked != null )
{ {
registry.put( new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, modelName ), "inventory" ), baked ); registry.put(
new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, model ), "inventory" ),
bakedModel
);
} }
} }
// And load the custom turtle models in too.
registry.put(
new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, "turtle_normal" ), "inventory" ),
bake( loader, TurtleModelLoader.INSTANCE.loadModel( new ResourceLocation( ComputerCraft.MOD_ID, "item/turtle_normal" ) ) )
);
registry.put(
new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, "turtle_advanced" ), "inventory" ),
bake( loader, TurtleModelLoader.INSTANCE.loadModel( new ResourceLocation( ComputerCraft.MOD_ID, "item/turtle_advanced" ) ) )
);
} }
@SubscribeEvent @SubscribeEvent
@@ -130,7 +142,7 @@ public final class ClientRegistry
case 2: // Light colour case 2: // Light colour
{ {
int light = ItemPocketComputer.getLightState( stack ); int light = ItemPocketComputer.getLightState( stack );
return light == -1 ? Colour.BLACK.getHex() : light; return light == -1 ? Colour.Black.getHex() : light;
} }
} }
}, ComputerCraft.Items.pocketComputerNormal, ComputerCraft.Items.pocketComputerAdvanced ); }, ComputerCraft.Items.pocketComputerNormal, ComputerCraft.Items.pocketComputerAdvanced );
@@ -141,4 +153,15 @@ public final class ClientRegistry
ComputerCraft.Blocks.turtleNormal, ComputerCraft.Blocks.turtleAdvanced ComputerCraft.Blocks.turtleNormal, ComputerCraft.Blocks.turtleAdvanced
); );
} }
private static IBakedModel bake( ModelLoader loader, IUnbakedModel model )
{
model.getTextures( loader::getUnbakedModel, new HashSet<>() );
return model.bake(
loader,
ModelLoader.defaultTextureGetter(),
new BasicState( model.getDefaultState(), false ), DefaultVertexFormats.BLOCK
);
}
} }

View File

@@ -5,15 +5,15 @@
*/ */
package dan200.computercraft.client.gui; package dan200.computercraft.client.gui;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.client.FrameInfo; import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.util.Colour; import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.Palette; import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.*; import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
@@ -24,10 +24,21 @@ import javax.annotation.Nullable;
public final class FixedWidthFontRenderer public final class FixedWidthFontRenderer
{ {
private static final Matrix4f IDENTITY = TransformationMatrix.identity().getMatrix();
private static final ResourceLocation FONT = new ResourceLocation( "computercraft", "textures/gui/term_font.png" ); private static final ResourceLocation FONT = new ResourceLocation( "computercraft", "textures/gui/term_font.png" );
/**
* Like {@link DefaultVertexFormats#POSITION_TEX_COLOR}, but flipped. This is backported from 1.15, hence the
* custom format.
*/
public static final VertexFormat POSITION_COLOR_TEX = new VertexFormat();
static
{
POSITION_COLOR_TEX.addElement( DefaultVertexFormats.POSITION_3F );
POSITION_COLOR_TEX.addElement( DefaultVertexFormats.COLOR_4UB );
POSITION_COLOR_TEX.addElement( DefaultVertexFormats.TEX_2F );
}
public static final int FONT_HEIGHT = 9; public static final int FONT_HEIGHT = 9;
public static final int FONT_WIDTH = 6; public static final int FONT_WIDTH = 6;
public static final float WIDTH = 256.0f; public static final float WIDTH = 256.0f;
@@ -35,24 +46,21 @@ public final class FixedWidthFontRenderer
public static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH; public static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
public static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH; public static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
public static final RenderType TYPE = Type.MAIN;
private FixedWidthFontRenderer() private FixedWidthFontRenderer()
{ {
} }
private static float toGreyscale( double[] rgb ) public static float toGreyscale( double[] rgb )
{ {
return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3); return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3);
} }
private static int getColour( char c ) public static int getColour( char c, Colour def )
{ {
int i = "0123456789abcdef".indexOf( c ); return 15 - Terminal.getColour( c, def );
return i < 0 ? 0 : 15 - i;
} }
private static void drawChar( Matrix4f transform, IVertexBuilder buffer, float x, float y, int index, float r, float g, float b ) private static void drawChar( BufferBuilder buffer, float x, float y, int index, float r, float g, float b )
{ {
// Short circuit to avoid the common case - the texture should be blank here after all. // Short circuit to avoid the common case - the texture should be blank here after all.
if( index == '\0' || index == ' ' ) return; if( index == '\0' || index == ' ' ) return;
@@ -63,27 +71,27 @@ public final class FixedWidthFontRenderer
int xStart = 1 + column * (FONT_WIDTH + 2); int xStart = 1 + column * (FONT_WIDTH + 2);
int yStart = 1 + row * (FONT_HEIGHT + 2); int yStart = 1 + row * (FONT_HEIGHT + 2);
buffer.pos( transform, x, y, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, yStart / WIDTH ).endVertex(); buffer.pos( x, y, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, yStart / WIDTH ).endVertex();
buffer.pos( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex(); buffer.pos( x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
buffer.pos( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).endVertex(); buffer.pos( x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).endVertex();
buffer.pos( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).endVertex(); buffer.pos( x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).endVertex();
buffer.pos( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex(); buffer.pos( x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
buffer.pos( transform, x + FONT_WIDTH, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex(); buffer.pos( x + FONT_WIDTH, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
} }
private static void drawQuad( Matrix4f transform, IVertexBuilder buffer, float x, float y, float width, float height, float r, float g, float b ) private static void drawQuad( BufferBuilder buffer, float x, float y, float width, float height, float r, float g, float b )
{ {
buffer.pos( transform, x, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_START ).endVertex(); buffer.pos( x, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_START ).endVertex();
buffer.pos( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_END ).endVertex(); buffer.pos( x, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_END ).endVertex();
buffer.pos( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_START ).endVertex(); buffer.pos( x + width, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_START ).endVertex();
buffer.pos( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_START ).endVertex(); buffer.pos( x + width, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_START ).endVertex();
buffer.pos( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_END ).endVertex(); buffer.pos( x, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_END ).endVertex();
buffer.pos( transform, x + width, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_END ).endVertex(); buffer.pos( x + width, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_END ).endVertex();
} }
private static void drawQuad( Matrix4f transform, IVertexBuilder buffer, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex ) private static void drawQuad( BufferBuilder buffer, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex )
{ {
double[] colour = palette.getColour( getColour( colourIndex ) ); double[] colour = palette.getColour( getColour( colourIndex, Colour.Black ) );
float r, g, b; float r, g, b;
if( greyscale ) if( greyscale )
{ {
@@ -96,23 +104,23 @@ public final class FixedWidthFontRenderer
b = (float) colour[2]; b = (float) colour[2];
} }
drawQuad( transform, buffer, x, y, width, height, r, g, b ); drawQuad( buffer, x, y, width, height, r, g, b );
} }
private static void drawBackground( private static void drawBackground(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder renderer, float x, float y, @Nonnull BufferBuilder renderer, float x, float y,
@Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale, @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
float leftMarginSize, float rightMarginSize, float height float leftMarginSize, float rightMarginSize, float height
) )
{ {
if( leftMarginSize > 0 ) if( leftMarginSize > 0 )
{ {
drawQuad( transform, renderer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ) ); drawQuad( renderer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ) );
} }
if( rightMarginSize > 0 ) if( rightMarginSize > 0 )
{ {
drawQuad( transform, renderer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ) ); drawQuad( renderer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ) );
} }
// Batch together runs of identical background cells. // Batch together runs of identical background cells.
@@ -125,7 +133,7 @@ public final class FixedWidthFontRenderer
if( blockColour != '\0' ) if( blockColour != '\0' )
{ {
drawQuad( transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour ); drawQuad( renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour );
} }
blockColour = colourIndex; blockColour = colourIndex;
@@ -134,24 +142,24 @@ public final class FixedWidthFontRenderer
if( blockColour != '\0' ) if( blockColour != '\0' )
{ {
drawQuad( transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour ); drawQuad( renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour );
} }
} }
public static void drawString( public static void drawString(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder renderer, float x, float y, @Nonnull BufferBuilder renderer, float x, float y,
@Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour,
@Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize @Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize
) )
{ {
if( backgroundColour != null ) if( backgroundColour != null )
{ {
drawBackground( transform, renderer, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT ); drawBackground( renderer, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT );
} }
for( int i = 0; i < text.length(); i++ ) for( int i = 0; i < text.length(); i++ )
{ {
double[] colour = palette.getColour( 15 - "0123456789abcdef".indexOf( textColour.charAt( i ) ) ); double[] colour = palette.getColour( getColour( textColour.charAt( i ), Colour.White ) );
float r, g, b; float r, g, b;
if( greyscale ) if( greyscale )
{ {
@@ -167,7 +175,7 @@ public final class FixedWidthFontRenderer
// Draw char // Draw char
int index = text.charAt( i ); int index = text.charAt( i );
if( index > 255 ) index = '?'; if( index > 255 ) index = '?';
drawChar( transform, renderer, x + i * FONT_WIDTH, y, index, r, g, b ); drawChar( renderer, x + i * FONT_WIDTH, y, index, r, g, b );
} }
} }
@@ -179,13 +187,15 @@ public final class FixedWidthFontRenderer
{ {
bindFont(); bindFont();
IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().getRenderTypeBuffers().getBufferSource(); Tessellator tessellator = Tessellator.getInstance();
drawString( IDENTITY, ((IRenderTypeBuffer) renderer).getBuffer( TYPE ), x, y, text, textColour, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize ); BufferBuilder buffer = tessellator.getBuffer();
renderer.finish(); begin( buffer );
drawString( buffer, x, y, text, textColour, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize );
tessellator.draw();
} }
public static void drawTerminalWithoutCursor( public static void drawTerminalWithoutCursor(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y, @Nonnull BufferBuilder buffer, float x, float y,
@Nonnull Terminal terminal, boolean greyscale, @Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
) )
@@ -195,13 +205,13 @@ public final class FixedWidthFontRenderer
// Top and bottom margins // Top and bottom margins
drawBackground( drawBackground(
transform, buffer, x, y - topMarginSize, buffer, x, y - topMarginSize,
terminal.getBackgroundColourLine( 0 ), palette, greyscale, terminal.getBackgroundColourLine( 0 ), palette, greyscale,
leftMarginSize, rightMarginSize, topMarginSize leftMarginSize, rightMarginSize, topMarginSize
); );
drawBackground( drawBackground(
transform, buffer, x, y + height * FONT_HEIGHT, buffer, x, y + height * FONT_HEIGHT,
terminal.getBackgroundColourLine( height - 1 ), palette, greyscale, terminal.getBackgroundColourLine( height - 1 ), palette, greyscale,
leftMarginSize, rightMarginSize, bottomMarginSize leftMarginSize, rightMarginSize, bottomMarginSize
); );
@@ -210,7 +220,7 @@ public final class FixedWidthFontRenderer
for( int i = 0; i < height; i++ ) for( int i = 0; i < height; i++ )
{ {
drawString( drawString(
transform, buffer, x, y + FixedWidthFontRenderer.FONT_HEIGHT * i, buffer, x, y + FixedWidthFontRenderer.FONT_HEIGHT * i,
terminal.getLine( i ), terminal.getTextColourLine( i ), terminal.getBackgroundColourLine( i ), terminal.getLine( i ), terminal.getTextColourLine( i ), terminal.getBackgroundColourLine( i ),
palette, greyscale, leftMarginSize, rightMarginSize palette, greyscale, leftMarginSize, rightMarginSize
); );
@@ -218,7 +228,7 @@ public final class FixedWidthFontRenderer
} }
public static void drawCursor( public static void drawCursor(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y, @Nonnull BufferBuilder buffer, float x, float y,
@Nonnull Terminal terminal, boolean greyscale @Nonnull Terminal terminal, boolean greyscale
) )
{ {
@@ -243,104 +253,75 @@ public final class FixedWidthFontRenderer
b = (float) colour[2]; b = (float) colour[2];
} }
drawChar( transform, buffer, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', r, g, b ); drawChar( buffer, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', r, g, b );
} }
} }
public static void drawTerminal( public static void drawTerminal(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y, @Nonnull BufferBuilder buffer, float x, float y,
@Nonnull Terminal terminal, boolean greyscale, @Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
) )
{ {
drawTerminalWithoutCursor( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize ); drawTerminalWithoutCursor( buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
drawCursor( transform, buffer, x, y, terminal, greyscale ); drawCursor( buffer, x, y, terminal, greyscale );
}
public static void drawTerminal(
@Nonnull Matrix4f transform, float x, float y, @Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
bindFont();
IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().getRenderTypeBuffers().getBufferSource();
IVertexBuilder buffer = renderer.getBuffer( TYPE );
drawTerminal( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
renderer.finish( TYPE );
} }
public static void drawTerminal( public static void drawTerminal(
float x, float y, @Nonnull Terminal terminal, boolean greyscale, float x, float y, @Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
) )
{
drawTerminal( IDENTITY, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
}
public static void drawEmptyTerminal( @Nonnull Matrix4f transform, @Nonnull IRenderTypeBuffer renderer, float x, float y, float width, float height )
{
Colour colour = Colour.BLACK;
drawQuad( transform, renderer.getBuffer( TYPE ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
}
public static void drawEmptyTerminal( @Nonnull Matrix4f transform, float x, float y, float width, float height )
{ {
bindFont(); bindFont();
IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().getRenderTypeBuffers().getBufferSource(); Tessellator tessellator = Tessellator.getInstance();
drawEmptyTerminal( transform, renderer, x, y, width, height ); BufferBuilder buffer = tessellator.getBuffer();
renderer.finish(); begin( buffer );
drawTerminal( buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
tessellator.draw();
} }
public static void drawEmptyTerminal( float x, float y, float width, float height ) public static void drawEmptyTerminal( float x, float y, float width, float height )
{ {
drawEmptyTerminal( IDENTITY, x, y, width, height ); bindFont();
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
begin( buffer );
Colour colour = Colour.Black;
drawQuad( buffer, x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
tessellator.draw();
} }
public static void drawBlocker( @Nonnull Matrix4f transform, @Nonnull IRenderTypeBuffer renderer, float x, float y, float width, float height ) public static void drawBlocker( @Nonnull BufferBuilder buffer, float x, float y, float width, float height )
{ {
Colour colour = Colour.BLACK; Colour colour = Colour.Black;
drawQuad( transform, renderer.getBuffer( Type.BLOCKER ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() ); drawQuad( buffer, x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
} }
private static void bindFont() public static void drawBlocker( float x, float y, float width, float height )
{
bindFont();
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
begin( buffer );
drawBlocker( buffer, x, y, width, height );
tessellator.draw();
}
public static void bindFont()
{ {
Minecraft.getInstance().getTextureManager().bindTexture( FONT ); Minecraft.getInstance().getTextureManager().bindTexture( FONT );
RenderSystem.texParameter( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP ); GlStateManager.texParameter( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP );
GlStateManager.enableTexture();
} }
private static final class Type extends RenderState public static void begin( BufferBuilder buffer )
{ {
private static final int GL_MODE = GL11.GL_TRIANGLES; buffer.begin( GL11.GL_TRIANGLES, POSITION_COLOR_TEX );
private static final VertexFormat FORMAT = DefaultVertexFormats.POSITION_COLOR_TEX;
static final RenderType MAIN = RenderType.makeType(
"terminal_font", FORMAT, GL_MODE, 1024,
false, false, // useDelegate, needsSorting
RenderType.State.getBuilder()
.texture( new RenderState.TextureState( FONT, false, false ) ) // blur, minimap
.alpha( DEFAULT_ALPHA )
.lightmap( LIGHTMAP_DISABLED )
.writeMask( COLOR_WRITE )
.build( false )
);
static final RenderType BLOCKER = RenderType.makeType(
"terminal_blocker", FORMAT, GL_MODE, 256,
false, false, // useDelegate, needsSorting
RenderType.State.getBuilder()
.texture( new RenderState.TextureState( FONT, false, false ) ) // blur, minimap
.alpha( DEFAULT_ALPHA )
.writeMask( DEPTH_WRITE )
.lightmap( LIGHTMAP_DISABLED )
.build( false )
);
private Type( String name, Runnable setup, Runnable destroy )
{
super( name, setup, destroy );
}
} }
} }

View File

@@ -5,7 +5,7 @@
*/ */
package dan200.computercraft.client.gui; package dan200.computercraft.client.gui;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.platform.GlStateManager;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.WidgetTerminal; import dan200.computercraft.client.gui.widgets.WidgetTerminal;
import dan200.computercraft.client.gui.widgets.WidgetWrapper; import dan200.computercraft.client.gui.widgets.WidgetWrapper;
@@ -134,17 +134,17 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Containe
terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() ); terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() );
// Draw a border around the terminal // Draw a border around the terminal
RenderSystem.color4f( 1.0f, 1.0f, 1.0f, 1.0f ); GlStateManager.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
switch( m_family ) switch( m_family )
{ {
case NORMAL: case Normal:
default: default:
minecraft.getTextureManager().bindTexture( BACKGROUND_NORMAL ); minecraft.getTextureManager().bindTexture( BACKGROUND_NORMAL );
break; break;
case ADVANCED: case Advanced:
minecraft.getTextureManager().bindTexture( BACKGROUND_ADVANCED ); minecraft.getTextureManager().bindTexture( BACKGROUND_ADVANCED );
break; break;
case COMMAND: case Command:
minecraft.getTextureManager().bindTexture( BACKGROUND_COMMAND ); minecraft.getTextureManager().bindTexture( BACKGROUND_COMMAND );
break; break;
} }

View File

@@ -5,7 +5,7 @@
*/ */
package dan200.computercraft.client.gui; package dan200.computercraft.client.gui;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.platform.GlStateManager;
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive; import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
import net.minecraft.client.gui.screen.inventory.ContainerScreen; import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
@@ -32,7 +32,7 @@ public class GuiDiskDrive extends ContainerScreen<ContainerDiskDrive>
@Override @Override
protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY ) protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
{ {
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F ); GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
minecraft.getTextureManager().bindTexture( BACKGROUND ); minecraft.getTextureManager().bindTexture( BACKGROUND );
blit( guiLeft, guiTop, 0, 0, xSize, ySize ); blit( guiLeft, guiTop, 0, 0, xSize, ySize );
} }

View File

@@ -5,7 +5,7 @@
*/ */
package dan200.computercraft.client.gui; package dan200.computercraft.client.gui;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.platform.GlStateManager;
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter; import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
import net.minecraft.client.gui.screen.inventory.ContainerScreen; import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
@@ -33,7 +33,7 @@ public class GuiPrinter extends ContainerScreen<ContainerPrinter>
@Override @Override
protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY ) protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
{ {
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F ); GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
minecraft.getTextureManager().bindTexture( BACKGROUND ); minecraft.getTextureManager().bindTexture( BACKGROUND );
blit( guiLeft, guiTop, 0, 0, xSize, ySize ); blit( guiLeft, guiTop, 0, 0, xSize, ySize );

View File

@@ -5,15 +5,11 @@
*/ */
package dan200.computercraft.client.gui; package dan200.computercraft.client.gui;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.platform.GlStateManager;
import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.common.ContainerHeldItem; import dan200.computercraft.shared.common.ContainerHeldItem;
import dan200.computercraft.shared.media.items.ItemPrintout; import dan200.computercraft.shared.media.items.ItemPrintout;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.inventory.ContainerScreen; 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.entity.player.PlayerInventory;
import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.ITextComponent;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
@@ -22,8 +18,6 @@ import static dan200.computercraft.client.render.PrintoutRenderer.*;
public class GuiPrintout extends ContainerScreen<ContainerHeldItem> public class GuiPrintout extends ContainerScreen<ContainerHeldItem>
{ {
private static final Matrix4f IDENTITY = TransformationMatrix.identity().getMatrix();
private final boolean m_book; private final boolean m_book;
private final int m_pages; private final int m_pages;
private final TextBuffer[] m_text; private final TextBuffer[] m_text;
@@ -94,22 +88,20 @@ public class GuiPrintout extends ContainerScreen<ContainerHeldItem>
public void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY ) public void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
{ {
// Draw the printout // Draw the printout
RenderSystem.color4f( 1.0f, 1.0f, 1.0f, 1.0f ); GlStateManager.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
RenderSystem.enableDepthTest(); GlStateManager.enableDepthTest();
IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().getRenderTypeBuffers().getBufferSource(); drawBorder( guiLeft, guiTop, blitOffset, m_page, m_pages, m_book );
drawBorder( IDENTITY, renderer, guiLeft, guiTop, getBlitOffset(), m_page, m_pages, m_book ); drawText( guiLeft + X_TEXT_MARGIN, guiTop + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * m_page, m_text, m_colours );
drawText( IDENTITY, renderer, guiLeft + X_TEXT_MARGIN, guiTop + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * m_page, m_text, m_colours );
renderer.finish();
} }
@Override @Override
public void render( int mouseX, int mouseY, float partialTicks ) public void render( int mouseX, int mouseY, float partialTicks )
{ {
// We must take the background further back in order to not overlap with our printed pages. // We must take the background further back in order to not overlap with our printed pages.
setBlitOffset( getBlitOffset() - 1 ); blitOffset--;
renderBackground(); renderBackground();
setBlitOffset( getBlitOffset() + 1 ); blitOffset++;
super.render( mouseX, mouseY, partialTicks ); super.render( mouseX, mouseY, partialTicks );
renderHoveredToolTip( mouseX, mouseY ); renderHoveredToolTip( mouseX, mouseY );

View File

@@ -5,7 +5,7 @@
*/ */
package dan200.computercraft.client.gui; package dan200.computercraft.client.gui;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.platform.GlStateManager;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.WidgetTerminal; import dan200.computercraft.client.gui.widgets.WidgetTerminal;
import dan200.computercraft.client.gui.widgets.WidgetWrapper; import dan200.computercraft.client.gui.widgets.WidgetWrapper;
@@ -98,7 +98,7 @@ public class GuiTurtle extends ContainerScreen<ContainerTurtle>
int slot = m_container.getSelectedSlot(); int slot = m_container.getSelectedSlot();
if( slot >= 0 ) if( slot >= 0 )
{ {
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F ); GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
int slotX = slot % 4; int slotX = slot % 4;
int slotY = slot / 4; int slotY = slot / 4;
minecraft.getTextureManager().bindTexture( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL ); minecraft.getTextureManager().bindTexture( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL );
@@ -110,11 +110,11 @@ public class GuiTurtle extends ContainerScreen<ContainerTurtle>
protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY ) protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
{ {
// Draw term // Draw term
boolean advanced = m_family == ComputerFamily.ADVANCED; boolean advanced = m_family == ComputerFamily.Advanced;
terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() ); terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() );
// Draw border/inventory // Draw border/inventory
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F ); GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
minecraft.getTextureManager().bindTexture( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL ); minecraft.getTextureManager().bindTexture( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL );
blit( guiLeft, guiTop, 0, 0, xSize, ySize ); blit( guiLeft, guiTop, 0, 0, xSize, ySize );

View File

@@ -17,9 +17,6 @@ import org.lwjgl.glfw.GLFW;
import java.util.BitSet; import java.util.BitSet;
import java.util.function.Supplier; import java.util.function.Supplier;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
public class WidgetTerminal implements IGuiEventListener public class WidgetTerminal implements IGuiEventListener
{ {
private static final float TERMINATE_TIME = 0.5f; private static final float TERMINATE_TIME = 0.5f;
@@ -176,8 +173,8 @@ public class WidgetTerminal implements IGuiEventListener
Terminal term = computer.getTerminal(); Terminal term = computer.getTerminal();
if( term != null ) if( term != null )
{ {
int charX = (int) (mouseX / FONT_WIDTH); int charX = (int) (mouseX / FixedWidthFontRenderer.FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT); int charY = (int) (mouseY / FixedWidthFontRenderer.FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 ); charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 ); charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
@@ -200,8 +197,8 @@ public class WidgetTerminal implements IGuiEventListener
Terminal term = computer.getTerminal(); Terminal term = computer.getTerminal();
if( term != null ) if( term != null )
{ {
int charX = (int) (mouseX / FONT_WIDTH); int charX = (int) (mouseX / FixedWidthFontRenderer.FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT); int charY = (int) (mouseY / FixedWidthFontRenderer.FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 ); charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 ); charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
@@ -227,8 +224,8 @@ public class WidgetTerminal implements IGuiEventListener
Terminal term = computer.getTerminal(); Terminal term = computer.getTerminal();
if( term != null ) if( term != null )
{ {
int charX = (int) (mouseX / FONT_WIDTH); int charX = (int) (mouseX / FixedWidthFontRenderer.FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT); int charY = (int) (mouseY / FixedWidthFontRenderer.FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 ); charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 ); charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
@@ -252,8 +249,8 @@ public class WidgetTerminal implements IGuiEventListener
Terminal term = computer.getTerminal(); Terminal term = computer.getTerminal();
if( term != null ) if( term != null )
{ {
int charX = (int) (mouseX / FONT_WIDTH); int charX = (int) (mouseX / FixedWidthFontRenderer.FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT); int charY = (int) (mouseY / FixedWidthFontRenderer.FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 ); charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 ); charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
@@ -321,15 +318,19 @@ public class WidgetTerminal implements IGuiEventListener
Terminal terminal = computer != null ? computer.getTerminal() : null; Terminal terminal = computer != null ? computer.getTerminal() : null;
if( terminal != null ) if( terminal != null )
{ {
FixedWidthFontRenderer.drawTerminal( originX, originY, terminal, !computer.isColour(), topMargin, bottomMargin, leftMargin, rightMargin ); FixedWidthFontRenderer.drawTerminal(
originX, originY,
terminal, !computer.isColour(), topMargin, bottomMargin, leftMargin, rightMargin
);
} }
else else
{ {
FixedWidthFontRenderer.drawEmptyTerminal( int x = originX - leftMargin;
originX - leftMargin, originY - rightMargin, int y = originY - rightMargin;
termWidth * FONT_WIDTH + leftMargin + rightMargin, int width = termWidth * FixedWidthFontRenderer.FONT_WIDTH + leftMargin + rightMargin;
termHeight * FONT_HEIGHT + topMargin + bottomMargin int height = termHeight * FixedWidthFontRenderer.FONT_HEIGHT + topMargin + bottomMargin;
);
FixedWidthFontRenderer.drawEmptyTerminal( x, y, width, height );
} }
} }
} }

View File

@@ -7,6 +7,7 @@ package dan200.computercraft.client.proxy;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.*; import dan200.computercraft.client.gui.*;
import dan200.computercraft.client.render.TileEntityCableRenderer;
import dan200.computercraft.client.render.TileEntityMonitorRenderer; import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.client.render.TileEntityTurtleRenderer; import dan200.computercraft.client.render.TileEntityTurtleRenderer;
import dan200.computercraft.client.render.TurtlePlayerRenderer; import dan200.computercraft.client.render.TurtlePlayerRenderer;
@@ -14,6 +15,7 @@ import dan200.computercraft.shared.common.ContainerHeldItem;
import dan200.computercraft.shared.computer.inventory.ContainerComputer; import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer; import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive; import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
import dan200.computercraft.shared.peripheral.modem.wired.TileCable;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor; import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor; import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter; import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
@@ -22,8 +24,6 @@ import dan200.computercraft.shared.turtle.blocks.TileTurtle;
import dan200.computercraft.shared.turtle.core.TurtlePlayer; import dan200.computercraft.shared.turtle.core.TurtlePlayer;
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle; import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
import net.minecraft.client.gui.ScreenManager; 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.api.distmarker.Dist;
import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
@@ -40,22 +40,12 @@ public final class ComputerCraftProxyClient
{ {
registerContainers(); registerContainers();
// While turtles themselves are not transparent, their upgrades may be.
RenderTypeLookup.setRenderLayer( ComputerCraft.Blocks.turtleNormal, RenderType.getTranslucent() );
RenderTypeLookup.setRenderLayer( ComputerCraft.Blocks.turtleAdvanced, RenderType.getTranslucent() );
// Monitors' textures have transparent fronts and so count as cutouts.
RenderTypeLookup.setRenderLayer( ComputerCraft.Blocks.monitorNormal, RenderType.getCutout() );
RenderTypeLookup.setRenderLayer( ComputerCraft.Blocks.monitorAdvanced, RenderType.getCutout() );
// Setup TESRs // Setup TESRs
ClientRegistry.bindTileEntityRenderer( TileMonitor.FACTORY_NORMAL, TileEntityMonitorRenderer::new ); ClientRegistry.bindTileEntitySpecialRenderer( TileMonitor.class, new TileEntityMonitorRenderer() );
ClientRegistry.bindTileEntityRenderer( TileMonitor.FACTORY_ADVANCED, TileEntityMonitorRenderer::new ); ClientRegistry.bindTileEntitySpecialRenderer( TileCable.class, new TileEntityCableRenderer() );
ClientRegistry.bindTileEntityRenderer( TileTurtle.FACTORY_NORMAL, TileEntityTurtleRenderer::new ); ClientRegistry.bindTileEntitySpecialRenderer( TileTurtle.class, new TileEntityTurtleRenderer() );
ClientRegistry.bindTileEntityRenderer( TileTurtle.FACTORY_ADVANCED, TileEntityTurtleRenderer::new );
// TODO: ClientRegistry.bindTileEntityRenderer( TileCable.FACTORY, x -> new TileEntityCableRenderer() );
RenderingRegistry.registerEntityRenderingHandler( TurtlePlayer.TYPE, TurtlePlayerRenderer::new ); RenderingRegistry.registerEntityRenderingHandler( TurtlePlayer.class, TurtlePlayerRenderer::new );
} }
private static void registerContainers() private static void registerContainers()

View File

@@ -5,27 +5,26 @@
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.peripheral.modem.wired.BlockCable; import dan200.computercraft.shared.peripheral.modem.wired.BlockCable;
import dan200.computercraft.shared.peripheral.modem.wired.CableShapes; import dan200.computercraft.shared.peripheral.modem.wired.CableShapes;
import dan200.computercraft.shared.util.WorldUtil; import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ActiveRenderInfo; 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.client.renderer.WorldRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.DrawHighlightEvent; import net.minecraftforge.client.event.DrawBlockHighlightEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import org.lwjgl.opengl.GL11;
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT ) @Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public final class CableHighlightRenderer public final class CableHighlightRenderer
@@ -38,12 +37,14 @@ public final class CableHighlightRenderer
* Draw an outline for a specific part of a cable "Multipart". * Draw an outline for a specific part of a cable "Multipart".
* *
* @param event The event to observe * @param event The event to observe
* @see WorldRenderer#drawSelectionBox(MatrixStack, IVertexBuilder, Entity, double, double, double, BlockPos, BlockState) * @see WorldRenderer#drawSelectionBox(ActiveRenderInfo, RayTraceResult, int)
*/ */
@SubscribeEvent @SubscribeEvent
public static void drawHighlight( DrawHighlightEvent.HighlightBlock event ) public static void drawHighlight( DrawBlockHighlightEvent event )
{ {
BlockRayTraceResult hit = event.getTarget(); if( event.getTarget().getType() != RayTraceResult.Type.BLOCK ) return;
BlockRayTraceResult hit = (BlockRayTraceResult) event.getTarget();
BlockPos pos = hit.getPos(); BlockPos pos = hit.getPos();
World world = event.getInfo().getRenderViewEntity().getEntityWorld(); World world = event.getInfo().getRenderViewEntity().getEntityWorld();
ActiveRenderInfo info = event.getInfo(); ActiveRenderInfo info = event.getInfo();
@@ -58,22 +59,31 @@ public final class CableHighlightRenderer
event.setCanceled( true ); event.setCanceled( true );
Minecraft mc = Minecraft.getInstance();
GlStateManager.enableBlend();
GlStateManager.blendFuncSeparate( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO );
GlStateManager.lineWidth( Math.max( 2.5F, mc.mainWindow.getFramebufferWidth() / 1920.0F * 2.5F ) );
GlStateManager.disableTexture();
GlStateManager.depthMask( false );
GlStateManager.matrixMode( GL11.GL_PROJECTION );
GlStateManager.pushMatrix();
GlStateManager.scalef( 1.0F, 1.0F, 0.999F );
VoxelShape shape = WorldUtil.isVecInside( CableShapes.getModemShape( state ), hit.getHitVec().subtract( pos.getX(), pos.getY(), pos.getZ() ) ) VoxelShape shape = WorldUtil.isVecInside( CableShapes.getModemShape( state ), hit.getHitVec().subtract( pos.getX(), pos.getY(), pos.getZ() ) )
? CableShapes.getModemShape( state ) ? CableShapes.getModemShape( state )
: CableShapes.getCableShape( state ); : CableShapes.getCableShape( state );
Vec3d cameraPos = info.getProjectedView(); Vec3d cameraPos = info.getProjectedView();
double xOffset = pos.getX() - cameraPos.getX(); WorldRenderer.drawShape(
double yOffset = pos.getY() - cameraPos.getY(); shape, pos.getX() - cameraPos.getX(), pos.getY() - cameraPos.getY(), pos.getZ() - cameraPos.getZ(),
double zOffset = pos.getZ() - cameraPos.getZ(); 0.0F, 0.0F, 0.0F, 0.4F
);
IVertexBuilder buffer = event.getBuffers().getBuffer( RenderType.getLines() ); GlStateManager.popMatrix();
Matrix4f matrix4f = event.getMatrix().getLast().getMatrix(); GlStateManager.matrixMode( GL11.GL_MODELVIEW );
shape.forEachEdge( ( x1, y1, z1, x2, y2, z2 ) -> { GlStateManager.depthMask( true );
buffer.pos( matrix4f, (float) (x1 + xOffset), (float) (y1 + yOffset), (float) (z1 + zOffset) ) GlStateManager.enableTexture();
.color( 0, 0, 0, 0.4f ).endVertex(); GlStateManager.disableBlend();
buffer.pos( matrix4f, (float) (x2 + xOffset), (float) (y2 + yOffset), (float) (z2 + zOffset) )
.color( 0, 0, 0, 0.4f ).endVertex();
} );
} }
} }

View File

@@ -5,12 +5,9 @@
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.platform.GlStateManager;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.AbstractClientPlayerEntity;
import net.minecraft.client.renderer.FirstPersonRenderer; 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.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
@@ -22,118 +19,99 @@ public abstract class ItemMapLikeRenderer
/** /**
* The main rendering method for the item. * The main rendering method for the item.
* *
* @param transform The matrix transformation stack * @param stack The stack to render
* @param render The buffer to render to * @see FirstPersonRenderer#renderMapFirstPerson(ItemStack)
* @param stack The stack to render
* @see FirstPersonRenderer#renderItemInFirstPerson(AbstractClientPlayerEntity, float, float, Hand, float, ItemStack, float, MatrixStack, IRenderTypeBuffer, int)
*/ */
protected abstract void renderItem( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack ); protected abstract void renderItem( ItemStack stack );
protected void renderItemFirstPerson( MatrixStack transform, IRenderTypeBuffer render, int lightTexture, Hand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack ) protected void renderItemFirstPerson( Hand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack )
{ {
PlayerEntity player = Minecraft.getInstance().player; PlayerEntity player = Minecraft.getInstance().player;
transform.push(); GlStateManager.pushMatrix();
if( hand == Hand.MAIN_HAND && player.getHeldItemOffhand().isEmpty() ) if( hand == Hand.MAIN_HAND && player.getHeldItemOffhand().isEmpty() )
{ {
renderItemFirstPersonCenter( transform, render, lightTexture, pitch, equipProgress, swingProgress, stack ); renderItemFirstPersonCenter( pitch, equipProgress, swingProgress, stack );
} }
else else
{ {
renderItemFirstPersonSide( renderItemFirstPersonSide(
transform, render, lightTexture,
hand == Hand.MAIN_HAND ? player.getPrimaryHand() : player.getPrimaryHand().opposite(), hand == Hand.MAIN_HAND ? player.getPrimaryHand() : player.getPrimaryHand().opposite(),
equipProgress, swingProgress, stack equipProgress, swingProgress, stack
); );
} }
transform.pop(); GlStateManager.popMatrix();
} }
/** /**
* Renders the item to one side of the player. * Renders the item to one side of the player.
* *
* @param transform The matrix transformation stack
* @param render The buffer to render to
* @param combinedLight The current light level
* @param side The side to render on * @param side The side to render on
* @param equipProgress The equip progress of this item * @param equipProgress The equip progress of this item
* @param swingProgress The swing progress of this item * @param swingProgress The swing progress of this item
* @param stack The stack to render * @param stack The stack to render
* @see FirstPersonRenderer#renderMapFirstPersonSide(MatrixStack, IRenderTypeBuffer, int, float, HandSide, float, ItemStack) * @see FirstPersonRenderer#renderMapFirstPersonSide(float, HandSide, float, ItemStack)
*/ */
private void renderItemFirstPersonSide( MatrixStack transform, IRenderTypeBuffer render, int combinedLight, HandSide side, float equipProgress, float swingProgress, ItemStack stack ) private void renderItemFirstPersonSide( HandSide side, float equipProgress, float swingProgress, ItemStack stack )
{ {
Minecraft minecraft = Minecraft.getInstance(); Minecraft minecraft = Minecraft.getInstance();
float offset = side == HandSide.RIGHT ? 1f : -1f; float offset = side == HandSide.RIGHT ? 1f : -1f;
transform.translate( offset * 0.125f, -0.125f, 0f ); GlStateManager.translatef( offset * 0.125f, -0.125f, 0f );
// If the player is not invisible then render a single arm // If the player is not invisible then render a single arm
if( !minecraft.player.isInvisible() ) if( !minecraft.player.isInvisible() )
{ {
transform.push(); GlStateManager.pushMatrix();
transform.rotate( Vector3f.ZP.rotationDegrees( offset * 10f ) ); GlStateManager.rotatef( offset * 10f, 0f, 0f, 1f );
minecraft.getFirstPersonRenderer().renderArmFirstPerson( transform, render, combinedLight, equipProgress, swingProgress, side ); minecraft.getFirstPersonRenderer().renderArmFirstPerson( equipProgress, swingProgress, side );
transform.pop(); GlStateManager.popMatrix();
} }
// Setup the appropriate transformations. This is just copied from the // Setup the appropriate transformations. This is just copied from the
// corresponding method in ItemRenderer. // corresponding method in ItemRenderer.
transform.push(); GlStateManager.pushMatrix();
transform.translate( offset * 0.51f, -0.08f + equipProgress * -1.2f, -0.75f ); GlStateManager.translatef( offset * 0.51f, -0.08f + equipProgress * -1.2f, -0.75f );
float f1 = MathHelper.sqrt( swingProgress ); float f1 = MathHelper.sqrt( swingProgress );
float f2 = MathHelper.sin( f1 * (float) Math.PI ); float f2 = MathHelper.sin( f1 * (float) Math.PI );
float f3 = -0.5f * f2; float f3 = -0.5f * f2;
float f4 = 0.4f * MathHelper.sin( f1 * ((float) Math.PI * 2f) ); float f4 = 0.4f * MathHelper.sin( f1 * ((float) Math.PI * 2f) );
float f5 = -0.3f * MathHelper.sin( swingProgress * (float) Math.PI ); float f5 = -0.3f * MathHelper.sin( swingProgress * (float) Math.PI );
transform.translate( offset * f3, f4 - 0.3f * f2, f5 ); GlStateManager.translatef( offset * f3, f4 - 0.3f * f2, f5 );
transform.rotate( Vector3f.XP.rotationDegrees( f2 * -45f ) ); GlStateManager.rotatef( f2 * -45f, 1f, 0f, 0f );
transform.rotate( Vector3f.YP.rotationDegrees( offset * f2 * -30f ) ); GlStateManager.rotatef( offset * f2 * -30f, 0f, 1f, 0f );
renderItem( transform, render, stack ); renderItem( stack );
transform.pop(); GlStateManager.popMatrix();
} }
/** /**
* Render an item in the middle of the screen. * Render an item in the middle of the screen.
* *
* @param transform The matrix transformation stack
* @param render The buffer to render to
* @param combinedLight The current light level
* @param pitch The pitch of the player * @param pitch The pitch of the player
* @param equipProgress The equip progress of this item * @param equipProgress The equip progress of this item
* @param swingProgress The swing progress of this item * @param swingProgress The swing progress of this item
* @param stack The stack to render * @param stack The stack to render
* @see FirstPersonRenderer#renderMapFirstPerson(MatrixStack, IRenderTypeBuffer, int, float, float, float) * @see FirstPersonRenderer#renderMapFirstPerson(float, float, float)
*/ */
private void renderItemFirstPersonCenter( MatrixStack transform, IRenderTypeBuffer render, int combinedLight, float pitch, float equipProgress, float swingProgress, ItemStack stack ) private void renderItemFirstPersonCenter( float pitch, float equipProgress, float swingProgress, ItemStack stack )
{ {
Minecraft minecraft = Minecraft.getInstance(); FirstPersonRenderer renderer = Minecraft.getInstance().getFirstPersonRenderer();
FirstPersonRenderer renderer = minecraft.getFirstPersonRenderer();
// Setup the appropriate transformations. This is just copied from the // Setup the appropriate transformations. This is just copied from the
// corresponding method in ItemRenderer. // corresponding method in ItemRenderer.
float swingRt = MathHelper.sqrt( swingProgress ); float swingRt = MathHelper.sqrt( swingProgress );
float tX = -0.2f * MathHelper.sin( swingProgress * (float) Math.PI ); float tX = -0.2f * MathHelper.sin( swingProgress * (float) Math.PI );
float tZ = -0.4f * MathHelper.sin( swingRt * (float) Math.PI ); float tZ = -0.4f * MathHelper.sin( swingRt * (float) Math.PI );
transform.translate( 0, -tX / 2, tZ ); GlStateManager.translatef( 0f, -tX / 2f, tZ );
float pitchAngle = renderer.getMapAngleFromPitch( pitch ); float pitchAngle = renderer.getMapAngleFromPitch( pitch );
transform.translate( 0, 0.04F + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f ); GlStateManager.translatef( 0f, 0.04f + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f );
transform.rotate( Vector3f.XP.rotationDegrees( pitchAngle * -85.0f ) ); GlStateManager.rotatef( pitchAngle * -85f, 1f, 0f, 0f );
if( !minecraft.player.isInvisible() ) renderer.renderArms();
{
transform.push();
transform.rotate( Vector3f.YP.rotationDegrees( 90.0F ) );
renderer.renderArm( transform, render, combinedLight, HandSide.RIGHT );
renderer.renderArm( transform, render, combinedLight, HandSide.LEFT );
transform.pop();
}
float rX = MathHelper.sin( swingRt * (float) Math.PI ); float rX = MathHelper.sin( swingRt * (float) Math.PI );
transform.rotate( Vector3f.XP.rotationDegrees( rX * 20.0F ) ); GlStateManager.rotatef( rX * 20f, 1f, 0f, 0f );
transform.scale( 2.0F, 2.0F, 2.0F ); GlStateManager.scalef( 2f, 2f, 2f );
renderItem( transform, render, stack ); renderItem( stack );
} }
} }

View File

@@ -5,9 +5,7 @@
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
@@ -16,11 +14,12 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.util.Colour; import dan200.computercraft.shared.util.Colour;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.*; import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RenderHandEvent; import net.minecraftforge.client.event.RenderSpecificHandEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
@@ -46,20 +45,17 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
} }
@SubscribeEvent @SubscribeEvent
public static void onRenderInHand( RenderHandEvent event ) public static void renderItem( RenderSpecificHandEvent event )
{ {
ItemStack stack = event.getItemStack(); ItemStack stack = event.getItemStack();
if( !(stack.getItem() instanceof ItemPocketComputer) ) return; if( !(stack.getItem() instanceof ItemPocketComputer) ) return;
event.setCanceled( true ); event.setCanceled( true );
INSTANCE.renderItemFirstPerson( INSTANCE.renderItemFirstPerson( event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack() );
event.getMatrixStack(), event.getBuffers(), event.getLight(),
event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack()
);
} }
@Override @Override
protected void renderItem( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack ) protected void renderItem( ItemStack stack )
{ {
ClientComputer computer = ItemPocketComputer.createClientComputer( stack ); ClientComputer computer = ItemPocketComputer.createClientComputer( stack );
Terminal terminal = computer == null ? null : computer.getTerminal(); Terminal terminal = computer == null ? null : computer.getTerminal();
@@ -81,45 +77,50 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
// Setup various transformations. Note that these are partially adapted from the corresponding method // Setup various transformations. Note that these are partially adapted from the corresponding method
// in ItemRenderer // in ItemRenderer
transform.push(); GlStateManager.pushMatrix();
transform.rotate( Vector3f.YP.rotationDegrees( 180f ) );
transform.rotate( Vector3f.ZP.rotationDegrees( 180f ) );
transform.scale( 0.5f, 0.5f, 0.5f );
float scale = 0.75f / Math.max( width + FRAME * 2, height + FRAME * 2 + LIGHT_HEIGHT ); GlStateManager.disableLighting();
transform.scale( scale, scale, 0 ); GlStateManager.disableDepthTest();
transform.translate( -0.5 * width, -0.5 * height, 0 );
GlStateManager.rotatef( 180f, 0f, 1f, 0f );
GlStateManager.rotatef( 180f, 0f, 0f, 1f );
GlStateManager.scalef( 0.5f, 0.5f, 0.5f );
double scale = 0.75 / Math.max( width + FRAME * 2, height + FRAME * 2 + LIGHT_HEIGHT );
GlStateManager.scaled( scale, scale, 0 );
GlStateManager.translated( -0.5 * width, -0.5 * height, 0 );
// Render the main frame // Render the main frame
ItemPocketComputer item = (ItemPocketComputer) stack.getItem(); ItemPocketComputer item = (ItemPocketComputer) stack.getItem();
ComputerFamily family = item.getFamily(); ComputerFamily family = item.getFamily();
int frameColour = item.getColour( stack ); int frameColour = item.getColour( stack );
renderFrame( family, frameColour, width, height );
Matrix4f matrix = transform.getLast().getMatrix();
renderFrame( matrix, family, frameColour, width, height );
// Render the light // Render the light
int lightColour = ItemPocketComputer.getLightState( stack ); int lightColour = ItemPocketComputer.getLightState( stack );
if( lightColour == -1 ) lightColour = Colour.BLACK.getHex(); if( lightColour == -1 ) lightColour = Colour.Black.getHex();
renderLight( matrix, lightColour, width, height ); renderLight( lightColour, width, height );
if( computer != null && terminal != null ) if( computer != null && terminal != null )
{ {
FixedWidthFontRenderer.drawTerminal( matrix, MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN ); FixedWidthFontRenderer.drawTerminal( MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN );
} }
else else
{ {
FixedWidthFontRenderer.drawEmptyTerminal( matrix, 0, 0, width, height ); FixedWidthFontRenderer.drawEmptyTerminal( 0, 0, width, height );
} }
transform.pop(); GlStateManager.enableDepthTest();
GlStateManager.enableLighting();
GlStateManager.popMatrix();
} }
private static void renderFrame( Matrix4f transform, ComputerFamily family, int colour, int width, int height ) private static void renderFrame( ComputerFamily family, int colour, int width, int height )
{ {
Minecraft.getInstance().getTextureManager().bindTexture( colour != -1 Minecraft.getInstance().getTextureManager().bindTexture( colour != -1
? BACKGROUND_COLOUR ? BACKGROUND_COLOUR
: family == ComputerFamily.NORMAL ? BACKGROUND_NORMAL : BACKGROUND_ADVANCED : family == ComputerFamily.Normal ? BACKGROUND_NORMAL : BACKGROUND_ADVANCED
); );
float r = ((colour >>> 16) & 0xFF) / 255.0f; float r = ((colour >>> 16) & 0xFF) / 255.0f;
@@ -128,38 +129,38 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
Tessellator tessellator = Tessellator.getInstance(); Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer(); BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR_TEX ); buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR );
// Top left, middle, right // Top left, middle, right
renderTexture( transform, buffer, -FRAME, -FRAME, 12, 28, FRAME, FRAME, r, g, b ); renderTexture( buffer, -FRAME, -FRAME, 12, 28, FRAME, FRAME, r, g, b );
renderTexture( transform, buffer, 0, -FRAME, 0, 0, width, FRAME, r, g, b ); renderTexture( buffer, 0, -FRAME, 0, 0, width, FRAME, r, g, b );
renderTexture( transform, buffer, width, -FRAME, 24, 28, FRAME, FRAME, r, g, b ); renderTexture( buffer, width, -FRAME, 24, 28, FRAME, FRAME, r, g, b );
// Left and bright border // Left and bright border
renderTexture( transform, buffer, -FRAME, 0, 0, 28, FRAME, height, r, g, b ); renderTexture( buffer, -FRAME, 0, 0, 28, FRAME, height, r, g, b );
renderTexture( transform, buffer, width, 0, 36, 28, FRAME, height, r, g, b ); renderTexture( buffer, width, 0, 36, 28, FRAME, height, r, g, b );
// Bottom left, middle, right. We do this in three portions: the top inner corners, an extended region for // Bottom left, middle, right. We do this in three portions: the top inner corners, an extended region for
// lights, and then the bottom outer corners. // lights, and then the bottom outer corners.
renderTexture( transform, buffer, -FRAME, height, 12, 40, FRAME, FRAME / 2, r, g, b ); renderTexture( buffer, -FRAME, height, 12, 40, FRAME, FRAME / 2, r, g, b );
renderTexture( transform, buffer, 0, height, 0, 12, width, FRAME / 2, r, g, b ); renderTexture( buffer, 0, height, 0, 12, width, FRAME / 2, r, g, b );
renderTexture( transform, buffer, width, height, 24, 40, FRAME, FRAME / 2, r, g, b ); renderTexture( buffer, width, height, 24, 40, FRAME, FRAME / 2, r, g, b );
renderTexture( transform, buffer, -FRAME, height + FRAME / 2, 12, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b ); renderTexture( buffer, -FRAME, height + FRAME / 2, 12, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
renderTexture( transform, buffer, 0, height + FRAME / 2, 0, 16, width, LIGHT_HEIGHT, FRAME, 4, r, g, b ); renderTexture( buffer, 0, height + FRAME / 2, 0, 16, width, LIGHT_HEIGHT, FRAME, 4, r, g, b );
renderTexture( transform, buffer, width, height + FRAME / 2, 24, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b ); renderTexture( buffer, width, height + FRAME / 2, 24, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
renderTexture( transform, buffer, -FRAME, height + LIGHT_HEIGHT + FRAME / 2, 12, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b ); renderTexture( buffer, -FRAME, height + LIGHT_HEIGHT + FRAME / 2, 12, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
renderTexture( transform, buffer, 0, height + LIGHT_HEIGHT + FRAME / 2, 0, 12 + FRAME / 2, width, FRAME / 2, r, g, b ); renderTexture( buffer, 0, height + LIGHT_HEIGHT + FRAME / 2, 0, 12 + FRAME / 2, width, FRAME / 2, r, g, b );
renderTexture( transform, buffer, width, height + LIGHT_HEIGHT + FRAME / 2, 24, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b ); renderTexture( buffer, width, height + LIGHT_HEIGHT + FRAME / 2, 24, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
tessellator.draw(); tessellator.draw();
} }
private static void renderLight( Matrix4f transform, int colour, int width, int height ) private static void renderLight( int colour, int width, int height )
{ {
RenderSystem.enableBlend(); GlStateManager.enableBlend();
RenderSystem.disableTexture(); GlStateManager.disableTexture();
float r = ((colour >>> 16) & 0xFF) / 255.0f; float r = ((colour >>> 16) & 0xFF) / 255.0f;
float g = ((colour >>> 8) & 0xFF) / 255.0f; float g = ((colour >>> 8) & 0xFF) / 255.0f;
@@ -168,26 +169,26 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
Tessellator tessellator = Tessellator.getInstance(); Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer(); BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR ); buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR );
buffer.pos( transform, width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + FRAME / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex(); buffer.pos( width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
buffer.pos( transform, width, height + LIGHT_HEIGHT + FRAME / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex(); buffer.pos( width, height + LIGHT_HEIGHT + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
buffer.pos( transform, width, height + FRAME / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex(); buffer.pos( width, height + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
buffer.pos( transform, width - LIGHT_HEIGHT * 2, height + FRAME / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex(); buffer.pos( width - LIGHT_HEIGHT * 2, height + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
tessellator.draw(); tessellator.draw();
RenderSystem.enableTexture(); GlStateManager.enableTexture();
} }
private static void renderTexture( Matrix4f transform, IVertexBuilder builder, int x, int y, int textureX, int textureY, int width, int height, float r, float g, float b ) private static void renderTexture( BufferBuilder builder, int x, int y, int textureX, int textureY, int width, int height, float r, float g, float b )
{ {
renderTexture( transform, builder, x, y, textureX, textureY, width, height, width, height, r, g, b ); renderTexture( builder, x, y, textureX, textureY, width, height, width, height, r, g, b );
} }
private static void renderTexture( Matrix4f transform, IVertexBuilder builder, int x, int y, int textureX, int textureY, int width, int height, int textureWidth, int textureHeight, float r, float g, float b ) private static void renderTexture( BufferBuilder builder, int x, int y, int textureX, int textureY, int width, int height, int textureWidth, int textureHeight, float r, float g, float b )
{ {
float scale = 1 / 255.0f; float scale = 1 / 255.0f;
builder.pos( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).tex( textureX * scale, (textureY + textureHeight) * scale ).endVertex(); builder.pos( x, y + height, 0 ).tex( textureX * scale, (textureY + textureHeight) * scale ).color( r, g, b, 1.0f ).endVertex();
builder.pos( transform, x + width, y + height, 0 ).color( r, g, b, 1.0f ).tex( (textureX + textureWidth) * scale, (textureY + textureHeight) * scale ).endVertex(); builder.pos( x + width, y + height, 0 ).tex( (textureX + textureWidth) * scale, (textureY + textureHeight) * scale ).color( r, g, b, 1.0f ).endVertex();
builder.pos( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).tex( (textureX + textureWidth) * scale, textureY * scale ).endVertex(); builder.pos( x + width, y, 0 ).tex( (textureX + textureWidth) * scale, textureY * scale ).color( r, g, b, 1.0f ).endVertex();
builder.pos( transform, x, y, 0 ).color( r, g, b, 1.0f ).tex( textureX * scale, textureY * scale ).endVertex(); builder.pos( x, y, 0 ).tex( textureX * scale, textureY * scale ).color( r, g, b, 1.0f ).endVertex();
} }
} }

View File

@@ -5,16 +5,13 @@
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.platform.GlStateManager;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.media.items.ItemPrintout; 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.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.client.event.RenderItemInFrameEvent; import net.minecraftforge.client.event.RenderItemInFrameEvent;
import net.minecraftforge.client.event.RenderSpecificHandEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
@@ -37,26 +34,30 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer
} }
@SubscribeEvent @SubscribeEvent
public static void onRenderInHand( RenderHandEvent event ) public static void onRenderInHand( RenderSpecificHandEvent event )
{ {
ItemStack stack = event.getItemStack(); ItemStack stack = event.getItemStack();
if( !(stack.getItem() instanceof ItemPrintout) ) return; if( !(stack.getItem() instanceof ItemPrintout) ) return;
event.setCanceled( true ); event.setCanceled( true );
INSTANCE.renderItemFirstPerson( INSTANCE.renderItemFirstPerson( event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack() );
event.getMatrixStack(), event.getBuffers(), event.getLight(),
event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack()
);
} }
@Override @Override
protected void renderItem( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack ) protected void renderItem( ItemStack stack )
{ {
transform.rotate( Vector3f.XP.rotationDegrees( 180f ) ); // Setup various transformations. Note that these are partially adapated from the corresponding method
transform.scale( 0.42f, 0.42f, -0.42f ); // in FirstPersonRenderer.renderFirstPersonMap
transform.translate( -0.5f, -0.48f, 0.0f ); GlStateManager.disableLighting();
drawPrintout( transform, render, stack ); GlStateManager.rotatef( 180f, 0f, 1f, 0f );
GlStateManager.rotatef( 180f, 0f, 0f, 1f );
GlStateManager.scalef( 0.42f, 0.42f, -0.42f );
GlStateManager.translatef( -0.5f, -0.48f, 0.0f );
drawPrintout( stack );
GlStateManager.enableLighting();
} }
@SubscribeEvent @SubscribeEvent
@@ -64,20 +65,24 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer
{ {
ItemStack stack = event.getItem(); ItemStack stack = event.getItem();
if( !(stack.getItem() instanceof ItemPrintout) ) return; if( !(stack.getItem() instanceof ItemPrintout) ) return;
event.setCanceled( true ); event.setCanceled( true );
MatrixStack transform = event.getMatrix(); GlStateManager.disableLighting();
// Move a little bit forward to ensure we're not clipping with the frame // Move a little bit forward to ensure we're not clipping with the frame
transform.translate( 0.0f, 0.0f, -0.001f ); GlStateManager.translatef( 0.0f, 0.0f, -0.001f );
transform.rotate( Vector3f.ZP.rotationDegrees( 180f ) ); GlStateManager.rotatef( 180f, 0f, 0f, 1f );
transform.scale( 0.95f, 0.95f, -0.95f ); GlStateManager.scalef( 0.95f, 0.95f, -0.95f );
transform.translate( -0.5f, -0.5f, 0.0f ); GlStateManager.translatef( -0.5f, -0.5f, 0.0f );
drawPrintout( transform, event.getBuffers(), stack ); drawPrintout( stack );
GlStateManager.enableLighting();
GlStateManager.disableBlend();
} }
private static void drawPrintout( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack ) private static void drawPrintout( ItemStack stack )
{ {
int pages = ItemPrintout.getPageCount( stack ); int pages = ItemPrintout.getPageCount( stack );
boolean book = ((ItemPrintout) stack.getItem()).getType() == ItemPrintout.Type.BOOK; boolean book = ((ItemPrintout) stack.getItem()).getType() == ItemPrintout.Type.BOOK;
@@ -100,14 +105,11 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer
double max = Math.max( visualHeight, visualWidth ); double max = Math.max( visualHeight, visualWidth );
// Scale the printout to fit correctly. // Scale the printout to fit correctly.
float scale = (float) (1.0 / max); double scale = 1.0 / max;
transform.scale( scale, scale, scale ); GlStateManager.scaled( scale, scale, scale );
transform.translate( (max - width) / 2.0, (max - height) / 2.0, 0.0 ); GlStateManager.translated( (max - width) / 2.0, (max - height) / 2.0, 0.0 );
Matrix4f matrix = transform.getLast().getMatrix(); drawBorder( 0, 0, -0.01, 0, pages, book );
drawBorder( matrix, render, 0, 0, -0.01f, 0, pages, book ); drawText( X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, ItemPrintout.getText( stack ), ItemPrintout.getColours( stack ) );
drawText( matrix, render,
X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, ItemPrintout.getText( stack ), ItemPrintout.getColours( stack )
);
} }
} }

View File

@@ -0,0 +1,269 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.Direction;
import net.minecraftforge.client.model.pipeline.IVertexConsumer;
import net.minecraftforge.client.model.pipeline.LightUtil;
import net.minecraftforge.client.model.pipeline.VertexTransformer;
import net.minecraftforge.common.model.TRSRTransformation;
import javax.annotation.Nonnull;
import javax.vecmath.Matrix4f;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;
import java.util.List;
/**
* Transforms vertices of a model, remaining aware of winding order, and rearranging
* vertices if needed.
*/
public final class ModelTransformer
{
private static final Matrix4f identity;
static
{
identity = new Matrix4f();
identity.setIdentity();
}
private ModelTransformer()
{
}
public static void transformQuadsTo( List<BakedQuad> output, List<BakedQuad> input, Matrix4f transform )
{
if( transform == null || transform.equals( identity ) )
{
output.addAll( input );
}
else
{
Matrix4f normalMatrix = new Matrix4f( transform );
normalMatrix.invert();
normalMatrix.transpose();
for( BakedQuad quad : input ) output.add( doTransformQuad( quad, transform, normalMatrix ) );
}
}
public static BakedQuad transformQuad( BakedQuad input, Matrix4f transform )
{
if( transform == null || transform.equals( identity ) ) return input;
Matrix4f normalMatrix = new Matrix4f( transform );
normalMatrix.invert();
normalMatrix.transpose();
return doTransformQuad( input, transform, normalMatrix );
}
private static BakedQuad doTransformQuad( BakedQuad input, Matrix4f positionMatrix, Matrix4f normalMatrix )
{
BakedQuadBuilder builder = new BakedQuadBuilder( input.getFormat() );
NormalAwareTransformer transformer = new NormalAwareTransformer( builder, positionMatrix, normalMatrix );
input.pipe( transformer );
if( transformer.areNormalsInverted() )
{
builder.swap( 1, 3 );
transformer.areNormalsInverted();
}
return builder.build();
}
/**
* A vertex transformer that tracks whether the normals have been inverted and so the vertices
* should be reordered so backface culling works as expected.
*/
private static class NormalAwareTransformer extends VertexTransformer
{
private final Matrix4f positionMatrix;
private final Matrix4f normalMatrix;
private int vertexIndex = 0, elementIndex = 0;
private final Point3f[] before = new Point3f[4];
private final Point3f[] after = new Point3f[4];
NormalAwareTransformer( IVertexConsumer parent, Matrix4f positionMatrix, Matrix4f normalMatrix )
{
super( parent );
this.positionMatrix = positionMatrix;
this.normalMatrix = normalMatrix;
}
@Override
public void setQuadOrientation( @Nonnull Direction orientation )
{
super.setQuadOrientation( orientation == null ? orientation : TRSRTransformation.rotate( positionMatrix, orientation ) );
}
@Override
public void put( int element, @Nonnull float... data )
{
switch( getVertexFormat().getElement( element ).getUsage() )
{
case POSITION:
{
Point3f vec = new Point3f( data );
Point3f newVec = new Point3f();
positionMatrix.transform( vec, newVec );
float[] newData = new float[4];
newVec.get( newData );
super.put( element, newData );
before[vertexIndex] = vec;
after[vertexIndex] = newVec;
break;
}
case NORMAL:
{
Vector3f vec = new Vector3f( data );
normalMatrix.transform( vec );
float[] newData = new float[4];
vec.get( newData );
super.put( element, newData );
break;
}
default:
super.put( element, data );
break;
}
elementIndex++;
if( elementIndex == getVertexFormat().getElementCount() )
{
vertexIndex++;
elementIndex = 0;
}
}
public boolean areNormalsInverted()
{
Vector3f temp1 = new Vector3f(), temp2 = new Vector3f();
Vector3f crossBefore = new Vector3f(), crossAfter = new Vector3f();
// Determine what cross product we expect to have
temp1.sub( before[1], before[0] );
temp2.sub( before[1], before[2] );
crossBefore.cross( temp1, temp2 );
normalMatrix.transform( crossBefore );
// And determine what cross product we actually have
temp1.sub( after[1], after[0] );
temp2.sub( after[1], after[2] );
crossAfter.cross( temp1, temp2 );
// If the angle between expected and actual cross product is greater than
// pi/2 radians then we will need to reorder our quads.
return Math.abs( crossBefore.angle( crossAfter ) ) >= Math.PI / 2;
}
}
/**
* A vertex consumer which is capable of building {@link BakedQuad}s.
*
* Equivalent to {@link net.minecraftforge.client.model.pipeline.UnpackedBakedQuad.Builder} but more memory
* efficient.
*
* This also provides the ability to swap vertices through {@link #swap(int, int)} to allow reordering.
*/
private static final class BakedQuadBuilder implements IVertexConsumer
{
private final VertexFormat format;
private final int[] vertexData;
private int vertexIndex = 0, elementIndex = 0;
private Direction orientation;
private int quadTint;
private boolean diffuse;
private TextureAtlasSprite texture;
private BakedQuadBuilder( VertexFormat format )
{
this.format = format;
vertexData = new int[format.getSize()];
}
@Nonnull
@Override
public VertexFormat getVertexFormat()
{
return format;
}
@Override
public void setQuadTint( int tint )
{
quadTint = tint;
}
@Override
public void setQuadOrientation( @Nonnull Direction orientation )
{
this.orientation = orientation;
}
@Override
public void setApplyDiffuseLighting( boolean diffuse )
{
this.diffuse = diffuse;
}
@Override
public void setTexture( @Nonnull TextureAtlasSprite texture )
{
this.texture = texture;
}
@Override
public void put( int element, @Nonnull float... data )
{
LightUtil.pack( data, vertexData, format, vertexIndex, element );
elementIndex++;
if( elementIndex == getVertexFormat().getElementCount() )
{
vertexIndex++;
elementIndex = 0;
}
}
public void swap( int a, int b )
{
int length = vertexData.length / 4;
for( int i = 0; i < length; i++ )
{
int temp = vertexData[a * length + i];
vertexData[a * length + i] = vertexData[b * length + i];
vertexData[b * length + i] = temp;
}
}
public BakedQuad build()
{
if( elementIndex != 0 || vertexIndex != 4 )
{
throw new IllegalStateException( "Got an unexpected number of elements/vertices" );
}
if( texture == null )
{
throw new IllegalStateException( "Texture has not been set" );
}
return new BakedQuad( vertexData, quadTint, orientation, texture, diffuse, format );
}
}
}

View File

@@ -5,45 +5,49 @@
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor; import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import net.minecraft.client.renderer.Matrix4f; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.DrawHighlightEvent; import net.minecraftforge.client.event.DrawBlockHighlightEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import org.lwjgl.opengl.GL11;
import java.util.EnumSet; import java.util.EnumSet;
import static net.minecraft.util.Direction.*; import static net.minecraft.util.Direction.*;
/**
* Overrides monitor highlighting to only render the outline of the <em>whole</em> monitor, rather than the current
* block. This means you do not get an intrusive outline on top of the screen.
*/
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT ) @Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public final class MonitorHighlightRenderer public final class MonitorHighlightRenderer
{ {
private static final float EXPAND = 0.002f;
private MonitorHighlightRenderer() private MonitorHighlightRenderer()
{ {
} }
@SubscribeEvent @SubscribeEvent
public static void drawHighlight( DrawHighlightEvent.HighlightBlock event ) public static void drawHighlight( DrawBlockHighlightEvent event )
{ {
// Preserve normal behaviour when crouching. if( event.getTarget().getType() != RayTraceResult.Type.BLOCK || event.getInfo().getRenderViewEntity().isSneaking() )
if( event.getInfo().getRenderViewEntity().isCrouching() ) return; {
return;
}
World world = event.getInfo().getRenderViewEntity().getEntityWorld(); World world = event.getInfo().getRenderViewEntity().getEntityWorld();
BlockPos pos = event.getTarget().getPos(); BlockPos pos = ((BlockRayTraceResult) event.getTarget()).getPos();
TileEntity tile = world.getTileEntity( pos ); TileEntity tile = world.getTileEntity( pos );
if( !(tile instanceof TileMonitor) ) return; if( !(tile instanceof TileMonitor) ) return;
@@ -60,37 +64,53 @@ public final class MonitorHighlightRenderer
if( monitor.getYIndex() != 0 ) faces.remove( monitor.getDown().getOpposite() ); if( monitor.getYIndex() != 0 ) faces.remove( monitor.getDown().getOpposite() );
if( monitor.getYIndex() != monitor.getHeight() - 1 ) faces.remove( monitor.getDown() ); if( monitor.getYIndex() != monitor.getHeight() - 1 ) faces.remove( monitor.getDown() );
MatrixStack transformStack = event.getMatrix(); GlStateManager.enableBlend();
GlStateManager.blendFuncSeparate( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO );
GlStateManager.lineWidth( Math.max( 2.5F, (float) Minecraft.getInstance().mainWindow.getFramebufferWidth() / 1920.0F * 2.5F ) );
GlStateManager.disableTexture();
GlStateManager.depthMask( false );
GlStateManager.pushMatrix();
Vec3d cameraPos = event.getInfo().getProjectedView(); Vec3d cameraPos = event.getInfo().getProjectedView();
transformStack.push(); GlStateManager.translated( pos.getX() - cameraPos.getX(), pos.getY() - cameraPos.getY(), pos.getZ() - cameraPos.getZ() );
transformStack.translate( pos.getX() - cameraPos.getX(), pos.getY() - cameraPos.getY(), pos.getZ() - cameraPos.getZ() );
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR );
// I wish I could think of a better way to do this // I wish I could think of a better way to do this
IVertexBuilder buffer = event.getBuffers().getBuffer( RenderType.getLines() ); if( faces.contains( NORTH ) || faces.contains( WEST ) ) line( buffer, 0, 0, 0, UP );
Matrix4f transform = transformStack.getLast().getMatrix(); if( faces.contains( SOUTH ) || faces.contains( WEST ) ) line( buffer, 0, 0, 1, UP );
if( faces.contains( NORTH ) || faces.contains( WEST ) ) line( buffer, transform, 0, 0, 0, UP ); if( faces.contains( NORTH ) || faces.contains( EAST ) ) line( buffer, 1, 0, 0, UP );
if( faces.contains( SOUTH ) || faces.contains( WEST ) ) line( buffer, transform, 0, 0, 1, UP ); if( faces.contains( SOUTH ) || faces.contains( EAST ) ) line( buffer, 1, 0, 1, UP );
if( faces.contains( NORTH ) || faces.contains( EAST ) ) line( buffer, transform, 1, 0, 0, UP ); if( faces.contains( NORTH ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 0, EAST );
if( faces.contains( SOUTH ) || faces.contains( EAST ) ) line( buffer, transform, 1, 0, 1, UP ); if( faces.contains( SOUTH ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 1, EAST );
if( faces.contains( NORTH ) || faces.contains( DOWN ) ) line( buffer, transform, 0, 0, 0, EAST ); if( faces.contains( NORTH ) || faces.contains( UP ) ) line( buffer, 0, 1, 0, EAST );
if( faces.contains( SOUTH ) || faces.contains( DOWN ) ) line( buffer, transform, 0, 0, 1, EAST ); if( faces.contains( SOUTH ) || faces.contains( UP ) ) line( buffer, 0, 1, 1, EAST );
if( faces.contains( NORTH ) || faces.contains( UP ) ) line( buffer, transform, 0, 1, 0, EAST ); if( faces.contains( WEST ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 0, SOUTH );
if( faces.contains( SOUTH ) || faces.contains( UP ) ) line( buffer, transform, 0, 1, 1, EAST ); if( faces.contains( EAST ) || faces.contains( DOWN ) ) line( buffer, 1, 0, 0, SOUTH );
if( faces.contains( WEST ) || faces.contains( DOWN ) ) line( buffer, transform, 0, 0, 0, SOUTH ); if( faces.contains( WEST ) || faces.contains( UP ) ) line( buffer, 0, 1, 0, SOUTH );
if( faces.contains( EAST ) || faces.contains( DOWN ) ) line( buffer, transform, 1, 0, 0, SOUTH ); if( faces.contains( EAST ) || faces.contains( UP ) ) line( buffer, 1, 1, 0, SOUTH );
if( faces.contains( WEST ) || faces.contains( UP ) ) line( buffer, transform, 0, 1, 0, SOUTH );
if( faces.contains( EAST ) || faces.contains( UP ) ) line( buffer, transform, 1, 1, 0, SOUTH );
transformStack.pop(); tessellator.draw();
GlStateManager.popMatrix();
GlStateManager.depthMask( true );
GlStateManager.enableTexture();
GlStateManager.disableBlend();
} }
private static void line( IVertexBuilder buffer, Matrix4f transform, float x, float y, float z, Direction direction ) private static void line( BufferBuilder buffer, int x, int y, int z, Direction direction )
{ {
buffer.pos( transform, x, y, z ).color( 0, 0, 0, 0.4f ).endVertex(); double minX = x == 0 ? -EXPAND : 1 + EXPAND;
buffer.pos( transform, double minY = y == 0 ? -EXPAND : 1 + EXPAND;
x + direction.getXOffset(), double minZ = z == 0 ? -EXPAND : 1 + EXPAND;
y + direction.getYOffset(),
z + direction.getZOffset() buffer.pos( minX, minY, minZ ).color( 0, 0, 0, 0.4f ).endVertex();
buffer.pos(
minX + direction.getXOffset() * (1 + EXPAND * 2),
minY + direction.getYOffset() * (1 + EXPAND * 2),
minZ + direction.getZOffset() * (1 + EXPAND * 2)
).color( 0, 0, 0, 0.4f ).endVertex(); ).color( 0, 0, 0, 0.4f ).endVertex();
} }
} }

View File

@@ -0,0 +1,156 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render;
import com.google.common.base.Strings;
import com.mojang.blaze3d.platform.GLX;
import com.mojang.blaze3d.platform.TextureUtil;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.shared.util.Palette;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import java.io.IOException;
import java.io.InputStream;
import java.nio.FloatBuffer;
class MonitorTextureBufferShader
{
static final int TEXTURE_INDEX = GL13.GL_TEXTURE3;
private static final FloatBuffer PALETTE_BUFFER = BufferUtils.createFloatBuffer( 16 * 3 );
private static int uniformFont;
private static int uniformWidth;
private static int uniformHeight;
private static int uniformTbo;
private static int uniformPalette;
private static boolean initialised;
private static boolean ok;
private static int program;
static void setupUniform( int width, int height, Palette palette, boolean greyscale )
{
GLX.glUniform1i( uniformWidth, width );
GLX.glUniform1i( uniformHeight, height );
PALETTE_BUFFER.rewind();
for( int i = 0; i < 16; i++ )
{
double[] colour = palette.getColour( i );
if( greyscale )
{
float f = FixedWidthFontRenderer.toGreyscale( colour );
PALETTE_BUFFER.put( f ).put( f ).put( f );
}
else
{
PALETTE_BUFFER.put( (float) colour[0] ).put( (float) colour[1] ).put( (float) colour[2] );
}
}
PALETTE_BUFFER.flip();
GLX.glUniform3( uniformPalette, PALETTE_BUFFER );
}
static boolean use()
{
if( initialised )
{
if( ok ) GLX.glUseProgram( program );
return ok;
}
if( ok = load() )
{
GL20.glUseProgram( program );
GLX.glUniform1i( uniformFont, 0 );
GLX.glUniform1i( uniformTbo, TEXTURE_INDEX - GL13.GL_TEXTURE0 );
}
return ok;
}
private static boolean load()
{
initialised = true;
try
{
int vertexShader = loadShader( GL20.GL_VERTEX_SHADER, "assets/computercraft/shaders/monitor.vert" );
int fragmentShader = loadShader( GL20.GL_FRAGMENT_SHADER, "assets/computercraft/shaders/monitor.frag" );
program = GLX.glCreateProgram();
GLX.glAttachShader( program, vertexShader );
GLX.glAttachShader( program, fragmentShader );
GL20.glBindAttribLocation( program, 0, "v_pos" );
GLX.glLinkProgram( program );
boolean ok = GLX.glGetProgrami( program, GL20.GL_LINK_STATUS ) != 0;
String log = GLX.glGetProgramInfoLog( program, Short.MAX_VALUE ).trim();
if( !Strings.isNullOrEmpty( log ) )
{
ComputerCraft.log.warn( "Problems when linking monitor shader: {}", log );
}
GL20.glDetachShader( program, vertexShader );
GL20.glDetachShader( program, fragmentShader );
GLX.glDeleteShader( vertexShader );
GLX.glDeleteShader( fragmentShader );
if( !ok ) return false;
uniformFont = getUniformLocation( program, "u_font" );
uniformWidth = getUniformLocation( program, "u_width" );
uniformHeight = getUniformLocation( program, "u_height" );
uniformTbo = getUniformLocation( program, "u_tbo" );
uniformPalette = getUniformLocation( program, "u_palette" );
ComputerCraft.log.info( "Loaded monitor shader." );
return true;
}
catch( Exception e )
{
ComputerCraft.log.error( "Cannot load monitor shaders", e );
return false;
}
}
private static int loadShader( int kind, String path ) throws IOException
{
String contents;
try( InputStream stream = TileEntityMonitorRenderer.class.getClassLoader().getResourceAsStream( path ) )
{
if( stream == null ) throw new IllegalArgumentException( "Cannot find " + path );
contents = TextureUtil.readResourceAsString( stream );
}
int shader = GLX.glCreateShader( kind );
GLX.glShaderSource( shader, contents );
GLX.glCompileShader( shader );
boolean ok = GLX.glGetShaderi( shader, GL20.GL_COMPILE_STATUS ) != 0;
String log = GLX.glGetShaderInfoLog( shader, Short.MAX_VALUE ).trim();
if( !Strings.isNullOrEmpty( log ) )
{
ComputerCraft.log.warn( "Problems when loading monitor shader {}: {}", path, log );
}
if( !ok ) throw new IllegalStateException( "Cannot compile shader " + path );
return shader;
}
private static int getUniformLocation( int program, String name )
{
int uniform = GLX.glGetUniformLocation( program, name );
if( uniform == -1 ) throw new IllegalStateException( "Cannot find uniform " + name );
return uniform;
}
}

View File

@@ -5,14 +5,15 @@
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.IVertexBuilder; import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.platform.GlStateManager.DestFactor;
import com.mojang.blaze3d.platform.GlStateManager.SourceFactor;
import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.util.Palette; import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.Matrix4f; import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.RenderState; import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
@@ -23,7 +24,7 @@ import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAG
public final class PrintoutRenderer public final class PrintoutRenderer
{ {
private static final ResourceLocation BG = new ResourceLocation( "computercraft", "textures/gui/printout.png" ); private static final ResourceLocation BG = new ResourceLocation( "computercraft", "textures/gui/printout.png" );
private static final float BG_SIZE = 256.0f; private static final double BG_SIZE = 256.0;
/** /**
* Width of a page. * Width of a page.
@@ -60,24 +61,27 @@ public final class PrintoutRenderer
private PrintoutRenderer() {} private PrintoutRenderer() {}
public static void drawText( Matrix4f transform, IRenderTypeBuffer renderer, int x, int y, int start, TextBuffer[] text, TextBuffer[] colours ) public static void drawText( int x, int y, int start, TextBuffer[] text, TextBuffer[] colours )
{ {
IVertexBuilder buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE );
for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ ) for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ )
{ {
FixedWidthFontRenderer.drawString( transform, buffer, FixedWidthFontRenderer.drawString(
x, y + line * FONT_HEIGHT, text[start + line], colours[start + line], null, Palette.DEFAULT, x, y + line * FONT_HEIGHT, text[start + line], colours[start + line], null, Palette.DEFAULT,
false, 0, 0 false, 0, 0
); );
} }
} }
public static void drawText( Matrix4f transform, IRenderTypeBuffer renderer, int x, int y, int start, String[] text, String[] colours ) public static void drawText( int x, int y, int start, String[] text, String[] colours )
{ {
IVertexBuilder buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE ); GlStateManager.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
GlStateManager.enableBlend();
GlStateManager.enableTexture();
GlStateManager.blendFuncSeparate( SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ZERO );
for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ ) for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ )
{ {
FixedWidthFontRenderer.drawString( transform, buffer, FixedWidthFontRenderer.drawString(
x, y + line * FONT_HEIGHT, x, y + line * FONT_HEIGHT,
new TextBuffer( text[start + line] ), new TextBuffer( colours[start + line] ), new TextBuffer( text[start + line] ), new TextBuffer( colours[start + line] ),
null, Palette.DEFAULT, false, 0, 0 null, Palette.DEFAULT, false, 0, 0
@@ -85,46 +89,55 @@ public final class PrintoutRenderer
} }
} }
public static void drawBorder( Matrix4f transform, IRenderTypeBuffer renderer, float x, float y, float z, int page, int pages, boolean isBook ) public static void drawBorder( double x, double y, double z, int page, int pages, boolean isBook )
{ {
GlStateManager.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
GlStateManager.enableBlend();
GlStateManager.enableTexture();
GlStateManager.blendFuncSeparate( SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ZERO );
Minecraft.getInstance().getTextureManager().bindTexture( BG );
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX );
int leftPages = page; int leftPages = page;
int rightPages = pages - page - 1; int rightPages = pages - page - 1;
IVertexBuilder buffer = renderer.getBuffer( Type.TYPE );
if( isBook ) if( isBook )
{ {
// Border // Border
float offset = offsetAt( pages ); double offset = offsetAt( pages );
float left = x - 4 - offset; final double left = x - 4 - offset;
float right = x + X_SIZE + offset - 4; final double right = x + X_SIZE + offset - 4;
// Left and right border // Left and right border
drawTexture( transform, buffer, left - 4, y - 8, z - 0.02f, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 ); drawTexture( buffer, left - 4, y - 8, z - 0.02, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 );
drawTexture( transform, buffer, right, y - 8, z - 0.02f, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 ); drawTexture( buffer, right, y - 8, z - 0.02, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 );
// Draw centre panel (just stretched texture, sorry). // Draw centre panel (just stretched texture, sorry).
drawTexture( transform, buffer, drawTexture( buffer,
x - offset, y, z - 0.02f, X_SIZE + offset * 2, Y_SIZE, x - offset, y, z - 0.02, X_SIZE + offset * 2, Y_SIZE,
COVER_X + COVER_SIZE / 2.0f, COVER_SIZE, COVER_SIZE, Y_SIZE COVER_X + COVER_SIZE / 2.0f, COVER_SIZE, COVER_SIZE, Y_SIZE
); );
float borderX = left; double borderX = left;
while( borderX < right ) while( borderX < right )
{ {
double thisWidth = Math.min( right - borderX, X_SIZE ); double thisWidth = Math.min( right - borderX, X_SIZE );
drawTexture( transform, buffer, borderX, y - 8, z - 0.02f, 0, COVER_Y, (float) thisWidth, COVER_SIZE ); drawTexture( buffer, borderX, y - 8, z - 0.02, 0, COVER_Y, thisWidth, COVER_SIZE );
drawTexture( transform, buffer, borderX, y + Y_SIZE - 4, z - 0.02f, 0, COVER_Y + COVER_SIZE, (float) thisWidth, COVER_SIZE ); drawTexture( buffer, borderX, y + Y_SIZE - 4, z - 0.02, 0, COVER_Y + COVER_SIZE, thisWidth, COVER_SIZE );
borderX += thisWidth; borderX += thisWidth;
} }
} }
// Left half // Left half
drawTexture( transform, buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2.0f, Y_SIZE ); drawTexture( buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2.0f, Y_SIZE );
for( int n = 0; n <= leftPages; n++ ) for( int n = 0; n <= leftPages; n++ )
{ {
drawTexture( transform, buffer, drawTexture( buffer,
x - offsetAt( n ), y, z - 1e-3f * n, x - offsetAt( n ), y, z - 1e-3 * n,
// Use the left "bold" fold for the outermost page // Use the left "bold" fold for the outermost page
n == leftPages ? 0 : X_FOLD_SIZE, 0, n == leftPages ? 0 : X_FOLD_SIZE, 0,
X_FOLD_SIZE, Y_SIZE X_FOLD_SIZE, Y_SIZE
@@ -132,54 +145,38 @@ public final class PrintoutRenderer
} }
// Right half // Right half
drawTexture( transform, buffer, x + X_SIZE / 2.0f, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2.0f, 0, X_SIZE / 2.0f, Y_SIZE ); drawTexture( buffer, x + X_SIZE / 2.0f, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2.0f, 0, X_SIZE / 2.0f, Y_SIZE );
for( int n = 0; n <= rightPages; n++ ) for( int n = 0; n <= rightPages; n++ )
{ {
drawTexture( transform, buffer, drawTexture( buffer,
x + (X_SIZE - X_FOLD_SIZE) + offsetAt( n ), y, z - 1e-3f * n, x + (X_SIZE - X_FOLD_SIZE) + offsetAt( n ), y, z - 1e-3 * n,
// Two folds, then the main page. Use the right "bold" fold for the outermost page. // Two folds, then the main page. Use the right "bold" fold for the outermost page.
X_FOLD_SIZE * 2 + X_SIZE + (n == rightPages ? X_FOLD_SIZE : 0), 0, X_FOLD_SIZE * 2 + X_SIZE + (n == rightPages ? X_FOLD_SIZE : 0), 0,
X_FOLD_SIZE, Y_SIZE X_FOLD_SIZE, Y_SIZE
); );
} }
tessellator.draw();
} }
private static void drawTexture( Matrix4f matrix, IVertexBuilder buffer, float x, float y, float z, float u, float v, float width, float height ) private static void drawTexture( BufferBuilder buffer, double x, double y, double z, double u, double v, double width, double height )
{ {
buffer.pos( matrix, x, y + height, z ).tex( u / BG_SIZE, (v + height) / BG_SIZE ).endVertex(); buffer.pos( x, y + height, z ).tex( u / BG_SIZE, (v + height) / BG_SIZE ).endVertex();
buffer.pos( matrix, x + width, y + height, z ).tex( (u + width) / BG_SIZE, (v + height) / BG_SIZE ).endVertex(); buffer.pos( x + width, y + height, z ).tex( (u + width) / BG_SIZE, (v + height) / BG_SIZE ).endVertex();
buffer.pos( matrix, x + width, y, z ).tex( (u + width) / BG_SIZE, v / BG_SIZE ).endVertex(); buffer.pos( x + width, y, z ).tex( (u + width) / BG_SIZE, v / BG_SIZE ).endVertex();
buffer.pos( matrix, x, y, z ).tex( u / BG_SIZE, v / BG_SIZE ).endVertex(); buffer.pos( x, y, z ).tex( u / BG_SIZE, v / BG_SIZE ).endVertex();
} }
private static void drawTexture( Matrix4f matrix, IVertexBuilder buffer, float x, float y, float z, float width, float height, float u, float v, float tWidth, float tHeight ) private static void drawTexture( BufferBuilder buffer, double x, double y, double z, double width, double height, double u, double v, double tWidth, double tHeight )
{ {
buffer.pos( matrix, x, y + height, z ).tex( u / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex(); buffer.pos( x, y + height, z ).tex( u / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex();
buffer.pos( matrix, x + width, y + height, z ).tex( (u + tWidth) / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex(); buffer.pos( x + width, y + height, z ).tex( (u + tWidth) / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex();
buffer.pos( matrix, x + width, y, z ).tex( (u + tWidth) / BG_SIZE, v / BG_SIZE ).endVertex(); buffer.pos( x + width, y, z ).tex( (u + tWidth) / BG_SIZE, v / BG_SIZE ).endVertex();
buffer.pos( matrix, x, y, z ).tex( u / BG_SIZE, v / BG_SIZE ).endVertex(); buffer.pos( x, y, z ).tex( u / BG_SIZE, v / BG_SIZE ).endVertex();
} }
public static float offsetAt( int page ) public static double offsetAt( int page )
{ {
return (float) (32 * (1 - Math.pow( 1.2, -page ))); return 32 * (1 - Math.pow( 1.2, -page ));
}
private static final class Type extends RenderState
{
static final RenderType TYPE = RenderType.makeType(
"printout_background", DefaultVertexFormats.POSITION_TEX, GL11.GL_QUADS, 1024,
false, false, // useDelegate, needsSorting
RenderType.State.getBuilder()
.texture( new RenderState.TextureState( BG, false, false ) ) // blur, minimap
.alpha( DEFAULT_ALPHA )
.lightmap( LIGHTMAP_DISABLED )
.build( false )
);
private Type( String name, Runnable setup, Runnable destroy )
{
super( name, setup, destroy );
}
} }
} }

View File

@@ -0,0 +1,140 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render;
import com.mojang.blaze3d.platform.GlStateManager;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.peripheral.modem.wired.BlockCable;
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
import dan200.computercraft.shared.peripheral.modem.wired.CableShapes;
import dan200.computercraft.shared.peripheral.modem.wired.TileCable;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.data.EmptyModelData;
import org.lwjgl.opengl.GL11;
import javax.annotation.Nonnull;
import java.util.Random;
/**
* Render breaking animation only over part of a {@link TileCable}.
*/
public class TileEntityCableRenderer extends TileEntityRenderer<TileCable>
{
private static final ResourceLocation[] DESTROY_STAGES = new ResourceLocation[10];
private static final Random random = new Random();
static
{
for( int i = 0; i < DESTROY_STAGES.length; i++ )
{
DESTROY_STAGES[i] = new ResourceLocation( "block/destroy_stage_" + i );
}
}
@Override
public void render( @Nonnull TileCable te, double x, double y, double z, float partialTicks, int destroyStage )
{
if( destroyStage < 0 ) return;
BlockPos pos = te.getPos();
Minecraft mc = Minecraft.getInstance();
RayTraceResult hit = mc.objectMouseOver;
if( hit == null || hit.getType() != RayTraceResult.Type.BLOCK || !((BlockRayTraceResult) hit).getPos().equals( pos ) )
{
return;
}
World world = te.getWorld();
BlockState state = world.getBlockState( pos );
Block block = state.getBlock();
if( block != ComputerCraft.Blocks.cable ) return;
state = WorldUtil.isVecInside( CableShapes.getModemShape( state ), hit.getHitVec().subtract( pos.getX(), pos.getY(), pos.getZ() ) )
? block.getDefaultState().with( BlockCable.MODEM, state.get( BlockCable.MODEM ) )
: state.with( BlockCable.MODEM, CableModemVariant.None );
IBakedModel model = mc.getBlockRendererDispatcher().getModelForState( state );
preRenderDamagedBlocks();
ForgeHooksClient.setRenderLayer( block.getRenderLayer() );
// See BlockRendererDispatcher#renderBlockDamage
TextureAtlasSprite breakingTexture = mc.getTextureMap().getSprite( DESTROY_STAGES[destroyStage] );
BufferBuilder buffer = Tessellator.getInstance().getBuffer();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.BLOCK );
buffer.setTranslation( x - pos.getX(), y - pos.getY(), z - pos.getZ() );
buffer.noColor();
mc.getBlockRendererDispatcher().getBlockModelRenderer().renderModel(
world,
ForgeHooksClient.getDamageModel( model, breakingTexture, state, world, pos, 0 ),
state, pos, buffer, true, random, state.getPositionRandom( pos ), EmptyModelData.INSTANCE
);
ForgeHooksClient.setRenderLayer( BlockRenderLayer.SOLID );
buffer.setTranslation( 0, 0, 0 );
Tessellator.getInstance().draw();
postRenderDamagedBlocks();
}
/**
* Set up the state for rendering block-breaking progress.
*
* @see WorldRenderer#preRenderDamagedBlocks()
*/
private void preRenderDamagedBlocks()
{
GlStateManager.disableLighting();
GlStateManager.enableBlend();
GlStateManager.blendFuncSeparate( GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.SRC_COLOR, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO );
GlStateManager.enableBlend();
GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 0.5F );
GlStateManager.polygonOffset( -3.0F, -3.0F );
GlStateManager.enablePolygonOffset();
GlStateManager.alphaFunc( 516, 0.1F );
GlStateManager.enableAlphaTest();
GlStateManager.pushMatrix();
}
/**
* Tear down the state for rendering block-breaking progress.
*
* @see WorldRenderer#postRenderDamagedBlocks()
*/
private void postRenderDamagedBlocks()
{
GlStateManager.disableAlphaTest();
GlStateManager.polygonOffset( 0.0F, 0.0F );
GlStateManager.disablePolygonOffset();
GlStateManager.disablePolygonOffset();
GlStateManager.depthMask( true );
GlStateManager.popMatrix();
}
}

View File

@@ -5,41 +5,44 @@
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.platform.GLX;
import com.mojang.blaze3d.vertex.IVertexBuilder; import com.mojang.blaze3d.platform.GlStateManager;
import dan200.computercraft.client.FrameInfo; import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor; import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor; import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.DirectionUtil; import dan200.computercraft.shared.util.DirectionUtil;
import net.minecraft.client.renderer.*; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GLAllocation;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.tileentity.TileEntityRenderer; import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexBuffer; import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL31;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.*;
import static dan200.computercraft.shared.peripheral.monitor.TileMonitor.RENDER_MARGIN;
public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor> public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
{ {
/**
* {@link TileMonitor#RENDER_MARGIN}, but a tiny bit of additional padding to ensure that there is no space between
* the monitor frame and contents.
*/
private static final float MARGIN = (float) (TileMonitor.RENDER_MARGIN * 1.1); private static final float MARGIN = (float) (TileMonitor.RENDER_MARGIN * 1.1);
private static ByteBuffer tboContents;
private static final Matrix4f IDENTITY = TransformationMatrix.identity().getMatrix();
public TileEntityMonitorRenderer( TileEntityRendererDispatcher rendererDispatcher )
{
super( rendererDispatcher );
}
@Override @Override
public void render( @Nonnull TileMonitor monitor, float partialTicks, @Nonnull MatrixStack transform, @Nonnull IRenderTypeBuffer renderer, int lightmapCoord, int overlayLight ) public void render( @Nonnull TileMonitor monitor, double posX, double posY, double posZ, float f, int i )
{ {
// Render from the origin monitor // Render from the origin monitor
ClientMonitor originTerminal = monitor.getClientMonitor(); ClientMonitor originTerminal = monitor.getClientMonitor();
@@ -61,6 +64,9 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
originTerminal.lastRenderPos = monitorPos; originTerminal.lastRenderPos = monitorPos;
BlockPos originPos = origin.getPos(); BlockPos originPos = origin.getPos();
posX += originPos.getX() - monitorPos.getX();
posY += originPos.getY() - monitorPos.getY();
posZ += originPos.getZ() - monitorPos.getZ();
// Determine orientation // Determine orientation
Direction dir = origin.getDirection(); Direction dir = origin.getDirection();
@@ -68,94 +74,183 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
float yaw = dir.getHorizontalAngle(); float yaw = dir.getHorizontalAngle();
float pitch = DirectionUtil.toPitchAngle( front ); float pitch = DirectionUtil.toPitchAngle( front );
// Setup initial transform GlStateManager.pushMatrix();
transform.push();
transform.translate(
originPos.getX() - monitorPos.getX() + 0.5,
originPos.getY() - monitorPos.getY() + 0.5,
originPos.getZ() - monitorPos.getZ() + 0.5
);
transform.rotate( Vector3f.YN.rotationDegrees( yaw ) ); // Setup initial transform
transform.rotate( Vector3f.XP.rotationDegrees( pitch ) ); GlStateManager.translated( posX + 0.5, posY + 0.5, posZ + 0.5 );
transform.translate( GlStateManager.rotatef( -yaw, 0.0f, 1.0f, 0.0f );
-0.5 + TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN, GlStateManager.rotatef( pitch, 1.0f, 0.0f, 0.0f );
origin.getHeight() - 0.5 - (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN) + 0, GlStateManager.translated(
-0.5 + TileMonitor.RENDER_BORDER + RENDER_MARGIN,
origin.getHeight() - 0.5 - (TileMonitor.RENDER_BORDER + RENDER_MARGIN) + 0,
0.5 0.5
); );
double xSize = origin.getWidth() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER); double xSize = origin.getWidth() - 2.0 * (RENDER_MARGIN + TileMonitor.RENDER_BORDER);
double ySize = origin.getHeight() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER); double ySize = origin.getHeight() - 2.0 * (RENDER_MARGIN + TileMonitor.RENDER_BORDER);
// Get renderers
Minecraft mc = Minecraft.getInstance();
// Set up render state for monitors. We disable writing to the depth buffer (we draw a "blocker" later),
// and setup lighting so that we render with a glow.
GlStateManager.depthMask( false );
GLX.glMultiTexCoord2f( GLX.GL_TEXTURE1, 0xFFFF, 0xFFFF );
GlStateManager.disableLighting();
mc.gameRenderer.disableLightmap();
// Draw the contents
Terminal terminal = originTerminal.getTerminal(); Terminal terminal = originTerminal.getTerminal();
if( terminal != null ) if( terminal != null )
{ {
boolean redraw = originTerminal.pollTerminalChanged();
if( originTerminal.buffer == null )
{
originTerminal.createBuffer( MonitorRenderer.VBO );
redraw = true;
}
VertexBuffer vbo = originTerminal.buffer;
// Draw a terminal // Draw a terminal
double xScale = xSize / (terminal.getWidth() * FixedWidthFontRenderer.FONT_WIDTH); double xScale = xSize / (terminal.getWidth() * FONT_WIDTH);
double yScale = ySize / (terminal.getHeight() * FixedWidthFontRenderer.FONT_HEIGHT); double yScale = ySize / (terminal.getHeight() * FONT_HEIGHT);
transform.push();
transform.scale( (float) xScale, (float) -yScale, 1.0f );
float xMargin = (float) (MARGIN / xScale); GlStateManager.pushMatrix();
float yMargin = (float) (MARGIN / yScale); GlStateManager.scaled( (float) xScale, (float) -yScale, 1.0f );
Matrix4f matrix = transform.getLast().getMatrix(); renderTerminal( originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) );
if( redraw ) GlStateManager.popMatrix();
{
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder builder = tessellator.getBuffer();
builder.begin( FixedWidthFontRenderer.TYPE.getDrawMode(), FixedWidthFontRenderer.TYPE.getVertexFormat() );
FixedWidthFontRenderer.drawTerminalWithoutCursor(
IDENTITY, builder, 0, 0,
terminal, !originTerminal.isColour(), yMargin, yMargin, xMargin, xMargin
);
builder.finishDrawing();
vbo.upload( builder );
}
// Sneaky hack here: we get a buffer now in order to flush existing ones and set up the appropriate
// render state. I've no clue how well this'll work in future versions of Minecraft, but it does the trick
// for now.
IVertexBuilder buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE );
FixedWidthFontRenderer.TYPE.setupRenderState();
vbo.bindBuffer();
FixedWidthFontRenderer.TYPE.getVertexFormat().setupBufferState( 0L );
vbo.draw( matrix, FixedWidthFontRenderer.TYPE.getDrawMode() );
VertexBuffer.unbindBuffer();
FixedWidthFontRenderer.TYPE.getVertexFormat().clearBufferState();
// We don't draw the cursor with the VBO, as it's dynamic and so we'll end up refreshing far more than is
// reasonable.
FixedWidthFontRenderer.drawCursor( matrix, buffer, 0, 0, terminal, !originTerminal.isColour() );
transform.pop();
} }
else else
{ {
FixedWidthFontRenderer.drawEmptyTerminal( FixedWidthFontRenderer.drawEmptyTerminal(
transform.getLast().getMatrix(), renderer,
-MARGIN, MARGIN, -MARGIN, MARGIN,
(float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2) (float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2)
); );
} }
// Tear down render state for monitors.
GlStateManager.depthMask( true );
mc.gameRenderer.enableLightmap();
GlStateManager.enableLighting();
// Draw the depth blocker
GlStateManager.colorMask( false, false, false, false );
FixedWidthFontRenderer.drawBlocker( FixedWidthFontRenderer.drawBlocker(
transform.getLast().getMatrix(), renderer,
(float) -TileMonitor.RENDER_MARGIN, (float) TileMonitor.RENDER_MARGIN, (float) -TileMonitor.RENDER_MARGIN, (float) TileMonitor.RENDER_MARGIN,
(float) (xSize + 2 * TileMonitor.RENDER_MARGIN), (float) -(ySize + TileMonitor.RENDER_MARGIN * 2) (float) (xSize + 2 * TileMonitor.RENDER_MARGIN), (float) -(ySize + TileMonitor.RENDER_MARGIN * 2)
); );
GlStateManager.colorMask( true, true, true, true );
transform.pop(); GlStateManager.popMatrix();
}
private static void renderTerminal( ClientMonitor monitor, float xMargin, float yMargin )
{
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
boolean redraw = monitor.pollTerminalChanged();
// Setup the buffers if needed. We get the renderer here, to avoid the (unlikely) race condition between
// creating the buffers and rendering.
MonitorRenderer renderer = MonitorRenderer.current();
if( monitor.createBuffer( renderer ) ) redraw = true;
FixedWidthFontRenderer.bindFont();
switch( renderer )
{
case TBO:
{
if( !MonitorTextureBufferShader.use() ) return;
Terminal terminal = monitor.getTerminal();
int width = terminal.getWidth(), height = terminal.getHeight();
int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT;
if( redraw )
{
int size = width * height * 3;
if( tboContents == null || tboContents.capacity() < size )
{
tboContents = GLAllocation.createDirectByteBuffer( size );
}
ByteBuffer monitorBuffer = tboContents;
monitorBuffer.clear();
for( int y = 0; y < height; y++ )
{
TextBuffer text = terminal.getLine( y ), textColour = terminal.getTextColourLine( y ), background = terminal.getBackgroundColourLine( y );
for( int x = 0; x < width; x++ )
{
monitorBuffer.put( (byte) (text.charAt( x ) & 0xFF) );
monitorBuffer.put( (byte) getColour( textColour.charAt( x ), Colour.White ) );
monitorBuffer.put( (byte) getColour( background.charAt( x ), Colour.Black ) );
}
}
monitorBuffer.flip();
GLX.glBindBuffer( GL31.GL_TEXTURE_BUFFER, monitor.tboBuffer );
GLX.glBufferData( GL31.GL_TEXTURE_BUFFER, monitorBuffer, GL15.GL_STATIC_DRAW );
GLX.glBindBuffer( GL31.GL_TEXTURE_BUFFER, 0 );
}
// Bind TBO texture and set up the uniforms. We've already set up the main font above.
GlStateManager.activeTexture( MonitorTextureBufferShader.TEXTURE_INDEX );
GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, monitor.tboTexture );
GlStateManager.activeTexture( GL13.GL_TEXTURE0 );
MonitorTextureBufferShader.setupUniform( width, height, terminal.getPalette(), !monitor.isColour() );
buffer.begin( GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION );
buffer.pos( -xMargin, -yMargin, 0 ).endVertex();
buffer.pos( -xMargin, pixelHeight + yMargin, 0 ).endVertex();
buffer.pos( pixelWidth + xMargin, -yMargin, 0 ).endVertex();
buffer.pos( pixelWidth + xMargin, pixelHeight + yMargin, 0 ).endVertex();
tessellator.draw();
GLX.glUseProgram( 0 );
break;
}
case VBO:
{
VertexBuffer vbo = monitor.buffer;
if( redraw )
{
renderTerminalTo( monitor, buffer, xMargin, yMargin );
buffer.finishDrawing();
buffer.reset();
vbo.bufferData( buffer.getByteBuffer() );
}
vbo.bindBuffer();
setupBufferFormat();
vbo.drawArrays( GL11.GL_TRIANGLES );
VertexBuffer.unbindBuffer();
break;
}
}
// We don't draw the cursor with a buffer, as it's dynamic and so we'll end up refreshing far more than is
// reasonable.
FixedWidthFontRenderer.begin( buffer );
FixedWidthFontRenderer.drawCursor( buffer, 0, 0, monitor.getTerminal(), !monitor.isColour() );
tessellator.draw();
}
private static void renderTerminalTo( ClientMonitor monitor, BufferBuilder buffer, float xMargin, float yMargin )
{
FixedWidthFontRenderer.begin( buffer );
FixedWidthFontRenderer.drawTerminalWithoutCursor(
buffer, 0, 0,
monitor.getTerminal(), !monitor.isColour(), yMargin, yMargin, xMargin, xMargin
);
}
public static void setupBufferFormat()
{
int stride = FixedWidthFontRenderer.POSITION_COLOR_TEX.getSize();
GlStateManager.vertexPointer( 3, GL11.GL_FLOAT, stride, 0 );
GlStateManager.enableClientState( GL11.GL_VERTEX_ARRAY );
GlStateManager.colorPointer( 4, GL11.GL_UNSIGNED_BYTE, stride, 12 );
GlStateManager.enableClientState( GL11.GL_COLOR_ARRAY );
GlStateManager.texCoordPointer( 2, GL11.GL_FLOAT, stride, 16 );
GlStateManager.enableClientState( GL11.GL_TEXTURE_COORD_ARRAY );
} }
} }

View File

@@ -5,9 +5,7 @@
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
@@ -15,26 +13,31 @@ import dan200.computercraft.shared.turtle.blocks.TileTurtle;
import dan200.computercraft.shared.util.DirectionUtil; import dan200.computercraft.shared.util.DirectionUtil;
import dan200.computercraft.shared.util.Holiday; import dan200.computercraft.shared.util.Holiday;
import dan200.computercraft.shared.util.HolidayUtil; import dan200.computercraft.shared.util.HolidayUtil;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.Atlases; import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.Tessellator;
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.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ModelManager; import net.minecraft.client.renderer.model.ModelManager;
import net.minecraft.client.renderer.model.ModelResourceLocation; import net.minecraft.client.renderer.model.ModelResourceLocation;
import net.minecraft.client.renderer.texture.AtlasTexture;
import net.minecraft.client.renderer.tileentity.TileEntityRenderer; import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.data.EmptyModelData; import net.minecraftforge.client.model.data.EmptyModelData;
import net.minecraftforge.client.model.pipeline.LightUtil;
import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.opengl.GL11;
import javax.annotation.Nonnull; import javax.vecmath.Matrix4f;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
@@ -45,147 +48,189 @@ public class TileEntityTurtleRenderer extends TileEntityRenderer<TileTurtle>
private static final ModelResourceLocation COLOUR_TURTLE_MODEL = new ModelResourceLocation( "computercraft:turtle_colour", "inventory" ); private static final ModelResourceLocation COLOUR_TURTLE_MODEL = new ModelResourceLocation( "computercraft:turtle_colour", "inventory" );
private static final ModelResourceLocation ELF_OVERLAY_MODEL = new ModelResourceLocation( "computercraft:turtle_elf_overlay", "inventory" ); private static final ModelResourceLocation ELF_OVERLAY_MODEL = new ModelResourceLocation( "computercraft:turtle_elf_overlay", "inventory" );
private final Random random = new Random( 0 ); @Override
public void render( TileTurtle tileEntity, double posX, double posY, double posZ, float partialTicks, int breaking )
public TileEntityTurtleRenderer( TileEntityRendererDispatcher renderDispatcher )
{ {
super( renderDispatcher ); if( tileEntity != null ) renderTurtleAt( tileEntity, posX, posY, posZ, partialTicks );
} }
public static ModelResourceLocation getTurtleModel( ComputerFamily family, boolean coloured ) public static ModelResourceLocation getTurtleModel( ComputerFamily family, boolean coloured )
{ {
switch( family ) switch( family )
{ {
case NORMAL: case Normal:
default: default:
return coloured ? COLOUR_TURTLE_MODEL : NORMAL_TURTLE_MODEL; return coloured ? COLOUR_TURTLE_MODEL : NORMAL_TURTLE_MODEL;
case ADVANCED: case Advanced:
return coloured ? COLOUR_TURTLE_MODEL : ADVANCED_TURTLE_MODEL; return coloured ? COLOUR_TURTLE_MODEL : ADVANCED_TURTLE_MODEL;
} }
} }
public static ModelResourceLocation getTurtleOverlayModel( ResourceLocation overlay, boolean christmas ) public static ModelResourceLocation getTurtleOverlayModel( ResourceLocation overlay, boolean christmas )
{ {
if( overlay != null ) return new ModelResourceLocation( overlay, "inventory" ); if( overlay != null )
if( christmas ) return ELF_OVERLAY_MODEL; {
return null; return new ModelResourceLocation( overlay, "inventory" );
}
else if( christmas )
{
return ELF_OVERLAY_MODEL;
}
else
{
return null;
}
} }
@Override private void renderTurtleAt( TileTurtle turtle, double posX, double posY, double posZ, float partialTicks )
public void render( @Nonnull TileTurtle turtle, float partialTicks, @Nonnull MatrixStack transform, @Nonnull IRenderTypeBuffer renderer, int lightmapCoord, int overlayLight )
{ {
// Render the label // Render the label
String label = turtle.createProxy().getLabel(); String label = turtle.createProxy().getLabel();
RayTraceResult hit = renderDispatcher.cameraHitResult; RayTraceResult hit = rendererDispatcher.cameraHitResult;
if( label != null && hit.getType() == RayTraceResult.Type.BLOCK && turtle.getPos().equals( ((BlockRayTraceResult) hit).getPos() ) ) if( label != null && hit.getType() == RayTraceResult.Type.BLOCK && turtle.getPos().equals( ((BlockRayTraceResult) hit).getPos() ) )
{ {
Minecraft mc = Minecraft.getInstance(); setLightmapDisabled( true );
FontRenderer font = renderDispatcher.fontRenderer; GameRenderer.drawNameplate(
getFontRenderer(), label,
transform.push(); (float) posX + 0.5F, (float) posY + 1.2F, (float) posZ + 0.5F, 0,
transform.translate( 0.5, 1.2, 0.5 ); rendererDispatcher.renderInfo.getYaw(), rendererDispatcher.renderInfo.getPitch(), false
transform.rotate( mc.getRenderManager().getCameraOrientation() ); );
transform.scale( -0.025f, -0.025f, 0.025f ); setLightmapDisabled( false );
Matrix4f matrix = transform.getLast().getMatrix();
int opacity = (int) (mc.gameSettings.getTextBackgroundOpacity( 0.25f ) * 255) << 24;
float width = -font.getStringWidth( label ) / 2.0f;
font.renderString( label, width, (float) 0, 0x20ffffff, false, matrix, renderer, true, opacity, lightmapCoord );
font.renderString( label, width, (float) 0, 0xffffffff, false, matrix, renderer, false, 0, lightmapCoord );
transform.pop();
} }
transform.push(); GlStateManager.pushMatrix();
try
// Setup the transform.
Vec3d offset = turtle.getRenderOffset( partialTicks );
float yaw = turtle.getRenderYaw( partialTicks );
transform.translate( offset.x, offset.y, offset.z );
transform.translate( 0.5f, 0.5f, 0.5f );
transform.rotate( Vector3f.YP.rotationDegrees( 180.0f - yaw ) );
if( label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )) )
{ {
// Flip the model BlockState state = turtle.getBlockState();
transform.scale( 1.0f, -1.0f, 1.0f ); // Setup the transform
} Vec3d offset = turtle.getRenderOffset( partialTicks );
transform.translate( -0.5f, -0.5f, -0.5f ); float yaw = turtle.getRenderYaw( partialTicks );
GlStateManager.translated( posX + offset.x, posY + offset.y, posZ + offset.z );
// Render the turtle // Render the turtle
int colour = turtle.getColour(); GlStateManager.translatef( 0.5f, 0.5f, 0.5f );
ComputerFamily family = turtle.getFamily(); GlStateManager.rotatef( 180.0f - yaw, 0.0f, 1.0f, 0.0f );
ResourceLocation overlay = turtle.getOverlay(); if( label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )) )
IVertexBuilder buffer = renderer.getBuffer( Atlases.getTranslucentCullBlockType() );
renderModel( transform, buffer, lightmapCoord, overlayLight, getTurtleModel( family, colour != -1 ), colour == -1 ? null : new int[] { colour } );
// Render the overlay
ModelResourceLocation overlayModel = getTurtleOverlayModel( overlay, HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS );
if( overlayModel != null )
{
renderModel( transform, buffer, lightmapCoord, overlayLight, overlayModel, null );
}
// Render the upgrades
renderUpgrade( transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks );
renderUpgrade( transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks );
transform.pop();
}
private void renderUpgrade( @Nonnull MatrixStack transform, @Nonnull IVertexBuilder renderer, int lightmapCoord, int overlayLight, TileTurtle turtle, TurtleSide side, float f )
{
ITurtleUpgrade upgrade = turtle.getUpgrade( side );
if( upgrade == null ) return;
transform.push();
float toolAngle = turtle.getToolRenderAngle( side, f );
transform.translate( 0.0f, 0.5f, 0.5f );
transform.rotate( Vector3f.XN.rotationDegrees( toolAngle ) );
transform.translate( 0.0f, -0.5f, -0.5f );
TransformedModel model = upgrade.getModel( turtle.getAccess(), side );
model.getMatrix().push( transform );
renderModel( transform, renderer, lightmapCoord, overlayLight, model.getModel(), null );
transform.pop();
transform.pop();
}
private void renderModel( @Nonnull MatrixStack transform, @Nonnull IVertexBuilder renderer, int lightmapCoord, int overlayLight, ModelResourceLocation modelLocation, int[] tints )
{
ModelManager modelManager = Minecraft.getInstance().getItemRenderer().getItemModelMesher().getModelManager();
renderModel( transform, renderer, lightmapCoord, overlayLight, modelManager.getModel( modelLocation ), tints );
}
private void renderModel( @Nonnull MatrixStack transform, @Nonnull IVertexBuilder renderer, int lightmapCoord, int overlayLight, IBakedModel model, int[] tints )
{
random.setSeed( 0 );
renderQuads( transform, renderer, lightmapCoord, overlayLight, model.getQuads( null, null, random, EmptyModelData.INSTANCE ), tints );
for( Direction facing : DirectionUtil.FACINGS )
{
renderQuads( transform, renderer, lightmapCoord, overlayLight, model.getQuads( null, facing, random, EmptyModelData.INSTANCE ), tints );
}
}
private static void renderQuads( @Nonnull MatrixStack transform, @Nonnull IVertexBuilder buffer, int lightmapCoord, int overlayLight, List<BakedQuad> quads, int[] tints )
{
MatrixStack.Entry matrix = transform.getLast();
for( BakedQuad bakedquad : quads )
{
int tint = -1;
if( tints != null && bakedquad.hasTintIndex() )
{ {
int idx = bakedquad.getTintIndex(); // Flip the model and swap the cull face as winding order will have changed.
if( idx >= 0 && idx < tints.length ) tint = tints[bakedquad.getTintIndex()]; GlStateManager.scalef( 1.0f, -1.0f, 1.0f );
GlStateManager.cullFace( GlStateManager.CullFace.FRONT );
}
GlStateManager.translatef( -0.5f, -0.5f, -0.5f );
// Render the turtle
int colour = turtle.getColour();
ComputerFamily family = turtle.getFamily();
ResourceLocation overlay = turtle.getOverlay();
renderModel( state, getTurtleModel( family, colour != -1 ), colour == -1 ? null : new int[] { colour } );
// Render the overlay
ModelResourceLocation overlayModel = getTurtleOverlayModel(
overlay,
HolidayUtil.getCurrentHoliday() == Holiday.Christmas
);
if( overlayModel != null )
{
GlStateManager.disableCull();
GlStateManager.enableBlend();
GlStateManager.blendFunc( GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA );
try
{
renderModel( state, overlayModel, null );
}
finally
{
GlStateManager.disableBlend();
GlStateManager.enableCull();
}
} }
float f = (float) (tint >> 16 & 255) / 255.0F; // Render the upgrades
float f1 = (float) (tint >> 8 & 255) / 255.0F; renderUpgrade( state, turtle, TurtleSide.Left, partialTicks );
float f2 = (float) (tint & 255) / 255.0F; renderUpgrade( state, turtle, TurtleSide.Right, partialTicks );
buffer.addVertexData( matrix, bakedquad, f, f1, f2, lightmapCoord, overlayLight, true ); }
finally
{
GlStateManager.popMatrix();
GlStateManager.cullFace( GlStateManager.CullFace.BACK );
} }
} }
private void renderUpgrade( BlockState state, TileTurtle turtle, TurtleSide side, float f )
{
ITurtleUpgrade upgrade = turtle.getUpgrade( side );
if( upgrade != null )
{
GlStateManager.pushMatrix();
try
{
float toolAngle = turtle.getToolRenderAngle( side, f );
GlStateManager.translatef( 0.0f, 0.5f, 0.5f );
GlStateManager.rotatef( -toolAngle, 1.0f, 0.0f, 0.0f );
GlStateManager.translatef( 0.0f, -0.5f, -0.5f );
Pair<IBakedModel, Matrix4f> pair = upgrade.getModel( turtle.getAccess(), side );
if( pair != null )
{
if( pair.getRight() != null )
{
ForgeHooksClient.multiplyCurrentGlMatrix( pair.getRight() );
}
if( pair.getLeft() != null )
{
renderModel( state, pair.getLeft(), null );
}
}
}
finally
{
GlStateManager.popMatrix();
}
}
}
private void renderModel( BlockState state, ModelResourceLocation modelLocation, int[] tints )
{
Minecraft mc = Minecraft.getInstance();
ModelManager modelManager = mc.getItemRenderer().getItemModelMesher().getModelManager();
renderModel( state, modelManager.getModel( modelLocation ), tints );
}
private void renderModel( BlockState state, IBakedModel model, int[] tints )
{
Random random = new Random( 0 );
Tessellator tessellator = Tessellator.getInstance();
rendererDispatcher.textureManager.bindTexture( AtlasTexture.LOCATION_BLOCKS_TEXTURE );
renderQuads( tessellator, model.getQuads( state, null, random, EmptyModelData.INSTANCE ), tints );
for( Direction facing : DirectionUtil.FACINGS )
{
renderQuads( tessellator, model.getQuads( state, facing, random, EmptyModelData.INSTANCE ), tints );
}
}
private static void renderQuads( Tessellator tessellator, List<BakedQuad> quads, int[] tints )
{
BufferBuilder buffer = tessellator.getBuffer();
VertexFormat format = DefaultVertexFormats.ITEM;
buffer.begin( GL11.GL_QUADS, format );
for( BakedQuad quad : quads )
{
VertexFormat quadFormat = quad.getFormat();
if( quadFormat != format )
{
tessellator.draw();
format = quadFormat;
buffer.begin( GL11.GL_QUADS, format );
}
int colour = 0xFFFFFFFF;
if( quad.hasTintIndex() && tints != null )
{
int index = quad.getTintIndex();
if( index >= 0 && index < tints.length ) colour = tints[index] | 0xFF000000;
}
LightUtil.renderQuadColor( buffer, quad, colour );
}
tessellator.draw();
}
} }

View File

@@ -5,27 +5,28 @@
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import com.mojang.datafixers.util.Pair;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import net.minecraft.client.renderer.model.*; import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.IUnbakedModel;
import net.minecraft.client.renderer.model.ModelBakery;
import net.minecraft.client.renderer.texture.ISprite;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.resources.IResourceManager; import net.minecraft.resources.IResourceManager;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.IModelConfiguration; import net.minecraftforge.client.model.ICustomModelLoader;
import net.minecraftforge.client.model.IModelLoader;
import net.minecraftforge.client.model.geometry.IModelGeometry;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
public final class TurtleModelLoader implements IModelLoader<TurtleModelLoader.TurtleModel> public final class TurtleModelLoader implements ICustomModelLoader
{ {
private static final ResourceLocation NORMAL_TURTLE_MODEL = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_normal" );
private static final ResourceLocation ADVANCED_TURTLE_MODEL = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_advanced" );
private static final ResourceLocation COLOUR_TURTLE_MODEL = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_colour" ); private static final ResourceLocation COLOUR_TURTLE_MODEL = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_colour" );
public static final TurtleModelLoader INSTANCE = new TurtleModelLoader(); public static final TurtleModelLoader INSTANCE = new TurtleModelLoader();
@@ -39,15 +40,32 @@ public final class TurtleModelLoader implements IModelLoader<TurtleModelLoader.T
{ {
} }
@Nonnull
@Override @Override
public TurtleModel read( @Nonnull JsonDeserializationContext deserializationContext, @Nonnull JsonObject modelContents ) public boolean accepts( @Nonnull ResourceLocation name )
{ {
ResourceLocation model = new ResourceLocation( JSONUtils.getString( modelContents, "model" ) ); return name.getNamespace().equals( ComputerCraft.MOD_ID )
return new TurtleModel( model ); && (name.getPath().equals( "item/turtle_normal" ) || name.getPath().equals( "item/turtle_advanced" ));
} }
public static final class TurtleModel implements IModelGeometry<TurtleModel> @Nonnull
@Override
public IUnbakedModel loadModel( @Nonnull ResourceLocation name )
{
if( name.getNamespace().equals( ComputerCraft.MOD_ID ) )
{
switch( name.getPath() )
{
case "item/turtle_normal":
return new TurtleModel( NORMAL_TURTLE_MODEL );
case "item/turtle_advanced":
return new TurtleModel( ADVANCED_TURTLE_MODEL );
}
}
throw new IllegalStateException( "Loader does not accept " + name );
}
private static final class TurtleModel implements IUnbakedModel
{ {
private final ResourceLocation family; private final ResourceLocation family;
@@ -56,21 +74,29 @@ public final class TurtleModelLoader implements IModelLoader<TurtleModelLoader.T
this.family = family; this.family = family;
} }
@Nonnull
@Override @Override
public Collection<Material> getTextures( IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<Pair<String, String>> missingTextureErrors ) public Collection<ResourceLocation> getDependencies()
{ {
Set<Material> materials = new HashSet<>(); return Arrays.asList( family, COLOUR_TURTLE_MODEL );
materials.addAll( modelGetter.apply( family ).getTextures( modelGetter, missingTextureErrors ) );
materials.addAll( modelGetter.apply( COLOUR_TURTLE_MODEL ).getTextures( modelGetter, missingTextureErrors ) );
return materials;
} }
@Nonnull
@Override @Override
public IBakedModel bake( IModelConfiguration owner, ModelBakery bakery, Function<Material, TextureAtlasSprite> spriteGetter, IModelTransform transform, ItemOverrideList overrides, ResourceLocation modelLocation ) public Collection<ResourceLocation> getTextures( @Nonnull Function<ResourceLocation, IUnbakedModel> modelGetter, @Nonnull Set<String> missingTextureErrors )
{
return getDependencies().stream()
.flatMap( x -> modelGetter.apply( x ).getTextures( modelGetter, missingTextureErrors ).stream() )
.collect( Collectors.toSet() );
}
@Nonnull
@Override
public IBakedModel bake( @Nonnull ModelBakery bakery, @Nonnull Function<ResourceLocation, TextureAtlasSprite> spriteGetter, @Nonnull ISprite sprite, @Nonnull VertexFormat format )
{ {
return new TurtleSmartItemModel( return new TurtleSmartItemModel(
bakery.getBakedModel( family, transform, spriteGetter ), bakery.getBakedModel( family, sprite, spriteGetter, format ),
bakery.getBakedModel( COLOUR_TURTLE_MODEL, transform, spriteGetter ) bakery.getBakedModel( COLOUR_TURTLE_MODEL, sprite, spriteGetter, format )
); );
} }
} }

View File

@@ -5,9 +5,7 @@
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import dan200.computercraft.api.client.TransformedModel;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.TransformationMatrix;
import net.minecraft.client.renderer.model.BakedQuad; import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemOverrideList; import net.minecraft.client.renderer.model.ItemOverrideList;
@@ -15,29 +13,32 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraftforge.client.model.data.EmptyModelData; import net.minecraftforge.client.model.data.EmptyModelData;
import net.minecraftforge.client.model.data.IModelData; import net.minecraftforge.client.model.data.IModelData;
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder;
import net.minecraftforge.client.model.pipeline.TRSRTransformer;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.vecmath.Matrix4f;
import java.util.*; import java.util.*;
public class TurtleMultiModel implements IBakedModel public class TurtleMultiModel implements IBakedModel
{ {
private final IBakedModel m_baseModel; private final IBakedModel m_baseModel;
private final IBakedModel m_overlayModel; private final IBakedModel m_overlayModel;
private final TransformationMatrix m_generalTransform; private final Matrix4f m_generalTransform;
private final TransformedModel m_leftUpgradeModel; private final IBakedModel m_leftUpgradeModel;
private final TransformedModel m_rightUpgradeModel; private final Matrix4f m_leftUpgradeTransform;
private final IBakedModel m_rightUpgradeModel;
private final Matrix4f m_rightUpgradeTransform;
private List<BakedQuad> m_generalQuads = null; private List<BakedQuad> m_generalQuads = null;
private Map<Direction, List<BakedQuad>> m_faceQuads = new EnumMap<>( Direction.class ); private Map<Direction, List<BakedQuad>> m_faceQuads = new EnumMap<>( Direction.class );
public TurtleMultiModel( IBakedModel baseModel, IBakedModel overlayModel, TransformationMatrix generalTransform, TransformedModel leftUpgradeModel, TransformedModel rightUpgradeModel ) public TurtleMultiModel( IBakedModel baseModel, IBakedModel overlayModel, Matrix4f generalTransform, IBakedModel leftUpgradeModel, Matrix4f leftUpgradeTransform, IBakedModel rightUpgradeModel, Matrix4f rightUpgradeTransform )
{ {
// Get the models // Get the models
m_baseModel = baseModel; m_baseModel = baseModel;
m_overlayModel = overlayModel; m_overlayModel = overlayModel;
m_leftUpgradeModel = leftUpgradeModel; m_leftUpgradeModel = leftUpgradeModel;
m_leftUpgradeTransform = leftUpgradeTransform;
m_rightUpgradeModel = rightUpgradeModel; m_rightUpgradeModel = rightUpgradeModel;
m_rightUpgradeTransform = rightUpgradeTransform;
m_generalTransform = generalTransform; m_generalTransform = generalTransform;
} }
@@ -68,22 +69,30 @@ public class TurtleMultiModel implements IBakedModel
private List<BakedQuad> buildQuads( BlockState state, Direction side, Random rand ) private List<BakedQuad> buildQuads( BlockState state, Direction side, Random rand )
{ {
ArrayList<BakedQuad> quads = new ArrayList<>(); ArrayList<BakedQuad> quads = new ArrayList<>();
ModelTransformer.transformQuadsTo( quads, m_baseModel.getQuads( state, side, rand, EmptyModelData.INSTANCE ), m_generalTransform );
transformQuadsTo( quads, m_baseModel.getQuads( state, side, rand, EmptyModelData.INSTANCE ), m_generalTransform );
if( m_overlayModel != null ) if( m_overlayModel != null )
{ {
transformQuadsTo( quads, m_overlayModel.getQuads( state, side, rand, EmptyModelData.INSTANCE ), m_generalTransform ); ModelTransformer.transformQuadsTo( quads, m_overlayModel.getQuads( state, side, rand, EmptyModelData.INSTANCE ), m_generalTransform );
} }
if( m_leftUpgradeModel != null ) if( m_leftUpgradeModel != null )
{ {
TransformationMatrix upgradeTransform = m_generalTransform.compose( m_leftUpgradeModel.getMatrix() ); Matrix4f upgradeTransform = m_generalTransform;
transformQuadsTo( quads, m_leftUpgradeModel.getModel().getQuads( state, side, rand, EmptyModelData.INSTANCE ), upgradeTransform ); if( m_leftUpgradeTransform != null )
{
upgradeTransform = new Matrix4f( m_generalTransform );
upgradeTransform.mul( m_leftUpgradeTransform );
}
ModelTransformer.transformQuadsTo( quads, m_leftUpgradeModel.getQuads( state, side, rand, EmptyModelData.INSTANCE ), upgradeTransform );
} }
if( m_rightUpgradeModel != null ) if( m_rightUpgradeModel != null )
{ {
TransformationMatrix upgradeTransform = m_generalTransform.compose( m_rightUpgradeModel.getMatrix() ); Matrix4f upgradeTransform = m_generalTransform;
transformQuadsTo( quads, m_rightUpgradeModel.getModel().getQuads( state, side, rand, EmptyModelData.INSTANCE ), upgradeTransform ); if( m_rightUpgradeTransform != null )
{
upgradeTransform = new Matrix4f( m_generalTransform );
upgradeTransform.mul( m_rightUpgradeTransform );
}
ModelTransformer.transformQuadsTo( quads, m_rightUpgradeModel.getQuads( state, side, rand, EmptyModelData.INSTANCE ), upgradeTransform );
} }
quads.trimToSize(); quads.trimToSize();
return quads; return quads;
@@ -107,12 +116,6 @@ public class TurtleMultiModel implements IBakedModel
return m_baseModel.isBuiltInRenderer(); return m_baseModel.isBuiltInRenderer();
} }
@Override
public boolean func_230044_c_()
{
return m_baseModel.func_230044_c_();
}
@Nonnull @Nonnull
@Override @Override
@Deprecated @Deprecated
@@ -135,15 +138,4 @@ public class TurtleMultiModel implements IBakedModel
{ {
return ItemOverrideList.EMPTY; return ItemOverrideList.EMPTY;
} }
private void transformQuadsTo( List<BakedQuad> output, List<BakedQuad> quads, TransformationMatrix transform )
{
for( BakedQuad quad : quads )
{
BakedQuadBuilder builder = new BakedQuadBuilder();
TRSRTransformer transformer = new TRSRTransformer( builder, transform );
quad.pipe( transformer );
output.add( builder.build() );
}
}
} }

View File

@@ -6,15 +6,14 @@
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import com.mojang.blaze3d.matrix.MatrixStack; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.GuiComputer;
import dan200.computercraft.shared.turtle.core.TurtlePlayer; import dan200.computercraft.shared.turtle.core.TurtlePlayer;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.entity.EntityRenderer; import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.entity.EntityRendererManager;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class TurtlePlayerRenderer extends EntityRenderer<TurtlePlayer> public class TurtlePlayerRenderer extends EntityRenderer<TurtlePlayer>
{ {
@@ -23,15 +22,16 @@ public class TurtlePlayerRenderer extends EntityRenderer<TurtlePlayer>
super( renderManager ); super( renderManager );
} }
@Nonnull
@Override @Override
public ResourceLocation getEntityTexture( @Nonnull TurtlePlayer entity ) public void doRender( @Nonnull TurtlePlayer entity, double x, double y, double z, float entityYaw, float partialTicks )
{ {
return GuiComputer.BACKGROUND_NORMAL; ComputerCraft.log.error( "Rendering TurtlePlayer on the client side, at {}", entity.getPosition() );
} }
@Nullable
@Override @Override
public void render( @Nonnull TurtlePlayer entityIn, float entityYaw, float partialTicks, @Nonnull MatrixStack transform, @Nonnull IRenderTypeBuffer buffer, int packedLightIn ) protected ResourceLocation getEntityTexture( @Nonnull TurtlePlayer entity )
{ {
return null;
} }
} }

View File

@@ -6,8 +6,6 @@
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.mojang.blaze3d.matrix.MatrixStack;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.turtle.items.ItemTurtle; import dan200.computercraft.shared.turtle.items.ItemTurtle;
@@ -15,7 +13,6 @@ import dan200.computercraft.shared.util.Holiday;
import dan200.computercraft.shared.util.HolidayUtil; import dan200.computercraft.shared.util.HolidayUtil;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.TransformationMatrix;
import net.minecraft.client.renderer.model.*; import net.minecraft.client.renderer.model.*;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
@@ -24,25 +21,28 @@ import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.client.model.data.IModelData; import net.minecraftforge.client.model.data.IModelData;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.vecmath.Matrix4f;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
public class TurtleSmartItemModel implements IBakedModel public class TurtleSmartItemModel implements IBakedModel
{ {
private static final TransformationMatrix identity, flip; private static final Matrix4f s_identity, s_flip;
static static
{ {
MatrixStack stack = new MatrixStack(); s_identity = new Matrix4f();
stack.scale( 0, -1, 0 ); s_identity.setIdentity();
stack.translate( 0, 0, 1 );
identity = TransformationMatrix.identity(); s_flip = new Matrix4f();
flip = new TransformationMatrix( stack.getLast().getMatrix() ); s_flip.setIdentity();
s_flip.m11 = -1; // Flip on the y axis
s_flip.m13 = 1; // Models go from (0,0,0) to (1,1,1), so push back up.
} }
private static class TurtleModelCombination private static class TurtleModelCombination
@@ -97,14 +97,15 @@ public class TurtleSmartItemModel implements IBakedModel
private final IBakedModel familyModel; private final IBakedModel familyModel;
private final IBakedModel colourModel; private final IBakedModel colourModel;
private final HashMap<TurtleModelCombination, IBakedModel> m_cachedModels = new HashMap<>(); private HashMap<TurtleModelCombination, IBakedModel> m_cachedModels;
private final ItemOverrideList m_overrides; private ItemOverrideList m_overrides;
public TurtleSmartItemModel( IBakedModel familyModel, IBakedModel colourModel ) public TurtleSmartItemModel( IBakedModel familyModel, IBakedModel colourModel )
{ {
this.familyModel = familyModel; this.familyModel = familyModel;
this.colourModel = colourModel; this.colourModel = colourModel;
m_cachedModels = new HashMap<>();
m_overrides = new ItemOverrideList() m_overrides = new ItemOverrideList()
{ {
@Nonnull @Nonnull
@@ -113,10 +114,10 @@ public class TurtleSmartItemModel implements IBakedModel
{ {
ItemTurtle turtle = (ItemTurtle) stack.getItem(); ItemTurtle turtle = (ItemTurtle) stack.getItem();
int colour = turtle.getColour( stack ); int colour = turtle.getColour( stack );
ITurtleUpgrade leftUpgrade = turtle.getUpgrade( stack, TurtleSide.LEFT ); ITurtleUpgrade leftUpgrade = turtle.getUpgrade( stack, TurtleSide.Left );
ITurtleUpgrade rightUpgrade = turtle.getUpgrade( stack, TurtleSide.RIGHT ); ITurtleUpgrade rightUpgrade = turtle.getUpgrade( stack, TurtleSide.Right );
ResourceLocation overlay = turtle.getOverlay( stack ); ResourceLocation overlay = turtle.getOverlay( stack );
boolean christmas = HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS; boolean christmas = HolidayUtil.getCurrentHoliday() == Holiday.Christmas;
String label = turtle.getLabel( stack ); String label = turtle.getLabel( stack );
boolean flip = label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )); boolean flip = label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" ));
TurtleModelCombination combo = new TurtleModelCombination( colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip ); TurtleModelCombination combo = new TurtleModelCombination( colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip );
@@ -143,10 +144,25 @@ public class TurtleSmartItemModel implements IBakedModel
IBakedModel baseModel = combo.m_colour ? colourModel : familyModel; IBakedModel baseModel = combo.m_colour ? colourModel : familyModel;
IBakedModel overlayModel = overlayModelLocation != null ? modelManager.getModel( overlayModelLocation ) : null; IBakedModel overlayModel = overlayModelLocation != null ? modelManager.getModel( overlayModelLocation ) : null;
TransformationMatrix transform = combo.m_flip ? flip : identity; Matrix4f transform = combo.m_flip ? s_flip : s_identity;
TransformedModel leftModel = combo.m_leftUpgrade != null ? combo.m_leftUpgrade.getModel( null, TurtleSide.LEFT ) : null; Pair<IBakedModel, Matrix4f> leftModel = combo.m_leftUpgrade != null ? combo.m_leftUpgrade.getModel( null, TurtleSide.Left ) : null;
TransformedModel rightModel = combo.m_rightUpgrade != null ? combo.m_rightUpgrade.getModel( null, TurtleSide.RIGHT ) : null; Pair<IBakedModel, Matrix4f> rightModel = combo.m_rightUpgrade != null ? combo.m_rightUpgrade.getModel( null, TurtleSide.Right ) : null;
return new TurtleMultiModel( baseModel, overlayModel, transform, leftModel, rightModel ); if( leftModel != null && rightModel != null )
{
return new TurtleMultiModel( baseModel, overlayModel, transform, leftModel.getLeft(), leftModel.getRight(), rightModel.getLeft(), rightModel.getRight() );
}
else if( leftModel != null )
{
return new TurtleMultiModel( baseModel, overlayModel, transform, leftModel.getLeft(), leftModel.getRight(), null, null );
}
else if( rightModel != null )
{
return new TurtleMultiModel( baseModel, overlayModel, transform, null, null, rightModel.getLeft(), rightModel.getRight() );
}
else
{
return new TurtleMultiModel( baseModel, overlayModel, transform, null, null, null, null );
}
} }
@Nonnull @Nonnull
@@ -183,12 +199,6 @@ public class TurtleSmartItemModel implements IBakedModel
return familyModel.isBuiltInRenderer(); return familyModel.isBuiltInRenderer();
} }
@Override
public boolean func_230044_c_()
{
return familyModel.func_230044_c_();
}
@Nonnull @Nonnull
@Override @Override
@Deprecated @Deprecated

View File

@@ -0,0 +1,181 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis;
import com.google.common.net.InetAddresses;
import dan200.computercraft.ComputerCraft;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
/**
* Used to determine whether a domain or IP address matches a series of patterns.
*/
public class AddressPredicate
{
private static final class HostRange
{
private final byte[] min;
private final byte[] max;
private HostRange( byte[] min, byte[] max )
{
this.min = min;
this.max = max;
}
public boolean contains( InetAddress address )
{
byte[] entry = address.getAddress();
if( entry.length != min.length ) return false;
for( int i = 0; i < entry.length; i++ )
{
int value = 0xFF & entry[i];
if( value < (0xFF & min[i]) || value > (0xFF & max[i]) ) return false;
}
return true;
}
}
private final List<Pattern> wildcards;
private final List<HostRange> ranges;
public AddressPredicate( String... filters )
{
this( Arrays.asList( filters ) );
}
public AddressPredicate( Iterable<? extends String> filters )
{
List<Pattern> wildcards = this.wildcards = new ArrayList<>();
List<HostRange> ranges = this.ranges = new ArrayList<>();
for( String filter : filters )
{
int cidr = filter.indexOf( '/' );
if( cidr >= 0 )
{
String addressStr = filter.substring( 0, cidr );
String prefixSizeStr = filter.substring( cidr + 1 );
int prefixSize;
try
{
prefixSize = Integer.parseInt( prefixSizeStr );
}
catch( NumberFormatException e )
{
ComputerCraft.log.error(
"Malformed http whitelist/blacklist entry '{}': Cannot extract size of CIDR mask from '{}'.",
filter, prefixSizeStr
);
continue;
}
InetAddress address;
try
{
address = InetAddresses.forString( addressStr );
}
catch( IllegalArgumentException e )
{
ComputerCraft.log.error(
"Malformed http whitelist/blacklist entry '{}': Cannot extract IP address from '{}'.",
filter, prefixSizeStr
);
continue;
}
// Mask the bytes of the IP address.
byte[] minBytes = address.getAddress(), maxBytes = address.getAddress();
int size = prefixSize;
for( int i = 0; i < minBytes.length; i++ )
{
if( size <= 0 )
{
minBytes[i] &= 0;
maxBytes[i] |= 0xFF;
}
else if( size < 8 )
{
minBytes[i] &= 0xFF << (8 - size);
maxBytes[i] |= ~(0xFF << (8 - size));
}
size -= 8;
}
ranges.add( new HostRange( minBytes, maxBytes ) );
}
else
{
wildcards.add( Pattern.compile( "^\\Q" + filter.replaceAll( "\\*", "\\\\E.*\\\\Q" ) + "\\E$" ) );
}
}
}
/**
* Determine whether a host name matches a series of patterns.
*
* This is intended to allow early exiting, before one has to look up the IP address. You should use
* {@link #matches(InetAddress)} instead of/in addition to this one.
*
* @param domain The domain to match.
* @return Whether the patterns were matched.
*/
public boolean matches( String domain )
{
for( Pattern domainPattern : wildcards )
{
if( domainPattern.matcher( domain ).matches() ) return true;
}
return false;
}
private boolean matchesAddress( InetAddress address )
{
String addressString = address.getHostAddress();
for( Pattern domainPattern : wildcards )
{
if( domainPattern.matcher( addressString ).matches() ) return true;
}
for( HostRange range : ranges )
{
if( range.contains( address ) ) return true;
}
return false;
}
/**
* Determine whether the given address matches a series of patterns.
*
* @param address The address to check.
* @return Whether it matches any of these patterns.
*/
public boolean matches( InetAddress address )
{
// Match the host name
String host = address.getHostName();
if( host != null && matches( host ) ) return true;
// Match the normal address
if( matchesAddress( address ) ) return true;
// If we're an IPv4 address in disguise then let's check that.
return address instanceof Inet6Address && InetAddresses.is6to4Address( (Inet6Address) address )
&& matchesAddress( InetAddresses.get6to4IPv4Address( (Inet6Address) address ) );
}
}

View File

@@ -0,0 +1,117 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.LuaException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Map;
/**
* A stub for any mods which depended on this version of the argument helper.
*
* @deprecated Use {@link dan200.computercraft.api.lua.ArgumentHelper}.
*/
@Deprecated
public final class ArgumentHelper
{
private ArgumentHelper()
{
}
@Nonnull
public static String getType( @Nullable Object type )
{
return dan200.computercraft.api.lua.ArgumentHelper.getType( type );
}
@Nonnull
public static LuaException badArgument( int index, @Nonnull String expected, @Nullable Object actual )
{
return dan200.computercraft.api.lua.ArgumentHelper.badArgumentOf( index, expected, actual );
}
@Nonnull
public static LuaException badArgument( int index, @Nonnull String expected, @Nonnull String actual )
{
return dan200.computercraft.api.lua.ArgumentHelper.badArgument( index, expected, actual );
}
public static double getNumber( @Nonnull Object[] args, int index ) throws LuaException
{
return dan200.computercraft.api.lua.ArgumentHelper.getDouble( args, index );
}
public static int getInt( @Nonnull Object[] args, int index ) throws LuaException
{
return dan200.computercraft.api.lua.ArgumentHelper.getInt( args, index );
}
public static long getLong( @Nonnull Object[] args, int index ) throws LuaException
{
return dan200.computercraft.api.lua.ArgumentHelper.getLong( args, index );
}
public static double getReal( @Nonnull Object[] args, int index ) throws LuaException
{
return dan200.computercraft.api.lua.ArgumentHelper.getFiniteDouble( args, index );
}
public static boolean getBoolean( @Nonnull Object[] args, int index ) throws LuaException
{
return dan200.computercraft.api.lua.ArgumentHelper.getBoolean( args, index );
}
@Nonnull
public static String getString( @Nonnull Object[] args, int index ) throws LuaException
{
return dan200.computercraft.api.lua.ArgumentHelper.getString( args, index );
}
@Nonnull
@SuppressWarnings( "unchecked" )
public static Map<Object, Object> getTable( @Nonnull Object[] args, int index ) throws LuaException
{
return (Map<Object, Object>) dan200.computercraft.api.lua.ArgumentHelper.getTable( args, index );
}
public static double optNumber( @Nonnull Object[] args, int index, double def ) throws LuaException
{
return dan200.computercraft.api.lua.ArgumentHelper.optDouble( args, index, def );
}
public static int optInt( @Nonnull Object[] args, int index, int def ) throws LuaException
{
return dan200.computercraft.api.lua.ArgumentHelper.optInt( args, index, def );
}
public static long optLong( @Nonnull Object[] args, int index, long def ) throws LuaException
{
return dan200.computercraft.api.lua.ArgumentHelper.optLong( args, index, def );
}
public static double optReal( @Nonnull Object[] args, int index, double def ) throws LuaException
{
return dan200.computercraft.api.lua.ArgumentHelper.optFiniteDouble( args, index, def );
}
public static boolean optBoolean( @Nonnull Object[] args, int index, boolean def ) throws LuaException
{
return dan200.computercraft.api.lua.ArgumentHelper.optBoolean( args, index, def );
}
public static String optString( @Nonnull Object[] args, int index, String def ) throws LuaException
{
return dan200.computercraft.api.lua.ArgumentHelper.optString( args, index, def );
}
@SuppressWarnings( "unchecked" )
public static Map<Object, Object> optTable( @Nonnull Object[] args, int index, Map<Object, Object> def ) throws LuaException
{
return (Map<Object, Object>) dan200.computercraft.api.lua.ArgumentHelper.optTable( args, index, def );
}
}

View File

@@ -13,6 +13,7 @@ import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.filesystem.FileSystemException; import dan200.computercraft.core.filesystem.FileSystemException;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@@ -122,7 +123,7 @@ public abstract class ComputerAccess implements IComputerAccess
m_environment.queueEvent( event, arguments ); m_environment.queueEvent( event, arguments );
} }
@Nonnull @Nullable
@Override @Override
public IWorkMonitor getMainThreadMonitor() public IWorkMonitor getMainThreadMonitor()
{ {

View File

@@ -173,7 +173,7 @@ public class HTTPAPI implements ILuaAPI
String address = getString( args, 0 ); String address = getString( args, 0 );
Map<?, ?> headerTbl = optTable( args, 1, Collections.emptyMap() ); Map<?, ?> headerTbl = optTable( args, 1, Collections.emptyMap() );
if( !ComputerCraft.httpWebsocketEnabled ) if( !ComputerCraft.http_websocket_enable )
{ {
throw new LuaException( "Websocket connections are disabled" ); throw new LuaException( "Websocket connections are disabled" );
} }

View File

@@ -0,0 +1,24 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis;
/**
* This exists purely to ensure binary compatibility.
*
* @see dan200.computercraft.api.lua.ILuaAPI
* @deprecated Use the version in the public API. Only exists for compatibility with CCEmuX.
*/
@Deprecated
public interface ILuaAPI extends dan200.computercraft.api.lua.ILuaAPI
{
void advance( double v );
@Override
default void update()
{
advance( 0.05 );
}
}

View File

@@ -11,8 +11,6 @@ import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.api.peripheral.NotAttachedException;
import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.core.tracking.TrackingField; import dan200.computercraft.core.tracking.TrackingField;
@@ -124,35 +122,53 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
@Override @Override
public synchronized String mount( @Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName ) public synchronized String mount( @Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName )
{ {
if( !m_attached ) throw new NotAttachedException(); if( !m_attached )
{
throw new RuntimeException( "You are not attached to this Computer" );
}
return super.mount( desiredLoc, mount, driveName ); return super.mount( desiredLoc, mount, driveName );
} }
@Override @Override
public synchronized String mountWritable( @Nonnull String desiredLoc, @Nonnull IWritableMount mount, @Nonnull String driveName ) public synchronized String mountWritable( @Nonnull String desiredLoc, @Nonnull IWritableMount mount, @Nonnull String driveName )
{ {
if( !m_attached ) throw new NotAttachedException(); if( !m_attached )
{
throw new RuntimeException( "You are not attached to this Computer" );
}
return super.mountWritable( desiredLoc, mount, driveName ); return super.mountWritable( desiredLoc, mount, driveName );
} }
@Override @Override
public synchronized void unmount( String location ) public synchronized void unmount( String location )
{ {
if( !m_attached ) throw new NotAttachedException(); if( !m_attached )
{
throw new RuntimeException( "You are not attached to this Computer" );
}
super.unmount( location ); super.unmount( location );
} }
@Override @Override
public int getID() public int getID()
{ {
if( !m_attached ) throw new NotAttachedException(); if( !m_attached )
{
throw new RuntimeException( "You are not attached to this Computer" );
}
return super.getID(); return super.getID();
} }
@Override @Override
public void queueEvent( @Nonnull final String event, final Object[] arguments ) public void queueEvent( @Nonnull final String event, final Object[] arguments )
{ {
if( !m_attached ) throw new NotAttachedException(); if( !m_attached )
{
throw new RuntimeException( "You are not attached to this Computer" );
}
super.queueEvent( event, arguments ); super.queueEvent( event, arguments );
} }
@@ -160,7 +176,10 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
@Override @Override
public String getAttachmentName() public String getAttachmentName()
{ {
if( !m_attached ) throw new NotAttachedException(); if( !m_attached )
{
throw new RuntimeException( "You are not attached to this Computer" );
}
return m_side; return m_side;
} }
@@ -168,7 +187,10 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
@Override @Override
public Map<String, IPeripheral> getAvailablePeripherals() public Map<String, IPeripheral> getAvailablePeripherals()
{ {
if( !m_attached ) throw new NotAttachedException(); if( !m_attached )
{
throw new RuntimeException( "You are not attached to this Computer" );
}
Map<String, IPeripheral> peripherals = new HashMap<>(); Map<String, IPeripheral> peripherals = new HashMap<>();
for( PeripheralWrapper wrapper : m_peripherals ) for( PeripheralWrapper wrapper : m_peripherals )
@@ -186,7 +208,10 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
@Override @Override
public IPeripheral getAvailablePeripheral( @Nonnull String name ) public IPeripheral getAvailablePeripheral( @Nonnull String name )
{ {
if( !m_attached ) throw new NotAttachedException(); if( !m_attached )
{
throw new RuntimeException( "You are not attached to this Computer" );
}
for( PeripheralWrapper wrapper : m_peripherals ) for( PeripheralWrapper wrapper : m_peripherals )
{ {
@@ -197,14 +222,6 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
} }
return null; return null;
} }
@Nonnull
@Override
public IWorkMonitor getMainThreadMonitor()
{
if( !m_attached ) throw new NotAttachedException();
return super.getMainThreadMonitor();
}
} }
private final IAPIEnvironment m_environment; private final IAPIEnvironment m_environment;

View File

@@ -16,11 +16,11 @@ import static dan200.computercraft.api.lua.ArgumentHelper.*;
public class RedstoneAPI implements ILuaAPI public class RedstoneAPI implements ILuaAPI
{ {
private IAPIEnvironment m_environment; private final IAPIEnvironment environment;
public RedstoneAPI( IAPIEnvironment environment ) public RedstoneAPI( IAPIEnvironment environment )
{ {
m_environment = environment; this.environment = environment;
} }
@Override @Override
@@ -63,31 +63,31 @@ public class RedstoneAPI implements ILuaAPI
// setOutput // setOutput
ComputerSide side = parseSide( args ); ComputerSide side = parseSide( args );
boolean output = getBoolean( args, 1 ); boolean output = getBoolean( args, 1 );
m_environment.setOutput( side, output ? 15 : 0 ); environment.setOutput( side, output ? 15 : 0 );
return null; return null;
} }
case 2: // getOutput case 2: // getOutput
return new Object[] { m_environment.getOutput( parseSide( args ) ) > 0 }; return new Object[] { environment.getOutput( parseSide( args ) ) > 0 };
case 3: // getInput case 3: // getInput
return new Object[] { m_environment.getInput( parseSide( args ) ) > 0 }; return new Object[] { environment.getInput( parseSide( args ) ) > 0 };
case 4: case 4:
{ {
// setBundledOutput // setBundledOutput
ComputerSide side = parseSide( args ); ComputerSide side = parseSide( args );
int output = getInt( args, 1 ); int output = getInt( args, 1 );
m_environment.setBundledOutput( side, output ); environment.setBundledOutput( side, output );
return null; return null;
} }
case 5: // getBundledOutput case 5: // getBundledOutput
return new Object[] { m_environment.getBundledOutput( parseSide( args ) ) }; return new Object[] { environment.getBundledOutput( parseSide( args ) ) };
case 6: // getBundledInput case 6: // getBundledInput
return new Object[] { m_environment.getBundledInput( parseSide( args ) ) }; return new Object[] { environment.getBundledInput( parseSide( args ) ) };
case 7: case 7:
{ {
// testBundledInput // testBundledInput
ComputerSide side = parseSide( args ); ComputerSide side = parseSide( args );
int mask = getInt( args, 1 ); int mask = getInt( args, 1 );
int input = m_environment.getBundledInput( side ); int input = environment.getBundledInput( side );
return new Object[] { (input & mask) == mask }; return new Object[] { (input & mask) == mask };
} }
case 8: case 8:
@@ -100,15 +100,15 @@ public class RedstoneAPI implements ILuaAPI
{ {
throw new LuaException( "Expected number in range 0-15" ); throw new LuaException( "Expected number in range 0-15" );
} }
m_environment.setOutput( side, output ); environment.setOutput( side, output );
return null; return null;
} }
case 10: case 10:
case 11: // getAnalogOutput/getAnalogueOutput case 11: // getAnalogOutput/getAnalogueOutput
return new Object[] { m_environment.getOutput( parseSide( args ) ) }; return new Object[] { environment.getOutput( parseSide( args ) ) };
case 12: case 12:
case 13: // getAnalogInput/getAnalogueInput case 13: // getAnalogInput/getAnalogueInput
return new Object[] { m_environment.getInput( parseSide( args ) ) }; return new Object[] { environment.getInput( parseSide( args ) ) };
default: default:
return null; return null;
} }

View File

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

View File

@@ -1,167 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis.http;
import com.google.common.net.InetAddresses;
import dan200.computercraft.ComputerCraft;
import javax.annotation.Nullable;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.regex.Pattern;
/**
* A pattern which matches an address, and controls whether it is accessible or not.
*/
public final class AddressRule
{
private static final class HostRange
{
private final byte[] min;
private final byte[] max;
private HostRange( byte[] min, byte[] max )
{
this.min = min;
this.max = max;
}
public boolean contains( InetAddress address )
{
byte[] entry = address.getAddress();
if( entry.length != min.length ) return false;
for( int i = 0; i < entry.length; i++ )
{
int value = 0xFF & entry[i];
if( value < (0xFF & min[i]) || value > (0xFF & max[i]) ) return false;
}
return true;
}
}
public enum Action
{
ALLOW,
DENY,
}
private final HostRange ip;
private final Pattern domainPattern;
private final Action action;
private AddressRule( HostRange ip, Pattern domainPattern, Action action )
{
this.ip = ip;
this.domainPattern = domainPattern;
this.action = action;
}
@Nullable
public static AddressRule parse( String filter, Action action )
{
int cidr = filter.indexOf( '/' );
if( cidr >= 0 )
{
String addressStr = filter.substring( 0, cidr );
String prefixSizeStr = filter.substring( cidr + 1 );
int prefixSize;
try
{
prefixSize = Integer.parseInt( prefixSizeStr );
}
catch( NumberFormatException e )
{
ComputerCraft.log.error(
"Malformed http whitelist/blacklist entry '{}': Cannot extract size of CIDR mask from '{}'.",
filter, prefixSizeStr
);
return null;
}
InetAddress address;
try
{
address = InetAddresses.forString( addressStr );
}
catch( IllegalArgumentException e )
{
ComputerCraft.log.error(
"Malformed http whitelist/blacklist entry '{}': Cannot extract IP address from '{}'.",
filter, prefixSizeStr
);
return null;
}
// Mask the bytes of the IP address.
byte[] minBytes = address.getAddress(), maxBytes = address.getAddress();
int size = prefixSize;
for( int i = 0; i < minBytes.length; i++ )
{
if( size <= 0 )
{
minBytes[i] &= 0;
maxBytes[i] |= 0xFF;
}
else if( size < 8 )
{
minBytes[i] &= 0xFF << (8 - size);
maxBytes[i] |= ~(0xFF << (8 - size));
}
size -= 8;
}
return new AddressRule( new HostRange( minBytes, maxBytes ), null, action );
}
else
{
Pattern pattern = Pattern.compile( "^\\Q" + filter.replaceAll( "\\*", "\\\\E.*\\\\Q" ) + "\\E$" );
return new AddressRule( null, pattern, action );
}
}
/**
* Determine whether the given address matches a series of patterns.
*
* @param domain The domain to match
* @param address The address to check.
* @return Whether it matches any of these patterns.
*/
public boolean matches( String domain, InetAddress address )
{
if( domainPattern != null )
{
if( domainPattern.matcher( domain ).matches() ) return true;
if( domainPattern.matcher( address.getHostName() ).matches() ) return true;
}
// Match the normal address
if( matchesAddress( address ) ) return true;
// If we're an IPv4 address in disguise then let's check that.
return address instanceof Inet6Address && InetAddresses.is6to4Address( (Inet6Address) address )
&& matchesAddress( InetAddresses.get6to4IPv4Address( (Inet6Address) address ) );
}
private boolean matchesAddress( InetAddress address )
{
if( domainPattern != null && domainPattern.matcher( address.getHostAddress() ).matches() ) return true;
return ip != null && ip.contains( address );
}
public static Action apply( Iterable<? extends AddressRule> rules, String domain, InetAddress address )
{
for( AddressRule rule : rules )
{
if( rule.matches( domain, address ) ) return rule.action;
}
return Action.DENY;
}
}

View File

@@ -97,6 +97,20 @@ public final class NetworkUtils
} }
} }
/**
* Checks a host is allowed.
*
* @param host The domain to check against
* @throws HTTPRequestException If the host is not permitted.
*/
public static void checkHost( String host ) throws HTTPRequestException
{
if( !ComputerCraft.http_whitelist.matches( host ) || ComputerCraft.http_blacklist.matches( host ) )
{
throw new HTTPRequestException( "Domain not permitted" );
}
}
/** /**
* Create a {@link InetSocketAddress} from the resolved {@code host} and port. * Create a {@link InetSocketAddress} from the resolved {@code host} and port.
* *
@@ -116,7 +130,7 @@ public final class NetworkUtils
if( socketAddress.isUnresolved() ) throw new HTTPRequestException( "Unknown host" ); if( socketAddress.isUnresolved() ) throw new HTTPRequestException( "Unknown host" );
InetAddress address = socketAddress.getAddress(); InetAddress address = socketAddress.getAddress();
if( AddressRule.apply( ComputerCraft.httpRules, host, address ) == AddressRule.Action.DENY ) if( !ComputerCraft.http_whitelist.matches( address ) || ComputerCraft.http_blacklist.matches( address ) )
{ {
throw new HTTPRequestException( "Domain not permitted" ); throw new HTTPRequestException( "Domain not permitted" );
} }

View File

@@ -106,7 +106,7 @@ public abstract class Resource<T extends Resource<T>> implements Closeable
protected static <T extends Closeable> T closeCloseable( T closeable ) protected static <T extends Closeable> T closeCloseable( T closeable )
{ {
if( closeable != null ) IoUtil.closeQuietly( closeable ); IoUtil.closeQuietly( closeable );
return null; return null;
} }

View File

@@ -119,6 +119,8 @@ public class HttpRequest extends Resource<HttpRequest>
{ {
throw new HTTPRequestException( "Invalid protocol '" + scheme + "'" ); throw new HTTPRequestException( "Invalid protocol '" + scheme + "'" );
} }
NetworkUtils.checkHost( url.getHost() );
} }
public void request( URI uri, HttpMethod method ) public void request( URI uri, HttpMethod method )

View File

@@ -110,6 +110,7 @@ public class Websocket extends Resource<Websocket>
throw new HTTPRequestException( "Invalid scheme '" + scheme + "'" ); throw new HTTPRequestException( "Invalid scheme '" + scheme + "'" );
} }
NetworkUtils.checkHost( uri.getHost() );
return uri; return uri;
} }
@@ -220,7 +221,7 @@ public class Websocket extends Resource<Websocket>
WeakReference<WebsocketHandle> websocketHandleRef = websocketHandle; WeakReference<WebsocketHandle> websocketHandleRef = websocketHandle;
WebsocketHandle websocketHandle = websocketHandleRef == null ? null : websocketHandleRef.get(); WebsocketHandle websocketHandle = websocketHandleRef == null ? null : websocketHandleRef.get();
if( websocketHandle != null ) IoUtil.closeQuietly( websocketHandle ); IoUtil.closeQuietly( websocketHandle );
this.websocketHandle = null; this.websocketHandle = null;
} }

View File

@@ -171,7 +171,7 @@ final class ComputerExecutor
apis.add( new FSAPI( environment ) ); apis.add( new FSAPI( environment ) );
apis.add( new PeripheralAPI( environment ) ); apis.add( new PeripheralAPI( environment ) );
apis.add( new OSAPI( environment ) ); apis.add( new OSAPI( environment ) );
if( ComputerCraft.httpEnabled ) apis.add( new HTTPAPI( environment ) ); if( ComputerCraft.http_enable ) apis.add( new HTTPAPI( environment ) );
// Load in the externally registered APIs. // Load in the externally registered APIs.
for( ILuaAPIFactory factory : ApiFactories.getAll() ) for( ILuaAPIFactory factory : ApiFactories.getAll() )
@@ -611,7 +611,7 @@ final class ComputerExecutor
terminal.reset(); terminal.reset();
// Display our primary error message // Display our primary error message
if( colour ) terminal.setTextColour( 15 - Colour.RED.ordinal() ); if( colour ) terminal.setTextColour( 15 - Colour.Red.ordinal() );
terminal.write( message ); terminal.write( message );
if( extra != null ) if( extra != null )
@@ -624,7 +624,7 @@ final class ComputerExecutor
// And display our generic "CC may be installed incorrectly" message. // And display our generic "CC may be installed incorrectly" message.
terminal.setCursorPos( 0, terminal.getCursorY() + 1 ); terminal.setCursorPos( 0, terminal.getCursorY() + 1 );
if( colour ) terminal.setTextColour( 15 - Colour.WHITE.ordinal() ); if( colour ) terminal.setTextColour( 15 - Colour.White.ordinal() );
terminal.write( "ComputerCraft may be installed incorrectly" ); terminal.write( "ComputerCraft may be installed incorrectly" );
} }

View File

@@ -9,15 +9,12 @@ import dan200.computercraft.api.filesystem.IFileSystem;
import dan200.computercraft.api.lua.IComputerSystem; import dan200.computercraft.api.lua.IComputerSystem;
import dan200.computercraft.api.lua.ILuaAPIFactory; import dan200.computercraft.api.lua.ILuaAPIFactory;
import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.apis.ComputerAccess; import dan200.computercraft.core.apis.ComputerAccess;
import dan200.computercraft.core.apis.IAPIEnvironment; import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.filesystem.FileSystem; import dan200.computercraft.core.filesystem.FileSystem;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Map;
/** /**
* Implementation of {@link IComputerAccess}/{@link IComputerSystem} for usage by externally registered APIs. * Implementation of {@link IComputerAccess}/{@link IComputerSystem} for usage by externally registered APIs.
@@ -57,19 +54,4 @@ public class ComputerSystem extends ComputerAccess implements IComputerSystem
{ {
return environment.getLabel(); return environment.getLabel();
} }
@Nonnull
@Override
public Map<String, IPeripheral> getAvailablePeripherals()
{
// TODO: Should this return peripherals on the current computer?
return Collections.emptyMap();
}
@Nullable
@Override
public IPeripheral getAvailablePeripheral( @Nonnull String name )
{
return null;
}
} }

View File

@@ -10,6 +10,7 @@ import dan200.computercraft.api.filesystem.IMount;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList; import java.util.ArrayList;
@@ -115,7 +116,8 @@ public class ComboMount implements IMount
@Nonnull @Nonnull
@Override @Override
public ReadableByteChannel openForRead( @Nonnull String path ) throws IOException @Deprecated
public InputStream openForRead( @Nonnull String path ) throws IOException
{ {
for( int i = m_parts.length - 1; i >= 0; --i ) for( int i = m_parts.length - 1; i >= 0; --i )
{ {
@@ -128,6 +130,21 @@ public class ComboMount implements IMount
throw new FileOperationException( path, "No such file" ); throw new FileOperationException( path, "No such file" );
} }
@Nonnull
@Override
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
{
for( int i = m_parts.length - 1; i >= 0; --i )
{
IMount part = m_parts[i];
if( part.exists( path ) && !part.isDirectory( path ) )
{
return part.openChannelForRead( path );
}
}
throw new FileOperationException( path, "No such file" );
}
@Nonnull @Nonnull
@Override @Override
public BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException public BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException

View File

@@ -10,6 +10,7 @@ import dan200.computercraft.api.filesystem.IMount;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import java.util.List; import java.util.List;
@@ -40,7 +41,16 @@ public class EmptyMount implements IMount
@Nonnull @Nonnull
@Override @Override
public ReadableByteChannel openForRead( @Nonnull String path ) throws IOException @Deprecated
public InputStream openForRead( @Nonnull String path ) throws IOException
{
throw new FileOperationException( path, "No such file" );
}
@Nonnull
@Override
@Deprecated
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
{ {
throw new FileOperationException( path, "No such file" ); throw new FileOperationException( path, "No such file" );
} }

View File

@@ -11,8 +11,7 @@ import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IWritableMount; import dan200.computercraft.api.filesystem.IWritableMount;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.File; import java.io.*;
import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.*; import java.nio.channels.*;
import java.nio.file.*; import java.nio.file.*;
@@ -201,7 +200,21 @@ public class FileMount implements IWritableMount
@Nonnull @Nonnull
@Override @Override
public ReadableByteChannel openForRead( @Nonnull String path ) throws IOException @Deprecated
public InputStream openForRead( @Nonnull String path ) throws IOException
{
if( created() )
{
File file = getRealPath( path );
if( file.exists() && !file.isDirectory() ) return new FileInputStream( file );
}
throw new FileOperationException( path, "No such file" );
}
@Nonnull
@Override
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
{ {
if( created() ) if( created() )
{ {
@@ -300,7 +313,23 @@ public class FileMount implements IWritableMount
@Nonnull @Nonnull
@Override @Override
public WritableByteChannel openForWrite( @Nonnull String path ) throws IOException @Deprecated
public OutputStream openForWrite( @Nonnull String path ) throws IOException
{
return Channels.newOutputStream( openChannelForWrite( path ) );
}
@Nonnull
@Override
@Deprecated
public OutputStream openForAppend( @Nonnull String path ) throws IOException
{
return Channels.newOutputStream( openChannelForAppend( path ) );
}
@Nonnull
@Override
public WritableByteChannel openChannelForWrite( @Nonnull String path ) throws IOException
{ {
create(); create();
File file = getRealPath( path ); File file = getRealPath( path );
@@ -321,7 +350,7 @@ public class FileMount implements IWritableMount
@Nonnull @Nonnull
@Override @Override
public WritableByteChannel openForAppend( @Nonnull String path ) throws IOException public WritableByteChannel openChannelForAppend( @Nonnull String path ) throws IOException
{ {
if( !created() ) if( !created() )
{ {

View File

@@ -366,8 +366,7 @@ public class FileSystem
Reference<?> ref; Reference<?> ref;
while( (ref = m_openFileQueue.poll()) != null ) while( (ref = m_openFileQueue.poll()) != null )
{ {
Closeable file = m_openFiles.remove( ref ); IoUtil.closeQuietly( m_openFiles.remove( ref ) );
if( file != null ) IoUtil.closeQuietly( file );
} }
} }
} }

View File

@@ -9,6 +9,9 @@ import dan200.computercraft.api.filesystem.IFileSystem;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel; import java.nio.channels.WritableByteChannel;
import java.util.Collections; import java.util.Collections;
@@ -52,7 +55,7 @@ public class FileSystemWrapperMount implements IFileSystem
@Nonnull @Nonnull
@Override @Override
public ReadableByteChannel openForRead( @Nonnull String path ) throws IOException public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
{ {
try try
{ {
@@ -67,7 +70,7 @@ public class FileSystemWrapperMount implements IFileSystem
@Nonnull @Nonnull
@Override @Override
public WritableByteChannel openForWrite( @Nonnull String path ) throws IOException public WritableByteChannel openChannelForWrite( @Nonnull String path ) throws IOException
{ {
try try
{ {
@@ -81,7 +84,7 @@ public class FileSystemWrapperMount implements IFileSystem
@Nonnull @Nonnull
@Override @Override
public WritableByteChannel openForAppend( @Nonnull String path ) throws IOException public WritableByteChannel openChannelForAppend( @Nonnull String path ) throws IOException
{ {
try try
{ {
@@ -93,6 +96,30 @@ public class FileSystemWrapperMount implements IFileSystem
} }
} }
@Nonnull
@Override
@Deprecated
public InputStream openForRead( @Nonnull String path ) throws IOException
{
return Channels.newInputStream( openChannelForRead( path ) );
}
@Nonnull
@Override
@Deprecated
public OutputStream openForWrite( @Nonnull String path ) throws IOException
{
return Channels.newOutputStream( openChannelForWrite( path ) );
}
@Nonnull
@Override
@Deprecated
public OutputStream openForAppend( @Nonnull String path ) throws IOException
{
return Channels.newOutputStream( openChannelForAppend( path ) );
}
@Override @Override
public long getRemainingSpace() throws IOException public long getRemainingSpace() throws IOException
{ {

View File

@@ -184,7 +184,15 @@ public class JarMount implements IMount
@Nonnull @Nonnull
@Override @Override
public ReadableByteChannel openForRead( @Nonnull String path ) throws IOException @Deprecated
public InputStream openForRead( @Nonnull String path ) throws IOException
{
return Channels.newInputStream( openChannelForRead( path ) );
}
@Nonnull
@Override
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
{ {
FileEntry file = get( path ); FileEntry file = get( path );
if( file != null && !file.isDirectory() ) if( file != null && !file.isDirectory() )

View File

@@ -20,11 +20,11 @@ import java.util.OptionalLong;
class MountWrapper class MountWrapper
{ {
private final String label; private String label;
private final String location; private String location;
private final IMount mount; private IMount mount;
private final IWritableMount writableMount; private IWritableMount writableMount;
MountWrapper( String label, String location, IMount mount ) MountWrapper( String label, String location, IMount mount )
{ {
@@ -36,9 +36,7 @@ class MountWrapper
MountWrapper( String label, String location, IWritableMount mount ) MountWrapper( String label, String location, IWritableMount mount )
{ {
this.label = label; this( label, location, (IMount) mount );
this.location = location;
this.mount = mount;
writableMount = mount; writableMount = mount;
} }
@@ -156,7 +154,7 @@ class MountWrapper
{ {
if( mount.exists( path ) && !mount.isDirectory( path ) ) if( mount.exists( path ) && !mount.isDirectory( path ) )
{ {
return mount.openForRead( path ); return mount.openChannelForRead( path );
} }
else else
{ {
@@ -234,7 +232,7 @@ class MountWrapper
writableMount.makeDirectory( dir ); writableMount.makeDirectory( dir );
} }
} }
return writableMount.openForWrite( path ); return writableMount.openChannelForWrite( path );
} }
} }
catch( AccessDeniedException e ) catch( AccessDeniedException e )
@@ -264,7 +262,7 @@ class MountWrapper
writableMount.makeDirectory( dir ); writableMount.makeDirectory( dir );
} }
} }
return writableMount.openForWrite( path ); return writableMount.openChannelForWrite( path );
} }
else if( mount.isDirectory( path ) ) else if( mount.isDirectory( path ) )
{ {
@@ -272,7 +270,7 @@ class MountWrapper
} }
else else
{ {
return writableMount.openForAppend( path ); return writableMount.openChannelForAppend( path );
} }
} }
catch( AccessDeniedException e ) catch( AccessDeniedException e )

View File

@@ -223,7 +223,15 @@ public final class ResourceMount implements IMount
@Nonnull @Nonnull
@Override @Override
public ReadableByteChannel openForRead( @Nonnull String path ) throws IOException @Deprecated
public InputStream openForRead( @Nonnull String path ) throws IOException
{
return Channels.newInputStream( openChannelForRead( path ) );
}
@Nonnull
@Override
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
{ {
FileEntry file = get( path ); FileEntry file = get( path );
if( file != null && !file.isDirectory() ) if( file != null && !file.isDirectory() )

View File

@@ -9,6 +9,7 @@ import dan200.computercraft.api.filesystem.IMount;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.List; import java.util.List;
@@ -50,11 +51,19 @@ public class SubMount implements IMount
@Nonnull @Nonnull
@Override @Override
public ReadableByteChannel openForRead( @Nonnull String path ) throws IOException @Deprecated
public InputStream openForRead( @Nonnull String path ) throws IOException
{ {
return parent.openForRead( getFullPath( path ) ); return parent.openForRead( getFullPath( path ) );
} }
@Nonnull
@Override
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
{
return parent.openChannelForRead( getFullPath( path ) );
}
@Nonnull @Nonnull
@Override @Override
public BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException public BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException

View File

@@ -5,18 +5,20 @@
*/ */
package dan200.computercraft.core.terminal; package dan200.computercraft.core.terminal;
import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.Palette; import dan200.computercraft.shared.util.Palette;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.PacketBuffer;
public class Terminal public class Terminal
{ {
private static final String base16 = "0123456789abcdef"; private static final String base16 = "0123456789abcdef";
private int m_cursorX; private int m_cursorX = 0;
private int m_cursorY; private int m_cursorY = 0;
private boolean m_cursorBlink; private boolean m_cursorBlink = false;
private int m_cursorColour; private int m_cursorColour = 0;
private int m_cursorBackgroundColour; private int m_cursorBackgroundColour = 15;
private int m_width; private int m_width;
private int m_height; private int m_height;
@@ -25,9 +27,9 @@ public class Terminal
private TextBuffer[] m_textColour; private TextBuffer[] m_textColour;
private TextBuffer[] m_backgroundColour; private TextBuffer[] m_backgroundColour;
private final Palette m_palette; private final Palette m_palette = new Palette();
private boolean m_changed; private boolean m_changed = false;
private final Runnable onChanged; private final Runnable onChanged;
public Terminal( int width, int height ) public Terminal( int width, int height )
@@ -41,9 +43,6 @@ public class Terminal
m_height = height; m_height = height;
onChanged = changedCallback; onChanged = changedCallback;
m_cursorColour = 0;
m_cursorBackgroundColour = 15;
m_text = new TextBuffer[m_height]; m_text = new TextBuffer[m_height];
m_textColour = new TextBuffer[m_height]; m_textColour = new TextBuffer[m_height];
m_backgroundColour = new TextBuffer[m_height]; m_backgroundColour = new TextBuffer[m_height];
@@ -53,14 +52,6 @@ public class Terminal
m_textColour[i] = new TextBuffer( base16.charAt( m_cursorColour ), m_width ); m_textColour[i] = new TextBuffer( base16.charAt( m_cursorColour ), m_width );
m_backgroundColour[i] = new TextBuffer( base16.charAt( m_cursorBackgroundColour ), m_width ); m_backgroundColour[i] = new TextBuffer( base16.charAt( m_cursorBackgroundColour ), m_width );
} }
m_cursorX = 0;
m_cursorY = 0;
m_cursorBlink = false;
m_changed = false;
m_palette = new Palette();
} }
public synchronized void reset() public synchronized void reset()
@@ -312,6 +303,19 @@ public class Terminal
return null; return null;
} }
/**
* Determine whether this terminal has changed.
*
* @return If this terminal is dirty.
* @deprecated All {@code *Changed()} methods are deprecated: one should pass in a callback
* instead.
*/
@Deprecated
public final boolean getChanged()
{
return m_changed;
}
public final void setChanged() public final void setChanged()
{ {
m_changed = true; m_changed = true;
@@ -323,6 +327,62 @@ public class Terminal
m_changed = false; m_changed = false;
} }
public synchronized void write( PacketBuffer buffer )
{
buffer.writeInt( m_cursorX );
buffer.writeInt( m_cursorY );
buffer.writeBoolean( m_cursorBlink );
buffer.writeByte( m_cursorBackgroundColour << 4 | m_cursorColour );
for( int y = 0; y < m_height; y++ )
{
TextBuffer text = m_text[y];
TextBuffer textColour = m_textColour[y];
TextBuffer backColour = m_backgroundColour[y];
for( int x = 0; x < m_width; x++ )
{
buffer.writeByte( text.charAt( x ) & 0xFF );
buffer.writeByte( getColour(
backColour.charAt( x ), Colour.Black ) << 4 |
getColour( textColour.charAt( x ), Colour.White )
);
}
}
m_palette.write( buffer );
}
public synchronized void read( PacketBuffer buffer )
{
m_cursorX = buffer.readInt();
m_cursorY = buffer.readInt();
m_cursorBlink = buffer.readBoolean();
byte cursorColour = buffer.readByte();
m_cursorBackgroundColour = (cursorColour >> 4) & 0xF;
m_cursorColour = cursorColour & 0xF;
for( int y = 0; y < m_height; y++ )
{
TextBuffer text = m_text[y];
TextBuffer textColour = m_textColour[y];
TextBuffer backColour = m_backgroundColour[y];
for( int x = 0; x < m_width; x++ )
{
text.setChar( x, (char) (buffer.readByte() & 0xFF) );
byte colour = buffer.readByte();
backColour.setChar( x, base16.charAt( (colour >> 4) & 0xF ) );
textColour.setChar( x, base16.charAt( colour & 0xF ) );
}
}
m_palette.read( buffer );
setChanged();
}
public synchronized CompoundNBT writeToNBT( CompoundNBT nbt ) public synchronized CompoundNBT writeToNBT( CompoundNBT nbt )
{ {
nbt.putInt( "term_cursorX", m_cursorX ); nbt.putInt( "term_cursorX", m_cursorX );
@@ -336,10 +396,8 @@ public class Terminal
nbt.putString( "term_textColour_" + n, m_textColour[n].toString() ); nbt.putString( "term_textColour_" + n, m_textColour[n].toString() );
nbt.putString( "term_textBgColour_" + n, m_backgroundColour[n].toString() ); nbt.putString( "term_textBgColour_" + n, m_backgroundColour[n].toString() );
} }
if( m_palette != null )
{ m_palette.writeToNBT( nbt );
m_palette.writeToNBT( nbt );
}
return nbt; return nbt;
} }
@@ -369,10 +427,15 @@ public class Terminal
m_backgroundColour[n].write( nbt.getString( "term_textBgColour_" + n ) ); m_backgroundColour[n].write( nbt.getString( "term_textBgColour_" + n ) );
} }
} }
if( m_palette != null )
{ m_palette.readFromNBT( nbt );
m_palette.readFromNBT( nbt );
}
setChanged(); setChanged();
} }
public static int getColour( char c, Colour def )
{
if( c >= '0' && c <= '9' ) return c - '0';
if( c >= 'a' && c <= 'f' ) return c - 'a' + 10;
return 15 - def.ordinal();
}
} }

View File

@@ -14,10 +14,9 @@ import net.minecraft.data.DataGenerator;
import net.minecraft.data.DirectoryCache; import net.minecraft.data.DirectoryCache;
import net.minecraft.data.IDataProvider; import net.minecraft.data.IDataProvider;
import net.minecraft.util.ResourceLocation; 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.LootTable;
import net.minecraft.world.storage.loot.LootTableManager; import net.minecraft.world.storage.loot.LootTableManager;
import net.minecraft.world.storage.loot.ValidationTracker; import net.minecraft.world.storage.loot.ValidationResults;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
@@ -43,17 +42,17 @@ public abstract class LootTableProvider implements IDataProvider
@Override @Override
public void act( @Nonnull DirectoryCache cache ) public void act( @Nonnull DirectoryCache cache )
{ {
Map<ResourceLocation, LootTable> tables = new HashMap<>();
ValidationTracker validation = new ValidationTracker( LootParameterSets.GENERIC, x -> null, tables::get );
ValidationResults validation = new ValidationResults();
Map<ResourceLocation, LootTable> tables = new HashMap<>();
registerLoot( ( id, table ) -> { registerLoot( ( id, table ) -> {
if( tables.containsKey( id ) ) validation.func_227530_a_( "Duplicate loot tables for " + id ); if( tables.containsKey( id ) ) validation.addProblem( "Duplicate loot tables for " + id );
tables.put( id, table ); tables.put( id, table );
} ); } );
tables.forEach( ( key, value ) -> LootTableManager.func_227508_a_( validation, key, value ) ); tables.forEach( ( key, value ) -> LootTableManager.func_215302_a( validation, key, value, tables::get ) );
Multimap<String, String> problems = validation.func_227527_a_(); Multimap<String, String> problems = validation.getProblems();
if( !problems.isEmpty() ) if( !problems.isEmpty() )
{ {
problems.forEach( ( child, problem ) -> problems.forEach( ( child, problem ) ->

View File

@@ -8,6 +8,7 @@ package dan200.computercraft.data;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition; import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
import dan200.computercraft.shared.data.HasComputerIdLootCondition;
import dan200.computercraft.shared.data.PlayerCreativeLootCondition; import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.data.DataGenerator; import net.minecraft.data.DataGenerator;
@@ -67,6 +68,7 @@ public class LootTables extends LootTableProvider
.addEntry( DynamicLootEntry.func_216162_a( new ResourceLocation( ComputerCraft.MOD_ID, "computer" ) ) ) .addEntry( DynamicLootEntry.func_216162_a( new ResourceLocation( ComputerCraft.MOD_ID, "computer" ) ) )
.acceptCondition( Alternative.builder( .acceptCondition( Alternative.builder(
BlockNamedEntityLootCondition.builder(), BlockNamedEntityLootCondition.builder(),
HasComputerIdLootCondition.builder(),
PlayerCreativeLootCondition.builder().inverted() PlayerCreativeLootCondition.builder().inverted()
) ) ) )
).build() ); ).build() );

View File

@@ -6,28 +6,27 @@
package dan200.computercraft.shared; package dan200.computercraft.shared;
import com.electronwill.nightconfig.core.CommentedConfig; import com.electronwill.nightconfig.core.CommentedConfig;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import com.electronwill.nightconfig.core.file.CommentedFileConfig; import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.google.common.base.CaseFormat; import com.google.common.base.CaseFormat;
import com.google.common.base.Converter; import com.google.common.base.Converter;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.turtle.event.TurtleAction; import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.core.apis.http.AddressRule; import dan200.computercraft.core.apis.AddressPredicate;
import dan200.computercraft.core.apis.http.websocket.Websocket; import dan200.computercraft.core.apis.http.websocket.Websocket;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
import net.minecraftforge.common.ForgeConfigSpec; import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.config.ModConfig;
import javax.annotation.Nullable; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static dan200.computercraft.ComputerCraft.DEFAULT_HTTP_BLACKLIST;
import static dan200.computercraft.ComputerCraft.DEFAULT_HTTP_WHITELIST;
import static net.minecraftforge.common.ForgeConfigSpec.Builder; import static net.minecraftforge.common.ForgeConfigSpec.Builder;
import static net.minecraftforge.common.ForgeConfigSpec.ConfigValue; import static net.minecraftforge.common.ForgeConfigSpec.ConfigValue;
@@ -52,7 +51,8 @@ public final class Config
private static final ConfigValue<Boolean> httpEnabled; private static final ConfigValue<Boolean> httpEnabled;
private static final ConfigValue<Boolean> httpWebsocketEnabled; private static final ConfigValue<Boolean> httpWebsocketEnabled;
private static final ConfigValue<List<? extends UnmodifiableConfig>> httpRules; private static final ConfigValue<List<? extends String>> httpWhitelist;
private static final ConfigValue<List<? extends String>> httpBlacklist;
private static final ConfigValue<Integer> httpTimeout; private static final ConfigValue<Integer> httpTimeout;
private static final ConfigValue<Integer> httpMaxRequests; private static final ConfigValue<Integer> httpMaxRequests;
@@ -67,6 +67,7 @@ public final class Config
private static final ConfigValue<Integer> modemRangeDuringStorm; private static final ConfigValue<Integer> modemRangeDuringStorm;
private static final ConfigValue<Integer> modemHighAltitudeRangeDuringStorm; private static final ConfigValue<Integer> modemHighAltitudeRangeDuringStorm;
private static final ConfigValue<Integer> maxNotesPerTick; private static final ConfigValue<Integer> maxNotesPerTick;
private static final ConfigValue<Integer> monitorBandwidth;
private static final ConfigValue<Boolean> turtlesNeedFuel; private static final ConfigValue<Boolean> turtlesNeedFuel;
private static final ConfigValue<Integer> turtleFuelLimit; private static final ConfigValue<Integer> turtleFuelLimit;
@@ -75,7 +76,10 @@ public final class Config
private static final ConfigValue<Boolean> turtlesCanPush; private static final ConfigValue<Boolean> turtlesCanPush;
private static final ConfigValue<List<? extends String>> turtleDisabledActions; private static final ConfigValue<List<? extends String>> turtleDisabledActions;
private static final ForgeConfigSpec spec; private static final ConfigValue<MonitorRenderer> monitorRenderer;
private static final ForgeConfigSpec commonSpec;
private static final ForgeConfigSpec clientSpec;
private Config() {} private Config() {}
@@ -152,25 +156,25 @@ public final class Config
builder.push( "http" ); builder.push( "http" );
httpEnabled = builder httpEnabled = builder
.comment( "Enable the \"http\" API on Computers (see \"rules\" for more fine grained control than this)." ) .comment( "Enable the \"http\" API on Computers (see \"http_whitelist\" and \"http_blacklist\" for more " +
.define( "enabled", ComputerCraft.httpEnabled ); "fine grained control than this)" )
.define( "enabled", ComputerCraft.http_enable );
httpWebsocketEnabled = builder httpWebsocketEnabled = builder
.comment( "Enable use of http websockets. This requires the \"http_enable\" option to also be true." ) .comment( "Enable use of http websockets. This requires the \"http_enable\" option to also be true." )
.define( "websocket_enabled", ComputerCraft.httpWebsocketEnabled ); .define( "websocket_enabled", ComputerCraft.http_websocket_enable );
httpRules = builder httpWhitelist = builder
.comment( "A list of rules which control which domains or IPs are allowed through the \"http\" API on computers.\n" + .comment( "A list of wildcards for domains or IP ranges that can be accessed through the \"http\" API on Computers.\n" +
"Each rule is an item with a 'host' to match against, and an action. " + "Set this to \"*\" to access to the entire internet. Example: \"*.pastebin.com\" will restrict access to just subdomains of pastebin.com.\n" +
"The host may be a domain name (\"pastebin.com\"),\n" + "You can use domain names (\"pastebin.com\"), wilcards (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\")." )
"wildcard (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\"). 'action' maybe 'allow' or 'block'. If no rules" + .defineList( "whitelist", Arrays.asList( DEFAULT_HTTP_WHITELIST ), x -> true );
"match, the domain will be blocked." )
.defineList( "rules", httpBlacklist = builder
Stream.concat( .comment( "A list of wildcards for domains or IP ranges that cannot be accessed through the \"http\" API on Computers.\n" +
Stream.of( ComputerCraft.DEFAULT_HTTP_DENY ).map( x -> makeRule( x, "deny" ) ), "If this is empty then all whitelisted domains will be accessible. Example: \"*.github.com\" will block access to all subdomains of github.com.\n" +
Stream.of( ComputerCraft.DEFAULT_HTTP_ALLOW ).map( x -> makeRule( x, "allow" ) ) "You can use domain names (\"pastebin.com\"), wilcards (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\")." )
).collect( Collectors.toList() ), .defineList( "blacklist", Arrays.asList( DEFAULT_HTTP_BLACKLIST ), x -> true );
x -> x instanceof UnmodifiableConfig && parseRule( (UnmodifiableConfig) x ) != null );
httpTimeout = builder httpTimeout = builder
.comment( "The period of time (in milliseconds) to wait before a HTTP request times out. Set to 0 for unlimited." ) .comment( "The period of time (in milliseconds) to wait before a HTTP request times out. Set to 0 for unlimited." )
@@ -227,6 +231,16 @@ public final class Config
.comment( "Maximum amount of notes a speaker can play at once" ) .comment( "Maximum amount of notes a speaker can play at once" )
.defineInRange( "max_notes_per_tick", ComputerCraft.maxNotesPerTick, 1, Integer.MAX_VALUE ); .defineInRange( "max_notes_per_tick", ComputerCraft.maxNotesPerTick, 1, Integer.MAX_VALUE );
monitorBandwidth = builder
.comment( "The limit to how much monitor data can be sent *per tick*. Note:\n" +
" - Bandwidth is measured before compression, so the data sent to the client is smaller.\n" +
" - This ignores the number of players a packet is sent to. Updating a monitor for one player consumes " +
"the same bandwidth limit as sending to 20.\n" +
" - A full sized monitor sends ~25kb of data. So the default (1MB) allows for ~40 monitors to be updated " +
"in a single tick. \n" +
"Set to 0 to disable." )
.defineInRange( "monitor_bandwidth", (int) ComputerCraft.monitorBandwidth, 0, Integer.MAX_VALUE );
builder.pop(); builder.pop();
} }
@@ -261,12 +275,20 @@ public final class Config
builder.pop(); builder.pop();
} }
spec = builder.build(); commonSpec = builder.build();
Builder clientBuilder = new Builder();
monitorRenderer = clientBuilder
.comment( "The renderer to use for monitors. Generally this should be kept at \"best\" - if " +
"monitors have performance issues, you may wish to experiment with alternative renderers." )
.defineEnum( "monitor_renderer", MonitorRenderer.BEST );
clientSpec = clientBuilder.build();
} }
public static void load() public static void load()
{ {
ModLoadingContext.get().registerConfig( ModConfig.Type.COMMON, spec ); ModLoadingContext.get().registerConfig( ModConfig.Type.COMMON, commonSpec );
ModLoadingContext.get().registerConfig( ModConfig.Type.CLIENT, clientSpec );
} }
public static void sync() public static void sync()
@@ -287,10 +309,10 @@ public final class Config
ComputerCraft.maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos( maxMainComputerTime.get() ); ComputerCraft.maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos( maxMainComputerTime.get() );
// HTTP // HTTP
ComputerCraft.httpEnabled = httpEnabled.get(); ComputerCraft.http_enable = httpEnabled.get();
ComputerCraft.httpWebsocketEnabled = httpWebsocketEnabled.get(); ComputerCraft.http_websocket_enable = httpWebsocketEnabled.get();
ComputerCraft.httpRules = Collections.unmodifiableList( httpRules.get().stream() ComputerCraft.http_whitelist = new AddressPredicate( httpWhitelist.get() );
.map( Config::parseRule ).filter( Objects::nonNull ).collect( Collectors.toList() ) ); ComputerCraft.http_blacklist = new AddressPredicate( httpBlacklist.get() );
ComputerCraft.httpTimeout = httpTimeout.get(); ComputerCraft.httpTimeout = httpTimeout.get();
ComputerCraft.httpMaxRequests = httpMaxRequests.get(); ComputerCraft.httpMaxRequests = httpMaxRequests.get();
@@ -306,6 +328,7 @@ public final class Config
ComputerCraft.modem_highAltitudeRange = modemHighAltitudeRange.get(); ComputerCraft.modem_highAltitudeRange = modemHighAltitudeRange.get();
ComputerCraft.modem_rangeDuringStorm = modemRangeDuringStorm.get(); ComputerCraft.modem_rangeDuringStorm = modemRangeDuringStorm.get();
ComputerCraft.modem_highAltitudeRangeDuringStorm = modemHighAltitudeRangeDuringStorm.get(); ComputerCraft.modem_highAltitudeRangeDuringStorm = modemHighAltitudeRangeDuringStorm.get();
ComputerCraft.monitorBandwidth = monitorBandwidth.get();
// Turtles // Turtles
ComputerCraft.turtlesNeedFuel = turtlesNeedFuel.get(); ComputerCraft.turtlesNeedFuel = turtlesNeedFuel.get();
@@ -316,6 +339,9 @@ public final class Config
ComputerCraft.turtleDisabledActions.clear(); ComputerCraft.turtleDisabledActions.clear();
for( String value : turtleDisabledActions.get() ) ComputerCraft.turtleDisabledActions.add( getAction( value ) ); for( String value : turtleDisabledActions.get() ) ComputerCraft.turtleDisabledActions.add( getAction( value ) );
// Client
ComputerCraft.monitorRenderer = monitorRenderer.get();
} }
@SubscribeEvent @SubscribeEvent
@@ -325,7 +351,7 @@ public final class Config
} }
@SubscribeEvent @SubscribeEvent
public static void sync( ModConfig.Reloading event ) public static void sync( ModConfig.ConfigReloading event )
{ {
// Ensure file configs are reloaded. Forge should probably do this, so worth checking in the future. // Ensure file configs are reloaded. Forge should probably do this, so worth checking in the future.
CommentedConfig config = event.getConfig().getConfigData(); CommentedConfig config = event.getConfig().getConfigData();
@@ -347,28 +373,4 @@ public final class Config
return null; return null;
} }
} }
private static UnmodifiableConfig makeRule( String host, String action )
{
com.electronwill.nightconfig.core.Config config = com.electronwill.nightconfig.core.Config.inMemory();
config.add( "host", host );
config.add( "action", action );
return config;
}
@Nullable
private static AddressRule parseRule( UnmodifiableConfig builder )
{
Object hostObj = builder.get( "host" );
Object actionObj = builder.get( "action" );
if( !(hostObj instanceof String) || !(actionObj instanceof String) ) return null;
String host = (String) hostObj, action = (String) actionObj;
for( AddressRule.Action candiate : AddressRule.Action.values() )
{
if( candiate.name().equalsIgnoreCase( action ) ) return AddressRule.parse( host, candiate );
}
return null;
}
} }

View File

@@ -84,17 +84,17 @@ public final class Registry
// Computers // Computers
ComputerCraft.Blocks.computerNormal = new BlockComputer( ComputerCraft.Blocks.computerNormal = new BlockComputer(
Block.Properties.create( Material.ROCK ).hardnessAndResistance( 2.0f ), Block.Properties.create( Material.ROCK ).hardnessAndResistance( 2.0f ),
ComputerFamily.NORMAL, TileComputer.FACTORY_NORMAL ComputerFamily.Normal, TileComputer.FACTORY_NORMAL
); );
ComputerCraft.Blocks.computerAdvanced = new BlockComputer( ComputerCraft.Blocks.computerAdvanced = new BlockComputer(
Block.Properties.create( Material.ROCK ).hardnessAndResistance( 2.0f ), Block.Properties.create( Material.ROCK ).hardnessAndResistance( 2.0f ),
ComputerFamily.ADVANCED, TileComputer.FACTORY_ADVANCED ComputerFamily.Advanced, TileComputer.FACTORY_ADVANCED
); );
ComputerCraft.Blocks.computerCommand = new BlockComputer( ComputerCraft.Blocks.computerCommand = new BlockComputer(
Block.Properties.create( Material.ROCK ).hardnessAndResistance( -1, 6000000.0F ), Block.Properties.create( Material.ROCK ).hardnessAndResistance( -1, 6000000.0F ),
ComputerFamily.COMMAND, TileCommandComputer.FACTORY ComputerFamily.Command, TileCommandComputer.FACTORY
); );
registry.registerAll( registry.registerAll(
@@ -106,12 +106,12 @@ public final class Registry
// Turtles // Turtles
ComputerCraft.Blocks.turtleNormal = new BlockTurtle( ComputerCraft.Blocks.turtleNormal = new BlockTurtle(
Block.Properties.create( Material.ROCK ).hardnessAndResistance( 2.5f ), Block.Properties.create( Material.ROCK ).hardnessAndResistance( 2.5f ),
ComputerFamily.NORMAL, TileTurtle.FACTORY_NORMAL ComputerFamily.Normal, TileTurtle.FACTORY_NORMAL
); );
ComputerCraft.Blocks.turtleAdvanced = new BlockTurtle( ComputerCraft.Blocks.turtleAdvanced = new BlockTurtle(
Block.Properties.create( Material.ROCK ).hardnessAndResistance( 2.5f ), Block.Properties.create( Material.ROCK ).hardnessAndResistance( 2.5f ),
ComputerFamily.ADVANCED, TileTurtle.FACTORY_ADVANCED ComputerFamily.Advanced, TileTurtle.FACTORY_ADVANCED
); );
registry.registerAll( registry.registerAll(
@@ -234,8 +234,8 @@ public final class Registry
); );
// Pocket computer // Pocket computer
ComputerCraft.Items.pocketComputerNormal = new ItemPocketComputer( defaultItem().maxStackSize( 1 ), ComputerFamily.NORMAL ); ComputerCraft.Items.pocketComputerNormal = new ItemPocketComputer( defaultItem().maxStackSize( 1 ), ComputerFamily.Normal );
ComputerCraft.Items.pocketComputerAdvanced = new ItemPocketComputer( defaultItem().maxStackSize( 1 ), ComputerFamily.ADVANCED ); ComputerCraft.Items.pocketComputerAdvanced = new ItemPocketComputer( defaultItem().maxStackSize( 1 ), ComputerFamily.Advanced );
registry.registerAll( registry.registerAll(
ComputerCraft.Items.pocketComputerNormal.setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "pocket_computer_normal" ) ), ComputerCraft.Items.pocketComputerNormal.setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "pocket_computer_normal" ) ),

View File

@@ -205,7 +205,7 @@ public final class CommandComputerCraft
int queued = 0; int queued = 0;
for( ServerComputer computer : computers ) for( ServerComputer computer : computers )
{ {
if( computer.getFamily() == ComputerFamily.COMMAND && computer.isOn() ) if( computer.getFamily() == ComputerFamily.Command && computer.isOn() )
{ {
computer.queueEvent( "computer_command", rest ); computer.queueEvent( "computer_command", rest );
queued++; queued++;

View File

@@ -11,14 +11,12 @@ import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.world.IBlockReader; import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorldReader; import net.minecraft.world.IWorldReader;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -47,13 +45,12 @@ public abstract class BlockGeneric extends Block
if( tile instanceof TileGeneric ) ((TileGeneric) tile).destroy(); if( tile instanceof TileGeneric ) ((TileGeneric) tile).destroy();
} }
@Nonnull
@Override @Override
@Deprecated @Deprecated
public final ActionResultType onBlockActivated( BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit ) public final boolean onBlockActivated( BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit )
{ {
TileEntity tile = world.getTileEntity( pos ); TileEntity tile = world.getTileEntity( pos );
return tile instanceof TileGeneric ? ((TileGeneric) tile).onActivate( player, hand, hit ) : ActionResultType.PASS; return tile instanceof TileGeneric && ((TileGeneric) tile).onActivate( player, hand, hit );
} }
@Override @Override
@@ -73,7 +70,7 @@ public abstract class BlockGeneric extends Block
@Override @Override
@Deprecated @Deprecated
public void tick( BlockState state, ServerWorld world, BlockPos pos, Random rand ) public void tick( BlockState state, World world, BlockPos pos, Random rand )
{ {
TileEntity te = world.getTileEntity( pos ); TileEntity te = world.getTileEntity( pos );
if( te instanceof TileGeneric ) ((TileGeneric) te).blockTick(); if( te instanceof TileGeneric ) ((TileGeneric) te).blockTick();

View File

@@ -6,7 +6,7 @@
package dan200.computercraft.shared.common; package dan200.computercraft.shared.common;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import net.minecraft.nbt.CompoundNBT; import dan200.computercraft.shared.network.client.TerminalState;
public class ClientTerminal implements ITerminal public class ClientTerminal implements ITerminal
{ {
@@ -46,14 +46,13 @@ public class ClientTerminal implements ITerminal
return m_colour; return m_colour;
} }
public void readDescription( CompoundNBT nbt ) public void read( TerminalState state )
{ {
m_colour = nbt.getBoolean( "colour" ); m_colour = state.colour;
if( nbt.contains( "terminal" ) ) if( state.hasTerminal() )
{ {
CompoundNBT terminal = nbt.getCompound( "terminal" ); resizeTerminal( state.width, state.height );
resizeTerminal( terminal.getInt( "term_width" ), terminal.getInt( "term_height" ) ); state.apply( m_terminal );
m_terminal.readFromNBT( terminal );
} }
else else
{ {

View File

@@ -6,7 +6,7 @@
package dan200.computercraft.shared.common; package dan200.computercraft.shared.common;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import net.minecraft.nbt.CompoundNBT; import dan200.computercraft.shared.network.client.TerminalState;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@@ -69,8 +69,6 @@ public class ServerTerminal implements ITerminal
return m_terminalChangedLastFrame; return m_terminalChangedLastFrame;
} }
// ITerminal implementation
@Override @Override
public Terminal getTerminal() public Terminal getTerminal()
{ {
@@ -83,18 +81,8 @@ public class ServerTerminal implements ITerminal
return m_colour; return m_colour;
} }
// Networking stuff public TerminalState write()
public void writeDescription( CompoundNBT nbt )
{ {
nbt.putBoolean( "colour", m_colour ); return new TerminalState( m_colour, m_terminal );
if( m_terminal != null )
{
CompoundNBT terminal = new CompoundNBT();
terminal.putInt( "term_width", m_terminal.getWidth() );
terminal.putInt( "term_height", m_terminal.getHeight() );
m_terminal.writeToNBT( terminal );
nbt.put( "terminal", terminal );
}
} }
} }

View File

@@ -12,7 +12,6 @@ import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SUpdateTileEntityPacket; import net.minecraft.network.play.server.SUpdateTileEntityPacket;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.BlockRayTraceResult;
@@ -38,10 +37,9 @@ public abstract class TileGeneric extends TileEntity
getWorld().notifyBlockUpdate( pos, state, state, 3 ); getWorld().notifyBlockUpdate( pos, state, state, 3 );
} }
@Nonnull public boolean onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
public ActionResultType onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
{ {
return ActionResultType.PASS; return false;
} }
public void onNeighbourChange( @Nonnull BlockPos neighbour ) public void onNeighbourChange( @Nonnull BlockPos neighbour )

View File

@@ -32,7 +32,7 @@ public class TileCommandComputer extends TileComputer
{ {
public static final NamedTileEntityType<TileCommandComputer> FACTORY = NamedTileEntityType.create( public static final NamedTileEntityType<TileCommandComputer> FACTORY = NamedTileEntityType.create(
new ResourceLocation( ComputerCraft.MOD_ID, "command_computer" ), new ResourceLocation( ComputerCraft.MOD_ID, "command_computer" ),
f -> new TileCommandComputer( ComputerFamily.COMMAND, f ) f -> new TileCommandComputer( ComputerFamily.Command, f )
); );
public class CommandReceiver implements ICommandSource public class CommandReceiver implements ICommandSource
@@ -120,6 +120,11 @@ public class TileCommandComputer extends TileComputer
@Override @Override
public boolean isUsable( PlayerEntity player, boolean ignoreRange ) public boolean isUsable( PlayerEntity player, boolean ignoreRange )
{
return isUsable( player ) && super.isUsable( player, ignoreRange );
}
public static boolean isUsable( PlayerEntity player )
{ {
MinecraftServer server = player.getServer(); MinecraftServer server = player.getServer();
if( server == null || !server.isCommandBlockEnabled() ) if( server == null || !server.isCommandBlockEnabled() )
@@ -127,14 +132,12 @@ public class TileCommandComputer extends TileComputer
player.sendStatusMessage( new TranslationTextComponent( "advMode.notEnabled" ), true ); player.sendStatusMessage( new TranslationTextComponent( "advMode.notEnabled" ), true );
return false; return false;
} }
else if( !player.canUseCommandBlock() ) else if( ComputerCraft.commandRequireCreative ? !player.canUseCommandBlock() : !server.getPlayerList().canSendCommands( player.getGameProfile() ) )
{ {
player.sendStatusMessage( new TranslationTextComponent( "advMode.notAllowed" ), true ); player.sendStatusMessage( new TranslationTextComponent( "advMode.notAllowed" ), true );
return false; return false;
} }
else
{ return true;
return super.isUsable( player, ignoreRange );
}
} }
} }

View File

@@ -27,12 +27,12 @@ public class TileComputer extends TileComputerBase
{ {
public static final NamedTileEntityType<TileComputer> FACTORY_NORMAL = NamedTileEntityType.create( public static final NamedTileEntityType<TileComputer> FACTORY_NORMAL = NamedTileEntityType.create(
new ResourceLocation( ComputerCraft.MOD_ID, "computer_normal" ), new ResourceLocation( ComputerCraft.MOD_ID, "computer_normal" ),
f -> new TileComputer( ComputerFamily.NORMAL, f ) f -> new TileComputer( ComputerFamily.Normal, f )
); );
public static final NamedTileEntityType<TileComputer> FACTORY_ADVANCED = NamedTileEntityType.create( public static final NamedTileEntityType<TileComputer> FACTORY_ADVANCED = NamedTileEntityType.create(
new ResourceLocation( ComputerCraft.MOD_ID, "computer_advanced" ), new ResourceLocation( ComputerCraft.MOD_ID, "computer_advanced" ),
f -> new TileComputer( ComputerFamily.ADVANCED, f ) f -> new TileComputer( ComputerFamily.Advanced, f )
); );
private ComputerProxy m_proxy; private ComputerProxy m_proxy;

View File

@@ -30,7 +30,6 @@ import net.minecraft.item.Items;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.ITickableTileEntity; import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.INameable; import net.minecraft.util.INameable;
@@ -103,9 +102,8 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
return false; return false;
} }
@Nonnull
@Override @Override
public ActionResultType onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit ) public boolean onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
{ {
ItemStack currentItem = player.getHeldItem( hand ); ItemStack currentItem = player.getHeldItem( hand );
if( !currentItem.isEmpty() && currentItem.getItem() == Items.NAME_TAG && canNameWithTag( player ) && currentItem.hasDisplayName() ) if( !currentItem.isEmpty() && currentItem.getItem() == Items.NAME_TAG && canNameWithTag( player ) && currentItem.hasDisplayName() )
@@ -116,9 +114,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
setLabel( currentItem.getDisplayName().getString() ); setLabel( currentItem.getDisplayName().getString() );
currentItem.shrink( 1 ); currentItem.shrink( 1 );
} }
return ActionResultType.SUCCESS; return true;
} }
else if( !player.isCrouching() ) else if( !player.isSneaking() )
{ {
// Regular right click to activate computer // Regular right click to activate computer
if( !getWorld().isRemote && isUsable( player, false ) ) if( !getWorld().isRemote && isUsable( player, false ) )
@@ -126,9 +124,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
createServerComputer().turnOn(); createServerComputer().turnOn();
new ComputerContainerData( createServerComputer() ).open( player, this ); new ComputerContainerData( createServerComputer() ).open( player, this );
} }
return ActionResultType.SUCCESS; return true;
} }
return ActionResultType.PASS; return false;
} }
@Override @Override
@@ -279,7 +277,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
if( offset.equals( neighbour ) ) if( offset.equals( neighbour ) )
{ {
updateSideInput( computer, dir, offset ); updateSideInput( computer, dir, offset );
break; return;
} }
} }

View File

@@ -7,7 +7,7 @@ package dan200.computercraft.shared.computer.core;
public enum ComputerFamily public enum ComputerFamily
{ {
NORMAL, Normal,
ADVANCED, Advanced,
COMMAND Command
} }

View File

@@ -35,4 +35,3 @@ public enum ComputerState implements IStringSerializable
return name; return name;
} }
} }

View File

@@ -50,7 +50,7 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
public ServerComputer( World world, int computerID, String label, int instanceID, ComputerFamily family, int terminalWidth, int terminalHeight ) public ServerComputer( World world, int computerID, String label, int instanceID, ComputerFamily family, int terminalWidth, int terminalHeight )
{ {
super( family != ComputerFamily.NORMAL, terminalWidth, terminalHeight ); super( family != ComputerFamily.Normal, terminalWidth, terminalHeight );
m_instanceID = instanceID; m_instanceID = instanceID;
m_world = world; m_world = world;
@@ -154,9 +154,7 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
protected NetworkMessage createTerminalPacket() protected NetworkMessage createTerminalPacket()
{ {
CompoundNBT tagCompound = new CompoundNBT(); return new ComputerTerminalClientMessage( getInstanceID(), write() );
writeDescription( tagCompound );
return new ComputerTerminalClientMessage( getInstanceID(), tagCompound );
} }
public void broadcastState( boolean force ) public void broadcastState( boolean force )

View File

@@ -6,6 +6,7 @@
package dan200.computercraft.shared.computer.inventory; package dan200.computercraft.shared.computer.inventory;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.blocks.TileCommandComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.IContainerComputer; import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.core.ServerComputer;
@@ -14,8 +15,6 @@ import dan200.computercraft.shared.network.container.ViewComputerContainerData;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.ContainerType; import net.minecraft.inventory.container.ContainerType;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.text.TranslationTextComponent;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@@ -48,18 +47,9 @@ public class ContainerViewComputer extends ContainerComputerBase implements ICon
} }
// If we're a command computer then ensure we're in creative // If we're a command computer then ensure we're in creative
if( computer.getFamily() == ComputerFamily.COMMAND ) if( computer.getFamily() == ComputerFamily.Command && !TileCommandComputer.isUsable( player ) )
{ {
MinecraftServer server = player.getServer(); return false;
if( server == null || !server.isCommandBlockEnabled() )
{
return false;
}
else if( !player.canUseCommandBlock() )
{
player.sendStatusMessage( new TranslationTextComponent( "advMode.notAllowed" ), false );
return false;
}
} }
return true; return true;

View File

@@ -19,9 +19,7 @@ public final class ComputerItemFactory
@Nonnull @Nonnull
public static ItemStack create( TileComputer tile ) public static ItemStack create( TileComputer tile )
{ {
String label = tile.getLabel(); return create( tile.getComputerID(), tile.getLabel(), tile.getFamily() );
int id = label != null ? tile.getComputerID() : -1;
return create( id, label, tile.getFamily() );
} }
@Nonnull @Nonnull
@@ -29,11 +27,11 @@ public final class ComputerItemFactory
{ {
switch( family ) switch( family )
{ {
case NORMAL: case Normal:
return ComputerCraft.Items.computerNormal.create( id, label ); return ComputerCraft.Items.computerNormal.create( id, label );
case ADVANCED: case Advanced:
return ComputerCraft.Items.computerAdvanced.create( id, label ); return ComputerCraft.Items.computerAdvanced.create( id, label );
case COMMAND: case Command:
return ComputerCraft.Items.computerCommand.create( id, label ); return ComputerCraft.Items.computerCommand.create( id, label );
default: default:
return ItemStack.EMPTY; return ItemStack.EMPTY;

View File

@@ -37,7 +37,7 @@ public abstract class ItemComputerBase extends BlockItem implements IComputerIte
@Override @Override
public void addInformation( @Nonnull ItemStack stack, @Nullable World world, @Nonnull List<ITextComponent> list, @Nonnull ITooltipFlag options ) public void addInformation( @Nonnull ItemStack stack, @Nullable World world, @Nonnull List<ITextComponent> list, @Nonnull ITooltipFlag options )
{ {
if( options.isAdvanced() ) if( options.isAdvanced() || getLabel( stack ) == null )
{ {
int id = getComputerID( stack ); int id = getComputerID( stack );
if( id >= 0 ) if( id >= 0 )
@@ -80,7 +80,7 @@ public abstract class ItemComputerBase extends BlockItem implements IComputerIte
public IMount createDataMount( @Nonnull ItemStack stack, @Nonnull World world ) public IMount createDataMount( @Nonnull ItemStack stack, @Nonnull World world )
{ {
ComputerFamily family = getFamily(); ComputerFamily family = getFamily();
if( family != ComputerFamily.COMMAND ) if( family != ComputerFamily.Command )
{ {
int id = getComputerID( stack ); int id = getComputerID( stack );
if( id >= 0 ) if( id >= 0 )

View File

@@ -0,0 +1,48 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.data;
import dan200.computercraft.shared.computer.blocks.IComputerTile;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.storage.loot.LootContext;
import net.minecraft.world.storage.loot.LootParameter;
import net.minecraft.world.storage.loot.LootParameters;
import net.minecraft.world.storage.loot.conditions.ILootCondition;
import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.Set;
/**
* A loot condition which checks if the tile entity has has a non-0 ID.
*/
public final class HasComputerIdLootCondition implements ILootCondition
{
public static final HasComputerIdLootCondition INSTANCE = new HasComputerIdLootCondition();
private HasComputerIdLootCondition()
{
}
@Override
public boolean test( LootContext lootContext )
{
TileEntity tile = lootContext.get( LootParameters.BLOCK_ENTITY );
return tile instanceof IComputerTile && ((IComputerTile) tile).getComputerID() >= 0;
}
@Nonnull
@Override
public Set<LootParameter<?>> getRequiredParameters()
{
return Collections.singleton( LootParameters.BLOCK_ENTITY );
}
public static IBuilder builder()
{
return () -> INSTANCE;
}
}

View File

@@ -116,8 +116,8 @@ public class JEIComputerCraft implements IModPlugin
StringBuilder name = new StringBuilder(); StringBuilder name = new StringBuilder();
// Add left and right upgrades to the identifier // Add left and right upgrades to the identifier
ITurtleUpgrade left = turtle.getUpgrade( stack, TurtleSide.LEFT ); ITurtleUpgrade left = turtle.getUpgrade( stack, TurtleSide.Left );
ITurtleUpgrade right = turtle.getUpgrade( stack, TurtleSide.RIGHT ); ITurtleUpgrade right = turtle.getUpgrade( stack, TurtleSide.Right );
if( left != null ) name.append( left.getUpgradeID() ); if( left != null ) name.append( left.getUpgradeID() );
if( left != null && right != null ) name.append( '|' ); if( left != null && right != null ) name.append( '|' );
if( right != null ) name.append( right.getUpgradeID() ); if( right != null ) name.append( right.getUpgradeID() );

View File

@@ -37,7 +37,7 @@ import static net.minecraft.util.NonNullList.from;
class RecipeResolver implements IRecipeManagerPlugin class RecipeResolver implements IRecipeManagerPlugin
{ {
static final ComputerFamily[] MAIN_FAMILIES = new ComputerFamily[] { ComputerFamily.NORMAL, ComputerFamily.ADVANCED }; static final ComputerFamily[] MAIN_FAMILIES = new ComputerFamily[] { ComputerFamily.Normal, ComputerFamily.Advanced };
private final Map<Item, List<UpgradeInfo>> upgradeItemLookup = new HashMap<>(); private final Map<Item, List<UpgradeInfo>> upgradeItemLookup = new HashMap<>();
private final List<UpgradeInfo> pocketUpgrades = new ArrayList<>(); private final List<UpgradeInfo> pocketUpgrades = new ArrayList<>();
@@ -150,8 +150,8 @@ class RecipeResolver implements IRecipeManagerPlugin
{ {
// Suggest possible upgrades which can be applied to this turtle // Suggest possible upgrades which can be applied to this turtle
ITurtleItem item = (ITurtleItem) stack.getItem(); ITurtleItem item = (ITurtleItem) stack.getItem();
ITurtleUpgrade left = item.getUpgrade( stack, TurtleSide.LEFT ); ITurtleUpgrade left = item.getUpgrade( stack, TurtleSide.Left );
ITurtleUpgrade right = item.getUpgrade( stack, TurtleSide.RIGHT ); ITurtleUpgrade right = item.getUpgrade( stack, TurtleSide.Right );
if( left != null && right != null ) return Collections.emptyList(); if( left != null && right != null ) return Collections.emptyList();
List<Shaped> recipes = new ArrayList<>(); List<Shaped> recipes = new ArrayList<>();
@@ -231,8 +231,8 @@ class RecipeResolver implements IRecipeManagerPlugin
ITurtleItem item = (ITurtleItem) stack.getItem(); ITurtleItem item = (ITurtleItem) stack.getItem();
List<Shaped> recipes = new ArrayList<>( 0 ); List<Shaped> recipes = new ArrayList<>( 0 );
ITurtleUpgrade left = item.getUpgrade( stack, TurtleSide.LEFT ); ITurtleUpgrade left = item.getUpgrade( stack, TurtleSide.Left );
ITurtleUpgrade right = item.getUpgrade( stack, TurtleSide.RIGHT ); ITurtleUpgrade right = item.getUpgrade( stack, TurtleSide.Right );
// The turtle is facing towards us, so upgrades on the left are actually crafted on the right. // The turtle is facing towards us, so upgrades on the left are actually crafted on the right.
if( left != null ) if( left != null )

View File

@@ -126,6 +126,6 @@ public class ItemDisk extends Item implements IMedia, IColouredItem
public int getColour( @Nonnull ItemStack stack ) public int getColour( @Nonnull ItemStack stack )
{ {
int colour = IColouredItem.getColourBasic( stack ); int colour = IColouredItem.getColourBasic( stack );
return colour == -1 ? Colour.WHITE.getHex() : colour; return colour == -1 ? Colour.White.getHex() : colour;
} }
} }

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