mirror of
https://github.com/Jikoo/OpenInv.git
synced 2026-11-04 08:42:05 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8df83b31d | ||
|
|
eb98bd79d3 | ||
|
|
e948fdc011 | ||
|
|
f5c31c32d3 | ||
|
|
56f8cf4d71 | ||
|
|
7f1f6ccc82 | ||
|
|
9196ab3e61 | ||
|
|
0ac7575bad | ||
|
|
b0c7c8421e | ||
|
|
9b54e4e22e | ||
|
|
5ac09bc5a3 | ||
|
|
4e89a04b36 | ||
|
|
73a9c743a6 | ||
|
|
8d897664ef | ||
|
|
03ea798ee0 | ||
|
|
08d4a0ea27 | ||
|
|
cd7219f472 | ||
|
|
532dffc068 | ||
|
|
6df4de1dd3 | ||
|
|
df913e33c3 | ||
|
|
21f6b005cd | ||
|
|
d2ce3a542a | ||
|
|
2c51423882 | ||
|
|
b8636cd385 | ||
|
|
46b8b0c928 | ||
|
|
8229d5f0d9 | ||
|
|
c6d36cc9cf | ||
|
|
54f078724f | ||
|
|
847f22f63f | ||
|
|
f121c113f0 | ||
|
|
8d60cd0726 | ||
|
|
7b504971b1 | ||
|
|
c1a20a4e24 | ||
|
|
5d75640cee | ||
|
|
cace6f6726 | ||
|
|
1967f4ebb1 | ||
|
|
fbdda9637e | ||
|
|
336aee88e2 | ||
|
|
d50b7433c7 | ||
|
|
e64261669b | ||
|
|
e8ab490d95 | ||
|
|
5eaf6243fb | ||
|
|
5e2cf56d6f | ||
|
|
0f0bde3e49 | ||
|
|
4198b7f22a | ||
|
|
8c15b3d078 | ||
|
|
335d849525 | ||
|
|
0fb9e5eed3 | ||
|
|
fe11217a27 | ||
|
|
5fea7f3e02 | ||
|
|
dbceff086a | ||
|
|
64899e5c3d | ||
|
|
bca080be7c | ||
|
|
09842fd168 | ||
|
|
7d63500c79 | ||
|
|
7fbb9a9da6 | ||
|
|
0597d7d9db | ||
|
|
1f2c2b6a49 | ||
|
|
59fff35fcc | ||
|
|
bb80d93510 | ||
|
|
8bf63e7f12 | ||
|
|
ebc063d34d | ||
|
|
b895cb400b | ||
|
|
1cfa9bdd81 | ||
|
|
e5b4e81ff9 | ||
|
|
e39dbc0044 | ||
|
|
09b3f83816 | ||
|
|
7cca23f7b1 | ||
|
|
dfdef394ce | ||
|
|
6325b353dc | ||
|
|
7a07ee0b3d | ||
|
|
e97ac3db88 | ||
|
|
f72b6749c4 | ||
|
|
f1bcb496cf | ||
|
|
89beeca853 | ||
|
|
282f8630ab | ||
|
|
8f374ef2a5 | ||
|
|
3d81fdd0ca | ||
|
|
2dde9ba28f | ||
|
|
96d2d2482a | ||
|
|
e543053eff | ||
|
|
8134887666 | ||
|
|
7ee6206ffb | ||
|
|
b81eab8191 | ||
|
|
8b71d3549f | ||
|
|
dd82f59516 | ||
|
|
80d3fb72e6 | ||
|
|
2ed23b24e5 | ||
|
|
b6d61d4cc9 | ||
|
|
c3f7eaebf4 | ||
|
|
52c90e1cea | ||
|
|
d887ff8eae | ||
|
|
0d5fb6a0ef | ||
|
|
6f8a6e868a | ||
|
|
70c1ecfc09 | ||
|
|
176272a4b9 | ||
|
|
c102e6650e | ||
|
|
c8fee82291 | ||
|
|
0ae9982ce5 | ||
|
|
59d358d907 | ||
|
|
2621181c5d | ||
|
|
48d044af8c | ||
|
|
9a7623c209 | ||
|
|
78354fdf68 | ||
|
|
859179d1fc | ||
|
|
79f53e9086 | ||
|
|
c14d777519 | ||
|
|
1829babe69 | ||
|
|
5aa959f1ac | ||
|
|
bc7e13f245 | ||
|
|
73edebff54 | ||
|
|
faf1d387e9 | ||
|
|
deed277376 | ||
|
|
28fc5aea70 | ||
|
|
bfcf0233fa | ||
|
|
3cf1aae89b | ||
|
|
db62878681 | ||
|
|
21cd52c166 | ||
|
|
9d715b61c0 | ||
|
|
d636cdd839 | ||
|
|
1bebdb5602 | ||
|
|
57caf85500 | ||
|
|
922586c925 | ||
|
|
9ec7bebecb |
No files matched your search
@@ -15,7 +15,7 @@ jobs:
|
||||
# Note: this is directly from GitHub's example for using data from a triggering workflow:
|
||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow
|
||||
- name: 'Download artifact'
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
@@ -42,12 +42,12 @@ jobs:
|
||||
echo "PR_NUMBER=$(cat pr_number)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Approve
|
||||
uses: hmarr/auto-approve-action@v3.2.1
|
||||
uses: hmarr/auto-approve-action@v4.0.0
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
pull-request-number: "${{ env.PR_NUMBER }}"
|
||||
- name: Merge
|
||||
uses: pascalgn/automerge-action@v0.15.6
|
||||
uses: pascalgn/automerge-action@v0.16.4
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
MERGE_LABELS: "dependencies,java"
|
||||
|
||||
@@ -3,9 +3,11 @@ name: OpenInv CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
- 'master'
|
||||
tags-ignore:
|
||||
- '**'
|
||||
paths-ignore:
|
||||
- resource-pack/**
|
||||
# Enable running CI via other Actions, i.e. for drafting releases and handling PRs.
|
||||
workflow_call:
|
||||
|
||||
@@ -13,12 +15,12 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
java-version: '21'
|
||||
cache: 'maven'
|
||||
|
||||
# Install Spigot dependencies if necessary.
|
||||
@@ -31,13 +33,13 @@ jobs:
|
||||
# Upload artifacts
|
||||
- name: Upload Distributable Jar
|
||||
id: upload-final
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: ./target/OpenInv.jar
|
||||
path: ./target/*.jar
|
||||
- name: Upload API Jar
|
||||
id: upload-api
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: api
|
||||
path: ./api/target/openinvapi*.jar
|
||||
@@ -13,22 +13,28 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Fetch all history - used to assemble changelog.
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set Release Variables
|
||||
run: bash ./scripts/set_release_env.sh
|
||||
|
||||
- name: Download Artifact
|
||||
uses: actions/download-artifact@v3
|
||||
- name: Download build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: dist
|
||||
|
||||
- name: Package resource pack
|
||||
run: |-
|
||||
pushd resource-pack
|
||||
zip -r ../dist/openinv-legibility-pack.zip .
|
||||
popd
|
||||
|
||||
- name: Create Release
|
||||
id: create-release
|
||||
uses: softprops/action-gh-release@v0.1.15
|
||||
uses: softprops/action-gh-release@v2.2.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
@@ -36,4 +42,4 @@ jobs:
|
||||
body: ${{ env.GENERATED_CHANGELOG }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
files: ./dist/OpenInv.jar
|
||||
files: ./dist/**
|
||||
@@ -9,12 +9,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Fetch Github Release Asset
|
||||
uses: dsaltares/fetch-gh-release-asset@1.1.1
|
||||
uses: dsaltares/fetch-gh-release-asset@1.1.2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: ${{ github.event.release.id }}
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
run: |
|
||||
mkdir -p ./pr
|
||||
echo ${{ github.event.number }} > ./pr/pr_number
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pr_number
|
||||
path: pr/
|
||||
@@ -0,0 +1,24 @@
|
||||
name: Resource Pack CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
tags-ignore:
|
||||
- '**'
|
||||
paths:
|
||||
- resource-pack/**
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build resource pack
|
||||
id: upload-resource-pack
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: openinv-legibility-pack
|
||||
path: ./resource-pack/
|
||||
compression-level: 9
|
||||
@@ -1,16 +1,17 @@
|
||||
## About
|
||||
OpenInv is a [Bukkit plugin](https://dev.bukkit.org/bukkit-plugins/openinv/) which allows users to open and edit anyone's inventory or ender chest - online or not!
|
||||
OpenInv is a [Bukkit plugin](https://dev.bukkit.org/projects/openinv) which allows users to open and edit anyone's inventory or ender chest - online or not!
|
||||
|
||||
## Features
|
||||
- **OpenInv**: Open anyone's inventory, even if they're offline.
|
||||
- Read-only mode! No edits allowed! Don't grant the permission `OpenInv.editinv`
|
||||
- Cross-world support! Don't grant `OpenInv.crossworld`
|
||||
- No self-opening! Don't grant `OpenInv.openself`
|
||||
- Drop items as the player! Place items in the unused slots to the right of the armor to drop them
|
||||
- Read-only mode! Don't grant edit permission.
|
||||
- Cross-world support! Allow access only from the same world.
|
||||
- No duplicate slots! Only armor is accessible when opening self (if allowed at all)!
|
||||
- Drop items as the player! Place items in the dropper slot in the bottom right. Can be disabled via permission!
|
||||
- Allow any item in armor slots! Configurable via permission.
|
||||
- **OpenEnder**: Open anyone's ender chest, even if they're offline.
|
||||
- Read-only mode! No edits allowed! Don't grant `OpenInv.editender`
|
||||
- Cross-world support! Don't grant `OpenInv.crossworld`
|
||||
- No opening others! Don't grant `OpenInv.openenderall`
|
||||
- Allow access only to own ender chest! Don't grant permission to open others.
|
||||
- Read-only mode! Don't grant edit permission.
|
||||
- Cross-world support! Allow access only from the same world.
|
||||
- **SilentContainer**: Open containers without displaying an animation or making sound.
|
||||
- **AnyContainer**: Open containers, even if blocked by ocelots or blocks.
|
||||
|
||||
@@ -18,88 +19,7 @@ OpenInv is a [Bukkit plugin](https://dev.bukkit.org/bukkit-plugins/openinv/) whi
|
||||
See [the wiki](https://github.com/Jikoo/OpenInv/wiki/Commands).
|
||||
|
||||
## Permissions
|
||||
<table>
|
||||
<tr>
|
||||
<th>Node</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.*</td>
|
||||
<td>Gives permission to use all of OpenInv.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.openinv</td>
|
||||
<td>Required to use /openinv.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.openself</td>
|
||||
<td>Required to open own inventory.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.editinv</td>
|
||||
<td>Required to make changes to open inventories.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.openonline</td>
|
||||
<td>Allows users to open online players' inventories. For compatibility reasons this is granted by the nodes OpenInv.openinv and OpenInv.openender.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.openoffline</td>
|
||||
<td>Allows users to open offline players' inventories. For compatibility reasons this is granted by the nodes OpenInv.openinv and OpenInv.openender.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.openender</td>
|
||||
<td>Required to use /openender.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.editender</td>
|
||||
<td>Required to make changes to open ender chests.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.openenderall</td>
|
||||
<td>Allows users to open others' ender chests. Without it, users can only open their own.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.exempt</td>
|
||||
<td>Prevents the player's inventory being opened by others.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.override</td>
|
||||
<td>Allows bypassing of the exempt permission.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.crossworld</td>
|
||||
<td>Allows cross-world usage of /openinv and /openender.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.search</td>
|
||||
<td>Required to use /searchinv and /searchender.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.searchenchant</td>
|
||||
<td>Required to use /searchenchant.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.anychest</td>
|
||||
<td>Required to use /anychest.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.any.default</td>
|
||||
<td>Cause AnyContainer to be enabled by default.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.silent</td>
|
||||
<td>Required to use /silentcontainer.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.silent.default</td>
|
||||
<td>Cause SilentContainer to be enabled by default.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.spectate</td>
|
||||
<td>Allows users in spectate gamemode to edit inventories.</td>
|
||||
</tr>
|
||||
</table>
|
||||
See [the wiki](https://github.com/Jikoo/OpenInv/wiki/Permissions)
|
||||
|
||||
## For Developers
|
||||
|
||||
@@ -129,11 +49,9 @@ As OpenInv is compiled against Mojang's mappings, you must run BuildTools with t
|
||||
`java -jar BuildTools.jar --remapped --rev $serverVersion`
|
||||
`$serverVersion` is the version of the server, i.e. `1.18.1`
|
||||
|
||||
To compile for a single version, specify the module you are targeting:
|
||||
`mvn -pl $moduleName -am clean install`
|
||||
`$moduleName` is the name of the module, i.e. `internal/v1_18_R1`.
|
||||
To compile just the API, execute Maven as usual:
|
||||
`mvn clean package`
|
||||
|
||||
To compile for a set of versions, use a profile. Select a profile using the `-P` argument:
|
||||
To compile the full plugin, use the provided profile with `-P all`:
|
||||
`mvn clean package -am -P all`
|
||||
The only provided profile is `all`. The final file is `target/OpenInv.jar`
|
||||
For more information, check out the [official Maven guide](http://maven.apache.org/guides/introduction/introduction-to-profiles.html).
|
||||
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
<version>5.1.5</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvtogglepersist</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<artifactId>annotations</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>OITogglePersist</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-final-jar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.parent.build.directory}</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<includes>
|
||||
<include>${project.build.finalName}.jar</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
package com.github.jikoo.openinv.togglepersist;
|
||||
|
||||
import com.google.errorprone.annotations.Keep;
|
||||
import com.lishid.openinv.event.PlayerToggledEvent;
|
||||
import com.lishid.openinv.util.setting.PlayerToggle;
|
||||
import com.lishid.openinv.util.setting.PlayerToggles;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class TogglePersist extends JavaPlugin implements Listener {
|
||||
|
||||
private final Map<UUID, Set<String>> enabledToggles = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
getServer().getPluginManager().registerEvents(this, this);
|
||||
|
||||
File file = new File(getDataFolder(), "toggles.yml");
|
||||
|
||||
// If there's no save file, there's nothing to load.
|
||||
if (!file.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Configuration loaded = YamlConfiguration.loadConfiguration(file);
|
||||
|
||||
// For each toggle, enable loaded players.
|
||||
for (String toggleName : loaded.getKeys(false)) {
|
||||
PlayerToggle toggle = PlayerToggles.get(toggleName);
|
||||
// Ensure toggle exists.
|
||||
if (toggle == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (String idString : loaded.getStringList(toggleName)) {
|
||||
// Ensure valid UUID.
|
||||
UUID uuid;
|
||||
try {
|
||||
uuid = UUID.fromString(idString);
|
||||
} catch (IllegalArgumentException e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Track that toggle is enabled.
|
||||
set(uuid, toggleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void set(UUID playerId, String toggleName) {
|
||||
enabledToggles.compute(playerId, (uuid, toggles) -> {
|
||||
if (toggles == null) {
|
||||
toggles = new HashSet<>();
|
||||
}
|
||||
toggles.add(toggleName);
|
||||
return toggles;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
Map<String, List<String>> converted = getSaveData();
|
||||
|
||||
YamlConfiguration data = new YamlConfiguration();
|
||||
for (Map.Entry<String, List<String>> playerToggle : converted.entrySet()) {
|
||||
data.set(playerToggle.getKey(), playerToggle.getValue());
|
||||
}
|
||||
|
||||
File file = new File(getDataFolder(), "toggles.yml");
|
||||
try {
|
||||
data.save(file);
|
||||
} catch (IOException e) {
|
||||
getLogger().log(Level.SEVERE, "Unable to save player toggle states", e);
|
||||
}
|
||||
}
|
||||
|
||||
private @NotNull Map<String, List<String>> getSaveData() {
|
||||
Map<String, List<String>> converted = new HashMap<>();
|
||||
|
||||
for (Map.Entry<UUID, Set<String>> playerToggles : enabledToggles.entrySet()) {
|
||||
String idString = playerToggles.getKey().toString();
|
||||
for (String toggleName : playerToggles.getValue()) {
|
||||
// Add player ID to listing for each enabled toggle.
|
||||
converted.compute(toggleName, (name, ids) -> {
|
||||
if (ids == null) {
|
||||
ids = new ArrayList<>();
|
||||
}
|
||||
ids.add(idString);
|
||||
return ids;
|
||||
});
|
||||
}
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
@Keep
|
||||
@EventHandler
|
||||
private void onPlayerJoin(@NotNull PlayerJoinEvent event) {
|
||||
UUID playerId = event.getPlayer().getUniqueId();
|
||||
Set<String> toggleNames = enabledToggles.get(playerId);
|
||||
|
||||
if (toggleNames == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String toggleName : toggleNames) {
|
||||
PlayerToggle toggle = PlayerToggles.get(toggleName);
|
||||
if (toggle != null) {
|
||||
toggle.set(playerId, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Keep
|
||||
@EventHandler
|
||||
private void onToggleSet(@NotNull PlayerToggledEvent event) {
|
||||
if (event.isEnabled()) {
|
||||
set(event.getPlayerId(), event.getToggle().getName());
|
||||
} else {
|
||||
enabledToggles.computeIfPresent(event.getPlayerId(), (uuid, toggles) -> {
|
||||
toggles.remove(event.getToggle().getName());
|
||||
return toggles.isEmpty() ? null : toggles;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
name: OITogglePersist
|
||||
main: com.github.jikoo.openinv.togglepersist.TogglePersist
|
||||
version: ${project.version}
|
||||
author: Jikoo
|
||||
description: An OpenInv addon allowing /anycontainer and /silentcontainer to persist across sessions.
|
||||
api-version: "1.20"
|
||||
depend: [ OpenInv ]
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<version>4.3.1</version>
|
||||
<version>5.1.5</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvapi</artifactId>
|
||||
|
||||
@@ -17,21 +17,19 @@
|
||||
package com.lishid.openinv;
|
||||
|
||||
import com.lishid.openinv.internal.IAnySilentContainer;
|
||||
import com.lishid.openinv.internal.IInventoryAccess;
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.util.InventoryAccess;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Interface defining behavior for the OpenInv plugin.
|
||||
*/
|
||||
@@ -80,22 +78,6 @@ public interface IOpenInv {
|
||||
*/
|
||||
@NotNull IAnySilentContainer getAnySilentContainer();
|
||||
|
||||
/**
|
||||
* @deprecated Use static {@link InventoryAccess} methods.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
default @NotNull IInventoryAccess getInventoryAccess() {
|
||||
return new InventoryAccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getAnyContainerStatus(OfflinePlayer)}. Not all containers are chests.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "4.2.0")
|
||||
default boolean getPlayerAnyChestStatus(@NotNull OfflinePlayer offline) {
|
||||
return getAnyContainerStatus(offline);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether a user has AnyContainer mode enabled.
|
||||
*
|
||||
@@ -104,14 +86,6 @@ public interface IOpenInv {
|
||||
*/
|
||||
boolean getAnyContainerStatus(@NotNull OfflinePlayer offline);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #setAnyContainerStatus(OfflinePlayer, boolean)}. Not all containers are chests.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "4.2.0")
|
||||
default void setPlayerAnyChestStatus(@NotNull OfflinePlayer offline, boolean status) {
|
||||
setAnyContainerStatus(offline, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether a user has AnyContainer mode enabled.
|
||||
*
|
||||
@@ -120,14 +94,6 @@ public interface IOpenInv {
|
||||
*/
|
||||
void setAnyContainerStatus(@NotNull OfflinePlayer offline, boolean status);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getSilentContainerStatus(OfflinePlayer)}. Not all containers are chests.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "4.2.0")
|
||||
default boolean getPlayerSilentChestStatus(@NotNull OfflinePlayer offline) {
|
||||
return getSilentContainerStatus(offline);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether a user has SilentContainer mode enabled.
|
||||
*
|
||||
@@ -136,14 +102,6 @@ public interface IOpenInv {
|
||||
*/
|
||||
boolean getSilentContainerStatus(@NotNull OfflinePlayer offline);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #setSilentContainerStatus(OfflinePlayer, boolean)}. Not all containers are chests.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "4.2.0")
|
||||
default void setPlayerSilentChestStatus(@NotNull OfflinePlayer offline, boolean status) {
|
||||
setSilentContainerStatus(offline, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether a user has SilentContainer mode enabled.
|
||||
*
|
||||
@@ -152,20 +110,6 @@ public interface IOpenInv {
|
||||
*/
|
||||
void setSilentContainerStatus(@NotNull OfflinePlayer offline, boolean status);
|
||||
|
||||
/**
|
||||
* Get a unique identifier by which the OfflinePlayer can be referenced.
|
||||
*
|
||||
* @deprecated Use {@link OfflinePlayer#getUniqueId()} and {@link UUID#toString()}. This was necessary for non-UUID
|
||||
* versions of Minecraft, but support for them has been dropped for years.
|
||||
* @param offline the OfflinePlayer
|
||||
* @return the identifier
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
default @NotNull String getPlayerID(@NotNull OfflinePlayer offline) {
|
||||
return offline.getUniqueId().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an {@link ISpecialEnderChest} for a user.
|
||||
*
|
||||
@@ -229,55 +173,6 @@ public interface IOpenInv {
|
||||
*/
|
||||
@Nullable OfflinePlayer matchPlayer(@NotNull String name);
|
||||
|
||||
/**
|
||||
* @deprecated OpenInv uses action bar chat for notifications. Whether they show is based on language settings.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
default boolean notifyAnyChest() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated OpenInv uses action bar chat for notifications. Whether they show is based on language settings.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
default boolean notifySilentChest() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated see {@link #retainPlayer(Player, Plugin)}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "4.2.0")
|
||||
default void releasePlayer(@NotNull Player player, @NotNull Plugin plugin) {}
|
||||
|
||||
/**
|
||||
* @deprecated OpenInv no longer uses an internal cache beyond maintaining copies of currently open inventories.
|
||||
* If you wish to use/modify a player, ensure either {@link IOpenInv#isPlayerLoaded(UUID)} is false or the player
|
||||
* instance is the same memory address as the one in use by OpenInv.
|
||||
* <pre>
|
||||
* public @NotNull Player savePlayerData(@NotNull Player player) {
|
||||
* IOpenInv openInv = ...
|
||||
* if (!openInv.disableSaving() && openInv.isPlayerLoaded(player.getUniqueId())) {
|
||||
* Player openInvLoadedPlayer = openInv.loadPlayer(myInUsePlayer);
|
||||
* if (openInvLoadedPlayer != player) {
|
||||
* // The copy loaded by OpenInv is not the same as our loaded copy. Push our changes.
|
||||
* copyPlayerModifications(player, openInvLoadedPlayer);
|
||||
* }
|
||||
* // OpenInv will handle saving data when the player is unloaded.
|
||||
* // Optionally, to be sure our changes will persist, save now.
|
||||
* // openInvLoadedPlayer.saveData();
|
||||
* return openInvLoadedPlayer;
|
||||
* }
|
||||
*
|
||||
* player.saveData();
|
||||
* return player;
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "4.2.0")
|
||||
default void retainPlayer(@NotNull Player player, @NotNull Plugin plugin) {}
|
||||
|
||||
/**
|
||||
* Forcibly close inventories of and unload any cached data for a user.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.lishid.openinv.event;
|
||||
|
||||
import com.google.errorprone.annotations.RestrictedApi;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Event fired before OpenInv saves a player's data when closing an {@link ISpecialInventory}.
|
||||
*/
|
||||
public class OpenPlayerSaveEvent extends PlayerSaveEvent {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
|
||||
private final ISpecialInventory inventory;
|
||||
|
||||
/**
|
||||
* Construct a new {@code OpenPlayerSaveEvent}.
|
||||
*
|
||||
* <p>The constructor is not considered part of the API, and may be subject to change.</p>
|
||||
*
|
||||
* @param player the player to be saved
|
||||
* @param inventory the {@link ISpecialInventory} being closed
|
||||
*/
|
||||
@RestrictedApi(
|
||||
explanation = "Constructor is not considered part of the API and may be subject to change.",
|
||||
link = "",
|
||||
allowedOnPath = ".*/com/lishid/openinv/event/OpenEvents.java")
|
||||
@ApiStatus.Internal
|
||||
OpenPlayerSaveEvent(@NotNull Player player, @NotNull ISpecialInventory inventory) {
|
||||
super(player);
|
||||
this.inventory = inventory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ISpecialInventory} that triggered the save by being closed.
|
||||
*
|
||||
* @return the special inventory
|
||||
*/
|
||||
public @NotNull ISpecialInventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return HANDLERS;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLERS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.lishid.openinv.event;
|
||||
|
||||
|
||||
import com.google.errorprone.annotations.RestrictedApi;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Event fired before a {@link Player} loaded via OpenInv is saved.
|
||||
*/
|
||||
public class PlayerSaveEvent extends PlayerEvent implements Cancellable {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
|
||||
private boolean cancelled = false;
|
||||
|
||||
/**
|
||||
* Construct a new {@code PlayerSaveEvent}.
|
||||
*
|
||||
* <p>The constructor is not considered part of the API, and may be subject to change.</p>
|
||||
*
|
||||
* @param player the player to be saved
|
||||
*/
|
||||
@RestrictedApi(
|
||||
explanation = "Constructor is not considered part of the API and may be subject to change.",
|
||||
link = "",
|
||||
allowedOnPath = ".*/com/lishid/openinv/event/(OpenPlayerSaveEvent|OpenEvents).java")
|
||||
@ApiStatus.Internal
|
||||
PlayerSaveEvent(@NotNull Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the event is cancelled.
|
||||
*
|
||||
* @return true if the event is cancelled
|
||||
*/
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the event is cancelled.
|
||||
*
|
||||
* @param cancel whether the event is cancelled
|
||||
*/
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return HANDLERS;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLERS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.lishid.openinv.event;
|
||||
|
||||
import com.google.errorprone.annotations.RestrictedApi;
|
||||
import com.lishid.openinv.util.setting.PlayerToggle;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Event fired after OpenInv modifies a toggleable setting for a player.
|
||||
*/
|
||||
public class PlayerToggledEvent extends Event {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
|
||||
private final @NotNull PlayerToggle toggle;
|
||||
private final @NotNull UUID uuid;
|
||||
private final boolean enabled;
|
||||
|
||||
@RestrictedApi(
|
||||
explanation = "Constructor is not considered part of the API and may be subject to change.",
|
||||
link = "",
|
||||
allowedOnPath = ".*/com/lishid/openinv/event/OpenEvents.java")
|
||||
@ApiStatus.Internal
|
||||
PlayerToggledEvent(@NotNull PlayerToggle toggle, @NotNull UUID uuid, boolean enabled) {
|
||||
this.toggle = toggle;
|
||||
this.uuid = uuid;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link PlayerToggle} affected.
|
||||
*
|
||||
* @return the toggle
|
||||
*/
|
||||
public @NotNull PlayerToggle getToggle() {
|
||||
return toggle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link UUID} of the player whose setting was changed.
|
||||
*
|
||||
* @return the player ID
|
||||
*/
|
||||
public @NotNull UUID getPlayerId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the toggle is enabled.
|
||||
*
|
||||
* @return true if the toggle is enabled
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return HANDLERS;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLERS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,14 +16,11 @@
|
||||
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
import org.bukkit.block.Barrel;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.EnderChest;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.Chest;
|
||||
import org.bukkit.block.data.Directional;
|
||||
import org.bukkit.entity.Cat;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
@@ -50,84 +47,32 @@ public interface IAnySilentContainer {
|
||||
*/
|
||||
void deactivateContainer(@NotNull Player player);
|
||||
|
||||
/**
|
||||
* @param player the player opening the container
|
||||
* @param block the {@link Block} of the container
|
||||
* @return true if the container is blocked
|
||||
* @deprecated use {@link #isAnyContainerNeeded(Block)}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "4.1.9")
|
||||
default boolean isAnyContainerNeeded(@NotNull Player player, @NotNull Block block) {
|
||||
return isAnyContainerNeeded(block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the container at the given coordinates is blocked.
|
||||
*
|
||||
* @param block the {@link Block} of the container
|
||||
* @return true if the container is blocked
|
||||
*/
|
||||
default boolean isAnyContainerNeeded(@NotNull Block block) {
|
||||
BlockState blockState = block.getState();
|
||||
|
||||
// Barrels do not require AnyContainer.
|
||||
if (blockState instanceof Barrel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enderchests require a non-occluding block on top to open.
|
||||
if (blockState instanceof EnderChest) {
|
||||
return block.getRelative(0, 1, 0).getType().isOccluding();
|
||||
}
|
||||
|
||||
// Shulker boxes require 1/2 a block clear in the direction they open.
|
||||
if (blockState instanceof ShulkerBox shulker) {
|
||||
if (isShulkerBlocked(shulker)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(blockState instanceof org.bukkit.block.Chest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isChestBlocked(block)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BlockData blockData = block.getBlockData();
|
||||
if (!(blockData instanceof Chest chest) || ((Chest) blockData).getType() == Chest.Type.SINGLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int ordinal = (chest.getFacing().ordinal() + 4 + (chest.getType() == Chest.Type.RIGHT ? -1 : 1)) % 4;
|
||||
BlockFace relativeFace = BlockFace.values()[ordinal];
|
||||
org.bukkit.block.Block relative = block.getRelative(relativeFace);
|
||||
|
||||
if (relative.getType() != block.getType()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockData relativeData = relative.getBlockData();
|
||||
if (!(relativeData instanceof Chest relativeChest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (relativeChest.getFacing() != chest.getFacing()
|
||||
|| relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isChestBlocked(relative);
|
||||
}
|
||||
boolean isAnyContainerNeeded(@NotNull Block block);
|
||||
|
||||
/**
|
||||
* Check if a {@link ShulkerBox} cannot be opened under ordinary circumstances.
|
||||
* Check if a shulker box block cannot be opened under ordinary circumstances.
|
||||
*
|
||||
* @param shulkerBox the shulker box container
|
||||
* @param shulkerBox the shulker box block
|
||||
* @return whether the container is blocked
|
||||
*/
|
||||
boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox);
|
||||
default boolean isShulkerBlocked(@NotNull Block shulkerBox) {
|
||||
Directional directional = (Directional) shulkerBox.getBlockData();
|
||||
BlockFace facing = directional.getFacing();
|
||||
// Construct a new 1-block bounding box at the origin.
|
||||
BoundingBox box = new BoundingBox(0, 0, 0, 1, 1, 1);
|
||||
// Expand the box in the direction the shulker will open.
|
||||
box.expand(facing, 0.5);
|
||||
// Move the box away from the origin by a block so only the expansion intersects with a box around the origin.
|
||||
box.shift(facing.getOppositeFace().getDirection());
|
||||
// Check if the relative block's collision shape (which will be at the origin) intersects with the expanded box.
|
||||
return shulkerBox.getRelative(facing).getCollisionShape().overlaps(box);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a chest cannot be opened under ordinary circumstances.
|
||||
@@ -138,7 +83,7 @@ public interface IAnySilentContainer {
|
||||
default boolean isChestBlocked(@NotNull Block chest) {
|
||||
org.bukkit.block.Block relative = chest.getRelative(0, 1, 0);
|
||||
return relative.getType().isOccluding()
|
||||
|| chest.getWorld().getNearbyEntities(BoundingBox.of(relative), entity -> entity instanceof Cat).size() > 0;
|
||||
|| !chest.getWorld().getNearbyEntities(BoundingBox.of(relative), Cat.class::isInstance).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,9 +92,7 @@ public interface IAnySilentContainer {
|
||||
* @param block the potential container
|
||||
* @return true if the type is a supported container
|
||||
*/
|
||||
default boolean isAnySilentContainer(@NotNull Block block) {
|
||||
return isAnySilentContainer(block.getState());
|
||||
}
|
||||
boolean isAnySilentContainer(@NotNull Block block);
|
||||
|
||||
/**
|
||||
* Check if the given {@link BlockState} is a container which can be unblocked or silenced.
|
||||
@@ -158,7 +101,8 @@ public interface IAnySilentContainer {
|
||||
* @return true if the type is a supported container
|
||||
*/
|
||||
default boolean isAnySilentContainer(@NotNull BlockState blockState) {
|
||||
return blockState instanceof InventoryHolder holder && isAnySilentContainer(holder);
|
||||
return (blockState instanceof InventoryHolder holder && isAnySilentContainer(holder))
|
||||
|| blockState instanceof EnderChest;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
import com.lishid.openinv.util.InventoryAccess;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @deprecated Use static {@link InventoryAccess} methods.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public interface IInventoryAccess {
|
||||
|
||||
/**
|
||||
* @deprecated Use static {@link InventoryAccess} methods.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
default @Nullable ISpecialEnderChest getSpecialEnderChest(@NotNull Inventory inventory) {
|
||||
return InventoryAccess.getEnderChest(inventory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use static {@link InventoryAccess} methods.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
default @Nullable ISpecialPlayerInventory getSpecialPlayerInventory(@NotNull Inventory inventory) {
|
||||
return InventoryAccess.getPlayerInventory(inventory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use static {@link InventoryAccess} methods.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
default boolean isSpecialEnderChest(@NotNull Inventory inventory) {
|
||||
return InventoryAccess.isEnderChest(inventory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use static {@link InventoryAccess} methods.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
default boolean isSpecialPlayerInventory(@NotNull Inventory inventory) {
|
||||
return InventoryAccess.isPlayerInventory(inventory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,9 +16,17 @@
|
||||
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* An {@link ISpecialInventory} representing an ender chest.
|
||||
*/
|
||||
public interface ISpecialEnderChest extends ISpecialInventory {
|
||||
|
||||
@Override
|
||||
default @NotNull InventoryType getBukkitType() {
|
||||
return InventoryType.ENDER_CHEST;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.lishid.openinv.internal;
|
||||
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -33,6 +34,13 @@ public interface ISpecialInventory {
|
||||
*/
|
||||
@NotNull Inventory getBukkitInventory();
|
||||
|
||||
/**
|
||||
* Get the {@link InventoryType} corresponding to this {@code ISpecialInventory}.
|
||||
*
|
||||
* @return the type of Bukkit inventory
|
||||
*/
|
||||
@NotNull InventoryType getBukkitType();
|
||||
|
||||
/**
|
||||
* Set the owning {@link Player} instance to a newly-joined user.
|
||||
*
|
||||
|
||||
@@ -16,35 +16,17 @@
|
||||
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
import java.util.List;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* An {@link ISpecialInventory} representing a player inventory.
|
||||
*/
|
||||
public interface ISpecialPlayerInventory extends ISpecialInventory {
|
||||
|
||||
/*
|
||||
* Player inventory usage varies from all other inventories - as the inventory is technically open at all times,
|
||||
* if the player is online or has been online while the inventory is open, they are in the viewer list.
|
||||
*/
|
||||
@Override
|
||||
default boolean isInUse() {
|
||||
Inventory inventory = getBukkitInventory();
|
||||
List<HumanEntity> viewers = inventory.getViewers();
|
||||
|
||||
if (viewers.size() != 1) {
|
||||
return !viewers.isEmpty();
|
||||
}
|
||||
|
||||
HumanEntity viewer = viewers.get(0);
|
||||
|
||||
if (!viewer.getUniqueId().equals(getPlayer().getUniqueId())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return viewer.getOpenInventory().getTopInventory().equals(inventory);
|
||||
default @NotNull InventoryType getBukkitType() {
|
||||
return InventoryType.PLAYER;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,39 +16,23 @@
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import com.lishid.openinv.internal.IInventoryAccess;
|
||||
import com.google.errorprone.annotations.RestrictedApi;
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import java.lang.reflect.Method;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class InventoryAccess implements IInventoryAccess {
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
private static Class<?> craftInventory = null;
|
||||
private static Method getInventory = null;
|
||||
public final class InventoryAccess {
|
||||
|
||||
static {
|
||||
String packageName = Bukkit.getServer().getClass().getPackage().getName();
|
||||
try {
|
||||
craftInventory = Class.forName(packageName + ".inventory.CraftInventory");
|
||||
getInventory = craftInventory.getDeclaredMethod("getInventory");
|
||||
} catch (ClassNotFoundException | NoSuchMethodException ignored) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #isUsable()}
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static boolean isUseable() {
|
||||
return isUsable();
|
||||
}
|
||||
private static @Nullable BiFunction<Inventory, Class<? extends ISpecialInventory>, ISpecialInventory> provider;
|
||||
|
||||
public static boolean isUsable() {
|
||||
return craftInventory != null && getInventory != null;
|
||||
return provider != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +53,7 @@ public final class InventoryAccess implements IInventoryAccess {
|
||||
* @return the backing implementation if available
|
||||
*/
|
||||
public static @Nullable ISpecialPlayerInventory getPlayerInventory(@NotNull Inventory inventory) {
|
||||
return getSpecialInventory(ISpecialPlayerInventory.class, inventory);
|
||||
return provider == null ? null : (ISpecialPlayerInventory) provider.apply(inventory, ISpecialPlayerInventory.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,34 +74,31 @@ public final class InventoryAccess implements IInventoryAccess {
|
||||
* @return the backing implementation if available
|
||||
*/
|
||||
public static @Nullable ISpecialEnderChest getEnderChest(@NotNull Inventory inventory) {
|
||||
return getSpecialInventory(ISpecialEnderChest.class, inventory);
|
||||
}
|
||||
|
||||
private static <T extends ISpecialInventory> @Nullable T getSpecialInventory(@NotNull Class<T> expected, @NotNull Inventory inventory) {
|
||||
Object inv;
|
||||
if (isUsable() && craftInventory.isAssignableFrom(inventory.getClass())) {
|
||||
try {
|
||||
inv = getInventory.invoke(inventory);
|
||||
if (expected.isInstance(inv)) {
|
||||
return expected.cast(inv);
|
||||
}
|
||||
} catch (ReflectiveOperationException ignored) {}
|
||||
}
|
||||
|
||||
// Use reflection to find the IInventory
|
||||
inv = ReflectionHelper.grabObjectByType(inventory, expected);
|
||||
|
||||
if (expected.isInstance(inv)) {
|
||||
return expected.cast(inv);
|
||||
}
|
||||
|
||||
return null;
|
||||
return provider == null ? null : (ISpecialEnderChest) provider.apply(inventory, ISpecialEnderChest.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Do not create a new instance to use static methods.
|
||||
* Get a {@link ISpecialInventory} backing an {@link Inventory}. Returns {@code null} if the inventory is not backed
|
||||
* by the correct class.
|
||||
*
|
||||
* @param inventory the Bukkit inventory
|
||||
* @return the backing implementation if available
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "4.2.0")
|
||||
public InventoryAccess() {}
|
||||
public static @Nullable ISpecialInventory getInventory(@NotNull Inventory inventory) {
|
||||
return provider == null ? null : provider.apply(inventory, ISpecialInventory.class);
|
||||
}
|
||||
|
||||
@RestrictedApi(
|
||||
explanation = "Not part of the API.",
|
||||
link = "",
|
||||
allowedOnPath = ".*/com/lishid/openinv/util/InternalAccessor.java")
|
||||
@ApiStatus.Internal
|
||||
static void setProvider(@Nullable BiFunction<Inventory, Class<? extends ISpecialInventory>, ISpecialInventory> provider) {
|
||||
InventoryAccess.provider = provider;
|
||||
}
|
||||
|
||||
private InventoryAccess() {
|
||||
throw new IllegalStateException("Cannot create instance of utility class.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.lishid.openinv.util.setting;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A per-player setting that may be enabled or disabled.
|
||||
*/
|
||||
public interface PlayerToggle {
|
||||
|
||||
/**
|
||||
* Get the name of the setting.
|
||||
*
|
||||
* @return the setting name
|
||||
*/
|
||||
@NotNull String getName();
|
||||
|
||||
/**
|
||||
* Get the state of the toggle for a particular player ID.
|
||||
*
|
||||
* @param uuid the player ID
|
||||
* @return true if the setting is enabled
|
||||
*/
|
||||
boolean is(@NotNull UUID uuid);
|
||||
|
||||
/**
|
||||
* Set the state of the toggle for a particular player ID.
|
||||
*
|
||||
* @param uuid the player ID
|
||||
* @param enabled whether the setting is enabled
|
||||
* @return true if the setting changed as a result of being set
|
||||
*/
|
||||
boolean set(@NotNull UUID uuid, boolean enabled);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.lishid.openinv.util.setting;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnmodifiableView;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Utility class containing all of OpenInv's {@link PlayerToggle PlayerToggles}.
|
||||
*/
|
||||
public final class PlayerToggles {
|
||||
|
||||
private static final Map<String, PlayerToggle> TOGGLES = new HashMap<>();
|
||||
private static final PlayerToggle ANY = add(new MemoryToggle("AnyContainer"));
|
||||
private static final PlayerToggle SILENT = add(new MemoryToggle("SilentContainer"));
|
||||
|
||||
/**
|
||||
* Get the AnyContainer toggle.
|
||||
*
|
||||
* @return the AnyContainer toggle
|
||||
*/
|
||||
public static @NotNull PlayerToggle any() {
|
||||
return ANY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SilentContainer toggle.
|
||||
*
|
||||
* @return the SilentContainer toggle
|
||||
*/
|
||||
public static @NotNull PlayerToggle silent() {
|
||||
return SILENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a toggle by name.
|
||||
*
|
||||
* @param toggleName the name of the toggle
|
||||
* @return the toggle, or null if no such toggle exists.
|
||||
*/
|
||||
public static @Nullable PlayerToggle get(@NotNull String toggleName) {
|
||||
PlayerToggle toggle = TOGGLES.get(toggleName);
|
||||
if (toggle == null) {
|
||||
toggle = TOGGLES.get(toggleName.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
return toggle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unmodifable view of all toggles available.
|
||||
*
|
||||
* @return a view of all toggles available
|
||||
*/
|
||||
public static @UnmodifiableView @NotNull Collection<PlayerToggle> get() {
|
||||
return Collections.unmodifiableCollection(TOGGLES.values());
|
||||
}
|
||||
|
||||
private static @NotNull PlayerToggle add(@NotNull PlayerToggle toggle) {
|
||||
TOGGLES.put(toggle.getName().toLowerCase(Locale.ENGLISH), toggle);
|
||||
return toggle;
|
||||
}
|
||||
|
||||
private PlayerToggles() {
|
||||
throw new IllegalStateException("Cannot create instance of utility class.");
|
||||
}
|
||||
|
||||
private static class MemoryToggle implements PlayerToggle {
|
||||
|
||||
private final @NotNull Set<UUID> enabled;
|
||||
private final @NotNull String name;
|
||||
|
||||
private MemoryToggle(@NotNull String name) {
|
||||
enabled = new HashSet<>();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean is(@NotNull UUID uuid) {
|
||||
return enabled.contains(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean set(@NotNull UUID uuid, boolean enabled) {
|
||||
if (enabled) {
|
||||
return this.enabled.add(uuid);
|
||||
} else {
|
||||
return this.enabled.remove(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<!--
|
||||
~ Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<version>4.3.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvassembly</artifactId>
|
||||
<name>OpenInvAssembly</name>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<build>
|
||||
<directory>../target</directory>
|
||||
<finalName>OpenInv</finalName>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>reactor-uberjar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<descriptors>
|
||||
<descriptor>src/assembly/reactor-uberjar.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -1,54 +0,0 @@
|
||||
<!--
|
||||
~ Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
|
||||
|
||||
<id>reactor-uberjar</id>
|
||||
|
||||
<formats>
|
||||
<format>jar</format>
|
||||
</formats>
|
||||
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
|
||||
<moduleSets>
|
||||
<moduleSet>
|
||||
|
||||
<useAllReactorProjects>true</useAllReactorProjects>
|
||||
|
||||
<binaries>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<unpack>true</unpack>
|
||||
<unpackOptions>
|
||||
<excludes>
|
||||
<!--
|
||||
- Exclude existing meta - assembly will write its own manifest, and the rest is maven
|
||||
- details that end users don't need. Ignoring it saves around 7KB after compression
|
||||
- with a single internal module present.
|
||||
-->
|
||||
<exclude>META-INF/**</exclude>
|
||||
</excludes>
|
||||
</unpackOptions>
|
||||
<!-- Exclude dependencies - shade plugin will handle their inclusion as necessary in individual jars. -->
|
||||
<includeDependencies>false</includeDependencies>
|
||||
</binaries>
|
||||
|
||||
</moduleSet>
|
||||
</moduleSets>
|
||||
|
||||
</assembly>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<version>5.1.5</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvcommon</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<artifactId>annotations</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.lishid.openinv.event;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.util.setting.PlayerToggle;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Construct and call events.
|
||||
*/
|
||||
public final class OpenEvents {
|
||||
|
||||
public static boolean saveCancelled(@NotNull Player player) {
|
||||
return call(new PlayerSaveEvent(player));
|
||||
}
|
||||
|
||||
public static boolean saveCancelled(@NotNull ISpecialInventory inventory) {
|
||||
return call(new OpenPlayerSaveEvent((Player) inventory.getPlayer(), inventory));
|
||||
}
|
||||
|
||||
public static void notifyPlayerToggle(@NotNull PlayerToggle toggle, @NotNull UUID uuid, boolean state) {
|
||||
Bukkit.getPluginManager().callEvent(new PlayerToggledEvent(toggle, uuid, state));
|
||||
}
|
||||
|
||||
private static <T extends Event & Cancellable> boolean call(T event) {
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
return event.isCancelled();
|
||||
}
|
||||
|
||||
private OpenEvents() {
|
||||
throw new IllegalStateException("Cannot create instance of utility class.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface Accessor {
|
||||
|
||||
@NotNull
|
||||
PlayerManager getPlayerManager();
|
||||
|
||||
@NotNull IAnySilentContainer getAnySilentContainer();
|
||||
|
||||
@NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player);
|
||||
|
||||
@NotNull ISpecialEnderChest createEnderChest(@NotNull Player player);
|
||||
|
||||
<T extends ISpecialInventory> @Nullable T get(@NotNull Inventory bukkitInventory, @NotNull Class<T> clazz);
|
||||
|
||||
void reload(@NotNull ConfigurationSection config);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
import org.bukkit.block.Barrel;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.EnderChest;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.Chest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public abstract class AnySilentContainerBase implements IAnySilentContainer {
|
||||
|
||||
private static final @Nullable Method BLOCK_GET_STATE_BOOLEAN;
|
||||
|
||||
static {
|
||||
@Nullable Method getState;
|
||||
try {
|
||||
//noinspection JavaReflectionMemberAccess
|
||||
getState = Block.class.getMethod("getState", boolean.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
getState = null;
|
||||
}
|
||||
BLOCK_GET_STATE_BOOLEAN = getState;
|
||||
}
|
||||
|
||||
private static BlockState getBlockState(Block block) {
|
||||
// Paper: Get state without snapshotting.
|
||||
if (BLOCK_GET_STATE_BOOLEAN != null) {
|
||||
try {
|
||||
return (BlockState) BLOCK_GET_STATE_BOOLEAN.invoke(block, false);
|
||||
} catch (ReflectiveOperationException ignored) {
|
||||
// If we encounter an issue, fall through to regular snapshotting method.
|
||||
}
|
||||
}
|
||||
return block.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnyContainerNeeded(@NotNull Block block) {
|
||||
BlockState blockState = getBlockState(block);
|
||||
|
||||
// Barrels do not require AnyContainer.
|
||||
if (blockState instanceof Barrel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enderchests require a non-occluding block on top to open.
|
||||
if (blockState instanceof EnderChest) {
|
||||
return block.getRelative(0, 1, 0).getType().isOccluding();
|
||||
}
|
||||
|
||||
// Shulker boxes require half a block clear in the direction they open.
|
||||
if (blockState instanceof ShulkerBox) {
|
||||
return isShulkerBlocked(block);
|
||||
}
|
||||
|
||||
if (!(blockState instanceof org.bukkit.block.Chest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isChestBlocked(block)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BlockData blockData = block.getBlockData();
|
||||
if (!(blockData instanceof Chest chest) || chest.getType() == Chest.Type.SINGLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockFace relativeFace = switch (chest.getFacing()) {
|
||||
case NORTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.WEST : BlockFace.EAST;
|
||||
case EAST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.NORTH : BlockFace.SOUTH;
|
||||
case SOUTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.EAST : BlockFace.WEST;
|
||||
case WEST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.SOUTH : BlockFace.NORTH;
|
||||
default -> BlockFace.SELF;
|
||||
};
|
||||
Block relative = block.getRelative(relativeFace);
|
||||
|
||||
if (relative.getType() != block.getType()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockData relativeData = relative.getBlockData();
|
||||
if (!(relativeData instanceof Chest relativeChest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (relativeChest.getFacing() != chest.getFacing()
|
||||
|| relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isChestBlocked(relative);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnySilentContainer(@NotNull Block block) {
|
||||
return isAnySilentContainer(getBlockState(block));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
public interface InternalOwned<T> {
|
||||
|
||||
T getOwnerHandle();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import com.lishid.openinv.util.lang.Replacement;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public enum InventoryViewTitle {
|
||||
|
||||
PLAYER_INVENTORY("container.player", "'s Inventory"),
|
||||
ENDER_CHEST("container.enderchest", "'s Ender Chest");
|
||||
|
||||
private final String localizationKey;
|
||||
private final String defaultSuffix;
|
||||
|
||||
InventoryViewTitle(String localizationKey, String defaultSuffix) {
|
||||
this.localizationKey = localizationKey;
|
||||
this.defaultSuffix = defaultSuffix;
|
||||
}
|
||||
|
||||
public @NotNull String getTitle(
|
||||
@NotNull LanguageManager lang,
|
||||
@NotNull Player viewer,
|
||||
@NotNull ISpecialInventory inventory) {
|
||||
HumanEntity owner = inventory.getPlayer();
|
||||
|
||||
String localTitle = lang.getLocalizedMessage(
|
||||
viewer,
|
||||
localizationKey,
|
||||
new Replacement("%player%", owner.getName()));
|
||||
return Objects.requireNonNullElseGet(localTitle, () -> owner.getName() + defaultSuffix);
|
||||
}
|
||||
|
||||
public static @Nullable InventoryViewTitle of(@NotNull ISpecialInventory inventory) {
|
||||
if (inventory instanceof ISpecialPlayerInventory) {
|
||||
return PLAYER_INVENTORY;
|
||||
} else if (inventory instanceof ISpecialEnderChest) {
|
||||
return ENDER_CHEST;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+2
-14
@@ -22,7 +22,7 @@ import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface IPlayerDataManager {
|
||||
public interface PlayerManager {
|
||||
|
||||
/**
|
||||
* Loads a Player for an OfflinePlayer.
|
||||
@@ -49,18 +49,6 @@ public interface IPlayerDataManager {
|
||||
*`
|
||||
* @return the InventoryView opened
|
||||
*/
|
||||
@Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory);
|
||||
|
||||
/**
|
||||
* Convert a raw slot number into a player inventory slot number.
|
||||
*
|
||||
* <p>Note that this method is specifically for converting an ISpecialPlayerInventory slot number into a regular
|
||||
* player inventory slot number.
|
||||
*
|
||||
* @param view the open inventory view
|
||||
* @param rawSlot the raw slot in the view
|
||||
* @return the converted slot number
|
||||
*/
|
||||
int convertToPlayerSlot(InventoryView view, int rawSlot);
|
||||
@Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory, boolean viewOnly);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
public interface ViewOnly {
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import org.bukkit.permissions.Permissible;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public enum Permissions {
|
||||
|
||||
INVENTORY_OPEN_SELF("inventory.open.self"),
|
||||
INVENTORY_OPEN_OTHER("inventory.open.other"),
|
||||
INVENTORY_EDIT_SELF("inventory.edit.self"),
|
||||
INVENTORY_EDIT_OTHER("inventory.edit.other"),
|
||||
INVENTORY_SLOT_HEAD_ANY("inventory.slot.head.any"),
|
||||
INVENTORY_SLOT_CHEST_ANY("inventory.slot.chest.any"),
|
||||
INVENTORY_SLOT_LEGS_ANY("inventory.slot.legs.any"),
|
||||
INVENTORY_SLOT_FEET_ANY("inventory.slot.feet.any"),
|
||||
INVENTORY_SLOT_DROP("inventory.slot.drop"),
|
||||
|
||||
ENDERCHEST_OPEN_SELF("enderchest.open.self"),
|
||||
ENDERCHEST_OPEN_OTHER("enderchest.open.other"),
|
||||
ENDERCHEST_EDIT_SELF("enderchest.edit.self"),
|
||||
ENDERCHEST_EDIT_OTHER("enderchest.edit.other"),
|
||||
|
||||
ACCESS_CROSSWORLD("access.crossworld"),
|
||||
ACCESS_OFFLINE("access.offline"),
|
||||
ACCESS_ONLINE("access.online"),
|
||||
|
||||
SPECTATE_CLICK("spectate.click"),
|
||||
|
||||
CONTAINER_ANY("container.any"),
|
||||
CONTAINER_SILENT("container.silent"),
|
||||
SEARCH_INVENTORY("search.inventory"),
|
||||
SEARCH_CONTAINER("search.container");
|
||||
|
||||
private final String permission;
|
||||
|
||||
Permissions(String permission) {
|
||||
this.permission = "openinv." + permission;
|
||||
}
|
||||
|
||||
public boolean hasPermission(@NotNull Permissible permissible) {
|
||||
return permissible.hasPermission(permission);
|
||||
}
|
||||
|
||||
}
|
||||
+7
-4
@@ -16,16 +16,15 @@
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* A utility for making reflection easier.
|
||||
*/
|
||||
public final class ReflectionHelper {
|
||||
|
||||
private ReflectionHelper() {}
|
||||
|
||||
/**
|
||||
* Grab an {@link Object} stored in a {@link Field} of another {@code Object}.
|
||||
*
|
||||
@@ -59,8 +58,8 @@ public final class ReflectionHelper {
|
||||
*/
|
||||
public static @Nullable Field grabFieldByType(Class<?> holderType, Class<?> fieldType) {
|
||||
for (Field field : holderType.getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
if (fieldType.isAssignableFrom(field.getType())) {
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
}
|
||||
}
|
||||
@@ -72,4 +71,8 @@ public final class ReflectionHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
private ReflectionHelper() {
|
||||
throw new IllegalStateException("Cannot create instance of utility class.");
|
||||
}
|
||||
|
||||
}
|
||||
+138
-36
@@ -16,24 +16,31 @@
|
||||
|
||||
package com.lishid.openinv.util.lang;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import net.md_5.bungee.api.ChatMessageType;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A simple language manager supporting both custom and bundled languages.
|
||||
@@ -42,14 +49,26 @@ import org.jetbrains.annotations.Nullable;
|
||||
*/
|
||||
public class LanguageManager {
|
||||
|
||||
private final OpenInv plugin;
|
||||
private final Plugin plugin;
|
||||
private final File folder;
|
||||
private final String defaultLocale;
|
||||
private final Map<String, YamlConfiguration> locales;
|
||||
|
||||
public LanguageManager(@NotNull OpenInv plugin, @NotNull String defaultLocale) {
|
||||
public LanguageManager(@NotNull Plugin plugin, @NotNull String defaultLocale) {
|
||||
this.plugin = plugin;
|
||||
this.defaultLocale = defaultLocale;
|
||||
this.locales = new HashMap<>();
|
||||
this.folder = new File(plugin.getDataFolder(), "locale");
|
||||
|
||||
if (!folder.exists() && !folder.mkdirs()) {
|
||||
plugin.getLogger().warning(() -> "Unable to create " + folder.getPath() + "! Languages may not be editable.");
|
||||
}
|
||||
|
||||
reload();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
this.locales.clear();
|
||||
getOrLoadLocale(defaultLocale);
|
||||
}
|
||||
|
||||
@@ -59,57 +78,86 @@ public class LanguageManager {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
File file = new File(plugin.getDataFolder(), locale + ".yml");
|
||||
LangLocation lang = bestMatch(locale, null);
|
||||
|
||||
// If a parent was a better match, check if it is already loaded.
|
||||
if (!locale.equals(lang.locale)) {
|
||||
loaded = locales.get(lang.locale);
|
||||
if (loaded != null) {
|
||||
locales.put(locale, loaded);
|
||||
return loaded;
|
||||
}
|
||||
}
|
||||
|
||||
// Load locale config from disk and bundled locale defaults.
|
||||
YamlConfiguration localeConfig = loadLocale(locale, file);
|
||||
YamlConfiguration localeConfig = loadLocale(lang);
|
||||
|
||||
// If the locale is not the default locale, also handle any missing translations from the default locale.
|
||||
if (!locale.equals(defaultLocale)) {
|
||||
addTranslationFallthrough(locale, localeConfig, file);
|
||||
addTranslationFallthrough(lang, localeConfig);
|
||||
|
||||
if (plugin.getConfig().getBoolean("settings.secret.warn-about-guess-section", true)
|
||||
&& localeConfig.isConfigurationSection("guess")) {
|
||||
// Warn that guess section exists. This should run once per language per server restart
|
||||
// when accessed by a user to hint to server owners that they can make UX improvements.
|
||||
plugin.getLogger().info(() -> "[LanguageManager] Missing translations from " + locale + ".yml! Check the guess section!");
|
||||
plugin.getLogger().info(() -> "[LanguageManager] Missing translations from " + lang.locale + ".yml! Check the guess section!");
|
||||
}
|
||||
}
|
||||
|
||||
locales.put(locale, localeConfig);
|
||||
locales.put(lang.locale, localeConfig);
|
||||
|
||||
return localeConfig;
|
||||
}
|
||||
|
||||
private @NotNull YamlConfiguration loadLocale(
|
||||
@NotNull String locale,
|
||||
@NotNull File file) {
|
||||
// Load defaults from the plugin's bundled resources.
|
||||
InputStream resourceStream = plugin.getResource("locale/" + locale + ".yml");
|
||||
private @NotNull LangLocation bestMatch(@NotNull String locale, @Nullable LangLocation initial) {
|
||||
File file = new File(folder, locale + ".yml");
|
||||
InputStream bundled = plugin.getResource("locale/" + locale + ".yml");
|
||||
|
||||
if (file.exists() || bundled != null) {
|
||||
return new LangLocation(locale, file, bundled);
|
||||
}
|
||||
|
||||
if (initial == null) {
|
||||
initial = new LangLocation(locale, file, null);
|
||||
}
|
||||
|
||||
int lastSeparator = locale.lastIndexOf('_');
|
||||
|
||||
// Must be at least some content before separator.
|
||||
if (lastSeparator < 1) {
|
||||
return initial;
|
||||
}
|
||||
|
||||
return bestMatch(locale.substring(0, lastSeparator), initial);
|
||||
}
|
||||
|
||||
private @NotNull YamlConfiguration loadLocale(@NotNull LangLocation lang) {
|
||||
YamlConfiguration localeConfigDefaults;
|
||||
if (resourceStream == null) {
|
||||
if (lang.bundled == null) {
|
||||
localeConfigDefaults = new YamlConfiguration();
|
||||
} else {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream))) {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(lang.bundled, StandardCharsets.UTF_8))) {
|
||||
localeConfigDefaults = YamlConfiguration.loadConfiguration(reader);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to load resource " + locale + ".yml");
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to load resource " + lang.locale + ".yml");
|
||||
localeConfigDefaults = new YamlConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
if (!file.exists()) {
|
||||
if (!lang.file.exists()) {
|
||||
// If the file does not exist on disk, save bundled defaults.
|
||||
try {
|
||||
localeConfigDefaults.save(file);
|
||||
localeConfigDefaults.save(lang.file);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml");
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + lang.locale + ".yml");
|
||||
}
|
||||
// Return loaded bundled locale.
|
||||
return localeConfigDefaults;
|
||||
}
|
||||
|
||||
// If the file does exist on disk, load it.
|
||||
YamlConfiguration localeConfig = YamlConfiguration.loadConfiguration(file);
|
||||
YamlConfiguration localeConfig = YamlConfiguration.loadConfiguration(lang.file);
|
||||
// Check for missing translations from the bundled file.
|
||||
List<String> newKeys = getMissingKeys(localeConfigDefaults, localeConfig::isSet);
|
||||
|
||||
@@ -135,22 +183,19 @@ public class LanguageManager {
|
||||
localeConfig.set("guess", null);
|
||||
}
|
||||
|
||||
plugin.getLogger().info(() -> "[LanguageManager] Added new translation keys to " + locale + ".yml: " + String.join(", ", newKeys));
|
||||
plugin.getLogger().info(() -> "[LanguageManager] Added new translation keys to " + lang.locale + ".yml: " + String.join(", ", newKeys));
|
||||
|
||||
// Write new keys to disk.
|
||||
try {
|
||||
localeConfig.save(file);
|
||||
localeConfig.save(lang.file);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml");
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + lang.locale + ".yml");
|
||||
}
|
||||
|
||||
return localeConfig;
|
||||
}
|
||||
|
||||
private void addTranslationFallthrough(
|
||||
@NotNull String locale,
|
||||
@NotNull YamlConfiguration localeConfig,
|
||||
@NotNull File file) {
|
||||
private void addTranslationFallthrough(@NotNull LangLocation location, @NotNull YamlConfiguration localeConfig) {
|
||||
YamlConfiguration defaultLocaleConfig = locales.get(defaultLocale);
|
||||
|
||||
// Get missing keys. Keys that already have a guess value are not new and don't need to trigger another write.
|
||||
@@ -166,9 +211,9 @@ public class LanguageManager {
|
||||
|
||||
// Write modified guess section to disk.
|
||||
try {
|
||||
localeConfig.save(file);
|
||||
localeConfig.save(location.file);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml");
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + location.locale + ".yml");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,8 +235,8 @@ public class LanguageManager {
|
||||
}
|
||||
|
||||
public @Nullable String getValue(@NotNull String key, @Nullable String locale) {
|
||||
String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase()).getString(key);
|
||||
if (value == null || value.isEmpty()) {
|
||||
String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase(Locale.ENGLISH)).getString(key);
|
||||
if (value == null || value.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -214,4 +259,61 @@ public class LanguageManager {
|
||||
return value;
|
||||
}
|
||||
|
||||
public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key) {
|
||||
return getValue(key, getLocale(sender));
|
||||
}
|
||||
|
||||
public @Nullable String getLocalizedMessage(
|
||||
@NotNull CommandSender sender,
|
||||
@NotNull String key,
|
||||
Replacement @NotNull ... replacements) {
|
||||
return getValue(key, getLocale(sender), replacements);
|
||||
}
|
||||
|
||||
private @NotNull String getLocale(@NotNull CommandSender sender) {
|
||||
if (sender instanceof Player) {
|
||||
return ((Player) sender).getLocale();
|
||||
} else {
|
||||
return plugin.getConfig().getString("settings.locale", "en");
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessage(@NotNull CommandSender sender, @NotNull String key) {
|
||||
String message = getLocalizedMessage(sender, key);
|
||||
|
||||
if (message != null && !message.isEmpty()) {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessage(@NotNull CommandSender sender, @NotNull String key, Replacement @NotNull... replacements) {
|
||||
String message = getLocalizedMessage(sender, key, replacements);
|
||||
|
||||
if (message != null && !message.isEmpty()) {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendSystemMessage(@NotNull Player player, @NotNull String key) {
|
||||
String message = getLocalizedMessage(player, key);
|
||||
|
||||
if (message == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int newline = message.indexOf('\n');
|
||||
if (newline != -1) {
|
||||
// No newlines in action bar chat.
|
||||
message = message.substring(0, newline);
|
||||
}
|
||||
|
||||
if (message.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacy(message));
|
||||
}
|
||||
|
||||
private record LangLocation(@NotNull String locale, @NotNull File file, @Nullable InputStream bundled) {}
|
||||
|
||||
}
|
||||
File renamed without changes.
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_19_R3;
|
||||
|
||||
import java.io.File;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.storage.PlayerDataStorage;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
|
||||
|
||||
public class OpenPlayer extends CraftPlayer {
|
||||
|
||||
public OpenPlayer(CraftServer server, ServerPlayer entity) {
|
||||
super(server, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadData() {
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle());
|
||||
if (loaded != null) {
|
||||
getHandle().readAdditionalSaveData(loaded);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveData() {
|
||||
ServerPlayer player = this.getHandle();
|
||||
// See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman)
|
||||
try {
|
||||
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
|
||||
|
||||
CompoundTag playerData = player.saveWithoutId(new CompoundTag());
|
||||
setExtraData(playerData);
|
||||
|
||||
if (!isOnline()) {
|
||||
// Special case: save old vehicle data
|
||||
CompoundTag oldData = worldNBTStorage.load(player);
|
||||
|
||||
if (oldData != null && oldData.contains("RootVehicle", 10)) {
|
||||
// See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound)
|
||||
playerData.put("RootVehicle", oldData.getCompound("RootVehicle"));
|
||||
}
|
||||
}
|
||||
|
||||
File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir());
|
||||
NbtIo.writeCompressed(playerData, file);
|
||||
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat");
|
||||
File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old");
|
||||
Util.safeReplaceFile(file1, file, file2);
|
||||
} catch (Exception e) {
|
||||
LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-265
@@ -1,265 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_19_R3;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.internal.IPlayerDataManager;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.OpenInventoryView;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.logging.Logger;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.event.CraftEventFactory;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftContainer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class PlayerDataManager implements IPlayerDataManager {
|
||||
|
||||
private @Nullable Field bukkitEntity;
|
||||
|
||||
public PlayerDataManager() {
|
||||
try {
|
||||
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
|
||||
} catch (NoSuchFieldException e) {
|
||||
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
|
||||
logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
|
||||
logger.log(java.util.logging.Level.WARNING, e.getMessage(), e);
|
||||
bukkitEntity = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull ServerPlayer getHandle(final Player player) {
|
||||
if (player instanceof CraftPlayer) {
|
||||
return ((CraftPlayer) player).getHandle();
|
||||
}
|
||||
|
||||
Server server = player.getServer();
|
||||
ServerPlayer nmsPlayer = null;
|
||||
|
||||
if (server instanceof CraftServer) {
|
||||
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId());
|
||||
}
|
||||
|
||||
if (nmsPlayer == null) {
|
||||
// Could use reflection to examine fields, but it's honestly not worth the bother.
|
||||
throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation");
|
||||
}
|
||||
|
||||
return nmsPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
|
||||
// Ensure player has data
|
||||
if (!offline.hasPlayedBefore()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a profile and entity to load the player data
|
||||
// See net.minecraft.server.players.PlayerList#canPlayerLogin
|
||||
// and net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello
|
||||
GameProfile profile = new GameProfile(offline.getUniqueId(),
|
||||
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
|
||||
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
ServerLevel worldServer = server.getLevel(Level.OVERWORLD);
|
||||
|
||||
if (worldServer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ServerPlayer entity = new ServerPlayer(server, worldServer, profile);
|
||||
|
||||
// Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak.
|
||||
entity.getAdvancements().stopListening();
|
||||
|
||||
try {
|
||||
injectPlayer(entity);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Load data. This also reads basic data into the player.
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loadedData = server.getPlayerList().playerIo.load(entity);
|
||||
|
||||
if (loadedData == null) {
|
||||
// Exceptions with loading are logged by Mojang.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Also read "extra" data.
|
||||
entity.readAdditionalSaveData(loadedData);
|
||||
|
||||
if (entity.level == null) {
|
||||
// Paper: Move player to spawn
|
||||
// SPIGOT-7340: Cannot call ServerPlayer#spawnIn with a null world
|
||||
ServerLevel level = null;
|
||||
Vec3 position = null;
|
||||
if (entity.getRespawnDimension() != null) {
|
||||
level = entity.server.getLevel(entity.getRespawnDimension());
|
||||
if (level != null && entity.getRespawnPosition() != null) {
|
||||
position = net.minecraft.world.entity.player.Player.findRespawnPositionAndUseSpawnBlock(level, entity.getRespawnPosition(), entity.getRespawnAngle(), false, false).orElse(null);
|
||||
}
|
||||
}
|
||||
if (level == null || position == null) {
|
||||
level = ((CraftWorld) server.server.getWorlds().get(0)).getHandle();
|
||||
position = Vec3.atCenterOf(level.getSharedSpawnPos());
|
||||
}
|
||||
entity.level = level;
|
||||
entity.setPos(position);
|
||||
}
|
||||
|
||||
// Return the Bukkit entity.
|
||||
return entity.getBukkitEntity();
|
||||
}
|
||||
|
||||
void injectPlayer(ServerPlayer player) throws IllegalAccessException {
|
||||
if (bukkitEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
bukkitEntity.setAccessible(true);
|
||||
|
||||
bukkitEntity.set(player, new OpenPlayer(player.server.server, player));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Player inject(@NotNull Player player) {
|
||||
try {
|
||||
ServerPlayer nmsPlayer = getHandle(player);
|
||||
if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) {
|
||||
return openPlayer;
|
||||
}
|
||||
injectPlayer(nmsPlayer);
|
||||
return nmsPlayer.getBukkitEntity();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
|
||||
|
||||
ServerPlayer nmsPlayer = getHandle(player);
|
||||
|
||||
if (nmsPlayer.connection == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InventoryView view = getView(player, inventory);
|
||||
|
||||
if (view == null) {
|
||||
return player.openInventory(inventory.getBukkitInventory());
|
||||
}
|
||||
|
||||
AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
|
||||
@Override
|
||||
public MenuType<?> getType() {
|
||||
return getContainers(inventory.getBukkitInventory().getSize());
|
||||
}
|
||||
};
|
||||
|
||||
container.setTitle(Component.literal(view.getTitle()));
|
||||
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
|
||||
|
||||
if (container == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(),
|
||||
Component.literal(container.getBukkitView().getTitle())));
|
||||
nmsPlayer.containerMenu = container;
|
||||
nmsPlayer.initMenu(container);
|
||||
|
||||
return container.getBukkitView();
|
||||
|
||||
}
|
||||
|
||||
private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) {
|
||||
if (inventory instanceof SpecialEnderChest) {
|
||||
return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest");
|
||||
} else if (inventory instanceof SpecialPlayerInventory) {
|
||||
return new OpenInventoryView(player, inventory, "container.player", "'s Inventory");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static @NotNull MenuType<?> getContainers(int inventorySize) {
|
||||
|
||||
return switch (inventorySize) {
|
||||
case 9 -> MenuType.GENERIC_9x1;
|
||||
case 18 -> MenuType.GENERIC_9x2;
|
||||
case 36 -> MenuType.GENERIC_9x4; // PLAYER
|
||||
case 41, 45 -> MenuType.GENERIC_9x5;
|
||||
case 54 -> MenuType.GENERIC_9x6;
|
||||
default -> MenuType.GENERIC_9x3; // Default 27-slot inventory
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertToPlayerSlot(InventoryView view, int rawSlot) {
|
||||
int topSize = view.getTopInventory().getSize();
|
||||
if (topSize <= rawSlot) {
|
||||
// Slot is not inside special inventory, use Bukkit logic.
|
||||
return view.convertSlot(rawSlot);
|
||||
}
|
||||
|
||||
// Main inventory, slots 0-26 -> 9-35
|
||||
if (rawSlot < 27) {
|
||||
return rawSlot + 9;
|
||||
}
|
||||
// Hotbar, slots 27-35 -> 0-8
|
||||
if (rawSlot < 36) {
|
||||
return rawSlot - 27;
|
||||
}
|
||||
// Armor, slots 36-39 -> 39-36
|
||||
if (rawSlot < 40) {
|
||||
return 36 + (39 - rawSlot);
|
||||
}
|
||||
// Off hand
|
||||
if (rawSlot == 40) {
|
||||
return 40;
|
||||
}
|
||||
// Drop slots, "out of inventory"
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
-350
@@ -1,350 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_19_R3;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.ContainerListener;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.player.StackedContents;
|
||||
import net.minecraft.world.inventory.PlayerEnderChestContainer;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
|
||||
|
||||
private final CraftInventory inventory;
|
||||
private ServerPlayer owner;
|
||||
private NonNullList<ItemStack> items;
|
||||
private boolean playerOnline;
|
||||
|
||||
public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) {
|
||||
super(PlayerDataManager.getHandle(player));
|
||||
this.inventory = new CraftInventory(this);
|
||||
this.owner = PlayerDataManager.getHandle(player);
|
||||
this.playerOnline = online;
|
||||
this.items = this.owner.getEnderChestInventory().items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CraftInventory getBukkitInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {
|
||||
this.playerOnline = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) {
|
||||
if (this.playerOnline) {
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayer offlinePlayer = this.owner;
|
||||
ServerPlayer onlinePlayer = PlayerDataManager.getHandle(player);
|
||||
|
||||
// Set owner to new player.
|
||||
this.owner = onlinePlayer;
|
||||
|
||||
// Set player's ender chest contents to our modified contents.
|
||||
PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory();
|
||||
for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) {
|
||||
onlineEnderChest.setItem(i, this.items.get(i));
|
||||
}
|
||||
|
||||
// Set our item array to the new inventory's array.
|
||||
this.items = onlineEnderChest.items;
|
||||
|
||||
// Add viewers to new inventory.
|
||||
onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction);
|
||||
|
||||
this.playerOnline = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.entity.Player getPlayer() {
|
||||
return owner.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
this.owner.getEnderChestInventory().setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity who) {
|
||||
this.owner.getEnderChestInventory().onOpen(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity who) {
|
||||
this.owner.getEnderChestInventory().onClose(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return this.owner.getEnderChestInventory().getViewers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActiveChest(EnderChestBlockEntity enderChest) {
|
||||
this.owner.getEnderChestInventory().setActiveChest(enderChest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActiveChest(EnderChestBlockEntity enderChest) {
|
||||
return this.owner.getEnderChestInventory().isActiveChest(enderChest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return this.owner.getEnderChestInventory().getMaxStackSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int i) {
|
||||
this.owner.getEnderChestInventory().setMaxStackSize(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryHolder getOwner() {
|
||||
return this.owner.getEnderChestInventory().getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Location getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(ContainerListener listener) {
|
||||
this.owner.getEnderChestInventory().addListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(ContainerListener listener) {
|
||||
this.owner.getEnderChestInventory().removeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int i) {
|
||||
return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int i, int j) {
|
||||
ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j);
|
||||
if (!itemstack.isEmpty()) {
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack addItem(ItemStack itemstack) {
|
||||
ItemStack localItem = itemstack.copy();
|
||||
this.moveItemToOccupiedSlotsWithSameType(localItem);
|
||||
if (localItem.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
} else {
|
||||
this.moveItemToEmptySlots(localItem);
|
||||
return localItem.isEmpty() ? ItemStack.EMPTY : localItem;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAddItem(ItemStack itemstack) {
|
||||
for (ItemStack itemstack1 : this.items) {
|
||||
if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void moveItemToEmptySlots(ItemStack itemstack) {
|
||||
for(int i = 0; i < this.getContainerSize(); ++i) {
|
||||
ItemStack localItem = this.getItem(i);
|
||||
if (localItem.isEmpty()) {
|
||||
this.setItem(i, itemstack.copy());
|
||||
itemstack.setCount(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) {
|
||||
for(int i = 0; i < this.getContainerSize(); ++i) {
|
||||
ItemStack localItem = this.getItem(i);
|
||||
if (ItemStack.isSameItemSameTags(localItem, itemstack)) {
|
||||
this.moveItemsBetweenStacks(itemstack, localItem);
|
||||
if (itemstack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) {
|
||||
int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize());
|
||||
int j = Math.min(itemstack.getCount(), i - itemstack1.getCount());
|
||||
if (j > 0) {
|
||||
itemstack1.grow(j);
|
||||
itemstack.shrink(j);
|
||||
this.setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int i) {
|
||||
ItemStack itemstack = this.items.get(i);
|
||||
if (itemstack.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
} else {
|
||||
this.items.set(i, ItemStack.EMPTY);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int i, ItemStack itemstack) {
|
||||
this.items.set(i, itemstack);
|
||||
if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) {
|
||||
itemstack.setCount(this.getMaxStackSize());
|
||||
}
|
||||
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return this.owner.getEnderChestInventory().getContainerSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.items.stream().allMatch(ItemStack::isEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startOpen(Player player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopOpen(Player player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceItem(int i, ItemStack itemstack) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
this.items.clear();
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStackedContents(StackedContents stackedContents) {
|
||||
for (ItemStack itemstack : this.items) {
|
||||
stackedContents.accountStack(itemstack);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> removeAllItems() {
|
||||
List<ItemStack> list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList());
|
||||
this.clearContent();
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemType(Item item, int i) {
|
||||
ItemStack itemstack = new ItemStack(item, 0);
|
||||
|
||||
for(int j = this.getContainerSize() - 1; j >= 0; --j) {
|
||||
ItemStack localItem = this.getItem(j);
|
||||
if (localItem.getItem().equals(item)) {
|
||||
int k = i - itemstack.getCount();
|
||||
ItemStack splitItem = localItem.split(k);
|
||||
itemstack.grow(splitItem.getCount());
|
||||
if (itemstack.getCount() == i) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromTag(ListTag listTag) {
|
||||
for (int i = 0; i < this.getContainerSize(); ++i) {
|
||||
this.setItem(i, ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
for (int i = 0; i < listTag.size(); ++i) {
|
||||
CompoundTag compoundTag = listTag.getCompound(i);
|
||||
int j = compoundTag.getByte("Slot") & 255;
|
||||
if (j < this.getContainerSize()) {
|
||||
this.setItem(j, ItemStack.of(compoundTag));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-806
@@ -1,806 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_19_R3;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.CrashReportCategory;
|
||||
import net.minecraft.ReportedException;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.tags.DamageTypeTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.player.StackedContents;
|
||||
import net.minecraft.world.item.ArmorItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory {
|
||||
|
||||
private final CraftInventory inventory;
|
||||
private boolean playerOnline;
|
||||
private Player player;
|
||||
private NonNullList<ItemStack> items;
|
||||
private NonNullList<ItemStack> armor;
|
||||
private NonNullList<ItemStack> offhand;
|
||||
private List<NonNullList<ItemStack>> compartments;
|
||||
|
||||
public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) {
|
||||
super(PlayerDataManager.getHandle(bukkitPlayer));
|
||||
this.inventory = new CraftInventory(this);
|
||||
this.playerOnline = online;
|
||||
this.player = super.player;
|
||||
this.selected = player.getInventory().selected;
|
||||
this.items = this.player.getInventory().items;
|
||||
this.armor = this.player.getInventory().armor;
|
||||
this.offhand = this.player.getInventory().offhand;
|
||||
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
|
||||
if (this.playerOnline) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player offlinePlayer = this.player;
|
||||
Player onlinePlayer = PlayerDataManager.getHandle(player);
|
||||
onlinePlayer.getInventory().transaction.addAll(this.transaction);
|
||||
|
||||
// Set owner to new player.
|
||||
this.player = onlinePlayer;
|
||||
|
||||
// Set player's inventory contents to our modified contents.
|
||||
Inventory onlineInventory = onlinePlayer.getInventory();
|
||||
for (int i = 0; i < getContainerSize(); ++i) {
|
||||
onlineInventory.setItem(i, getRawItem(i));
|
||||
}
|
||||
onlineInventory.selected = this.selected;
|
||||
|
||||
// Set our item arrays to the new inventory's arrays.
|
||||
this.items = onlineInventory.items;
|
||||
this.armor = onlineInventory.armor;
|
||||
this.offhand = onlineInventory.offhand;
|
||||
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
|
||||
|
||||
// Add existing viewers to new viewer list.
|
||||
Inventory offlineInventory = offlinePlayer.getInventory();
|
||||
// Remove self from listing - player is always a viewer of their own inventory, prevent duplicates.
|
||||
offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity());
|
||||
onlineInventory.transaction.addAll(offlineInventory.transaction);
|
||||
|
||||
this.playerOnline = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CraftInventory getBukkitInventory() {
|
||||
return this.inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {
|
||||
this.playerOnline = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HumanEntity getPlayer() {
|
||||
return this.player.getBukkitEntity();
|
||||
}
|
||||
|
||||
private @NotNull ItemStack getRawItem(int i) {
|
||||
if (i < 0) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
NonNullList<ItemStack> list;
|
||||
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
|
||||
list = iterator.next();
|
||||
if (i < list.size()) {
|
||||
return list.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
private void setRawItem(int i, @NotNull ItemStack itemStack) {
|
||||
if (i < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
NonNullList<ItemStack> list;
|
||||
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
|
||||
list = iterator.next();
|
||||
if (i < list.size()) {
|
||||
list.set(i, itemStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record IndexedCompartment(@Nullable NonNullList<ItemStack> compartment, int index) {}
|
||||
|
||||
private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) {
|
||||
if (index < items.size()) {
|
||||
return new IndexedCompartment(items, getReversedItemSlotNum(index));
|
||||
}
|
||||
|
||||
index -= items.size();
|
||||
|
||||
if (index < armor.size()) {
|
||||
return new IndexedCompartment(armor, getReversedArmorSlotNum(index));
|
||||
}
|
||||
|
||||
index -= armor.size();
|
||||
|
||||
if (index < offhand.size()) {
|
||||
return new IndexedCompartment(offhand, index);
|
||||
}
|
||||
|
||||
index -= offhand.size();
|
||||
|
||||
return new IndexedCompartment(null, index);
|
||||
}
|
||||
|
||||
private int getReversedArmorSlotNum(final int i) {
|
||||
if (i == 0) {
|
||||
return 3;
|
||||
}
|
||||
if (i == 1) {
|
||||
return 2;
|
||||
}
|
||||
if (i == 2) {
|
||||
return 1;
|
||||
}
|
||||
if (i == 3) {
|
||||
return 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
private int getReversedItemSlotNum(final int i) {
|
||||
if (i >= 27) {
|
||||
return i - 27;
|
||||
}
|
||||
return i + 9;
|
||||
}
|
||||
|
||||
private boolean contains(Predicate<ItemStack> predicate) {
|
||||
return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getArmorContents() {
|
||||
return this.armor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity who) {
|
||||
this.player.getInventory().onOpen(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity who) {
|
||||
this.player.getInventory().onClose(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return this.player.getInventory().getViewers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryHolder getOwner() {
|
||||
return this.player.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return this.player.getInventory().getMaxStackSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int size) {
|
||||
this.player.getInventory().setMaxStackSize(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return this.player.getBukkitEntity().getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCustomName() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getSelected() {
|
||||
return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) {
|
||||
return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int canHold(ItemStack itemstack) {
|
||||
int remains = itemstack.getCount();
|
||||
|
||||
for (int i = 0; i < this.items.size(); ++i) {
|
||||
ItemStack itemstack1 = this.getRawItem(i);
|
||||
if (itemstack1.isEmpty()) {
|
||||
return itemstack.getCount();
|
||||
}
|
||||
|
||||
if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) {
|
||||
remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount();
|
||||
}
|
||||
|
||||
if (remains <= 0) {
|
||||
return itemstack.getCount();
|
||||
}
|
||||
}
|
||||
|
||||
ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size());
|
||||
if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) {
|
||||
remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount();
|
||||
}
|
||||
|
||||
return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFreeSlot() {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
if (this.items.get(i).isEmpty()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPickedItem(ItemStack itemstack) {
|
||||
int i = this.findSlotMatchingItem(itemstack);
|
||||
if (isHotbarSlot(i)) {
|
||||
this.selected = i;
|
||||
} else if (i == -1) {
|
||||
this.selected = this.getSuitableHotbarSlot();
|
||||
if (!this.items.get(this.selected).isEmpty()) {
|
||||
int j = this.getFreeSlot();
|
||||
if (j != -1) {
|
||||
this.items.set(j, this.items.get(this.selected));
|
||||
}
|
||||
}
|
||||
|
||||
this.items.set(this.selected, itemstack);
|
||||
} else {
|
||||
this.pickSlot(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pickSlot(int i) {
|
||||
this.selected = this.getSuitableHotbarSlot();
|
||||
ItemStack itemstack = this.items.get(this.selected);
|
||||
this.items.set(this.selected, this.items.get(i));
|
||||
this.items.set(i, itemstack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findSlotMatchingItem(ItemStack itemstack) {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findSlotMatchingUnusedItem(ItemStack itemStack) {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
ItemStack localItem = this.items.get(i);
|
||||
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSuitableHotbarSlot() {
|
||||
int i;
|
||||
int j;
|
||||
for(j = 0; j < 9; ++j) {
|
||||
i = (this.selected + j) % 9;
|
||||
if (this.items.get(i).isEmpty()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
for(j = 0; j < 9; ++j) {
|
||||
i = (this.selected + j) % 9;
|
||||
if (!this.items.get(i).isEnchanted()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return this.selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swapPaint(double d0) {
|
||||
if (d0 > 0.0D) {
|
||||
d0 = 1.0D;
|
||||
}
|
||||
|
||||
if (d0 < 0.0D) {
|
||||
d0 = -1.0D;
|
||||
}
|
||||
|
||||
this.selected = (int) (this.selected - d0);
|
||||
|
||||
while (this.selected < 0) {
|
||||
this.selected += 9;
|
||||
}
|
||||
|
||||
while(this.selected >= 9) {
|
||||
this.selected -= 9;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int clearOrCountMatchingItems(Predicate<ItemStack> predicate, int i, Container container) {
|
||||
byte b0 = 0;
|
||||
boolean flag = i == 0;
|
||||
int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag);
|
||||
j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag);
|
||||
ItemStack itemstack = this.player.containerMenu.getCarried();
|
||||
j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag);
|
||||
if (itemstack.isEmpty()) {
|
||||
this.player.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
private int addResource(ItemStack itemstack) {
|
||||
int i = this.getSlotWithRemainingSpace(itemstack);
|
||||
if (i == -1) {
|
||||
i = this.getFreeSlot();
|
||||
}
|
||||
|
||||
return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack);
|
||||
}
|
||||
|
||||
private int addResource(int i, ItemStack itemstack) {
|
||||
Item item = itemstack.getItem();
|
||||
int j = itemstack.getCount();
|
||||
ItemStack localItemStack = this.getRawItem(i);
|
||||
if (localItemStack.isEmpty()) {
|
||||
localItemStack = new ItemStack(item, 0);
|
||||
if (itemstack.hasTag()) {
|
||||
// hasTag ensures tag not null
|
||||
//noinspection ConstantConditions
|
||||
localItemStack.setTag(itemstack.getTag().copy());
|
||||
}
|
||||
|
||||
this.setRawItem(i, localItemStack);
|
||||
}
|
||||
|
||||
int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount());
|
||||
|
||||
if (k > this.getMaxStackSize() - localItemStack.getCount()) {
|
||||
k = this.getMaxStackSize() - localItemStack.getCount();
|
||||
}
|
||||
|
||||
if (k != 0) {
|
||||
j -= k;
|
||||
localItemStack.grow(k);
|
||||
localItemStack.setPopTime(5);
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSlotWithRemainingSpace(ItemStack itemstack) {
|
||||
if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) {
|
||||
return this.selected;
|
||||
} else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) {
|
||||
return 40;
|
||||
} else {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
for (int i = 0; i < compartment.size(); ++i) {
|
||||
if (!compartment.get(i).isEmpty()) {
|
||||
compartment.get(i).inventoryTick(this.player.level, this.player, i, this.selected == i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(ItemStack itemStack) {
|
||||
return this.add(-1, itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(int i, ItemStack itemStack) {
|
||||
if (itemStack.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
try {
|
||||
if (itemStack.isDamaged()) {
|
||||
if (i == -1) {
|
||||
i = this.getFreeSlot();
|
||||
}
|
||||
|
||||
if (i >= 0) {
|
||||
this.items.set(i, itemStack.copy());
|
||||
this.items.get(i).setPopTime(5);
|
||||
itemStack.setCount(0);
|
||||
return true;
|
||||
} else if (this.player.getAbilities().instabuild) {
|
||||
itemStack.setCount(0);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
int j;
|
||||
do {
|
||||
j = itemStack.getCount();
|
||||
if (i == -1) {
|
||||
itemStack.setCount(this.addResource(itemStack));
|
||||
} else {
|
||||
itemStack.setCount(this.addResource(i, itemStack));
|
||||
}
|
||||
} while(!itemStack.isEmpty() && itemStack.getCount() < j);
|
||||
|
||||
if (itemStack.getCount() == j && this.player.getAbilities().instabuild) {
|
||||
itemStack.setCount(0);
|
||||
return true;
|
||||
} else {
|
||||
return itemStack.getCount() < j;
|
||||
}
|
||||
}
|
||||
} catch (Throwable var6) {
|
||||
CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory");
|
||||
CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added");
|
||||
crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem()));
|
||||
crashReportCategory.setDetail("Item data", itemStack.getDamageValue());
|
||||
crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString());
|
||||
throw new ReportedException(crashReport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeItemBackInInventory(ItemStack itemStack) {
|
||||
this.placeItemBackInInventory(itemStack, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeItemBackInInventory(ItemStack itemStack, boolean flag) {
|
||||
while(true) {
|
||||
if (!itemStack.isEmpty()) {
|
||||
int i = this.getSlotWithRemainingSpace(itemStack);
|
||||
if (i == -1) {
|
||||
i = this.getFreeSlot();
|
||||
}
|
||||
|
||||
if (i != -1) {
|
||||
int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount();
|
||||
if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) {
|
||||
((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
this.player.drop(itemStack, false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int rawIndex, final int j) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null
|
||||
|| indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeItem(ItemStack itemStack) {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
for (int i = 0; i < compartment.size(); ++i) {
|
||||
if (compartment.get(i) == itemStack) {
|
||||
compartment.set(i, ItemStack.EMPTY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int rawIndex) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY);
|
||||
|
||||
if (removed.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int rawIndex, final ItemStack itemStack) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null) {
|
||||
this.player.drop(itemStack, true);
|
||||
return;
|
||||
}
|
||||
|
||||
indexedCompartment.compartment().set(indexedCompartment.index(), itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDestroySpeed(BlockState blockState) {
|
||||
return this.items.get(this.selected).getDestroySpeed(blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListTag save(ListTag listTag) {
|
||||
for (int i = 0; i < this.items.size(); ++i) {
|
||||
if (!this.items.get(i).isEmpty()) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putByte("Slot", (byte)i);
|
||||
this.items.get(i).save(compoundTag);
|
||||
listTag.add(compoundTag);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < this.armor.size(); ++i) {
|
||||
if (!this.armor.get(i).isEmpty()) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putByte("Slot", (byte)(i + 100));
|
||||
this.armor.get(i).save(compoundTag);
|
||||
listTag.add(compoundTag);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < this.offhand.size(); ++i) {
|
||||
if (!this.offhand.get(i).isEmpty()) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putByte("Slot", (byte)(i + 150));
|
||||
this.offhand.get(i).save(compoundTag);
|
||||
listTag.add(compoundTag);
|
||||
}
|
||||
}
|
||||
|
||||
return listTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(ListTag listTag) {
|
||||
this.items.clear();
|
||||
this.armor.clear();
|
||||
this.offhand.clear();
|
||||
|
||||
for(int i = 0; i < listTag.size(); ++i) {
|
||||
CompoundTag compoundTag = listTag.getCompound(i);
|
||||
int j = compoundTag.getByte("Slot") & 255;
|
||||
ItemStack itemstack = ItemStack.of(compoundTag);
|
||||
if (!itemstack.isEmpty()) {
|
||||
if (j < this.items.size()) {
|
||||
this.items.set(j, itemstack);
|
||||
} else if (j >= 100 && j < this.armor.size() + 100) {
|
||||
this.armor.set(j - 100, itemstack);
|
||||
} else if (j >= 150 && j < this.offhand.size() + 150) {
|
||||
this.offhand.set(j - 150, itemstack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return 45;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return !contains(itemStack -> !itemStack.isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int rawIndex) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
return indexedCompartment.compartment().get(indexedCompartment.index());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getName() {
|
||||
return this.player.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getArmor(int index) {
|
||||
return this.armor.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) {
|
||||
if (damage > 0.0F) {
|
||||
damage /= 4.0F;
|
||||
if (damage < 1.0F) {
|
||||
damage = 1.0F;
|
||||
}
|
||||
|
||||
for (int index : armorIndices) {
|
||||
ItemStack itemstack = this.armor.get(index);
|
||||
if ((!damagesource.is(DamageTypeTags.IS_FIRE) || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) {
|
||||
itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropAll() {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
for (int i = 0; i < compartment.size(); ++i) {
|
||||
ItemStack itemstack = compartment.get(i);
|
||||
if (!itemstack.isEmpty()) {
|
||||
this.player.drop(itemstack, true, false);
|
||||
compartment.set(i, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
super.setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimesChanged() {
|
||||
return super.getTimesChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(ItemStack itemstack) {
|
||||
return contains(itemStack -> itemStack.isEmpty() && itemStack.sameItem(itemstack));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(TagKey<Item> tagKey) {
|
||||
|
||||
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceWith(Inventory inventory) {
|
||||
Function<Integer, ItemStack> getter;
|
||||
|
||||
if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) {
|
||||
getter = specialPlayerInventory::getRawItem;
|
||||
} else {
|
||||
getter = inventory::getItem;
|
||||
}
|
||||
|
||||
for(int i = 0; i < this.getContainerSize(); ++i) {
|
||||
this.setRawItem(i, getter.apply(i));
|
||||
}
|
||||
|
||||
this.selected = inventory.selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
compartment.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStackedContents(StackedContents stackedContents) {
|
||||
for (ItemStack itemstack : this.items) {
|
||||
stackedContents.accountSimpleStack(itemstack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeFromSelected(boolean dropWholeStack) {
|
||||
ItemStack itemstack = this.getSelected();
|
||||
return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_20_R1;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import java.io.File;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.storage.PlayerDataStorage;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
|
||||
|
||||
public class OpenPlayer extends CraftPlayer {
|
||||
|
||||
public OpenPlayer(CraftServer server, ServerPlayer entity) {
|
||||
super(server, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadData() {
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle());
|
||||
if (loaded != null) {
|
||||
getHandle().readAdditionalSaveData(loaded);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveData() {
|
||||
ServerPlayer player = this.getHandle();
|
||||
// See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman)
|
||||
try {
|
||||
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
|
||||
|
||||
CompoundTag playerData = player.saveWithoutId(new CompoundTag());
|
||||
setExtraData(playerData);
|
||||
|
||||
if (!isOnline()) {
|
||||
// Special case: save old vehicle data
|
||||
CompoundTag oldData = worldNBTStorage.load(player);
|
||||
|
||||
if (oldData != null && oldData.contains("RootVehicle", 10)) {
|
||||
// See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound)
|
||||
playerData.put("RootVehicle", oldData.getCompound("RootVehicle"));
|
||||
}
|
||||
}
|
||||
|
||||
File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir());
|
||||
NbtIo.writeCompressed(playerData, file);
|
||||
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat");
|
||||
File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old");
|
||||
Util.safeReplaceFile(file1, file, file2);
|
||||
} catch (Exception e) {
|
||||
LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-249
@@ -1,249 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_20_R1;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.internal.IPlayerDataManager;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.OpenInventoryView;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.logging.Logger;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.event.CraftEventFactory;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftContainer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class PlayerDataManager implements IPlayerDataManager {
|
||||
|
||||
private @Nullable Field bukkitEntity;
|
||||
|
||||
public PlayerDataManager() {
|
||||
try {
|
||||
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
|
||||
} catch (NoSuchFieldException e) {
|
||||
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
|
||||
logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
|
||||
logger.log(java.util.logging.Level.WARNING, e.getMessage(), e);
|
||||
bukkitEntity = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull ServerPlayer getHandle(final Player player) {
|
||||
if (player instanceof CraftPlayer) {
|
||||
return ((CraftPlayer) player).getHandle();
|
||||
}
|
||||
|
||||
Server server = player.getServer();
|
||||
ServerPlayer nmsPlayer = null;
|
||||
|
||||
if (server instanceof CraftServer) {
|
||||
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId());
|
||||
}
|
||||
|
||||
if (nmsPlayer == null) {
|
||||
// Could use reflection to examine fields, but it's honestly not worth the bother.
|
||||
throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation");
|
||||
}
|
||||
|
||||
return nmsPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
|
||||
// Ensure player has data
|
||||
if (!offline.hasPlayedBefore()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a profile and entity to load the player data
|
||||
// See net.minecraft.server.players.PlayerList#canPlayerLogin
|
||||
// and net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello
|
||||
GameProfile profile = new GameProfile(offline.getUniqueId(),
|
||||
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
|
||||
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
ServerLevel worldServer = server.getLevel(Level.OVERWORLD);
|
||||
|
||||
if (worldServer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ServerPlayer entity = new ServerPlayer(server, worldServer, profile);
|
||||
|
||||
// Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak.
|
||||
entity.getAdvancements().stopListening();
|
||||
|
||||
try {
|
||||
injectPlayer(entity);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Load data. This also reads basic data into the player.
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loadedData = server.getPlayerList().playerIo.load(entity);
|
||||
|
||||
if (loadedData == null) {
|
||||
// Exceptions with loading are logged by Mojang.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Also read "extra" data.
|
||||
entity.readAdditionalSaveData(loadedData);
|
||||
|
||||
if (entity.level() == null) {
|
||||
// Paper: Move player to spawn
|
||||
entity.spawnIn(null);
|
||||
}
|
||||
|
||||
// Return the Bukkit entity.
|
||||
return entity.getBukkitEntity();
|
||||
}
|
||||
|
||||
void injectPlayer(ServerPlayer player) throws IllegalAccessException {
|
||||
if (bukkitEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
bukkitEntity.setAccessible(true);
|
||||
|
||||
bukkitEntity.set(player, new OpenPlayer(player.server.server, player));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Player inject(@NotNull Player player) {
|
||||
try {
|
||||
ServerPlayer nmsPlayer = getHandle(player);
|
||||
if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) {
|
||||
return openPlayer;
|
||||
}
|
||||
injectPlayer(nmsPlayer);
|
||||
return nmsPlayer.getBukkitEntity();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
|
||||
|
||||
ServerPlayer nmsPlayer = getHandle(player);
|
||||
|
||||
if (nmsPlayer.connection == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InventoryView view = getView(player, inventory);
|
||||
|
||||
if (view == null) {
|
||||
return player.openInventory(inventory.getBukkitInventory());
|
||||
}
|
||||
|
||||
AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
|
||||
@Override
|
||||
public MenuType<?> getType() {
|
||||
return getContainers(inventory.getBukkitInventory().getSize());
|
||||
}
|
||||
};
|
||||
|
||||
container.setTitle(Component.literal(view.getTitle()));
|
||||
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
|
||||
|
||||
if (container == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(),
|
||||
Component.literal(container.getBukkitView().getTitle())));
|
||||
nmsPlayer.containerMenu = container;
|
||||
nmsPlayer.initMenu(container);
|
||||
|
||||
return container.getBukkitView();
|
||||
|
||||
}
|
||||
|
||||
private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) {
|
||||
if (inventory instanceof SpecialEnderChest) {
|
||||
return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest");
|
||||
} else if (inventory instanceof SpecialPlayerInventory) {
|
||||
return new OpenInventoryView(player, inventory, "container.player", "'s Inventory");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static @NotNull MenuType<?> getContainers(int inventorySize) {
|
||||
|
||||
return switch (inventorySize) {
|
||||
case 9 -> MenuType.GENERIC_9x1;
|
||||
case 18 -> MenuType.GENERIC_9x2;
|
||||
case 36 -> MenuType.GENERIC_9x4; // PLAYER
|
||||
case 41, 45 -> MenuType.GENERIC_9x5;
|
||||
case 54 -> MenuType.GENERIC_9x6;
|
||||
default -> MenuType.GENERIC_9x3; // Default 27-slot inventory
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertToPlayerSlot(InventoryView view, int rawSlot) {
|
||||
int topSize = view.getTopInventory().getSize();
|
||||
if (topSize <= rawSlot) {
|
||||
// Slot is not inside special inventory, use Bukkit logic.
|
||||
return view.convertSlot(rawSlot);
|
||||
}
|
||||
|
||||
// Main inventory, slots 0-26 -> 9-35
|
||||
if (rawSlot < 27) {
|
||||
return rawSlot + 9;
|
||||
}
|
||||
// Hotbar, slots 27-35 -> 0-8
|
||||
if (rawSlot < 36) {
|
||||
return rawSlot - 27;
|
||||
}
|
||||
// Armor, slots 36-39 -> 39-36
|
||||
if (rawSlot < 40) {
|
||||
return 36 + (39 - rawSlot);
|
||||
}
|
||||
// Off hand
|
||||
if (rawSlot == 40) {
|
||||
return 40;
|
||||
}
|
||||
// Drop slots, "out of inventory"
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
-350
@@ -1,350 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_20_R1;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.ContainerListener;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.player.StackedContents;
|
||||
import net.minecraft.world.inventory.PlayerEnderChestContainer;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
|
||||
|
||||
private final CraftInventory inventory;
|
||||
private ServerPlayer owner;
|
||||
private NonNullList<ItemStack> items;
|
||||
private boolean playerOnline;
|
||||
|
||||
public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) {
|
||||
super(PlayerDataManager.getHandle(player));
|
||||
this.inventory = new CraftInventory(this);
|
||||
this.owner = PlayerDataManager.getHandle(player);
|
||||
this.playerOnline = online;
|
||||
this.items = this.owner.getEnderChestInventory().items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CraftInventory getBukkitInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {
|
||||
this.playerOnline = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) {
|
||||
if (this.playerOnline) {
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayer offlinePlayer = this.owner;
|
||||
ServerPlayer onlinePlayer = PlayerDataManager.getHandle(player);
|
||||
|
||||
// Set owner to new player.
|
||||
this.owner = onlinePlayer;
|
||||
|
||||
// Set player's ender chest contents to our modified contents.
|
||||
PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory();
|
||||
for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) {
|
||||
onlineEnderChest.setItem(i, this.items.get(i));
|
||||
}
|
||||
|
||||
// Set our item array to the new inventory's array.
|
||||
this.items = onlineEnderChest.items;
|
||||
|
||||
// Add viewers to new inventory.
|
||||
onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction);
|
||||
|
||||
this.playerOnline = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.entity.Player getPlayer() {
|
||||
return owner.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
this.owner.getEnderChestInventory().setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity who) {
|
||||
this.owner.getEnderChestInventory().onOpen(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity who) {
|
||||
this.owner.getEnderChestInventory().onClose(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return this.owner.getEnderChestInventory().getViewers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActiveChest(EnderChestBlockEntity enderChest) {
|
||||
this.owner.getEnderChestInventory().setActiveChest(enderChest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActiveChest(EnderChestBlockEntity enderChest) {
|
||||
return this.owner.getEnderChestInventory().isActiveChest(enderChest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return this.owner.getEnderChestInventory().getMaxStackSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int i) {
|
||||
this.owner.getEnderChestInventory().setMaxStackSize(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryHolder getOwner() {
|
||||
return this.owner.getEnderChestInventory().getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Location getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(ContainerListener listener) {
|
||||
this.owner.getEnderChestInventory().addListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(ContainerListener listener) {
|
||||
this.owner.getEnderChestInventory().removeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int i) {
|
||||
return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int i, int j) {
|
||||
ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j);
|
||||
if (!itemstack.isEmpty()) {
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack addItem(ItemStack itemstack) {
|
||||
ItemStack localItem = itemstack.copy();
|
||||
this.moveItemToOccupiedSlotsWithSameType(localItem);
|
||||
if (localItem.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
} else {
|
||||
this.moveItemToEmptySlots(localItem);
|
||||
return localItem.isEmpty() ? ItemStack.EMPTY : localItem;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAddItem(ItemStack itemstack) {
|
||||
for (ItemStack itemstack1 : this.items) {
|
||||
if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void moveItemToEmptySlots(ItemStack itemstack) {
|
||||
for(int i = 0; i < this.getContainerSize(); ++i) {
|
||||
ItemStack localItem = this.getItem(i);
|
||||
if (localItem.isEmpty()) {
|
||||
this.setItem(i, itemstack.copy());
|
||||
itemstack.setCount(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) {
|
||||
for(int i = 0; i < this.getContainerSize(); ++i) {
|
||||
ItemStack localItem = this.getItem(i);
|
||||
if (ItemStack.isSameItemSameTags(localItem, itemstack)) {
|
||||
this.moveItemsBetweenStacks(itemstack, localItem);
|
||||
if (itemstack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) {
|
||||
int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize());
|
||||
int j = Math.min(itemstack.getCount(), i - itemstack1.getCount());
|
||||
if (j > 0) {
|
||||
itemstack1.grow(j);
|
||||
itemstack.shrink(j);
|
||||
this.setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int i) {
|
||||
ItemStack itemstack = this.items.get(i);
|
||||
if (itemstack.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
} else {
|
||||
this.items.set(i, ItemStack.EMPTY);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int i, ItemStack itemstack) {
|
||||
this.items.set(i, itemstack);
|
||||
if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) {
|
||||
itemstack.setCount(this.getMaxStackSize());
|
||||
}
|
||||
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return this.owner.getEnderChestInventory().getContainerSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.items.stream().allMatch(ItemStack::isEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startOpen(Player player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopOpen(Player player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceItem(int i, ItemStack itemstack) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
this.items.clear();
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStackedContents(StackedContents stackedContents) {
|
||||
for (ItemStack itemstack : this.items) {
|
||||
stackedContents.accountStack(itemstack);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> removeAllItems() {
|
||||
List<ItemStack> list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList());
|
||||
this.clearContent();
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemType(Item item, int i) {
|
||||
ItemStack itemstack = new ItemStack(item, 0);
|
||||
|
||||
for(int j = this.getContainerSize() - 1; j >= 0; --j) {
|
||||
ItemStack localItem = this.getItem(j);
|
||||
if (localItem.getItem().equals(item)) {
|
||||
int k = i - itemstack.getCount();
|
||||
ItemStack splitItem = localItem.split(k);
|
||||
itemstack.grow(splitItem.getCount());
|
||||
if (itemstack.getCount() == i) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromTag(ListTag listTag) {
|
||||
for (int i = 0; i < this.getContainerSize(); ++i) {
|
||||
this.setItem(i, ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
for (int i = 0; i < listTag.size(); ++i) {
|
||||
CompoundTag compoundTag = listTag.getCompound(i);
|
||||
int j = compoundTag.getByte("Slot") & 255;
|
||||
if (j < this.getContainerSize()) {
|
||||
this.setItem(j, ItemStack.of(compoundTag));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-806
@@ -1,806 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_20_R1;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.CrashReportCategory;
|
||||
import net.minecraft.ReportedException;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.tags.DamageTypeTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.player.StackedContents;
|
||||
import net.minecraft.world.item.ArmorItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory {
|
||||
|
||||
private final CraftInventory inventory;
|
||||
private boolean playerOnline;
|
||||
private Player player;
|
||||
private NonNullList<ItemStack> items;
|
||||
private NonNullList<ItemStack> armor;
|
||||
private NonNullList<ItemStack> offhand;
|
||||
private List<NonNullList<ItemStack>> compartments;
|
||||
|
||||
public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) {
|
||||
super(PlayerDataManager.getHandle(bukkitPlayer));
|
||||
this.inventory = new CraftInventory(this);
|
||||
this.playerOnline = online;
|
||||
this.player = super.player;
|
||||
this.selected = player.getInventory().selected;
|
||||
this.items = this.player.getInventory().items;
|
||||
this.armor = this.player.getInventory().armor;
|
||||
this.offhand = this.player.getInventory().offhand;
|
||||
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
|
||||
if (this.playerOnline) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player offlinePlayer = this.player;
|
||||
Player onlinePlayer = PlayerDataManager.getHandle(player);
|
||||
onlinePlayer.getInventory().transaction.addAll(this.transaction);
|
||||
|
||||
// Set owner to new player.
|
||||
this.player = onlinePlayer;
|
||||
|
||||
// Set player's inventory contents to our modified contents.
|
||||
Inventory onlineInventory = onlinePlayer.getInventory();
|
||||
for (int i = 0; i < getContainerSize(); ++i) {
|
||||
onlineInventory.setItem(i, getRawItem(i));
|
||||
}
|
||||
onlineInventory.selected = this.selected;
|
||||
|
||||
// Set our item arrays to the new inventory's arrays.
|
||||
this.items = onlineInventory.items;
|
||||
this.armor = onlineInventory.armor;
|
||||
this.offhand = onlineInventory.offhand;
|
||||
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
|
||||
|
||||
// Add existing viewers to new viewer list.
|
||||
Inventory offlineInventory = offlinePlayer.getInventory();
|
||||
// Remove self from listing - player is always a viewer of their own inventory, prevent duplicates.
|
||||
offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity());
|
||||
onlineInventory.transaction.addAll(offlineInventory.transaction);
|
||||
|
||||
this.playerOnline = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CraftInventory getBukkitInventory() {
|
||||
return this.inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {
|
||||
this.playerOnline = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HumanEntity getPlayer() {
|
||||
return this.player.getBukkitEntity();
|
||||
}
|
||||
|
||||
private @NotNull ItemStack getRawItem(int i) {
|
||||
if (i < 0) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
NonNullList<ItemStack> list;
|
||||
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
|
||||
list = iterator.next();
|
||||
if (i < list.size()) {
|
||||
return list.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
private void setRawItem(int i, @NotNull ItemStack itemStack) {
|
||||
if (i < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
NonNullList<ItemStack> list;
|
||||
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
|
||||
list = iterator.next();
|
||||
if (i < list.size()) {
|
||||
list.set(i, itemStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record IndexedCompartment(@Nullable NonNullList<ItemStack> compartment, int index) {}
|
||||
|
||||
private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) {
|
||||
if (index < items.size()) {
|
||||
return new IndexedCompartment(items, getReversedItemSlotNum(index));
|
||||
}
|
||||
|
||||
index -= items.size();
|
||||
|
||||
if (index < armor.size()) {
|
||||
return new IndexedCompartment(armor, getReversedArmorSlotNum(index));
|
||||
}
|
||||
|
||||
index -= armor.size();
|
||||
|
||||
if (index < offhand.size()) {
|
||||
return new IndexedCompartment(offhand, index);
|
||||
}
|
||||
|
||||
index -= offhand.size();
|
||||
|
||||
return new IndexedCompartment(null, index);
|
||||
}
|
||||
|
||||
private int getReversedArmorSlotNum(final int i) {
|
||||
if (i == 0) {
|
||||
return 3;
|
||||
}
|
||||
if (i == 1) {
|
||||
return 2;
|
||||
}
|
||||
if (i == 2) {
|
||||
return 1;
|
||||
}
|
||||
if (i == 3) {
|
||||
return 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
private int getReversedItemSlotNum(final int i) {
|
||||
if (i >= 27) {
|
||||
return i - 27;
|
||||
}
|
||||
return i + 9;
|
||||
}
|
||||
|
||||
private boolean contains(Predicate<ItemStack> predicate) {
|
||||
return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getArmorContents() {
|
||||
return this.armor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity who) {
|
||||
this.player.getInventory().onOpen(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity who) {
|
||||
this.player.getInventory().onClose(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return this.player.getInventory().getViewers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryHolder getOwner() {
|
||||
return this.player.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return this.player.getInventory().getMaxStackSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int size) {
|
||||
this.player.getInventory().setMaxStackSize(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return this.player.getBukkitEntity().getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCustomName() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getSelected() {
|
||||
return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) {
|
||||
return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int canHold(ItemStack itemstack) {
|
||||
int remains = itemstack.getCount();
|
||||
|
||||
for (int i = 0; i < this.items.size(); ++i) {
|
||||
ItemStack itemstack1 = this.getRawItem(i);
|
||||
if (itemstack1.isEmpty()) {
|
||||
return itemstack.getCount();
|
||||
}
|
||||
|
||||
if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) {
|
||||
remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount();
|
||||
}
|
||||
|
||||
if (remains <= 0) {
|
||||
return itemstack.getCount();
|
||||
}
|
||||
}
|
||||
|
||||
ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size());
|
||||
if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) {
|
||||
remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount();
|
||||
}
|
||||
|
||||
return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFreeSlot() {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
if (this.items.get(i).isEmpty()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPickedItem(ItemStack itemstack) {
|
||||
int i = this.findSlotMatchingItem(itemstack);
|
||||
if (isHotbarSlot(i)) {
|
||||
this.selected = i;
|
||||
} else if (i == -1) {
|
||||
this.selected = this.getSuitableHotbarSlot();
|
||||
if (!this.items.get(this.selected).isEmpty()) {
|
||||
int j = this.getFreeSlot();
|
||||
if (j != -1) {
|
||||
this.items.set(j, this.items.get(this.selected));
|
||||
}
|
||||
}
|
||||
|
||||
this.items.set(this.selected, itemstack);
|
||||
} else {
|
||||
this.pickSlot(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pickSlot(int i) {
|
||||
this.selected = this.getSuitableHotbarSlot();
|
||||
ItemStack itemstack = this.items.get(this.selected);
|
||||
this.items.set(this.selected, this.items.get(i));
|
||||
this.items.set(i, itemstack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findSlotMatchingItem(ItemStack itemstack) {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findSlotMatchingUnusedItem(ItemStack itemStack) {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
ItemStack localItem = this.items.get(i);
|
||||
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSuitableHotbarSlot() {
|
||||
int i;
|
||||
int j;
|
||||
for(j = 0; j < 9; ++j) {
|
||||
i = (this.selected + j) % 9;
|
||||
if (this.items.get(i).isEmpty()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
for(j = 0; j < 9; ++j) {
|
||||
i = (this.selected + j) % 9;
|
||||
if (!this.items.get(i).isEnchanted()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return this.selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swapPaint(double d0) {
|
||||
if (d0 > 0.0D) {
|
||||
d0 = 1.0D;
|
||||
}
|
||||
|
||||
if (d0 < 0.0D) {
|
||||
d0 = -1.0D;
|
||||
}
|
||||
|
||||
this.selected = (int) (this.selected - d0);
|
||||
|
||||
while (this.selected < 0) {
|
||||
this.selected += 9;
|
||||
}
|
||||
|
||||
while(this.selected >= 9) {
|
||||
this.selected -= 9;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int clearOrCountMatchingItems(Predicate<ItemStack> predicate, int i, Container container) {
|
||||
byte b0 = 0;
|
||||
boolean flag = i == 0;
|
||||
int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag);
|
||||
j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag);
|
||||
ItemStack itemstack = this.player.containerMenu.getCarried();
|
||||
j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag);
|
||||
if (itemstack.isEmpty()) {
|
||||
this.player.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
private int addResource(ItemStack itemstack) {
|
||||
int i = this.getSlotWithRemainingSpace(itemstack);
|
||||
if (i == -1) {
|
||||
i = this.getFreeSlot();
|
||||
}
|
||||
|
||||
return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack);
|
||||
}
|
||||
|
||||
private int addResource(int i, ItemStack itemstack) {
|
||||
Item item = itemstack.getItem();
|
||||
int j = itemstack.getCount();
|
||||
ItemStack localItemStack = this.getRawItem(i);
|
||||
if (localItemStack.isEmpty()) {
|
||||
localItemStack = new ItemStack(item, 0);
|
||||
if (itemstack.hasTag()) {
|
||||
// hasTag ensures tag not null
|
||||
//noinspection ConstantConditions
|
||||
localItemStack.setTag(itemstack.getTag().copy());
|
||||
}
|
||||
|
||||
this.setRawItem(i, localItemStack);
|
||||
}
|
||||
|
||||
int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount());
|
||||
|
||||
if (k > this.getMaxStackSize() - localItemStack.getCount()) {
|
||||
k = this.getMaxStackSize() - localItemStack.getCount();
|
||||
}
|
||||
|
||||
if (k != 0) {
|
||||
j -= k;
|
||||
localItemStack.grow(k);
|
||||
localItemStack.setPopTime(5);
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSlotWithRemainingSpace(ItemStack itemstack) {
|
||||
if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) {
|
||||
return this.selected;
|
||||
} else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) {
|
||||
return 40;
|
||||
} else {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
for (int i = 0; i < compartment.size(); ++i) {
|
||||
if (!compartment.get(i).isEmpty()) {
|
||||
compartment.get(i).inventoryTick(this.player.level(), this.player, i, this.selected == i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(ItemStack itemStack) {
|
||||
return this.add(-1, itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(int i, ItemStack itemStack) {
|
||||
if (itemStack.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
try {
|
||||
if (itemStack.isDamaged()) {
|
||||
if (i == -1) {
|
||||
i = this.getFreeSlot();
|
||||
}
|
||||
|
||||
if (i >= 0) {
|
||||
this.items.set(i, itemStack.copy());
|
||||
this.items.get(i).setPopTime(5);
|
||||
itemStack.setCount(0);
|
||||
return true;
|
||||
} else if (this.player.getAbilities().instabuild) {
|
||||
itemStack.setCount(0);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
int j;
|
||||
do {
|
||||
j = itemStack.getCount();
|
||||
if (i == -1) {
|
||||
itemStack.setCount(this.addResource(itemStack));
|
||||
} else {
|
||||
itemStack.setCount(this.addResource(i, itemStack));
|
||||
}
|
||||
} while(!itemStack.isEmpty() && itemStack.getCount() < j);
|
||||
|
||||
if (itemStack.getCount() == j && this.player.getAbilities().instabuild) {
|
||||
itemStack.setCount(0);
|
||||
return true;
|
||||
} else {
|
||||
return itemStack.getCount() < j;
|
||||
}
|
||||
}
|
||||
} catch (Throwable var6) {
|
||||
CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory");
|
||||
CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added");
|
||||
crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem()));
|
||||
crashReportCategory.setDetail("Item data", itemStack.getDamageValue());
|
||||
crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString());
|
||||
throw new ReportedException(crashReport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeItemBackInInventory(ItemStack itemStack) {
|
||||
this.placeItemBackInInventory(itemStack, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeItemBackInInventory(ItemStack itemStack, boolean flag) {
|
||||
while(true) {
|
||||
if (!itemStack.isEmpty()) {
|
||||
int i = this.getSlotWithRemainingSpace(itemStack);
|
||||
if (i == -1) {
|
||||
i = this.getFreeSlot();
|
||||
}
|
||||
|
||||
if (i != -1) {
|
||||
int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount();
|
||||
if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) {
|
||||
((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
this.player.drop(itemStack, false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int rawIndex, final int j) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null
|
||||
|| indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeItem(ItemStack itemStack) {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
for (int i = 0; i < compartment.size(); ++i) {
|
||||
if (compartment.get(i) == itemStack) {
|
||||
compartment.set(i, ItemStack.EMPTY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int rawIndex) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY);
|
||||
|
||||
if (removed.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int rawIndex, final ItemStack itemStack) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null) {
|
||||
this.player.drop(itemStack, true);
|
||||
return;
|
||||
}
|
||||
|
||||
indexedCompartment.compartment().set(indexedCompartment.index(), itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDestroySpeed(BlockState blockState) {
|
||||
return this.items.get(this.selected).getDestroySpeed(blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListTag save(ListTag listTag) {
|
||||
for (int i = 0; i < this.items.size(); ++i) {
|
||||
if (!this.items.get(i).isEmpty()) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putByte("Slot", (byte)i);
|
||||
this.items.get(i).save(compoundTag);
|
||||
listTag.add(compoundTag);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < this.armor.size(); ++i) {
|
||||
if (!this.armor.get(i).isEmpty()) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putByte("Slot", (byte)(i + 100));
|
||||
this.armor.get(i).save(compoundTag);
|
||||
listTag.add(compoundTag);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < this.offhand.size(); ++i) {
|
||||
if (!this.offhand.get(i).isEmpty()) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putByte("Slot", (byte)(i + 150));
|
||||
this.offhand.get(i).save(compoundTag);
|
||||
listTag.add(compoundTag);
|
||||
}
|
||||
}
|
||||
|
||||
return listTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(ListTag listTag) {
|
||||
this.items.clear();
|
||||
this.armor.clear();
|
||||
this.offhand.clear();
|
||||
|
||||
for(int i = 0; i < listTag.size(); ++i) {
|
||||
CompoundTag compoundTag = listTag.getCompound(i);
|
||||
int j = compoundTag.getByte("Slot") & 255;
|
||||
ItemStack itemstack = ItemStack.of(compoundTag);
|
||||
if (!itemstack.isEmpty()) {
|
||||
if (j < this.items.size()) {
|
||||
this.items.set(j, itemstack);
|
||||
} else if (j >= 100 && j < this.armor.size() + 100) {
|
||||
this.armor.set(j - 100, itemstack);
|
||||
} else if (j >= 150 && j < this.offhand.size() + 150) {
|
||||
this.offhand.set(j - 150, itemstack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return 45;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return !contains(itemStack -> !itemStack.isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int rawIndex) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
return indexedCompartment.compartment().get(indexedCompartment.index());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getName() {
|
||||
return this.player.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getArmor(int index) {
|
||||
return this.armor.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) {
|
||||
if (damage > 0.0F) {
|
||||
damage /= 4.0F;
|
||||
if (damage < 1.0F) {
|
||||
damage = 1.0F;
|
||||
}
|
||||
|
||||
for (int index : armorIndices) {
|
||||
ItemStack itemstack = this.armor.get(index);
|
||||
if ((!damagesource.is(DamageTypeTags.IS_FIRE) || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) {
|
||||
itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropAll() {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
for (int i = 0; i < compartment.size(); ++i) {
|
||||
ItemStack itemstack = compartment.get(i);
|
||||
if (!itemstack.isEmpty()) {
|
||||
this.player.drop(itemstack, true, false);
|
||||
compartment.set(i, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
super.setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimesChanged() {
|
||||
return super.getTimesChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(ItemStack itemstack) {
|
||||
return contains(itemStack -> itemStack.isEmpty() && itemStack.is(itemstack.getItem()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(TagKey<Item> tagKey) {
|
||||
|
||||
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceWith(Inventory inventory) {
|
||||
Function<Integer, ItemStack> getter;
|
||||
|
||||
if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) {
|
||||
getter = specialPlayerInventory::getRawItem;
|
||||
} else {
|
||||
getter = inventory::getItem;
|
||||
}
|
||||
|
||||
for(int i = 0; i < this.getContainerSize(); ++i) {
|
||||
this.setRawItem(i, getter.apply(i));
|
||||
}
|
||||
|
||||
this.selected = inventory.selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
compartment.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStackedContents(StackedContents stackedContents) {
|
||||
for (ItemStack itemstack : this.items) {
|
||||
stackedContents.accountSimpleStack(itemstack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeFromSelected(boolean dropWholeStack) {
|
||||
ItemStack itemstack = this.getSelected();
|
||||
return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,16 +23,16 @@
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
<version>4.3.1</version>
|
||||
<version>5.1.5</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvadapter1_20_R1</artifactId>
|
||||
<name>OpenInvAdapter1_20_R1</name>
|
||||
<artifactId>openinvadapter1_21_R2</artifactId>
|
||||
<name>OpenInvAdapter1_21_R2</name>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<spigot.version>1.20-R0.1-SNAPSHOT</spigot.version>
|
||||
<maven.compiler.system>21</maven.compiler.system>
|
||||
<maven.compiler.release>21</maven.compiler.release>
|
||||
<spigot.version>1.21.3-R0.1-SNAPSHOT</spigot.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -51,10 +51,9 @@
|
||||
<dependency>
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvplugincore</artifactId>
|
||||
<artifactId>openinvcommon</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -65,9 +64,6 @@
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2;
|
||||
|
||||
import com.lishid.openinv.internal.Accessor;
|
||||
import com.lishid.openinv.internal.IAnySilentContainer;
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.AnySilentContainer;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenEnderChest;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.Placeholders;
|
||||
import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import net.minecraft.world.Container;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class InternalAccessor implements Accessor {
|
||||
|
||||
private final @NotNull Logger logger;
|
||||
private final @NotNull PlayerManager manager;
|
||||
private final @NotNull AnySilentContainer anySilentContainer;
|
||||
|
||||
public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) {
|
||||
this.logger = logger;
|
||||
manager = new PlayerManager(logger);
|
||||
anySilentContainer = new AnySilentContainer(logger, lang);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PlayerManager getPlayerManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IAnySilentContainer getAnySilentContainer() {
|
||||
return anySilentContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) {
|
||||
return new OpenInventory(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) {
|
||||
return new OpenEnderChest(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ISpecialInventory> @Nullable T get(@NotNull Inventory bukkitInventory, @NotNull Class<T> clazz) {
|
||||
if (!(bukkitInventory instanceof CraftInventory craftInventory)) {
|
||||
return null;
|
||||
}
|
||||
Container container = craftInventory.getInventory();
|
||||
if (clazz.isInstance(container)) {
|
||||
return clazz.cast(container);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(@NotNull ConfigurationSection config) {
|
||||
ConfigurationSection placeholders = config.getConfigurationSection("placeholders");
|
||||
if (placeholders != null) {
|
||||
try {
|
||||
Placeholders.load(placeholders);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+21
-54
@@ -14,21 +14,19 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_20_R1;
|
||||
package com.lishid.openinv.internal.v1_21_R2.container;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.internal.IAnySilentContainer;
|
||||
import com.lishid.openinv.internal.AnySilentContainerBase;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.menu.OpenChestMenu;
|
||||
import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager;
|
||||
import com.lishid.openinv.util.ReflectionHelper;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.logging.Logger;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.ServerPlayerGameMode;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.SimpleMenuProvider;
|
||||
import net.minecraft.world.entity.monster.Shulker;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.PlayerEnderChestContainer;
|
||||
@@ -41,30 +39,31 @@ import net.minecraft.world.level.block.TrappedChestBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class AnySilentContainer implements IAnySilentContainer {
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class AnySilentContainer extends AnySilentContainerBase {
|
||||
|
||||
private final @NotNull Logger logger;
|
||||
private final @NotNull LanguageManager lang;
|
||||
private @Nullable Field serverPlayerGameModeGameType;
|
||||
|
||||
public AnySilentContainer() {
|
||||
public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) {
|
||||
this.logger = logger;
|
||||
this.lang = lang;
|
||||
try {
|
||||
try {
|
||||
this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b");
|
||||
this.serverPlayerGameModeGameType.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
|
||||
logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!");
|
||||
logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues");
|
||||
logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail.");
|
||||
@@ -72,43 +71,11 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class);
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
|
||||
logger.warning("Unable to directly write player game mode! SilentContainer will fail.");
|
||||
logger.log(java.util.logging.Level.WARNING, "Error obtaining GameType field", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox) {
|
||||
org.bukkit.World bukkitWorld = shulkerBox.getWorld();
|
||||
if (!(bukkitWorld instanceof CraftWorld)) {
|
||||
bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID());
|
||||
}
|
||||
|
||||
if (!(bukkitWorld instanceof CraftWorld craftWorld)) {
|
||||
Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!");
|
||||
OpenInv.getPlugin(OpenInv.class).getLogger().log(java.util.logging.Level.WARNING, exception.getMessage(), exception);
|
||||
return false;
|
||||
}
|
||||
|
||||
final ServerLevel world = craftWorld.getHandle();
|
||||
final BlockPos blockPosition = new BlockPos(shulkerBox.getX(), shulkerBox.getY(), shulkerBox.getZ());
|
||||
final BlockEntity tile = world.getBlockEntity(blockPosition);
|
||||
|
||||
if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity)
|
||||
|| shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockState blockState = world.getBlockState(blockPosition);
|
||||
|
||||
// See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen
|
||||
AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F)
|
||||
.move(blockPosition)
|
||||
.deflate(1.0E-6D);
|
||||
return !world.noCollision(boundingBox);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activateContainer(
|
||||
@NotNull final Player bukkitPlayer,
|
||||
@@ -122,7 +89,7 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
return true;
|
||||
}
|
||||
|
||||
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
|
||||
ServerPlayer player = PlayerManager.getHandle(bukkitPlayer);
|
||||
|
||||
final net.minecraft.world.level.Level level = player.level();
|
||||
final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
|
||||
@@ -137,10 +104,10 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
PlayerEnderChestContainer enderChest = player.getEnderChestInventory();
|
||||
enderChest.setActiveChest(enderChestTile);
|
||||
player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> {
|
||||
MenuType<?> containers = PlayerDataManager.getContainers(enderChest.getContainerSize());
|
||||
MenuType<?> containers = OpenChestMenu.getChestMenuType(enderChest.getContainerSize());
|
||||
int rows = enderChest.getContainerSize() / 9;
|
||||
return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows);
|
||||
}, Component.translatable(("container.enderchest"))));
|
||||
}, Component.translatable("container.enderchest")));
|
||||
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
|
||||
return true;
|
||||
}
|
||||
@@ -158,7 +125,7 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true);
|
||||
|
||||
if (menuProvider == null) {
|
||||
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
|
||||
lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -190,7 +157,7 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
|
||||
if (blockEntity instanceof RandomizableContainerBlockEntity lootable) {
|
||||
if (lootable.lootTable != null) {
|
||||
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
|
||||
lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -208,7 +175,7 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
|
||||
ServerPlayer player = PlayerManager.getHandle(bukkitPlayer);
|
||||
|
||||
// Force game mode change without informing plugins or players.
|
||||
// Regular game mode set calls GameModeChangeEvent and is cancellable.
|
||||
@@ -236,7 +203,7 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
this.serverPlayerGameModeGameType.setAccessible(true);
|
||||
this.serverPlayerGameModeGameType.set(player.gameMode, gameMode);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e);
|
||||
}
|
||||
}
|
||||
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.InternalOwned;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.menu.OpenEnderChestMenu;
|
||||
import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.player.StackedItemContents;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.StackedContentsCompatible;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OpenEnderChest implements Container, StackedContentsCompatible, InternalOwned<ServerPlayer>,
|
||||
ISpecialEnderChest {
|
||||
|
||||
private CraftInventory inventory;
|
||||
private @NotNull ServerPlayer owner;
|
||||
private NonNullList<ItemStack> items;
|
||||
private int maxStack = 64;
|
||||
private final List<HumanEntity> transaction = new ArrayList<>();
|
||||
|
||||
public OpenEnderChest(@NotNull org.bukkit.entity.Player player) {
|
||||
this.owner = PlayerManager.getHandle(player);
|
||||
this.items = owner.getEnderChestInventory().items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ServerPlayer getOwnerHandle() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() {
|
||||
if (inventory == null) {
|
||||
inventory = new CraftInventory(this) {
|
||||
@Override
|
||||
public @NotNull InventoryType getType() {
|
||||
return InventoryType.ENDER_CHEST;
|
||||
}
|
||||
};
|
||||
}
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
|
||||
owner = PlayerManager.getHandle(player);
|
||||
NonNullList<ItemStack> activeItems = owner.getEnderChestInventory().items;
|
||||
|
||||
// Guard against size changing. Theoretically on Purpur all row variations still have 6 rows internally.
|
||||
int max = Math.min(items.size(), activeItems.size());
|
||||
for (int index = 0; index < max; ++index) {
|
||||
activeItems.set(index, items.get(index));
|
||||
}
|
||||
|
||||
items = activeItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.entity.Player getPlayer() {
|
||||
return owner.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return items.stream().allMatch(ItemStack::isEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
return index >= 0 && index < items.size() ? items.get(index) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int index, int amount) {
|
||||
ItemStack itemstack = ContainerHelper.removeItem(items, index, amount);
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
setChanged();
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int index) {
|
||||
return index >= 0 && index < items.size() ? items.set(index, ItemStack.EMPTY) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, ItemStack itemStack) {
|
||||
if (index >= 0 && index < items.size()) {
|
||||
items.set(index, itemStack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return maxStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
this.owner.getEnderChestInventory().setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity craftHumanEntity) {
|
||||
transaction.add(craftHumanEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity craftHumanEntity) {
|
||||
transaction.remove(craftHumanEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.entity.Player getOwner() {
|
||||
return getPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int size) {
|
||||
maxStack = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Location getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
items.clear();
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStackedContents(StackedItemContents stackedContents) {
|
||||
for (ItemStack itemstack : items) {
|
||||
stackedContents.accountStack(itemstack);
|
||||
}
|
||||
}
|
||||
|
||||
public Component getTitle() {
|
||||
return Component.translatableWithFallback("openinv.container.enderchest.prefix", "", owner.getName())
|
||||
.append(Component.translatable("container.enderchest"))
|
||||
.append(Component.translatableWithFallback("openinv.container.enderchest.suffix", " - %s", owner.getName()));
|
||||
}
|
||||
|
||||
public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) {
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
return new OpenEnderChestMenu(this, serverPlayer, i, viewOnly);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+364
@@ -0,0 +1,364 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.internal.InternalOwned;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenPlayerInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.menu.OpenInventoryMenu;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.Content;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentCrafting;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentCraftingResult;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentCursor;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentDrop;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentEquipment;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentList;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentOffHand;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentViewOnly;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotViewOnly;
|
||||
import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OpenInventory implements Container, InternalOwned<ServerPlayer>, ISpecialPlayerInventory {
|
||||
|
||||
private final List<Content> slots;
|
||||
private final int size;
|
||||
private ServerPlayer owner;
|
||||
private int maxStackSize = 99;
|
||||
private CraftInventory bukkitEntity;
|
||||
public List<HumanEntity> transaction = new ArrayList<>();
|
||||
|
||||
public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) {
|
||||
owner = PlayerManager.getHandle(bukkitPlayer);
|
||||
|
||||
// Get total size, rounding up to nearest 9 for client compatibility.
|
||||
int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1;
|
||||
size = ((int) Math.ceil(rawSize / 9.0)) * 9;
|
||||
|
||||
slots = NonNullList.withSize(size, new ContentViewOnly(owner));
|
||||
setupSlots();
|
||||
}
|
||||
|
||||
private void setupSlots() {
|
||||
// Top of inventory: Regular contents.
|
||||
int nextIndex = addMainInventory();
|
||||
|
||||
// If inventory is expected size, we can arrange slots to be pretty.
|
||||
Inventory ownerInv = owner.getInventory();
|
||||
if (ownerInv.items.size() == 36
|
||||
&& ownerInv.armor.size() == 4
|
||||
&& ownerInv.offhand.size() == 1
|
||||
&& owner.inventoryMenu.getCraftSlots().getContainerSize() == 4) {
|
||||
// Armor slots: Bottom left.
|
||||
addArmor(36);
|
||||
// Off-hand: Below chestplate.
|
||||
addOffHand(46);
|
||||
// Drop slot: Bottom right.
|
||||
slots.set(53, new ContentDrop(owner));
|
||||
// Cursor slot: Above drop.
|
||||
slots.set(44, new ContentCursor(owner));
|
||||
|
||||
// Crafting is displayed in the bottom right corner.
|
||||
// As we're using the pretty view, this is a 3x2.
|
||||
addCrafting(41, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise we'll just add elements linearly.
|
||||
nextIndex = addArmor(nextIndex);
|
||||
nextIndex = addOffHand(nextIndex);
|
||||
nextIndex = addCrafting(nextIndex, false);
|
||||
slots.set(nextIndex, new ContentCursor(owner));
|
||||
// Drop slot last.
|
||||
slots.set(slots.size() - 1, new ContentDrop(owner));
|
||||
}
|
||||
|
||||
private int addMainInventory() {
|
||||
int listSize = owner.getInventory().items.size();
|
||||
// Hotbar slots are 0-8. We want those to appear on the bottom of the inventory like a normal player inventory,
|
||||
// so everything else needs to move up a row.
|
||||
int hotbarDiff = listSize - 9;
|
||||
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
|
||||
InventoryType.SlotType type;
|
||||
int invIndex;
|
||||
if (localIndex < hotbarDiff) {
|
||||
invIndex = localIndex + 9;
|
||||
type = InventoryType.SlotType.CONTAINER;
|
||||
} else {
|
||||
type = InventoryType.SlotType.QUICKBAR;
|
||||
invIndex = localIndex - hotbarDiff;
|
||||
}
|
||||
|
||||
slots.set(localIndex, new ContentList(owner, invIndex, type) {
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
items = holder.getInventory().items;
|
||||
}
|
||||
});
|
||||
}
|
||||
return listSize;
|
||||
}
|
||||
|
||||
private int addArmor(int startIndex) {
|
||||
int listSize = owner.getInventory().armor.size();
|
||||
|
||||
for (int i = 0; i < listSize; ++i) {
|
||||
// Armor slots go bottom to top; boots are slot 0, helmet is slot 3.
|
||||
// Since we have to display horizontally due to space restrictions,
|
||||
// making the left side the "top" is more user-friendly.
|
||||
int armorIndex;
|
||||
EquipmentSlot slot;
|
||||
switch (i) {
|
||||
case 3 -> {
|
||||
armorIndex = 0;
|
||||
slot = EquipmentSlot.FEET;
|
||||
}
|
||||
case 2 -> {
|
||||
armorIndex = 1;
|
||||
slot = EquipmentSlot.LEGS;
|
||||
}
|
||||
case 1 -> {
|
||||
armorIndex = 2;
|
||||
slot = EquipmentSlot.CHEST;
|
||||
}
|
||||
case 0 -> {
|
||||
armorIndex = 3;
|
||||
slot = EquipmentSlot.HEAD;
|
||||
}
|
||||
default -> {
|
||||
// In the event that new armor slots are added, they can be placed at the end.
|
||||
armorIndex = i;
|
||||
slot = EquipmentSlot.MAINHAND;
|
||||
}
|
||||
}
|
||||
|
||||
slots.set(startIndex + i, new ContentEquipment(owner, armorIndex, slot));
|
||||
}
|
||||
|
||||
return startIndex + listSize;
|
||||
}
|
||||
|
||||
private int addOffHand(int startIndex) {
|
||||
int listSize = owner.getInventory().offhand.size();
|
||||
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
|
||||
slots.set(startIndex + localIndex, new ContentOffHand(owner, localIndex));
|
||||
}
|
||||
return startIndex + listSize;
|
||||
}
|
||||
|
||||
private int addCrafting(int startIndex, boolean pretty) {
|
||||
int listSize = owner.inventoryMenu.getCraftSlots().getContents().size();
|
||||
pretty &= listSize == 4;
|
||||
|
||||
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
|
||||
// Pretty display is a 2x2 rather than linear.
|
||||
// If index is in top row, grid is not 2x2, or pretty is disabled, just use current index.
|
||||
// Otherwise, subtract 2 and add 9 to start in the same position on the next row.
|
||||
int modIndex = startIndex + (localIndex < 2 || !pretty ? localIndex : localIndex + 7);
|
||||
|
||||
slots.set(modIndex, new ContentCrafting(owner, localIndex));
|
||||
}
|
||||
|
||||
if (pretty) {
|
||||
slots.set(startIndex + 2, new ContentViewOnly(owner) {
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotViewOnly(container, slot, x, y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return Placeholders.craftingOutput;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
slots.set(startIndex + 11, new ContentCraftingResult(owner));
|
||||
}
|
||||
|
||||
return startIndex + listSize;
|
||||
}
|
||||
|
||||
public Slot getMenuSlot(int index, int x, int y) {
|
||||
return slots.get(index).asSlot(this, index, x, y);
|
||||
}
|
||||
|
||||
public InventoryType.SlotType getSlotType(int index) {
|
||||
return slots.get(index).getSlotType();
|
||||
}
|
||||
|
||||
public @NotNull Component getTitle(@Nullable ServerPlayer viewer) {
|
||||
MutableComponent component = Component.empty();
|
||||
// Prefix for use with custom bitmap image fonts.
|
||||
if (owner.equals(viewer)) {
|
||||
component.append(
|
||||
Component.translatableWithFallback("openinv.container.inventory.self", "")
|
||||
.withStyle(style -> style
|
||||
.withFont(ResourceLocation.parse("openinv:font/inventory"))
|
||||
.withColor(ChatFormatting.WHITE)));
|
||||
} else {
|
||||
component.append(
|
||||
Component.translatableWithFallback("openinv.container.inventory.other", "")
|
||||
.withStyle(style -> style
|
||||
.withFont(ResourceLocation.parse("openinv:font/inventory"))
|
||||
.withColor(ChatFormatting.WHITE)));
|
||||
}
|
||||
// Normal title: "Inventory - OwnerName"
|
||||
component.append(Component.translatableWithFallback("openinv.container.inventory.prefix", "", owner.getName()))
|
||||
.append(Component.translatable("container.inventory"))
|
||||
.append(Component.translatableWithFallback("openinv.container.inventory.suffix", " - %s", owner.getName()));
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerPlayer getOwnerHandle() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() {
|
||||
if (bukkitEntity == null) {
|
||||
bukkitEntity = new OpenPlayerInventory(this);
|
||||
}
|
||||
return bukkitEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
|
||||
ServerPlayer newOwner = PlayerManager.getHandle(player);
|
||||
// Only transfer regular inventory - crafting and cursor slots are transient.
|
||||
newOwner.getInventory().replaceWith(owner.getInventory());
|
||||
owner = newOwner;
|
||||
// Update slots to point to new inventory.
|
||||
slots.forEach(slot -> slot.setHolder(newOwner));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {}
|
||||
|
||||
@Override
|
||||
public boolean isInUse() {
|
||||
return !transaction.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.entity.Player getPlayer() {
|
||||
return getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return slots.stream().map(Content::get).allMatch(ItemStack::isEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
return slots.get(index).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int index, int amount) {
|
||||
return slots.get(index).removePartial(amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int index) {
|
||||
return slots.get(index).remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, ItemStack itemStack) {
|
||||
slots.get(index).set(itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return maxStackSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int maxStackSize) {
|
||||
this.maxStackSize = maxStackSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
NonNullList<ItemStack> contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY);
|
||||
for (int i = 0; i < getContainerSize(); ++i) {
|
||||
contents.set(i, getItem(i));
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity viewer) {
|
||||
transaction.add(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity viewer) {
|
||||
transaction.remove(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.entity.Player getOwner() {
|
||||
return owner.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return owner.getBukkitEntity().getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
owner.getInventory().clearContent();
|
||||
owner.inventoryMenu.getCraftSlots().clearContent();
|
||||
owner.inventoryMenu.slotsChanged(owner.inventoryMenu.getCraftSlots());
|
||||
owner.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) {
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
return new OpenInventoryMenu(this, serverPlayer, i, viewOnly);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.TagParser;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.component.CustomModelData;
|
||||
import net.minecraft.world.item.component.DyedItemColor;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraft.world.level.block.entity.BannerPattern;
|
||||
import net.minecraft.world.level.block.entity.BannerPatternLayers;
|
||||
import net.minecraft.world.level.block.entity.BannerPatterns;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.CraftRegistry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class Placeholders {
|
||||
|
||||
private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(9999);
|
||||
public static final @NotNull EnumMap<GameType, ItemStack> BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class);
|
||||
public static @NotNull ItemStack craftingOutput = defaultCraftingOutput();
|
||||
public static @NotNull ItemStack cursor = defaultCursor();
|
||||
public static @NotNull ItemStack drop = defaultDrop();
|
||||
public static @NotNull ItemStack emptyHelmet = getEmptyArmor(Items.LEATHER_HELMET);
|
||||
public static @NotNull ItemStack emptyChestplate = getEmptyArmor(Items.LEATHER_CHESTPLATE);
|
||||
public static @NotNull ItemStack emptyLeggings = getEmptyArmor(Items.LEATHER_LEGGINGS);
|
||||
public static @NotNull ItemStack emptyBoots = getEmptyArmor(Items.LEATHER_BOOTS);
|
||||
public static @NotNull ItemStack emptyOffHand = getEmptyShield();
|
||||
public static @NotNull ItemStack notSlot = defaultNotSlot();
|
||||
public static @NotNull ItemStack blockedOffline = defaultBlockedOffline();
|
||||
|
||||
static {
|
||||
for (GameType type : GameType.values()) {
|
||||
// Barrier: "Not available - Creative" etc.
|
||||
ItemStack typeItem = new ItemStack(Items.BARRIER);
|
||||
typeItem.set(
|
||||
DataComponents.ITEM_NAME,
|
||||
Component.translatable("options.narrator.notavailable").append(" - ").append(type.getShortDisplayName()));
|
||||
BLOCKED_GAME_TYPE.put(type, typeItem);
|
||||
}
|
||||
}
|
||||
|
||||
public static void load(@NotNull ConfigurationSection section) throws Exception {
|
||||
craftingOutput = parse(section, "crafting-output", craftingOutput);
|
||||
cursor = parse(section, "cursor", cursor);
|
||||
drop = parse(section, "drop", drop);
|
||||
emptyHelmet = parse(section, "empty-helmet", emptyHelmet);
|
||||
emptyChestplate = parse(section, "empty-chestplate", emptyChestplate);
|
||||
emptyLeggings = parse(section, "empty-leggings", emptyLeggings);
|
||||
emptyBoots = parse(section, "empty-boots", emptyBoots);
|
||||
emptyOffHand = parse(section, "empty-off-hand", emptyOffHand);
|
||||
notSlot = parse(section, "not-a-slot", notSlot);
|
||||
blockedOffline = parse(section, "blocked.offline", blockedOffline);
|
||||
BLOCKED_GAME_TYPE.put(GameType.CREATIVE, parse(section, "blocked.creative", BLOCKED_GAME_TYPE.get(GameType.CREATIVE)));
|
||||
BLOCKED_GAME_TYPE.put(GameType.SPECTATOR, parse(section, "blocked.spectator", BLOCKED_GAME_TYPE.get(GameType.SPECTATOR)));
|
||||
}
|
||||
|
||||
private static @NotNull ItemStack parse(
|
||||
@NotNull ConfigurationSection section,
|
||||
@NotNull String path,
|
||||
@NotNull ItemStack defaultStack) throws Exception {
|
||||
String itemText = section.getString(path);
|
||||
|
||||
if (itemText == null) {
|
||||
return defaultStack;
|
||||
}
|
||||
|
||||
CompoundTag compoundTag = TagParser.parseTag(itemText);
|
||||
Optional<ItemStack> parsed = ItemStack.parse(CraftRegistry.getMinecraftRegistry(), compoundTag);
|
||||
return parsed.filter(itemStack -> !itemStack.isEmpty()).orElse(defaultStack);
|
||||
}
|
||||
|
||||
public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) {
|
||||
if (serverPlayer.connection == null || serverPlayer.connection.isDisconnected()) {
|
||||
return blockedOffline;
|
||||
}
|
||||
|
||||
return BLOCKED_GAME_TYPE.getOrDefault(serverPlayer.gameMode.getGameModeForPlayer(), ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
private static ItemStack defaultCraftingOutput() {
|
||||
// Crafting table: "Crafting"
|
||||
ItemStack itemStack = new ItemStack(Items.CRAFTING_TABLE);
|
||||
itemStack.set(DataComponents.ITEM_NAME, Component.translatable("container.crafting"));
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultCursor() {
|
||||
// Cursor-like banner with no tooltip
|
||||
ItemStack itemStack = new ItemStack(Items.WHITE_BANNER);
|
||||
RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry();
|
||||
Registry<BannerPattern> bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN);
|
||||
BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT).value();
|
||||
BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT).value();
|
||||
BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER).value();
|
||||
itemStack.set(DataComponents.BANNER_PATTERNS,
|
||||
new BannerPatternLayers(List.of(
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY))));
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultDrop() {
|
||||
// Dropper: "Drop Selected Item"
|
||||
ItemStack itemStack = new ItemStack(Items.DROPPER);
|
||||
// Note: translatable component, not keybind component! We want the text identifying the keybind, not the key.
|
||||
itemStack.set(DataComponents.ITEM_NAME, Component.translatable("key.drop"));
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack getEmptyArmor(ItemLike item) {
|
||||
// Inventory-background-grey-ish leather armor with no tooltip
|
||||
ItemStack itemStack = new ItemStack(item);
|
||||
DyedItemColor color = new DyedItemColor(0xC8C8C8, false);
|
||||
itemStack.set(DataComponents.DYED_COLOR, color);
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack getEmptyShield() {
|
||||
ItemStack itemStack = new ItemStack(Items.SHIELD);
|
||||
itemStack.set(DataComponents.BASE_COLOR, DyeColor.MAGENTA);
|
||||
RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry();
|
||||
Registry<BannerPattern> bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN);
|
||||
BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL).value();
|
||||
BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT).value();
|
||||
BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT).value();
|
||||
BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT).value();
|
||||
BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT).value();
|
||||
itemStack.set(DataComponents.BANNER_PATTERNS,
|
||||
new BannerPatternLayers(List.of(
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfLeft), DyeColor.BLACK),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topLeft), DyeColor.MAGENTA),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK))));
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultNotSlot() {
|
||||
// White pane with no tooltip
|
||||
ItemStack itemStack = new ItemStack(Items.WHITE_STAINED_GLASS_PANE);
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultBlockedOffline() {
|
||||
// Barrier: "Not available - Offline"
|
||||
ItemStack itemStack = new ItemStack(Items.BARRIER);
|
||||
itemStack.set(DataComponents.ITEM_NAME,
|
||||
Component.translatable("options.narrator.notavailable")
|
||||
.append(Component.literal(" - "))
|
||||
.append(Component.translatable("gui.socialInteractions.status_offline")));
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private Placeholders() {
|
||||
throw new IllegalStateException("Cannot create instance of utility class.");
|
||||
}
|
||||
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.bukkit;
|
||||
|
||||
import com.lishid.openinv.internal.ViewOnly;
|
||||
import net.minecraft.world.Container;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
* A locked down "empty" inventory that rejects plugin interaction.
|
||||
*/
|
||||
public class OpenDummyInventory extends CraftInventory implements ViewOnly {
|
||||
|
||||
private final InventoryType type;
|
||||
|
||||
public OpenDummyInventory(Container inventory, InventoryType type) {
|
||||
super(inventory);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getItem(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, @Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> addItem(@NotNull ItemStack... items) throws IllegalArgumentException {
|
||||
return arrayToHashMap(items);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> removeItem(@NotNull ItemStack... items) throws IllegalArgumentException {
|
||||
return arrayToHashMap(items);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
private static @NotNull HashMap<Integer, ItemStack> arrayToHashMap(@NotNull ItemStack[] items) {
|
||||
HashMap<Integer, ItemStack> ignored = new HashMap<>();
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
ignored.put(index, items[index]);
|
||||
}
|
||||
return ignored;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getContents() {
|
||||
return new ItemStack[getSize()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContents(@NotNull ItemStack[] items) throws IllegalArgumentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getStorageContents() {
|
||||
return new ItemStack[getSize()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStorageContents(@NotNull ItemStack[] items) throws IllegalArgumentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull Material material) throws IllegalArgumentException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@Nullable ItemStack item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull Material material, int amount) throws IllegalArgumentException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@Nullable ItemStack item, int amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAtLeast(@Nullable ItemStack item, int amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> all(
|
||||
@NotNull Material material) throws IllegalArgumentException {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> all(@Nullable ItemStack item) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int first(@NotNull Material material) throws IllegalArgumentException {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int first(@NotNull ItemStack item) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int firstEmpty() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull Material material) throws IllegalArgumentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(int index) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ListIterator<ItemStack> iterator() {
|
||||
return Collections.emptyListIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ListIterator<ItemStack> iterator(int index) {
|
||||
return Collections.emptyListIterator();
|
||||
}
|
||||
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.bukkit;
|
||||
|
||||
import net.minecraft.world.Container;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OpenDummyPlayerInventory extends OpenDummyInventory implements PlayerInventory {
|
||||
|
||||
public OpenDummyPlayerInventory(Container inventory) {
|
||||
super(inventory, InventoryType.PLAYER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HumanEntity getHolder() {
|
||||
return (HumanEntity) super.getHolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getArmorContents() {
|
||||
return new ItemStack[4];
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getExtraContents() {
|
||||
return new ItemStack[4];
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getHelmet() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getChestplate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getLeggings() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getBoots() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(@NotNull EquipmentSlot slot, @Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getItem(@NotNull EquipmentSlot slot) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArmorContents(@Nullable ItemStack[] items) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtraContents(@Nullable ItemStack[] items) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHelmet(@Nullable ItemStack helmet) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChestplate(@Nullable ItemStack chestplate) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeggings(@Nullable ItemStack leggings) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoots(@Nullable ItemStack boots) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInMainHand() {
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInMainHand(@Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInOffHand() {
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInOffHand(@Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInHand() {
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInHand(@Nullable ItemStack stack) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeldItemSlot() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeldItemSlot(int slot) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.bukkit;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OpenPlayerInventory extends CraftInventory implements PlayerInventory {
|
||||
|
||||
public OpenPlayerInventory(@NotNull OpenInventory inventory) {
|
||||
super(inventory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull OpenInventory getInventory() {
|
||||
return (OpenInventory) super.getInventory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().getContents());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContents(ItemStack[] items) {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
int size = internal.getContainerSize();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
|
||||
for (int index = 0; index < size; ++index) {
|
||||
if (index < items.length) {
|
||||
internal.setItem(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
} else {
|
||||
internal.setItem(index, net.minecraft.world.item.ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getStorageContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStorageContents(ItemStack[] items) throws IllegalArgumentException {
|
||||
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().items;
|
||||
int size = list.size();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
list.set(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType getType() {
|
||||
return InventoryType.PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Player getHolder() {
|
||||
return getInventory().getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getArmorContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().armor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArmorContents(@Nullable ItemStack[] items) {
|
||||
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().armor;
|
||||
int size = list.size();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
list.set(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getExtraContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtraContents(@Nullable ItemStack[] items) {
|
||||
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().offhand;
|
||||
int size = list.size();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
list.set(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getHelmet() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.HEAD.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHelmet(@Nullable ItemStack helmet) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.HEAD.getIndex(), CraftItemStack.asNMSCopy(helmet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getChestplate() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.HEAD.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChestplate(@Nullable ItemStack chestplate) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.CHEST.getIndex(), CraftItemStack.asNMSCopy(chestplate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getLeggings() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.LEGS.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeggings(@Nullable ItemStack leggings) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.LEGS.getIndex(), CraftItemStack.asNMSCopy(leggings));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getBoots() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.FEET.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoots(@Nullable ItemStack boots) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.FEET.getIndex(), CraftItemStack.asNMSCopy(boots));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInMainHand() {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
return CraftItemStack.asCraftMirror(internal.getItem(internal.selected));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInMainHand(@Nullable ItemStack item) {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
internal.setItem(internal.selected, CraftItemStack.asNMSCopy(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInOffHand() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand.getFirst());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInOffHand(@Nullable ItemStack item) {
|
||||
getInventory().getOwnerHandle().getInventory().offhand.set(0, CraftItemStack.asNMSCopy(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInHand() {
|
||||
return getItemInMainHand();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInHand(@Nullable ItemStack stack) {
|
||||
setItemInMainHand(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeldItemSlot() {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
return internal.items.size() - 9 + internal.selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeldItemSlot(int slot) {
|
||||
slot %= 9;
|
||||
getInventory().getOwnerHandle().getInventory().selected = slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) {
|
||||
return switch (slot) {
|
||||
case HAND -> getItemInMainHand();
|
||||
case OFF_HAND -> getItemInOffHand();
|
||||
case FEET -> getBoots();
|
||||
case LEGS -> getLeggings();
|
||||
case CHEST -> getChestplate();
|
||||
case HEAD -> getHelmet();
|
||||
default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(@NotNull org.bukkit.inventory.EquipmentSlot slot, @Nullable ItemStack item) {
|
||||
switch (slot) {
|
||||
case HAND -> setItemInMainHand(item);
|
||||
case OFF_HAND -> setItemInOffHand(item);
|
||||
case FEET -> setBoots(item);
|
||||
case LEGS -> setLeggings(item);
|
||||
case CHEST -> setChestplate(item);
|
||||
case HEAD -> setHelmet(item);
|
||||
default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.bukkit;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class OpenPlayerInventorySelf extends OpenPlayerInventory {
|
||||
|
||||
private final int offset;
|
||||
|
||||
public OpenPlayerInventorySelf(@NotNull OpenInventory inventory, int offset) {
|
||||
super(inventory);
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
return super.getItem(offset + index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, ItemStack item) {
|
||||
super.setItem(offset + index, item);
|
||||
}
|
||||
|
||||
}
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.menu;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.InternalOwned;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenDummyInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotPlaceholder;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotViewOnly;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.inventory.ClickType;
|
||||
import net.minecraft.world.inventory.ContainerData;
|
||||
import net.minecraft.world.inventory.ContainerListener;
|
||||
import net.minecraft.world.inventory.ContainerSynchronizer;
|
||||
import net.minecraft.world.inventory.DataSlot;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventoryView;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}.
|
||||
*/
|
||||
public abstract class OpenChestMenu<T extends Container & ISpecialInventory & InternalOwned<ServerPlayer>>
|
||||
extends AbstractContainerMenu {
|
||||
|
||||
protected static final int BOTTOM_INVENTORY_SIZE = 36;
|
||||
|
||||
protected final T container;
|
||||
protected final ServerPlayer viewer;
|
||||
protected final boolean viewOnly;
|
||||
protected final boolean ownContainer;
|
||||
protected final int topSize;
|
||||
private CraftInventoryView<OpenChestMenu<T>, Inventory> bukkitEntity;
|
||||
// Syncher fields
|
||||
private @Nullable ContainerSynchronizer synchronizer;
|
||||
private final List<DataSlot> dataSlots = new ArrayList<>();
|
||||
private final IntList remoteDataSlots = new IntArrayList();
|
||||
private final List<ContainerListener> containerListeners = new ArrayList<>();
|
||||
private ItemStack remoteCarried = ItemStack.EMPTY;
|
||||
private boolean suppressRemoteUpdates;
|
||||
|
||||
protected OpenChestMenu(
|
||||
@NotNull MenuType<ChestMenu> type,
|
||||
int containerCounter,
|
||||
@NotNull T container,
|
||||
@NotNull ServerPlayer viewer,
|
||||
boolean viewOnly) {
|
||||
super(type, containerCounter);
|
||||
this.container = container;
|
||||
this.viewer = viewer;
|
||||
this.viewOnly = viewOnly;
|
||||
ownContainer = container.getOwnerHandle().equals(viewer);
|
||||
topSize = getTopSize(viewer);
|
||||
|
||||
preSlotSetup();
|
||||
|
||||
int upperRows = topSize / 9;
|
||||
// View's upper inventory - our container
|
||||
for (int row = 0; row < upperRows; ++row) {
|
||||
for (int col = 0; col < 9; ++col) {
|
||||
// x and y for client purposes, but hey, we're thorough here.
|
||||
// Adapted from net.minecraft.world.inventory.ChestMenu
|
||||
int x = 8 + col * 18;
|
||||
int y = 18 + row * 18;
|
||||
int index = row * 9 + col;
|
||||
|
||||
// Guard against weird inventory sizes.
|
||||
if (index >= container.getContainerSize()) {
|
||||
addSlot(new SlotViewOnly(container, index, x, y));
|
||||
continue;
|
||||
}
|
||||
|
||||
Slot slot = getUpperSlot(index, x, y);
|
||||
|
||||
addSlot(slot);
|
||||
}
|
||||
}
|
||||
|
||||
// View's lower inventory - viewer inventory
|
||||
int playerInvPad = (upperRows - 4) * 18;
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
for (int col = 0; col < 9; ++col) {
|
||||
int x = 8 + col * 18;
|
||||
int y = playerInvPad + row * 18 + 103;
|
||||
addSlot(new Slot(viewer.getInventory(), row * 9 + col + 9, x, y));
|
||||
}
|
||||
}
|
||||
// Hotbar
|
||||
for (int col = 0; col < 9; ++col) {
|
||||
int x = 8 + col * 18;
|
||||
int y = playerInvPad + 161;
|
||||
addSlot(new Slot(viewer.getInventory(), col, x, y));
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull MenuType<ChestMenu> getChestMenuType(int inventorySize) {
|
||||
inventorySize = ((int) Math.ceil(inventorySize / 9.0)) * 9;
|
||||
return switch (inventorySize) {
|
||||
case 9 -> MenuType.GENERIC_9x1;
|
||||
case 18 -> MenuType.GENERIC_9x2;
|
||||
case 27 -> MenuType.GENERIC_9x3;
|
||||
case 36 -> MenuType.GENERIC_9x4;
|
||||
case 45 -> MenuType.GENERIC_9x5;
|
||||
case 54 -> MenuType.GENERIC_9x6;
|
||||
default -> throw new IllegalArgumentException("Inventory size unsupported: " + inventorySize);
|
||||
};
|
||||
}
|
||||
|
||||
protected void preSlotSetup() {}
|
||||
|
||||
protected @NotNull Slot getUpperSlot(int index, int x, int y) {
|
||||
if (viewOnly) {
|
||||
return new SlotViewOnly(container, index, x, y);
|
||||
}
|
||||
return new Slot(container, index, x, y);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final @NotNull CraftInventoryView<OpenChestMenu<T>, Inventory> getBukkitView() {
|
||||
if (bukkitEntity == null) {
|
||||
bukkitEntity = createBukkitEntity();
|
||||
}
|
||||
|
||||
return bukkitEntity;
|
||||
}
|
||||
|
||||
protected @NotNull CraftInventoryView<OpenChestMenu<T>, Inventory> createBukkitEntity() {
|
||||
Inventory top;
|
||||
if (viewOnly) {
|
||||
top = new OpenDummyInventory(container, container.getBukkitType());
|
||||
} else {
|
||||
top = container.getBukkitInventory();
|
||||
}
|
||||
return new CraftInventoryView<>(viewer.getBukkitEntity(), top, this) {
|
||||
@Override
|
||||
public @Nullable Inventory getInventory(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return null;
|
||||
}
|
||||
return super.getInventory(rawSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertSlot(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return InventoryView.OUTSIDE;
|
||||
}
|
||||
return super.convertSlot(rawSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType.SlotType getSlotType(int slot) {
|
||||
if (viewOnly) {
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
return super.getSlotType(slot);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private int getTopSize(ServerPlayer viewer) {
|
||||
MenuType<?> menuType = getType();
|
||||
if (menuType == null) {
|
||||
throw new IllegalStateException("MenuType cannot be null!");
|
||||
} else if (menuType == MenuType.GENERIC_9x1) {
|
||||
return 9;
|
||||
} else if (menuType == MenuType.GENERIC_9x2) {
|
||||
return 18;
|
||||
} else if (menuType == MenuType.GENERIC_9x3) {
|
||||
return 27;
|
||||
} else if (menuType == MenuType.GENERIC_9x4) {
|
||||
return 36;
|
||||
} else if (menuType == MenuType.GENERIC_9x5) {
|
||||
return 45;
|
||||
} else if (menuType == MenuType.GENERIC_9x6) {
|
||||
return 54;
|
||||
}
|
||||
// This is a bit gross, but allows us a safe fallthrough.
|
||||
return menuType.create(-1, viewer.getInventory()).slots.size() - BOTTOM_INVENTORY_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reimplementation of {@link AbstractContainerMenu#moveItemStackTo(ItemStack, int, int, boolean)} that ignores fake
|
||||
* slots and respects {@link Slot#hasItem()}.
|
||||
*
|
||||
* @param itemStack the stack to quick-move
|
||||
* @param rangeLow the start of the range of slots that can be moved to, inclusive
|
||||
* @param rangeHigh the end of the range of slots that can be moved to, exclusive
|
||||
* @param topDown whether to start at the top of the range or bottom
|
||||
* @return whether the stack was modified as a result of being quick-moved
|
||||
*/
|
||||
@Override
|
||||
protected boolean moveItemStackTo(ItemStack itemStack, int rangeLow, int rangeHigh, boolean topDown) {
|
||||
boolean modified = false;
|
||||
boolean stackable = itemStack.isStackable();
|
||||
Slot firstEmpty = null;
|
||||
|
||||
for (int index = topDown ? rangeHigh - 1 : rangeLow;
|
||||
!itemStack.isEmpty() && (topDown ? index >= rangeLow : index < rangeHigh);
|
||||
index += topDown ? -1 : 1
|
||||
) {
|
||||
Slot slot = slots.get(index);
|
||||
// If the slot cannot be added to, check the next slot.
|
||||
if (slot.isFake() || !slot.mayPlace(itemStack)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (slot.hasItem()) {
|
||||
// If the item isn't stackable, check the next slot.
|
||||
if (!stackable) {
|
||||
continue;
|
||||
}
|
||||
// Otherwise, add as many as we can from our stack to the slot.
|
||||
modified |= addToExistingStack(itemStack, slot);
|
||||
} else {
|
||||
// If this is the first empty slot, keep track of it for later use.
|
||||
if (firstEmpty == null) {
|
||||
firstEmpty = slot;
|
||||
}
|
||||
// If the item isn't stackable, we've located the slot we're adding it to, so we're done.
|
||||
if (!stackable) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the item hasn't been fully added yet, add as many as we can to the first open slot.
|
||||
if (!itemStack.isEmpty() && firstEmpty != null) {
|
||||
firstEmpty.setByPlayer(itemStack.split(Math.min(itemStack.getCount(), firstEmpty.getMaxStackSize(itemStack))));
|
||||
firstEmpty.setChanged();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
private static boolean addToExistingStack(ItemStack itemStack, Slot slot) {
|
||||
ItemStack existing = slot.getItem();
|
||||
|
||||
// If the items aren't the same, we can't add our item.
|
||||
if (!ItemStack.isSameItemSameComponents(itemStack, existing)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int max = slot.getMaxStackSize(existing);
|
||||
int existingCount = existing.getCount();
|
||||
|
||||
// If the stack is already full, we can't add more.
|
||||
if (existingCount >= max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int total = existingCount + itemStack.getCount();
|
||||
|
||||
// If the existing item can accept the entirety of our item, we're done!
|
||||
if (total <= max) {
|
||||
itemStack.setCount(0);
|
||||
existing.setCount(total);
|
||||
slot.setChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, add as many as we can.
|
||||
itemStack.shrink(max - existingCount);
|
||||
existing.setCount(max);
|
||||
slot.setChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clicked(int i, int j, ClickType clickType, Player player) {
|
||||
if (viewOnly) {
|
||||
if (clickType == ClickType.QUICK_CRAFT) {
|
||||
sendAllDataToRemote();
|
||||
}
|
||||
return;
|
||||
}
|
||||
super.clicked(i, j, clickType, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Overrides from here on are purely to modify the sync process to send placeholder items.
|
||||
@Override
|
||||
protected Slot addSlot(Slot slot) {
|
||||
slot.index = this.slots.size();
|
||||
this.slots.add(slot);
|
||||
this.lastSlots.add(ItemStack.EMPTY);
|
||||
this.remoteSlots.add(ItemStack.EMPTY);
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataSlot addDataSlot(DataSlot dataSlot) {
|
||||
this.dataSlots.add(dataSlot);
|
||||
this.remoteDataSlots.add(0);
|
||||
return dataSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDataSlots(ContainerData containerData) {
|
||||
for (int i = 0; i < containerData.getCount(); i++) {
|
||||
this.addDataSlot(DataSlot.forContainer(containerData, i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSlotListener(ContainerListener containerListener) {
|
||||
if (!this.containerListeners.contains(containerListener)) {
|
||||
this.containerListeners.add(containerListener);
|
||||
this.broadcastChanges();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSynchronizer(ContainerSynchronizer containerSynchronizer) {
|
||||
this.synchronizer = containerSynchronizer;
|
||||
this.sendAllDataToRemote();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendAllDataToRemote() {
|
||||
for (int index = 0; index < slots.size(); ++index) {
|
||||
Slot slot = slots.get(index);
|
||||
this.remoteSlots.set(index, (slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem()).copy());
|
||||
}
|
||||
|
||||
remoteCarried = getCarried().copy();
|
||||
|
||||
for (int index = 0; index < this.dataSlots.size(); ++index) {
|
||||
this.remoteDataSlots.set(index, this.dataSlots.get(index).get());
|
||||
}
|
||||
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendInitialData(this, this.remoteSlots, this.remoteCarried, this.remoteDataSlots.toIntArray());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastCarriedItem() {
|
||||
this.remoteCarried = this.getCarried().copy();
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendCarriedChange(this, this.remoteCarried);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSlotListener(ContainerListener containerListener) {
|
||||
this.containerListeners.remove(containerListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastChanges() {
|
||||
for (int index = 0; index < this.slots.size(); ++index) {
|
||||
Slot slot = this.slots.get(index);
|
||||
ItemStack itemstack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem();
|
||||
Supplier<ItemStack> supplier = Suppliers.memoize(itemstack::copy);
|
||||
this.triggerSlotListeners(index, itemstack, supplier);
|
||||
this.synchronizeSlotToRemote(index, itemstack, supplier);
|
||||
}
|
||||
|
||||
this.synchronizeCarriedToRemote();
|
||||
|
||||
for (int index = 0; index < this.dataSlots.size(); ++index) {
|
||||
DataSlot dataSlot = this.dataSlots.get(index);
|
||||
int j = dataSlot.get();
|
||||
if (dataSlot.checkAndClearUpdateFlag()) {
|
||||
this.updateDataSlotListeners(index, j);
|
||||
}
|
||||
|
||||
this.synchronizeDataSlotToRemote(index, j);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastFullState() {
|
||||
for (int index = 0; index < this.slots.size(); ++index) {
|
||||
ItemStack itemstack = this.slots.get(index).getItem();
|
||||
this.triggerSlotListeners(index, itemstack, itemstack::copy);
|
||||
}
|
||||
|
||||
for (int index = 0; index < this.dataSlots.size(); ++index) {
|
||||
DataSlot containerproperty = this.dataSlots.get(index);
|
||||
if (containerproperty.checkAndClearUpdateFlag()) {
|
||||
this.updateDataSlotListeners(index, containerproperty.get());
|
||||
}
|
||||
}
|
||||
|
||||
this.sendAllDataToRemote();
|
||||
}
|
||||
|
||||
private void updateDataSlotListeners(int i, int j) {
|
||||
for (ContainerListener containerListener : this.containerListeners) {
|
||||
containerListener.dataChanged(this, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerSlotListeners(int index, ItemStack itemStack, Supplier<ItemStack> supplier) {
|
||||
ItemStack itemStack1 = this.lastSlots.get(index);
|
||||
if (!ItemStack.matches(itemStack1, itemStack)) {
|
||||
ItemStack itemStack2 = supplier.get();
|
||||
this.lastSlots.set(index, itemStack2);
|
||||
|
||||
for (ContainerListener containerListener : this.containerListeners) {
|
||||
containerListener.slotChanged(this, index, itemStack2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void synchronizeSlotToRemote(int i, ItemStack itemStack, Supplier<ItemStack> supplier) {
|
||||
if (!this.suppressRemoteUpdates) {
|
||||
ItemStack itemStack1 = this.remoteSlots.get(i);
|
||||
if (!ItemStack.matches(itemStack1, itemStack)) {
|
||||
ItemStack itemstack2 = supplier.get();
|
||||
this.remoteSlots.set(i, itemstack2);
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendSlotChange(this, i, itemstack2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void synchronizeDataSlotToRemote(int index, int value) {
|
||||
if (!this.suppressRemoteUpdates) {
|
||||
int existing = this.remoteDataSlots.getInt(index);
|
||||
if (existing != value) {
|
||||
this.remoteDataSlots.set(index, value);
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendDataChange(this, index, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void synchronizeCarriedToRemote() {
|
||||
if (!this.suppressRemoteUpdates && !ItemStack.matches(this.getCarried(), this.remoteCarried)) {
|
||||
this.remoteCarried = this.getCarried().copy();
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendCarriedChange(this, this.remoteCarried);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoteCarried(ItemStack itemstack) {
|
||||
this.remoteCarried = itemstack.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suppressRemoteUpdates() {
|
||||
this.suppressRemoteUpdates = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeRemoteUpdates() {
|
||||
this.suppressRemoteUpdates = false;
|
||||
}
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.menu;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenEnderChest;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class OpenEnderChestMenu extends OpenChestMenu<OpenEnderChest> {
|
||||
|
||||
public OpenEnderChestMenu(
|
||||
@NotNull OpenEnderChest enderChest,
|
||||
@NotNull ServerPlayer viewer,
|
||||
int containerId,
|
||||
boolean viewOnly) {
|
||||
super(getChestMenuType(enderChest.getContainerSize()), containerId, enderChest, viewer, viewOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
if (viewOnly) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
// See ChestMenu
|
||||
Slot slot = this.slots.get(index);
|
||||
|
||||
if (slot.isFake() || !slot.hasItem()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack itemStack = slot.getItem();
|
||||
ItemStack original = itemStack.copy();
|
||||
|
||||
if (index < topSize) {
|
||||
if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
} else if (!this.moveItemStackTo(itemStack, 0, topSize, false)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
if (itemStack.isEmpty()) {
|
||||
slot.setByPlayer(ItemStack.EMPTY);
|
||||
} else {
|
||||
slot.setChanged();
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
}
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.menu;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenDummyPlayerInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenPlayerInventorySelf;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentDrop;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentEquipment;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotViewOnly;
|
||||
import com.lishid.openinv.util.Permissions;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventoryView;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OpenInventoryMenu extends OpenChestMenu<OpenInventory> {
|
||||
|
||||
private int offset;
|
||||
|
||||
public OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i, boolean viewOnly) {
|
||||
super(getMenuType(inventory, viewer), i, inventory, viewer, viewOnly);
|
||||
}
|
||||
|
||||
private static MenuType<ChestMenu> getMenuType(OpenInventory inventory, ServerPlayer viewer) {
|
||||
int size = inventory.getContainerSize();
|
||||
// Disallow duplicate access to own main inventory contents.
|
||||
if (inventory.getOwnerHandle().equals(viewer)) {
|
||||
size -= viewer.getInventory().items.size();
|
||||
size = ((int) Math.ceil(size / 9.0)) * 9;
|
||||
}
|
||||
|
||||
return getChestMenuType(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preSlotSetup() {
|
||||
offset = ownContainer ? viewer.getInventory().items.size() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Slot getUpperSlot(int index, int x, int y) {
|
||||
index += offset;
|
||||
Slot slot = container.getMenuSlot(index, x, y);
|
||||
|
||||
// If the slot cannot be interacted with there's nothing to configure.
|
||||
if (slot.getClass().equals(SlotViewOnly.class)) {
|
||||
return slot;
|
||||
}
|
||||
|
||||
// Remove drop slot if viewer is not allowed to use it.
|
||||
if (slot instanceof ContentDrop.SlotDrop
|
||||
&& (viewOnly || !Permissions.INVENTORY_SLOT_DROP.hasPermission(viewer.getBukkitEntity()))) {
|
||||
return new SlotViewOnly(container, index, x, y);
|
||||
}
|
||||
|
||||
if (slot instanceof ContentEquipment.SlotEquipment equipment) {
|
||||
if (viewOnly) {
|
||||
return SlotViewOnly.wrap(slot);
|
||||
}
|
||||
|
||||
Permissions perm = switch (equipment.getEquipmentSlot()) {
|
||||
case HEAD -> Permissions.INVENTORY_SLOT_HEAD_ANY;
|
||||
case CHEST -> Permissions.INVENTORY_SLOT_CHEST_ANY;
|
||||
case LEGS -> Permissions.INVENTORY_SLOT_LEGS_ANY;
|
||||
case FEET -> Permissions.INVENTORY_SLOT_FEET_ANY;
|
||||
// Off-hand can hold anything, not just equipment.
|
||||
default -> null;
|
||||
};
|
||||
|
||||
// If the viewer doesn't have permission, only allow equipment the viewee can equip in the slot.
|
||||
if (perm != null && !perm.hasPermission(viewer.getBukkitEntity())) {
|
||||
equipment.onlyEquipmentFor(container.getOwnerHandle());
|
||||
}
|
||||
|
||||
// Equipment slots are a core part of the inventory, so they will always be shown.
|
||||
return slot;
|
||||
}
|
||||
|
||||
// When viewing own inventory, only allow access to equipment and drop slots (equipment allowed above).
|
||||
if (ownContainer && !(slot instanceof ContentDrop.SlotDrop)) {
|
||||
return new SlotViewOnly(container, index, x, y);
|
||||
}
|
||||
|
||||
if (viewOnly) {
|
||||
return SlotViewOnly.wrap(slot);
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull CraftInventoryView<OpenChestMenu<OpenInventory>, Inventory> createBukkitEntity() {
|
||||
org.bukkit.inventory.Inventory bukkitInventory;
|
||||
if (viewOnly) {
|
||||
bukkitInventory = new OpenDummyPlayerInventory(container);
|
||||
} else if (ownContainer) {
|
||||
bukkitInventory = new OpenPlayerInventorySelf(container, offset);
|
||||
} else {
|
||||
bukkitInventory = container.getBukkitInventory();
|
||||
}
|
||||
|
||||
return new CraftInventoryView<>(viewer.getBukkitEntity(), bukkitInventory, this) {
|
||||
@Override
|
||||
public org.bukkit.inventory.ItemStack getItem(int index) {
|
||||
if (viewOnly || index < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Slot slot = slots.get(index);
|
||||
return CraftItemStack.asCraftMirror(slot.hasItem() ? slot.getItem() : ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInTop(int rawSlot) {
|
||||
return rawSlot < topSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Inventory getInventory(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return null;
|
||||
}
|
||||
if (rawSlot == InventoryView.OUTSIDE || rawSlot == -1) {
|
||||
return null;
|
||||
}
|
||||
Preconditions.checkArgument(rawSlot >= 0 && rawSlot < topSize + offset + BOTTOM_INVENTORY_SIZE,
|
||||
"Slot %s outside of inventory", rawSlot);
|
||||
if (rawSlot > topSize) {
|
||||
return getBottomInventory();
|
||||
}
|
||||
Slot slot = slots.get(rawSlot);
|
||||
if (slot.isFake()) {
|
||||
return null;
|
||||
}
|
||||
return getTopInventory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertSlot(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return InventoryView.OUTSIDE;
|
||||
}
|
||||
if (rawSlot < 0) {
|
||||
return rawSlot;
|
||||
}
|
||||
if (rawSlot < topSize) {
|
||||
Slot slot = slots.get(rawSlot);
|
||||
if (slot.isFake()) {
|
||||
return InventoryView.OUTSIDE;
|
||||
}
|
||||
return rawSlot;
|
||||
}
|
||||
|
||||
int slot = rawSlot - topSize;
|
||||
|
||||
if (slot >= 27) {
|
||||
slot -= 27;
|
||||
} else {
|
||||
slot += 9;
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType.SlotType getSlotType(int slot) {
|
||||
if (viewOnly || slot < 0) {
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
if (slot >= topSize) {
|
||||
slot -= topSize;
|
||||
if (slot >= 27) {
|
||||
return InventoryType.SlotType.QUICKBAR;
|
||||
}
|
||||
return InventoryType.SlotType.CONTAINER;
|
||||
}
|
||||
return OpenInventoryMenu.this.container.getSlotType(offset + slot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countSlots() {
|
||||
return topSize + BOTTOM_INVENTORY_SIZE;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
if (viewOnly) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
// See ChestMenu and InventoryMenu
|
||||
Slot slot = this.slots.get(index);
|
||||
|
||||
if (!slot.hasItem() || slot.isFake()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack itemStack = slot.getItem();
|
||||
ItemStack originalStack = itemStack.copy();
|
||||
|
||||
if (index < topSize) {
|
||||
// If we're moving top to bottom, do a normal transfer.
|
||||
if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
} else {
|
||||
EquipmentSlot equipmentSlot = player.getEquipmentSlotForItem(itemStack);
|
||||
boolean movedGear = switch (equipmentSlot) {
|
||||
// If this is gear, try to move it to the correct slot first.
|
||||
case OFFHAND, FEET, LEGS, CHEST, HEAD -> {
|
||||
// Locate the correct slot in the contents following the main inventory.
|
||||
for (int extra = container.getOwnerHandle().getInventory().items.size() - offset; extra < topSize; ++extra) {
|
||||
Slot extraSlot = getSlot(extra);
|
||||
if (extraSlot instanceof ContentEquipment.SlotEquipment equipSlot
|
||||
&& equipSlot.getEquipmentSlot() == equipmentSlot) {
|
||||
// If we've found a matching slot, try to move to it.
|
||||
// If this succeeds, even partially, we will not attempt to move to other slots.
|
||||
// Otherwise, armor is already occupied, so we'll fall through to main inventory.
|
||||
yield this.moveItemStackTo(itemStack, extra, extra + 1, false);
|
||||
}
|
||||
}
|
||||
yield false;
|
||||
}
|
||||
// Non-gear gets no special treatment.
|
||||
default -> false;
|
||||
};
|
||||
|
||||
// If main inventory is not available, there's nowhere else to move.
|
||||
if (offset != 0) {
|
||||
if (!movedGear) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
} else {
|
||||
// If we didn't move to a gear slot, try to move to a main inventory slot.
|
||||
if (!movedGear && !this.moveItemStackTo(itemStack, 0, container.getOwnerHandle().getInventory().items.size(), true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itemStack.isEmpty()) {
|
||||
slot.setByPlayer(ItemStack.EMPTY);
|
||||
} else {
|
||||
slot.setChanged();
|
||||
}
|
||||
|
||||
return originalStack;
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* An interface defining behaviors for entries in a {@link Container}. Used to reduce duplicate content reordering.
|
||||
*/
|
||||
public interface Content {
|
||||
|
||||
/**
|
||||
* Update internal holder.
|
||||
*
|
||||
* @param holder the new holder
|
||||
*/
|
||||
void setHolder(@NotNull ServerPlayer holder);
|
||||
|
||||
/**
|
||||
* Get the current item.
|
||||
*
|
||||
* @return the current item
|
||||
*/
|
||||
ItemStack get();
|
||||
|
||||
/**
|
||||
* Remove the current item.
|
||||
*
|
||||
* @return the current item
|
||||
*/
|
||||
ItemStack remove();
|
||||
|
||||
/**
|
||||
* Remove some of the current item.
|
||||
*
|
||||
* @return the current item
|
||||
*/
|
||||
ItemStack removePartial(int amount);
|
||||
|
||||
/**
|
||||
* Set the current item. If slot is currently not usable, will drop item instead.
|
||||
*
|
||||
* @param itemStack the item to set
|
||||
*/
|
||||
void set(ItemStack itemStack);
|
||||
|
||||
/**
|
||||
* Get a {@link Slot} for use in a {@link net.minecraft.world.inventory.AbstractContainerMenu ContainerMenu}. Will
|
||||
* impose any specific restrictions to insertion or removal.
|
||||
*
|
||||
* @param container the backing container
|
||||
* @param slot the slot of the backing container represented
|
||||
* @param x clientside x dimension from top left of inventory, not used
|
||||
* @param y clientside y dimension from top left of inventory, not used
|
||||
* @return a menu slot
|
||||
*/
|
||||
Slot asSlot(Container container, int slot, int x, int y);
|
||||
|
||||
/**
|
||||
* Get a loose Bukkit translation of what this slot stores. For example, any slot that drops items at the owner rather
|
||||
* than insert them will report itself as being {@link org.bukkit.event.inventory.InventoryType.SlotType#OUTSIDE}.
|
||||
*
|
||||
* @return the closes Bukkit slot type
|
||||
*/
|
||||
org.bukkit.event.inventory.InventoryType.SlotType getSlotType();
|
||||
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A slot in a survival crafting inventory. Unavailable when not online in a survival mode.
|
||||
*/
|
||||
public class ContentCrafting implements Content {
|
||||
|
||||
private final int index;
|
||||
private ServerPlayer holder;
|
||||
private List<ItemStack> items;
|
||||
|
||||
public ContentCrafting(@NotNull ServerPlayer holder, int index) {
|
||||
setHolder(holder);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
private boolean isAvailable() {
|
||||
return isAvailable(holder);
|
||||
}
|
||||
|
||||
static boolean isAvailable(@NotNull ServerPlayer holder) {
|
||||
// Player must be online and not in creative - since the creative client is (semi-)authoritative,
|
||||
// it ignores changes without extra help, and will delete the item as a result.
|
||||
// Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory.
|
||||
return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
// Note: CraftingContainer#getItems is immutable! Be careful with updates.
|
||||
this.items = holder.inventoryMenu.getCraftSlots().getContents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return isAvailable() ? items.get(index) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
if (!this.isAvailable()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
ItemStack removed = items.remove(index);
|
||||
if (removed.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
if (!this.isAvailable()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
ItemStack removed = ContainerHelper.removeItem(items, index, amount);
|
||||
if (removed.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
if (isAvailable()) {
|
||||
items.set(index, itemStack);
|
||||
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
|
||||
} else {
|
||||
holder.drop(itemStack, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotCrafting(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return isAvailable() ? InventoryType.SlotType.CRAFTING : InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
public class SlotCrafting extends SlotPlaceholder {
|
||||
|
||||
private SlotCrafting(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return isAvailable() ? items.get(ContentCrafting.this.index) : Placeholders.survivalOnly(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPickup(Player player) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack itemStack) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return isAvailable() && super.hasItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return !isAvailable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.InventoryMenu;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot allowing viewing of the crafting result.
|
||||
*
|
||||
* <p>Unmodifiable because I said so. Use your own crafting grid.</p>
|
||||
*/
|
||||
public class ContentCraftingResult extends ContentViewOnly {
|
||||
|
||||
public ContentCraftingResult(@NotNull ServerPlayer holder) {
|
||||
super(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
InventoryMenu inventoryMenu = holder.inventoryMenu;
|
||||
return inventoryMenu.getResultSlot().getItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotViewOnly(container, slot, x, y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
if (!ContentCrafting.isAvailable(holder)) {
|
||||
return Placeholders.survivalOnly(holder);
|
||||
}
|
||||
InventoryMenu inventoryMenu = holder.inventoryMenu;
|
||||
return inventoryMenu.getResultSlot().getItem();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return InventoryType.SlotType.RESULT;
|
||||
}
|
||||
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot wrapping the active menu's cursor. Unavailable when not online in a survival mode.
|
||||
*/
|
||||
public class ContentCursor implements Content {
|
||||
|
||||
private @NotNull ServerPlayer holder;
|
||||
|
||||
public ContentCursor(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return isAvailable() ? holder.containerMenu.getCarried() : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
ItemStack carried = holder.containerMenu.getCarried();
|
||||
holder.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
return carried;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
ItemStack carried = holder.containerMenu.getCarried();
|
||||
if (!carried.isEmpty() && carried.getCount() >= amount) {
|
||||
ItemStack value = carried.split(amount);
|
||||
if (carried.isEmpty()) {
|
||||
holder.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
if (isAvailable()) {
|
||||
holder.containerMenu.setCarried(itemStack);
|
||||
} else {
|
||||
holder.drop(itemStack, false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAvailable() {
|
||||
// Player must be online and not in creative - since the creative client is (semi-)authoritative,
|
||||
// it ignores changes without extra help, and will delete the item as a result.
|
||||
// Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory.
|
||||
return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotCursor(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
// As close as possible to "not real"
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
public class SlotCursor extends SlotPlaceholder {
|
||||
|
||||
private SlotCursor(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
if (!isAvailable()) {
|
||||
return Placeholders.survivalOnly(holder);
|
||||
}
|
||||
ItemStack carried = holder.containerMenu.getCarried();
|
||||
return carried.isEmpty() ? Placeholders.cursor : carried;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPickup(Player player) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack itemStack) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return isAvailable() && super.hasItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A fake slot used to drop items. Unavailable offline.
|
||||
*/
|
||||
public class ContentDrop implements Content {
|
||||
|
||||
private ServerPlayer holder;
|
||||
|
||||
public ContentDrop(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
holder.drop(itemStack, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotDrop(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
// Behaves like dropping an item outside the screen, just by the target player.
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
public class SlotDrop extends SlotPlaceholder {
|
||||
|
||||
private SlotDrop(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return holder.connection != null && !holder.connection.isDisconnected()
|
||||
? Placeholders.drop
|
||||
: Placeholders.blockedOffline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack itemStack) {
|
||||
return holder.connection != null && !holder.connection.isDisconnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot for equipment that displays placeholders if empty.
|
||||
*/
|
||||
public class ContentEquipment extends ContentList {
|
||||
|
||||
private final ItemStack placeholder;
|
||||
private final EquipmentSlot equipmentSlot;
|
||||
|
||||
public ContentEquipment(ServerPlayer holder, int index, EquipmentSlot equipmentSlot) {
|
||||
super(holder, index, InventoryType.SlotType.ARMOR);
|
||||
placeholder = switch (equipmentSlot) {
|
||||
case HEAD -> Placeholders.emptyHelmet;
|
||||
case CHEST -> Placeholders.emptyChestplate;
|
||||
case LEGS -> Placeholders.emptyLeggings;
|
||||
case FEET -> Placeholders.emptyBoots;
|
||||
default -> Placeholders.emptyOffHand;
|
||||
};
|
||||
this.equipmentSlot = equipmentSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.items = holder.getInventory().armor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotEquipment(container, slot, x, y);
|
||||
}
|
||||
|
||||
public class SlotEquipment extends SlotPlaceholder {
|
||||
|
||||
private ServerPlayer viewer;
|
||||
|
||||
SlotEquipment(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
ItemStack itemStack = getItem();
|
||||
if (!itemStack.isEmpty()) {
|
||||
return itemStack;
|
||||
}
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
public EquipmentSlot getEquipmentSlot() {
|
||||
return equipmentSlot;
|
||||
}
|
||||
|
||||
public void onlyEquipmentFor(ServerPlayer viewer) {
|
||||
this.viewer = viewer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack var0) {
|
||||
if (viewer == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return equipmentSlot == EquipmentSlot.OFFHAND || viewer.getEquipmentSlotForItem(var0) == equipmentSlot;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A normal slot backed by an item list.
|
||||
*/
|
||||
public abstract class ContentList implements Content {
|
||||
|
||||
private final int index;
|
||||
private final InventoryType.SlotType slotType;
|
||||
protected List<ItemStack> items;
|
||||
|
||||
public ContentList(ServerPlayer holder, int index, InventoryType.SlotType slotType) {
|
||||
this.index = index;
|
||||
this.slotType = slotType;
|
||||
setHolder(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return items.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
ItemStack removed = items.remove(index);
|
||||
return removed == null || removed.isEmpty() ? ItemStack.EMPTY : removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
return ContainerHelper.removeItem(items, index, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
items.set(index, itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new Slot(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return slotType;
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot for equipment that updates held items if necessary.
|
||||
*/
|
||||
public class ContentOffHand extends ContentEquipment {
|
||||
|
||||
private ServerPlayer holder;
|
||||
|
||||
public ContentOffHand(ServerPlayer holder, int localIndex) {
|
||||
super(holder, localIndex, EquipmentSlot.OFFHAND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.items = holder.getInventory().offhand;
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return InventoryType.SlotType.QUICKBAR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotEquipment(container, slot, x, y) {
|
||||
@Override
|
||||
public void setChanged() {
|
||||
if (holder.connection != null
|
||||
&& !holder.connection.isDisconnected()
|
||||
&& holder.containerMenu != holder.inventoryMenu) {
|
||||
holder.resendItemInHands();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A view-only slot that can't be interacted with.
|
||||
*/
|
||||
public class ContentViewOnly implements Content {
|
||||
|
||||
@NotNull ServerPlayer holder;
|
||||
|
||||
public ContentViewOnly(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
this.holder.drop(itemStack, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotViewOnly(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
* An implementation of a slot as used by a menu that may have fake placeholder items.
|
||||
*
|
||||
* <p>Used to prevent plugins (particularly sorting plugins) from adding placeholders to inventories.</p>
|
||||
*/
|
||||
public abstract class SlotPlaceholder extends Slot {
|
||||
|
||||
public SlotPlaceholder(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
public abstract ItemStack getOrDefault();
|
||||
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.Placeholders;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A view-only {@link Slot}. "Blank" by default, but can wrap another slot to display its content.
|
||||
*/
|
||||
public class SlotViewOnly extends SlotPlaceholder {
|
||||
|
||||
public static @NotNull SlotViewOnly wrap(@NotNull Slot wrapped) {
|
||||
SlotViewOnly wrapper;
|
||||
if (wrapped instanceof SlotPlaceholder placeholder) {
|
||||
wrapper = new SlotViewOnly(wrapped.container, wrapped.slot, wrapped.x, wrapped.y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return placeholder.getOrDefault();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
wrapper = new SlotViewOnly(wrapped.container, wrapped.slot, wrapped.x, wrapped.y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return wrapped.getItem();
|
||||
}
|
||||
};
|
||||
}
|
||||
wrapper.index = wrapped.index;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public SlotViewOnly(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return Placeholders.notSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuickCraft(ItemStack var0, ItemStack var1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTake(Player var0, ItemStack var1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack var0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByPlayer(ItemStack newStack) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByPlayer(ItemStack newStack, ItemStack oldStack) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack var0) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize(ItemStack itemStack) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove(int amount) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPickup(Player var0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ItemStack> tryRemove(int var0, int var1, Player var2) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack safeTake(int var0, int var1, Player var2) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack safeInsert(ItemStack itemStack) {
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack safeInsert(ItemStack itemStack, int amount) {
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowModification(Player var0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSlot() {
|
||||
return this.slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHighlightable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.player;
|
||||
|
||||
import com.lishid.openinv.event.OpenEvents;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.nbt.NumericTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.storage.PlayerDataStorage;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
|
||||
public class OpenPlayer extends CraftPlayer {
|
||||
|
||||
/**
|
||||
* List of tags to always reset when saving.
|
||||
*
|
||||
* @see net.minecraft.world.entity.Entity#saveWithoutId(CompoundTag)
|
||||
* @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag)
|
||||
* @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag)
|
||||
* @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag)
|
||||
*/
|
||||
private static final Set<String> RESET_TAGS = Set.of(
|
||||
// Entity#saveWithoutId(CompoundTag)
|
||||
"CustomName",
|
||||
"CustomNameVisible",
|
||||
"Silent",
|
||||
"NoGravity",
|
||||
"Glowing",
|
||||
"TicksFrozen",
|
||||
"HasVisualFire",
|
||||
"Tags",
|
||||
"Passengers",
|
||||
// ServerPlayer#addAdditionalSaveData(CompoundTag)
|
||||
// Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle
|
||||
"warden_spawn_tracker",
|
||||
"enteredNetherPosition",
|
||||
"SpawnX",
|
||||
"SpawnY",
|
||||
"SpawnZ",
|
||||
"SpawnForced",
|
||||
"SpawnAngle",
|
||||
"SpawnDimension",
|
||||
"raid_omen_position",
|
||||
"ender_pearls",
|
||||
// Player#addAdditionalSaveData(CompoundTag)
|
||||
"ShoulderEntityLeft",
|
||||
"ShoulderEntityRight",
|
||||
"LastDeathLocation",
|
||||
"current_explosion_impact_pos",
|
||||
// LivingEntity#addAdditionalSaveData(CompoundTag)
|
||||
"active_effects",
|
||||
"SleepingX",
|
||||
"SleepingY",
|
||||
"SleepingZ",
|
||||
"Brain"
|
||||
);
|
||||
|
||||
private final PlayerManager manager;
|
||||
|
||||
OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) {
|
||||
super(server, entity);
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadData() {
|
||||
manager.loadData(getHandle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveData() {
|
||||
if (OpenEvents.saveCancelled(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayer player = this.getHandle();
|
||||
// See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman)
|
||||
try {
|
||||
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
|
||||
|
||||
CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player.getName().getString(), player.getStringUUID()).orElse(null);
|
||||
CompoundTag playerData = getWritableTag(oldData);
|
||||
playerData = player.saveWithoutId(playerData);
|
||||
setExtraData(playerData);
|
||||
|
||||
if (oldData != null) {
|
||||
// Revert certain special data values when offline.
|
||||
revertSpecialValues(playerData, oldData);
|
||||
}
|
||||
|
||||
Path playerDataDir = worldNBTStorage.getPlayerDir().toPath();
|
||||
Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat");
|
||||
NbtIo.writeCompressed(playerData, tempFile);
|
||||
Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat");
|
||||
Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old");
|
||||
Util.safeReplaceFile(dataFile, tempFile, backupFile);
|
||||
} catch (Exception e) {
|
||||
LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Contract("null -> new")
|
||||
private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) {
|
||||
if (oldData == null) {
|
||||
return new CompoundTag();
|
||||
}
|
||||
|
||||
// Copy old data. This is a deep clone, so operating on it should be safe.
|
||||
oldData = oldData.copy();
|
||||
|
||||
// Remove vanilla/server data that is not written every time.
|
||||
oldData.getAllKeys()
|
||||
.removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit"));
|
||||
|
||||
return oldData;
|
||||
}
|
||||
|
||||
private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) {
|
||||
// Revert automatic updates to play timestamps.
|
||||
copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class);
|
||||
copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class);
|
||||
copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class);
|
||||
}
|
||||
|
||||
private <T extends Tag> void copyValue(
|
||||
@NotNull CompoundTag source,
|
||||
@NotNull CompoundTag target,
|
||||
@NotNull String container,
|
||||
@NotNull String key,
|
||||
@SuppressWarnings("SameParameterValue") @NotNull Class<T> tagType) {
|
||||
CompoundTag oldContainer = getTag(source, container, CompoundTag.class);
|
||||
CompoundTag newContainer = getTag(target, container, CompoundTag.class);
|
||||
|
||||
// New container being null means the server implementation doesn't store this data.
|
||||
if (newContainer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If old tag exists, copy it to new location, removing otherwise.
|
||||
setTag(newContainer, key, getTag(oldContainer, key, tagType));
|
||||
}
|
||||
|
||||
private <T extends Tag> @Nullable T getTag(
|
||||
@Nullable CompoundTag container,
|
||||
@NotNull String key,
|
||||
@NotNull Class<T> dataType) {
|
||||
if (container == null) {
|
||||
return null;
|
||||
}
|
||||
Tag value = container.get(key);
|
||||
if (value == null || !dataType.isAssignableFrom(value.getClass())) {
|
||||
return null;
|
||||
}
|
||||
return dataType.cast(value);
|
||||
}
|
||||
|
||||
private <T extends Tag> void setTag(
|
||||
@NotNull CompoundTag container,
|
||||
@NotNull String key,
|
||||
@Nullable T data) {
|
||||
if (data == null) {
|
||||
container.remove(key);
|
||||
} else {
|
||||
container.put(key, data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.player;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenEnderChest;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
import net.minecraft.server.level.ParticleStatus;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.ChatVisiblity;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.event.CraftEventFactory;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class PlayerManager implements com.lishid.openinv.internal.PlayerManager {
|
||||
|
||||
private static boolean paper;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName("io.papermc.paper.configuration.Configuration");
|
||||
paper = true;
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
paper = false;
|
||||
}
|
||||
}
|
||||
|
||||
private final @NotNull Logger logger;
|
||||
private @Nullable Field bukkitEntity;
|
||||
|
||||
public PlayerManager(@NotNull Logger logger) {
|
||||
this.logger = logger;
|
||||
try {
|
||||
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
|
||||
} catch (NoSuchFieldException e) {
|
||||
logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!");
|
||||
logger.log(java.util.logging.Level.WARNING, e.getMessage(), e);
|
||||
bukkitEntity = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull ServerPlayer getHandle(final Player player) {
|
||||
if (player instanceof CraftPlayer) {
|
||||
return ((CraftPlayer) player).getHandle();
|
||||
}
|
||||
|
||||
Server server = player.getServer();
|
||||
ServerPlayer nmsPlayer = null;
|
||||
|
||||
if (server instanceof CraftServer) {
|
||||
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId());
|
||||
}
|
||||
|
||||
if (nmsPlayer == null) {
|
||||
// Could use reflection to examine fields, but it's honestly not worth the bother.
|
||||
throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName());
|
||||
}
|
||||
|
||||
return nmsPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
|
||||
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
ServerLevel worldServer = server.getLevel(Level.OVERWORLD);
|
||||
|
||||
if (worldServer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a new ServerPlayer.
|
||||
ServerPlayer entity = createNewPlayer(server, worldServer, offline);
|
||||
|
||||
// Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak.
|
||||
entity.getAdvancements().stopListening();
|
||||
|
||||
// Try to load the player's data.
|
||||
if (loadData(entity)) {
|
||||
// If data is loaded successfully, return the Bukkit entity.
|
||||
return entity.getBukkitEntity();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private @NotNull ServerPlayer createNewPlayer(
|
||||
@NotNull MinecraftServer server,
|
||||
@NotNull ServerLevel worldServer,
|
||||
@NotNull final OfflinePlayer offline) {
|
||||
// See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile)
|
||||
// See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket)
|
||||
GameProfile profile = new GameProfile(offline.getUniqueId(),
|
||||
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
|
||||
|
||||
ClientInformation dummyInfo = new ClientInformation(
|
||||
"en_us",
|
||||
1, // Reduce distance just in case.
|
||||
ChatVisiblity.HIDDEN, // Don't accept chat.
|
||||
false,
|
||||
ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION,
|
||||
ServerPlayer.DEFAULT_MAIN_HAND,
|
||||
true,
|
||||
false, // Don't list in player list (not that this player is in the list anyway).
|
||||
ParticleStatus.MINIMAL
|
||||
);
|
||||
|
||||
ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo);
|
||||
|
||||
try {
|
||||
injectPlayer(entity);
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.log(
|
||||
java.util.logging.Level.WARNING,
|
||||
e,
|
||||
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
boolean loadData(@NotNull ServerPlayer player) {
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null);
|
||||
|
||||
if (loadedData == null) {
|
||||
// Exceptions with loading are logged by Mojang.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read basic data into the player.
|
||||
player.load(loadedData);
|
||||
// Also read "extra" data.
|
||||
player.readAdditionalSaveData(loadedData);
|
||||
// Game type settings are also loaded separately.
|
||||
player.loadGameTypes(loadedData);
|
||||
|
||||
if (paper) {
|
||||
// Paper: world is not loaded by ServerPlayer#load(CompoundTag).
|
||||
parseWorld(player, loadedData);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) {
|
||||
// See PlayerList#placeNewPlayer
|
||||
World bukkitWorld;
|
||||
if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) {
|
||||
// Modern Bukkit world.
|
||||
bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast")));
|
||||
} else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) {
|
||||
// Legacy Bukkit world.
|
||||
bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world"));
|
||||
} else {
|
||||
// Vanilla player data.
|
||||
DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension")))
|
||||
.resultOrPartial(logger::warning)
|
||||
.map(player.server::getLevel)
|
||||
// If ServerLevel exists, set, otherwise move to spawn.
|
||||
.ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null));
|
||||
return;
|
||||
}
|
||||
if (bukkitWorld == null) {
|
||||
player.spawnIn(null);
|
||||
return;
|
||||
}
|
||||
player.setServerLevel(((CraftWorld) bukkitWorld).getHandle());
|
||||
}
|
||||
|
||||
private void injectPlayer(ServerPlayer player) throws IllegalAccessException {
|
||||
if (bukkitEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
bukkitEntity.setAccessible(true);
|
||||
|
||||
bukkitEntity.set(player, new OpenPlayer(player.server.server, player, this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Player inject(@NotNull Player player) {
|
||||
try {
|
||||
ServerPlayer nmsPlayer = getHandle(player);
|
||||
if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) {
|
||||
return openPlayer;
|
||||
}
|
||||
injectPlayer(nmsPlayer);
|
||||
return nmsPlayer.getBukkitEntity();
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.log(
|
||||
java.util.logging.Level.WARNING,
|
||||
e,
|
||||
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InventoryView openInventory(@NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory, boolean viewOnly) {
|
||||
ServerPlayer player = getHandle(bukkitPlayer);
|
||||
|
||||
if (player.connection == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider)
|
||||
AbstractContainerMenu menu;
|
||||
Component title;
|
||||
if (inventory instanceof OpenInventory playerInv) {
|
||||
menu = playerInv.createMenu(player, player.nextContainerCounter(), viewOnly);
|
||||
title = playerInv.getTitle(player);
|
||||
} else if (inventory instanceof OpenEnderChest enderChest) {
|
||||
menu = enderChest.createMenu(player, player.nextContainerCounter(), viewOnly);
|
||||
title = enderChest.getTitle();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Should never happen, player is a ServerPlayer with an active connection.
|
||||
if (menu == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set up title. Title can only be set once for a menu, and is set during the open process.
|
||||
// Further title changes are a hack where the client is sent a "new" inventory with the same ID,
|
||||
// resulting in a title change but no other state modifications (like cursor position).
|
||||
menu.setTitle(title);
|
||||
|
||||
menu = CraftEventFactory.callInventoryOpenEvent(player, menu, false);
|
||||
|
||||
// Menu is null if event is cancelled.
|
||||
if (menu == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
player.containerMenu = menu;
|
||||
player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), menu.getTitle()));
|
||||
player.initMenu(menu);
|
||||
|
||||
return menu.getBukkitView();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,16 +23,16 @@
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
<version>4.3.1</version>
|
||||
<version>5.1.5</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvadapter1_19_R3</artifactId>
|
||||
<name>OpenInvAdapter1_19_R3</name>
|
||||
<artifactId>openinvadapter1_21_R3</artifactId>
|
||||
<name>OpenInvAdapter1_21_R3</name>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<spigot.version>1.19.4-R0.1-SNAPSHOT</spigot.version>
|
||||
<maven.compiler.system>21</maven.compiler.system>
|
||||
<maven.compiler.release>21</maven.compiler.release>
|
||||
<spigot.version>1.21.4-R0.1-SNAPSHOT</spigot.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -51,10 +51,9 @@
|
||||
<dependency>
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvplugincore</artifactId>
|
||||
<artifactId>openinvcommon</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -65,9 +64,6 @@
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3;
|
||||
|
||||
import com.lishid.openinv.internal.Accessor;
|
||||
import com.lishid.openinv.internal.IAnySilentContainer;
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.AnySilentContainer;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenEnderChest;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.Placeholders;
|
||||
import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import net.minecraft.world.Container;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class InternalAccessor implements Accessor {
|
||||
|
||||
private final @NotNull Logger logger;
|
||||
private final @NotNull PlayerManager manager;
|
||||
private final @NotNull AnySilentContainer anySilentContainer;
|
||||
|
||||
public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) {
|
||||
this.logger = logger;
|
||||
manager = new PlayerManager(logger);
|
||||
anySilentContainer = new AnySilentContainer(logger, lang);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PlayerManager getPlayerManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IAnySilentContainer getAnySilentContainer() {
|
||||
return anySilentContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) {
|
||||
return new OpenInventory(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) {
|
||||
return new OpenEnderChest(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ISpecialInventory> @Nullable T get(@NotNull Inventory bukkitInventory, @NotNull Class<T> clazz) {
|
||||
if (!(bukkitInventory instanceof CraftInventory craftInventory)) {
|
||||
return null;
|
||||
}
|
||||
Container container = craftInventory.getInventory();
|
||||
if (clazz.isInstance(container)) {
|
||||
return clazz.cast(container);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(@NotNull ConfigurationSection config) {
|
||||
ConfigurationSection placeholders = config.getConfigurationSection("placeholders");
|
||||
if (placeholders != null) {
|
||||
try {
|
||||
Placeholders.load(placeholders);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+23
-57
@@ -14,22 +14,19 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_19_R3;
|
||||
package com.lishid.openinv.internal.v1_21_R3.container;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.internal.IAnySilentContainer;
|
||||
import com.lishid.openinv.internal.AnySilentContainerBase;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.menu.OpenChestMenu;
|
||||
import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager;
|
||||
import com.lishid.openinv.util.ReflectionHelper;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.ServerPlayerGameMode;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.SimpleMenuProvider;
|
||||
import net.minecraft.world.entity.monster.Shulker;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.PlayerEnderChestContainer;
|
||||
@@ -42,30 +39,31 @@ import net.minecraft.world.level.block.TrappedChestBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class AnySilentContainer implements IAnySilentContainer {
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class AnySilentContainer extends AnySilentContainerBase {
|
||||
|
||||
private final @NotNull Logger logger;
|
||||
private final @NotNull LanguageManager lang;
|
||||
private @Nullable Field serverPlayerGameModeGameType;
|
||||
|
||||
public AnySilentContainer() {
|
||||
public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) {
|
||||
this.logger = logger;
|
||||
this.lang = lang;
|
||||
try {
|
||||
try {
|
||||
this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b");
|
||||
this.serverPlayerGameModeGameType.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
|
||||
logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!");
|
||||
logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues");
|
||||
logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail.");
|
||||
@@ -73,43 +71,11 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class);
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
|
||||
logger.warning("Unable to directly write player game mode! SilentContainer will fail.");
|
||||
logger.log(Level.WARNING, "Error obtaining GameType field", e);
|
||||
logger.log(java.util.logging.Level.WARNING, "Error obtaining GameType field", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox) {
|
||||
org.bukkit.World bukkitWorld = shulkerBox.getWorld();
|
||||
if (!(bukkitWorld instanceof CraftWorld)) {
|
||||
bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID());
|
||||
}
|
||||
|
||||
if (!(bukkitWorld instanceof CraftWorld craftWorld)) {
|
||||
Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!");
|
||||
OpenInv.getPlugin(OpenInv.class).getLogger().log(Level.WARNING, exception.getMessage(), exception);
|
||||
return false;
|
||||
}
|
||||
|
||||
final ServerLevel world = craftWorld.getHandle();
|
||||
final BlockPos blockPosition = new BlockPos(shulkerBox.getX(), shulkerBox.getY(), shulkerBox.getZ());
|
||||
final BlockEntity tile = world.getBlockEntity(blockPosition);
|
||||
|
||||
if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity)
|
||||
|| shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockState blockState = world.getBlockState(blockPosition);
|
||||
|
||||
// See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen
|
||||
AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F)
|
||||
.move(blockPosition)
|
||||
.deflate(1.0E-6D);
|
||||
return !world.noCollision(boundingBox);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activateContainer(
|
||||
@NotNull final Player bukkitPlayer,
|
||||
@@ -123,9 +89,9 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
return true;
|
||||
}
|
||||
|
||||
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
|
||||
ServerPlayer player = PlayerManager.getHandle(bukkitPlayer);
|
||||
|
||||
final ServerLevel level = player.getLevel();
|
||||
final net.minecraft.world.level.Level level = player.level();
|
||||
final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
|
||||
final BlockEntity blockEntity = level.getBlockEntity(blockPos);
|
||||
|
||||
@@ -138,10 +104,10 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
PlayerEnderChestContainer enderChest = player.getEnderChestInventory();
|
||||
enderChest.setActiveChest(enderChestTile);
|
||||
player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> {
|
||||
MenuType<?> containers = PlayerDataManager.getContainers(enderChest.getContainerSize());
|
||||
MenuType<?> containers = OpenChestMenu.getChestMenuType(enderChest.getContainerSize());
|
||||
int rows = enderChest.getContainerSize() / 9;
|
||||
return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows);
|
||||
}, Component.translatable(("container.enderchest"))));
|
||||
}, Component.translatable("container.enderchest")));
|
||||
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
|
||||
return true;
|
||||
}
|
||||
@@ -159,7 +125,7 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true);
|
||||
|
||||
if (menuProvider == null) {
|
||||
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
|
||||
lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -191,7 +157,7 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
|
||||
if (blockEntity instanceof RandomizableContainerBlockEntity lootable) {
|
||||
if (lootable.lootTable != null) {
|
||||
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
|
||||
lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -209,7 +175,7 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
|
||||
ServerPlayer player = PlayerManager.getHandle(bukkitPlayer);
|
||||
|
||||
// Force game mode change without informing plugins or players.
|
||||
// Regular game mode set calls GameModeChangeEvent and is cancellable.
|
||||
@@ -237,7 +203,7 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
this.serverPlayerGameModeGameType.setAccessible(true);
|
||||
this.serverPlayerGameModeGameType.set(player.gameMode, gameMode);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e);
|
||||
}
|
||||
}
|
||||
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.InternalOwned;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.menu.OpenEnderChestMenu;
|
||||
import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.player.StackedItemContents;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.StackedContentsCompatible;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OpenEnderChest implements Container, StackedContentsCompatible, InternalOwned<ServerPlayer>,
|
||||
ISpecialEnderChest {
|
||||
|
||||
private CraftInventory inventory;
|
||||
private @NotNull ServerPlayer owner;
|
||||
private NonNullList<ItemStack> items;
|
||||
private int maxStack = 64;
|
||||
private final List<HumanEntity> transaction = new ArrayList<>();
|
||||
|
||||
public OpenEnderChest(@NotNull org.bukkit.entity.Player player) {
|
||||
this.owner = PlayerManager.getHandle(player);
|
||||
this.items = owner.getEnderChestInventory().items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ServerPlayer getOwnerHandle() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() {
|
||||
if (inventory == null) {
|
||||
inventory = new CraftInventory(this) {
|
||||
@Override
|
||||
public @NotNull InventoryType getType() {
|
||||
return InventoryType.ENDER_CHEST;
|
||||
}
|
||||
};
|
||||
}
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
|
||||
owner = PlayerManager.getHandle(player);
|
||||
NonNullList<ItemStack> activeItems = owner.getEnderChestInventory().items;
|
||||
|
||||
// Guard against size changing. Theoretically on Purpur all row variations still have 6 rows internally.
|
||||
int max = Math.min(items.size(), activeItems.size());
|
||||
for (int index = 0; index < max; ++index) {
|
||||
activeItems.set(index, items.get(index));
|
||||
}
|
||||
|
||||
items = activeItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.entity.Player getPlayer() {
|
||||
return owner.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return items.stream().allMatch(ItemStack::isEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
return index >= 0 && index < items.size() ? items.get(index) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int index, int amount) {
|
||||
ItemStack itemstack = ContainerHelper.removeItem(items, index, amount);
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
setChanged();
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int index) {
|
||||
return index >= 0 && index < items.size() ? items.set(index, ItemStack.EMPTY) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, ItemStack itemStack) {
|
||||
if (index >= 0 && index < items.size()) {
|
||||
items.set(index, itemStack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return maxStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
this.owner.getEnderChestInventory().setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity craftHumanEntity) {
|
||||
transaction.add(craftHumanEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity craftHumanEntity) {
|
||||
transaction.remove(craftHumanEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.entity.Player getOwner() {
|
||||
return getPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int size) {
|
||||
maxStack = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Location getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
items.clear();
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStackedContents(StackedItemContents stackedContents) {
|
||||
for (ItemStack itemstack : items) {
|
||||
stackedContents.accountStack(itemstack);
|
||||
}
|
||||
}
|
||||
|
||||
public Component getTitle() {
|
||||
return Component.translatableWithFallback("openinv.container.enderchest.prefix", "", owner.getName())
|
||||
.append(Component.translatable("container.enderchest"))
|
||||
.append(Component.translatableWithFallback("openinv.container.enderchest.suffix", " - %s", owner.getName()));
|
||||
}
|
||||
|
||||
public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) {
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
return new OpenEnderChestMenu(this, serverPlayer, i, viewOnly);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+364
@@ -0,0 +1,364 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.internal.InternalOwned;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenPlayerInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.menu.OpenInventoryMenu;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.Content;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentCrafting;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentCraftingResult;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentCursor;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentDrop;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentEquipment;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentList;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentOffHand;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentViewOnly;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotViewOnly;
|
||||
import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OpenInventory implements Container, InternalOwned<ServerPlayer>, ISpecialPlayerInventory {
|
||||
|
||||
private final List<Content> slots;
|
||||
private final int size;
|
||||
private ServerPlayer owner;
|
||||
private int maxStackSize = 99;
|
||||
private CraftInventory bukkitEntity;
|
||||
public List<HumanEntity> transaction = new ArrayList<>();
|
||||
|
||||
public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) {
|
||||
owner = PlayerManager.getHandle(bukkitPlayer);
|
||||
|
||||
// Get total size, rounding up to nearest 9 for client compatibility.
|
||||
int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1;
|
||||
size = ((int) Math.ceil(rawSize / 9.0)) * 9;
|
||||
|
||||
slots = NonNullList.withSize(size, new ContentViewOnly(owner));
|
||||
setupSlots();
|
||||
}
|
||||
|
||||
private void setupSlots() {
|
||||
// Top of inventory: Regular contents.
|
||||
int nextIndex = addMainInventory();
|
||||
|
||||
// If inventory is expected size, we can arrange slots to be pretty.
|
||||
Inventory ownerInv = owner.getInventory();
|
||||
if (ownerInv.items.size() == 36
|
||||
&& ownerInv.armor.size() == 4
|
||||
&& ownerInv.offhand.size() == 1
|
||||
&& owner.inventoryMenu.getCraftSlots().getContainerSize() == 4) {
|
||||
// Armor slots: Bottom left.
|
||||
addArmor(36);
|
||||
// Off-hand: Below chestplate.
|
||||
addOffHand(46);
|
||||
// Drop slot: Bottom right.
|
||||
slots.set(53, new ContentDrop(owner));
|
||||
// Cursor slot: Above drop.
|
||||
slots.set(44, new ContentCursor(owner));
|
||||
|
||||
// Crafting is displayed in the bottom right corner.
|
||||
// As we're using the pretty view, this is a 3x2.
|
||||
addCrafting(41, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise we'll just add elements linearly.
|
||||
nextIndex = addArmor(nextIndex);
|
||||
nextIndex = addOffHand(nextIndex);
|
||||
nextIndex = addCrafting(nextIndex, false);
|
||||
slots.set(nextIndex, new ContentCursor(owner));
|
||||
// Drop slot last.
|
||||
slots.set(slots.size() - 1, new ContentDrop(owner));
|
||||
}
|
||||
|
||||
private int addMainInventory() {
|
||||
int listSize = owner.getInventory().items.size();
|
||||
// Hotbar slots are 0-8. We want those to appear on the bottom of the inventory like a normal player inventory,
|
||||
// so everything else needs to move up a row.
|
||||
int hotbarDiff = listSize - 9;
|
||||
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
|
||||
InventoryType.SlotType type;
|
||||
int invIndex;
|
||||
if (localIndex < hotbarDiff) {
|
||||
invIndex = localIndex + 9;
|
||||
type = InventoryType.SlotType.CONTAINER;
|
||||
} else {
|
||||
type = InventoryType.SlotType.QUICKBAR;
|
||||
invIndex = localIndex - hotbarDiff;
|
||||
}
|
||||
|
||||
slots.set(localIndex, new ContentList(owner, invIndex, type) {
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
items = holder.getInventory().items;
|
||||
}
|
||||
});
|
||||
}
|
||||
return listSize;
|
||||
}
|
||||
|
||||
private int addArmor(int startIndex) {
|
||||
int listSize = owner.getInventory().armor.size();
|
||||
|
||||
for (int i = 0; i < listSize; ++i) {
|
||||
// Armor slots go bottom to top; boots are slot 0, helmet is slot 3.
|
||||
// Since we have to display horizontally due to space restrictions,
|
||||
// making the left side the "top" is more user-friendly.
|
||||
int armorIndex;
|
||||
EquipmentSlot slot;
|
||||
switch (i) {
|
||||
case 3 -> {
|
||||
armorIndex = 0;
|
||||
slot = EquipmentSlot.FEET;
|
||||
}
|
||||
case 2 -> {
|
||||
armorIndex = 1;
|
||||
slot = EquipmentSlot.LEGS;
|
||||
}
|
||||
case 1 -> {
|
||||
armorIndex = 2;
|
||||
slot = EquipmentSlot.CHEST;
|
||||
}
|
||||
case 0 -> {
|
||||
armorIndex = 3;
|
||||
slot = EquipmentSlot.HEAD;
|
||||
}
|
||||
default -> {
|
||||
// In the event that new armor slots are added, they can be placed at the end.
|
||||
armorIndex = i;
|
||||
slot = EquipmentSlot.MAINHAND;
|
||||
}
|
||||
}
|
||||
|
||||
slots.set(startIndex + i, new ContentEquipment(owner, armorIndex, slot));
|
||||
}
|
||||
|
||||
return startIndex + listSize;
|
||||
}
|
||||
|
||||
private int addOffHand(int startIndex) {
|
||||
int listSize = owner.getInventory().offhand.size();
|
||||
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
|
||||
slots.set(startIndex + localIndex, new ContentOffHand(owner, localIndex));
|
||||
}
|
||||
return startIndex + listSize;
|
||||
}
|
||||
|
||||
private int addCrafting(int startIndex, boolean pretty) {
|
||||
int listSize = owner.inventoryMenu.getCraftSlots().getContents().size();
|
||||
pretty &= listSize == 4;
|
||||
|
||||
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
|
||||
// Pretty display is a 2x2 rather than linear.
|
||||
// If index is in top row, grid is not 2x2, or pretty is disabled, just use current index.
|
||||
// Otherwise, subtract 2 and add 9 to start in the same position on the next row.
|
||||
int modIndex = startIndex + (localIndex < 2 || !pretty ? localIndex : localIndex + 7);
|
||||
|
||||
slots.set(modIndex, new ContentCrafting(owner, localIndex));
|
||||
}
|
||||
|
||||
if (pretty) {
|
||||
slots.set(startIndex + 2, new ContentViewOnly(owner) {
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotViewOnly(container, slot, x, y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return Placeholders.craftingOutput;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
slots.set(startIndex + 11, new ContentCraftingResult(owner));
|
||||
}
|
||||
|
||||
return startIndex + listSize;
|
||||
}
|
||||
|
||||
public Slot getMenuSlot(int index, int x, int y) {
|
||||
return slots.get(index).asSlot(this, index, x, y);
|
||||
}
|
||||
|
||||
public InventoryType.SlotType getSlotType(int index) {
|
||||
return slots.get(index).getSlotType();
|
||||
}
|
||||
|
||||
public @NotNull Component getTitle(@Nullable ServerPlayer viewer) {
|
||||
MutableComponent component = Component.empty();
|
||||
// Prefix for use with custom bitmap image fonts.
|
||||
if (owner.equals(viewer)) {
|
||||
component.append(
|
||||
Component.translatableWithFallback("openinv.container.inventory.self", "")
|
||||
.withStyle(style -> style
|
||||
.withFont(ResourceLocation.parse("openinv:font/inventory"))
|
||||
.withColor(ChatFormatting.WHITE)));
|
||||
} else {
|
||||
component.append(
|
||||
Component.translatableWithFallback("openinv.container.inventory.other", "")
|
||||
.withStyle(style -> style
|
||||
.withFont(ResourceLocation.parse("openinv:font/inventory"))
|
||||
.withColor(ChatFormatting.WHITE)));
|
||||
}
|
||||
// Normal title: "Inventory - OwnerName"
|
||||
component.append(Component.translatableWithFallback("openinv.container.inventory.prefix", "", owner.getName()))
|
||||
.append(Component.translatable("container.inventory"))
|
||||
.append(Component.translatableWithFallback("openinv.container.inventory.suffix", " - %s", owner.getName()));
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerPlayer getOwnerHandle() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() {
|
||||
if (bukkitEntity == null) {
|
||||
bukkitEntity = new OpenPlayerInventory(this);
|
||||
}
|
||||
return bukkitEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
|
||||
ServerPlayer newOwner = PlayerManager.getHandle(player);
|
||||
// Only transfer regular inventory - crafting and cursor slots are transient.
|
||||
newOwner.getInventory().replaceWith(owner.getInventory());
|
||||
owner = newOwner;
|
||||
// Update slots to point to new inventory.
|
||||
slots.forEach(slot -> slot.setHolder(newOwner));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {}
|
||||
|
||||
@Override
|
||||
public boolean isInUse() {
|
||||
return !transaction.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.entity.Player getPlayer() {
|
||||
return getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return slots.stream().map(Content::get).allMatch(ItemStack::isEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
return slots.get(index).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int index, int amount) {
|
||||
return slots.get(index).removePartial(amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int index) {
|
||||
return slots.get(index).remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, ItemStack itemStack) {
|
||||
slots.get(index).set(itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return maxStackSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int maxStackSize) {
|
||||
this.maxStackSize = maxStackSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
NonNullList<ItemStack> contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY);
|
||||
for (int i = 0; i < getContainerSize(); ++i) {
|
||||
contents.set(i, getItem(i));
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity viewer) {
|
||||
transaction.add(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity viewer) {
|
||||
transaction.remove(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.entity.Player getOwner() {
|
||||
return owner.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return owner.getBukkitEntity().getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
owner.getInventory().clearContent();
|
||||
owner.inventoryMenu.getCraftSlots().clearContent();
|
||||
owner.inventoryMenu.slotsChanged(owner.inventoryMenu.getCraftSlots());
|
||||
owner.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) {
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
return new OpenInventoryMenu(this, serverPlayer, i, viewOnly);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.TagParser;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.component.CustomModelData;
|
||||
import net.minecraft.world.item.component.DyedItemColor;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraft.world.level.block.entity.BannerPattern;
|
||||
import net.minecraft.world.level.block.entity.BannerPatternLayers;
|
||||
import net.minecraft.world.level.block.entity.BannerPatterns;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.CraftRegistry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class Placeholders {
|
||||
|
||||
private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(List.of(), List.of(), List.of("openinv:custom"), List.of());
|
||||
public static final @NotNull EnumMap<GameType, ItemStack> BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class);
|
||||
public static @NotNull ItemStack craftingOutput = defaultCraftingOutput();
|
||||
public static @NotNull ItemStack cursor = defaultCursor();
|
||||
public static @NotNull ItemStack drop = defaultDrop();
|
||||
public static @NotNull ItemStack emptyHelmet = getEmptyArmor(Items.LEATHER_HELMET);
|
||||
public static @NotNull ItemStack emptyChestplate = getEmptyArmor(Items.LEATHER_CHESTPLATE);
|
||||
public static @NotNull ItemStack emptyLeggings = getEmptyArmor(Items.LEATHER_LEGGINGS);
|
||||
public static @NotNull ItemStack emptyBoots = getEmptyArmor(Items.LEATHER_BOOTS);
|
||||
public static @NotNull ItemStack emptyOffHand = getEmptyShield();
|
||||
public static @NotNull ItemStack notSlot = defaultNotSlot();
|
||||
public static @NotNull ItemStack blockedOffline = defaultBlockedOffline();
|
||||
|
||||
static {
|
||||
for (GameType type : GameType.values()) {
|
||||
// Barrier: "Not available - Creative" etc.
|
||||
ItemStack typeItem = new ItemStack(Items.BARRIER);
|
||||
typeItem.set(
|
||||
DataComponents.ITEM_NAME,
|
||||
Component.translatable("options.narrator.notavailable").append(" - ").append(type.getShortDisplayName()));
|
||||
BLOCKED_GAME_TYPE.put(type, typeItem);
|
||||
}
|
||||
}
|
||||
|
||||
public static void load(@NotNull ConfigurationSection section) throws Exception {
|
||||
craftingOutput = parse(section, "crafting-output", craftingOutput);
|
||||
cursor = parse(section, "cursor", cursor);
|
||||
drop = parse(section, "drop", drop);
|
||||
emptyHelmet = parse(section, "empty-helmet", emptyHelmet);
|
||||
emptyChestplate = parse(section, "empty-chestplate", emptyChestplate);
|
||||
emptyLeggings = parse(section, "empty-leggings", emptyLeggings);
|
||||
emptyBoots = parse(section, "empty-boots", emptyBoots);
|
||||
emptyOffHand = parse(section, "empty-off-hand", emptyOffHand);
|
||||
notSlot = parse(section, "not-a-slot", notSlot);
|
||||
blockedOffline = parse(section, "blocked.offline", blockedOffline);
|
||||
BLOCKED_GAME_TYPE.put(GameType.CREATIVE, parse(section, "blocked.creative", BLOCKED_GAME_TYPE.get(GameType.CREATIVE)));
|
||||
BLOCKED_GAME_TYPE.put(GameType.SPECTATOR, parse(section, "blocked.spectator", BLOCKED_GAME_TYPE.get(GameType.SPECTATOR)));
|
||||
}
|
||||
|
||||
private static @NotNull ItemStack parse(
|
||||
@NotNull ConfigurationSection section,
|
||||
@NotNull String path,
|
||||
@NotNull ItemStack defaultStack) throws Exception {
|
||||
String itemText = section.getString(path);
|
||||
|
||||
if (itemText == null) {
|
||||
return defaultStack;
|
||||
}
|
||||
|
||||
CompoundTag compoundTag = TagParser.parseTag(itemText);
|
||||
Optional<ItemStack> parsed = ItemStack.parse(CraftRegistry.getMinecraftRegistry(), compoundTag);
|
||||
return parsed.filter(itemStack -> !itemStack.isEmpty()).orElse(defaultStack);
|
||||
}
|
||||
|
||||
public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) {
|
||||
if (serverPlayer.connection == null || serverPlayer.connection.isDisconnected()) {
|
||||
return blockedOffline;
|
||||
}
|
||||
|
||||
return BLOCKED_GAME_TYPE.getOrDefault(serverPlayer.gameMode.getGameModeForPlayer(), ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
private static ItemStack defaultCraftingOutput() {
|
||||
// Crafting table: "Crafting"
|
||||
ItemStack itemStack = new ItemStack(Items.CRAFTING_TABLE);
|
||||
itemStack.set(DataComponents.ITEM_NAME, Component.translatable("container.crafting"));
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultCursor() {
|
||||
// Cursor-like banner with no tooltip
|
||||
ItemStack itemStack = new ItemStack(Items.WHITE_BANNER);
|
||||
RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry();
|
||||
Registry<BannerPattern> bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN);
|
||||
BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT).value();
|
||||
BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT).value();
|
||||
BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER).value();
|
||||
itemStack.set(DataComponents.BANNER_PATTERNS,
|
||||
new BannerPatternLayers(List.of(
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY))));
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultDrop() {
|
||||
// Dropper: "Drop Selected Item"
|
||||
ItemStack itemStack = new ItemStack(Items.DROPPER);
|
||||
// Note: translatable component, not keybind component! We want the text identifying the keybind, not the key.
|
||||
itemStack.set(DataComponents.ITEM_NAME, Component.translatable("key.drop"));
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack getEmptyArmor(ItemLike item) {
|
||||
// Inventory-background-grey-ish leather armor with no tooltip
|
||||
ItemStack itemStack = new ItemStack(item);
|
||||
DyedItemColor color = new DyedItemColor(0xC8C8C8, false);
|
||||
itemStack.set(DataComponents.DYED_COLOR, color);
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack getEmptyShield() {
|
||||
ItemStack itemStack = new ItemStack(Items.SHIELD);
|
||||
itemStack.set(DataComponents.BASE_COLOR, DyeColor.MAGENTA);
|
||||
RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry();
|
||||
Registry<BannerPattern> bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN);
|
||||
BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL).value();
|
||||
BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT).value();
|
||||
BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT).value();
|
||||
BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT).value();
|
||||
BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT).value();
|
||||
itemStack.set(DataComponents.BANNER_PATTERNS,
|
||||
new BannerPatternLayers(List.of(
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfLeft), DyeColor.BLACK),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topLeft), DyeColor.MAGENTA),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK))));
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultNotSlot() {
|
||||
// White pane with no tooltip
|
||||
ItemStack itemStack = new ItemStack(Items.WHITE_STAINED_GLASS_PANE);
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultBlockedOffline() {
|
||||
// Barrier: "Not available - Offline"
|
||||
ItemStack itemStack = new ItemStack(Items.BARRIER);
|
||||
itemStack.set(DataComponents.ITEM_NAME,
|
||||
Component.translatable("options.narrator.notavailable")
|
||||
.append(Component.literal(" - "))
|
||||
.append(Component.translatable("gui.socialInteractions.status_offline")));
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private Placeholders() {
|
||||
throw new IllegalStateException("Cannot create instance of utility class.");
|
||||
}
|
||||
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.bukkit;
|
||||
|
||||
import com.lishid.openinv.internal.ViewOnly;
|
||||
import net.minecraft.world.Container;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
* A locked down "empty" inventory that rejects plugin interaction.
|
||||
*/
|
||||
public class OpenDummyInventory extends CraftInventory implements ViewOnly {
|
||||
|
||||
private final InventoryType type;
|
||||
|
||||
public OpenDummyInventory(Container inventory, InventoryType type) {
|
||||
super(inventory);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getItem(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, @Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> addItem(@NotNull ItemStack... items) throws IllegalArgumentException {
|
||||
return arrayToHashMap(items);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> removeItem(@NotNull ItemStack... items) throws IllegalArgumentException {
|
||||
return arrayToHashMap(items);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
private static @NotNull HashMap<Integer, ItemStack> arrayToHashMap(@NotNull ItemStack[] items) {
|
||||
HashMap<Integer, ItemStack> ignored = new HashMap<>();
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
ignored.put(index, items[index]);
|
||||
}
|
||||
return ignored;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getContents() {
|
||||
return new ItemStack[getSize()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContents(@NotNull ItemStack[] items) throws IllegalArgumentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getStorageContents() {
|
||||
return new ItemStack[getSize()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStorageContents(@NotNull ItemStack[] items) throws IllegalArgumentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull Material material) throws IllegalArgumentException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@Nullable ItemStack item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull Material material, int amount) throws IllegalArgumentException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@Nullable ItemStack item, int amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAtLeast(@Nullable ItemStack item, int amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> all(
|
||||
@NotNull Material material) throws IllegalArgumentException {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> all(@Nullable ItemStack item) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int first(@NotNull Material material) throws IllegalArgumentException {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int first(@NotNull ItemStack item) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int firstEmpty() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull Material material) throws IllegalArgumentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(int index) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ListIterator<ItemStack> iterator() {
|
||||
return Collections.emptyListIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ListIterator<ItemStack> iterator(int index) {
|
||||
return Collections.emptyListIterator();
|
||||
}
|
||||
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.bukkit;
|
||||
|
||||
import net.minecraft.world.Container;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OpenDummyPlayerInventory extends OpenDummyInventory implements PlayerInventory {
|
||||
|
||||
public OpenDummyPlayerInventory(Container inventory) {
|
||||
super(inventory, InventoryType.PLAYER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HumanEntity getHolder() {
|
||||
return (HumanEntity) super.getHolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getArmorContents() {
|
||||
return new ItemStack[4];
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getExtraContents() {
|
||||
return new ItemStack[4];
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getHelmet() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getChestplate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getLeggings() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getBoots() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(@NotNull EquipmentSlot slot, @Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getItem(@NotNull EquipmentSlot slot) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArmorContents(@Nullable ItemStack[] items) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtraContents(@Nullable ItemStack[] items) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHelmet(@Nullable ItemStack helmet) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChestplate(@Nullable ItemStack chestplate) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeggings(@Nullable ItemStack leggings) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoots(@Nullable ItemStack boots) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInMainHand() {
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInMainHand(@Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInOffHand() {
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInOffHand(@Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInHand() {
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInHand(@Nullable ItemStack stack) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeldItemSlot() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeldItemSlot(int slot) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.bukkit;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OpenPlayerInventory extends CraftInventory implements PlayerInventory {
|
||||
|
||||
public OpenPlayerInventory(@NotNull OpenInventory inventory) {
|
||||
super(inventory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull OpenInventory getInventory() {
|
||||
return (OpenInventory) super.getInventory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().getContents());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContents(ItemStack[] items) {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
int size = internal.getContainerSize();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
|
||||
for (int index = 0; index < size; ++index) {
|
||||
if (index < items.length) {
|
||||
internal.setItem(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
} else {
|
||||
internal.setItem(index, net.minecraft.world.item.ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getStorageContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStorageContents(ItemStack[] items) throws IllegalArgumentException {
|
||||
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().items;
|
||||
int size = list.size();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
list.set(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType getType() {
|
||||
return InventoryType.PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Player getHolder() {
|
||||
return getInventory().getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getArmorContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().armor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArmorContents(@Nullable ItemStack[] items) {
|
||||
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().armor;
|
||||
int size = list.size();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
list.set(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getExtraContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtraContents(@Nullable ItemStack[] items) {
|
||||
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().offhand;
|
||||
int size = list.size();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
list.set(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getHelmet() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.HEAD.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHelmet(@Nullable ItemStack helmet) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.HEAD.getIndex(), CraftItemStack.asNMSCopy(helmet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getChestplate() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.HEAD.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChestplate(@Nullable ItemStack chestplate) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.CHEST.getIndex(), CraftItemStack.asNMSCopy(chestplate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getLeggings() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.LEGS.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeggings(@Nullable ItemStack leggings) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.LEGS.getIndex(), CraftItemStack.asNMSCopy(leggings));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getBoots() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.FEET.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoots(@Nullable ItemStack boots) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.FEET.getIndex(), CraftItemStack.asNMSCopy(boots));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInMainHand() {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
return CraftItemStack.asCraftMirror(internal.getItem(internal.selected));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInMainHand(@Nullable ItemStack item) {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
internal.setItem(internal.selected, CraftItemStack.asNMSCopy(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInOffHand() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand.getFirst());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInOffHand(@Nullable ItemStack item) {
|
||||
getInventory().getOwnerHandle().getInventory().offhand.set(0, CraftItemStack.asNMSCopy(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInHand() {
|
||||
return getItemInMainHand();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInHand(@Nullable ItemStack stack) {
|
||||
setItemInMainHand(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeldItemSlot() {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
return internal.items.size() - 9 + internal.selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeldItemSlot(int slot) {
|
||||
slot %= 9;
|
||||
getInventory().getOwnerHandle().getInventory().selected = slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) {
|
||||
return switch (slot) {
|
||||
case HAND -> getItemInMainHand();
|
||||
case OFF_HAND -> getItemInOffHand();
|
||||
case FEET -> getBoots();
|
||||
case LEGS -> getLeggings();
|
||||
case CHEST -> getChestplate();
|
||||
case HEAD -> getHelmet();
|
||||
default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(@NotNull org.bukkit.inventory.EquipmentSlot slot, @Nullable ItemStack item) {
|
||||
switch (slot) {
|
||||
case HAND -> setItemInMainHand(item);
|
||||
case OFF_HAND -> setItemInOffHand(item);
|
||||
case FEET -> setBoots(item);
|
||||
case LEGS -> setLeggings(item);
|
||||
case CHEST -> setChestplate(item);
|
||||
case HEAD -> setHelmet(item);
|
||||
default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.bukkit;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class OpenPlayerInventorySelf extends OpenPlayerInventory {
|
||||
|
||||
private final int offset;
|
||||
|
||||
public OpenPlayerInventorySelf(@NotNull OpenInventory inventory, int offset) {
|
||||
super(inventory);
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
return super.getItem(offset + index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, ItemStack item) {
|
||||
super.setItem(offset + index, item);
|
||||
}
|
||||
|
||||
}
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.menu;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.InternalOwned;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenDummyInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotPlaceholder;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotViewOnly;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.inventory.ClickType;
|
||||
import net.minecraft.world.inventory.ContainerData;
|
||||
import net.minecraft.world.inventory.ContainerListener;
|
||||
import net.minecraft.world.inventory.ContainerSynchronizer;
|
||||
import net.minecraft.world.inventory.DataSlot;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventoryView;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}.
|
||||
*/
|
||||
public abstract class OpenChestMenu<T extends Container & ISpecialInventory & InternalOwned<ServerPlayer>>
|
||||
extends AbstractContainerMenu {
|
||||
|
||||
protected static final int BOTTOM_INVENTORY_SIZE = 36;
|
||||
|
||||
protected final T container;
|
||||
protected final ServerPlayer viewer;
|
||||
protected final boolean viewOnly;
|
||||
protected final boolean ownContainer;
|
||||
protected final int topSize;
|
||||
private CraftInventoryView<OpenChestMenu<T>, Inventory> bukkitEntity;
|
||||
// Syncher fields
|
||||
private @Nullable ContainerSynchronizer synchronizer;
|
||||
private final List<DataSlot> dataSlots = new ArrayList<>();
|
||||
private final IntList remoteDataSlots = new IntArrayList();
|
||||
private final List<ContainerListener> containerListeners = new ArrayList<>();
|
||||
private ItemStack remoteCarried = ItemStack.EMPTY;
|
||||
private boolean suppressRemoteUpdates;
|
||||
|
||||
protected OpenChestMenu(
|
||||
@NotNull MenuType<ChestMenu> type,
|
||||
int containerCounter,
|
||||
@NotNull T container,
|
||||
@NotNull ServerPlayer viewer,
|
||||
boolean viewOnly) {
|
||||
super(type, containerCounter);
|
||||
this.container = container;
|
||||
this.viewer = viewer;
|
||||
this.viewOnly = viewOnly;
|
||||
ownContainer = container.getOwnerHandle().equals(viewer);
|
||||
topSize = getTopSize(viewer);
|
||||
|
||||
preSlotSetup();
|
||||
|
||||
int upperRows = topSize / 9;
|
||||
// View's upper inventory - our container
|
||||
for (int row = 0; row < upperRows; ++row) {
|
||||
for (int col = 0; col < 9; ++col) {
|
||||
// x and y for client purposes, but hey, we're thorough here.
|
||||
// Adapted from net.minecraft.world.inventory.ChestMenu
|
||||
int x = 8 + col * 18;
|
||||
int y = 18 + row * 18;
|
||||
int index = row * 9 + col;
|
||||
|
||||
// Guard against weird inventory sizes.
|
||||
if (index >= container.getContainerSize()) {
|
||||
addSlot(new SlotViewOnly(container, index, x, y));
|
||||
continue;
|
||||
}
|
||||
|
||||
Slot slot = getUpperSlot(index, x, y);
|
||||
|
||||
addSlot(slot);
|
||||
}
|
||||
}
|
||||
|
||||
// View's lower inventory - viewer inventory
|
||||
int playerInvPad = (upperRows - 4) * 18;
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
for (int col = 0; col < 9; ++col) {
|
||||
int x = 8 + col * 18;
|
||||
int y = playerInvPad + row * 18 + 103;
|
||||
addSlot(new Slot(viewer.getInventory(), row * 9 + col + 9, x, y));
|
||||
}
|
||||
}
|
||||
// Hotbar
|
||||
for (int col = 0; col < 9; ++col) {
|
||||
int x = 8 + col * 18;
|
||||
int y = playerInvPad + 161;
|
||||
addSlot(new Slot(viewer.getInventory(), col, x, y));
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull MenuType<ChestMenu> getChestMenuType(int inventorySize) {
|
||||
inventorySize = ((int) Math.ceil(inventorySize / 9.0)) * 9;
|
||||
return switch (inventorySize) {
|
||||
case 9 -> MenuType.GENERIC_9x1;
|
||||
case 18 -> MenuType.GENERIC_9x2;
|
||||
case 27 -> MenuType.GENERIC_9x3;
|
||||
case 36 -> MenuType.GENERIC_9x4;
|
||||
case 45 -> MenuType.GENERIC_9x5;
|
||||
case 54 -> MenuType.GENERIC_9x6;
|
||||
default -> throw new IllegalArgumentException("Inventory size unsupported: " + inventorySize);
|
||||
};
|
||||
}
|
||||
|
||||
protected void preSlotSetup() {}
|
||||
|
||||
protected @NotNull Slot getUpperSlot(int index, int x, int y) {
|
||||
if (viewOnly) {
|
||||
return new SlotViewOnly(container, index, x, y);
|
||||
}
|
||||
return new Slot(container, index, x, y);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final @NotNull CraftInventoryView<OpenChestMenu<T>, Inventory> getBukkitView() {
|
||||
if (bukkitEntity == null) {
|
||||
bukkitEntity = createBukkitEntity();
|
||||
}
|
||||
|
||||
return bukkitEntity;
|
||||
}
|
||||
|
||||
protected @NotNull CraftInventoryView<OpenChestMenu<T>, Inventory> createBukkitEntity() {
|
||||
Inventory top;
|
||||
if (viewOnly) {
|
||||
top = new OpenDummyInventory(container, container.getBukkitType());
|
||||
} else {
|
||||
top = container.getBukkitInventory();
|
||||
}
|
||||
return new CraftInventoryView<>(viewer.getBukkitEntity(), top, this) {
|
||||
@Override
|
||||
public @Nullable Inventory getInventory(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return null;
|
||||
}
|
||||
return super.getInventory(rawSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertSlot(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return InventoryView.OUTSIDE;
|
||||
}
|
||||
return super.convertSlot(rawSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType.SlotType getSlotType(int slot) {
|
||||
if (viewOnly) {
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
return super.getSlotType(slot);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private int getTopSize(ServerPlayer viewer) {
|
||||
MenuType<?> menuType = getType();
|
||||
if (menuType == null) {
|
||||
throw new IllegalStateException("MenuType cannot be null!");
|
||||
} else if (menuType == MenuType.GENERIC_9x1) {
|
||||
return 9;
|
||||
} else if (menuType == MenuType.GENERIC_9x2) {
|
||||
return 18;
|
||||
} else if (menuType == MenuType.GENERIC_9x3) {
|
||||
return 27;
|
||||
} else if (menuType == MenuType.GENERIC_9x4) {
|
||||
return 36;
|
||||
} else if (menuType == MenuType.GENERIC_9x5) {
|
||||
return 45;
|
||||
} else if (menuType == MenuType.GENERIC_9x6) {
|
||||
return 54;
|
||||
}
|
||||
// This is a bit gross, but allows us a safe fallthrough.
|
||||
return menuType.create(-1, viewer.getInventory()).slots.size() - BOTTOM_INVENTORY_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reimplementation of {@link AbstractContainerMenu#moveItemStackTo(ItemStack, int, int, boolean)} that ignores fake
|
||||
* slots and respects {@link Slot#hasItem()}.
|
||||
*
|
||||
* @param itemStack the stack to quick-move
|
||||
* @param rangeLow the start of the range of slots that can be moved to, inclusive
|
||||
* @param rangeHigh the end of the range of slots that can be moved to, exclusive
|
||||
* @param topDown whether to start at the top of the range or bottom
|
||||
* @return whether the stack was modified as a result of being quick-moved
|
||||
*/
|
||||
@Override
|
||||
protected boolean moveItemStackTo(ItemStack itemStack, int rangeLow, int rangeHigh, boolean topDown) {
|
||||
boolean modified = false;
|
||||
boolean stackable = itemStack.isStackable();
|
||||
Slot firstEmpty = null;
|
||||
|
||||
for (int index = topDown ? rangeHigh - 1 : rangeLow;
|
||||
!itemStack.isEmpty() && (topDown ? index >= rangeLow : index < rangeHigh);
|
||||
index += topDown ? -1 : 1
|
||||
) {
|
||||
Slot slot = slots.get(index);
|
||||
// If the slot cannot be added to, check the next slot.
|
||||
if (slot.isFake() || !slot.mayPlace(itemStack)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (slot.hasItem()) {
|
||||
// If the item isn't stackable, check the next slot.
|
||||
if (!stackable) {
|
||||
continue;
|
||||
}
|
||||
// Otherwise, add as many as we can from our stack to the slot.
|
||||
modified |= addToExistingStack(itemStack, slot);
|
||||
} else {
|
||||
// If this is the first empty slot, keep track of it for later use.
|
||||
if (firstEmpty == null) {
|
||||
firstEmpty = slot;
|
||||
}
|
||||
// If the item isn't stackable, we've located the slot we're adding it to, so we're done.
|
||||
if (!stackable) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the item hasn't been fully added yet, add as many as we can to the first open slot.
|
||||
if (!itemStack.isEmpty() && firstEmpty != null) {
|
||||
firstEmpty.setByPlayer(itemStack.split(Math.min(itemStack.getCount(), firstEmpty.getMaxStackSize(itemStack))));
|
||||
firstEmpty.setChanged();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
private static boolean addToExistingStack(ItemStack itemStack, Slot slot) {
|
||||
ItemStack existing = slot.getItem();
|
||||
|
||||
// If the items aren't the same, we can't add our item.
|
||||
if (!ItemStack.isSameItemSameComponents(itemStack, existing)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int max = slot.getMaxStackSize(existing);
|
||||
int existingCount = existing.getCount();
|
||||
|
||||
// If the stack is already full, we can't add more.
|
||||
if (existingCount >= max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int total = existingCount + itemStack.getCount();
|
||||
|
||||
// If the existing item can accept the entirety of our item, we're done!
|
||||
if (total <= max) {
|
||||
itemStack.setCount(0);
|
||||
existing.setCount(total);
|
||||
slot.setChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, add as many as we can.
|
||||
itemStack.shrink(max - existingCount);
|
||||
existing.setCount(max);
|
||||
slot.setChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clicked(int i, int j, ClickType clickType, Player player) {
|
||||
if (viewOnly) {
|
||||
if (clickType == ClickType.QUICK_CRAFT) {
|
||||
sendAllDataToRemote();
|
||||
}
|
||||
return;
|
||||
}
|
||||
super.clicked(i, j, clickType, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Overrides from here on are purely to modify the sync process to send placeholder items.
|
||||
@Override
|
||||
protected Slot addSlot(Slot slot) {
|
||||
slot.index = this.slots.size();
|
||||
this.slots.add(slot);
|
||||
this.lastSlots.add(ItemStack.EMPTY);
|
||||
this.remoteSlots.add(ItemStack.EMPTY);
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataSlot addDataSlot(DataSlot dataSlot) {
|
||||
this.dataSlots.add(dataSlot);
|
||||
this.remoteDataSlots.add(0);
|
||||
return dataSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDataSlots(ContainerData containerData) {
|
||||
for (int i = 0; i < containerData.getCount(); i++) {
|
||||
this.addDataSlot(DataSlot.forContainer(containerData, i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSlotListener(ContainerListener containerListener) {
|
||||
if (!this.containerListeners.contains(containerListener)) {
|
||||
this.containerListeners.add(containerListener);
|
||||
this.broadcastChanges();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSynchronizer(ContainerSynchronizer containerSynchronizer) {
|
||||
this.synchronizer = containerSynchronizer;
|
||||
this.sendAllDataToRemote();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendAllDataToRemote() {
|
||||
for (int index = 0; index < slots.size(); ++index) {
|
||||
Slot slot = slots.get(index);
|
||||
this.remoteSlots.set(index, (slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem()).copy());
|
||||
}
|
||||
|
||||
remoteCarried = getCarried().copy();
|
||||
|
||||
for (int index = 0; index < this.dataSlots.size(); ++index) {
|
||||
this.remoteDataSlots.set(index, this.dataSlots.get(index).get());
|
||||
}
|
||||
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendInitialData(this, this.remoteSlots, this.remoteCarried, this.remoteDataSlots.toIntArray());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastCarriedItem() {
|
||||
this.remoteCarried = this.getCarried().copy();
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendCarriedChange(this, this.remoteCarried);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSlotListener(ContainerListener containerListener) {
|
||||
this.containerListeners.remove(containerListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastChanges() {
|
||||
for (int index = 0; index < this.slots.size(); ++index) {
|
||||
Slot slot = this.slots.get(index);
|
||||
ItemStack itemstack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem();
|
||||
Supplier<ItemStack> supplier = Suppliers.memoize(itemstack::copy);
|
||||
this.triggerSlotListeners(index, itemstack, supplier);
|
||||
this.synchronizeSlotToRemote(index, itemstack, supplier);
|
||||
}
|
||||
|
||||
this.synchronizeCarriedToRemote();
|
||||
|
||||
for (int index = 0; index < this.dataSlots.size(); ++index) {
|
||||
DataSlot dataSlot = this.dataSlots.get(index);
|
||||
int j = dataSlot.get();
|
||||
if (dataSlot.checkAndClearUpdateFlag()) {
|
||||
this.updateDataSlotListeners(index, j);
|
||||
}
|
||||
|
||||
this.synchronizeDataSlotToRemote(index, j);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastFullState() {
|
||||
for (int index = 0; index < this.slots.size(); ++index) {
|
||||
ItemStack itemstack = this.slots.get(index).getItem();
|
||||
this.triggerSlotListeners(index, itemstack, itemstack::copy);
|
||||
}
|
||||
|
||||
for (int index = 0; index < this.dataSlots.size(); ++index) {
|
||||
DataSlot containerproperty = this.dataSlots.get(index);
|
||||
if (containerproperty.checkAndClearUpdateFlag()) {
|
||||
this.updateDataSlotListeners(index, containerproperty.get());
|
||||
}
|
||||
}
|
||||
|
||||
this.sendAllDataToRemote();
|
||||
}
|
||||
|
||||
private void updateDataSlotListeners(int i, int j) {
|
||||
for (ContainerListener containerListener : this.containerListeners) {
|
||||
containerListener.dataChanged(this, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerSlotListeners(int index, ItemStack itemStack, Supplier<ItemStack> supplier) {
|
||||
ItemStack itemStack1 = this.lastSlots.get(index);
|
||||
if (!ItemStack.matches(itemStack1, itemStack)) {
|
||||
ItemStack itemStack2 = supplier.get();
|
||||
this.lastSlots.set(index, itemStack2);
|
||||
|
||||
for (ContainerListener containerListener : this.containerListeners) {
|
||||
containerListener.slotChanged(this, index, itemStack2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void synchronizeSlotToRemote(int i, ItemStack itemStack, Supplier<ItemStack> supplier) {
|
||||
if (!this.suppressRemoteUpdates) {
|
||||
ItemStack itemStack1 = this.remoteSlots.get(i);
|
||||
if (!ItemStack.matches(itemStack1, itemStack)) {
|
||||
ItemStack itemstack2 = supplier.get();
|
||||
this.remoteSlots.set(i, itemstack2);
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendSlotChange(this, i, itemstack2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void synchronizeDataSlotToRemote(int index, int value) {
|
||||
if (!this.suppressRemoteUpdates) {
|
||||
int existing = this.remoteDataSlots.getInt(index);
|
||||
if (existing != value) {
|
||||
this.remoteDataSlots.set(index, value);
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendDataChange(this, index, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void synchronizeCarriedToRemote() {
|
||||
if (!this.suppressRemoteUpdates && !ItemStack.matches(this.getCarried(), this.remoteCarried)) {
|
||||
this.remoteCarried = this.getCarried().copy();
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendCarriedChange(this, this.remoteCarried);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoteCarried(ItemStack itemstack) {
|
||||
this.remoteCarried = itemstack.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suppressRemoteUpdates() {
|
||||
this.suppressRemoteUpdates = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeRemoteUpdates() {
|
||||
this.suppressRemoteUpdates = false;
|
||||
}
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.menu;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenEnderChest;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class OpenEnderChestMenu extends OpenChestMenu<OpenEnderChest> {
|
||||
|
||||
public OpenEnderChestMenu(
|
||||
@NotNull OpenEnderChest enderChest,
|
||||
@NotNull ServerPlayer viewer,
|
||||
int containerId,
|
||||
boolean viewOnly) {
|
||||
super(getChestMenuType(enderChest.getContainerSize()), containerId, enderChest, viewer, viewOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
if (viewOnly) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
// See ChestMenu
|
||||
Slot slot = this.slots.get(index);
|
||||
|
||||
if (slot.isFake() || !slot.hasItem()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack itemStack = slot.getItem();
|
||||
ItemStack original = itemStack.copy();
|
||||
|
||||
if (index < topSize) {
|
||||
if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
} else if (!this.moveItemStackTo(itemStack, 0, topSize, false)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
if (itemStack.isEmpty()) {
|
||||
slot.setByPlayer(ItemStack.EMPTY);
|
||||
} else {
|
||||
slot.setChanged();
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
}
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.menu;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenDummyPlayerInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenPlayerInventorySelf;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentDrop;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentEquipment;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotViewOnly;
|
||||
import com.lishid.openinv.util.Permissions;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventoryView;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OpenInventoryMenu extends OpenChestMenu<OpenInventory> {
|
||||
|
||||
private int offset;
|
||||
|
||||
public OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i, boolean viewOnly) {
|
||||
super(getMenuType(inventory, viewer), i, inventory, viewer, viewOnly);
|
||||
}
|
||||
|
||||
private static MenuType<ChestMenu> getMenuType(OpenInventory inventory, ServerPlayer viewer) {
|
||||
int size = inventory.getContainerSize();
|
||||
// Disallow duplicate access to own main inventory contents.
|
||||
if (inventory.getOwnerHandle().equals(viewer)) {
|
||||
size -= viewer.getInventory().items.size();
|
||||
size = ((int) Math.ceil(size / 9.0)) * 9;
|
||||
}
|
||||
|
||||
return getChestMenuType(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preSlotSetup() {
|
||||
offset = ownContainer ? viewer.getInventory().items.size() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Slot getUpperSlot(int index, int x, int y) {
|
||||
index += offset;
|
||||
Slot slot = container.getMenuSlot(index, x, y);
|
||||
|
||||
// If the slot cannot be interacted with there's nothing to configure.
|
||||
if (slot.getClass().equals(SlotViewOnly.class)) {
|
||||
return slot;
|
||||
}
|
||||
|
||||
// Remove drop slot if viewer is not allowed to use it.
|
||||
if (slot instanceof ContentDrop.SlotDrop
|
||||
&& (viewOnly || !Permissions.INVENTORY_SLOT_DROP.hasPermission(viewer.getBukkitEntity()))) {
|
||||
return new SlotViewOnly(container, index, x, y);
|
||||
}
|
||||
|
||||
if (slot instanceof ContentEquipment.SlotEquipment equipment) {
|
||||
if (viewOnly) {
|
||||
return SlotViewOnly.wrap(slot);
|
||||
}
|
||||
|
||||
Permissions perm = switch (equipment.getEquipmentSlot()) {
|
||||
case HEAD -> Permissions.INVENTORY_SLOT_HEAD_ANY;
|
||||
case CHEST -> Permissions.INVENTORY_SLOT_CHEST_ANY;
|
||||
case LEGS -> Permissions.INVENTORY_SLOT_LEGS_ANY;
|
||||
case FEET -> Permissions.INVENTORY_SLOT_FEET_ANY;
|
||||
// Off-hand can hold anything, not just equipment.
|
||||
default -> null;
|
||||
};
|
||||
|
||||
// If the viewer doesn't have permission, only allow equipment the viewee can equip in the slot.
|
||||
if (perm != null && !perm.hasPermission(viewer.getBukkitEntity())) {
|
||||
equipment.onlyEquipmentFor(container.getOwnerHandle());
|
||||
}
|
||||
|
||||
// Equipment slots are a core part of the inventory, so they will always be shown.
|
||||
return slot;
|
||||
}
|
||||
|
||||
// When viewing own inventory, only allow access to equipment and drop slots (equipment allowed above).
|
||||
if (ownContainer && !(slot instanceof ContentDrop.SlotDrop)) {
|
||||
return new SlotViewOnly(container, index, x, y);
|
||||
}
|
||||
|
||||
if (viewOnly) {
|
||||
return SlotViewOnly.wrap(slot);
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull CraftInventoryView<OpenChestMenu<OpenInventory>, Inventory> createBukkitEntity() {
|
||||
org.bukkit.inventory.Inventory bukkitInventory;
|
||||
if (viewOnly) {
|
||||
bukkitInventory = new OpenDummyPlayerInventory(container);
|
||||
} else if (ownContainer) {
|
||||
bukkitInventory = new OpenPlayerInventorySelf(container, offset);
|
||||
} else {
|
||||
bukkitInventory = container.getBukkitInventory();
|
||||
}
|
||||
|
||||
return new CraftInventoryView<>(viewer.getBukkitEntity(), bukkitInventory, this) {
|
||||
@Override
|
||||
public org.bukkit.inventory.ItemStack getItem(int index) {
|
||||
if (viewOnly || index < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Slot slot = slots.get(index);
|
||||
return CraftItemStack.asCraftMirror(slot.hasItem() ? slot.getItem() : ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInTop(int rawSlot) {
|
||||
return rawSlot < topSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Inventory getInventory(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return null;
|
||||
}
|
||||
if (rawSlot == InventoryView.OUTSIDE || rawSlot == -1) {
|
||||
return null;
|
||||
}
|
||||
Preconditions.checkArgument(rawSlot >= 0 && rawSlot < topSize + offset + BOTTOM_INVENTORY_SIZE,
|
||||
"Slot %s outside of inventory", rawSlot);
|
||||
if (rawSlot > topSize) {
|
||||
return getBottomInventory();
|
||||
}
|
||||
Slot slot = slots.get(rawSlot);
|
||||
if (slot.isFake()) {
|
||||
return null;
|
||||
}
|
||||
return getTopInventory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertSlot(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return InventoryView.OUTSIDE;
|
||||
}
|
||||
if (rawSlot < 0) {
|
||||
return rawSlot;
|
||||
}
|
||||
if (rawSlot < topSize) {
|
||||
Slot slot = slots.get(rawSlot);
|
||||
if (slot.isFake()) {
|
||||
return InventoryView.OUTSIDE;
|
||||
}
|
||||
return rawSlot;
|
||||
}
|
||||
|
||||
int slot = rawSlot - topSize;
|
||||
|
||||
if (slot >= 27) {
|
||||
slot -= 27;
|
||||
} else {
|
||||
slot += 9;
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType.SlotType getSlotType(int slot) {
|
||||
if (viewOnly || slot < 0) {
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
if (slot >= topSize) {
|
||||
slot -= topSize;
|
||||
if (slot >= 27) {
|
||||
return InventoryType.SlotType.QUICKBAR;
|
||||
}
|
||||
return InventoryType.SlotType.CONTAINER;
|
||||
}
|
||||
return OpenInventoryMenu.this.container.getSlotType(offset + slot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countSlots() {
|
||||
return topSize + BOTTOM_INVENTORY_SIZE;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
if (viewOnly) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
// See ChestMenu and InventoryMenu
|
||||
Slot slot = this.slots.get(index);
|
||||
|
||||
if (!slot.hasItem() || slot.isFake()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack itemStack = slot.getItem();
|
||||
ItemStack originalStack = itemStack.copy();
|
||||
|
||||
if (index < topSize) {
|
||||
// If we're moving top to bottom, do a normal transfer.
|
||||
if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
} else {
|
||||
EquipmentSlot equipmentSlot = player.getEquipmentSlotForItem(itemStack);
|
||||
boolean movedGear = switch (equipmentSlot) {
|
||||
// If this is gear, try to move it to the correct slot first.
|
||||
case OFFHAND, FEET, LEGS, CHEST, HEAD -> {
|
||||
// Locate the correct slot in the contents following the main inventory.
|
||||
for (int extra = container.getOwnerHandle().getInventory().items.size() - offset; extra < topSize; ++extra) {
|
||||
Slot extraSlot = getSlot(extra);
|
||||
if (extraSlot instanceof ContentEquipment.SlotEquipment equipSlot
|
||||
&& equipSlot.getEquipmentSlot() == equipmentSlot) {
|
||||
// If we've found a matching slot, try to move to it.
|
||||
// If this succeeds, even partially, we will not attempt to move to other slots.
|
||||
// Otherwise, armor is already occupied, so we'll fall through to main inventory.
|
||||
yield this.moveItemStackTo(itemStack, extra, extra + 1, false);
|
||||
}
|
||||
}
|
||||
yield false;
|
||||
}
|
||||
// Non-gear gets no special treatment.
|
||||
default -> false;
|
||||
};
|
||||
|
||||
// If main inventory is not available, there's nowhere else to move.
|
||||
if (offset != 0) {
|
||||
if (!movedGear) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
} else {
|
||||
// If we didn't move to a gear slot, try to move to a main inventory slot.
|
||||
if (!movedGear && !this.moveItemStackTo(itemStack, 0, container.getOwnerHandle().getInventory().items.size(), true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itemStack.isEmpty()) {
|
||||
slot.setByPlayer(ItemStack.EMPTY);
|
||||
} else {
|
||||
slot.setChanged();
|
||||
}
|
||||
|
||||
return originalStack;
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* An interface defining behaviors for entries in a {@link Container}. Used to reduce duplicate content reordering.
|
||||
*/
|
||||
public interface Content {
|
||||
|
||||
/**
|
||||
* Update internal holder.
|
||||
*
|
||||
* @param holder the new holder
|
||||
*/
|
||||
void setHolder(@NotNull ServerPlayer holder);
|
||||
|
||||
/**
|
||||
* Get the current item.
|
||||
*
|
||||
* @return the current item
|
||||
*/
|
||||
ItemStack get();
|
||||
|
||||
/**
|
||||
* Remove the current item.
|
||||
*
|
||||
* @return the current item
|
||||
*/
|
||||
ItemStack remove();
|
||||
|
||||
/**
|
||||
* Remove some of the current item.
|
||||
*
|
||||
* @return the current item
|
||||
*/
|
||||
ItemStack removePartial(int amount);
|
||||
|
||||
/**
|
||||
* Set the current item. If slot is currently not usable, will drop item instead.
|
||||
*
|
||||
* @param itemStack the item to set
|
||||
*/
|
||||
void set(ItemStack itemStack);
|
||||
|
||||
/**
|
||||
* Get a {@link Slot} for use in a {@link net.minecraft.world.inventory.AbstractContainerMenu ContainerMenu}. Will
|
||||
* impose any specific restrictions to insertion or removal.
|
||||
*
|
||||
* @param container the backing container
|
||||
* @param slot the slot of the backing container represented
|
||||
* @param x clientside x dimension from top left of inventory, not used
|
||||
* @param y clientside y dimension from top left of inventory, not used
|
||||
* @return a menu slot
|
||||
*/
|
||||
Slot asSlot(Container container, int slot, int x, int y);
|
||||
|
||||
/**
|
||||
* Get a loose Bukkit translation of what this slot stores. For example, any slot that drops items at the owner rather
|
||||
* than insert them will report itself as being {@link org.bukkit.event.inventory.InventoryType.SlotType#OUTSIDE}.
|
||||
*
|
||||
* @return the closes Bukkit slot type
|
||||
*/
|
||||
org.bukkit.event.inventory.InventoryType.SlotType getSlotType();
|
||||
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A slot in a survival crafting inventory. Unavailable when not online in a survival mode.
|
||||
*/
|
||||
public class ContentCrafting implements Content {
|
||||
|
||||
private final int index;
|
||||
private ServerPlayer holder;
|
||||
private List<ItemStack> items;
|
||||
|
||||
public ContentCrafting(@NotNull ServerPlayer holder, int index) {
|
||||
setHolder(holder);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
private boolean isAvailable() {
|
||||
return isAvailable(holder);
|
||||
}
|
||||
|
||||
static boolean isAvailable(@NotNull ServerPlayer holder) {
|
||||
// Player must be online and not in creative - since the creative client is (semi-)authoritative,
|
||||
// it ignores changes without extra help, and will delete the item as a result.
|
||||
// Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory.
|
||||
return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
// Note: CraftingContainer#getItems is immutable! Be careful with updates.
|
||||
this.items = holder.inventoryMenu.getCraftSlots().getContents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return isAvailable() ? items.get(index) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
if (!this.isAvailable()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
ItemStack removed = items.remove(index);
|
||||
if (removed.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
if (!this.isAvailable()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
ItemStack removed = ContainerHelper.removeItem(items, index, amount);
|
||||
if (removed.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
if (isAvailable()) {
|
||||
items.set(index, itemStack);
|
||||
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
|
||||
} else {
|
||||
holder.drop(itemStack, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotCrafting(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return isAvailable() ? InventoryType.SlotType.CRAFTING : InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
public class SlotCrafting extends SlotPlaceholder {
|
||||
|
||||
private SlotCrafting(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return isAvailable() ? items.get(ContentCrafting.this.index) : Placeholders.survivalOnly(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPickup(Player player) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack itemStack) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return isAvailable() && super.hasItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return !isAvailable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.InventoryMenu;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot allowing viewing of the crafting result.
|
||||
*
|
||||
* <p>Unmodifiable because I said so. Use your own crafting grid.</p>
|
||||
*/
|
||||
public class ContentCraftingResult extends ContentViewOnly {
|
||||
|
||||
public ContentCraftingResult(@NotNull ServerPlayer holder) {
|
||||
super(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
InventoryMenu inventoryMenu = holder.inventoryMenu;
|
||||
return inventoryMenu.getResultSlot().getItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotViewOnly(container, slot, x, y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
if (!ContentCrafting.isAvailable(holder)) {
|
||||
return Placeholders.survivalOnly(holder);
|
||||
}
|
||||
InventoryMenu inventoryMenu = holder.inventoryMenu;
|
||||
return inventoryMenu.getResultSlot().getItem();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return InventoryType.SlotType.RESULT;
|
||||
}
|
||||
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot wrapping the active menu's cursor. Unavailable when not online in a survival mode.
|
||||
*/
|
||||
public class ContentCursor implements Content {
|
||||
|
||||
private @NotNull ServerPlayer holder;
|
||||
|
||||
public ContentCursor(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return isAvailable() ? holder.containerMenu.getCarried() : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
ItemStack carried = holder.containerMenu.getCarried();
|
||||
holder.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
return carried;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
ItemStack carried = holder.containerMenu.getCarried();
|
||||
if (!carried.isEmpty() && carried.getCount() >= amount) {
|
||||
ItemStack value = carried.split(amount);
|
||||
if (carried.isEmpty()) {
|
||||
holder.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
if (isAvailable()) {
|
||||
holder.containerMenu.setCarried(itemStack);
|
||||
} else {
|
||||
holder.drop(itemStack, false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAvailable() {
|
||||
// Player must be online and not in creative - since the creative client is (semi-)authoritative,
|
||||
// it ignores changes without extra help, and will delete the item as a result.
|
||||
// Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory.
|
||||
return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotCursor(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
// As close as possible to "not real"
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
public class SlotCursor extends SlotPlaceholder {
|
||||
|
||||
private SlotCursor(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
if (!isAvailable()) {
|
||||
return Placeholders.survivalOnly(holder);
|
||||
}
|
||||
ItemStack carried = holder.containerMenu.getCarried();
|
||||
return carried.isEmpty() ? Placeholders.cursor : carried;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPickup(Player player) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack itemStack) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return isAvailable() && super.hasItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A fake slot used to drop items. Unavailable offline.
|
||||
*/
|
||||
public class ContentDrop implements Content {
|
||||
|
||||
private ServerPlayer holder;
|
||||
|
||||
public ContentDrop(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
holder.drop(itemStack, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotDrop(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
// Behaves like dropping an item outside the screen, just by the target player.
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
public class SlotDrop extends SlotPlaceholder {
|
||||
|
||||
private SlotDrop(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return holder.connection != null && !holder.connection.isDisconnected()
|
||||
? Placeholders.drop
|
||||
: Placeholders.blockedOffline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack itemStack) {
|
||||
return holder.connection != null && !holder.connection.isDisconnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot for equipment that displays placeholders if empty.
|
||||
*/
|
||||
public class ContentEquipment extends ContentList {
|
||||
|
||||
private final ItemStack placeholder;
|
||||
private final EquipmentSlot equipmentSlot;
|
||||
|
||||
public ContentEquipment(ServerPlayer holder, int index, EquipmentSlot equipmentSlot) {
|
||||
super(holder, index, InventoryType.SlotType.ARMOR);
|
||||
placeholder = switch (equipmentSlot) {
|
||||
case HEAD -> Placeholders.emptyHelmet;
|
||||
case CHEST -> Placeholders.emptyChestplate;
|
||||
case LEGS -> Placeholders.emptyLeggings;
|
||||
case FEET -> Placeholders.emptyBoots;
|
||||
default -> Placeholders.emptyOffHand;
|
||||
};
|
||||
this.equipmentSlot = equipmentSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.items = holder.getInventory().armor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotEquipment(container, slot, x, y);
|
||||
}
|
||||
|
||||
public class SlotEquipment extends SlotPlaceholder {
|
||||
|
||||
private ServerPlayer viewer;
|
||||
|
||||
SlotEquipment(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
ItemStack itemStack = getItem();
|
||||
if (!itemStack.isEmpty()) {
|
||||
return itemStack;
|
||||
}
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
public EquipmentSlot getEquipmentSlot() {
|
||||
return equipmentSlot;
|
||||
}
|
||||
|
||||
public void onlyEquipmentFor(ServerPlayer viewer) {
|
||||
this.viewer = viewer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack var0) {
|
||||
if (viewer == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return equipmentSlot == EquipmentSlot.OFFHAND || viewer.getEquipmentSlotForItem(var0) == equipmentSlot;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A normal slot backed by an item list.
|
||||
*/
|
||||
public abstract class ContentList implements Content {
|
||||
|
||||
private final int index;
|
||||
private final InventoryType.SlotType slotType;
|
||||
protected List<ItemStack> items;
|
||||
|
||||
public ContentList(ServerPlayer holder, int index, InventoryType.SlotType slotType) {
|
||||
this.index = index;
|
||||
this.slotType = slotType;
|
||||
setHolder(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return items.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
ItemStack removed = items.remove(index);
|
||||
return removed == null || removed.isEmpty() ? ItemStack.EMPTY : removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
return ContainerHelper.removeItem(items, index, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
items.set(index, itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new Slot(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return slotType;
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot for equipment that updates held items if necessary.
|
||||
*/
|
||||
public class ContentOffHand extends ContentEquipment {
|
||||
|
||||
private ServerPlayer holder;
|
||||
|
||||
public ContentOffHand(ServerPlayer holder, int localIndex) {
|
||||
super(holder, localIndex, EquipmentSlot.OFFHAND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.items = holder.getInventory().offhand;
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return InventoryType.SlotType.QUICKBAR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotEquipment(container, slot, x, y) {
|
||||
@Override
|
||||
public void setChanged() {
|
||||
if (holder.connection != null
|
||||
&& !holder.connection.isDisconnected()
|
||||
&& holder.containerMenu != holder.inventoryMenu) {
|
||||
holder.resendItemInHands();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A view-only slot that can't be interacted with.
|
||||
*/
|
||||
public class ContentViewOnly implements Content {
|
||||
|
||||
@NotNull ServerPlayer holder;
|
||||
|
||||
public ContentViewOnly(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
this.holder.drop(itemStack, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotViewOnly(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
* An implementation of a slot as used by a menu that may have fake placeholder items.
|
||||
*
|
||||
* <p>Used to prevent plugins (particularly sorting plugins) from adding placeholders to inventories.</p>
|
||||
*/
|
||||
public abstract class SlotPlaceholder extends Slot {
|
||||
|
||||
public SlotPlaceholder(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
public abstract ItemStack getOrDefault();
|
||||
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.Placeholders;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A view-only {@link Slot}. "Blank" by default, but can wrap another slot to display its content.
|
||||
*/
|
||||
public class SlotViewOnly extends SlotPlaceholder {
|
||||
|
||||
public static @NotNull SlotViewOnly wrap(@NotNull Slot wrapped) {
|
||||
SlotViewOnly wrapper;
|
||||
if (wrapped instanceof SlotPlaceholder placeholder) {
|
||||
wrapper = new SlotViewOnly(wrapped.container, wrapped.slot, wrapped.x, wrapped.y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return placeholder.getOrDefault();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
wrapper = new SlotViewOnly(wrapped.container, wrapped.slot, wrapped.x, wrapped.y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return wrapped.getItem();
|
||||
}
|
||||
};
|
||||
}
|
||||
wrapper.index = wrapped.index;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public SlotViewOnly(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return Placeholders.notSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuickCraft(ItemStack var0, ItemStack var1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTake(Player var0, ItemStack var1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack var0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByPlayer(ItemStack newStack) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByPlayer(ItemStack newStack, ItemStack oldStack) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack var0) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize(ItemStack itemStack) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove(int amount) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPickup(Player var0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ItemStack> tryRemove(int var0, int var1, Player var2) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack safeTake(int var0, int var1, Player var2) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack safeInsert(ItemStack itemStack) {
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack safeInsert(ItemStack itemStack, int amount) {
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowModification(Player var0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSlot() {
|
||||
return this.slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHighlightable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.player;
|
||||
|
||||
import com.lishid.openinv.event.OpenEvents;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.nbt.NumericTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.storage.PlayerDataStorage;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
|
||||
public class OpenPlayer extends CraftPlayer {
|
||||
|
||||
/**
|
||||
* List of tags to always reset when saving.
|
||||
*
|
||||
* @see net.minecraft.world.entity.Entity#saveWithoutId(CompoundTag)
|
||||
* @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag)
|
||||
* @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag)
|
||||
* @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag)
|
||||
*/
|
||||
private static final Set<String> RESET_TAGS = Set.of(
|
||||
// Entity#saveWithoutId(CompoundTag)
|
||||
"CustomName",
|
||||
"CustomNameVisible",
|
||||
"Silent",
|
||||
"NoGravity",
|
||||
"Glowing",
|
||||
"TicksFrozen",
|
||||
"HasVisualFire",
|
||||
"Tags",
|
||||
"Passengers",
|
||||
// ServerPlayer#addAdditionalSaveData(CompoundTag)
|
||||
// Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle
|
||||
"warden_spawn_tracker",
|
||||
"enteredNetherPosition",
|
||||
"SpawnX",
|
||||
"SpawnY",
|
||||
"SpawnZ",
|
||||
"SpawnForced",
|
||||
"SpawnAngle",
|
||||
"SpawnDimension",
|
||||
"raid_omen_position",
|
||||
"ender_pearls",
|
||||
// Player#addAdditionalSaveData(CompoundTag)
|
||||
"ShoulderEntityLeft",
|
||||
"ShoulderEntityRight",
|
||||
"LastDeathLocation",
|
||||
"current_explosion_impact_pos",
|
||||
// LivingEntity#addAdditionalSaveData(CompoundTag)
|
||||
"active_effects",
|
||||
"SleepingX",
|
||||
"SleepingY",
|
||||
"SleepingZ",
|
||||
"Brain"
|
||||
);
|
||||
|
||||
private final PlayerManager manager;
|
||||
|
||||
OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) {
|
||||
super(server, entity);
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadData() {
|
||||
manager.loadData(getHandle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveData() {
|
||||
if (OpenEvents.saveCancelled(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayer player = this.getHandle();
|
||||
// See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman)
|
||||
try {
|
||||
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
|
||||
|
||||
CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player.getName().getString(), player.getStringUUID()).orElse(null);
|
||||
CompoundTag playerData = getWritableTag(oldData);
|
||||
playerData = player.saveWithoutId(playerData);
|
||||
setExtraData(playerData);
|
||||
|
||||
if (oldData != null) {
|
||||
// Revert certain special data values when offline.
|
||||
revertSpecialValues(playerData, oldData);
|
||||
}
|
||||
|
||||
Path playerDataDir = worldNBTStorage.getPlayerDir().toPath();
|
||||
Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat");
|
||||
NbtIo.writeCompressed(playerData, tempFile);
|
||||
Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat");
|
||||
Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old");
|
||||
Util.safeReplaceFile(dataFile, tempFile, backupFile);
|
||||
} catch (Exception e) {
|
||||
LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Contract("null -> new")
|
||||
private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) {
|
||||
if (oldData == null) {
|
||||
return new CompoundTag();
|
||||
}
|
||||
|
||||
// Copy old data. This is a deep clone, so operating on it should be safe.
|
||||
oldData = oldData.copy();
|
||||
|
||||
// Remove vanilla/server data that is not written every time.
|
||||
oldData.getAllKeys()
|
||||
.removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit"));
|
||||
|
||||
return oldData;
|
||||
}
|
||||
|
||||
private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) {
|
||||
// Revert automatic updates to play timestamps.
|
||||
copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class);
|
||||
copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class);
|
||||
copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class);
|
||||
}
|
||||
|
||||
private <T extends Tag> void copyValue(
|
||||
@NotNull CompoundTag source,
|
||||
@NotNull CompoundTag target,
|
||||
@NotNull String container,
|
||||
@NotNull String key,
|
||||
@SuppressWarnings("SameParameterValue") @NotNull Class<T> tagType) {
|
||||
CompoundTag oldContainer = getTag(source, container, CompoundTag.class);
|
||||
CompoundTag newContainer = getTag(target, container, CompoundTag.class);
|
||||
|
||||
// New container being null means the server implementation doesn't store this data.
|
||||
if (newContainer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If old tag exists, copy it to new location, removing otherwise.
|
||||
setTag(newContainer, key, getTag(oldContainer, key, tagType));
|
||||
}
|
||||
|
||||
private <T extends Tag> @Nullable T getTag(
|
||||
@Nullable CompoundTag container,
|
||||
@NotNull String key,
|
||||
@NotNull Class<T> dataType) {
|
||||
if (container == null) {
|
||||
return null;
|
||||
}
|
||||
Tag value = container.get(key);
|
||||
if (value == null || !dataType.isAssignableFrom(value.getClass())) {
|
||||
return null;
|
||||
}
|
||||
return dataType.cast(value);
|
||||
}
|
||||
|
||||
private <T extends Tag> void setTag(
|
||||
@NotNull CompoundTag container,
|
||||
@NotNull String key,
|
||||
@Nullable T data) {
|
||||
if (data == null) {
|
||||
container.remove(key);
|
||||
} else {
|
||||
container.put(key, data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.player;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenEnderChest;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
import net.minecraft.server.level.ParticleStatus;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.ChatVisiblity;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.event.CraftEventFactory;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class PlayerManager implements com.lishid.openinv.internal.PlayerManager {
|
||||
|
||||
private static boolean paper;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName("io.papermc.paper.configuration.Configuration");
|
||||
paper = true;
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
paper = false;
|
||||
}
|
||||
}
|
||||
|
||||
private final @NotNull Logger logger;
|
||||
private @Nullable Field bukkitEntity;
|
||||
|
||||
public PlayerManager(@NotNull Logger logger) {
|
||||
this.logger = logger;
|
||||
try {
|
||||
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
|
||||
} catch (NoSuchFieldException e) {
|
||||
logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!");
|
||||
logger.log(java.util.logging.Level.WARNING, e.getMessage(), e);
|
||||
bukkitEntity = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull ServerPlayer getHandle(final Player player) {
|
||||
if (player instanceof CraftPlayer) {
|
||||
return ((CraftPlayer) player).getHandle();
|
||||
}
|
||||
|
||||
Server server = player.getServer();
|
||||
ServerPlayer nmsPlayer = null;
|
||||
|
||||
if (server instanceof CraftServer) {
|
||||
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId());
|
||||
}
|
||||
|
||||
if (nmsPlayer == null) {
|
||||
// Could use reflection to examine fields, but it's honestly not worth the bother.
|
||||
throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName());
|
||||
}
|
||||
|
||||
return nmsPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
|
||||
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
ServerLevel worldServer = server.getLevel(Level.OVERWORLD);
|
||||
|
||||
if (worldServer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a new ServerPlayer.
|
||||
ServerPlayer entity = createNewPlayer(server, worldServer, offline);
|
||||
|
||||
// Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak.
|
||||
entity.getAdvancements().stopListening();
|
||||
|
||||
// Try to load the player's data.
|
||||
if (loadData(entity)) {
|
||||
// If data is loaded successfully, return the Bukkit entity.
|
||||
return entity.getBukkitEntity();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private @NotNull ServerPlayer createNewPlayer(
|
||||
@NotNull MinecraftServer server,
|
||||
@NotNull ServerLevel worldServer,
|
||||
@NotNull final OfflinePlayer offline) {
|
||||
// See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile)
|
||||
// See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket)
|
||||
GameProfile profile = new GameProfile(offline.getUniqueId(),
|
||||
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
|
||||
|
||||
ClientInformation dummyInfo = new ClientInformation(
|
||||
"en_us",
|
||||
1, // Reduce distance just in case.
|
||||
ChatVisiblity.HIDDEN, // Don't accept chat.
|
||||
false,
|
||||
ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION,
|
||||
ServerPlayer.DEFAULT_MAIN_HAND,
|
||||
true,
|
||||
false, // Don't list in player list (not that this player is in the list anyway).
|
||||
ParticleStatus.MINIMAL
|
||||
);
|
||||
|
||||
ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo);
|
||||
|
||||
try {
|
||||
injectPlayer(entity);
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.log(
|
||||
java.util.logging.Level.WARNING,
|
||||
e,
|
||||
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
boolean loadData(@NotNull ServerPlayer player) {
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null);
|
||||
|
||||
if (loadedData == null) {
|
||||
// Exceptions with loading are logged by Mojang.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read basic data into the player.
|
||||
player.load(loadedData);
|
||||
// Also read "extra" data.
|
||||
player.readAdditionalSaveData(loadedData);
|
||||
// Game type settings are also loaded separately.
|
||||
player.loadGameTypes(loadedData);
|
||||
|
||||
if (paper) {
|
||||
// Paper: world is not loaded by ServerPlayer#load(CompoundTag).
|
||||
parseWorld(player, loadedData);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) {
|
||||
// See PlayerList#placeNewPlayer
|
||||
World bukkitWorld;
|
||||
if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) {
|
||||
// Modern Bukkit world.
|
||||
bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast")));
|
||||
} else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) {
|
||||
// Legacy Bukkit world.
|
||||
bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world"));
|
||||
} else {
|
||||
// Vanilla player data.
|
||||
DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension")))
|
||||
.resultOrPartial(logger::warning)
|
||||
.map(player.server::getLevel)
|
||||
// If ServerLevel exists, set, otherwise move to spawn.
|
||||
.ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null));
|
||||
return;
|
||||
}
|
||||
if (bukkitWorld == null) {
|
||||
player.spawnIn(null);
|
||||
return;
|
||||
}
|
||||
player.setServerLevel(((CraftWorld) bukkitWorld).getHandle());
|
||||
}
|
||||
|
||||
private void injectPlayer(ServerPlayer player) throws IllegalAccessException {
|
||||
if (bukkitEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
bukkitEntity.setAccessible(true);
|
||||
|
||||
bukkitEntity.set(player, new OpenPlayer(player.server.server, player, this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Player inject(@NotNull Player player) {
|
||||
try {
|
||||
ServerPlayer nmsPlayer = getHandle(player);
|
||||
if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) {
|
||||
return openPlayer;
|
||||
}
|
||||
injectPlayer(nmsPlayer);
|
||||
return nmsPlayer.getBukkitEntity();
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.log(
|
||||
java.util.logging.Level.WARNING,
|
||||
e,
|
||||
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InventoryView openInventory(@NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory, boolean viewOnly) {
|
||||
ServerPlayer player = getHandle(bukkitPlayer);
|
||||
|
||||
if (player.connection == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider)
|
||||
AbstractContainerMenu menu;
|
||||
Component title;
|
||||
if (inventory instanceof OpenInventory playerInv) {
|
||||
menu = playerInv.createMenu(player, player.nextContainerCounter(), viewOnly);
|
||||
title = playerInv.getTitle(player);
|
||||
} else if (inventory instanceof OpenEnderChest enderChest) {
|
||||
menu = enderChest.createMenu(player, player.nextContainerCounter(), viewOnly);
|
||||
title = enderChest.getTitle();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Should never happen, player is a ServerPlayer with an active connection.
|
||||
if (menu == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set up title. Title can only be set once for a menu, and is set during the open process.
|
||||
// Further title changes are a hack where the client is sent a "new" inventory with the same ID,
|
||||
// resulting in a title change but no other state modifications (like cursor position).
|
||||
menu.setTitle(title);
|
||||
|
||||
menu = CraftEventFactory.callInventoryOpenEvent(player, menu, false);
|
||||
|
||||
// Menu is null if event is cancelled.
|
||||
if (menu == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
player.containerMenu = menu;
|
||||
player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), menu.getTitle()));
|
||||
player.initMenu(menu);
|
||||
|
||||
return menu.getBukkitView();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
before_install:
|
||||
- sdk install java 21.0.4-tem
|
||||
- sdk use java 21.0.4-tem
|
||||
- mvn wrapper:wrapper -Dmaven=3.9.8
|
||||
+83
-6
@@ -21,28 +21,49 @@
|
||||
<parent>
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<version>4.3.1</version>
|
||||
<version>5.1.5</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvplugincore</artifactId>
|
||||
<artifactId>openinvplugin</artifactId>
|
||||
<name>OpenInvPlugin</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>planarwrappers</artifactId>
|
||||
<groupId>com.github.jikoo</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>annotations</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>openinvcommon</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_21_R3</artifactId>
|
||||
<version>5.1.5</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_21_R2</artifactId>
|
||||
<version>5.1.5</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>annotations</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>OpenInv</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
@@ -53,10 +74,66 @@
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<filters>
|
||||
<filter>
|
||||
<!-- Always shade entirety of required modules. -->
|
||||
<artifact>com.lishid:openinv*</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<!-- Don't warn about file conflicts that'll be clobbered anyway. -->
|
||||
<excludes>
|
||||
<exclude>META-INF/MANIFEST.MF</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>com.github.jikoo.planarwrappers</pattern>
|
||||
<shadedPattern>com.github.jikoo.openinv.planarwrappers</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-final-jar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.parent.build.directory}</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<includes>
|
||||
<include>${project.build.finalName}.jar</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv;
|
||||
|
||||
import com.lishid.openinv.internal.IAnySilentContainer;
|
||||
import com.lishid.openinv.internal.IPlayerDataManager;
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.util.InventoryAccess;
|
||||
import java.lang.reflect.Constructor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class InternalAccessor {
|
||||
|
||||
private final @NotNull Plugin plugin;
|
||||
private final String version;
|
||||
private boolean supported = false;
|
||||
private IPlayerDataManager playerDataManager;
|
||||
private IAnySilentContainer anySilentContainer;
|
||||
|
||||
InternalAccessor(@NotNull Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
String packageName = plugin.getServer().getClass().getPackage().getName();
|
||||
this.version = packageName.substring(packageName.lastIndexOf('.') + 1);
|
||||
|
||||
try {
|
||||
Class.forName("com.lishid.openinv.internal." + this.version + ".SpecialPlayerInventory");
|
||||
Class.forName("com.lishid.openinv.internal." + this.version + ".SpecialEnderChest");
|
||||
this.playerDataManager = this.createObject(IPlayerDataManager.class, "PlayerDataManager");
|
||||
this.anySilentContainer = this.createObject(IAnySilentContainer.class, "AnySilentContainer");
|
||||
this.supported = InventoryAccess.isUsable();
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
public String getReleasesLink() {
|
||||
|
||||
return switch (version) {
|
||||
case "1_4_5", "1_4_6", "v1_4_R1", "v1_5_R2", "v1_5_R3", "v1_6_R1", "v1_6_R2", "v1_6_R3",
|
||||
"v1_7_R1", "v1_7_R2", "v1_7_R3", "v1_7_R4", "v1_8_R1", "v1_8_R2",
|
||||
"v1_9_R1", "v1_9_R2", "v1_10_R1", "v1_11_R1", "v1_12_R1"
|
||||
-> "https://github.com/lishid/OpenInv/releases/tag/4.0.0 (OpenInv-legacy)";
|
||||
case "v1_13_R1" -> "https://github.com/lishid/OpenInv/releases/tag/4.0.0";
|
||||
case "v1_13_R2" -> "https://github.com/lishid/OpenInv/releases/tag/4.0.7";
|
||||
case "v1_14_R1" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.1";
|
||||
case "v1_16_R1" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.4";
|
||||
case "v1_8_R3", "v1_15_R1", "v1_16_R2" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.5";
|
||||
case "v1_16_R3" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8";
|
||||
case "v1_17_R1", "v1_18_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.1.10";
|
||||
case "v1_19_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.2.2";
|
||||
case "v1_18_R2", "v1_19_R2" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0";
|
||||
default -> "https://github.com/Jikoo/OpenInv/releases";
|
||||
};
|
||||
}
|
||||
|
||||
private @NotNull <T> T createObject(
|
||||
@NotNull Class<? extends T> assignableClass,
|
||||
@NotNull String className,
|
||||
@NotNull Object @NotNull ... params)
|
||||
throws ClassCastException, ReflectiveOperationException {
|
||||
// Fetch internal class if it exists.
|
||||
Class<?> internalClass = Class.forName("com.lishid.openinv.internal." + this.version + "." + className);
|
||||
|
||||
// Quick return: no parameters, no need to fiddle about finding the correct constructor.
|
||||
if (params.length == 0) {
|
||||
return assignableClass.cast(internalClass.getConstructor().newInstance());
|
||||
}
|
||||
|
||||
// Search constructors for one matching the given parameters
|
||||
nextConstructor: for (Constructor<?> constructor : internalClass.getConstructors()) {
|
||||
Class<?>[] requiredClasses = constructor.getParameterTypes();
|
||||
if (requiredClasses.length != params.length) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < params.length; ++i) {
|
||||
if (!requiredClasses[i].isAssignableFrom(params[i].getClass())) {
|
||||
continue nextConstructor;
|
||||
}
|
||||
}
|
||||
return assignableClass.cast(constructor.newInstance(params));
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder("Found class ").append(internalClass.getName())
|
||||
.append(" but cannot find any matching constructors for [");
|
||||
for (Object object : params) {
|
||||
builder.append(object.getClass().getName()).append(", ");
|
||||
}
|
||||
builder.delete(builder.length() - 2, builder.length());
|
||||
|
||||
String message = builder.append(']').toString();
|
||||
this.plugin.getLogger().warning(message);
|
||||
|
||||
throw new NoSuchMethodException(message);
|
||||
}
|
||||
|
||||
private @NotNull <T extends ISpecialInventory> T createSpecialInventory(
|
||||
@NotNull Class<? extends T> assignableClass,
|
||||
@NotNull String className,
|
||||
@NotNull Player player,
|
||||
boolean online) throws InstantiationException {
|
||||
if (!this.supported) {
|
||||
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
|
||||
}
|
||||
try {
|
||||
return this.createObject(assignableClass, className, player, online);
|
||||
} catch (Exception original) {
|
||||
InstantiationException exception = new InstantiationException(String.format("Unable to create a new %s", className));
|
||||
exception.initCause(original.fillInStackTrace());
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the IAnySilentContainer implementation for the current server version.
|
||||
*
|
||||
* @return the IAnySilentContainer
|
||||
* @throws IllegalStateException if server version is unsupported
|
||||
*/
|
||||
public @NotNull IAnySilentContainer getAnySilentContainer() {
|
||||
if (!this.supported) {
|
||||
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
|
||||
}
|
||||
return this.anySilentContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the IPlayerDataManager implementation for the current server version.
|
||||
*
|
||||
* @return the IPlayerDataManager
|
||||
* @throws IllegalStateException if server version is unsupported
|
||||
*/
|
||||
public @NotNull IPlayerDataManager getPlayerDataManager() {
|
||||
if (!this.supported) {
|
||||
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
|
||||
}
|
||||
return this.playerDataManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server implementation version.
|
||||
*
|
||||
* @return the version
|
||||
*/
|
||||
public @NotNull String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the server implementation is supported.
|
||||
*
|
||||
* @return true if initialized for a supported server version
|
||||
*/
|
||||
public boolean isSupported() {
|
||||
return this.supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the ISpecialEnderChest implementation for the given Player, or
|
||||
* null if the current version is unsupported.
|
||||
*
|
||||
* @param player the Player
|
||||
* @param online true if the Player is online
|
||||
* @return the ISpecialEnderChest created
|
||||
* @throws InstantiationException if the ISpecialEnderChest could not be instantiated
|
||||
*/
|
||||
public ISpecialEnderChest newSpecialEnderChest(final Player player, final boolean online) throws InstantiationException {
|
||||
return this.createSpecialInventory(ISpecialEnderChest.class, "SpecialEnderChest", player, online);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the ISpecialPlayerInventory implementation for the given Player..
|
||||
*
|
||||
* @param player the Player
|
||||
* @param online true if the Player is online
|
||||
* @return the ISpecialPlayerInventory created
|
||||
* @throws InstantiationException if the ISpecialPlayerInventory could not be instantiated
|
||||
*/
|
||||
public ISpecialPlayerInventory newSpecialPlayerInventory(final Player player, final boolean online) throws InstantiationException {
|
||||
return this.createSpecialInventory(ISpecialPlayerInventory.class, "SpecialPlayerInventory", player, online);
|
||||
}
|
||||
|
||||
}
|
||||
Loaded 100 of 170 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user