Merge branch 'mc-1.15.x' into mc-1.16.x
@ -16,6 +16,3 @@ indent_size = 2
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
[*.properties]
|
||||
insert_final_newline = false
|
||||
|
17
.github/matchers/checkstyle.json
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "checkstyle",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^([a-z]+) ([\\w./-]+):(\\d+):(\\d+): (.*)$",
|
||||
"severity": 1,
|
||||
"file": 2,
|
||||
"line": 3,
|
||||
"column": 4,
|
||||
"message": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
18
.github/matchers/illuaminate.json
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "illuaminate",
|
||||
"severity": "warning",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^([\\w./-]+):\\[(\\d+):(\\d+)\\-(?:\\d+):(?:\\d+)\\]: (.*) \\[([a-z:-]+)\\]$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"message": 4,
|
||||
"code": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
15
.github/matchers/junit.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "junit",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^## ([\\w./-]+):(\\d+): (.*)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"message": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
36
.github/workflows/main-ci.yml
vendored
@ -23,10 +23,15 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
|
||||
- name: Disable Gradle daemon
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
./gradlew assemble --no-daemon || ./gradlew assemble --no-daemon
|
||||
./gradlew downloadAssets --no-daemon || ./gradlew downloadAssets --no-daemon
|
||||
./gradlew assemble || ./gradlew assemble
|
||||
./gradlew downloadAssets || ./gradlew downloadAssets
|
||||
./gradlew build
|
||||
|
||||
- name: Upload Jar
|
||||
@ -36,18 +41,21 @@ jobs:
|
||||
path: build/libs
|
||||
|
||||
- name: Upload Coverage
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
continue-on-error: true
|
||||
uses: codecov/codecov-action@v1
|
||||
|
||||
- name: Generate Java documentation stubs
|
||||
run: ./gradlew luaJavadoc --no-daemon
|
||||
- name: Parse test reports
|
||||
run: ./tools/parse-reports.py
|
||||
if: ${{ failure() }}
|
||||
|
||||
- name: Lint Lua code
|
||||
- name: Cache pre-commit
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pre-commit
|
||||
key: ${{ runner.os }}-pre-commit-${{ hashFiles('config/pre-commit/config.yml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pre-commit-
|
||||
|
||||
- name: Run linters
|
||||
run: |
|
||||
test -d bin || mkdir bin
|
||||
test -f bin/illuaminate || wget -q -Obin/illuaminate https://squiddev.cc/illuaminate/linux-x86-64/illuaminate
|
||||
chmod +x bin/illuaminate
|
||||
bin/illuaminate lint
|
||||
|
||||
- name: Check whitespace
|
||||
run: python3 tools/check-lines.py
|
||||
pip install pre-commit
|
||||
pre-commit run --config config/pre-commit/config.yml --show-diff-on-failure --all --color=always
|
||||
|
@ -65,7 +65,7 @@ Gradle should be your entrypoint to building most documentation. There's two tas
|
||||
|
||||
#### Writing documentation
|
||||
illuaminate's documentation system is not currently documented (somewhat ironic), but is _largely_ the same as
|
||||
[ldoc][ldoc]. Documentation comments are written in Markdown,
|
||||
[ldoc][ldoc]. Documentation comments are written in Markdown,
|
||||
|
||||
Our markdown engine does _not_ support GitHub flavoured markdown, and so does not support all the features one might
|
||||
expect (such as tables). It is very much recommended that you build and preview the docs locally first.
|
||||
@ -77,18 +77,18 @@ entire test suite (and some additional bits of verification).
|
||||
Before we get into writing tests, it's worth mentioning the various test suites that CC: Tweaked has:
|
||||
- "Core" Java (`./src/test/java`): These test core bits of the mod which don't require any Minecraft interaction.
|
||||
This includes the `@LuaFunction` system, file system code, etc...
|
||||
|
||||
|
||||
These tests are run by `./gradlew test`.
|
||||
|
||||
- CraftOS (`./src/test/resources/test-rom/`): These tests are written in Lua, and ensure the Lua environment, libraries
|
||||
and programs work as expected. These are (generally) written to be able to be run on emulators too, to provide some
|
||||
sort of compliance test.
|
||||
|
||||
|
||||
These tests are run by the '"Core" Java' test suite, and so are also run with `./gradlew test`.
|
||||
|
||||
- In-game (`./src/test/java/dan200/computercraft/ingame/`): These tests are run on an actual Minecraft server, using
|
||||
[the same system Mojang do][mc-test]. The aim of these is to test in-game behaviour of blocks and peripherals.
|
||||
|
||||
|
||||
These are run by `./gradlew testInGame`.
|
||||
|
||||
## CraftOS tests
|
||||
|
14
LICENSE
@ -19,14 +19,14 @@ Mod: The mod code designated by the present license, in source form, binary
|
||||
form, as obtained standalone, as part of a wider distribution or resulting from
|
||||
the compilation of the original or modified sources.
|
||||
|
||||
Dependency: Code required for the mod to work properly. This includes
|
||||
Dependency: Code required for the mod to work properly. This includes
|
||||
dependencies required to compile the code as well as any file or modification
|
||||
that is explicitly or implicitly required for the mod to be working.
|
||||
|
||||
1. Scope
|
||||
--------
|
||||
|
||||
The present license is granted to any user of the mod. As a prerequisite,
|
||||
The present license is granted to any user of the mod. As a prerequisite,
|
||||
a user must own a legally acquired copy of Minecraft
|
||||
|
||||
2. Liability
|
||||
@ -41,13 +41,13 @@ or misuse of this mod fall on the user.
|
||||
3. Play rights
|
||||
--------------
|
||||
|
||||
The user is allowed to install this mod on a Minecraft client or server and to play
|
||||
The user is allowed to install this mod on a Minecraft client or server and to play
|
||||
without restriction.
|
||||
|
||||
4. Modification rights
|
||||
----------------------
|
||||
|
||||
The user has the right to decompile the source code, look at either the
|
||||
The user has the right to decompile the source code, look at either the
|
||||
decompiled version or the original source code, and to modify it.
|
||||
|
||||
5. Distribution of original or modified copy rights
|
||||
@ -61,10 +61,10 @@ include:
|
||||
- patch to its source or binary files
|
||||
- any copy of a portion of its binary source files
|
||||
|
||||
The user is allowed to redistribute this mod partially, in totality, or
|
||||
The user is allowed to redistribute this mod partially, in totality, or
|
||||
included in a distribution.
|
||||
|
||||
When distributing binary files, the user must provide means to obtain its
|
||||
When distributing binary files, the user must provide means to obtain its
|
||||
entire set of sources or modified sources at no cost.
|
||||
|
||||
All distributions of this mod must remain licensed under the CCPL.
|
||||
@ -92,7 +92,7 @@ must be made available at no cost and remain licensed under the CCPL.
|
||||
---------------
|
||||
|
||||
If you choose to contribute code or assets to be included in this mod, you
|
||||
agree that, if added to to the main repository at
|
||||
agree that, if added to to the main repository at
|
||||
https://github.com/dan200/ComputerCraft, your contributions will be covered by
|
||||
this license, and that Daniel Ratcliffe will retain the right to re-license the
|
||||
mod, including your contributions, in part or in whole, under other licenses.
|
||||
|
@ -142,10 +142,7 @@ dependencies {
|
||||
|
||||
runtimeOnly fg.deobf("mezz.jei:jei-1.16.4:7.6.0.58")
|
||||
|
||||
compileOnly 'com.google.auto.service:auto-service:1.0-rc7'
|
||||
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'
|
||||
|
||||
shade 'org.squiddev:Cobalt:0.5.1-SNAPSHOT'
|
||||
shade 'org.squiddev:Cobalt:0.5.2-SNAPSHOT'
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.0'
|
||||
@ -156,7 +153,7 @@ dependencies {
|
||||
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8'
|
||||
testAnnotationProcessor 'org.spongepowered:mixin:0.8.2:processor'
|
||||
|
||||
cctJavadoc 'cc.tweaked:cct-javadoc:1.3.0'
|
||||
cctJavadoc 'cc.tweaked:cct-javadoc:1.4.0'
|
||||
}
|
||||
|
||||
// Compile tasks
|
||||
|
@ -2488,4 +2488,4 @@
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
</component>
|
||||
|
@ -58,4 +58,4 @@
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</code_scheme>
|
||||
|
55
config/pre-commit/config.yml
Normal file
@ -0,0 +1,55 @@
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.2.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-merge-conflict
|
||||
|
||||
# Quick syntax checkers
|
||||
- id: check-xml
|
||||
- id: check-yaml
|
||||
- id: check-toml
|
||||
- id: check-json
|
||||
exclude: "tsconfig\\.json$"
|
||||
|
||||
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
|
||||
rev: 2.3.5
|
||||
hooks:
|
||||
- id: editorconfig-checker
|
||||
args: ['-disable-indentation']
|
||||
exclude: "^(.*\\.(bat)|LICENSE)$"
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: checkstyle
|
||||
name: Check Java codestyle
|
||||
files: ".*\\.java$"
|
||||
language: system
|
||||
entry: ./gradlew checkstyleMain checkstyleTest
|
||||
pass_filenames: false
|
||||
require_serial: true
|
||||
- id: license
|
||||
name: Check Java license headers
|
||||
files: ".*\\.java$"
|
||||
language: system
|
||||
entry: ./gradlew licenseFormat
|
||||
pass_filenames: false
|
||||
require_serial: true
|
||||
- id: illuaminate
|
||||
name: Check Lua code
|
||||
files: ".*\\.(lua|java|md)"
|
||||
language: script
|
||||
entry: config/pre-commit/illuaminate-lint.sh
|
||||
pass_filenames: false
|
||||
require_serial: true
|
||||
|
||||
exclude: |
|
||||
(?x)^(
|
||||
src/generated|
|
||||
src/test/resources/test-rom/data/json-parsing/|
|
||||
src/test/server-files/|
|
||||
config/idea/
|
||||
)
|
16
config/pre-commit/illuaminate-lint.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env sh
|
||||
set -e
|
||||
|
||||
test -d bin || mkdir bin
|
||||
test -f bin/illuaminate || curl -s -obin/illuaminate https://squiddev.cc/illuaminate/linux-x86-64/illuaminate
|
||||
chmod +x bin/illuaminate
|
||||
|
||||
if [ -n ${GITHUB_ACTIONS+x} ]; then
|
||||
# Register a problem matcher (see https://github.com/actions/toolkit/blob/master/docs/problem-matchers.md)
|
||||
# for illuaminate.
|
||||
echo "::add-matcher::.github/matchers/illuaminate.json"
|
||||
trap 'echo "::remove-matcher owner=illuaminate::"' EXIT
|
||||
fi
|
||||
|
||||
./gradlew luaJavadoc
|
||||
bin/illuaminate lint
|
@ -20,7 +20,7 @@ Prints each key when the user presses it, and if the key is being held.
|
||||
|
||||
```lua
|
||||
while true do
|
||||
local event, key, is_held = os.pullEvent("key")
|
||||
local event, key, is_held = os.pullEvent("key")
|
||||
print(("%s held=%s"):format(keys.getName(key), is_held))
|
||||
end
|
||||
```
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Mod properties
|
||||
mod_version=1.95.3
|
||||
mod_version=1.96.0
|
||||
|
||||
# Minecraft properties (update mods.toml when changing)
|
||||
mc_version=1.16.4
|
||||
|
@ -8,7 +8,6 @@ package dan200.computercraft;
|
||||
import dan200.computercraft.api.turtle.event.TurtleAction;
|
||||
import dan200.computercraft.core.apis.http.options.Action;
|
||||
import dan200.computercraft.core.apis.http.options.AddressRule;
|
||||
import dan200.computercraft.core.asm.GenericSource;
|
||||
import dan200.computercraft.shared.Config;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputerRegistry;
|
||||
@ -17,7 +16,6 @@ import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
|
||||
import dan200.computercraft.shared.pocket.peripherals.PocketModem;
|
||||
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
|
||||
import dan200.computercraft.shared.turtle.upgrades.*;
|
||||
import dan200.computercraft.shared.util.ServiceUtil;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@ -117,6 +115,5 @@ public final class ComputerCraft
|
||||
{
|
||||
Config.setup();
|
||||
Registry.setup();
|
||||
GenericSource.setup( () -> ServiceUtil.loadServicesForge( GenericSource.class ) );
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ package dan200.computercraft;
|
||||
import dan200.computercraft.api.ComputerCraftAPI.IComputerCraftAPI;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.lua.GenericSource;
|
||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||
import dan200.computercraft.api.media.IMediaProvider;
|
||||
import dan200.computercraft.api.network.IPacketNetwork;
|
||||
@ -18,9 +19,11 @@ import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.core.apis.ApiFactories;
|
||||
import dan200.computercraft.core.asm.GenericMethod;
|
||||
import dan200.computercraft.core.filesystem.FileMount;
|
||||
import dan200.computercraft.core.filesystem.ResourceMount;
|
||||
import dan200.computercraft.shared.*;
|
||||
import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider;
|
||||
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
|
||||
import dan200.computercraft.shared.util.IDAssigner;
|
||||
import dan200.computercraft.shared.wired.WiredNode;
|
||||
@ -31,6 +34,7 @@ import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockReader;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.server.ServerLifecycleHooks;
|
||||
@ -108,6 +112,18 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
|
||||
Peripherals.register( provider );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerGenericSource( @Nonnull GenericSource source )
|
||||
{
|
||||
GenericMethod.register( source );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerGenericCapability( @Nonnull Capability<?> capability )
|
||||
{
|
||||
GenericPeripheralProvider.addCapability( capability );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade )
|
||||
{
|
||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.api;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.lua.GenericSource;
|
||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||
import dan200.computercraft.api.media.IMedia;
|
||||
import dan200.computercraft.api.media.IMediaProvider;
|
||||
@ -23,6 +24,7 @@ import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockReader;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@ -116,7 +118,7 @@ public final class ComputerCraftAPI
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a peripheral provider to convert blocks into {@link IPeripheral} implementations.
|
||||
* rers a peripheral provider to convert blocks into {@link IPeripheral} implementations.
|
||||
*
|
||||
* @param provider The peripheral provider to register.
|
||||
* @see IPeripheral
|
||||
@ -127,6 +129,28 @@ public final class ComputerCraftAPI
|
||||
getInstance().registerPeripheralProvider( provider );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a method source for generic peripherals.
|
||||
*
|
||||
* @param source The method source to register.
|
||||
* @see GenericSource
|
||||
*/
|
||||
public static void registerGenericSource( @Nonnull GenericSource source )
|
||||
{
|
||||
getInstance().registerGenericSource( source );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a capability that can be used by generic peripherals.
|
||||
*
|
||||
* @param capability The capability to register.
|
||||
* @see GenericSource
|
||||
*/
|
||||
public static void registerGenericCapability( @Nonnull Capability<?> capability )
|
||||
{
|
||||
getInstance().registerGenericCapability( capability );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new turtle turtle for use in ComputerCraft. After calling this,
|
||||
* users should be able to craft Turtles with your new turtle. It is recommended to call
|
||||
@ -258,6 +282,10 @@ public final class ComputerCraftAPI
|
||||
|
||||
void registerPeripheralProvider( @Nonnull IPeripheralProvider provider );
|
||||
|
||||
void registerGenericSource( @Nonnull GenericSource source );
|
||||
|
||||
void registerGenericCapability( @Nonnull Capability<?> capability );
|
||||
|
||||
void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade );
|
||||
|
||||
void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider );
|
||||
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.peripheral.IPeripheralProvider;
|
||||
import dan200.computercraft.core.asm.LuaMethod;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.items.IItemHandler;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A generic source of {@link LuaMethod} functions.
|
||||
*
|
||||
* Unlike normal objects ({@link IDynamicLuaObject} or {@link IPeripheral}), methods do not target this object but
|
||||
* instead are defined as {@code static} and accept their target as the first parameter. This allows you to inject
|
||||
* methods onto objects you do not own, as well as declaring methods for a specific "trait" (for instance, a
|
||||
* {@link Capability}).
|
||||
*
|
||||
* Currently the "generic peripheral" system is incompatible with normal peripherals. Normal {@link IPeripheralProvider}
|
||||
* or {@link IPeripheral} implementations take priority. Tile entities which use this system are given a peripheral name
|
||||
* determined by their id, rather than any peripheral provider. This will hopefully change in the future, once a suitable
|
||||
* design has been established.
|
||||
*
|
||||
* For example, the main CC: Tweaked mod defines a generic source for inventories, which works on {@link IItemHandler}s:
|
||||
*
|
||||
* <pre>{@code
|
||||
* public class InventoryMethods implements GenericSource {
|
||||
* \@LuaFunction( mainThread = true )
|
||||
* public static int size(IItemHandler inventory) {
|
||||
* return inventory.getSlots();
|
||||
* }
|
||||
*
|
||||
* // ...
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @see ComputerCraftAPI#registerGenericSource(GenericSource)
|
||||
* @see ComputerCraftAPI#registerGenericCapability(Capability) New capabilities (those not built into Forge) must be
|
||||
* explicitly given to the generic peripheral system, as there is no way to enumerate all capabilities.
|
||||
*/
|
||||
public interface GenericSource
|
||||
{
|
||||
/**
|
||||
* A unique identifier for this generic source.
|
||||
*
|
||||
* This is currently unused, but may be used in the future to allow disabling specific sources. It is recommended
|
||||
* to return an identifier using your mod's ID.
|
||||
*
|
||||
* @return This source's identifier.
|
||||
*/
|
||||
@Nonnull
|
||||
ResourceLocation id();
|
||||
}
|
@ -47,7 +47,10 @@ public class ComputerBorderRenderer
|
||||
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 GAP = 4;
|
||||
private static final int LIGHT_BORDER_Y = 56;
|
||||
private static final int LIGHT_CORNER_Y = 80;
|
||||
|
||||
public static final int LIGHT_HEIGHT = 8;
|
||||
|
||||
private static final float TEX_SCALE = 1 / 256.0f;
|
||||
|
||||
@ -101,15 +104,15 @@ public class ComputerBorderRenderer
|
||||
|
||||
public static void render( Matrix4f transform, IVertexBuilder buffer, int x, int y, int z, int width, int height, float r, float g, float b )
|
||||
{
|
||||
render( transform, buffer, x, y, z, width, height, 0, r, g, b );
|
||||
render( transform, buffer, x, y, z, width, height, false, r, g, b );
|
||||
}
|
||||
|
||||
public static void render( Matrix4f transform, IVertexBuilder buffer, int x, int y, int z, int width, int height, int borderHeight, float r, float g, float b )
|
||||
public static void render( Matrix4f transform, IVertexBuilder buffer, int x, int y, int z, int width, int height, boolean withLight, float r, float g, float b )
|
||||
{
|
||||
new ComputerBorderRenderer( transform, buffer, z, r, g, b ).doRender( x, y, width, height, borderHeight );
|
||||
new ComputerBorderRenderer( transform, buffer, z, r, g, b ).doRender( x, y, width, height, withLight );
|
||||
}
|
||||
|
||||
public void doRender( int x, int y, int width, int height, int bottomHeight )
|
||||
public void doRender( int x, int y, int width, int height, boolean withLight )
|
||||
{
|
||||
int endX = x + width;
|
||||
int endY = y + height;
|
||||
@ -125,28 +128,18 @@ public class ComputerBorderRenderer
|
||||
|
||||
// Bottom bar. We allow for drawing a stretched version, which allows for additional elements (such as the
|
||||
// pocket computer's lights).
|
||||
if( bottomHeight <= 0 )
|
||||
if( withLight )
|
||||
{
|
||||
renderTexture( x, endY, 0, LIGHT_BORDER_Y, endX - x, BORDER + LIGHT_HEIGHT, BORDER, BORDER + LIGHT_HEIGHT );
|
||||
renderTexture( x - BORDER, endY, CORNER_LEFT_X, LIGHT_CORNER_Y, BORDER, BORDER + LIGHT_HEIGHT );
|
||||
renderTexture( endX, endY, CORNER_RIGHT_X, LIGHT_CORNER_Y, BORDER, BORDER + LIGHT_HEIGHT );
|
||||
}
|
||||
else
|
||||
{
|
||||
renderLine( x, endY, 0, BORDER, endX - x, BORDER );
|
||||
renderCorner( x - BORDER, endY, CORNER_LEFT_X, CORNER_BOTTOM_Y );
|
||||
renderCorner( endX, endY, CORNER_RIGHT_X, CORNER_BOTTOM_Y );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Bottom left, middle, right. We do this in three portions: the top inner corners, an extended region for
|
||||
// lights, and then the bottom outer corners.
|
||||
renderTexture( x - BORDER, endY, CORNER_LEFT_X, CORNER_BOTTOM_Y, BORDER, BORDER / 2 );
|
||||
renderTexture( x, endY, 0, BORDER, width, BORDER / 2, BORDER, BORDER / 2 );
|
||||
renderTexture( endX, endY, CORNER_RIGHT_X, CORNER_BOTTOM_Y, BORDER, BORDER / 2 );
|
||||
|
||||
renderTexture( x - BORDER, endY + BORDER / 2, CORNER_LEFT_X, CORNER_BOTTOM_Y + GAP, BORDER, bottomHeight, BORDER, GAP );
|
||||
renderTexture( x, endY + BORDER / 2, 0, BORDER + GAP, width, bottomHeight, BORDER, GAP );
|
||||
renderTexture( endX, endY + BORDER / 2, CORNER_RIGHT_X, CORNER_BOTTOM_Y + GAP, BORDER, bottomHeight, BORDER, GAP );
|
||||
|
||||
renderTexture( x - BORDER, endY + bottomHeight + BORDER / 2, CORNER_LEFT_X, CORNER_BOTTOM_Y + BORDER / 2, BORDER, BORDER / 2 );
|
||||
renderTexture( x, endY + bottomHeight + BORDER / 2, 0, BORDER + BORDER / 2, width, BORDER / 2 );
|
||||
renderTexture( endX, endY + bottomHeight + BORDER / 2, CORNER_RIGHT_X, CORNER_BOTTOM_Y + BORDER / 2, BORDER, BORDER / 2 );
|
||||
}
|
||||
}
|
||||
|
||||
private void renderCorner( int x, int y, int u, int v )
|
||||
|
@ -30,8 +30,7 @@ import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.*;
|
||||
|
||||
/**
|
||||
* Emulates map rendering for pocket computers.
|
||||
@ -39,8 +38,6 @@ import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
|
||||
public final class ItemPocketRenderer extends ItemMapLikeRenderer
|
||||
{
|
||||
private static final int LIGHT_HEIGHT = 8;
|
||||
|
||||
private static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer();
|
||||
|
||||
private ItemPocketRenderer()
|
||||
@ -131,7 +128,7 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
|
||||
BufferBuilder buffer = tessellator.getBuilder();
|
||||
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR_TEX );
|
||||
|
||||
ComputerBorderRenderer.render( transform, buffer, 0, 0, 0, width, height, LIGHT_HEIGHT, r, g, b );
|
||||
ComputerBorderRenderer.render( transform, buffer, 0, 0, 0, width, height, true, r, g, b );
|
||||
|
||||
tessellator.end();
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ final class LuaDateTime
|
||||
formatter.appendValue( ChronoField.HOUR_OF_DAY, 2 );
|
||||
break;
|
||||
case 'I':
|
||||
formatter.appendValue( ChronoField.HOUR_OF_AMPM );
|
||||
formatter.appendValue( ChronoField.HOUR_OF_AMPM, 2 );
|
||||
break;
|
||||
case 'j':
|
||||
formatter.appendValue( ChronoField.DAY_OF_YEAR, 3 );
|
||||
|
@ -11,10 +11,7 @@ import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.primitives.Primitives;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Type;
|
||||
@ -111,7 +108,7 @@ public final class Generator<T>
|
||||
addMethod( methods, method, annotation, instance );
|
||||
}
|
||||
|
||||
for( GenericSource.GenericMethod method : GenericSource.GenericMethod.all() )
|
||||
for( GenericMethod method : GenericMethod.all() )
|
||||
{
|
||||
if( !method.target.isAssignableFrom( klass ) ) continue;
|
||||
|
||||
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.core.asm;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.GenericSource;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A generic method is a method belonging to a {@link GenericSource} with a known target.
|
||||
*/
|
||||
public class GenericMethod
|
||||
{
|
||||
final Method method;
|
||||
final LuaFunction annotation;
|
||||
final Class<?> target;
|
||||
|
||||
private static final List<GenericSource> sources = new ArrayList<>();
|
||||
private static List<GenericMethod> cache;
|
||||
|
||||
GenericMethod( Method method, LuaFunction annotation, Class<?> target )
|
||||
{
|
||||
this.method = method;
|
||||
this.annotation = annotation;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all public static methods annotated with {@link LuaFunction} which belong to a {@link GenericSource}.
|
||||
*
|
||||
* @return All available generic methods.
|
||||
*/
|
||||
static List<GenericMethod> all()
|
||||
{
|
||||
if( cache != null ) return cache;
|
||||
return cache = sources.stream()
|
||||
.flatMap( x -> Arrays.stream( x.getClass().getDeclaredMethods() ) )
|
||||
.map( method ->
|
||||
{
|
||||
LuaFunction annotation = method.getAnnotation( LuaFunction.class );
|
||||
if( annotation == null ) return null;
|
||||
|
||||
if( !Modifier.isStatic( method.getModifiers() ) )
|
||||
{
|
||||
ComputerCraft.log.error( "GenericSource method {}.{} should be static.", method.getDeclaringClass(), method.getName() );
|
||||
return null;
|
||||
}
|
||||
|
||||
Type[] types = method.getGenericParameterTypes();
|
||||
if( types.length == 0 )
|
||||
{
|
||||
ComputerCraft.log.error( "GenericSource method {}.{} has no parameters.", method.getDeclaringClass(), method.getName() );
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<?> target = Reflect.getRawType( method, types[0], false );
|
||||
if( target == null ) return null;
|
||||
|
||||
return new GenericMethod( method, annotation, target );
|
||||
} )
|
||||
.filter( Objects::nonNull )
|
||||
.collect( Collectors.toList() );
|
||||
}
|
||||
|
||||
|
||||
public static synchronized void register( @Nonnull GenericSource source )
|
||||
{
|
||||
Objects.requireNonNull( source, "Source cannot be null" );
|
||||
|
||||
if( cache != null )
|
||||
{
|
||||
ComputerCraft.log.warn( "Registering a generic source {} after cache has been built. This source will be ignored.", cache );
|
||||
}
|
||||
|
||||
sources.add( source );
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.core.asm;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider;
|
||||
import dan200.computercraft.shared.util.ServiceUtil;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A generic source of {@link LuaMethod} functions. This allows for injecting methods onto objects you do not own.
|
||||
*
|
||||
* Unlike conventional Lua objects, the annotated methods should be {@code static}, with their target as the first
|
||||
* parameter.
|
||||
*
|
||||
* This is used by the generic peripheral system ({@link GenericPeripheralProvider}) to provide methods for arbitrary
|
||||
* tile entities. Eventually this'll be be exposed in the public API. Until it is stabilised, it will remain in this
|
||||
* package - do not use it in external mods!
|
||||
*/
|
||||
public interface GenericSource
|
||||
{
|
||||
/**
|
||||
* A unique identifier for this generic source. This may be used in the future to allow disabling specific sources.
|
||||
*
|
||||
* @return This source's identifier.
|
||||
*/
|
||||
@Nonnull
|
||||
ResourceLocation id();
|
||||
|
||||
/**
|
||||
* Register a stream of generic sources.
|
||||
*
|
||||
* @param sources The source of generic methods.
|
||||
* @see ServiceUtil For ways to load this. Sadly {@link java.util.ServiceLoader} is broken under Forge, but we don't
|
||||
* want to add a hard-dep on Forge within core either.
|
||||
*/
|
||||
static void setup( Supplier<Stream<GenericSource>> sources )
|
||||
{
|
||||
GenericMethod.sources = sources;
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic method is a method belonging to a {@link GenericSource} with a known target.
|
||||
*/
|
||||
class GenericMethod
|
||||
{
|
||||
final Method method;
|
||||
final LuaFunction annotation;
|
||||
final Class<?> target;
|
||||
|
||||
static Supplier<Stream<GenericSource>> sources;
|
||||
private static List<GenericMethod> cache;
|
||||
|
||||
GenericMethod( Method method, LuaFunction annotation, Class<?> target )
|
||||
{
|
||||
this.method = method;
|
||||
this.annotation = annotation;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all public static methods annotated with {@link LuaFunction} which belong to a {@link GenericSource}.
|
||||
*
|
||||
* @return All available generic methods.
|
||||
*/
|
||||
static List<GenericMethod> all()
|
||||
{
|
||||
if( cache != null ) return cache;
|
||||
if( sources == null )
|
||||
{
|
||||
ComputerCraft.log.warn( "Getting GenericMethods without a provider" );
|
||||
return cache = Collections.emptyList();
|
||||
}
|
||||
|
||||
return cache = sources.get()
|
||||
.flatMap( x -> Arrays.stream( x.getClass().getDeclaredMethods() ) )
|
||||
.map( method ->
|
||||
{
|
||||
LuaFunction annotation = method.getAnnotation( LuaFunction.class );
|
||||
if( annotation == null ) return null;
|
||||
|
||||
if( !Modifier.isStatic( method.getModifiers() ) )
|
||||
{
|
||||
ComputerCraft.log.error( "GenericSource method {}.{} should be static.", method.getDeclaringClass(), method.getName() );
|
||||
return null;
|
||||
}
|
||||
|
||||
Type[] types = method.getGenericParameterTypes();
|
||||
if( types.length == 0 )
|
||||
{
|
||||
ComputerCraft.log.error( "GenericSource method {}.{} has no parameters.", method.getDeclaringClass(), method.getName() );
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<?> target = Reflect.getRawType( method, types[0], false );
|
||||
if( target == null ) return null;
|
||||
|
||||
return new GenericMethod( method, annotation, target );
|
||||
} )
|
||||
.filter( Objects::nonNull )
|
||||
.collect( Collectors.toList() );
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import com.google.common.io.ByteStreams;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.core.apis.handles.ArrayByteChannel;
|
||||
import dan200.computercraft.shared.util.IoUtil;
|
||||
import net.minecraft.resources.IReloadableResourceManager;
|
||||
import net.minecraft.resources.IResource;
|
||||
import net.minecraft.resources.IResourceManager;
|
||||
@ -122,7 +123,7 @@ public final class ResourceMount implements IMount
|
||||
if( !hasAny )
|
||||
{
|
||||
ComputerCraft.log.warn( "Cannot find any files under /data/{}/{} for resource mount.", namespace, subPath );
|
||||
if( newRoot != null )
|
||||
if( existingNamespace != null )
|
||||
{
|
||||
ComputerCraft.log.warn( "There are files under /data/{}/{} though. Did you get the wrong namespace?", existingNamespace, subPath );
|
||||
}
|
||||
@ -244,11 +245,20 @@ public final class ResourceMount implements IMount
|
||||
byte[] contents = CONTENTS_CACHE.getIfPresent( file );
|
||||
if( contents != null ) return new ArrayByteChannel( contents );
|
||||
|
||||
try( InputStream stream = manager.getResource( file.identifier ).getInputStream() )
|
||||
try
|
||||
{
|
||||
InputStream stream = manager.getResource( file.identifier ).getInputStream();
|
||||
if( stream.available() > MAX_CACHED_SIZE ) return Channels.newChannel( stream );
|
||||
|
||||
contents = ByteStreams.toByteArray( stream );
|
||||
try
|
||||
{
|
||||
contents = ByteStreams.toByteArray( stream );
|
||||
}
|
||||
finally
|
||||
{
|
||||
IoUtil.closeQuietly( stream );
|
||||
}
|
||||
|
||||
CONTENTS_CACHE.put( file, contents );
|
||||
return new ArrayByteChannel( contents );
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ public final class Registry
|
||||
{
|
||||
// Upgrades
|
||||
ComputerCraft.TurtleUpgrades.wirelessModemNormal = new TurtleModem( false, new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_normal" ) );
|
||||
TurtleUpgrades.register( ComputerCraft.TurtleUpgrades.wirelessModemNormal );
|
||||
ComputerCraftAPI.registerTurtleUpgrade( ComputerCraft.TurtleUpgrades.wirelessModemNormal );
|
||||
|
||||
ComputerCraft.TurtleUpgrades.wirelessModemAdvanced = new TurtleModem( true, new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_advanced" ) );
|
||||
ComputerCraftAPI.registerTurtleUpgrade( ComputerCraft.TurtleUpgrades.wirelessModemAdvanced );
|
||||
|
@ -79,7 +79,10 @@ public final class ColourableRecipe extends SpecialRecipe
|
||||
}
|
||||
|
||||
if( colourable.isEmpty() ) return ItemStack.EMPTY;
|
||||
return ((IColouredItem) colourable.getItem()).withColour( colourable, tracker.getColour() );
|
||||
|
||||
ItemStack stack = ((IColouredItem) colourable.getItem()).withColour( colourable, tracker.getColour() );
|
||||
stack.setCount( 1 );
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -15,22 +15,22 @@ import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.common.util.NonNullConsumer;
|
||||
import net.minecraftforge.energy.CapabilityEnergy;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
import net.minecraftforge.items.CapabilityItemHandler;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class GenericPeripheralProvider
|
||||
{
|
||||
private static final Capability<?>[] CAPABILITIES = new Capability<?>[] {
|
||||
CapabilityItemHandler.ITEM_HANDLER_CAPABILITY,
|
||||
CapabilityEnergy.ENERGY,
|
||||
CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY,
|
||||
};
|
||||
private static final ArrayList<Capability<?>> capabilities = new ArrayList<>();
|
||||
|
||||
public static synchronized void addCapability( Capability<?> capability )
|
||||
{
|
||||
Objects.requireNonNull( capability, "Capability cannot be null" );
|
||||
if( !capabilities.contains( capability ) ) capabilities.add( capability );
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static IPeripheral getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side, NonNullConsumer<LazyOptional<IPeripheral>> invalidate )
|
||||
@ -43,7 +43,7 @@ public class GenericPeripheralProvider
|
||||
List<NamedMethod<PeripheralMethod>> tileMethods = PeripheralMethod.GENERATOR.getMethods( tile.getClass() );
|
||||
if( !tileMethods.isEmpty() ) addSaturated( saturated, tile, tileMethods );
|
||||
|
||||
for( Capability<?> capability : CAPABILITIES )
|
||||
for( Capability<?> capability : capabilities )
|
||||
{
|
||||
LazyOptional<?> wrapper = tile.getCapability( capability );
|
||||
wrapper.ifPresent( contents -> {
|
||||
|
@ -5,12 +5,11 @@
|
||||
*/
|
||||
package dan200.computercraft.shared.peripheral.generic.methods;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.GenericSource;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.core.asm.GenericSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.energy.IEnergyStorage;
|
||||
import net.minecraftforge.versions.forge.ForgeVersion;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
@ -26,14 +25,13 @@ import javax.annotation.Nonnull;
|
||||
*
|
||||
* @cc.module energy_storage
|
||||
*/
|
||||
@AutoService( GenericSource.class )
|
||||
public class EnergyMethods implements GenericSource
|
||||
{
|
||||
@Nonnull
|
||||
@Override
|
||||
public ResourceLocation id()
|
||||
{
|
||||
return new ResourceLocation( ForgeVersion.MOD_ID, "energy" );
|
||||
return new ResourceLocation( ComputerCraft.MOD_ID, "energy" );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,12 +5,12 @@
|
||||
*/
|
||||
package dan200.computercraft.shared.peripheral.generic.methods;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.GenericSource;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.asm.GenericSource;
|
||||
import dan200.computercraft.shared.peripheral.generic.data.FluidData;
|
||||
import net.minecraft.fluid.Fluid;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
@ -20,7 +20,6 @@ import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import net.minecraftforge.versions.forge.ForgeVersion;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@ -35,14 +34,13 @@ import static dan200.computercraft.shared.peripheral.generic.methods.ArgumentHel
|
||||
*
|
||||
* @cc.module fluid_storage
|
||||
*/
|
||||
@AutoService( GenericSource.class )
|
||||
public class FluidMethods implements GenericSource
|
||||
{
|
||||
@Nonnull
|
||||
@Override
|
||||
public ResourceLocation id()
|
||||
{
|
||||
return new ResourceLocation( ForgeVersion.MOD_ID, "fluid" );
|
||||
return new ResourceLocation( ComputerCraft.MOD_ID, "fluid" );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,12 +5,12 @@
|
||||
*/
|
||||
package dan200.computercraft.shared.peripheral.generic.methods;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.GenericSource;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.asm.GenericSource;
|
||||
import dan200.computercraft.shared.peripheral.generic.data.ItemData;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@ -21,7 +21,6 @@ import net.minecraftforge.items.CapabilityItemHandler;
|
||||
import net.minecraftforge.items.IItemHandler;
|
||||
import net.minecraftforge.items.ItemHandlerHelper;
|
||||
import net.minecraftforge.items.wrapper.InvWrapper;
|
||||
import net.minecraftforge.versions.forge.ForgeVersion;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@ -36,14 +35,13 @@ import static dan200.computercraft.shared.peripheral.generic.methods.ArgumentHel
|
||||
*
|
||||
* @cc.module inventory
|
||||
*/
|
||||
@AutoService( GenericSource.class )
|
||||
public class InventoryMethods implements GenericSource
|
||||
{
|
||||
@Nonnull
|
||||
@Override
|
||||
public ResourceLocation id()
|
||||
{
|
||||
return new ResourceLocation( ForgeVersion.MOD_ID, "inventory" );
|
||||
return new ResourceLocation( ComputerCraft.MOD_ID, "inventory" );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,7 +61,8 @@ public class InventoryMethods implements GenericSource
|
||||
*
|
||||
* Each item in the inventory is represented by a table containing some basic information, much like
|
||||
* {@link dan200.computercraft.shared.turtle.apis.TurtleAPI#getItemDetail} includes. More information can be fetched
|
||||
* with {@link #getItemDetail}.
|
||||
* with {@link #getItemDetail}. The table contains the item `name`, the `count` and an a (potentially nil) hash of
|
||||
* the item's `nbt.` This NBT data doesn't contain anything useful, but allows you to distinguish identical items.
|
||||
*
|
||||
* The returned table is sparse, and so empty slots will be `nil` - it is recommended to loop over using `pairs`
|
||||
* rather than `ipairs`.
|
||||
@ -71,6 +70,14 @@ public class InventoryMethods implements GenericSource
|
||||
* @param inventory The current inventory.
|
||||
* @return All items in this inventory.
|
||||
* @cc.treturn { (table|nil)... } All items in this inventory.
|
||||
* @cc.usage Find an adjacent chest and print all items in it.
|
||||
*
|
||||
* <pre>{@code
|
||||
* local chest = peripheral.find("minecraft:chest")
|
||||
* for slot, item in pairs(chest.list()) do
|
||||
* print(("%d x %s in slot %d"):format(item.count, item.name, slot))
|
||||
* end
|
||||
* }</pre>
|
||||
*/
|
||||
@LuaFunction( mainThread = true )
|
||||
public static Map<Integer, Map<String, ?>> list( IItemHandler inventory )
|
||||
@ -89,11 +96,32 @@ public class InventoryMethods implements GenericSource
|
||||
/**
|
||||
* Get detailed information about an item.
|
||||
*
|
||||
* The returned information contains the same information as each item in
|
||||
* {@link #list}, as well as additional details like the display name
|
||||
* (`displayName`) and item durability (`damage`, `maxDamage`, `durability`).
|
||||
*
|
||||
* Some items include more information (such as enchantments) - it is
|
||||
* recommended to print it out using @{textutils.serialize} or in the Lua
|
||||
* REPL, to explore what is available.
|
||||
*
|
||||
* @param inventory The current inventory.
|
||||
* @param slot The slot to get information about.
|
||||
* @return Information about the item in this slot, or {@code nil} if not present.
|
||||
* @throws LuaException If the slot is out of range.
|
||||
* @cc.treturn table Information about the item in this slot, or {@code nil} if not present.
|
||||
* @cc.usage Print some information about the first in a chest.
|
||||
*
|
||||
* <pre>{@code
|
||||
* local chest = peripheral.find("minecraft:chest")
|
||||
* local item = chest.getItemDetail(1)
|
||||
* if not item then print("No item") return end
|
||||
*
|
||||
* print(("%s (%s)"):format(item.displayName, item.name))
|
||||
* print(("Count: %d/%d"):format(item.count, item.maxCount))
|
||||
* if item.damage then
|
||||
* print(("Damage: %d/%d"):format(item.damage, item.maxDamage))
|
||||
* end
|
||||
* }</pre>
|
||||
*/
|
||||
@Nullable
|
||||
@LuaFunction( mainThread = true )
|
||||
@ -105,6 +133,33 @@ public class InventoryMethods implements GenericSource
|
||||
return stack.isEmpty() ? null : ItemData.fill( new HashMap<>(), stack );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum number of items which can be stored in this slot.
|
||||
*
|
||||
* Typically this will be limited to 64 items. However, some inventories (such as barrels or caches) can store
|
||||
* hundreds or thousands of items in one slot.
|
||||
*
|
||||
* @param inventory Inventory to probe.
|
||||
* @param slot The slot
|
||||
* @return The maximum number of items in this slot.
|
||||
* @throws LuaException If the slot is out of range.
|
||||
* @cc.usage Count the maximum number of items an adjacent chest can hold.
|
||||
* <pre>{@code
|
||||
* local chest = peripheral.find("minecraft:chest")
|
||||
* local total = 0
|
||||
* for i = 1, chest.size() do
|
||||
* total = total + chest.getItemLimit(i)
|
||||
* end
|
||||
* print(total)
|
||||
* }</pre>
|
||||
*/
|
||||
@LuaFunction( mainThread = true )
|
||||
public static int getItemLimit( IItemHandler inventory, int slot ) throws LuaException
|
||||
{
|
||||
assertBetween( slot, 1, inventory.getSlots(), "Slot out of range (%s)" );
|
||||
return inventory.getSlotLimit( slot );
|
||||
}
|
||||
|
||||
/**
|
||||
* Push items from one inventory to another connected one.
|
||||
*
|
||||
|
@ -20,6 +20,7 @@ import net.minecraft.tileentity.TileEntityType;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
import net.minecraftforge.fml.RegistryObject;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@ -85,9 +86,14 @@ public class BlockMonitor extends BlockGeneric
|
||||
if( entity instanceof TileMonitor && !world.isClientSide )
|
||||
{
|
||||
TileMonitor monitor = (TileMonitor) entity;
|
||||
monitor.contractNeighbours();
|
||||
monitor.contract();
|
||||
monitor.expand();
|
||||
// Defer the block update if we're being placed by another TE. See #691
|
||||
if ( livingEntity == null || livingEntity instanceof FakePlayer )
|
||||
{
|
||||
monitor.updateNeighborsDeferred();
|
||||
return;
|
||||
}
|
||||
|
||||
monitor.updateNeighbors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ public class TileMonitor extends TileGeneric
|
||||
private LazyOptional<IPeripheral> peripheralCap;
|
||||
private final Set<IComputerAccess> computers = new HashSet<>();
|
||||
|
||||
private boolean needsUpdate = false;
|
||||
private boolean destroyed = false;
|
||||
private boolean visiting = false;
|
||||
|
||||
@ -148,6 +149,12 @@ public class TileMonitor extends TileGeneric
|
||||
@Override
|
||||
public void blockTick()
|
||||
{
|
||||
if ( needsUpdate )
|
||||
{
|
||||
needsUpdate = false;
|
||||
updateNeighbors();
|
||||
}
|
||||
|
||||
if( xIndex != 0 || yIndex != 0 || serverMonitor == null ) return;
|
||||
|
||||
serverMonitor.clearChanged();
|
||||
@ -526,6 +533,18 @@ public class TileMonitor extends TileGeneric
|
||||
return true;
|
||||
}
|
||||
|
||||
void updateNeighborsDeferred()
|
||||
{
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
void updateNeighbors()
|
||||
{
|
||||
contractNeighbours();
|
||||
contract();
|
||||
expand();
|
||||
}
|
||||
|
||||
@SuppressWarnings( "StatementWithEmptyBody" )
|
||||
void expand()
|
||||
{
|
||||
|
@ -23,6 +23,9 @@ import dan200.computercraft.shared.data.HasComputerIdLootCondition;
|
||||
import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
|
||||
import dan200.computercraft.shared.media.items.RecordMedia;
|
||||
import dan200.computercraft.shared.network.NetworkHandler;
|
||||
import dan200.computercraft.shared.peripheral.generic.methods.EnergyMethods;
|
||||
import dan200.computercraft.shared.peripheral.generic.methods.FluidMethods;
|
||||
import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods;
|
||||
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
|
||||
import dan200.computercraft.shared.util.NullStorage;
|
||||
import net.minecraft.inventory.container.Container;
|
||||
@ -32,15 +35,19 @@ import net.minecraft.loot.*;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.energy.CapabilityEnergy;
|
||||
import net.minecraftforge.event.LootTableLoadEvent;
|
||||
import net.minecraftforge.event.RegisterCommandsEvent;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
import net.minecraftforge.fml.DeferredWorkQueue;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.fml.event.server.FMLServerStartedEvent;
|
||||
import net.minecraftforge.fml.event.server.FMLServerStoppedEvent;
|
||||
import net.minecraftforge.items.CapabilityItemHandler;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
@ -55,11 +62,15 @@ public final class ComputerCraftProxyCommon
|
||||
{
|
||||
NetworkHandler.setup();
|
||||
|
||||
net.minecraftforge.fml.DeferredWorkQueue.runLater( () -> {
|
||||
DeferredWorkQueue.runLater( () -> {
|
||||
registerProviders();
|
||||
ArgumentSerializers.register();
|
||||
registerLoot();
|
||||
} );
|
||||
|
||||
ComputerCraftAPI.registerGenericSource( new InventoryMethods() );
|
||||
ComputerCraftAPI.registerGenericSource( new FluidMethods() );
|
||||
ComputerCraftAPI.registerGenericSource( new EnergyMethods() );
|
||||
}
|
||||
|
||||
public static void registerLoot()
|
||||
@ -90,6 +101,12 @@ public final class ComputerCraftProxyCommon
|
||||
// Register capabilities
|
||||
CapabilityManager.INSTANCE.register( IWiredElement.class, new NullStorage<>(), () -> null );
|
||||
CapabilityManager.INSTANCE.register( IPeripheral.class, new NullStorage<>(), () -> null );
|
||||
|
||||
// Register generic capabilities. This can technically be done off-thread, but we need it to happen
|
||||
// after Forge's common setup, so this is easiest.
|
||||
ComputerCraftAPI.registerGenericCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY );
|
||||
ComputerCraftAPI.registerGenericCapability( CapabilityEnergy.ENERGY );
|
||||
ComputerCraftAPI.registerGenericCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY );
|
||||
}
|
||||
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID )
|
||||
|
@ -16,6 +16,7 @@ import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
import dan200.computercraft.core.asm.TaskCallback;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
import dan200.computercraft.shared.peripheral.generic.data.ItemData;
|
||||
import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods;
|
||||
import dan200.computercraft.shared.turtle.core.*;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
@ -660,6 +661,18 @@ public class TurtleAPI implements ILuaAPI
|
||||
* @return The turtle command result.
|
||||
* @cc.treturn boolean Whether there is a block in front of the turtle.
|
||||
* @cc.treturn table|string Information about the block in front, or a message explaining that there is no block.
|
||||
* @cc.usage <pre>{@code
|
||||
* local has_block, data = turtle.inspect()
|
||||
* if has_block then
|
||||
* print(textutils.serialize(data))
|
||||
* -- {
|
||||
* -- name = "minecraft:oak_log",
|
||||
* -- state = { axis = "x" },
|
||||
* -- tags = { ["minecraft:logs"] = true, ... },
|
||||
* -- }
|
||||
* else
|
||||
* print("No block in front of the turtle")
|
||||
* end}</pre>
|
||||
*/
|
||||
@LuaFunction
|
||||
public final MethodResult inspect()
|
||||
@ -702,6 +715,7 @@ public class TurtleAPI implements ILuaAPI
|
||||
* more information about the item at the cost of taking longer to run.
|
||||
* @return The command result.
|
||||
* @throws LuaException If the slot is out of range.
|
||||
* @see InventoryMethods#getItemDetail Describes the information returned by a detailed query.
|
||||
* @cc.treturn nil|table Information about the given slot, or {@code nil} if it is empty.
|
||||
* @cc.usage Print the current slot, assuming it contains 13 dirt.
|
||||
*
|
||||
|
@ -5,9 +5,11 @@
|
||||
*/
|
||||
package dan200.computercraft.shared.util;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.inventory.ISidedInventory;
|
||||
import net.minecraft.inventory.ISidedInventoryProvider;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.Direction;
|
||||
@ -23,6 +25,7 @@ import net.minecraftforge.items.wrapper.SidedInvWrapper;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class InventoryUtil
|
||||
{
|
||||
@ -41,7 +44,8 @@ public final class InventoryUtil
|
||||
|
||||
// Methods for finding inventories:
|
||||
|
||||
public static IItemHandler getInventory( World world, BlockPos pos, Direction side )
|
||||
@Nullable
|
||||
public static IItemHandler getInventory( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side )
|
||||
{
|
||||
// Look for tile with inventory
|
||||
TileEntity tileEntity = world.getBlockEntity( pos );
|
||||
@ -52,7 +56,7 @@ public final class InventoryUtil
|
||||
{
|
||||
return itemHandler.orElseThrow( NullPointerException::new );
|
||||
}
|
||||
else if( side != null && tileEntity instanceof ISidedInventory )
|
||||
else if( tileEntity instanceof ISidedInventory )
|
||||
{
|
||||
return new SidedInvWrapper( (ISidedInventory) tileEntity, side );
|
||||
}
|
||||
@ -62,6 +66,13 @@ public final class InventoryUtil
|
||||
}
|
||||
}
|
||||
|
||||
BlockState block = world.getBlockState( pos );
|
||||
if( block.getBlock() instanceof ISidedInventoryProvider )
|
||||
{
|
||||
ISidedInventory inventory = ((ISidedInventoryProvider) block.getBlock()).getContainer( block, world, pos );
|
||||
return new SidedInvWrapper( inventory, side );
|
||||
}
|
||||
|
||||
// Look for entity with inventory
|
||||
Vector3d vecStart = new Vector3d(
|
||||
pos.getX() + 0.5 + 0.6 * side.getStepX(),
|
||||
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.util;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
public final class ServiceUtil
|
||||
{
|
||||
private static final Type AUTO_SERVICE = Type.getType( "Lcom/google/auto/service/AutoService;" );
|
||||
|
||||
private ServiceUtil()
|
||||
{
|
||||
}
|
||||
|
||||
public static <T> Stream<T> loadServices( Class<T> target )
|
||||
{
|
||||
return StreamSupport.stream( ServiceLoader.load( target, ServiceUtil.class.getClassLoader() ).spliterator(), false );
|
||||
}
|
||||
|
||||
public static <T> Stream<T> loadServicesForge( Class<T> target )
|
||||
{
|
||||
Type type = Type.getType( target );
|
||||
ClassLoader loader = ComputerCraftAPI.class.getClassLoader();
|
||||
return ModList.get().getAllScanData().stream()
|
||||
.flatMap( x -> x.getAnnotations().stream() )
|
||||
.filter( x -> x.getAnnotationType().equals( AUTO_SERVICE ) )
|
||||
.filter( x -> {
|
||||
Object value = x.getAnnotationData().get( "value" );
|
||||
return value instanceof List<?> && ((List<?>) value).contains( type );
|
||||
} )
|
||||
.flatMap( x -> {
|
||||
try
|
||||
{
|
||||
Class<?> klass = loader.loadClass( x.getClassType().getClassName() );
|
||||
if( !target.isAssignableFrom( klass ) )
|
||||
{
|
||||
ComputerCraft.log.error( "{} is not a subtype of {}", x.getClassType().getClassName(), target.getName() );
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
Class<? extends T> casted = klass.asSubclass( target );
|
||||
return Stream.of( casted.newInstance() );
|
||||
}
|
||||
catch( ReflectiveOperationException e )
|
||||
{
|
||||
ComputerCraft.log.error( "Cannot load {}", x.getClassType(), e );
|
||||
return Stream.empty();
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 456 B After Width: | Height: | Size: 398 B |
Before Width: | Height: | Size: 534 B After Width: | Height: | Size: 476 B |
Before Width: | Height: | Size: 475 B After Width: | Height: | Size: 417 B |
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 526 B |
Before Width: | Height: | Size: 575 B After Width: | Height: | Size: 517 B |
Before Width: | Height: | Size: 503 B After Width: | Height: | Size: 404 B |
Before Width: | Height: | Size: 633 B After Width: | Height: | Size: 534 B |
Before Width: | Height: | Size: 525 B After Width: | Height: | Size: 426 B |
Before Width: | Height: | Size: 641 B After Width: | Height: | Size: 560 B |
Before Width: | Height: | Size: 635 B After Width: | Height: | Size: 554 B |
Before Width: | Height: | Size: 421 B After Width: | Height: | Size: 193 B |
Before Width: | Height: | Size: 501 B After Width: | Height: | Size: 214 B |
Before Width: | Height: | Size: 439 B After Width: | Height: | Size: 199 B |
Before Width: | Height: | Size: 327 B After Width: | Height: | Size: 217 B |
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 195 B |
Before Width: | Height: | Size: 327 B After Width: | Height: | Size: 218 B |
Before Width: | Height: | Size: 327 B After Width: | Height: | Size: 217 B |
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 195 B |
Before Width: | Height: | Size: 546 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 548 B After Width: | Height: | Size: 490 B |
Before Width: | Height: | Size: 416 B After Width: | Height: | Size: 358 B |
Before Width: | Height: | Size: 418 B After Width: | Height: | Size: 360 B |
Before Width: | Height: | Size: 342 B After Width: | Height: | Size: 284 B |
Before Width: | Height: | Size: 263 B After Width: | Height: | Size: 205 B |
Before Width: | Height: | Size: 353 B After Width: | Height: | Size: 295 B |
Before Width: | Height: | Size: 508 B After Width: | Height: | Size: 450 B |
Before Width: | Height: | Size: 365 B After Width: | Height: | Size: 307 B |
Before Width: | Height: | Size: 306 B After Width: | Height: | Size: 248 B |
Before Width: | Height: | Size: 357 B After Width: | Height: | Size: 299 B |
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 224 B |
Before Width: | Height: | Size: 194 B After Width: | Height: | Size: 136 B |
Before Width: | Height: | Size: 285 B After Width: | Height: | Size: 227 B |
Before Width: | Height: | Size: 234 B After Width: | Height: | Size: 176 B |
Before Width: | Height: | Size: 227 B After Width: | Height: | Size: 169 B |
Before Width: | Height: | Size: 294 B After Width: | Height: | Size: 236 B |
Before Width: | Height: | Size: 518 B After Width: | Height: | Size: 460 B |
Before Width: | Height: | Size: 210 B After Width: | Height: | Size: 152 B |
Before Width: | Height: | Size: 292 B After Width: | Height: | Size: 234 B |
Before Width: | Height: | Size: 580 B After Width: | Height: | Size: 522 B |
Before Width: | Height: | Size: 553 B After Width: | Height: | Size: 495 B |
Before Width: | Height: | Size: 542 B After Width: | Height: | Size: 484 B |
Before Width: | Height: | Size: 571 B After Width: | Height: | Size: 513 B |
Before Width: | Height: | Size: 569 B After Width: | Height: | Size: 511 B |
Before Width: | Height: | Size: 545 B After Width: | Height: | Size: 487 B |
Before Width: | Height: | Size: 566 B After Width: | Height: | Size: 508 B |
Before Width: | Height: | Size: 535 B After Width: | Height: | Size: 477 B |
Before Width: | Height: | Size: 580 B After Width: | Height: | Size: 522 B |
Before Width: | Height: | Size: 522 B After Width: | Height: | Size: 464 B |
Before Width: | Height: | Size: 545 B After Width: | Height: | Size: 487 B |
Before Width: | Height: | Size: 556 B After Width: | Height: | Size: 498 B |
Before Width: | Height: | Size: 503 B After Width: | Height: | Size: 445 B |
Before Width: | Height: | Size: 527 B After Width: | Height: | Size: 469 B |
Before Width: | Height: | Size: 563 B After Width: | Height: | Size: 505 B |
Before Width: | Height: | Size: 524 B After Width: | Height: | Size: 466 B |
Before Width: | Height: | Size: 540 B After Width: | Height: | Size: 482 B |
Before Width: | Height: | Size: 566 B After Width: | Height: | Size: 508 B |
Before Width: | Height: | Size: 545 B After Width: | Height: | Size: 487 B |
Before Width: | Height: | Size: 569 B After Width: | Height: | Size: 511 B |
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 195 B |
Before Width: | Height: | Size: 278 B After Width: | Height: | Size: 186 B |
Before Width: | Height: | Size: 237 B After Width: | Height: | Size: 182 B |
Before Width: | Height: | Size: 267 B After Width: | Height: | Size: 193 B |
Before Width: | Height: | Size: 248 B After Width: | Height: | Size: 175 B |