mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-17 15:07:38 +00:00
Compare commits
15 Commits
v1.19.4-1.
...
v1.20.1-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3371c4651c | ||
![]() |
e6bc1e4e27 | ||
![]() |
ae50f900af | ||
![]() |
48889ceb89 | ||
![]() |
8f1bf4341c | ||
![]() |
9a48b53a83 | ||
![]() |
9519448e43 | ||
![]() |
915b6f9d81 | ||
![]() |
a98f3b2a4c | ||
![]() |
ebaf49508f | ||
![]() |
c45fc94752 | ||
![]() |
fd1f6dda32 | ||
![]() |
5d6389dc50 | ||
![]() |
1ece2aa23b | ||
![]() |
ff1e5f6823 |
@@ -6,7 +6,7 @@
|
|||||||
# See https://pre-commit.com/hooks.html for more hooks
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.4.0
|
rev: v4.0.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
@@ -20,14 +20,14 @@ repos:
|
|||||||
exclude: "tsconfig\\.json$"
|
exclude: "tsconfig\\.json$"
|
||||||
|
|
||||||
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
|
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
|
||||||
rev: 2.7.2
|
rev: 2.3.54
|
||||||
hooks:
|
hooks:
|
||||||
- id: editorconfig-checker
|
- id: editorconfig-checker
|
||||||
args: ['-disable-indentation']
|
args: ['-disable-indentation']
|
||||||
exclude: "^(.*\\.(bat)|LICENSE)$"
|
exclude: "^(.*\\.(bat)|LICENSE)$"
|
||||||
|
|
||||||
- repo: https://github.com/fsfe/reuse-tool
|
- repo: https://github.com/fsfe/reuse-tool
|
||||||
rev: v2.1.0
|
rev: v1.1.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: reuse
|
- id: reuse
|
||||||
|
|
||||||
|
@@ -66,7 +66,6 @@ repositories {
|
|||||||
includeGroup("me.shedaniel")
|
includeGroup("me.shedaniel")
|
||||||
includeGroup("mezz.jei")
|
includeGroup("mezz.jei")
|
||||||
includeModule("com.terraformersmc", "modmenu")
|
includeModule("com.terraformersmc", "modmenu")
|
||||||
includeModule("me.lucko", "fabric-permissions-api")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,11 +9,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`alarm`] event is fired when an alarm started with [`os.setAlarm`] completes.
|
The @{alarm} event is fired when an alarm started with @{os.setAlarm} completes.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`number`]: The ID of the alarm that finished.
|
2. @{number}: The ID of the alarm that finished.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Starts a timer and then waits for it to complete.
|
Starts a timer and then waits for it to complete.
|
||||||
|
@@ -9,15 +9,15 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: LicenseRef-CCPL
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`char`] event is fired when a character is typed on the keyboard.
|
The @{char} event is fired when a character is typed on the keyboard.
|
||||||
|
|
||||||
The [`char`] event is different to a key press. Sometimes multiple key presses may result in one character being
|
The @{char} event is different to a key press. Sometimes multiple key presses may result in one character being
|
||||||
typed (for instance, on some European keyboards). Similarly, some keys (e.g. <kbd>Ctrl</kbd>) do not have any
|
typed (for instance, on some European keyboards). Similarly, some keys (e.g. <kbd>Ctrl</kbd>) do not have any
|
||||||
corresponding character. The [`key`] should be used if you want to listen to key presses themselves.
|
corresponding character. The @{key} should be used if you want to listen to key presses themselves.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The string representing the character that was pressed.
|
2. @{string}: The string representing the character that was pressed.
|
||||||
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
@@ -8,11 +8,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`computer_command`] event is fired when the `/computercraft queue` command is run for the current computer.
|
The @{computer_command} event is fired when the `/computercraft queue` command is run for the current computer.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]<abbr title="Variable number of arguments">…</abbr>: The arguments passed to the command.
|
2. @{string}<abbr title="Variable number of arguments">…</abbr>: The arguments passed to the command.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints the contents of messages sent:
|
Prints the contents of messages sent:
|
||||||
|
@@ -9,11 +9,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`disk`] event is fired when a disk is inserted into an adjacent or networked disk drive.
|
The @{disk} event is fired when a disk is inserted into an adjacent or networked disk drive.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The side of the disk drive that had a disk inserted.
|
2. @{string}: The side of the disk drive that had a disk inserted.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a disk is inserted:
|
Prints a message when a disk is inserted:
|
||||||
|
@@ -9,11 +9,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`disk_eject`] event is fired when a disk is removed from an adjacent or networked disk drive.
|
The @{disk_eject} event is fired when a disk is removed from an adjacent or networked disk drive.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The side of the disk drive that had a disk removed.
|
2. @{string}: The side of the disk drive that had a disk removed.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a disk is removed:
|
Prints a message when a disk is removed:
|
||||||
|
@@ -9,15 +9,15 @@ SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`file_transfer`] event is queued when a user drags-and-drops a file on an open computer.
|
The @{file_transfer} event is queued when a user drags-and-drops a file on an open computer.
|
||||||
|
|
||||||
This event contains a single argument of type [`TransferredFiles`], which can be used to [get the files to be
|
This event contains a single argument of type @{TransferredFiles}, which can be used to @{TransferredFiles.getFiles|get
|
||||||
transferred][`TransferredFiles.getFiles`]. Each file returned is a [binary file handle][`fs.BinaryReadHandle`] with an
|
the files to be transferred}. Each file returned is a @{fs.BinaryReadHandle|binary file handle} with an additional
|
||||||
additional [getName][`TransferredFile.getName`] method.
|
@{TransferredFile.getName|getName} method.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. [`string`]: The event name
|
1. @{string}: The event name
|
||||||
2. [`TransferredFiles`]: The list of transferred files.
|
2. @{TransferredFiles}: The list of transferred files.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Waits for a user to drop files on top of the computer, then prints the list of files and the size of each file.
|
Waits for a user to drop files on top of the computer, then prints the list of files and the size of each file.
|
||||||
|
@@ -9,12 +9,12 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`http_check`] event is fired when a URL check finishes.
|
The @{http_check} event is fired when a URL check finishes.
|
||||||
|
|
||||||
This event is normally handled inside [`http.checkURL`], but it can still be seen when using [`http.checkURLAsync`].
|
This event is normally handled inside @{http.checkURL}, but it can still be seen when using @{http.checkURLAsync}.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The URL requested to be checked.
|
2. @{string}: The URL requested to be checked.
|
||||||
3. [`boolean`]: Whether the check succeeded.
|
3. @{boolean}: Whether the check succeeded.
|
||||||
4. <span class="type">[`string`]|[`nil`]</span>: If the check failed, a reason explaining why the check failed.
|
4. <span class="type">@{string}|@{nil}</span>: If the check failed, a reason explaining why the check failed.
|
||||||
|
@@ -9,15 +9,15 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`http_failure`] event is fired when an HTTP request fails.
|
The @{http_failure} event is fired when an HTTP request fails.
|
||||||
|
|
||||||
This event is normally handled inside [`http.get`] and [`http.post`], but it can still be seen when using [`http.request`].
|
This event is normally handled inside @{http.get} and @{http.post}, but it can still be seen when using @{http.request}.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The URL of the site requested.
|
2. @{string}: The URL of the site requested.
|
||||||
3. [`string`]: An error describing the failure.
|
3. @{string}: An error describing the failure.
|
||||||
4. <span class="type">[`http.Response`]|[`nil`]</span>: A response handle if the connection succeeded, but the server's
|
4. <span class="type">@{http.Response}|@{nil}</span>: A response handle if the connection succeeded, but the server's
|
||||||
response indicated failure.
|
response indicated failure.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
@@ -9,14 +9,14 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`http_success`] event is fired when an HTTP request returns successfully.
|
The @{http_success} event is fired when an HTTP request returns successfully.
|
||||||
|
|
||||||
This event is normally handled inside [`http.get`] and [`http.post`], but it can still be seen when using [`http.request`].
|
This event is normally handled inside @{http.get} and @{http.post}, but it can still be seen when using @{http.request}.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The URL of the site requested.
|
2. @{string}: The URL of the site requested.
|
||||||
3. [`http.Response`]: The successful HTTP response.
|
3. @{http.Response}: The successful HTTP response.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints the content of a website (this may fail if the request fails):
|
Prints the content of a website (this may fail if the request fails):
|
||||||
|
@@ -11,15 +11,15 @@ SPDX-License-Identifier: LicenseRef-CCPL
|
|||||||
This event is fired when any key is pressed while the terminal is focused.
|
This event is fired when any key is pressed while the terminal is focused.
|
||||||
|
|
||||||
This event returns a numerical "key code" (for instance, <kbd>F1</kbd> is 290). This value may vary between versions and
|
This event returns a numerical "key code" (for instance, <kbd>F1</kbd> is 290). This value may vary between versions and
|
||||||
so it is recommended to use the constants in the [`keys`] API rather than hard coding numeric values.
|
so it is recommended to use the constants in the @{keys} API rather than hard coding numeric values.
|
||||||
|
|
||||||
If the button pressed represented a printable character, then the [`key`] event will be followed immediately by a [`char`]
|
If the button pressed represented a printable character, then the @{key} event will be followed immediately by a @{char}
|
||||||
event. If you are consuming text input, use a [`char`] event instead!
|
event. If you are consuming text input, use a @{char} event instead!
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`number`]: The numerical key value of the key pressed.
|
2. @{number}: The numerical key value of the key pressed.
|
||||||
3. [`boolean`]: Whether the key event was generated while holding the key ([`true`]), rather than pressing it the first time ([`false`]).
|
3. @{boolean}: Whether the key event was generated while holding the key (@{true}), rather than pressing it the first time (@{false}).
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints each key when the user presses it, and if the key is being held.
|
Prints each key when the user presses it, and if the key is being held.
|
||||||
|
@@ -12,14 +12,14 @@ SPDX-License-Identifier: LicenseRef-CCPL
|
|||||||
Fired whenever a key is released (or the terminal is closed while a key was being pressed).
|
Fired whenever a key is released (or the terminal is closed while a key was being pressed).
|
||||||
|
|
||||||
This event returns a numerical "key code" (for instance, <kbd>F1</kbd> is 290). This value may vary between versions and
|
This event returns a numerical "key code" (for instance, <kbd>F1</kbd> is 290). This value may vary between versions and
|
||||||
so it is recommended to use the constants in the [`keys`] API rather than hard coding numeric values.
|
so it is recommended to use the constants in the @{keys} API rather than hard coding numeric values.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`number`]: The numerical key value of the key pressed.
|
2. @{number}: The numerical key value of the key pressed.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints each key released on the keyboard whenever a [`key_up`] event is fired.
|
Prints each key released on the keyboard whenever a @{key_up} event is fired.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
while true do
|
while true do
|
||||||
|
@@ -8,18 +8,18 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`modem_message`] event is fired when a message is received on an open channel on any [`modem`].
|
The @{modem_message} event is fired when a message is received on an open channel on any @{modem}.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The side of the modem that received the message.
|
2. @{string}: The side of the modem that received the message.
|
||||||
3. [`number`]: The channel that the message was sent on.
|
3. @{number}: The channel that the message was sent on.
|
||||||
4. [`number`]: The reply channel set by the sender.
|
4. @{number}: The reply channel set by the sender.
|
||||||
5. [`any`]: The message as sent by the sender.
|
5. @{any}: The message as sent by the sender.
|
||||||
6. <span class="type">[`number`]|[`nil`]</span>: The distance between the sender and the receiver in blocks, or [`nil`] if the message was sent between dimensions.
|
6. <span class="type">@{number}|@{nil}</span>: The distance between the sender and the receiver in blocks, or @{nil} if the message was sent between dimensions.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Wraps a [`modem`] peripheral, opens channel 0 for listening, and prints all received messages.
|
Wraps a @{modem} peripheral, opens channel 0 for listening, and prints all received messages.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local modem = peripheral.find("modem") or error("No modem attached", 0)
|
local modem = peripheral.find("modem") or error("No modem attached", 0)
|
||||||
|
@@ -8,11 +8,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`monitor_resize`] event is fired when an adjacent or networked monitor's size is changed.
|
The @{monitor_resize} event is fired when an adjacent or networked monitor's size is changed.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The side or network ID of the monitor that was resized.
|
2. @{string}: The side or network ID of the monitor that was resized.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a monitor is resized:
|
Prints a message when a monitor is resized:
|
||||||
|
@@ -8,13 +8,13 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`monitor_touch`] event is fired when an adjacent or networked Advanced Monitor is right-clicked.
|
The @{monitor_touch} event is fired when an adjacent or networked Advanced Monitor is right-clicked.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The side or network ID of the monitor that was touched.
|
2. @{string}: The side or network ID of the monitor that was touched.
|
||||||
3. [`number`]: The X coordinate of the touch, in characters.
|
3. @{number}: The X coordinate of the touch, in characters.
|
||||||
4. [`number`]: The Y coordinate of the touch, in characters.
|
4. @{number}: The Y coordinate of the touch, in characters.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a monitor is touched:
|
Prints a message when a monitor is touched:
|
||||||
|
@@ -12,13 +12,13 @@ This event is fired when the terminal is clicked with a mouse. This event is onl
|
|||||||
advanced turtles and pocket computers).
|
advanced turtles and pocket computers).
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`number`]: The mouse button that was clicked.
|
2. @{number}: The mouse button that was clicked.
|
||||||
3. [`number`]: The X-coordinate of the click.
|
3. @{number}: The X-coordinate of the click.
|
||||||
4. [`number`]: The Y-coordinate of the click.
|
4. @{number}: The Y-coordinate of the click.
|
||||||
|
|
||||||
## Mouse buttons
|
## Mouse buttons
|
||||||
Several mouse events ([`mouse_click`], [`mouse_up`], [`mouse_scroll`]) contain a "mouse button" code. This takes a
|
Several mouse events (@{mouse_click}, @{mouse_up}, @{mouse_scroll}) contain a "mouse button" code. This takes a
|
||||||
numerical value depending on which button on your mouse was last pressed when this event occurred.
|
numerical value depending on which button on your mouse was last pressed when this event occurred.
|
||||||
|
|
||||||
| Button Code | Mouse Button |
|
| Button Code | Mouse Button |
|
||||||
|
@@ -12,10 +12,10 @@ SPDX-License-Identifier: LicenseRef-CCPL
|
|||||||
This event is fired every time the mouse is moved while a mouse button is being held.
|
This event is fired every time the mouse is moved while a mouse button is being held.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`number`]: The [mouse button](mouse_click.html#Mouse_buttons) that is being pressed.
|
2. @{number}: The [mouse button](mouse_click.html#Mouse_buttons) that is being pressed.
|
||||||
3. [`number`]: The X-coordinate of the mouse.
|
3. @{number}: The X-coordinate of the mouse.
|
||||||
4. [`number`]: The Y-coordinate of the mouse.
|
4. @{number}: The Y-coordinate of the mouse.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Print the button and the coordinates whenever the mouse is dragged.
|
Print the button and the coordinates whenever the mouse is dragged.
|
||||||
|
@@ -11,10 +11,10 @@ SPDX-License-Identifier: LicenseRef-CCPL
|
|||||||
This event is fired when a mouse wheel is scrolled in the terminal.
|
This event is fired when a mouse wheel is scrolled in the terminal.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`number`]: The direction of the scroll. (-1 = up, 1 = down)
|
2. @{number}: The direction of the scroll. (-1 = up, 1 = down)
|
||||||
3. [`number`]: The X-coordinate of the mouse when scrolling.
|
3. @{number}: The X-coordinate of the mouse when scrolling.
|
||||||
4. [`number`]: The Y-coordinate of the mouse when scrolling.
|
4. @{number}: The Y-coordinate of the mouse when scrolling.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints the direction of each scroll, and the position of the mouse at the time.
|
Prints the direction of each scroll, and the position of the mouse at the time.
|
||||||
|
@@ -11,10 +11,10 @@ SPDX-License-Identifier: LicenseRef-CCPL
|
|||||||
This event is fired when a mouse button is released or a held mouse leaves the computer's terminal.
|
This event is fired when a mouse button is released or a held mouse leaves the computer's terminal.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`number`]: The [mouse button](mouse_click.html#Mouse_buttons) that was released.
|
2. @{number}: The [mouse button](mouse_click.html#Mouse_buttons) that was released.
|
||||||
3. [`number`]: The X-coordinate of the mouse.
|
3. @{number}: The X-coordinate of the mouse.
|
||||||
4. [`number`]: The Y-coordinate of the mouse.
|
4. @{number}: The Y-coordinate of the mouse.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints the coordinates and button number whenever the mouse is released.
|
Prints the coordinates and button number whenever the mouse is released.
|
||||||
|
@@ -8,11 +8,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`paste`] event is fired when text is pasted into the computer through Ctrl-V (or ⌘V on Mac).
|
The @{paste} event is fired when text is pasted into the computer through Ctrl-V (or ⌘V on Mac).
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`] The text that was pasted.
|
2. @{string} The text that was pasted.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints pasted text:
|
Prints pasted text:
|
||||||
|
@@ -9,11 +9,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`peripheral`] event is fired when a peripheral is attached on a side or to a modem.
|
The @{peripheral} event is fired when a peripheral is attached on a side or to a modem.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The side the peripheral was attached to.
|
2. @{string}: The side the peripheral was attached to.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a peripheral is attached:
|
Prints a message when a peripheral is attached:
|
||||||
|
@@ -9,11 +9,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`peripheral_detach`] event is fired when a peripheral is detached from a side or from a modem.
|
The @{peripheral_detach} event is fired when a peripheral is detached from a side or from a modem.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The side the peripheral was detached from.
|
2. @{string}: The side the peripheral was detached from.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a peripheral is detached:
|
Prints a message when a peripheral is detached:
|
||||||
|
@@ -10,17 +10,17 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`rednet_message`] event is fired when a message is sent over Rednet.
|
The @{rednet_message} event is fired when a message is sent over Rednet.
|
||||||
|
|
||||||
This event is usually handled by [`rednet.receive`], but it can also be pulled manually.
|
This event is usually handled by @{rednet.receive}, but it can also be pulled manually.
|
||||||
|
|
||||||
[`rednet_message`] events are sent by [`rednet.run`] in the top-level coroutine in response to [`modem_message`] events. A [`rednet_message`] event is always preceded by a [`modem_message`] event. They are generated inside CraftOS rather than being sent by the ComputerCraft machine.
|
@{rednet_message} events are sent by @{rednet.run} in the top-level coroutine in response to @{modem_message} events. A @{rednet_message} event is always preceded by a @{modem_message} event. They are generated inside CraftOS rather than being sent by the ComputerCraft machine.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`number`]: The ID of the sending computer.
|
2. @{number}: The ID of the sending computer.
|
||||||
3. [`any`]: The message sent.
|
3. @{any}: The message sent.
|
||||||
4. <span class="type">[`string`]|[`nil`]</span>: The protocol of the message, if provided.
|
4. <span class="type">@{string}|@{nil}</span>: The protocol of the message, if provided.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when one is sent:
|
Prints a message when one is sent:
|
||||||
|
@@ -8,10 +8,10 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`event!redstone`] event is fired whenever any redstone inputs on the computer change.
|
The @{event!redstone} event is fired whenever any redstone inputs on the computer change.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a redstone input changes:
|
Prints a message when a redstone input changes:
|
||||||
|
@@ -10,13 +10,13 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The name of the speaker which is available to play more audio.
|
2. @{string}: The name of the speaker which is available to play more audio.
|
||||||
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
This uses [`io.lines`] to read audio data in blocks of 16KiB from "example_song.dfpwm", and then attempts to play it
|
This uses @{io.lines} to read audio data in blocks of 16KiB from "example_song.dfpwm", and then attempts to play it
|
||||||
using [`speaker.playAudio`]. If the speaker's buffer is full, it waits for an event and tries again.
|
using @{speaker.playAudio}. If the speaker's buffer is full, it waits for an event and tries again.
|
||||||
|
|
||||||
```lua {data-peripheral=speaker}
|
```lua {data-peripheral=speaker}
|
||||||
local dfpwm = require("cc.audio.dfpwm")
|
local dfpwm = require("cc.audio.dfpwm")
|
||||||
|
@@ -9,13 +9,13 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`task_complete`] event is fired when an asynchronous task completes. This is usually handled inside the function call that queued the task; however, functions such as [`commands.execAsync`] return immediately so the user can wait for completion.
|
The @{task_complete} event is fired when an asynchronous task completes. This is usually handled inside the function call that queued the task; however, functions such as @{commands.execAsync} return immediately so the user can wait for completion.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`number`]: The ID of the task that completed.
|
2. @{number}: The ID of the task that completed.
|
||||||
3. [`boolean`]: Whether the command succeeded.
|
3. @{boolean}: Whether the command succeeded.
|
||||||
4. [`string`]: If the command failed, an error message explaining the failure. (This is not present if the command succeeded.)
|
4. @{string}: If the command failed, an error message explaining the failure. (This is not present if the command succeeded.)
|
||||||
5. <abbr title="Variable number of arguments">…</abbr>: Any parameters returned from the command.
|
5. <abbr title="Variable number of arguments">…</abbr>: Any parameters returned from the command.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
@@ -8,15 +8,15 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`term_resize`] event is fired when the main terminal is resized. For instance:
|
The @{term_resize} event is fired when the main terminal is resized. For instance:
|
||||||
- When a the tab bar is shown or hidden in [`multishell`].
|
- When a the tab bar is shown or hidden in @{multishell}.
|
||||||
- When the terminal is redirected to a monitor via the "monitor" program and the monitor is resized.
|
- When the terminal is redirected to a monitor via the "monitor" program and the monitor is resized.
|
||||||
|
|
||||||
When this event fires, some parts of the terminal may have been moved or deleted. Simple terminal programs (those
|
When this event fires, some parts of the terminal may have been moved or deleted. Simple terminal programs (those
|
||||||
not using [`term.setCursorPos`]) can ignore this event, but more complex GUI programs should redraw the entire screen.
|
not using @{term.setCursorPos}) can ignore this event, but more complex GUI programs should redraw the entire screen.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Print a message each time the terminal is resized.
|
Print a message each time the terminal is resized.
|
||||||
|
@@ -8,14 +8,14 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`terminate`] event is fired when <kbd>Ctrl-T</kbd> is held down.
|
The @{terminate} event is fired when <kbd>Ctrl-T</kbd> is held down.
|
||||||
|
|
||||||
This event is normally handled by [`os.pullEvent`], and will not be returned. However, [`os.pullEventRaw`] will return this event when fired.
|
This event is normally handled by @{os.pullEvent}, and will not be returned. However, @{os.pullEventRaw} will return this event when fired.
|
||||||
|
|
||||||
[`terminate`] will be sent even when a filter is provided to [`os.pullEventRaw`]. When using [`os.pullEventRaw`] with a filter, make sure to check that the event is not [`terminate`].
|
@{terminate} will be sent even when a filter is provided to @{os.pullEventRaw}. When using @{os.pullEventRaw} with a filter, make sure to check that the event is not @{terminate}.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when Ctrl-T is held:
|
Prints a message when Ctrl-T is held:
|
||||||
|
@@ -9,11 +9,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`timer`] event is fired when a timer started with [`os.startTimer`] completes.
|
The @{timer} event is fired when a timer started with @{os.startTimer} completes.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`number`]: The ID of the timer that finished.
|
2. @{number}: The ID of the timer that finished.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Start and wait for a timer to finish.
|
Start and wait for a timer to finish.
|
||||||
|
@@ -8,10 +8,10 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`turtle_inventory`] event is fired when a turtle's inventory is changed.
|
The @{turtle_inventory} event is fired when a turtle's inventory is changed.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when the inventory is changed:
|
Prints a message when the inventory is changed:
|
||||||
|
@@ -8,16 +8,16 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`websocket_closed`] event is fired when an open WebSocket connection is closed.
|
The @{websocket_closed} event is fired when an open WebSocket connection is closed.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The URL of the WebSocket that was closed.
|
2. @{string}: The URL of the WebSocket that was closed.
|
||||||
3. <span class="type">[`string`]|[`nil`]</span>: The [server-provided reason][close_reason]
|
3. <span class="type">@{string}|@{nil}</span>: The [server-provided reason][close_reason]
|
||||||
the websocket was closed. This will be [`nil`] if the connection was closed
|
the websocket was closed. This will be @{nil} if the connection was closed
|
||||||
abnormally.
|
abnormally.
|
||||||
4. <span class="type">[`number`]|[`nil`]</span>: The [connection close code][close_code],
|
4. <span class="type">@{number}|@{nil}</span>: The [connection close code][close_code],
|
||||||
indicating why the socket was closed. This will be [`nil`] if the connection
|
indicating why the socket was closed. This will be @{nil} if the connection
|
||||||
was closed abnormally.
|
was closed abnormally.
|
||||||
|
|
||||||
[close_reason]: https://www.rfc-editor.org/rfc/rfc6455.html#section-7.1.6 "The WebSocket Connection Close Reason, RFC 6455"
|
[close_reason]: https://www.rfc-editor.org/rfc/rfc6455.html#section-7.1.6 "The WebSocket Connection Close Reason, RFC 6455"
|
||||||
|
@@ -9,14 +9,14 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`websocket_failure`] event is fired when a WebSocket connection request fails.
|
The @{websocket_failure} event is fired when a WebSocket connection request fails.
|
||||||
|
|
||||||
This event is normally handled inside [`http.websocket`], but it can still be seen when using [`http.websocketAsync`].
|
This event is normally handled inside @{http.websocket}, but it can still be seen when using @{http.websocketAsync}.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The URL of the site requested.
|
2. @{string}: The URL of the site requested.
|
||||||
3. [`string`]: An error describing the failure.
|
3. @{string}: An error describing the failure.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints an error why the website cannot be contacted:
|
Prints an error why the website cannot be contacted:
|
||||||
|
@@ -8,15 +8,15 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`websocket_message`] event is fired when a message is received on an open WebSocket connection.
|
The @{websocket_message} event is fired when a message is received on an open WebSocket connection.
|
||||||
|
|
||||||
This event is normally handled by [`http.Websocket.receive`], but it can also be pulled manually.
|
This event is normally handled by @{http.Websocket.receive}, but it can also be pulled manually.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The URL of the WebSocket.
|
2. @{string}: The URL of the WebSocket.
|
||||||
3. [`string`]: The contents of the message.
|
3. @{string}: The contents of the message.
|
||||||
4. [`boolean`]: Whether this is a binary message.
|
4. @{boolean}: Whether this is a binary message.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message sent by a WebSocket:
|
Prints a message sent by a WebSocket:
|
||||||
|
@@ -9,14 +9,14 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`websocket_success`] event is fired when a WebSocket connection request returns successfully.
|
The @{websocket_success} event is fired when a WebSocket connection request returns successfully.
|
||||||
|
|
||||||
This event is normally handled inside [`http.websocket`], but it can still be seen when using [`http.websocketAsync`].
|
This event is normally handled inside @{http.websocket}, but it can still be seen when using @{http.websocketAsync}.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. @{string}: The event name.
|
||||||
2. [`string`]: The URL of the site.
|
2. @{string}: The URL of the site.
|
||||||
3. [`http.Websocket`]: The handle for the WebSocket.
|
3. @{http.Websocket}: The handle for the WebSocket.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints the content of a website (this may fail if the request fails):
|
Prints the content of a website (this may fail if the request fails):
|
||||||
|
@@ -9,7 +9,7 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
# Setting up GPS
|
# Setting up GPS
|
||||||
The [`gps`] API allows computers and turtles to find their current position using wireless modems.
|
The @{gps} API allows computers and turtles to find their current position using wireless modems.
|
||||||
|
|
||||||
In order to use GPS, you'll need to set up multiple *GPS hosts*. These are computers running the special `gps host`
|
In order to use GPS, you'll need to set up multiple *GPS hosts*. These are computers running the special `gps host`
|
||||||
program, which tell other computers the host's position. Several hosts running together are known as a *GPS
|
program, which tell other computers the host's position. Several hosts running together are known as a *GPS
|
||||||
@@ -19,21 +19,22 @@ In order to give the best results, a GPS constellation needs at least four compu
|
|||||||
constellation is redundant, but it does not cause problems.
|
constellation is redundant, but it does not cause problems.
|
||||||
|
|
||||||
## Building a GPS constellation
|
## Building a GPS constellation
|
||||||
<img alt="An example GPS constellation." src="/images/gps-constellation-example.png" class="big-image" />
|
{.big-image}
|
||||||
|
|
||||||
We are going to build our GPS constellation as shown in the image above. You will need 4 computers and either 4 wireless
|
We are going to build our GPS constellation as shown in the image above. You will need 4 computers and either 4 wireless
|
||||||
modems or 4 ender modems. Try not to mix ender and wireless modems together as you might get some odd behavior when your
|
modems or 4 ender modems. Try not to mix ender and wireless modems together as you might get some odd behavior when your
|
||||||
requesting computers are out of range.
|
requesting computers are out of range.
|
||||||
|
|
||||||
> [Ender modems vs wireless modems][!TIP]
|
:::tip Ender modems vs wireless modems
|
||||||
> Ender modems have a very large range, which makes them very useful for setting up GPS hosts. If you do this then you
|
Ender modems have a very large range, which makes them very useful for setting up GPS hosts. If you do this then you
|
||||||
> will likely only need one GPS constellation for the whole dimension (such as the Overworld or Nether).
|
will likely only need one GPS constellation for the whole dimension (such as the Overworld or Nether).
|
||||||
>
|
|
||||||
> If you do use wireless modems then you may find that you need multiple GPS constellations to cover your needs.
|
If you do use wireless modems then you may find that you need multiple GPS constellations to cover your needs.
|
||||||
>
|
|
||||||
> A computer needs a wireless or ender modem and to be in range of a GPS constellation that is in the same dimension as
|
A computer needs a wireless or ender modem and to be in range of a GPS constellation that is in the same dimension as it
|
||||||
> it to use the GPS API. The reason for this is that ComputerCraft mimics real-life GPS by making use of the distance
|
to use the GPS API. The reason for this is that ComputerCraft mimics real-life GPS by making use of the distance
|
||||||
> parameter of [modem messages][`modem_message`] and some maths.
|
parameter of @{modem_message|modem messages} and some maths.
|
||||||
|
:::
|
||||||
|
|
||||||
Locate where you want to place your GPS constellation. You will need an area at least 6 blocks high, 6 blocks wide, and
|
Locate where you want to place your GPS constellation. You will need an area at least 6 blocks high, 6 blocks wide, and
|
||||||
6 blocks deep (6x6x6). If you are using wireless modems then you may want to build your constellation as high as you can
|
6 blocks deep (6x6x6). If you are using wireless modems then you may want to build your constellation as high as you can
|
||||||
@@ -78,16 +79,18 @@ To hide Minecraft's debug screen, press <kbd>F3</kbd> again.
|
|||||||
Create similar startup files for the other computers in your constellation, making sure to input the each computer's own
|
Create similar startup files for the other computers in your constellation, making sure to input the each computer's own
|
||||||
coordinates.
|
coordinates.
|
||||||
|
|
||||||
> [Modem messages come from the computer's position, not the modem's][!WARNING]
|
:::caution Modem messages come from the computer's position, not the modem's
|
||||||
> Wireless modems transmit from the block that they are attached to *not* the block space that they occupy, the
|
Wireless modems transmit from the block that they are attached to *not* the block space that they occupy, the
|
||||||
> coordinates that you input into your GPS host should be the position of the computer and not the position of the modem.
|
coordinates that you input into your GPS host should be the position of the computer and not the position of the modem.
|
||||||
|
:::
|
||||||
|
|
||||||
Congratulations, your constellation is now fully set up! You can test it by placing another computer close by, placing a
|
Congratulations, your constellation is now fully set up! You can test it by placing another computer close by, placing a
|
||||||
wireless modem on it, and running the `gps locate` program (or calling the [`gps.locate`] function).
|
wireless modem on it, and running the `gps locate` program (or calling the @{gps.locate} function).
|
||||||
|
|
||||||
> [Why use Minecraft's coordinates?][!INFO]
|
:::info Why use Minecraft's coordinates?
|
||||||
> CC doesn't care if you use Minecraft's coordinate system, so long as all of the GPS hosts with overlapping ranges use
|
CC doesn't care if you use Minecraft's coordinate system, so long as all of the GPS hosts with overlapping ranges use
|
||||||
> the same reference point (requesting computers will get confused if hosts have different reference points). However,
|
the same reference point (requesting computers will get confused if hosts have different reference points). However,
|
||||||
> using MC's coordinate system does provide a nice standard to adopt server-wide. It also is consistent with how command
|
using MC's coordinate system does provide a nice standard to adopt server-wide. It also is consistent with how command
|
||||||
> computers get their location, they use MC's command system to get their block which returns that in MC's coordinate
|
computers get their location, they use MC's command system to get their block which returns that in MC's coordinate
|
||||||
> system.
|
system.
|
||||||
|
:::
|
||||||
|
@@ -11,7 +11,7 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
# Playing audio with speakers
|
# Playing audio with speakers
|
||||||
CC: Tweaked's speaker peripheral provides a powerful way to play any audio you like with the [`speaker.playAudio`]
|
CC: Tweaked's speaker peripheral provides a powerful way to play any audio you like with the @{speaker.playAudio}
|
||||||
method. However, for people unfamiliar with digital audio, it's not the most intuitive thing to use. This guide provides
|
method. However, for people unfamiliar with digital audio, it's not the most intuitive thing to use. This guide provides
|
||||||
an introduction to digital audio, demonstrates how to play music with CC: Tweaked's speakers, and then briefly discusses
|
an introduction to digital audio, demonstrates how to play music with CC: Tweaked's speakers, and then briefly discusses
|
||||||
the more complex topic of audio processing.
|
the more complex topic of audio processing.
|
||||||
@@ -60,7 +60,7 @@ sine waves (and why wouldn't you?), you'd need a table with almost 3 _million_.
|
|||||||
up very quickly, and these tables take up more and more memory.
|
up very quickly, and these tables take up more and more memory.
|
||||||
|
|
||||||
Instead of building our entire song (well, sine wave) in one go, we can produce it in small batches, each of which get
|
Instead of building our entire song (well, sine wave) in one go, we can produce it in small batches, each of which get
|
||||||
passed off to [`speaker.playAudio`] when the time is right. This allows us to build a _stream_ of audio, where we read
|
passed off to @{speaker.playAudio} when the time is right. This allows us to build a _stream_ of audio, where we read
|
||||||
chunks of audio one at a time (either from a file or a tone generator like above), do some optional processing to each
|
chunks of audio one at a time (either from a file or a tone generator like above), do some optional processing to each
|
||||||
one, and then play them.
|
one, and then play them.
|
||||||
|
|
||||||
@@ -84,15 +84,15 @@ end
|
|||||||
```
|
```
|
||||||
|
|
||||||
It looks pretty similar to before, aside from we've wrapped the generation and playing code in a while loop, and added a
|
It looks pretty similar to before, aside from we've wrapped the generation and playing code in a while loop, and added a
|
||||||
rather odd loop with [`speaker.playAudio`] and [`os.pullEvent`].
|
rather odd loop with @{speaker.playAudio} and @{os.pullEvent}.
|
||||||
|
|
||||||
Let's talk about this loop, why do we need to keep calling [`speaker.playAudio`]? Remember that what we're trying to do
|
Let's talk about this loop, why do we need to keep calling @{speaker.playAudio}? Remember that what we're trying to do
|
||||||
here is avoid keeping too much audio in memory at once. However, if we're generating audio quicker than the speakers can
|
here is avoid keeping too much audio in memory at once. However, if we're generating audio quicker than the speakers can
|
||||||
play it, we're not helping at all - all this audio is still hanging around waiting to be played!
|
play it, we're not helping at all - all this audio is still hanging around waiting to be played!
|
||||||
|
|
||||||
In order to avoid this, the speaker rejects any new chunks of audio if its backlog is too large. When this happens,
|
In order to avoid this, the speaker rejects any new chunks of audio if its backlog is too large. When this happens,
|
||||||
[`speaker.playAudio`] returns false. Once enough audio has played, and the backlog has been reduced, a
|
@{speaker.playAudio} returns false. Once enough audio has played, and the backlog has been reduced, a
|
||||||
[`speaker_audio_empty`] event is queued, and we can try to play our chunk once more.
|
@{speaker_audio_empty} event is queued, and we can try to play our chunk once more.
|
||||||
|
|
||||||
## Storing audio
|
## Storing audio
|
||||||
PCM is a fantastic way of representing audio when we want to manipulate it, but it's not very efficient when we want to
|
PCM is a fantastic way of representing audio when we want to manipulate it, but it's not very efficient when we want to
|
||||||
@@ -106,7 +106,7 @@ computer. Instead, we need something much simpler.
|
|||||||
|
|
||||||
DFPWM (Dynamic Filter Pulse Width Modulation) is the de facto standard audio format of the ComputerCraft (and
|
DFPWM (Dynamic Filter Pulse Width Modulation) is the de facto standard audio format of the ComputerCraft (and
|
||||||
OpenComputers) world. Originally popularised by the addon mod [Computronics], CC:T now has built-in support for it with
|
OpenComputers) world. Originally popularised by the addon mod [Computronics], CC:T now has built-in support for it with
|
||||||
the [`cc.audio.dfpwm`] module. This allows you to read DFPWM files from disk, decode them to PCM, and then play them
|
the @{cc.audio.dfpwm} module. This allows you to read DFPWM files from disk, decode them to PCM, and then play them
|
||||||
using the speaker.
|
using the speaker.
|
||||||
|
|
||||||
Let's dive in with an example, and we'll explain things afterwards:
|
Let's dive in with an example, and we'll explain things afterwards:
|
||||||
@@ -125,16 +125,16 @@ for chunk in io.lines("data/example.dfpwm", 16 * 1024) do
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
Once again, we see the [`speaker.playAudio`]/[`speaker_audio_empty`] loop. However, the rest of the program is a little
|
Once again, we see the @{speaker.playAudio}/@{speaker_audio_empty} loop. However, the rest of the program is a little
|
||||||
different.
|
different.
|
||||||
|
|
||||||
First, we require the dfpwm module and call [`cc.audio.dfpwm.make_decoder`] to construct a new decoder. This decoder
|
First, we require the dfpwm module and call @{cc.audio.dfpwm.make_decoder} to construct a new decoder. This decoder
|
||||||
accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker.
|
accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker.
|
||||||
|
|
||||||
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each
|
As mentioned above, @{speaker.playAudio} accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each
|
||||||
sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use
|
sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use
|
||||||
[`io.lines`], which provides a nice way to loop over chunks of a file. You can of course just use [`fs.open`] and
|
@{io.lines}, which provides a nice way to loop over chunks of a file. You can of course just use @{fs.open} and
|
||||||
[`fs.BinaryReadHandle.read`] if you prefer.
|
@{fs.BinaryReadHandle.read} if you prefer.
|
||||||
|
|
||||||
## Processing audio
|
## Processing audio
|
||||||
As mentioned near the beginning of this guide, PCM audio is pretty easy to work with as it's just a list of amplitudes.
|
As mentioned near the beginning of this guide, PCM audio is pretty easy to work with as it's just a list of amplitudes.
|
||||||
@@ -189,9 +189,10 @@ for chunk in io.lines("data/example.dfpwm", 16 * 1024) do
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
> [Confused?][!NOTE]
|
:::note Confused?
|
||||||
> Don't worry if you don't understand this example. It's quite advanced, and does use some ideas that this guide doesn't
|
Don't worry if you don't understand this example. It's quite advanced, and does use some ideas that this guide doesn't
|
||||||
> cover. That said, don't be afraid to ask on [GitHub Discussions] or [IRC] either!
|
cover. That said, don't be afraid to ask on [GitHub Discussions] or [IRC] either!
|
||||||
|
:::
|
||||||
|
|
||||||
It's worth noting that the examples of audio processing we've mentioned here are about manipulating the _amplitude_ of
|
It's worth noting that the examples of audio processing we've mentioned here are about manipulating the _amplitude_ of
|
||||||
the wave. If you wanted to modify the _frequency_ (for instance, shifting the pitch), things get rather more complex.
|
the wave. If you wanted to modify the _frequency_ (for instance, shifting the pitch), things get rather more complex.
|
||||||
|
@@ -13,7 +13,7 @@ A library is a collection of useful functions and other definitions which is sto
|
|||||||
might want to create a library because you have some functions which are used in multiple programs, or just to split
|
might want to create a library because you have some functions which are used in multiple programs, or just to split
|
||||||
your program into multiple more modular files.
|
your program into multiple more modular files.
|
||||||
|
|
||||||
Let's say we want to create a small library to make working with the [terminal][`term`] a little easier. We'll provide two
|
Let's say we want to create a small library to make working with the @{term|terminal} a little easier. We'll provide two
|
||||||
functions: `reset`, which clears the terminal and sets the cursor to (1, 1), and `write_center`, which prints some text
|
functions: `reset`, which clears the terminal and sets the cursor to (1, 1), and `write_center`, which prints some text
|
||||||
in the middle of the screen.
|
in the middle of the screen.
|
||||||
|
|
||||||
@@ -48,32 +48,32 @@ more_term.write_center("Hello, world!")
|
|||||||
When run, this'll clear the screen and print some text in the middle of the first line.
|
When run, this'll clear the screen and print some text in the middle of the first line.
|
||||||
|
|
||||||
## require in depth
|
## require in depth
|
||||||
While the previous section is a good introduction to how [`require`] operates, there are a couple of remaining points
|
While the previous section is a good introduction to how @{require} operates, there are a couple of remaining points
|
||||||
which are worth mentioning for more advanced usage.
|
which are worth mentioning for more advanced usage.
|
||||||
|
|
||||||
### Libraries can return anything
|
### Libraries can return anything
|
||||||
In our above example, we return a table containing the functions we want to expose. However, it's worth pointing out
|
In our above example, we return a table containing the functions we want to expose. However, it's worth pointing out
|
||||||
that you can return ''anything'' from your library - a table, a function or even just a string! [`require`] treats them
|
that you can return ''anything'' from your library - a table, a function or even just a string! @{require} treats them
|
||||||
all the same, and just returns whatever your library provides.
|
all the same, and just returns whatever your library provides.
|
||||||
|
|
||||||
### Module resolution and the package path
|
### Module resolution and the package path
|
||||||
In the above examples, we defined our library in a file, and [`require`] read from it. While this is what you'll do most
|
In the above examples, we defined our library in a file, and @{require} read from it. While this is what you'll do most
|
||||||
of the time, it is possible to make [`require`] look elsewhere for your library, such as downloading from a website or
|
of the time, it is possible to make @{require} look elsewhere for your library, such as downloading from a website or
|
||||||
loading from an in-memory library store.
|
loading from an in-memory library store.
|
||||||
|
|
||||||
As a result, the *module name* you pass to [`require`] doesn't correspond to a file path. One common mistake is to load
|
As a result, the *module name* you pass to @{require} doesn't correspond to a file path. One common mistake is to load
|
||||||
code from a sub-directory using `require("folder/library")` or even `require("folder/library.lua")`, neither of which
|
code from a sub-directory using `require("folder/library")` or even `require("folder/library.lua")`, neither of which
|
||||||
will do quite what you expect.
|
will do quite what you expect.
|
||||||
|
|
||||||
When loading libraries (also referred to as *modules*) from files, [`require`] searches along the [*module
|
When loading libraries (also referred to as *modules*) from files, @{require} searches along the *@{package.path|module
|
||||||
path*][`package.path`]. By default, this looks something like:
|
path}*. By default, this looks something like:
|
||||||
|
|
||||||
* `?.lua`
|
* `?.lua`
|
||||||
* `?/init.lua`
|
* `?/init.lua`
|
||||||
* `/rom/modules/main/?.lua`
|
* `/rom/modules/main/?.lua`
|
||||||
* etc...
|
* etc...
|
||||||
|
|
||||||
When you call `require("my_library")`, [`require`] replaces the `?` in each element of the path with your module name, and
|
When you call `require("my_library")`, @{require} replaces the `?` in each element of the path with your module name, and
|
||||||
checks if the file exists. In this case, we'd look for `my_library.lua`, `my_library/init.lua`,
|
checks if the file exists. In this case, we'd look for `my_library.lua`, `my_library/init.lua`,
|
||||||
`/rom/modules/main/my_library.lua` and so on. Note that this works *relative to the current program*, so if your
|
`/rom/modules/main/my_library.lua` and so on. Note that this works *relative to the current program*, so if your
|
||||||
program is actually called `folder/program`, then we'll look for `folder/my_library.lua`, etc...
|
program is actually called `folder/program`, then we'll look for `folder/my_library.lua`, etc...
|
||||||
@@ -86,4 +86,4 @@ before we start looking for the library.
|
|||||||
There are several external resources which go into require in a little more detail:
|
There are several external resources which go into require in a little more detail:
|
||||||
|
|
||||||
- The [Lua Module tutorial](http://lua-users.org/wiki/ModulesTutorial) on the Lua wiki.
|
- The [Lua Module tutorial](http://lua-users.org/wiki/ModulesTutorial) on the Lua wiki.
|
||||||
- [Lua's manual section on `require`](https://www.lua.org/manual/5.1/manual.html#pdf-require).
|
- [Lua's manual section on @{require}](https://www.lua.org/manual/5.1/manual.html#pdf-require).
|
||||||
|
@@ -15,13 +15,13 @@ CC: Tweaked can be installed from [CurseForge] or [Modrinth]. It runs on both [M
|
|||||||
Controlled using the [Lua programming language][lua], CC: Tweaked's computers provides all the tools you need to start
|
Controlled using the [Lua programming language][lua], CC: Tweaked's computers provides all the tools you need to start
|
||||||
writing code and automating your Minecraft world.
|
writing code and automating your Minecraft world.
|
||||||
|
|
||||||
<img alt="A ComputerCraft terminal open and ready to be programmed." src="images/basic-terminal.png" class="big-image" />
|
{.big-image}
|
||||||
|
|
||||||
While computers are incredibly powerful, they're rather limited by their inability to move about. *Turtles* are the
|
While computers are incredibly powerful, they're rather limited by their inability to move about. *Turtles* are the
|
||||||
solution here. They can move about the world, placing and breaking blocks, swinging a sword to protect you from zombies,
|
solution here. They can move about the world, placing and breaking blocks, swinging a sword to protect you from zombies,
|
||||||
or whatever else you program them to!
|
or whatever else you program them to!
|
||||||
|
|
||||||
<img alt="A turtle tunneling in Minecraft." src="images/turtle.png" class="big-image" />
|
{.big-image}
|
||||||
|
|
||||||
Not all problems can be solved with a pickaxe though, and so CC: Tweaked also provides a bunch of additional peripherals
|
Not all problems can be solved with a pickaxe though, and so CC: Tweaked also provides a bunch of additional peripherals
|
||||||
for your computers. You can play a tune with speakers, display text or images on a monitor, connect all your
|
for your computers. You can play a tune with speakers, display text or images on a monitor, connect all your
|
||||||
@@ -30,7 +30,7 @@ computers together with modems, and much more.
|
|||||||
Computers can now also interact with inventories such as chests, allowing you to build complex inventory and item
|
Computers can now also interact with inventories such as chests, allowing you to build complex inventory and item
|
||||||
management systems.
|
management systems.
|
||||||
|
|
||||||
<img alt="A chest's contents being read by a computer and displayed on a monitor." src="images/peripherals.png" class="big-image" />
|
{.big-image}
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
While ComputerCraft is lovely for both experienced programmers and for people who have never coded before, it can be a
|
While ComputerCraft is lovely for both experienced programmers and for people who have never coded before, it can be a
|
||||||
|
@@ -13,20 +13,22 @@ include standard Lua functions.
|
|||||||
|
|
||||||
As it waits for a fixed amount of world ticks, `time` will automatically be
|
As it waits for a fixed amount of world ticks, `time` will automatically be
|
||||||
rounded up to the nearest multiple of 0.05 seconds. If you are using coroutines
|
rounded up to the nearest multiple of 0.05 seconds. If you are using coroutines
|
||||||
or the [parallel API][`parallel`], it will only pause execution of the current
|
or the @{parallel|parallel API}, it will only pause execution of the current
|
||||||
thread, not the whole program.
|
thread, not the whole program.
|
||||||
|
|
||||||
> [!TIP]
|
:::tip
|
||||||
> Because sleep internally uses timers, it is a function that yields. This means
|
Because sleep internally uses timers, it is a function that yields. This means
|
||||||
> that you can use it to prevent "Too long without yielding" errors. However, as
|
that you can use it to prevent "Too long without yielding" errors. However, as
|
||||||
> the minimum sleep time is 0.05 seconds, it will slow your program down.
|
the minimum sleep time is 0.05 seconds, it will slow your program down.
|
||||||
|
:::
|
||||||
|
|
||||||
> [!WARNING]
|
:::caution
|
||||||
> Internally, this function queues and waits for a timer event (using
|
Internally, this function queues and waits for a timer event (using
|
||||||
> [`os.startTimer`]), however it does not listen for any other events. This means
|
@{os.startTimer}), however it does not listen for any other events. This means
|
||||||
> that any event that occurs while sleeping will be entirely discarded. If you
|
that any event that occurs while sleeping will be entirely discarded. If you
|
||||||
> need to receive events while sleeping, consider using [timers][`os.startTimer`],
|
need to receive events while sleeping, consider using @{os.startTimer|timers},
|
||||||
> or the [parallel API][`parallel`].
|
or the @{parallel|parallel API}.
|
||||||
|
:::
|
||||||
|
|
||||||
@tparam number time The number of seconds to sleep for, rounded up to the
|
@tparam number time The number of seconds to sleep for, rounded up to the
|
||||||
nearest multiple of 0.05.
|
nearest multiple of 0.05.
|
||||||
@@ -114,7 +116,7 @@ function read(replaceChar, history, completeFn, default) end
|
|||||||
|
|
||||||
--- Stores the current ComputerCraft and Minecraft versions.
|
--- Stores the current ComputerCraft and Minecraft versions.
|
||||||
--
|
--
|
||||||
-- Outside of Minecraft (for instance, in an emulator) [`_HOST`] will contain the
|
-- Outside of Minecraft (for instance, in an emulator) @{_HOST} will contain the
|
||||||
-- emulator's version instead.
|
-- emulator's version instead.
|
||||||
--
|
--
|
||||||
-- For example, `ComputerCraft 1.93.0 (Minecraft 1.15.2)`.
|
-- For example, `ComputerCraft 1.93.0 (Minecraft 1.15.2)`.
|
||||||
|
@@ -15,27 +15,27 @@ variables and functions exported by it will by available through the use of
|
|||||||
@deprecated When possible it's best to avoid using this function. It pollutes
|
@deprecated When possible it's best to avoid using this function. It pollutes
|
||||||
the global table and can mask errors.
|
the global table and can mask errors.
|
||||||
|
|
||||||
[`require`] should be used to load libraries instead.
|
@{require} should be used to load libraries instead.
|
||||||
]]
|
]]
|
||||||
function loadAPI(path) end
|
function loadAPI(path) end
|
||||||
|
|
||||||
--- Unloads an API which was loaded by [`os.loadAPI`].
|
--- Unloads an API which was loaded by @{os.loadAPI}.
|
||||||
--
|
--
|
||||||
-- This effectively removes the specified table from `_G`.
|
-- This effectively removes the specified table from `_G`.
|
||||||
--
|
--
|
||||||
-- @tparam string name The name of the API to unload.
|
-- @tparam string name The name of the API to unload.
|
||||||
-- @since 1.2
|
-- @since 1.2
|
||||||
-- @deprecated See [`os.loadAPI`] for why.
|
-- @deprecated See @{os.loadAPI} for why.
|
||||||
function unloadAPI(name) end
|
function unloadAPI(name) end
|
||||||
|
|
||||||
--[[- Pause execution of the current thread and waits for any events matching
|
--[[- Pause execution of the current thread and waits for any events matching
|
||||||
`filter`.
|
`filter`.
|
||||||
|
|
||||||
This function [yields][`coroutine.yield`] the current process and waits for it
|
This function @{coroutine.yield|yields} the current process and waits for it
|
||||||
to be resumed with a vararg list where the first element matches `filter`.
|
to be resumed with a vararg list where the first element matches `filter`.
|
||||||
If no `filter` is supplied, this will match all events.
|
If no `filter` is supplied, this will match all events.
|
||||||
|
|
||||||
Unlike [`os.pullEventRaw`], it will stop the application upon a "terminate"
|
Unlike @{os.pullEventRaw}, it will stop the application upon a "terminate"
|
||||||
event, printing the error "Terminated".
|
event, printing the error "Terminated".
|
||||||
|
|
||||||
@tparam[opt] string filter Event to filter for.
|
@tparam[opt] string filter Event to filter for.
|
||||||
@@ -69,7 +69,7 @@ function pullEvent(filter) end
|
|||||||
--[[- Pause execution of the current thread and waits for events, including the
|
--[[- Pause execution of the current thread and waits for events, including the
|
||||||
`terminate` event.
|
`terminate` event.
|
||||||
|
|
||||||
This behaves almost the same as [`os.pullEvent`], except it allows you to handle
|
This behaves almost the same as @{os.pullEvent}, except it allows you to handle
|
||||||
the `terminate` event yourself - the program will not stop execution when
|
the `terminate` event yourself - the program will not stop execution when
|
||||||
<kbd>Ctrl+T</kbd> is pressed.
|
<kbd>Ctrl+T</kbd> is pressed.
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ the `terminate` event yourself - the program will not stop execution when
|
|||||||
]]
|
]]
|
||||||
function pullEventRaw(filter) end
|
function pullEventRaw(filter) end
|
||||||
|
|
||||||
--- Pauses execution for the specified number of seconds, alias of [`_G.sleep`].
|
--- Pauses execution for the specified number of seconds, alias of @{_G.sleep}.
|
||||||
--
|
--
|
||||||
-- @tparam number time The number of seconds to sleep for, rounded up to the
|
-- @tparam number time The number of seconds to sleep for, rounded up to the
|
||||||
-- nearest multiple of 0.05.
|
-- nearest multiple of 0.05.
|
||||||
@@ -109,12 +109,12 @@ arguments.
|
|||||||
|
|
||||||
This function does not resolve program names like the shell does. This means
|
This function does not resolve program names like the shell does. This means
|
||||||
that, for example, `os.run("edit")` will not work. As well as this, it does not
|
that, for example, `os.run("edit")` will not work. As well as this, it does not
|
||||||
provide access to the [`shell`] API in the environment. For this behaviour, use
|
provide access to the @{shell} API in the environment. For this behaviour, use
|
||||||
[`shell.run`] instead.
|
@{shell.run} instead.
|
||||||
|
|
||||||
If the program cannot be found, or failed to run, it will print the error and
|
If the program cannot be found, or failed to run, it will print the error and
|
||||||
return `false`. If you want to handle this more gracefully, use an alternative
|
return `false`. If you want to handle this more gracefully, use an alternative
|
||||||
such as [`loadfile`].
|
such as @{loadfile}.
|
||||||
|
|
||||||
@tparam table env The environment to run the program with.
|
@tparam table env The environment to run the program with.
|
||||||
@tparam string path The exact path of the program to run.
|
@tparam string path The exact path of the program to run.
|
||||||
|
@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
|
|||||||
|
|
||||||
# Mod properties
|
# Mod properties
|
||||||
isUnstable=false
|
isUnstable=false
|
||||||
modVersion=1.108.0
|
modVersion=1.107.0
|
||||||
|
|
||||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||||
mcVersion=1.19.4
|
mcVersion=1.20.1
|
||||||
|
@@ -7,9 +7,9 @@
|
|||||||
# Minecraft
|
# Minecraft
|
||||||
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
||||||
# Remember to update corresponding versions in fabric.mod.json/mods.toml
|
# Remember to update corresponding versions in fabric.mod.json/mods.toml
|
||||||
fabric-api = "0.86.1+1.19.4"
|
fabric-api = "0.86.1+1.20.1"
|
||||||
fabric-loader = "0.14.21"
|
fabric-loader = "0.14.21"
|
||||||
forge = "45.0.42"
|
forge = "47.1.0"
|
||||||
forgeSpi = "6.0.0"
|
forgeSpi = "6.0.0"
|
||||||
mixin = "0.8.5"
|
mixin = "0.8.5"
|
||||||
parchment = "2023.06.26"
|
parchment = "2023.06.26"
|
||||||
@@ -19,8 +19,8 @@ parchmentMc = "1.19.4"
|
|||||||
asm = "9.3"
|
asm = "9.3"
|
||||||
autoService = "1.0.1"
|
autoService = "1.0.1"
|
||||||
checkerFramework = "3.32.0"
|
checkerFramework = "3.32.0"
|
||||||
cobalt = "0.7.3"
|
cobalt = "0.7.1"
|
||||||
cobalt-next = "0.7.4" # Not a real version, used to constrain the version we accept.
|
cobalt-next = "0.7.2" # Not a real version, used to constrain the version we accept.
|
||||||
fastutil = "8.5.9"
|
fastutil = "8.5.9"
|
||||||
guava = "31.1-jre"
|
guava = "31.1-jre"
|
||||||
jetbrainsAnnotations = "24.0.1"
|
jetbrainsAnnotations = "24.0.1"
|
||||||
@@ -33,15 +33,14 @@ nightConfig = "3.6.5"
|
|||||||
slf4j = "1.7.36"
|
slf4j = "1.7.36"
|
||||||
|
|
||||||
# Minecraft mods
|
# Minecraft mods
|
||||||
emi = "1.0.8+1.19.4"
|
emi = "1.0.8+1.20.1"
|
||||||
fabricPermissions = "0.2.20221016"
|
iris = "1.6.4+1.20"
|
||||||
iris = "1.5.2+1.19.4"
|
jei = "15.2.0.22"
|
||||||
jei = "13.1.0.11"
|
modmenu = "7.1.0"
|
||||||
modmenu = "6.1.0-rc.1"
|
|
||||||
oculus = "1.2.5"
|
oculus = "1.2.5"
|
||||||
rei = "10.0.578"
|
rei = "12.0.626"
|
||||||
rubidium = "0.6.1"
|
rubidium = "0.6.1"
|
||||||
sodium = "mc1.19.4-0.4.10"
|
sodium = "mc1.20-0.4.10"
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
byteBuddy = "1.14.2"
|
byteBuddy = "1.14.2"
|
||||||
@@ -50,7 +49,7 @@ jqwik = "1.7.2"
|
|||||||
junit = "5.9.2"
|
junit = "5.9.2"
|
||||||
|
|
||||||
# Build tools
|
# Build tools
|
||||||
cctJavadoc = "1.8.0"
|
cctJavadoc = "1.7.0"
|
||||||
checkstyle = "10.3.4"
|
checkstyle = "10.3.4"
|
||||||
curseForgeGradle = "1.0.14"
|
curseForgeGradle = "1.0.14"
|
||||||
errorProne-core = "2.18.0"
|
errorProne-core = "2.18.0"
|
||||||
@@ -59,7 +58,7 @@ fabric-loom = "1.3.7"
|
|||||||
forgeGradle = "6.0.8"
|
forgeGradle = "6.0.8"
|
||||||
githubRelease = "2.2.12"
|
githubRelease = "2.2.12"
|
||||||
ideaExt = "1.1.6"
|
ideaExt = "1.1.6"
|
||||||
illuaminate = "0.1.0-40-g975cbc3"
|
illuaminate = "0.1.0-28-ga7efd71"
|
||||||
librarian = "1.+"
|
librarian = "1.+"
|
||||||
minotaur = "2.+"
|
minotaur = "2.+"
|
||||||
mixinGradle = "0.7.+"
|
mixinGradle = "0.7.+"
|
||||||
@@ -94,12 +93,11 @@ slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
|||||||
# Minecraft mods
|
# Minecraft mods
|
||||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||||
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
||||||
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
|
|
||||||
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
||||||
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
|
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
|
||||||
jei-api = { module = "mezz.jei:jei-1.19.4-common-api", version.ref = "jei" }
|
jei-api = { module = "mezz.jei:jei-1.20.1-common-api", version.ref = "jei" }
|
||||||
jei-fabric = { module = "mezz.jei:jei-1.19.4-fabric", version.ref = "jei" }
|
jei-fabric = { module = "mezz.jei:jei-1.20.1-fabric", version.ref = "jei" }
|
||||||
jei-forge = { module = "mezz.jei:jei-1.19.4-forge", version.ref = "jei" }
|
jei-forge = { module = "mezz.jei:jei-1.20.1-forge", version.ref = "jei" }
|
||||||
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
||||||
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
||||||
oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" }
|
oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" }
|
||||||
@@ -156,7 +154,7 @@ externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
|
|||||||
externalMods-forge-compile = ["oculus", "jei-api"]
|
externalMods-forge-compile = ["oculus", "jei-api"]
|
||||||
externalMods-forge-runtime = ["jei-forge"]
|
externalMods-forge-runtime = ["jei-forge"]
|
||||||
externalMods-fabric = ["nightConfig-core", "nightConfig-toml"]
|
externalMods-fabric = ["nightConfig-core", "nightConfig-toml"]
|
||||||
externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"]
|
externalMods-fabric-compile = ["iris", "jei-api", "rei-api", "rei-builtin"]
|
||||||
externalMods-fabric-runtime = ["jei-fabric", "modmenu"]
|
externalMods-fabric-runtime = ["jei-fabric", "modmenu"]
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
/projects/core/src/test/resources/test-rom
|
/projects/core/src/test/resources/test-rom
|
||||||
/projects/web/src/mount)
|
/projects/web/src/mount)
|
||||||
|
|
||||||
|
|
||||||
(doc
|
(doc
|
||||||
; Also defined in projects/web/build.gradle.kts
|
; Also defined in projects/web/build.gradle.kts
|
||||||
(destination /projects/web/build/illuaminate)
|
(destination /projects/web/build/illuaminate)
|
||||||
@@ -49,8 +50,6 @@
|
|||||||
(at /
|
(at /
|
||||||
(linters
|
(linters
|
||||||
syntax:string-index
|
syntax:string-index
|
||||||
doc:docusaurus-admonition
|
|
||||||
doc:ldoc-reference
|
|
||||||
|
|
||||||
;; It'd be nice to avoid this, but right now there's a lot of instances of
|
;; It'd be nice to avoid this, but right now there's a lot of instances of
|
||||||
;; it.
|
;; it.
|
||||||
@@ -83,19 +82,23 @@
|
|||||||
;; isn't smart enough.
|
;; isn't smart enough.
|
||||||
sleep write printError read rs)))
|
sleep write printError read rs)))
|
||||||
|
|
||||||
;; We disable the unused global linter in bios.lua, APIs and our documentation
|
;; We disable the unused global linter in bios.lua and the APIs. In the future
|
||||||
;; stubs docs. In the future hopefully we'll get illuaminate to handle this.
|
;; hopefully we'll get illuaminate to handle this.
|
||||||
(at
|
(at
|
||||||
(/doc/stub/
|
(/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||||
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/)
|
||||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/
|
(linters -var:unused-global)
|
||||||
/projects/forge/build/docs/luaJavadoc/)
|
(lint (allow-toplevel-global true)))
|
||||||
|
|
||||||
|
;; Silence some variable warnings in documentation stubs.
|
||||||
|
(at (/doc/stub/ /projects/forge/build/docs/luaJavadoc/)
|
||||||
(linters -var:unused-global)
|
(linters -var:unused-global)
|
||||||
(lint (allow-toplevel-global true)))
|
(lint (allow-toplevel-global true)))
|
||||||
|
|
||||||
;; Suppress warnings for currently undocumented modules.
|
;; Suppress warnings for currently undocumented modules.
|
||||||
(at
|
(at
|
||||||
(; Lua APIs
|
(; Lua APIs
|
||||||
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/io.lua
|
||||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/window.lua)
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/window.lua)
|
||||||
|
|
||||||
(linters -doc:undocumented -doc:undocumented-arg -doc:undocumented-return))
|
(linters -doc:undocumented -doc:undocumented-arg -doc:undocumented-return))
|
||||||
|
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
package dan200.computercraft.api.pocket;
|
package dan200.computercraft.api.pocket;
|
||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||||
@@ -30,14 +29,6 @@ import java.util.function.Function;
|
|||||||
* @see PocketUpgradeDataProvider
|
* @see PocketUpgradeDataProvider
|
||||||
*/
|
*/
|
||||||
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T> {
|
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T> {
|
||||||
/**
|
|
||||||
* The ID for the associated registry.
|
|
||||||
*
|
|
||||||
* @deprecated Use {@link #registryId()} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
ResourceKey<Registry<PocketUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade_serialiser"));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ID for the associated registry.
|
* The ID for the associated registry.
|
||||||
*
|
*
|
||||||
|
@@ -16,7 +16,6 @@ import net.minecraft.nbt.CompoundTag;
|
|||||||
import net.minecraft.world.Container;
|
import net.minecraft.world.Container;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -71,29 +70,6 @@ public interface ITurtleAccess {
|
|||||||
*/
|
*/
|
||||||
boolean teleportTo(Level world, BlockPos pos);
|
boolean teleportTo(Level world, BlockPos pos);
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a vector containing the floating point co-ordinates at which the turtle is rendered.
|
|
||||||
* This will shift when the turtle is moving.
|
|
||||||
*
|
|
||||||
* @param f The subframe fraction.
|
|
||||||
* @return A vector containing the floating point co-ordinates at which the turtle resides.
|
|
||||||
* @see #getVisualYaw(float)
|
|
||||||
* @deprecated Will be removed in 1.20.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
Vec3 getVisualPosition(float f);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the yaw the turtle is facing when it is rendered.
|
|
||||||
*
|
|
||||||
* @param f The subframe fraction.
|
|
||||||
* @return The yaw the turtle is facing.
|
|
||||||
* @see #getVisualPosition(float)
|
|
||||||
* @deprecated Will be removed in 1.20.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
float getVisualYaw(float f);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the world direction the turtle is currently facing.
|
* Returns the world direction the turtle is currently facing.
|
||||||
*
|
*
|
||||||
|
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
package dan200.computercraft.api.turtle;
|
package dan200.computercraft.api.turtle;
|
||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||||
@@ -65,14 +64,6 @@ import java.util.function.Function;
|
|||||||
* @see dan200.computercraft.api.client.turtle.TurtleUpgradeModeller
|
* @see dan200.computercraft.api.client.turtle.TurtleUpgradeModeller
|
||||||
*/
|
*/
|
||||||
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T> {
|
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T> {
|
||||||
/**
|
|
||||||
* The ID for the associated registry.
|
|
||||||
*
|
|
||||||
* @deprecated Use {@link #registryId()} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade_serialiser"));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ID for the associated registry.
|
* The ID for the associated registry.
|
||||||
*
|
*
|
||||||
|
@@ -21,7 +21,6 @@ import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
|||||||
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
||||||
import dan200.computercraft.shared.media.items.DiskItem;
|
import dan200.computercraft.shared.media.items.DiskItem;
|
||||||
import dan200.computercraft.shared.media.items.TreasureDiskItem;
|
import dan200.computercraft.shared.media.items.TreasureDiskItem;
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.color.item.ItemColor;
|
import net.minecraft.client.color.item.ItemColor;
|
||||||
import net.minecraft.client.gui.screens.MenuScreens;
|
import net.minecraft.client.gui.screens.MenuScreens;
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
@@ -31,7 +30,6 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
|
|||||||
import net.minecraft.client.renderer.item.ClampedItemPropertyFunction;
|
import net.minecraft.client.renderer.item.ClampedItemPropertyFunction;
|
||||||
import net.minecraft.client.renderer.item.ItemProperties;
|
import net.minecraft.client.renderer.item.ItemProperties;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
|
||||||
import net.minecraft.server.packs.resources.ResourceProvider;
|
import net.minecraft.server.packs.resources.ResourceProvider;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
@@ -109,10 +107,6 @@ public final class ClientRegistry {
|
|||||||
for (var item : items) ItemProperties.register(item.get(), id, getter);
|
for (var item : items) ItemProperties.register(item.get(), id, getter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerReloadListeners(Consumer<PreparableReloadListener> register, Minecraft minecraft) {
|
|
||||||
register.accept(GuiSprites.initialise(minecraft.getTextureManager()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String[] EXTRA_MODELS = new String[]{
|
private static final String[] EXTRA_MODELS = new String[]{
|
||||||
"block/turtle_colour",
|
"block/turtle_colour",
|
||||||
"block/turtle_elf_overlay",
|
"block/turtle_elf_overlay",
|
||||||
|
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.gui;
|
package dan200.computercraft.client.gui;
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||||
import dan200.computercraft.client.gui.widgets.DynamicImageButton;
|
import dan200.computercraft.client.gui.widgets.DynamicImageButton;
|
||||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||||
@@ -19,6 +18,7 @@ import dan200.computercraft.shared.config.Config;
|
|||||||
import dan200.computercraft.shared.network.server.UploadFileMessage;
|
import dan200.computercraft.shared.network.server.UploadFileMessage;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
@@ -124,10 +124,10 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
renderBackground(stack);
|
renderBackground(graphics);
|
||||||
super.render(stack, mouseX, mouseY, partialTicks);
|
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||||
renderTooltip(stack, mouseX, mouseY);
|
renderTooltip(graphics, mouseX, mouseY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -147,7 +147,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderLabels(PoseStack transform, int mouseX, int mouseY) {
|
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
|
||||||
// Skip rendering labels.
|
// Skip rendering labels.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,19 +4,16 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.gui;
|
package dan200.computercraft.client.gui;
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import com.mojang.blaze3d.vertex.Tesselator;
|
|
||||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||||
import dan200.computercraft.client.render.RenderTypes;
|
|
||||||
import dan200.computercraft.client.render.SpriteRenderer;
|
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
|
|
||||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
|
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
|
||||||
|
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A GUI for computers which renders the terminal (and border), but with no UI elements.
|
* A GUI for computers which renders the terminal (and border), but with no UI elements.
|
||||||
@@ -39,19 +36,14 @@ public final class ComputerScreen<T extends AbstractComputerMenu> extends Abstra
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderBg(PoseStack stack, float partialTicks, int mouseX, int mouseY) {
|
public void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||||
// Draw a border around the terminal
|
// Draw a border around the terminal
|
||||||
var terminal = getTerminal();
|
var terminal = getTerminal();
|
||||||
var buffers = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
var texture = ComputerBorderRenderer.getTexture(family);
|
||||||
|
|
||||||
var spriteRenderer = SpriteRenderer.createForGui(stack, buffers.getBuffer(RenderTypes.GUI_SPRITES));
|
|
||||||
var computerTextures = GuiSprites.getComputerTextures(family);
|
|
||||||
|
|
||||||
ComputerBorderRenderer.render(
|
ComputerBorderRenderer.render(
|
||||||
spriteRenderer, computerTextures,
|
graphics.pose().last().pose(), texture, terminal.getX(), terminal.getY(),
|
||||||
terminal.getX(), terminal.getY(), terminal.getWidth(), terminal.getHeight(), false
|
FULL_BRIGHT_LIGHTMAP, terminal.getWidth(), terminal.getHeight()
|
||||||
);
|
);
|
||||||
ComputerSidebar.renderBackground(spriteRenderer, computerTextures, leftPos, topPos + sidebarYOffset);
|
ComputerSidebar.renderBackground(graphics, texture, leftPos, topPos + sidebarYOffset);
|
||||||
buffers.endBatch();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,9 +4,8 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.gui;
|
package dan200.computercraft.client.gui;
|
||||||
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveMenu;
|
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveMenu;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
@@ -23,16 +22,14 @@ public class DiskDriveScreen extends AbstractContainerScreen<DiskDriveMenu> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) {
|
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
graphics.blit(BACKGROUND, leftPos, topPos, 0, 0, imageWidth, imageHeight);
|
||||||
RenderSystem.setShaderTexture(0, BACKGROUND);
|
|
||||||
blit(transform, leftPos, topPos, 0, 0, imageWidth, imageHeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(PoseStack transform, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
renderBackground(transform);
|
renderBackground(graphics);
|
||||||
super.render(transform, mouseX, mouseY, partialTicks);
|
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||||
renderTooltip(transform, mouseX, mouseY);
|
renderTooltip(graphics, mouseX, mouseY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,127 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.client.gui;
|
|
||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
|
||||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
|
||||||
import dan200.computercraft.data.client.ClientDataProviders;
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureManager;
|
|
||||||
import net.minecraft.client.resources.TextureAtlasHolder;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sprite sheet for all GUI texutres in the mod.
|
|
||||||
*/
|
|
||||||
public final class GuiSprites extends TextureAtlasHolder {
|
|
||||||
public static final ResourceLocation SPRITE_SHEET = new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui");
|
|
||||||
public static final ResourceLocation TEXTURE = SPRITE_SHEET.withPath(x -> "textures/atlas/" + x + ".png");
|
|
||||||
|
|
||||||
public static final ButtonTextures TURNED_OFF = button("turned_off");
|
|
||||||
public static final ButtonTextures TURNED_ON = button("turned_on");
|
|
||||||
public static final ButtonTextures TERMINATE = button("terminate");
|
|
||||||
|
|
||||||
public static final ComputerTextures COMPUTER_NORMAL = computer("normal", true, true);
|
|
||||||
public static final ComputerTextures COMPUTER_ADVANCED = computer("advanced", true, true);
|
|
||||||
public static final ComputerTextures COMPUTER_COMMAND = computer("command", false, true);
|
|
||||||
public static final ComputerTextures COMPUTER_COLOUR = computer("colour", true, false);
|
|
||||||
|
|
||||||
private static ButtonTextures button(String name) {
|
|
||||||
return new ButtonTextures(
|
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/buttons/" + name),
|
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/buttons/" + name + "_hover")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ComputerTextures computer(String name, boolean pocket, boolean sidebar) {
|
|
||||||
return new ComputerTextures(
|
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/border_" + name),
|
|
||||||
pocket ? new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/pocket_bottom_" + name) : null,
|
|
||||||
sidebar ? new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/sidebar_" + name) : null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static @Nullable GuiSprites instance;
|
|
||||||
|
|
||||||
private GuiSprites(TextureManager textureManager) {
|
|
||||||
super(textureManager, TEXTURE, SPRITE_SHEET);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialise the singleton {@link GuiSprites} instance.
|
|
||||||
*
|
|
||||||
* @param textureManager The current texture manager.
|
|
||||||
* @return The singleton {@link GuiSprites} instance, to register as resource reload listener.
|
|
||||||
*/
|
|
||||||
public static GuiSprites initialise(TextureManager textureManager) {
|
|
||||||
if (instance != null) throw new IllegalStateException("GuiSprites has already been initialised");
|
|
||||||
return instance = new GuiSprites(textureManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lookup a texture on the atlas.
|
|
||||||
*
|
|
||||||
* @param texture The texture to find.
|
|
||||||
* @return The sprite on the atlas.
|
|
||||||
*/
|
|
||||||
public static TextureAtlasSprite get(ResourceLocation texture) {
|
|
||||||
if (instance == null) throw new IllegalStateException("GuiSprites has not been initialised");
|
|
||||||
return instance.getSprite(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the appropriate textures to use for a particular computer family.
|
|
||||||
*
|
|
||||||
* @param family The computer family.
|
|
||||||
* @return The family-specific textures.
|
|
||||||
*/
|
|
||||||
public static ComputerTextures getComputerTextures(ComputerFamily family) {
|
|
||||||
return switch (family) {
|
|
||||||
case NORMAL -> COMPUTER_NORMAL;
|
|
||||||
case ADVANCED -> COMPUTER_ADVANCED;
|
|
||||||
case COMMAND -> COMPUTER_COMMAND;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A set of sprites for a button, with both a normal and "active" state.
|
|
||||||
*
|
|
||||||
* @param normal The normal texture for the button.
|
|
||||||
* @param active The texture for the button when it is active (hovered or focused).
|
|
||||||
*/
|
|
||||||
public record ButtonTextures(ResourceLocation normal, ResourceLocation active) {
|
|
||||||
public TextureAtlasSprite get(boolean active) {
|
|
||||||
return GuiSprites.get(active ? this.active : normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stream<ResourceLocation> textures() {
|
|
||||||
return Stream.of(normal, active);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the set of sprites for a computer family.
|
|
||||||
*
|
|
||||||
* @param border The texture for the computer's border.
|
|
||||||
* @param pocketBottom The texture for the bottom of a pocket computer.
|
|
||||||
* @param sidebar The texture for the computer sidebar.
|
|
||||||
* @see ComputerBorderRenderer
|
|
||||||
* @see ClientDataProviders
|
|
||||||
*/
|
|
||||||
public record ComputerTextures(
|
|
||||||
ResourceLocation border,
|
|
||||||
@Nullable ResourceLocation pocketBottom,
|
|
||||||
@Nullable ResourceLocation sidebar
|
|
||||||
) {
|
|
||||||
public Stream<ResourceLocation> textures() {
|
|
||||||
return Stream.of(border, pocketBottom, sidebar).filter(Objects::nonNull);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -4,10 +4,8 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.gui;
|
package dan200.computercraft.client.gui;
|
||||||
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.GuiComponent;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.components.toasts.Toast;
|
import net.minecraft.client.gui.components.toasts.Toast;
|
||||||
import net.minecraft.client.gui.components.toasts.ToastComponent;
|
import net.minecraft.client.gui.components.toasts.ToastComponent;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
@@ -73,55 +71,52 @@ public class ItemToast implements Toast {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Visibility render(PoseStack transform, ToastComponent component, long time) {
|
public Visibility render(GuiGraphics graphics, ToastComponent component, long time) {
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
|
|
||||||
firstDisplay = time;
|
firstDisplay = time;
|
||||||
isNew = false;
|
isNew = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderSystem.setShaderTexture(0, TEXTURE);
|
|
||||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
|
||||||
|
|
||||||
if (width == 160 && message.size() <= 1) {
|
if (width == 160 && message.size() <= 1) {
|
||||||
GuiComponent.blit(transform, 0, 0, 0, 64, width, height());
|
graphics.blit(TEXTURE, 0, 0, 0, 64, width, height());
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
var height = height();
|
var height = height();
|
||||||
|
|
||||||
var bottom = Math.min(4, height - 28);
|
var bottom = Math.min(4, height - 28);
|
||||||
renderBackgroundRow(transform, component, width, 0, 0, 28);
|
renderBackgroundRow(graphics, width, 0, 0, 28);
|
||||||
|
|
||||||
for (var i = 28; i < height - bottom; i += 10) {
|
for (var i = 28; i < height - bottom; i += 10) {
|
||||||
renderBackgroundRow(transform, component, width, 16, i, Math.min(16, height - i - bottom));
|
renderBackgroundRow(graphics, width, 16, i, Math.min(16, height - i - bottom));
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBackgroundRow(transform, component, width, 32 - bottom, height - bottom, bottom);
|
renderBackgroundRow(graphics, width, 32 - bottom, height - bottom, bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
var textX = MARGIN;
|
var textX = MARGIN;
|
||||||
if (!stack.isEmpty()) {
|
if (!stack.isEmpty()) {
|
||||||
textX += MARGIN + IMAGE_SIZE;
|
textX += MARGIN + IMAGE_SIZE;
|
||||||
component.getMinecraft().getItemRenderer().renderAndDecorateFakeItem(transform, stack, MARGIN, MARGIN + height() / 2 - IMAGE_SIZE);
|
graphics.renderFakeItem(stack, MARGIN, MARGIN + height() / 2 - IMAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
component.getMinecraft().font.draw(transform, title, textX, MARGIN, 0xff500050);
|
graphics.drawString(component.getMinecraft().font, title, textX, MARGIN, 0xff500050, false);
|
||||||
for (var i = 0; i < message.size(); ++i) {
|
for (var i = 0; i < message.size(); ++i) {
|
||||||
component.getMinecraft().font.draw(transform, message.get(i), textX, (float) (LINE_SPACING + (i + 1) * LINE_SPACING), 0xff000000);
|
graphics.drawString(component.getMinecraft().font, message.get(i), textX, LINE_SPACING + (i + 1) * LINE_SPACING, 0xff000000, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return time - firstDisplay < DISPLAY_TIME ? Visibility.SHOW : Visibility.HIDE;
|
return time - firstDisplay < DISPLAY_TIME ? Visibility.SHOW : Visibility.HIDE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void renderBackgroundRow(PoseStack transform, ToastComponent component, int x, int u, int y, int height) {
|
private static void renderBackgroundRow(GuiGraphics graphics, int x, int u, int y, int height) {
|
||||||
var leftOffset = 5;
|
var leftOffset = 5;
|
||||||
var rightOffset = Math.min(60, x - leftOffset);
|
var rightOffset = Math.min(60, x - leftOffset);
|
||||||
|
|
||||||
GuiComponent.blit(transform, 0, y, 0, 32 + u, leftOffset, height);
|
graphics.blit(TEXTURE, 0, y, 0, 32 + u, leftOffset, height);
|
||||||
for (var k = leftOffset; k < x - rightOffset; k += 64) {
|
for (var k = leftOffset; k < x - rightOffset; k += 64) {
|
||||||
GuiComponent.blit(transform, k, y, 32, 32 + u, Math.min(64, x - k - rightOffset), height);
|
graphics.blit(TEXTURE, k, y, 32, 32 + u, Math.min(64, x - k - rightOffset), height);
|
||||||
}
|
}
|
||||||
|
|
||||||
GuiComponent.blit(transform, x - rightOffset, y, 160 - rightOffset, 32 + u, rightOffset, height);
|
graphics.blit(TEXTURE, x - rightOffset, y, 160 - rightOffset, 32 + u, rightOffset, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.gui;
|
package dan200.computercraft.client.gui;
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
import net.minecraft.client.KeyMapping;
|
import net.minecraft.client.KeyMapping;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
import net.minecraft.client.gui.screens.inventory.MenuAccess;
|
import net.minecraft.client.gui.screens.inventory.MenuAccess;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
@@ -42,7 +42,6 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void init() {
|
protected void init() {
|
||||||
passEvents = true; // Pass mouse vents through to the game's mouse handler.
|
|
||||||
// First ensure we're still grabbing the mouse, so the user can look around. Then reset bits of state that
|
// First ensure we're still grabbing the mouse, so the user can look around. Then reset bits of state that
|
||||||
// grabbing unsets.
|
// grabbing unsets.
|
||||||
minecraft.mouseHandler.grabMouse();
|
minecraft.mouseHandler.grabMouse();
|
||||||
@@ -91,15 +90,15 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(PoseStack transform, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
super.render(transform, mouseX, mouseY, partialTicks);
|
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||||
|
|
||||||
var font = minecraft.font;
|
var font = minecraft.font;
|
||||||
var lines = font.split(Component.translatable("gui.computercraft.pocket_computer_overlay"), (int) (width * 0.8));
|
var lines = font.split(Component.translatable("gui.computercraft.pocket_computer_overlay"), (int) (width * 0.8));
|
||||||
var y = 10.0f;
|
var y = 10;
|
||||||
for (var line : lines) {
|
for (var line : lines) {
|
||||||
font.drawShadow(transform, line, (float) ((width / 2) - (minecraft.font.width(line) / 2)), y, 0xFFFFFF);
|
graphics.drawString(font, line, (width / 2) - (minecraft.font.width(line) / 2), y, 0xFFFFFF, true);
|
||||||
y += 9.0f;
|
y += 9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,9 +4,8 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.gui;
|
package dan200.computercraft.client.gui;
|
||||||
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.components.AbstractWidget;
|
import net.minecraft.client.gui.components.AbstractWidget;
|
||||||
import net.minecraft.client.gui.components.Button;
|
import net.minecraft.client.gui.components.Button;
|
||||||
import net.minecraft.client.gui.components.MultiLineLabel;
|
import net.minecraft.client.gui.components.MultiLineLabel;
|
||||||
@@ -86,20 +85,19 @@ public final class OptionScreen extends Screen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(PoseStack transform, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
renderBackground(transform);
|
renderBackground(graphics);
|
||||||
|
|
||||||
// Render the actual texture.
|
// Render the actual texture.
|
||||||
RenderSystem.setShaderTexture(0, BACKGROUND);
|
graphics.blit(BACKGROUND, x, y, 0, 0, innerWidth, PADDING);
|
||||||
blit(transform, x, y, 0, 0, innerWidth, PADDING);
|
graphics.blit(BACKGROUND,
|
||||||
blit(transform,
|
|
||||||
x, y + PADDING, 0, PADDING, innerWidth, innerHeight - PADDING * 2,
|
x, y + PADDING, 0, PADDING, innerWidth, innerHeight - PADDING * 2,
|
||||||
innerWidth, PADDING
|
innerWidth, PADDING
|
||||||
);
|
);
|
||||||
blit(transform, x, y + innerHeight - PADDING, 0, 256 - PADDING, innerWidth, PADDING);
|
graphics.blit(BACKGROUND, x, y + innerHeight - PADDING, 0, 256 - PADDING, innerWidth, PADDING);
|
||||||
|
|
||||||
assertNonNull(messageRenderer).renderLeftAlignedNoShadow(transform, x + PADDING, y + PADDING, FONT_HEIGHT, 0x404040);
|
assertNonNull(messageRenderer).renderLeftAlignedNoShadow(graphics, x + PADDING, y + PADDING, FONT_HEIGHT, 0x404040);
|
||||||
super.render(transform, mouseX, mouseY, partialTicks);
|
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -4,9 +4,8 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.gui;
|
package dan200.computercraft.client.gui;
|
||||||
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import dan200.computercraft.shared.peripheral.printer.PrinterMenu;
|
import dan200.computercraft.shared.peripheral.printer.PrinterMenu;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
@@ -23,18 +22,16 @@ public class PrinterScreen extends AbstractContainerScreen<PrinterMenu> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) {
|
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
graphics.blit(BACKGROUND, leftPos, topPos, 0, 0, imageWidth, imageHeight);
|
||||||
RenderSystem.setShaderTexture(0, BACKGROUND);
|
|
||||||
blit(transform, leftPos, topPos, 0, 0, imageWidth, imageHeight);
|
|
||||||
|
|
||||||
if (getMenu().isPrinting()) blit(transform, leftPos + 34, topPos + 21, 176, 0, 25, 45);
|
if (getMenu().isPrinting()) graphics.blit(BACKGROUND, leftPos + 34, topPos + 21, 176, 0, 25, 45);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
renderBackground(stack);
|
renderBackground(graphics);
|
||||||
super.render(stack, mouseX, mouseY, partialTicks);
|
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||||
renderTooltip(stack, mouseX, mouseY);
|
renderTooltip(graphics, mouseX, mouseY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,12 +4,11 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.gui;
|
package dan200.computercraft.client.gui;
|
||||||
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import com.mojang.blaze3d.vertex.Tesselator;
|
import com.mojang.blaze3d.vertex.Tesselator;
|
||||||
import dan200.computercraft.core.terminal.TextBuffer;
|
import dan200.computercraft.core.terminal.TextBuffer;
|
||||||
import dan200.computercraft.shared.common.HeldItemMenu;
|
import dan200.computercraft.shared.common.HeldItemMenu;
|
||||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
@@ -83,30 +82,27 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) {
|
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||||
// Draw the printout
|
// Draw the printout
|
||||||
RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f);
|
|
||||||
RenderSystem.enableDepthTest();
|
|
||||||
|
|
||||||
var renderer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
var renderer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
||||||
drawBorder(transform, renderer, leftPos, topPos, 0, page, pages, book, FULL_BRIGHT_LIGHTMAP);
|
drawBorder(graphics.pose(), renderer, leftPos, topPos, 0, page, pages, book, FULL_BRIGHT_LIGHTMAP);
|
||||||
drawText(transform, renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours);
|
drawText(graphics.pose(), renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours);
|
||||||
renderer.endBatch();
|
renderer.endBatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics graphics, 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.
|
||||||
stack.pushPose();
|
graphics.pose().pushPose();
|
||||||
stack.translate(0, 0, -1);
|
graphics.pose().translate(0, 0, -1);
|
||||||
renderBackground(stack);
|
renderBackground(graphics);
|
||||||
stack.popPose();
|
graphics.pose().popPose();
|
||||||
|
|
||||||
super.render(stack, mouseX, mouseY, partialTicks);
|
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderLabels(PoseStack transform, int mouseX, int mouseY) {
|
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
|
||||||
// Skip rendering labels.
|
// Skip rendering labels.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,18 +4,14 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.gui;
|
package dan200.computercraft.client.gui;
|
||||||
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import com.mojang.blaze3d.vertex.Tesselator;
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||||
import dan200.computercraft.client.render.RenderTypes;
|
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||||
import dan200.computercraft.client.render.SpriteRenderer;
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
@@ -47,26 +43,22 @@ public class TurtleScreen extends AbstractComputerScreen<TurtleMenu> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) {
|
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||||
var advanced = family == ComputerFamily.ADVANCED;
|
var advanced = family == ComputerFamily.ADVANCED;
|
||||||
RenderSystem.setShaderTexture(0, advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL);
|
var texture = advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL;
|
||||||
blit(transform, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, 0, TEX_WIDTH, TEX_HEIGHT, FULL_TEX_SIZE, FULL_TEX_SIZE);
|
graphics.blit(texture, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, 0, TEX_WIDTH, TEX_HEIGHT, FULL_TEX_SIZE, FULL_TEX_SIZE);
|
||||||
|
|
||||||
// Render selected slot
|
// Render selected slot
|
||||||
var slot = getMenu().getSelectedSlot();
|
var slot = getMenu().getSelectedSlot();
|
||||||
if (slot >= 0) {
|
if (slot >= 0) {
|
||||||
var slotX = slot % 4;
|
var slotX = slot % 4;
|
||||||
var slotY = slot / 4;
|
var slotY = slot / 4;
|
||||||
blit(transform,
|
graphics.blit(texture,
|
||||||
leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18, 0,
|
leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18, 0,
|
||||||
0, 217, 24, 24, FULL_TEX_SIZE, FULL_TEX_SIZE
|
0, 217, 24, 24, FULL_TEX_SIZE, FULL_TEX_SIZE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render sidebar
|
ComputerSidebar.renderBackground(graphics, ComputerBorderRenderer.getTexture(family), leftPos, topPos + sidebarYOffset);
|
||||||
var buffers = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
|
||||||
var spriteRenderer = SpriteRenderer.createForGui(transform, buffers.getBuffer(RenderTypes.GUI_SPRITES));
|
|
||||||
ComputerSidebar.renderBackground(spriteRenderer, GuiSprites.getComputerTextures(family), leftPos, topPos + sidebarYOffset);
|
|
||||||
buffers.endBatch();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,13 +4,15 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.gui.widgets;
|
package dan200.computercraft.client.gui.widgets;
|
||||||
|
|
||||||
import dan200.computercraft.client.gui.GuiSprites;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.client.gui.widgets.DynamicImageButton.HintedMessage;
|
import dan200.computercraft.client.gui.widgets.DynamicImageButton.HintedMessage;
|
||||||
import dan200.computercraft.client.render.SpriteRenderer;
|
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||||
import dan200.computercraft.shared.computer.core.InputHandler;
|
import dan200.computercraft.shared.computer.core.InputHandler;
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.components.AbstractWidget;
|
import net.minecraft.client.gui.components.AbstractWidget;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@@ -19,18 +21,22 @@ import java.util.function.Consumer;
|
|||||||
* Registers buttons to interact with a computer.
|
* Registers buttons to interact with a computer.
|
||||||
*/
|
*/
|
||||||
public final class ComputerSidebar {
|
public final class ComputerSidebar {
|
||||||
|
private static final ResourceLocation TEXTURE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/buttons.png");
|
||||||
|
|
||||||
|
private static final int TEX_SIZE = 64;
|
||||||
|
|
||||||
private static final int ICON_WIDTH = 12;
|
private static final int ICON_WIDTH = 12;
|
||||||
private static final int ICON_HEIGHT = 12;
|
private static final int ICON_HEIGHT = 12;
|
||||||
private static final int ICON_MARGIN = 2;
|
private static final int ICON_MARGIN = 2;
|
||||||
|
|
||||||
|
private static final int ICON_TEX_Y_DIFF = 14;
|
||||||
|
|
||||||
private static final int CORNERS_BORDER = 3;
|
private static final int CORNERS_BORDER = 3;
|
||||||
private static final int FULL_BORDER = CORNERS_BORDER + ICON_MARGIN;
|
private static final int FULL_BORDER = CORNERS_BORDER + ICON_MARGIN;
|
||||||
|
|
||||||
private static final int BUTTONS = 2;
|
private static final int BUTTONS = 2;
|
||||||
private static final int HEIGHT = (ICON_HEIGHT + ICON_MARGIN * 2) * BUTTONS + CORNERS_BORDER * 2;
|
private static final int HEIGHT = (ICON_HEIGHT + ICON_MARGIN * 2) * BUTTONS + CORNERS_BORDER * 2;
|
||||||
|
|
||||||
private static final int TEX_HEIGHT = 14;
|
|
||||||
|
|
||||||
private ComputerSidebar() {
|
private ComputerSidebar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,18 +50,16 @@ public final class ComputerSidebar {
|
|||||||
Component.translatable("gui.computercraft.tooltip.turn_off.key")
|
Component.translatable("gui.computercraft.tooltip.turn_off.key")
|
||||||
);
|
);
|
||||||
add.accept(new DynamicImageButton(
|
add.accept(new DynamicImageButton(
|
||||||
x, y, ICON_WIDTH, ICON_HEIGHT,
|
x, y, ICON_WIDTH, ICON_HEIGHT, () -> isOn.getAsBoolean() ? 15 : 1, 1, ICON_TEX_Y_DIFF,
|
||||||
h -> isOn.getAsBoolean() ? GuiSprites.TURNED_ON.get(h) : GuiSprites.TURNED_OFF.get(h),
|
TEXTURE, TEX_SIZE, TEX_SIZE, b -> toggleComputer(isOn, input),
|
||||||
b -> toggleComputer(isOn, input),
|
|
||||||
() -> isOn.getAsBoolean() ? turnOff : turnOn
|
() -> isOn.getAsBoolean() ? turnOff : turnOn
|
||||||
));
|
));
|
||||||
|
|
||||||
y += ICON_HEIGHT + ICON_MARGIN * 2;
|
y += ICON_HEIGHT + ICON_MARGIN * 2;
|
||||||
|
|
||||||
add.accept(new DynamicImageButton(
|
add.accept(new DynamicImageButton(
|
||||||
x, y, ICON_WIDTH, ICON_HEIGHT,
|
x, y, ICON_WIDTH, ICON_HEIGHT, 29, 1, ICON_TEX_Y_DIFF,
|
||||||
GuiSprites.TERMINATE::get,
|
TEXTURE, TEX_SIZE, TEX_SIZE, b -> input.queueEvent("terminate"),
|
||||||
b -> input.queueEvent("terminate"),
|
|
||||||
new HintedMessage(
|
new HintedMessage(
|
||||||
Component.translatable("gui.computercraft.tooltip.terminate"),
|
Component.translatable("gui.computercraft.tooltip.terminate"),
|
||||||
Component.translatable("gui.computercraft.tooltip.terminate.key")
|
Component.translatable("gui.computercraft.tooltip.terminate.key")
|
||||||
@@ -63,12 +67,22 @@ public final class ComputerSidebar {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void renderBackground(SpriteRenderer renderer, GuiSprites.ComputerTextures textures, int x, int y) {
|
public static void renderBackground(GuiGraphics graphics, ResourceLocation texture, int x, int y) {
|
||||||
var texture = textures.sidebar();
|
graphics.blit(texture,
|
||||||
if (texture == null) throw new NullPointerException(textures + " has no sidebar texture");
|
x, y, 0, 102, AbstractComputerMenu.SIDEBAR_WIDTH, FULL_BORDER,
|
||||||
var sprite = GuiSprites.get(texture);
|
ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE
|
||||||
|
);
|
||||||
|
|
||||||
renderer.blitVerticalSliced(sprite, x, y, AbstractComputerMenu.SIDEBAR_WIDTH, HEIGHT, FULL_BORDER, FULL_BORDER, TEX_HEIGHT);
|
graphics.blit(texture,
|
||||||
|
x, y + FULL_BORDER, AbstractComputerMenu.SIDEBAR_WIDTH, HEIGHT - FULL_BORDER * 2,
|
||||||
|
0, 107, AbstractComputerMenu.SIDEBAR_WIDTH, 4,
|
||||||
|
ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE
|
||||||
|
);
|
||||||
|
|
||||||
|
graphics.blit(texture,
|
||||||
|
x, y + HEIGHT - FULL_BORDER, 0, 111, AbstractComputerMenu.SIDEBAR_WIDTH, FULL_BORDER,
|
||||||
|
ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void toggleComputer(BooleanSupplier isOn, InputHandler input) {
|
private static void toggleComputer(BooleanSupplier isOn, InputHandler input) {
|
||||||
|
@@ -5,15 +5,15 @@
|
|||||||
package dan200.computercraft.client.gui.widgets;
|
package dan200.computercraft.client.gui.widgets;
|
||||||
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import it.unimi.dsi.fastutil.booleans.Boolean2ObjectFunction;
|
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.components.Button;
|
import net.minecraft.client.gui.components.Button;
|
||||||
import net.minecraft.client.gui.components.Tooltip;
|
import net.minecraft.client.gui.components.Tooltip;
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.function.IntSupplier;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,42 +21,58 @@ import java.util.function.Supplier;
|
|||||||
* dynamically.
|
* dynamically.
|
||||||
*/
|
*/
|
||||||
public class DynamicImageButton extends Button {
|
public class DynamicImageButton extends Button {
|
||||||
private final Boolean2ObjectFunction<TextureAtlasSprite> texture;
|
private final ResourceLocation texture;
|
||||||
|
private final IntSupplier xTexStart;
|
||||||
|
private final int yTexStart;
|
||||||
|
private final int yDiffTex;
|
||||||
|
private final int textureWidth;
|
||||||
|
private final int textureHeight;
|
||||||
private final Supplier<HintedMessage> message;
|
private final Supplier<HintedMessage> message;
|
||||||
|
|
||||||
public DynamicImageButton(
|
public DynamicImageButton(
|
||||||
int x, int y, int width, int height, Boolean2ObjectFunction<TextureAtlasSprite> texture, OnPress onPress,
|
int x, int y, int width, int height, int xTexStart, int yTexStart, int yDiffTex,
|
||||||
HintedMessage message
|
ResourceLocation texture, int textureWidth, int textureHeight,
|
||||||
|
OnPress onPress, HintedMessage message
|
||||||
) {
|
) {
|
||||||
this(x, y, width, height, texture, onPress, () -> message);
|
this(
|
||||||
|
x, y, width, height, () -> xTexStart, yTexStart, yDiffTex,
|
||||||
|
texture, textureWidth, textureHeight,
|
||||||
|
onPress, () -> message
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicImageButton(
|
public DynamicImageButton(
|
||||||
int x, int y, int width, int height,
|
int x, int y, int width, int height, IntSupplier xTexStart, int yTexStart, int yDiffTex,
|
||||||
Boolean2ObjectFunction<TextureAtlasSprite> texture,
|
ResourceLocation texture, int textureWidth, int textureHeight,
|
||||||
OnPress onPress, Supplier<HintedMessage> message
|
OnPress onPress, Supplier<HintedMessage> message
|
||||||
) {
|
) {
|
||||||
super(x, y, width, height, Component.empty(), onPress, DEFAULT_NARRATION);
|
super(x, y, width, height, Component.empty(), onPress, DEFAULT_NARRATION);
|
||||||
|
this.textureWidth = textureWidth;
|
||||||
|
this.textureHeight = textureHeight;
|
||||||
|
this.xTexStart = xTexStart;
|
||||||
|
this.yTexStart = yTexStart;
|
||||||
|
this.yDiffTex = yDiffTex;
|
||||||
this.texture = texture;
|
this.texture = texture;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderWidget(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
|
public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
var texture = this.texture.get(isHoveredOrFocused());
|
RenderSystem.enableBlend();
|
||||||
RenderSystem.setShaderTexture(0, texture.atlasLocation());
|
|
||||||
RenderSystem.disableDepthTest();
|
|
||||||
|
|
||||||
blit(stack, getX(), getY(), 0, width, height, texture);
|
|
||||||
RenderSystem.enableDepthTest();
|
RenderSystem.enableDepthTest();
|
||||||
|
|
||||||
|
var yTex = yTexStart;
|
||||||
|
if (isHoveredOrFocused()) yTex += yDiffTex;
|
||||||
|
|
||||||
|
graphics.blit(texture, getX(), getY(), xTexStart.getAsInt(), yTex, width, height, textureWidth, textureHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
var message = this.message.get();
|
var message = this.message.get();
|
||||||
setMessage(message.message());
|
setMessage(message.message());
|
||||||
setTooltip(message.tooltip());
|
setTooltip(message.tooltip());
|
||||||
super.render(stack, mouseX, mouseY, partialTicks);
|
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
public record HintedMessage(Component message, Tooltip tooltip) {
|
public record HintedMessage(Component message, Tooltip tooltip) {
|
||||||
|
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.gui.widgets;
|
package dan200.computercraft.client.gui.widgets;
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import com.mojang.blaze3d.vertex.Tesselator;
|
import com.mojang.blaze3d.vertex.Tesselator;
|
||||||
import dan200.computercraft.client.render.RenderTypes;
|
import dan200.computercraft.client.render.RenderTypes;
|
||||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||||
@@ -12,6 +11,7 @@ import dan200.computercraft.core.terminal.Terminal;
|
|||||||
import dan200.computercraft.shared.computer.core.InputHandler;
|
import dan200.computercraft.shared.computer.core.InputHandler;
|
||||||
import net.minecraft.SharedConstants;
|
import net.minecraft.SharedConstants;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.components.AbstractWidget;
|
import net.minecraft.client.gui.components.AbstractWidget;
|
||||||
import net.minecraft.client.gui.narration.NarratedElementType;
|
import net.minecraft.client.gui.narration.NarratedElementType;
|
||||||
import net.minecraft.client.gui.narration.NarrationElementOutput;
|
import net.minecraft.client.gui.narration.NarrationElementOutput;
|
||||||
@@ -274,11 +274,11 @@ public class TerminalWidget extends AbstractWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderWidget(PoseStack transform, int mouseX, int mouseY, float partialTicks) {
|
public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
if (!visible) return;
|
if (!visible) return;
|
||||||
|
|
||||||
var bufferSource = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
var bufferSource = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
||||||
var emitter = FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL));
|
var emitter = FixedWidthFontRenderer.toVertexConsumer(graphics.pose(), bufferSource.getBuffer(RenderTypes.TERMINAL));
|
||||||
|
|
||||||
FixedWidthFontRenderer.drawTerminal(
|
FixedWidthFontRenderer.drawTerminal(
|
||||||
emitter,
|
emitter,
|
||||||
|
@@ -52,7 +52,7 @@ public abstract class AbstractClientNetworkContext implements ClientNetworkConte
|
|||||||
var player = Minecraft.getInstance().player;
|
var player = Minecraft.getInstance().player;
|
||||||
if (player == null) return;
|
if (player == null) return;
|
||||||
|
|
||||||
var te = player.level.getBlockEntity(pos);
|
var te = player.level().getBlockEntity(pos);
|
||||||
if (!(te instanceof MonitorBlockEntity monitor)) return;
|
if (!(te instanceof MonitorBlockEntity monitor)) return;
|
||||||
|
|
||||||
monitor.read(terminal);
|
monitor.read(terminal);
|
||||||
|
@@ -4,17 +4,25 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.render;
|
package dan200.computercraft.client.render;
|
||||||
|
|
||||||
import dan200.computercraft.client.gui.GuiSprites;
|
import com.mojang.blaze3d.vertex.Tesselator;
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import static dan200.computercraft.client.render.SpriteRenderer.u;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import static dan200.computercraft.client.render.SpriteRenderer.v;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the borders of computers, either for a GUI ({@link dan200.computercraft.client.gui.ComputerScreen}) or
|
* Renders the borders of computers, either for a GUI ({@link dan200.computercraft.client.gui.ComputerScreen}) or
|
||||||
* {@linkplain PocketItemRenderer in-hand pocket computers}.
|
* {@linkplain PocketItemRenderer in-hand pocket computers}.
|
||||||
*/
|
*/
|
||||||
public final class ComputerBorderRenderer {
|
public class ComputerBorderRenderer {
|
||||||
|
private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/corners_normal.png");
|
||||||
|
private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/corners_advanced.png");
|
||||||
|
private static final ResourceLocation BACKGROUND_COMMAND = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/corners_command.png");
|
||||||
|
public static final ResourceLocation BACKGROUND_COLOUR = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/corners_colour.png");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The margin between the terminal and its border.
|
* The margin between the terminal and its border.
|
||||||
*/
|
*/
|
||||||
@@ -25,51 +33,100 @@ public final class ComputerBorderRenderer {
|
|||||||
*/
|
*/
|
||||||
public static final int BORDER = 12;
|
public static final int BORDER = 12;
|
||||||
|
|
||||||
|
private static final int CORNER_TOP_Y = 28;
|
||||||
|
private static final int CORNER_BOTTOM_Y = CORNER_TOP_Y + BORDER;
|
||||||
|
private static final int CORNER_LEFT_X = BORDER;
|
||||||
|
private static final int CORNER_RIGHT_X = CORNER_LEFT_X + BORDER;
|
||||||
|
private static final int BORDER_RIGHT_X = 36;
|
||||||
|
private static final int LIGHT_BORDER_Y = 56;
|
||||||
|
private static final int LIGHT_CORNER_Y = 80;
|
||||||
|
|
||||||
public static final int LIGHT_HEIGHT = 8;
|
public static final int LIGHT_HEIGHT = 8;
|
||||||
|
|
||||||
private static final int TEX_SIZE = 36;
|
public static final int TEX_SIZE = 256;
|
||||||
|
private static final float TEX_SCALE = 1 / (float) TEX_SIZE;
|
||||||
|
|
||||||
private ComputerBorderRenderer() {
|
private final Matrix4f transform;
|
||||||
|
private final VertexConsumer builder;
|
||||||
|
private final int light;
|
||||||
|
private final int z;
|
||||||
|
private final float r, g, b;
|
||||||
|
|
||||||
|
public ComputerBorderRenderer(Matrix4f transform, VertexConsumer builder, int z, int light, float r, float g, float b) {
|
||||||
|
this.transform = transform;
|
||||||
|
this.builder = builder;
|
||||||
|
this.z = z;
|
||||||
|
this.light = light;
|
||||||
|
this.r = r;
|
||||||
|
this.g = g;
|
||||||
|
this.b = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void render(SpriteRenderer renderer, GuiSprites.ComputerTextures textures, int x, int y, int width, int height, boolean withLight) {
|
public static ResourceLocation getTexture(ComputerFamily family) {
|
||||||
|
return switch (family) {
|
||||||
|
case NORMAL -> BACKGROUND_NORMAL;
|
||||||
|
case ADVANCED -> BACKGROUND_ADVANCED;
|
||||||
|
case COMMAND -> BACKGROUND_COMMAND;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RenderType getRenderType(ResourceLocation location) {
|
||||||
|
// See note in RenderTypes about why we use text rather than anything intuitive.
|
||||||
|
return RenderType.text(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void render(Matrix4f transform, ResourceLocation location, int x, int y, int light, int width, int height) {
|
||||||
|
var source = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
||||||
|
render(transform, source.getBuffer(getRenderType(location)), x, y, 1, light, width, height, false, 1, 1, 1);
|
||||||
|
source.endBatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void render(Matrix4f transform, VertexConsumer buffer, int x, int y, int z, int light, int width, int height, boolean withLight, float r, float g, float b) {
|
||||||
|
new ComputerBorderRenderer(transform, buffer, z, light, r, g, b).doRender(x, y, width, height, withLight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doRender(int x, int y, int width, int height, boolean withLight) {
|
||||||
var endX = x + width;
|
var endX = x + width;
|
||||||
var endY = y + height;
|
var endY = y + height;
|
||||||
|
|
||||||
var border = GuiSprites.get(textures.border());
|
// Vertical bars
|
||||||
|
renderLine(x - BORDER, y, 0, CORNER_TOP_Y, BORDER, endY - y);
|
||||||
|
renderLine(endX, y, BORDER_RIGHT_X, CORNER_TOP_Y, BORDER, endY - y);
|
||||||
|
|
||||||
// Top bar
|
// Top bar
|
||||||
blitBorder(renderer, border, x - BORDER, y - BORDER, 0, 0, BORDER, BORDER);
|
renderLine(x, y - BORDER, 0, 0, endX - x, BORDER);
|
||||||
blitBorder(renderer, border, x, y - BORDER, BORDER, 0, width, BORDER);
|
renderCorner(x - BORDER, y - BORDER, CORNER_LEFT_X, CORNER_TOP_Y);
|
||||||
blitBorder(renderer, border, endX, y - BORDER, BORDER * 2, 0, BORDER, BORDER);
|
renderCorner(endX, y - BORDER, CORNER_RIGHT_X, CORNER_TOP_Y);
|
||||||
|
|
||||||
// Vertical bars
|
|
||||||
blitBorder(renderer, border, x - BORDER, y, 0, BORDER, BORDER, height);
|
|
||||||
blitBorder(renderer, border, endX, y, BORDER * 2, BORDER, BORDER, height);
|
|
||||||
|
|
||||||
// Bottom bar. We allow for drawing a stretched version, which allows for additional elements (such as the
|
// Bottom bar. We allow for drawing a stretched version, which allows for additional elements (such as the
|
||||||
// pocket computer's lights).
|
// pocket computer's lights).
|
||||||
if (withLight) {
|
if (withLight) {
|
||||||
var pocketBottomTexture = textures.pocketBottom();
|
renderTexture(x, endY, 0, LIGHT_BORDER_Y, endX - x, BORDER + LIGHT_HEIGHT, BORDER, BORDER + LIGHT_HEIGHT);
|
||||||
if (pocketBottomTexture == null) throw new NullPointerException(textures + " has no pocket texture");
|
renderTexture(x - BORDER, endY, CORNER_LEFT_X, LIGHT_CORNER_Y, BORDER, BORDER + LIGHT_HEIGHT);
|
||||||
var pocketBottom = GuiSprites.get(pocketBottomTexture);
|
renderTexture(endX, endY, CORNER_RIGHT_X, LIGHT_CORNER_Y, BORDER, BORDER + LIGHT_HEIGHT);
|
||||||
|
|
||||||
renderer.blitHorizontalSliced(
|
|
||||||
pocketBottom, x - BORDER, endY, width + BORDER * 2, BORDER + LIGHT_HEIGHT,
|
|
||||||
BORDER, BORDER, BORDER * 3
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
blitBorder(renderer, border, x - BORDER, endY, 0, BORDER * 2, BORDER, BORDER);
|
renderLine(x, endY, 0, BORDER, endX - x, BORDER);
|
||||||
blitBorder(renderer, border, x, endY, BORDER, BORDER * 2, width, BORDER);
|
renderCorner(x - BORDER, endY, CORNER_LEFT_X, CORNER_BOTTOM_Y);
|
||||||
blitBorder(renderer, border, endX, endY, BORDER * 2, BORDER * 2, BORDER, BORDER);
|
renderCorner(endX, endY, CORNER_RIGHT_X, CORNER_BOTTOM_Y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void blitBorder(SpriteRenderer renderer, TextureAtlasSprite sprite, int x, int y, int u, int v, int width, int height) {
|
private void renderCorner(int x, int y, int u, int v) {
|
||||||
renderer.blit(
|
renderTexture(x, y, u, v, BORDER, BORDER, BORDER, BORDER);
|
||||||
x, y, width, height,
|
}
|
||||||
u(sprite, u, TEX_SIZE), v(sprite, v, TEX_SIZE),
|
|
||||||
u(sprite, u + BORDER, TEX_SIZE), v(sprite, v + BORDER, TEX_SIZE)
|
private void renderLine(int x, int y, int u, int v, int width, int height) {
|
||||||
);
|
renderTexture(x, y, u, v, width, height, BORDER, BORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderTexture(int x, int y, int u, int v, int width, int height) {
|
||||||
|
renderTexture(x, y, u, v, width, height, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderTexture(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) {
|
||||||
|
builder.vertex(transform, x, y + height, z).color(r, g, b, 1.0f).uv(u * TEX_SCALE, (v + textureHeight) * TEX_SCALE).uv2(light).endVertex();
|
||||||
|
builder.vertex(transform, x + width, y + height, z).color(r, g, b, 1.0f).uv((u + textureWidth) * TEX_SCALE, (v + textureHeight) * TEX_SCALE).uv2(light).endVertex();
|
||||||
|
builder.vertex(transform, x + width, y, z).color(r, g, b, 1.0f).uv((u + textureWidth) * TEX_SCALE, v * TEX_SCALE).uv2(light).endVertex();
|
||||||
|
builder.vertex(transform, x, y, z).color(r, g, b, 1.0f).uv(u * TEX_SCALE, v * TEX_SCALE).uv2(light).endVertex();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,6 @@ package dan200.computercraft.client.render;
|
|||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import com.mojang.math.Axis;
|
import com.mojang.math.Axis;
|
||||||
import dan200.computercraft.client.gui.GuiSprites;
|
|
||||||
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
@@ -73,14 +72,13 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void renderFrame(Matrix4f transform, MultiBufferSource render, ComputerFamily family, int colour, int light, int width, int height) {
|
private static void renderFrame(Matrix4f transform, MultiBufferSource render, ComputerFamily family, int colour, int light, int width, int height) {
|
||||||
var texture = colour != -1 ? GuiSprites.COMPUTER_COLOUR : GuiSprites.getComputerTextures(family);
|
var texture = colour != -1 ? ComputerBorderRenderer.BACKGROUND_COLOUR : ComputerBorderRenderer.getTexture(family);
|
||||||
|
|
||||||
var r = (colour >>> 16) & 0xFF;
|
var r = ((colour >>> 16) & 0xFF) / 255.0f;
|
||||||
var g = (colour >>> 8) & 0xFF;
|
var g = ((colour >>> 8) & 0xFF) / 255.0f;
|
||||||
var b = colour & 0xFF;
|
var b = (colour & 0xFF) / 255.0f;
|
||||||
|
|
||||||
var spriteRenderer = new SpriteRenderer(transform, render.getBuffer(RenderTypes.GUI_SPRITES), 0, light, r, g, b);
|
ComputerBorderRenderer.render(transform, render.getBuffer(ComputerBorderRenderer.getRenderType(texture)), 0, 0, 0, light, width, height, true, r, g, b);
|
||||||
ComputerBorderRenderer.render(spriteRenderer, texture, 0, 0, width, height, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void renderLight(PoseStack transform, MultiBufferSource render, int colour, int width, int height) {
|
private static void renderLight(PoseStack transform, MultiBufferSource render, int colour, int width, int height) {
|
||||||
|
@@ -7,7 +7,6 @@ package dan200.computercraft.client.render;
|
|||||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.client.gui.GuiSprites;
|
|
||||||
import dan200.computercraft.client.render.monitor.MonitorTextureBufferShader;
|
import dan200.computercraft.client.render.monitor.MonitorTextureBufferShader;
|
||||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||||
import net.minecraft.client.renderer.GameRenderer;
|
import net.minecraft.client.renderer.GameRenderer;
|
||||||
@@ -54,11 +53,6 @@ public class RenderTypes {
|
|||||||
*/
|
*/
|
||||||
public static final RenderType PRINTOUT_BACKGROUND = RenderType.text(new ResourceLocation("computercraft", "textures/gui/printout.png"));
|
public static final RenderType PRINTOUT_BACKGROUND = RenderType.text(new ResourceLocation("computercraft", "textures/gui/printout.png"));
|
||||||
|
|
||||||
/**
|
|
||||||
* Render type for {@linkplain GuiSprites GUI sprites}.
|
|
||||||
*/
|
|
||||||
public static final RenderType GUI_SPRITES = RenderType.text(GuiSprites.TEXTURE);
|
|
||||||
|
|
||||||
public static MonitorTextureBufferShader getMonitorTextureBufferShader() {
|
public static MonitorTextureBufferShader getMonitorTextureBufferShader() {
|
||||||
if (monitorTboShader == null) throw new NullPointerException("MonitorTboShader has not been registered");
|
if (monitorTboShader == null) throw new NullPointerException("MonitorTboShader has not been registered");
|
||||||
return monitorTboShader;
|
return monitorTboShader;
|
||||||
|
@@ -1,131 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.client.render;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
|
||||||
import net.minecraft.client.gui.GuiComponent;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
import org.joml.Matrix4f;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link GuiComponent}-equivalent which is suitable for both rendering in to a GUI and in-world (as part of an entity
|
|
||||||
* renderer).
|
|
||||||
* <p>
|
|
||||||
* This batches all render calls together, though requires that all {@link TextureAtlasSprite}s are on the same sprite
|
|
||||||
* sheet.
|
|
||||||
*/
|
|
||||||
public class SpriteRenderer {
|
|
||||||
private final Matrix4f transform;
|
|
||||||
private final VertexConsumer builder;
|
|
||||||
private final int light;
|
|
||||||
private final int z;
|
|
||||||
private final int r, g, b;
|
|
||||||
|
|
||||||
public SpriteRenderer(Matrix4f transform, VertexConsumer builder, int z, int light, int r, int g, int b) {
|
|
||||||
this.transform = transform;
|
|
||||||
this.builder = builder;
|
|
||||||
this.z = z;
|
|
||||||
this.light = light;
|
|
||||||
this.r = r;
|
|
||||||
this.g = g;
|
|
||||||
this.b = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SpriteRenderer createForGui(PoseStack stack, VertexConsumer builder) {
|
|
||||||
return new SpriteRenderer(stack.last().pose(), builder, 0, RenderTypes.FULL_BRIGHT_LIGHTMAP, 255, 255, 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render a single sprite.
|
|
||||||
*
|
|
||||||
* @param sprite The texture to draw.
|
|
||||||
* @param x The x position of the rectangle we'll draw.
|
|
||||||
* @param y The x position of the rectangle we'll draw.
|
|
||||||
* @param width The width of the rectangle we'll draw.
|
|
||||||
* @param height The height of the rectangle we'll draw.
|
|
||||||
*/
|
|
||||||
public void blit(TextureAtlasSprite sprite, int x, int y, int width, int height) {
|
|
||||||
blit(x, y, width, height, sprite.getU0(), sprite.getV0(), sprite.getU1(), sprite.getV1());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render a horizontal 3-sliced texture (i.e. split into left, middle and right). Unlike {@link GuiComponent#blitNineSliced},
|
|
||||||
* the middle texture is stretched rather than repeated.
|
|
||||||
*
|
|
||||||
* @param sprite The texture to draw.
|
|
||||||
* @param x The x position of the rectangle we'll draw.
|
|
||||||
* @param y The x position of the rectangle we'll draw.
|
|
||||||
* @param width The width of the rectangle we'll draw.
|
|
||||||
* @param height The height of the rectangle we'll draw.
|
|
||||||
* @param leftBorder The width of the left border.
|
|
||||||
* @param rightBorder The width of the right border.
|
|
||||||
* @param textureWidth The width of the whole texture.
|
|
||||||
*/
|
|
||||||
public void blitHorizontalSliced(TextureAtlasSprite sprite, int x, int y, int width, int height, int leftBorder, int rightBorder, int textureWidth) {
|
|
||||||
// TODO(1.20.2)/TODO(1.21.0): Drive this from mcmeta files, like vanilla does.
|
|
||||||
if (width < leftBorder + rightBorder) throw new IllegalArgumentException("width is less than two borders");
|
|
||||||
|
|
||||||
var centerStart = SpriteRenderer.u(sprite, leftBorder, textureWidth);
|
|
||||||
var centerEnd = SpriteRenderer.u(sprite, textureWidth - rightBorder, textureWidth);
|
|
||||||
|
|
||||||
blit(x, y, leftBorder, height, sprite.getU0(), sprite.getV0(), centerStart, sprite.getV1());
|
|
||||||
blit(x + leftBorder, y, width - leftBorder - rightBorder, height, centerStart, sprite.getV0(), centerEnd, sprite.getV1());
|
|
||||||
blit(x + width - rightBorder, y, rightBorder, height, centerEnd, sprite.getV0(), sprite.getU1(), sprite.getV1());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render a vertical 3-sliced texture (i.e. split into top, middle and bottom). Unlike {@link GuiComponent#blitNineSliced},
|
|
||||||
* the middle texture is stretched rather than repeated.
|
|
||||||
*
|
|
||||||
* @param sprite The texture to draw.
|
|
||||||
* @param x The x position of the rectangle we'll draw.
|
|
||||||
* @param y The x position of the rectangle we'll draw.
|
|
||||||
* @param width The width of the rectangle we'll draw.
|
|
||||||
* @param height The height of the rectangle we'll draw.
|
|
||||||
* @param topBorder The height of the top border.
|
|
||||||
* @param bottomBorder The height of the bottom border.
|
|
||||||
* @param textureHeight The height of the whole texture.
|
|
||||||
*/
|
|
||||||
public void blitVerticalSliced(TextureAtlasSprite sprite, int x, int y, int width, int height, int topBorder, int bottomBorder, int textureHeight) {
|
|
||||||
// TODO(1.20.2)/TODO(1.21.0): Drive this from mcmeta files, like vanilla does.
|
|
||||||
if (width < topBorder + bottomBorder) throw new IllegalArgumentException("height is less than two borders");
|
|
||||||
|
|
||||||
var centerStart = SpriteRenderer.v(sprite, topBorder, textureHeight);
|
|
||||||
var centerEnd = SpriteRenderer.v(sprite, textureHeight - bottomBorder, textureHeight);
|
|
||||||
|
|
||||||
blit(x, y, width, topBorder, sprite.getU0(), sprite.getV0(), sprite.getU1(), centerStart);
|
|
||||||
blit(x, y + topBorder, width, height - topBorder - bottomBorder, sprite.getU0(), centerStart, sprite.getU1(), centerEnd);
|
|
||||||
blit(x, y + height - bottomBorder, width, bottomBorder, sprite.getU0(), centerEnd, sprite.getU1(), sprite.getV1());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The low-level blit function, used to render a portion of the sprite sheet. Unlike other functions, this takes uvs rather than a single sprite.
|
|
||||||
*
|
|
||||||
* @param x The x position of the rectangle we'll draw.
|
|
||||||
* @param y The x position of the rectangle we'll draw.
|
|
||||||
* @param width The width of the rectangle we'll draw.
|
|
||||||
* @param height The height of the rectangle we'll draw.
|
|
||||||
* @param u0 The first U coordinate.
|
|
||||||
* @param v0 The first V coordinate.
|
|
||||||
* @param u1 The second U coordinate.
|
|
||||||
* @param v1 The second V coordinate.
|
|
||||||
*/
|
|
||||||
public void blit(
|
|
||||||
int x, int y, int width, int height, float u0, float v0, float u1, float v1) {
|
|
||||||
builder.vertex(transform, x, y + height, z).color(r, g, b, 255).uv(u0, v1).uv2(light).endVertex();
|
|
||||||
builder.vertex(transform, x + width, y + height, z).color(r, g, b, 255).uv(u1, v1).uv2(light).endVertex();
|
|
||||||
builder.vertex(transform, x + width, y, z).color(r, g, b, 255).uv(u1, v0).uv2(light).endVertex();
|
|
||||||
builder.vertex(transform, x, y, z).color(r, g, b, 255).uv(u0, v0).uv2(light).endVertex();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float u(TextureAtlasSprite sprite, int x, int width) {
|
|
||||||
return sprite.getU((double) x / width * 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float v(TextureAtlasSprite sprite, int y, int height) {
|
|
||||||
return sprite.getV((double) y / height * 16);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -7,10 +7,7 @@ package dan200.computercraft.client.render.monitor;
|
|||||||
import com.mojang.blaze3d.platform.GlStateManager;
|
import com.mojang.blaze3d.platform.GlStateManager;
|
||||||
import com.mojang.blaze3d.platform.MemoryTracker;
|
import com.mojang.blaze3d.platform.MemoryTracker;
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.*;
|
||||||
import com.mojang.blaze3d.vertex.Tesselator;
|
|
||||||
import com.mojang.blaze3d.vertex.VertexBuffer;
|
|
||||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
|
||||||
import com.mojang.math.Axis;
|
import com.mojang.math.Axis;
|
||||||
import dan200.computercraft.client.FrameInfo;
|
import dan200.computercraft.client.FrameInfo;
|
||||||
import dan200.computercraft.client.integration.ShaderMod;
|
import dan200.computercraft.client.integration.ShaderMod;
|
||||||
@@ -170,7 +167,7 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
|
|||||||
tboVertex(buffer, matrix, -xMargin, pixelHeight + yMargin);
|
tboVertex(buffer, matrix, -xMargin, pixelHeight + yMargin);
|
||||||
tboVertex(buffer, matrix, pixelWidth + xMargin, -yMargin);
|
tboVertex(buffer, matrix, pixelWidth + xMargin, -yMargin);
|
||||||
tboVertex(buffer, matrix, pixelWidth + xMargin, pixelHeight + yMargin);
|
tboVertex(buffer, matrix, pixelWidth + xMargin, pixelHeight + yMargin);
|
||||||
RenderTypes.MONITOR_TBO.end(buffer, 0, 0, 0);
|
RenderTypes.MONITOR_TBO.end(buffer, VertexSorting.DISTANCE_TO_ORIGIN);
|
||||||
}
|
}
|
||||||
case VBO -> {
|
case VBO -> {
|
||||||
var backgroundBuffer = assertNonNull(renderState.backgroundBuffer);
|
var backgroundBuffer = assertNonNull(renderState.backgroundBuffer);
|
||||||
|
@@ -25,6 +25,7 @@ public class DirectVertexBuffer extends VertexBuffer {
|
|||||||
private int actualIndexCount;
|
private int actualIndexCount;
|
||||||
|
|
||||||
public DirectVertexBuffer() {
|
public DirectVertexBuffer() {
|
||||||
|
super(Usage.STATIC);
|
||||||
if (DirectBuffers.HAS_DSA) {
|
if (DirectBuffers.HAS_DSA) {
|
||||||
RenderSystem.glDeleteBuffers(vertexBufferId);
|
RenderSystem.glDeleteBuffers(vertexBufferId);
|
||||||
if (DirectBuffers.ON_LINUX) BufferUploader.reset(); // See comment on DirectBuffers.deleteBuffer.
|
if (DirectBuffers.ON_LINUX) BufferUploader.reset(); // See comment on DirectBuffers.deleteBuffer.
|
||||||
|
@@ -4,10 +4,8 @@
|
|||||||
|
|
||||||
package dan200.computercraft.data.client;
|
package dan200.computercraft.data.client;
|
||||||
|
|
||||||
import dan200.computercraft.client.gui.GuiSprites;
|
|
||||||
import dan200.computercraft.data.DataProviders;
|
import dan200.computercraft.data.DataProviders;
|
||||||
import dan200.computercraft.shared.turtle.inventory.UpgradeSlot;
|
import dan200.computercraft.shared.turtle.inventory.UpgradeSlot;
|
||||||
import net.minecraft.client.renderer.texture.atlas.SpriteSource;
|
|
||||||
import net.minecraft.client.renderer.texture.atlas.SpriteSources;
|
import net.minecraft.client.renderer.texture.atlas.SpriteSources;
|
||||||
import net.minecraft.client.renderer.texture.atlas.sources.SingleFile;
|
import net.minecraft.client.renderer.texture.atlas.sources.SingleFile;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
@@ -15,7 +13,6 @@ import net.minecraft.server.packs.PackType;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A version of {@link DataProviders} which relies on client-side classes.
|
* A version of {@link DataProviders} which relies on client-side classes.
|
||||||
@@ -32,17 +29,6 @@ public final class ClientDataProviders {
|
|||||||
new SingleFile(UpgradeSlot.LEFT_UPGRADE, Optional.empty()),
|
new SingleFile(UpgradeSlot.LEFT_UPGRADE, Optional.empty()),
|
||||||
new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty())
|
new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty())
|
||||||
));
|
));
|
||||||
out.accept(GuiSprites.SPRITE_SHEET, Stream.of(
|
|
||||||
// Buttons
|
|
||||||
GuiSprites.TURNED_OFF.textures(),
|
|
||||||
GuiSprites.TURNED_ON.textures(),
|
|
||||||
GuiSprites.TERMINATE.textures(),
|
|
||||||
// Computers
|
|
||||||
GuiSprites.COMPUTER_NORMAL.textures(),
|
|
||||||
GuiSprites.COMPUTER_ADVANCED.textures(),
|
|
||||||
GuiSprites.COMPUTER_COMMAND.textures(),
|
|
||||||
GuiSprites.COMPUTER_COLOUR.textures()
|
|
||||||
).flatMap(x -> x).<SpriteSource>map(x -> new SingleFile(x, Optional.empty())).toList());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,7 @@ import net.minecraft.world.level.storage.loot.entries.LootItem;
|
|||||||
import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
|
import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
|
||||||
import net.minecraft.world.level.storage.loot.functions.CopyNameFunction;
|
import net.minecraft.world.level.storage.loot.functions.CopyNameFunction;
|
||||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
|
||||||
import net.minecraft.world.level.storage.loot.predicates.AlternativeLootItemCondition;
|
import net.minecraft.world.level.storage.loot.predicates.AnyOfCondition;
|
||||||
import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition;
|
import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition;
|
||||||
import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition;
|
import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition;
|
||||||
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
|
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
|
||||||
@@ -79,7 +79,7 @@ class LootTableProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void registerGeneric(BiConsumer<ResourceLocation, LootTable.Builder> add) {
|
private static void registerGeneric(BiConsumer<ResourceLocation, LootTable.Builder> add) {
|
||||||
add.accept(CommonHooks.LOOT_TREASURE_DISK, LootTable.lootTable());
|
add.accept(CommonHooks.TREASURE_DISK_LOOT, LootTable.lootTable());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void selfDrop(BiConsumer<ResourceLocation, LootTable.Builder> add, Supplier<? extends Block> wrapper) {
|
private static void selfDrop(BiConsumer<ResourceLocation, LootTable.Builder> add, Supplier<? extends Block> wrapper) {
|
||||||
@@ -98,7 +98,7 @@ class LootTableProvider {
|
|||||||
blockDrop(
|
blockDrop(
|
||||||
add, block,
|
add, block,
|
||||||
DynamicLoot.dynamicEntry(new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer")),
|
DynamicLoot.dynamicEntry(new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer")),
|
||||||
AlternativeLootItemCondition.alternative(
|
AnyOfCondition.anyOf(
|
||||||
BlockNamedEntityLootCondition.BUILDER,
|
BlockNamedEntityLootCondition.BUILDER,
|
||||||
HasComputerIdLootCondition.BUILDER,
|
HasComputerIdLootCondition.BUILDER,
|
||||||
PlayerCreativeLootCondition.BUILDER.invert()
|
PlayerCreativeLootCondition.BUILDER.invert()
|
||||||
|
@@ -88,6 +88,8 @@ class TagProvider {
|
|||||||
ModRegistry.Items.MONITOR_ADVANCED.get()
|
ModRegistry.Items.MONITOR_ADVANCED.get()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
tags.tag(ItemTags.BOOKSHELF_BOOKS).add(ModRegistry.Items.PRINTED_BOOK.get());
|
||||||
|
|
||||||
tags.tag(ComputerCraftTags.Items.TURTLE_CAN_PLACE)
|
tags.tag(ComputerCraftTags.Items.TURTLE_CAN_PLACE)
|
||||||
.add(Items.GLASS_BOTTLE)
|
.add(Items.GLASS_BOTTLE)
|
||||||
.addTag(ItemTags.BOATS);
|
.addTag(ItemTags.BOATS);
|
||||||
|
@@ -28,8 +28,6 @@ import net.minecraft.world.level.storage.loot.entries.LootTableReference;
|
|||||||
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
|
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
@@ -75,9 +73,9 @@ public final class CommonHooks {
|
|||||||
MonitorWatcher.onWatch(chunk, player);
|
MonitorWatcher.onWatch(chunk, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final ResourceLocation LOOT_TREASURE_DISK = new ResourceLocation(ComputerCraftAPI.MOD_ID, "treasure_disk");
|
public static final ResourceLocation TREASURE_DISK_LOOT = new ResourceLocation(ComputerCraftAPI.MOD_ID, "treasure_disk");
|
||||||
|
|
||||||
private static final Set<ResourceLocation> TABLES = new HashSet<>(Arrays.asList(
|
private static final Set<ResourceLocation> TREASURE_DISK_LOOT_TABLES = Set.of(
|
||||||
BuiltInLootTables.SIMPLE_DUNGEON,
|
BuiltInLootTables.SIMPLE_DUNGEON,
|
||||||
BuiltInLootTables.ABANDONED_MINESHAFT,
|
BuiltInLootTables.ABANDONED_MINESHAFT,
|
||||||
BuiltInLootTables.STRONGHOLD_CORRIDOR,
|
BuiltInLootTables.STRONGHOLD_CORRIDOR,
|
||||||
@@ -88,14 +86,15 @@ public final class CommonHooks {
|
|||||||
BuiltInLootTables.IGLOO_CHEST,
|
BuiltInLootTables.IGLOO_CHEST,
|
||||||
BuiltInLootTables.WOODLAND_MANSION,
|
BuiltInLootTables.WOODLAND_MANSION,
|
||||||
BuiltInLootTables.VILLAGE_CARTOGRAPHER
|
BuiltInLootTables.VILLAGE_CARTOGRAPHER
|
||||||
));
|
);
|
||||||
|
|
||||||
|
|
||||||
public static @Nullable LootPool.Builder getExtraLootPool(ResourceLocation lootTable) {
|
public static @Nullable LootPool.Builder getExtraLootPool(ResourceLocation lootTable) {
|
||||||
if (!lootTable.getNamespace().equals("minecraft") || !TABLES.contains(lootTable)) return null;
|
if (!lootTable.getNamespace().equals("minecraft") || !TREASURE_DISK_LOOT_TABLES.contains(lootTable)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return LootPool.lootPool()
|
return LootPool.lootPool()
|
||||||
.add(LootTableReference.lootTableReference(LOOT_TREASURE_DISK))
|
.add(LootTableReference.lootTableReference(TREASURE_DISK_LOOT))
|
||||||
.setRolls(ConstantValue.exactly(1));
|
.setRolls(ConstantValue.exactly(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,7 +15,6 @@ import dan200.computercraft.api.upgrades.UpgradeData;
|
|||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
import dan200.computercraft.impl.PocketUpgrades;
|
import dan200.computercraft.impl.PocketUpgrades;
|
||||||
import dan200.computercraft.impl.TurtleUpgrades;
|
import dan200.computercraft.impl.TurtleUpgrades;
|
||||||
import dan200.computercraft.shared.command.UserLevel;
|
|
||||||
import dan200.computercraft.shared.command.arguments.ComputerArgumentType;
|
import dan200.computercraft.shared.command.arguments.ComputerArgumentType;
|
||||||
import dan200.computercraft.shared.command.arguments.ComputersArgumentType;
|
import dan200.computercraft.shared.command.arguments.ComputersArgumentType;
|
||||||
import dan200.computercraft.shared.command.arguments.RepeatArgumentType;
|
import dan200.computercraft.shared.command.arguments.RepeatArgumentType;
|
||||||
@@ -38,7 +37,6 @@ import dan200.computercraft.shared.data.HasComputerIdLootCondition;
|
|||||||
import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
|
import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
|
||||||
import dan200.computercraft.shared.details.BlockDetails;
|
import dan200.computercraft.shared.details.BlockDetails;
|
||||||
import dan200.computercraft.shared.details.ItemDetails;
|
import dan200.computercraft.shared.details.ItemDetails;
|
||||||
import dan200.computercraft.shared.integration.PermissionRegistry;
|
|
||||||
import dan200.computercraft.shared.media.items.DiskItem;
|
import dan200.computercraft.shared.media.items.DiskItem;
|
||||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||||
import dan200.computercraft.shared.media.items.RecordMedia;
|
import dan200.computercraft.shared.media.items.RecordMedia;
|
||||||
@@ -79,7 +77,6 @@ import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
|
|||||||
import dan200.computercraft.shared.turtle.upgrades.*;
|
import dan200.computercraft.shared.turtle.upgrades.*;
|
||||||
import dan200.computercraft.shared.util.ImpostorRecipe;
|
import dan200.computercraft.shared.util.ImpostorRecipe;
|
||||||
import dan200.computercraft.shared.util.ImpostorShapelessRecipe;
|
import dan200.computercraft.shared.util.ImpostorShapelessRecipe;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||||
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
|
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
@@ -97,11 +94,10 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
|||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.material.Material;
|
import net.minecraft.world.level.material.MapColor;
|
||||||
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
|
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
|
||||||
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers ComputerCraft's registry entries and additional objects, such as {@link CauldronInteraction}s and
|
* Registers ComputerCraft's registry entries and additional objects, such as {@link CauldronInteraction}s and
|
||||||
@@ -117,7 +113,7 @@ public final class ModRegistry {
|
|||||||
static final RegistrationHelper<Block> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.BLOCK);
|
static final RegistrationHelper<Block> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.BLOCK);
|
||||||
|
|
||||||
private static BlockBehaviour.Properties properties() {
|
private static BlockBehaviour.Properties properties() {
|
||||||
return BlockBehaviour.Properties.of(Material.STONE).strength(2);
|
return BlockBehaviour.Properties.of().strength(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BlockBehaviour.Properties computerProperties() {
|
private static BlockBehaviour.Properties computerProperties() {
|
||||||
@@ -127,17 +123,17 @@ public final class ModRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static BlockBehaviour.Properties turtleProperties() {
|
private static BlockBehaviour.Properties turtleProperties() {
|
||||||
return BlockBehaviour.Properties.of(Material.STONE).strength(2.5f);
|
return BlockBehaviour.Properties.of().strength(2.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BlockBehaviour.Properties modemProperties() {
|
private static BlockBehaviour.Properties modemProperties() {
|
||||||
return BlockBehaviour.Properties.of(Material.STONE).strength(1.5f);
|
return BlockBehaviour.Properties.of().strength(1.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_NORMAL = REGISTRY.register("computer_normal",
|
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_NORMAL = REGISTRY.register("computer_normal",
|
||||||
() -> new ComputerBlock<>(computerProperties(), ComputerFamily.NORMAL, BlockEntities.COMPUTER_NORMAL));
|
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.STONE), ComputerFamily.NORMAL, BlockEntities.COMPUTER_NORMAL));
|
||||||
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_ADVANCED = REGISTRY.register("computer_advanced",
|
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_ADVANCED = REGISTRY.register("computer_advanced",
|
||||||
() -> new ComputerBlock<>(computerProperties(), ComputerFamily.ADVANCED, BlockEntities.COMPUTER_ADVANCED));
|
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.GOLD), ComputerFamily.ADVANCED, BlockEntities.COMPUTER_ADVANCED));
|
||||||
|
|
||||||
public static final RegistryEntry<ComputerBlock<CommandComputerBlockEntity>> COMPUTER_COMMAND = REGISTRY.register("computer_command", () -> new ComputerBlock<>(
|
public static final RegistryEntry<ComputerBlock<CommandComputerBlockEntity>> COMPUTER_COMMAND = REGISTRY.register("computer_command", () -> new ComputerBlock<>(
|
||||||
computerProperties().strength(-1, 6000000.0F),
|
computerProperties().strength(-1, 6000000.0F),
|
||||||
@@ -145,27 +141,27 @@ public final class ModRegistry {
|
|||||||
));
|
));
|
||||||
|
|
||||||
public static final RegistryEntry<TurtleBlock> TURTLE_NORMAL = REGISTRY.register("turtle_normal",
|
public static final RegistryEntry<TurtleBlock> TURTLE_NORMAL = REGISTRY.register("turtle_normal",
|
||||||
() -> new TurtleBlock(turtleProperties(), ComputerFamily.NORMAL, BlockEntities.TURTLE_NORMAL));
|
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.STONE), ComputerFamily.NORMAL, BlockEntities.TURTLE_NORMAL));
|
||||||
public static final RegistryEntry<TurtleBlock> TURTLE_ADVANCED = REGISTRY.register("turtle_advanced",
|
public static final RegistryEntry<TurtleBlock> TURTLE_ADVANCED = REGISTRY.register("turtle_advanced",
|
||||||
() -> new TurtleBlock(turtleProperties(), ComputerFamily.ADVANCED, BlockEntities.TURTLE_ADVANCED));
|
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.GOLD), ComputerFamily.ADVANCED, BlockEntities.TURTLE_ADVANCED));
|
||||||
|
|
||||||
public static final RegistryEntry<SpeakerBlock> SPEAKER = REGISTRY.register("speaker", () -> new SpeakerBlock(properties()));
|
public static final RegistryEntry<SpeakerBlock> SPEAKER = REGISTRY.register("speaker", () -> new SpeakerBlock(properties().mapColor(MapColor.STONE)));
|
||||||
public static final RegistryEntry<DiskDriveBlock> DISK_DRIVE = REGISTRY.register("disk_drive", () -> new DiskDriveBlock(properties()));
|
public static final RegistryEntry<DiskDriveBlock> DISK_DRIVE = REGISTRY.register("disk_drive", () -> new DiskDriveBlock(properties().mapColor(MapColor.STONE)));
|
||||||
public static final RegistryEntry<PrinterBlock> PRINTER = REGISTRY.register("printer", () -> new PrinterBlock(properties()));
|
public static final RegistryEntry<PrinterBlock> PRINTER = REGISTRY.register("printer", () -> new PrinterBlock(properties().mapColor(MapColor.STONE)));
|
||||||
|
|
||||||
public static final RegistryEntry<MonitorBlock> MONITOR_NORMAL = REGISTRY.register("monitor_normal",
|
public static final RegistryEntry<MonitorBlock> MONITOR_NORMAL = REGISTRY.register("monitor_normal",
|
||||||
() -> new MonitorBlock(properties(), BlockEntities.MONITOR_NORMAL));
|
() -> new MonitorBlock(properties().mapColor(MapColor.STONE), BlockEntities.MONITOR_NORMAL));
|
||||||
public static final RegistryEntry<MonitorBlock> MONITOR_ADVANCED = REGISTRY.register("monitor_advanced",
|
public static final RegistryEntry<MonitorBlock> MONITOR_ADVANCED = REGISTRY.register("monitor_advanced",
|
||||||
() -> new MonitorBlock(properties(), BlockEntities.MONITOR_ADVANCED));
|
() -> new MonitorBlock(properties().mapColor(MapColor.GOLD), BlockEntities.MONITOR_ADVANCED));
|
||||||
|
|
||||||
public static final RegistryEntry<WirelessModemBlock> WIRELESS_MODEM_NORMAL = REGISTRY.register("wireless_modem_normal",
|
public static final RegistryEntry<WirelessModemBlock> WIRELESS_MODEM_NORMAL = REGISTRY.register("wireless_modem_normal",
|
||||||
() -> new WirelessModemBlock(properties(), BlockEntities.WIRELESS_MODEM_NORMAL));
|
() -> new WirelessModemBlock(properties().mapColor(MapColor.STONE), BlockEntities.WIRELESS_MODEM_NORMAL));
|
||||||
public static final RegistryEntry<WirelessModemBlock> WIRELESS_MODEM_ADVANCED = REGISTRY.register("wireless_modem_advanced",
|
public static final RegistryEntry<WirelessModemBlock> WIRELESS_MODEM_ADVANCED = REGISTRY.register("wireless_modem_advanced",
|
||||||
() -> new WirelessModemBlock(properties(), BlockEntities.WIRELESS_MODEM_ADVANCED));
|
() -> new WirelessModemBlock(properties().mapColor(MapColor.GOLD), BlockEntities.WIRELESS_MODEM_ADVANCED));
|
||||||
|
|
||||||
public static final RegistryEntry<WiredModemFullBlock> WIRED_MODEM_FULL = REGISTRY.register("wired_modem_full",
|
public static final RegistryEntry<WiredModemFullBlock> WIRED_MODEM_FULL = REGISTRY.register("wired_modem_full",
|
||||||
() -> new WiredModemFullBlock(modemProperties()));
|
() -> new WiredModemFullBlock(modemProperties().mapColor(MapColor.STONE)));
|
||||||
public static final RegistryEntry<CableBlock> CABLE = REGISTRY.register("cable", () -> new CableBlock(modemProperties()));
|
public static final RegistryEntry<CableBlock> CABLE = REGISTRY.register("cable", () -> new CableBlock(modemProperties().mapColor(MapColor.STONE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class BlockEntities {
|
public static class BlockEntities {
|
||||||
@@ -370,63 +366,11 @@ public final class ModRegistry {
|
|||||||
public static final RegistryEntry<ImpostorShapelessRecipe.Serializer> IMPOSTOR_SHAPELESS = REGISTRY.register("impostor_shapeless", ImpostorShapelessRecipe.Serializer::new);
|
public static final RegistryEntry<ImpostorShapelessRecipe.Serializer> IMPOSTOR_SHAPELESS = REGISTRY.register("impostor_shapeless", ImpostorShapelessRecipe.Serializer::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Permissions {
|
static class CreativeTabs {
|
||||||
static final PermissionRegistry REGISTRY = PermissionRegistry.create();
|
static final RegistrationHelper<CreativeModeTab> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.CREATIVE_MODE_TAB);
|
||||||
|
|
||||||
public static final Predicate<CommandSourceStack> PERMISSION_DUMP = REGISTRY.registerCommand("dump", UserLevel.OWNER_OP);
|
@SuppressWarnings("unused")
|
||||||
public static final Predicate<CommandSourceStack> PERMISSION_SHUTDOWN = REGISTRY.registerCommand("shutdown", UserLevel.OWNER_OP);
|
private static final RegistryEntry<CreativeModeTab> TAB = REGISTRY.register("tab", () -> PlatformHelper.get().newCreativeModeTab()
|
||||||
public static final Predicate<CommandSourceStack> PERMISSION_TURN_ON = REGISTRY.registerCommand("turn_on", UserLevel.OWNER_OP);
|
|
||||||
public static final Predicate<CommandSourceStack> PERMISSION_TP = REGISTRY.registerCommand("tp", UserLevel.OP);
|
|
||||||
public static final Predicate<CommandSourceStack> PERMISSION_TRACK = REGISTRY.registerCommand("track", UserLevel.OWNER_OP);
|
|
||||||
public static final Predicate<CommandSourceStack> PERMISSION_QUEUE = REGISTRY.registerCommand("queue", UserLevel.ANYONE);
|
|
||||||
public static final Predicate<CommandSourceStack> PERMISSION_VIEW = REGISTRY.registerCommand("view", UserLevel.OP);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register any objects which don't have to be done on the main thread.
|
|
||||||
*/
|
|
||||||
public static void register() {
|
|
||||||
Blocks.REGISTRY.register();
|
|
||||||
BlockEntities.REGISTRY.register();
|
|
||||||
Items.REGISTRY.register();
|
|
||||||
TurtleSerialisers.REGISTRY.register();
|
|
||||||
PocketUpgradeSerialisers.REGISTRY.register();
|
|
||||||
Menus.REGISTRY.register();
|
|
||||||
ArgumentTypes.REGISTRY.register();
|
|
||||||
LootItemConditionTypes.REGISTRY.register();
|
|
||||||
RecipeSerializers.REGISTRY.register();
|
|
||||||
Permissions.REGISTRY.register();
|
|
||||||
|
|
||||||
// Register bundled power providers
|
|
||||||
ComputerCraftAPI.registerBundledRedstoneProvider(new DefaultBundledRedstoneProvider());
|
|
||||||
ComputerCraftAPI.registerRefuelHandler(new FurnaceRefuelHandler());
|
|
||||||
ComputerCraftAPI.registerMediaProvider(stack -> {
|
|
||||||
var item = stack.getItem();
|
|
||||||
if (item instanceof IMedia media) return media;
|
|
||||||
if (item instanceof RecordItem) return RecordMedia.INSTANCE;
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
VanillaDetailRegistries.ITEM_STACK.addProvider(ItemDetails::fill);
|
|
||||||
VanillaDetailRegistries.BLOCK_IN_WORLD.addProvider(BlockDetails::fill);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register any objects which must be done on the main thread.
|
|
||||||
*/
|
|
||||||
public static void registerMainThread() {
|
|
||||||
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION);
|
|
||||||
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configure a {@link CreativeModeTab.Builder} to contain all of ComputerCraft's items.
|
|
||||||
*
|
|
||||||
* @param builder The builder to configure.
|
|
||||||
* @return The same building, for calling {@link CreativeModeTab.Builder#build()} on.
|
|
||||||
*/
|
|
||||||
public static CreativeModeTab.Builder registerCreativeTab(CreativeModeTab.Builder builder) {
|
|
||||||
return builder
|
|
||||||
.icon(() -> new ItemStack(Items.COMPUTER_NORMAL.get()))
|
.icon(() -> new ItemStack(Items.COMPUTER_NORMAL.get()))
|
||||||
.title(Component.translatable("itemGroup.computercraft"))
|
.title(Component.translatable("itemGroup.computercraft"))
|
||||||
.displayItems((context, out) -> {
|
.displayItems((context, out) -> {
|
||||||
@@ -458,7 +402,45 @@ public final class ModRegistry {
|
|||||||
for (var colour = 0; colour < 16; colour++) {
|
for (var colour = 0; colour < 16; colour++) {
|
||||||
out.accept(DiskItem.createFromIDAndColour(-1, null, Colour.VALUES[colour].getHex()));
|
out.accept(DiskItem.createFromIDAndColour(-1, null, Colour.VALUES[colour].getHex()));
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register any objects which don't have to be done on the main thread.
|
||||||
|
*/
|
||||||
|
public static void register() {
|
||||||
|
Blocks.REGISTRY.register();
|
||||||
|
BlockEntities.REGISTRY.register();
|
||||||
|
Items.REGISTRY.register();
|
||||||
|
TurtleSerialisers.REGISTRY.register();
|
||||||
|
PocketUpgradeSerialisers.REGISTRY.register();
|
||||||
|
Menus.REGISTRY.register();
|
||||||
|
ArgumentTypes.REGISTRY.register();
|
||||||
|
LootItemConditionTypes.REGISTRY.register();
|
||||||
|
RecipeSerializers.REGISTRY.register();
|
||||||
|
CreativeTabs.REGISTRY.register();
|
||||||
|
|
||||||
|
// Register bundled power providers
|
||||||
|
ComputerCraftAPI.registerBundledRedstoneProvider(new DefaultBundledRedstoneProvider());
|
||||||
|
ComputerCraftAPI.registerRefuelHandler(new FurnaceRefuelHandler());
|
||||||
|
ComputerCraftAPI.registerMediaProvider(stack -> {
|
||||||
|
var item = stack.getItem();
|
||||||
|
if (item instanceof IMedia media) return media;
|
||||||
|
if (item instanceof RecordItem) return RecordMedia.INSTANCE;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
VanillaDetailRegistries.ITEM_STACK.addProvider(ItemDetails::fill);
|
||||||
|
VanillaDetailRegistries.BLOCK_IN_WORLD.addProvider(BlockDetails::fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register any objects which must be done on the main thread.
|
||||||
|
*/
|
||||||
|
public static void registerMainThread() {
|
||||||
|
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION);
|
||||||
|
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle) {
|
private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle) {
|
||||||
|
@@ -11,7 +11,6 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|||||||
import com.mojang.brigadier.suggestion.Suggestions;
|
import com.mojang.brigadier.suggestion.Suggestions;
|
||||||
import dan200.computercraft.core.computer.ComputerSide;
|
import dan200.computercraft.core.computer.ComputerSide;
|
||||||
import dan200.computercraft.core.metrics.Metrics;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
|
||||||
import dan200.computercraft.shared.command.arguments.ComputersArgumentType;
|
import dan200.computercraft.shared.command.arguments.ComputersArgumentType;
|
||||||
import dan200.computercraft.shared.command.text.TableBuilder;
|
import dan200.computercraft.shared.command.text.TableBuilder;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
@@ -61,7 +60,7 @@ public final class CommandComputerCraft {
|
|||||||
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||||
dispatcher.register(choice("computercraft")
|
dispatcher.register(choice("computercraft")
|
||||||
.then(literal("dump")
|
.then(literal("dump")
|
||||||
.requires(ModRegistry.Permissions.PERMISSION_DUMP)
|
.requires(UserLevel.OWNER_OP)
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
var table = new TableBuilder("DumpAll", "Computer", "On", "Position");
|
var table = new TableBuilder("DumpAll", "Computer", "On", "Position");
|
||||||
|
|
||||||
@@ -119,7 +118,7 @@ public final class CommandComputerCraft {
|
|||||||
})))
|
})))
|
||||||
|
|
||||||
.then(command("shutdown")
|
.then(command("shutdown")
|
||||||
.requires(ModRegistry.Permissions.PERMISSION_SHUTDOWN)
|
.requires(UserLevel.OWNER_OP)
|
||||||
.argManyValue("computers", manyComputers(), s -> ServerContext.get(s.getServer()).registry().getComputers())
|
.argManyValue("computers", manyComputers(), s -> ServerContext.get(s.getServer()).registry().getComputers())
|
||||||
.executes((context, computerSelectors) -> {
|
.executes((context, computerSelectors) -> {
|
||||||
var shutdown = 0;
|
var shutdown = 0;
|
||||||
@@ -128,12 +127,14 @@ public final class CommandComputerCraft {
|
|||||||
if (computer.isOn()) shutdown++;
|
if (computer.isOn()) shutdown++;
|
||||||
computer.shutdown();
|
computer.shutdown();
|
||||||
}
|
}
|
||||||
context.getSource().sendSuccess(Component.translatable("commands.computercraft.shutdown.done", shutdown, computers.size()), false);
|
|
||||||
|
var didShutdown = shutdown;
|
||||||
|
context.getSource().sendSuccess(() -> Component.translatable("commands.computercraft.shutdown.done", didShutdown, computers.size()), false);
|
||||||
return shutdown;
|
return shutdown;
|
||||||
}))
|
}))
|
||||||
|
|
||||||
.then(command("turn-on")
|
.then(command("turn-on")
|
||||||
.requires(ModRegistry.Permissions.PERMISSION_TURN_ON)
|
.requires(UserLevel.OWNER_OP)
|
||||||
.argManyValue("computers", manyComputers(), s -> ServerContext.get(s.getServer()).registry().getComputers())
|
.argManyValue("computers", manyComputers(), s -> ServerContext.get(s.getServer()).registry().getComputers())
|
||||||
.executes((context, computerSelectors) -> {
|
.executes((context, computerSelectors) -> {
|
||||||
var on = 0;
|
var on = 0;
|
||||||
@@ -142,12 +143,14 @@ public final class CommandComputerCraft {
|
|||||||
if (!computer.isOn()) on++;
|
if (!computer.isOn()) on++;
|
||||||
computer.turnOn();
|
computer.turnOn();
|
||||||
}
|
}
|
||||||
context.getSource().sendSuccess(Component.translatable("commands.computercraft.turn_on.done", on, computers.size()), false);
|
|
||||||
|
var didOn = on;
|
||||||
|
context.getSource().sendSuccess(() -> Component.translatable("commands.computercraft.turn_on.done", didOn, computers.size()), false);
|
||||||
return on;
|
return on;
|
||||||
}))
|
}))
|
||||||
|
|
||||||
.then(command("tp")
|
.then(command("tp")
|
||||||
.requires(ModRegistry.Permissions.PERMISSION_TP)
|
.requires(UserLevel.OP)
|
||||||
.arg("computer", oneComputer())
|
.arg("computer", oneComputer())
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
var computer = getComputerArgument(context, "computer");
|
var computer = getComputerArgument(context, "computer");
|
||||||
@@ -172,7 +175,7 @@ public final class CommandComputerCraft {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
.then(command("queue")
|
.then(command("queue")
|
||||||
.requires(ModRegistry.Permissions.PERMISSION_QUEUE)
|
.requires(UserLevel.ANYONE)
|
||||||
.arg(
|
.arg(
|
||||||
RequiredArgumentBuilder.<CommandSourceStack, ComputersArgumentType.ComputersSupplier>argument("computer", manyComputers())
|
RequiredArgumentBuilder.<CommandSourceStack, ComputersArgumentType.ComputersSupplier>argument("computer", manyComputers())
|
||||||
.suggests((context, builder) -> Suggestions.empty())
|
.suggests((context, builder) -> Suggestions.empty())
|
||||||
@@ -194,7 +197,7 @@ public final class CommandComputerCraft {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
.then(command("view")
|
.then(command("view")
|
||||||
.requires(ModRegistry.Permissions.PERMISSION_VIEW)
|
.requires(UserLevel.OP)
|
||||||
.arg("computer", oneComputer())
|
.arg("computer", oneComputer())
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
var player = context.getSource().getPlayerOrException();
|
var player = context.getSource().getPlayerOrException();
|
||||||
@@ -214,13 +217,13 @@ public final class CommandComputerCraft {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
.then(choice("track")
|
.then(choice("track")
|
||||||
.requires(ModRegistry.Permissions.PERMISSION_TRACK)
|
|
||||||
.then(command("start")
|
.then(command("start")
|
||||||
|
.requires(UserLevel.OWNER_OP)
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
getMetricsInstance(context.getSource()).start();
|
getMetricsInstance(context.getSource()).start();
|
||||||
|
|
||||||
var stopCommand = "/computercraft track stop";
|
var stopCommand = "/computercraft track stop";
|
||||||
context.getSource().sendSuccess(Component.translatable(
|
context.getSource().sendSuccess(() -> Component.translatable(
|
||||||
"commands.computercraft.track.start.stop",
|
"commands.computercraft.track.start.stop",
|
||||||
link(text(stopCommand), stopCommand, Component.translatable("commands.computercraft.track.stop.action"))
|
link(text(stopCommand), stopCommand, Component.translatable("commands.computercraft.track.stop.action"))
|
||||||
), false);
|
), false);
|
||||||
@@ -228,6 +231,7 @@ public final class CommandComputerCraft {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
.then(command("stop")
|
.then(command("stop")
|
||||||
|
.requires(UserLevel.OWNER_OP)
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
var timings = getMetricsInstance(context.getSource());
|
var timings = getMetricsInstance(context.getSource());
|
||||||
if (!timings.stop()) throw NOT_TRACKING_EXCEPTION.create();
|
if (!timings.stop()) throw NOT_TRACKING_EXCEPTION.create();
|
||||||
@@ -236,6 +240,7 @@ public final class CommandComputerCraft {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
.then(command("dump")
|
.then(command("dump")
|
||||||
|
.requires(UserLevel.OWNER_OP)
|
||||||
.argManyValue("fields", metric(), DEFAULT_FIELDS)
|
.argManyValue("fields", metric(), DEFAULT_FIELDS)
|
||||||
.executes((context, fields) -> {
|
.executes((context, fields) -> {
|
||||||
AggregatedMetric sort;
|
AggregatedMetric sort;
|
||||||
@@ -269,25 +274,23 @@ public final class CommandComputerCraft {
|
|||||||
out.append(" (id " + computerId + ")");
|
out.append(" (id " + computerId + ")");
|
||||||
|
|
||||||
// And, if we're a player, some useful links
|
// And, if we're a player, some useful links
|
||||||
if (serverComputer != null && isPlayer(source)) {
|
if (serverComputer != null && UserLevel.OP.test(source) && isPlayer(source)) {
|
||||||
if (ModRegistry.Permissions.PERMISSION_TP.test(source)) {
|
out
|
||||||
out.append(" ").append(link(
|
.append(" ")
|
||||||
|
.append(link(
|
||||||
text("\u261b"),
|
text("\u261b"),
|
||||||
"/computercraft tp " + serverComputer.getInstanceID(),
|
"/computercraft tp " + serverComputer.getInstanceID(),
|
||||||
Component.translatable("commands.computercraft.tp.action")
|
Component.translatable("commands.computercraft.tp.action")
|
||||||
));
|
))
|
||||||
}
|
.append(" ")
|
||||||
|
.append(link(
|
||||||
if (ModRegistry.Permissions.PERMISSION_VIEW.test(source)) {
|
|
||||||
out.append(" ").append(link(
|
|
||||||
text("\u20e2"),
|
text("\u20e2"),
|
||||||
"/computercraft view " + serverComputer.getInstanceID(),
|
"/computercraft view " + serverComputer.getInstanceID(),
|
||||||
Component.translatable("commands.computercraft.view.action")
|
Component.translatable("commands.computercraft.view.action")
|
||||||
));
|
));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPlayer(source) && UserLevel.isOwner(source)) {
|
if (UserLevel.OWNER.test(source) && isPlayer(source)) {
|
||||||
var linkPath = linkStorage(source, computerId);
|
var linkPath = linkStorage(source, computerId);
|
||||||
if (linkPath != null) out.append(" ").append(linkPath);
|
if (linkPath != null) out.append(" ").append(linkPath);
|
||||||
}
|
}
|
||||||
@@ -296,7 +299,7 @@ public final class CommandComputerCraft {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Component linkPosition(CommandSourceStack context, ServerComputer computer) {
|
private static Component linkPosition(CommandSourceStack context, ServerComputer computer) {
|
||||||
if (ModRegistry.Permissions.PERMISSION_TP.test(context)) {
|
if (UserLevel.OP.test(context)) {
|
||||||
return link(
|
return link(
|
||||||
position(computer.getPosition()),
|
position(computer.getPosition()),
|
||||||
"/computercraft tp " + computer.getInstanceID(),
|
"/computercraft tp " + computer.getInstanceID(),
|
||||||
|
@@ -10,6 +10,7 @@ import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
|||||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.commands.SharedSuggestionProvider;
|
import net.minecraft.commands.SharedSuggestionProvider;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -21,8 +22,8 @@ public final class CommandUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isPlayer(CommandSourceStack output) {
|
public static boolean isPlayer(CommandSourceStack output) {
|
||||||
var player = output.getPlayer();
|
var sender = output.getEntity();
|
||||||
return player != null && !PlatformHelper.get().isFakePlayer(player);
|
return sender instanceof ServerPlayer player && !PlatformHelper.get().isFakePlayer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
package dan200.computercraft.shared.command;
|
package dan200.computercraft.shared.command;
|
||||||
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
@@ -13,6 +13,11 @@ import java.util.function.Predicate;
|
|||||||
* The level a user must be at in order to execute a command.
|
* The level a user must be at in order to execute a command.
|
||||||
*/
|
*/
|
||||||
public enum UserLevel implements Predicate<CommandSourceStack> {
|
public enum UserLevel implements Predicate<CommandSourceStack> {
|
||||||
|
/**
|
||||||
|
* Only can be used by the owner of the server: namely the server console or the player in SSP.
|
||||||
|
*/
|
||||||
|
OWNER,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can only be used by ops.
|
* Can only be used by ops.
|
||||||
*/
|
*/
|
||||||
@@ -30,6 +35,7 @@ public enum UserLevel implements Predicate<CommandSourceStack> {
|
|||||||
|
|
||||||
public int toLevel() {
|
public int toLevel() {
|
||||||
return switch (this) {
|
return switch (this) {
|
||||||
|
case OWNER -> 4;
|
||||||
case OP, OWNER_OP -> 2;
|
case OP, OWNER_OP -> 2;
|
||||||
case ANYONE -> 0;
|
case ANYONE -> 0;
|
||||||
};
|
};
|
||||||
@@ -38,26 +44,39 @@ public enum UserLevel implements Predicate<CommandSourceStack> {
|
|||||||
@Override
|
@Override
|
||||||
public boolean test(CommandSourceStack source) {
|
public boolean test(CommandSourceStack source) {
|
||||||
if (this == ANYONE) return true;
|
if (this == ANYONE) return true;
|
||||||
|
if (this == OWNER) return isOwner(source);
|
||||||
if (this == OWNER_OP && isOwner(source)) return true;
|
if (this == OWNER_OP && isOwner(source)) return true;
|
||||||
return source.hasPermission(toLevel());
|
return source.hasPermission(toLevel());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean test(ServerPlayer source) {
|
/**
|
||||||
if (this == ANYONE) return true;
|
* Take the union of two {@link UserLevel}s.
|
||||||
if (this == OWNER_OP && isOwner(source)) return true;
|
* <p>
|
||||||
return source.hasPermissions(toLevel());
|
* This satisfies the property that for all sources {@code s}, {@code a.test(s) || b.test(s) == (a ∪ b).test(s)}.
|
||||||
|
*
|
||||||
|
* @param left The first user level to take the union of.
|
||||||
|
* @param right The second user level to take the union of.
|
||||||
|
* @return The union of two levels.
|
||||||
|
*/
|
||||||
|
public static UserLevel union(UserLevel left, UserLevel right) {
|
||||||
|
if (left == right) return left;
|
||||||
|
|
||||||
|
// x ∪ ANYONE = ANYONE
|
||||||
|
if (left == ANYONE || right == ANYONE) return ANYONE;
|
||||||
|
|
||||||
|
// x ∪ OWNER = OWNER
|
||||||
|
if (left == OWNER) return right;
|
||||||
|
if (right == OWNER) return left;
|
||||||
|
|
||||||
|
// At this point, we have x != y and x, y ∈ { OP, OWNER_OP }.
|
||||||
|
return OWNER_OP;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isOwner(CommandSourceStack source) {
|
private static boolean isOwner(CommandSourceStack source) {
|
||||||
var server = source.getServer();
|
var server = source.getServer();
|
||||||
var player = source.getPlayer();
|
var sender = source.getEntity();
|
||||||
return server.isDedicatedServer()
|
return server.isDedicatedServer()
|
||||||
? source.getEntity() == null && source.hasPermission(4) && source.getTextName().equals("Server")
|
? source.getEntity() == null && source.hasPermission(4) && source.getTextName().equals("Server")
|
||||||
: player != null && server.isSingleplayerOwner(player.getGameProfile());
|
: sender instanceof Player player && server.isSingleplayerOwner(player.getGameProfile());
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isOwner(ServerPlayer player) {
|
|
||||||
var server = player.getServer();
|
|
||||||
return server != null && server.isSingleplayerOwner(player.getGameProfile());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -44,8 +44,7 @@ public class CommandBuilder<S> implements CommandNodeBuilder<S, Command<S>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CommandBuilder<S> requires(Predicate<S> predicate) {
|
public CommandBuilder<S> requires(Predicate<S> predicate) {
|
||||||
if (requires != null) throw new IllegalStateException("Requires already set");
|
requires = requires == null ? predicate : requires.and(predicate);
|
||||||
requires = predicate;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
import com.mojang.brigadier.tree.CommandNode;
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
import dan200.computercraft.shared.command.UserLevel;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.network.chat.ClickEvent;
|
import net.minecraft.network.chat.ClickEvent;
|
||||||
@@ -30,7 +31,6 @@ import static dan200.computercraft.shared.command.text.ChatHelpers.coloured;
|
|||||||
*/
|
*/
|
||||||
public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<CommandSourceStack> {
|
public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<CommandSourceStack> {
|
||||||
private final Collection<HelpingArgumentBuilder> children = new ArrayList<>();
|
private final Collection<HelpingArgumentBuilder> children = new ArrayList<>();
|
||||||
private @Nullable Predicate<CommandSourceStack> requirement;
|
|
||||||
|
|
||||||
private HelpingArgumentBuilder(String literal) {
|
private HelpingArgumentBuilder(String literal) {
|
||||||
super(literal);
|
super(literal);
|
||||||
@@ -41,20 +41,26 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HelpingArgumentBuilder requires(Predicate<CommandSourceStack> requirement) {
|
public LiteralArgumentBuilder<CommandSourceStack> requires(Predicate<CommandSourceStack> requirement) {
|
||||||
this.requirement = requirement;
|
throw new IllegalStateException("Cannot use requires on a HelpingArgumentBuilder");
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Predicate<CommandSourceStack> getRequirement() {
|
public Predicate<CommandSourceStack> getRequirement() {
|
||||||
if (requirement != null) return requirement;
|
// The requirement of this node is the union of all child's requirements.
|
||||||
|
|
||||||
var requirements = Stream.concat(
|
var requirements = Stream.concat(
|
||||||
children.stream().map(ArgumentBuilder::getRequirement),
|
children.stream().map(ArgumentBuilder::getRequirement),
|
||||||
getArguments().stream().map(CommandNode::getRequirement)
|
getArguments().stream().map(CommandNode::getRequirement)
|
||||||
).toList();
|
).toList();
|
||||||
return x -> requirements.stream().anyMatch(y -> y.test(x));
|
|
||||||
|
// If all requirements are a UserLevel, take the union of those instead.
|
||||||
|
var userLevel = UserLevel.OWNER;
|
||||||
|
for (var requirement : requirements) {
|
||||||
|
if (!(requirement instanceof UserLevel level)) return x -> requirements.stream().anyMatch(y -> y.test(x));
|
||||||
|
userLevel = UserLevel.union(userLevel, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
return userLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -147,14 +153,14 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<Command
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int run(CommandContext<CommandSourceStack> context) {
|
public int run(CommandContext<CommandSourceStack> context) {
|
||||||
context.getSource().sendSuccess(getHelp(context, assertNonNull(node), id, command), false);
|
context.getSource().sendSuccess(() -> getHelp(context, assertNonNull(node), id, command), false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command<CommandSourceStack> helpForChild(CommandNode<CommandSourceStack> node, String id, String command) {
|
private static Command<CommandSourceStack> helpForChild(CommandNode<CommandSourceStack> node, String id, String command) {
|
||||||
return context -> {
|
return context -> {
|
||||||
context.getSource().sendSuccess(getHelp(context, node, id + "." + node.getName().replace('-', '_'), command + " " + node.getName()), false);
|
context.getSource().sendSuccess(() -> getHelp(context, node, id + "." + node.getName().replace('-', '_'), command + " " + node.getName()), false);
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -175,7 +181,7 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<Command
|
|||||||
.append(Component.translatable("commands." + id + ".desc"));
|
.append(Component.translatable("commands." + id + ".desc"));
|
||||||
|
|
||||||
for (var child : node.getChildren()) {
|
for (var child : node.getChildren()) {
|
||||||
if (!child.canUse(context.getSource()) || !(child instanceof LiteralCommandNode)) {
|
if (!child.getRequirement().test(context.getSource()) || !(child instanceof LiteralCommandNode)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -37,6 +37,6 @@ public class ServerTableFormatter implements TableFormatter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeLine(String label, Component component) {
|
public void writeLine(String label, Component component) {
|
||||||
source.sendSuccess(component, false);
|
source.sendSuccess(() -> component, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -168,7 +168,7 @@ public class CommandAPI implements ILuaAPI {
|
|||||||
/**
|
/**
|
||||||
* Get information about a range of blocks.
|
* Get information about a range of blocks.
|
||||||
* <p>
|
* <p>
|
||||||
* This returns the same information as [`getBlockInfo`], just for multiple
|
* This returns the same information as @{getBlockInfo}, just for multiple
|
||||||
* blocks at once.
|
* blocks at once.
|
||||||
* <p>
|
* <p>
|
||||||
* Blocks are traversed by ascending y level, followed by z and x - the returned
|
* Blocks are traversed by ascending y level, followed by z and x - the returned
|
||||||
@@ -225,7 +225,7 @@ public class CommandAPI implements ILuaAPI {
|
|||||||
* Get some basic information about a block.
|
* Get some basic information about a block.
|
||||||
* <p>
|
* <p>
|
||||||
* The returned table contains the current name, metadata and block state (as
|
* 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
|
* with @{turtle.inspect}). If there is a tile entity for that block, its NBT
|
||||||
* will also be returned.
|
* will also be returned.
|
||||||
*
|
*
|
||||||
* @param x The x position of the block to query.
|
* @param x The x position of the block to query.
|
||||||
|
@@ -32,7 +32,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
|||||||
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.storage.loot.LootContext;
|
import net.minecraft.world.level.storage.loot.LootParams;
|
||||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||||||
import net.minecraft.world.phys.BlockHitResult;
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
@@ -138,13 +138,12 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
|||||||
|
|
||||||
var tile = world.getBlockEntity(pos);
|
var tile = world.getBlockEntity(pos);
|
||||||
if (tile instanceof AbstractComputerBlockEntity computer) {
|
if (tile instanceof AbstractComputerBlockEntity computer) {
|
||||||
var context = new LootContext.Builder(serverWorld)
|
var context = new LootParams.Builder(serverWorld)
|
||||||
.withRandom(world.random)
|
|
||||||
.withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos))
|
.withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos))
|
||||||
.withParameter(LootContextParams.TOOL, player.getMainHandItem())
|
.withParameter(LootContextParams.TOOL, player.getMainHandItem())
|
||||||
.withParameter(LootContextParams.THIS_ENTITY, player)
|
.withParameter(LootContextParams.THIS_ENTITY, player)
|
||||||
.withParameter(LootContextParams.BLOCK_ENTITY, tile)
|
.withParameter(LootContextParams.BLOCK_ENTITY, tile)
|
||||||
.withDynamicDrop(DROP, (ctx, out) -> out.accept(getItem(computer)));
|
.withDynamicDrop(DROP, out -> out.accept(getItem(computer)));
|
||||||
for (var item : state.getDrops(context)) {
|
for (var item : state.getDrops(context)) {
|
||||||
popResource(world, pos, item);
|
popResource(world, pos, item);
|
||||||
}
|
}
|
||||||
|
@@ -5,10 +5,18 @@
|
|||||||
package dan200.computercraft.shared.computer.terminal;
|
package dan200.computercraft.shared.computer.terminal;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufInputStream;
|
||||||
|
import io.netty.buffer.ByteBufOutputStream;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A snapshot of a terminal's state.
|
* A snapshot of a terminal's state.
|
||||||
@@ -23,10 +31,20 @@ public class TerminalState {
|
|||||||
public final int width;
|
public final int width;
|
||||||
public final int height;
|
public final int height;
|
||||||
|
|
||||||
|
private final boolean compress;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final ByteBuf buffer;
|
private final ByteBuf buffer;
|
||||||
|
|
||||||
|
private @Nullable ByteBuf compressed;
|
||||||
|
|
||||||
public TerminalState(@Nullable NetworkedTerminal terminal) {
|
public TerminalState(@Nullable NetworkedTerminal terminal) {
|
||||||
|
this(terminal, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TerminalState(@Nullable NetworkedTerminal terminal, boolean compress) {
|
||||||
|
this.compress = compress;
|
||||||
|
|
||||||
if (terminal == null) {
|
if (terminal == null) {
|
||||||
colour = false;
|
colour = false;
|
||||||
width = height = 0;
|
width = height = 0;
|
||||||
@@ -43,13 +61,14 @@ public class TerminalState {
|
|||||||
|
|
||||||
public TerminalState(FriendlyByteBuf buf) {
|
public TerminalState(FriendlyByteBuf buf) {
|
||||||
colour = buf.readBoolean();
|
colour = buf.readBoolean();
|
||||||
|
compress = buf.readBoolean();
|
||||||
|
|
||||||
if (buf.readBoolean()) {
|
if (buf.readBoolean()) {
|
||||||
width = buf.readVarInt();
|
width = buf.readVarInt();
|
||||||
height = buf.readVarInt();
|
height = buf.readVarInt();
|
||||||
|
|
||||||
var length = buf.readVarInt();
|
var length = buf.readVarInt();
|
||||||
buffer = buf.readBytes(length);
|
buffer = readCompressed(buf, length, compress);
|
||||||
} else {
|
} else {
|
||||||
width = height = 0;
|
width = height = 0;
|
||||||
buffer = null;
|
buffer = null;
|
||||||
@@ -58,13 +77,16 @@ public class TerminalState {
|
|||||||
|
|
||||||
public void write(FriendlyByteBuf buf) {
|
public void write(FriendlyByteBuf buf) {
|
||||||
buf.writeBoolean(colour);
|
buf.writeBoolean(colour);
|
||||||
|
buf.writeBoolean(compress);
|
||||||
|
|
||||||
buf.writeBoolean(buffer != null);
|
buf.writeBoolean(buffer != null);
|
||||||
if (buffer != null) {
|
if (buffer != null) {
|
||||||
buf.writeVarInt(width);
|
buf.writeVarInt(width);
|
||||||
buf.writeVarInt(height);
|
buf.writeVarInt(height);
|
||||||
buf.writeVarInt(buffer.readableBytes());
|
|
||||||
buf.writeBytes(buffer, buffer.readerIndex(), buffer.readableBytes());
|
var sendBuffer = getCompressed();
|
||||||
|
buf.writeVarInt(sendBuffer.readableBytes());
|
||||||
|
buf.writeBytes(sendBuffer, sendBuffer.readerIndex(), sendBuffer.readableBytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,4 +110,40 @@ public class TerminalState {
|
|||||||
terminal.read(new FriendlyByteBuf(buffer));
|
terminal.read(new FriendlyByteBuf(buffer));
|
||||||
return terminal;
|
return terminal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ByteBuf getCompressed() {
|
||||||
|
if (buffer == null) throw new NullPointerException("buffer");
|
||||||
|
if (!compress) return buffer;
|
||||||
|
if (compressed != null) return compressed;
|
||||||
|
|
||||||
|
var compressed = Unpooled.buffer();
|
||||||
|
try (OutputStream stream = new GZIPOutputStream(new ByteBufOutputStream(compressed))) {
|
||||||
|
stream.write(buffer.array(), buffer.arrayOffset(), buffer.readableBytes());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.compressed = compressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ByteBuf readCompressed(ByteBuf buf, int length, boolean compress) {
|
||||||
|
if (compress) {
|
||||||
|
var buffer = Unpooled.buffer();
|
||||||
|
try (InputStream stream = new GZIPInputStream(new ByteBufInputStream(buf, length))) {
|
||||||
|
var swap = new byte[8192];
|
||||||
|
while (true) {
|
||||||
|
var bytes = stream.read(swap);
|
||||||
|
if (bytes == -1) break;
|
||||||
|
buffer.writeBytes(swap, 0, bytes);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
} else {
|
||||||
|
var buffer = Unpooled.buffer(length);
|
||||||
|
buf.readBytes(buffer, length);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,10 +26,6 @@ class AddressRuleConfig {
|
|||||||
|
|
||||||
private static final AddressRule REJECT_ALL = AddressRule.parse("*", OptionalInt.empty(), Action.DENY.toPartial());
|
private static final AddressRule REJECT_ALL = AddressRule.parse("*", OptionalInt.empty(), Action.DENY.toPartial());
|
||||||
|
|
||||||
private static final Set<String> knownKeys = Set.of(
|
|
||||||
"host", "action", "max_download", "max_upload", "max_websocket_message", "use_proxy"
|
|
||||||
);
|
|
||||||
|
|
||||||
public static List<UnmodifiableConfig> defaultRules() {
|
public static List<UnmodifiableConfig> defaultRules() {
|
||||||
return List.of(
|
return List.of(
|
||||||
makeRule(config -> {
|
makeRule(config -> {
|
||||||
@@ -92,20 +88,9 @@ class AddressRuleConfig {
|
|||||||
var port = unboxOptInt(get(builder, "port", Number.class));
|
var port = unboxOptInt(get(builder, "port", Number.class));
|
||||||
var maxUpload = unboxOptLong(get(builder, "max_upload", Number.class).map(Number::longValue));
|
var maxUpload = unboxOptLong(get(builder, "max_upload", Number.class).map(Number::longValue));
|
||||||
var maxDownload = unboxOptLong(get(builder, "max_download", Number.class).map(Number::longValue));
|
var maxDownload = unboxOptLong(get(builder, "max_download", Number.class).map(Number::longValue));
|
||||||
var websocketMessage = unboxOptInt(
|
var websocketMessage = unboxOptInt(get(builder, "websocket_message", Number.class).map(Number::intValue));
|
||||||
get(builder, "max_websocket_message", Number.class)
|
|
||||||
// Fallback to (incorrect) websocket_message option.
|
|
||||||
.or(() -> get(builder, "websocket_message", Number.class))
|
|
||||||
.map(Number::intValue)
|
|
||||||
);
|
|
||||||
var useProxy = get(builder, "use_proxy", Boolean.class);
|
var useProxy = get(builder, "use_proxy", Boolean.class);
|
||||||
|
|
||||||
// Find unknown keys and warn about them.
|
|
||||||
var unknownKeys = builder.entrySet().stream().map(UnmodifiableConfig.Entry::getKey).filter(x -> !knownKeys.contains(x)).toList();
|
|
||||||
if (!unknownKeys.isEmpty()) {
|
|
||||||
LOG.warn("Unknown config {} {} in address rule.", unknownKeys.size() == 1 ? "option" : "options", String.join(", ", unknownKeys));
|
|
||||||
}
|
|
||||||
|
|
||||||
var options = new PartialOptions(
|
var options = new PartialOptions(
|
||||||
action,
|
action,
|
||||||
maxUpload,
|
maxUpload,
|
||||||
|
@@ -1,79 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.shared.integration;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
|
||||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
|
||||||
import dan200.computercraft.shared.command.UserLevel;
|
|
||||||
import dan200.computercraft.shared.platform.RegistrationHelper;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
|
|
||||||
import javax.annotation.OverridingMethodsMustInvokeSuper;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ServiceLoader;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A registry of nodes in a permission system.
|
|
||||||
* <p>
|
|
||||||
* This acts as an abstraction layer over permission systems such Forge's built-in permissions API, or Fabric's
|
|
||||||
* unofficial <a href="https://github.com/lucko/fabric-permissions-api">fabric-permissions-api-v0</a>.
|
|
||||||
* <p>
|
|
||||||
* This behaves similarly to {@link RegistrationHelper} (aka Forge's deferred registry), in that you {@linkplain #create()
|
|
||||||
* create a registry}, {@linkplain #registerCommand(String, UserLevel) add nodes to it} and then finally {@linkplain
|
|
||||||
* #register()} all created nodes.
|
|
||||||
*
|
|
||||||
* @see dan200.computercraft.shared.ModRegistry.Permissions
|
|
||||||
*/
|
|
||||||
public abstract class PermissionRegistry {
|
|
||||||
private boolean frozen = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a permission node for a command. The registered node should be of the form {@code "command." + command}.
|
|
||||||
*
|
|
||||||
* @param command The name of the command. This should be one of the subcommands under the {@code /computercraft}
|
|
||||||
* subcommand, and not something general.
|
|
||||||
* @param fallback The default/fallback permission check.
|
|
||||||
* @return The resulting predicate which should be passed to {@link ArgumentBuilder#requires(Predicate)}.
|
|
||||||
* @see CommandComputerCraft
|
|
||||||
*/
|
|
||||||
public abstract Predicate<CommandSourceStack> registerCommand(String command, UserLevel fallback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check that the registry has not been frozen (namely {@link #register()} has been called). This should be called
|
|
||||||
* before registering each node.
|
|
||||||
*/
|
|
||||||
protected void checkNotFrozen() {
|
|
||||||
if (frozen) throw new IllegalStateException("Permission registry has been frozen.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Freeze the permissions registry and register the underlying nodes.
|
|
||||||
*/
|
|
||||||
@OverridingMethodsMustInvokeSuper
|
|
||||||
public void register() {
|
|
||||||
frozen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface Provider {
|
|
||||||
Optional<PermissionRegistry> get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PermissionRegistry create() {
|
|
||||||
return ServiceLoader.load(Provider.class)
|
|
||||||
.stream()
|
|
||||||
.flatMap(x -> x.get().get().stream())
|
|
||||||
.findFirst()
|
|
||||||
.orElseGet(DefaultPermissionRegistry::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DefaultPermissionRegistry extends PermissionRegistry {
|
|
||||||
@Override
|
|
||||||
public Predicate<CommandSourceStack> registerCommand(String command, UserLevel fallback) {
|
|
||||||
checkNotFrozen();
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -6,11 +6,13 @@ package dan200.computercraft.shared.network.client;
|
|||||||
|
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
|
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
|
||||||
|
import dan200.computercraft.shared.platform.RegistryWrappers;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import net.minecraft.sounds.SoundEvent;
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts or stops a record on the client, depending on if {@link #soundEvent} is {@code null}.
|
* Starts or stops a record on the client, depending on if {@link #soundEvent} is {@code null}.
|
||||||
@@ -38,19 +40,28 @@ public class PlayRecordClientMessage implements NetworkMessage<ClientNetworkCont
|
|||||||
|
|
||||||
public PlayRecordClientMessage(FriendlyByteBuf buf) {
|
public PlayRecordClientMessage(FriendlyByteBuf buf) {
|
||||||
pos = buf.readBlockPos();
|
pos = buf.readBlockPos();
|
||||||
soundEvent = buf.readNullable(SoundEvent::readFromNetwork);
|
soundEvent = buf.readBoolean() ? RegistryWrappers.readKey(buf, RegistryWrappers.SOUND_EVENTS) : null;
|
||||||
name = buf.readNullable(FriendlyByteBuf::readUtf);
|
name = buf.readBoolean() ? buf.readUtf(Short.MAX_VALUE) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toBytes(FriendlyByteBuf buf) {
|
public void toBytes(FriendlyByteBuf buf) {
|
||||||
buf.writeBlockPos(pos);
|
buf.writeBlockPos(pos);
|
||||||
buf.writeNullable(soundEvent, (b, e) -> e.writeToNetwork(b));
|
writeOptional(buf, soundEvent, (b, e) -> RegistryWrappers.writeKey(b, RegistryWrappers.SOUND_EVENTS, e));
|
||||||
buf.writeNullable(name, FriendlyByteBuf::writeUtf);
|
writeOptional(buf, name, FriendlyByteBuf::writeUtf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(ClientNetworkContext context) {
|
public void handle(ClientNetworkContext context) {
|
||||||
context.handlePlayRecord(pos, soundEvent, name);
|
context.handlePlayRecord(pos, soundEvent, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T> void writeOptional(FriendlyByteBuf out, @Nullable T object, BiConsumer<FriendlyByteBuf, T> write) {
|
||||||
|
if (object == null) {
|
||||||
|
out.writeBoolean(false);
|
||||||
|
} else {
|
||||||
|
out.writeBoolean(true);
|
||||||
|
write.accept(out, object);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.peripheral.modem;
|
package dan200.computercraft.shared.peripheral.modem;
|
||||||
|
|
||||||
import com.google.errorprone.annotations.concurrent.GuardedBy;
|
|
||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
import dan200.computercraft.api.lua.LuaFunction;
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
import dan200.computercraft.api.network.Packet;
|
import dan200.computercraft.api.network.Packet;
|
||||||
@@ -21,9 +20,10 @@ import java.util.Set;
|
|||||||
/**
|
/**
|
||||||
* Modems allow you to send messages between computers over long distances.
|
* Modems allow you to send messages between computers over long distances.
|
||||||
* <p>
|
* <p>
|
||||||
* > [!TIP]
|
* :::tip
|
||||||
* > Modems provide a fairly basic set of methods, which makes them very flexible but often hard to work with. The
|
* Modems provide a fairly basic set of methods, which makes them very flexible but often hard to work with. The
|
||||||
* > [`rednet`] API is built on top of modems, and provides a more user-friendly interface.
|
* {@literal @}{rednet} API is built on top of modems, and provides a more user-friendly interface.
|
||||||
|
* :::
|
||||||
* <p>
|
* <p>
|
||||||
* ## Sending and receiving messages
|
* ## Sending and receiving messages
|
||||||
* Modems operate on a series of channels, a bit like frequencies on a radio. Any modem can send a message on a
|
* Modems operate on a series of channels, a bit like frequencies on a radio. Any modem can send a message on a
|
||||||
@@ -31,11 +31,11 @@ import java.util.Set;
|
|||||||
* messages.
|
* messages.
|
||||||
* <p>
|
* <p>
|
||||||
* Channels are represented as an integer between 0 and 65535 inclusive. These channels don't have any defined meaning,
|
* Channels are represented as an integer between 0 and 65535 inclusive. These channels don't have any defined meaning,
|
||||||
* though some APIs or programs will assign a meaning to them. For instance, the [`gps`] module sends all its messages on
|
* though some APIs or programs will assign a meaning to them. For instance, the @{gps} module sends all its messages on
|
||||||
* channel 65534 ([`gps.CHANNEL_GPS`]), while [`rednet`] uses channels equal to the computer's ID.
|
* channel 65534 (@{gps.CHANNEL_GPS}), while @{rednet} uses channels equal to the computer's ID.
|
||||||
* <p>
|
* <p>
|
||||||
* - Sending messages is done with the {@link #transmit(int, int, Object)} message.
|
* - Sending messages is done with the {@link #transmit(int, int, Object)} message.
|
||||||
* - Receiving messages is done by listening to the [`modem_message`] event.
|
* - Receiving messages is done by listening to the @{modem_message} event.
|
||||||
* <p>
|
* <p>
|
||||||
* ## Types of modem
|
* ## Types of modem
|
||||||
* CC: Tweaked comes with three kinds of modem, with different capabilities.
|
* CC: Tweaked comes with three kinds of modem, with different capabilities.
|
||||||
@@ -85,7 +85,7 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public abstract class ModemPeripheral implements IPeripheral, PacketSender, PacketReceiver {
|
public abstract class ModemPeripheral implements IPeripheral, PacketSender, PacketReceiver {
|
||||||
private @Nullable PacketNetwork network;
|
private @Nullable PacketNetwork network;
|
||||||
private final @GuardedBy("computers") Set<IComputerAccess> computers = new HashSet<>(1);
|
private final Set<IComputerAccess> computers = new HashSet<>(1);
|
||||||
private final ModemState state;
|
private final ModemState state;
|
||||||
|
|
||||||
protected ModemPeripheral(ModemState state) {
|
protected ModemPeripheral(ModemState state) {
|
||||||
@@ -197,8 +197,9 @@ public abstract class ModemPeripheral implements IPeripheral, PacketSender, Pack
|
|||||||
* Sends a modem message on a certain channel. Modems listening on the channel will queue a {@code modem_message}
|
* Sends a modem message on a certain channel. Modems listening on the channel will queue a {@code modem_message}
|
||||||
* event on adjacent computers.
|
* event on adjacent computers.
|
||||||
* <p>
|
* <p>
|
||||||
* > [!NOTE]
|
* :::note
|
||||||
* > The channel does not need be open to send a message.
|
* The channel does not need be open to send a message.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @param channel The channel to send messages on.
|
* @param channel The channel to send messages on.
|
||||||
* @param replyChannel The channel that responses to this message should be sent on. This can be the same as
|
* @param replyChannel The channel that responses to this message should be sent on. This can be the same as
|
||||||
|
@@ -80,8 +80,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
|||||||
* If this computer is attached to the network, it _will not_ be included in
|
* If this computer is attached to the network, it _will not_ be included in
|
||||||
* this list.
|
* this list.
|
||||||
* <p>
|
* <p>
|
||||||
* > [!NOTE]
|
* :::note
|
||||||
* > This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @param computer The calling computer.
|
* @param computer The calling computer.
|
||||||
* @return Remote peripheral names on the network.
|
* @return Remote peripheral names on the network.
|
||||||
@@ -95,8 +96,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
|||||||
/**
|
/**
|
||||||
* Determine if a peripheral is available on this wired network.
|
* Determine if a peripheral is available on this wired network.
|
||||||
* <p>
|
* <p>
|
||||||
* > [!NOTE]
|
* :::note
|
||||||
* > This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @param computer The calling computer.
|
* @param computer The calling computer.
|
||||||
* @param name The peripheral's name.
|
* @param name The peripheral's name.
|
||||||
@@ -111,8 +113,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
|||||||
/**
|
/**
|
||||||
* Get the type of a peripheral is available on this wired network.
|
* Get the type of a peripheral is available on this wired network.
|
||||||
* <p>
|
* <p>
|
||||||
* > [!NOTE]
|
* :::note
|
||||||
* > This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @param computer The calling computer.
|
* @param computer The calling computer.
|
||||||
* @param name The peripheral's name.
|
* @param name The peripheral's name.
|
||||||
@@ -130,8 +133,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
|||||||
/**
|
/**
|
||||||
* Check a peripheral is of a particular type.
|
* Check a peripheral is of a particular type.
|
||||||
* <p>
|
* <p>
|
||||||
* > [!NOTE]
|
* :::note
|
||||||
* > This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @param computer The calling computer.
|
* @param computer The calling computer.
|
||||||
* @param name The peripheral's name.
|
* @param name The peripheral's name.
|
||||||
@@ -150,8 +154,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
|||||||
/**
|
/**
|
||||||
* Get all available methods for the remote peripheral with the given name.
|
* Get all available methods for the remote peripheral with the given name.
|
||||||
* <p>
|
* <p>
|
||||||
* > [!NOTE]
|
* :::note
|
||||||
* > This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @param computer The calling computer.
|
* @param computer The calling computer.
|
||||||
* @param name The peripheral's name.
|
* @param name The peripheral's name.
|
||||||
@@ -170,8 +175,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
|||||||
/**
|
/**
|
||||||
* Call a method on a peripheral on this wired network.
|
* Call a method on a peripheral on this wired network.
|
||||||
* <p>
|
* <p>
|
||||||
* > [!NOTE]
|
* :::note
|
||||||
* > This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @param computer The calling computer.
|
* @param computer The calling computer.
|
||||||
* @param context The Lua context we're executing in.
|
* @param context The Lua context we're executing in.
|
||||||
@@ -199,8 +205,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
|||||||
* may be used by other computers on the network to wrap this computer as a
|
* may be used by other computers on the network to wrap this computer as a
|
||||||
* peripheral.
|
* peripheral.
|
||||||
* <p>
|
* <p>
|
||||||
* > [!NOTE]
|
* :::note
|
||||||
* > This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @return The current computer's name.
|
* @return The current computer's name.
|
||||||
* @cc.treturn string|nil The current computer's name on the wired network.
|
* @cc.treturn string|nil The current computer's name on the wired network.
|
||||||
|
@@ -25,9 +25,8 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Collections;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class MonitorBlockEntity extends BlockEntity {
|
public class MonitorBlockEntity extends BlockEntity {
|
||||||
@@ -47,7 +46,7 @@ public class MonitorBlockEntity extends BlockEntity {
|
|||||||
private @Nullable ServerMonitor serverMonitor;
|
private @Nullable ServerMonitor serverMonitor;
|
||||||
private @Nullable ClientMonitor clientMonitor;
|
private @Nullable ClientMonitor clientMonitor;
|
||||||
private @Nullable MonitorPeripheral peripheral;
|
private @Nullable MonitorPeripheral peripheral;
|
||||||
private final Set<IComputerAccess> computers = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
private final Set<IComputerAccess> computers = new HashSet<>();
|
||||||
|
|
||||||
private boolean needsUpdate = false;
|
private boolean needsUpdate = false;
|
||||||
private boolean needsValidating = false;
|
private boolean needsValidating = false;
|
||||||
|
@@ -18,7 +18,7 @@ import javax.annotation.Nullable;
|
|||||||
* Monitors are a block which act as a terminal, displaying information on one side. This allows them to be read and
|
* Monitors are a block which act as a terminal, displaying information on one side. This allows them to be read and
|
||||||
* interacted with in-world without opening a GUI.
|
* interacted with in-world without opening a GUI.
|
||||||
* <p>
|
* <p>
|
||||||
* Monitors act as [terminal redirects][`term.Redirect`] and so expose the same methods, as well as several additional
|
* Monitors act as @{term.Redirect|terminal redirects} and so expose the same methods, as well as several additional
|
||||||
* ones, which are documented below.
|
* ones, which are documented below.
|
||||||
* <p>
|
* <p>
|
||||||
* Like computers, monitors come in both normal (no colour) and advanced (colour) varieties.
|
* Like computers, monitors come in both normal (no colour) and advanced (colour) varieties.
|
||||||
|
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.peripheral.speaker;
|
package dan200.computercraft.shared.peripheral.speaker;
|
||||||
|
|
||||||
import com.google.errorprone.annotations.concurrent.GuardedBy;
|
|
||||||
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.lua.LuaFunction;
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
@@ -58,7 +57,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
|
|||||||
public static final int SAMPLE_RATE = 48000;
|
public static final int SAMPLE_RATE = 48000;
|
||||||
|
|
||||||
private final UUID source = UUID.randomUUID();
|
private final UUID source = UUID.randomUUID();
|
||||||
private final @GuardedBy("computers") Set<IComputerAccess> computers = new HashSet<>();
|
private final Set<IComputerAccess> computers = new HashSet<>();
|
||||||
|
|
||||||
private long clock = 0;
|
private long clock = 0;
|
||||||
private long lastPositionTime;
|
private long lastPositionTime;
|
||||||
@@ -272,15 +271,16 @@ public abstract class SpeakerPeripheral implements IPeripheral {
|
|||||||
* <p>
|
* <p>
|
||||||
* This accepts a list of audio samples as amplitudes between -128 and 127. These are stored in an internal buffer
|
* This accepts a list of audio samples as amplitudes between -128 and 127. These are stored in an internal buffer
|
||||||
* and played back at 48kHz. If this buffer is full, this function will return {@literal false}. You should wait for
|
* and played back at 48kHz. If this buffer is full, this function will return {@literal false}. You should wait for
|
||||||
* a [`speaker_audio_empty`] event before trying again.
|
* a @{speaker_audio_empty} event before trying again.
|
||||||
* <p>
|
* <p>
|
||||||
* > [!NOTE]
|
* :::note
|
||||||
* > The speaker only buffers a single call to {@link #playAudio} at once. This means if you try to play a small
|
* The speaker only buffers a single call to {@link #playAudio} at once. This means if you try to play a small
|
||||||
* > number of samples, you'll have a lot of stutter. You should try to play as many samples in one call as possible
|
* number of samples, you'll have a lot of stutter. You should try to play as many samples in one call as possible
|
||||||
* > (up to 128×1024), as this reduces the chances of audio stuttering or halting, especially when the server or
|
* (up to 128×1024), as this reduces the chances of audio stuttering or halting, especially when the server or
|
||||||
* > computer is lagging.
|
* computer is lagging.
|
||||||
|
* :::
|
||||||
* <p>
|
* <p>
|
||||||
* [`speaker_audio`] provides a more complete guide to using speakers
|
* {@literal @}{speaker_audio} provides a more complete guide to using speakers
|
||||||
*
|
*
|
||||||
* @param context The Lua context.
|
* @param context The Lua context.
|
||||||
* @param audio The audio data to play.
|
* @param audio The audio data to play.
|
||||||
@@ -291,7 +291,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
|
|||||||
* @cc.tparam [opt] number volume The volume to play this audio at. If not given, defaults to the previous volume
|
* @cc.tparam [opt] number volume The volume to play this audio at. If not given, defaults to the previous volume
|
||||||
* given to {@link #playAudio}.
|
* given to {@link #playAudio}.
|
||||||
* @cc.since 1.100
|
* @cc.since 1.100
|
||||||
* @cc.usage Read an audio file, decode it using [`cc.audio.dfpwm`], and play it using the speaker.
|
* @cc.usage Read an audio file, decode it using @{cc.audio.dfpwm}, and play it using the speaker.
|
||||||
*
|
*
|
||||||
* <pre data-peripheral="speaker">{@code
|
* <pre data-peripheral="speaker">{@code
|
||||||
* local dfpwm = require("cc.audio.dfpwm")
|
* local dfpwm = require("cc.audio.dfpwm")
|
||||||
|
@@ -19,7 +19,7 @@ public record SpeakerPosition(@Nullable Level level, Vec3 position, @Nullable En
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static SpeakerPosition of(Entity entity) {
|
public static SpeakerPosition of(Entity entity) {
|
||||||
return new SpeakerPosition(entity.level, entity.getEyePosition(1), entity);
|
return new SpeakerPosition(entity.level(), entity.getEyePosition(1), entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean withinDistance(SpeakerPosition other, double distanceSq) {
|
public boolean withinDistance(SpeakerPosition other, double distanceSq) {
|
||||||
|
@@ -33,6 +33,7 @@ import net.minecraft.world.entity.player.Player;
|
|||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
import net.minecraft.world.inventory.CraftingContainer;
|
import net.minecraft.world.inventory.CraftingContainer;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
|
import net.minecraft.world.item.CreativeModeTab;
|
||||||
import net.minecraft.world.item.DyeColor;
|
import net.minecraft.world.item.DyeColor;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
@@ -279,6 +280,13 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
|
|||||||
*/
|
*/
|
||||||
int getBurnTime(ItemStack stack);
|
int getBurnTime(ItemStack stack);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a builder for a new creative tab.
|
||||||
|
*
|
||||||
|
* @return The creative tab builder.
|
||||||
|
*/
|
||||||
|
CreativeModeTab.Builder newCreativeModeTab();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the "container" item to be returned after crafting. For instance, crafting with a lava bucket should return
|
* Get the "container" item to be returned after crafting. For instance, crafting with a lava bucket should return
|
||||||
* an empty bucket.
|
* an empty bucket.
|
||||||
|
@@ -10,6 +10,7 @@ import net.minecraft.core.Registry;
|
|||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.sounds.SoundEvent;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||||
@@ -32,6 +33,7 @@ public final class RegistryWrappers {
|
|||||||
public static final RegistryWrapper<Fluid> FLUIDS = PlatformHelper.get().wrap(Registries.FLUID);
|
public static final RegistryWrapper<Fluid> FLUIDS = PlatformHelper.get().wrap(Registries.FLUID);
|
||||||
public static final RegistryWrapper<Enchantment> ENCHANTMENTS = PlatformHelper.get().wrap(Registries.ENCHANTMENT);
|
public static final RegistryWrapper<Enchantment> ENCHANTMENTS = PlatformHelper.get().wrap(Registries.ENCHANTMENT);
|
||||||
public static final RegistryWrapper<ArgumentTypeInfo<?, ?>> COMMAND_ARGUMENT_TYPES = PlatformHelper.get().wrap(Registries.COMMAND_ARGUMENT_TYPE);
|
public static final RegistryWrapper<ArgumentTypeInfo<?, ?>> COMMAND_ARGUMENT_TYPES = PlatformHelper.get().wrap(Registries.COMMAND_ARGUMENT_TYPE);
|
||||||
|
public static final RegistryWrapper<SoundEvent> SOUND_EVENTS = PlatformHelper.get().wrap(Registries.SOUND_EVENT);
|
||||||
public static final RegistryWrapper<RecipeSerializer<?>> RECIPE_SERIALIZERS = PlatformHelper.get().wrap(Registries.RECIPE_SERIALIZER);
|
public static final RegistryWrapper<RecipeSerializer<?>> RECIPE_SERIALIZERS = PlatformHelper.get().wrap(Registries.RECIPE_SERIALIZER);
|
||||||
public static final RegistryWrapper<MenuType<?>> MENU = PlatformHelper.get().wrap(Registries.MENU);
|
public static final RegistryWrapper<MenuType<?>> MENU = PlatformHelper.get().wrap(Registries.MENU);
|
||||||
|
|
||||||
|
@@ -153,7 +153,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
|
|||||||
super.tickServer();
|
super.tickServer();
|
||||||
|
|
||||||
// Find any players which have gone missing and remove them from the tracking list.
|
// Find any players which have gone missing and remove them from the tracking list.
|
||||||
tracking.removeIf(player -> !player.isAlive() || player.level != getLevel());
|
tracking.removeIf(player -> !player.isAlive() || player.level() != getLevel());
|
||||||
|
|
||||||
// And now find any new players, add them to the tracking list, and broadcast state where appropriate.
|
// And now find any new players, add them to the tracking list, and broadcast state where appropriate.
|
||||||
var sendState = hasOutputChanged() || lightChanged;
|
var sendState = hasOutputChanged() || lightChanged;
|
||||||
|
@@ -48,7 +48,7 @@ public class PocketComputerMenuProvider implements MenuProvider {
|
|||||||
isTypingOnly ? ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get() : ModRegistry.Menus.POCKET_COMPUTER.get(), id, inventory,
|
isTypingOnly ? ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get() : ModRegistry.Menus.POCKET_COMPUTER.get(), id, inventory,
|
||||||
p -> {
|
p -> {
|
||||||
var stack = p.getItemInHand(hand);
|
var stack = p.getItemInHand(hand);
|
||||||
return stack.getItem() == item && PocketComputerItem.getServerComputer(assertNonNull(entity.level.getServer()), stack) == computer;
|
return stack.getItem() == item && PocketComputerItem.getServerComputer(assertNonNull(entity.level().getServer()), stack) == computer;
|
||||||
},
|
},
|
||||||
computer, item.getFamily()
|
computer, item.getFamily()
|
||||||
);
|
);
|
||||||
|
@@ -127,10 +127,11 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
|||||||
|
|
||||||
@ForgeOverride
|
@ForgeOverride
|
||||||
public boolean onEntityItemUpdate(ItemStack stack, ItemEntity entity) {
|
public boolean onEntityItemUpdate(ItemStack stack, ItemEntity entity) {
|
||||||
if (entity.level.isClientSide || entity.level.getServer() == null) return false;
|
var level = entity.level();
|
||||||
|
if (level.isClientSide || level.getServer() == null) return false;
|
||||||
|
|
||||||
var computer = getServerComputer(entity.level.getServer(), stack);
|
var computer = getServerComputer(level.getServer(), stack);
|
||||||
if (computer != null && tick(stack, entity.level, entity, computer)) entity.setItem(stack.copy());
|
if (computer != null && tick(stack, entity.level(), entity, computer)) entity.setItem(stack.copy());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -37,7 +37,7 @@ public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
|
|||||||
public void update() {
|
public void update() {
|
||||||
var entity = access.getEntity();
|
var entity = access.getEntity();
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
level = entity.level;
|
level = entity.level();
|
||||||
position = entity.position();
|
position = entity.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,38 +23,40 @@ import java.util.Optional;
|
|||||||
* Turtles are capable of moving through the world. As turtles are blocks themselves, they are confined to Minecraft's
|
* Turtles are capable of moving through the world. As turtles are blocks themselves, they are confined to Minecraft's
|
||||||
* grid, moving a single block at a time.
|
* grid, moving a single block at a time.
|
||||||
* <p>
|
* <p>
|
||||||
* [`turtle.forward`] and [`turtle.back`] move the turtle in the direction it is facing, while [`turtle.up`] and
|
* {@literal @}{turtle.forward} and @{turtle.back} move the turtle in the direction it is facing, while @{turtle.up} and
|
||||||
* [`turtle.down`] move it up and down (as one might expect!). In order to move left or right, you first need
|
* {@literal @}{turtle.down} move it up and down (as one might expect!). In order to move left or right, you first need
|
||||||
* to turn the turtle using [`turtle.turnLeft`]/[`turtle.turnRight`] and then move forward or backwards.
|
* to turn the turtle using @{turtle.turnLeft}/@{turtle.turnRight} and then move forward or backwards.
|
||||||
* <p>
|
* <p>
|
||||||
* > [!INFO]
|
* :::info
|
||||||
* > The name "turtle" comes from [Turtle graphics], which originated from the Logo programming language. Here you'd
|
* The name "turtle" comes from [Turtle graphics], which originated from the Logo programming language. Here you'd move
|
||||||
* > move a turtle with various commands like "move 10" and "turn left", much like ComputerCraft's turtles!
|
* a turtle with various commands like "move 10" and "turn left", much like ComputerCraft's turtles!
|
||||||
|
* :::
|
||||||
* <p>
|
* <p>
|
||||||
* Moving a turtle (though not turning it) consumes *fuel*. If a turtle does not have any [fuel][`turtle.refuel`], it
|
* Moving a turtle (though not turning it) consumes *fuel*. If a turtle does not have any @{turtle.refuel|fuel}, it
|
||||||
* won't move, and the movement functions will return [`false`]. If your turtle isn't going anywhere, the first thing to
|
* won't move, and the movement functions will return @{false}. If your turtle isn't going anywhere, the first thing to
|
||||||
* check is if you've fuelled your turtle.
|
* check is if you've fuelled your turtle.
|
||||||
* <p>
|
* <p>
|
||||||
* > [Handling errors][!TIP]
|
* :::tip Handling errors
|
||||||
* > Many turtle functions can fail in various ways. For instance, a turtle cannot move forward if there's already a
|
* Many turtle functions can fail in various ways. For instance, a turtle cannot move forward if there's already a block
|
||||||
* > block there. Instead of erroring, functions which can fail either return [`true`] if they succeed, or [`false`] and
|
* there. Instead of erroring, functions which can fail either return @{true} if they succeed, or @{false} and some
|
||||||
* > some error message if they fail.
|
* error message if they fail.
|
||||||
* >
|
* <p>
|
||||||
* > Unexpected failures can often lead to strange behaviour. It's often a good idea to check the return values of these
|
* Unexpected failures can often lead to strange behaviour. It's often a good idea to check the return values of these
|
||||||
* > functions, or wrap them in [`assert`] (for instance, use `assert(turtle.forward())` rather than `turtle.forward()`),
|
* functions, or wrap them in @{assert} (for instance, use `assert(turtle.forward())` rather than `turtle.forward()`),
|
||||||
* > so the program doesn't misbehave.
|
* so the program doesn't misbehave.
|
||||||
|
* :::
|
||||||
* <p>
|
* <p>
|
||||||
* ## Turtle upgrades
|
* ## Turtle upgrades
|
||||||
* While a normal turtle can move about the world and place blocks, its functionality is limited. Thankfully, turtles
|
* While a normal turtle can move about the world and place blocks, its functionality is limited. Thankfully, turtles
|
||||||
* can be upgraded with *tools* and [peripherals][`peripheral`]. Turtles have two upgrade slots, one on the left and right
|
* can be upgraded with *tools* and @{peripheral|peripherals}. Turtles have two upgrade slots, one on the left and right
|
||||||
* sides. Upgrades can be equipped by crafting a turtle with the upgrade, or calling the [`turtle.equipLeft`]/[`turtle.equipRight`]
|
* sides. Upgrades can be equipped by crafting a turtle with the upgrade, or calling the @{turtle.equipLeft}/@{turtle.equipRight}
|
||||||
* functions.
|
* functions.
|
||||||
* <p>
|
* <p>
|
||||||
* Turtle tools allow you to break blocks ([`turtle.dig`]) and attack entities ([`turtle.attack`]). Some tools are more
|
* Turtle tools allow you to break blocks (@{turtle.dig}) and attack entities (@{turtle.attack}). Some tools are more
|
||||||
* suitable to a task than others. For instance, a diamond pickaxe can break every block, while a sword does more
|
* suitable to a task than others. For instance, a diamond pickaxe can break every block, while a sword does more
|
||||||
* damage. Other tools have more niche use-cases, for instance hoes can til dirt.
|
* damage. Other tools have more niche use-cases, for instance hoes can til dirt.
|
||||||
* <p>
|
* <p>
|
||||||
* Peripherals (such as the [wireless modem][`modem`] or [`speaker`]) can also be equipped as upgrades. These are then
|
* Peripherals (such as the @{modem|wireless modem} or @{speaker}) can also be equipped as upgrades. These are then
|
||||||
* accessible by accessing the `"left"` or `"right"` peripheral.
|
* accessible by accessing the `"left"` or `"right"` peripheral.
|
||||||
* <p>
|
* <p>
|
||||||
* [Turtle Graphics]: https://en.wikipedia.org/wiki/Turtle_graphics "Turtle graphics"
|
* [Turtle Graphics]: https://en.wikipedia.org/wiki/Turtle_graphics "Turtle graphics"
|
||||||
@@ -288,7 +290,7 @@ public class TurtleAPI implements ILuaAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drop the currently selected stack into the inventory below the turtle, or as an item into the world if
|
* Drop the currently selected stack into the inventory in front of the turtle, or as an item into the world if
|
||||||
* there is no inventory.
|
* there is no inventory.
|
||||||
*
|
*
|
||||||
* @param count The number of items to drop. If not given, the entire stack will be dropped.
|
* @param count The number of items to drop. If not given, the entire stack will be dropped.
|
||||||
|
@@ -317,7 +317,6 @@ public class TurtleBrain implements TurtleAccessInternal {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Vec3 getVisualPosition(float f) {
|
public Vec3 getVisualPosition(float f) {
|
||||||
var offset = getRenderOffset(f);
|
var offset = getRenderOffset(f);
|
||||||
var pos = owner.getBlockPos();
|
var pos = owner.getBlockPos();
|
||||||
@@ -328,7 +327,6 @@ public class TurtleBrain implements TurtleAccessInternal {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getVisualYaw(float f) {
|
public float getVisualYaw(float f) {
|
||||||
var yaw = getDirection().toYRot();
|
var yaw = getDirection().toYRot();
|
||||||
switch (animation) {
|
switch (animation) {
|
||||||
|
@@ -44,7 +44,7 @@ public class TurtleMoveCommand implements TurtleCommand {
|
|||||||
var state = oldWorld.getBlockState(newPosition);
|
var state = oldWorld.getBlockState(newPosition);
|
||||||
if (!oldWorld.isEmptyBlock(newPosition) &&
|
if (!oldWorld.isEmptyBlock(newPosition) &&
|
||||||
!WorldUtil.isLiquidBlock(oldWorld, newPosition) &&
|
!WorldUtil.isLiquidBlock(oldWorld, newPosition) &&
|
||||||
!state.getMaterial().isReplaceable()) {
|
!state.canBeReplaced()) {
|
||||||
return TurtleCommandResult.failure("Movement obstructed");
|
return TurtleCommandResult.failure("Movement obstructed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user