Compare commits

...
38 Commits
Author SHA1 Message Date
Jikoo 7a07ee0b3d Bump version to 4.4.7 for release 2024-06-16 11:53:21 -04:00
Adam e97ac3db88 Fix inventory view construction on 1.21 (#208) 2024-06-16 15:47:09 +00:00
Jikoo f72b6749c4 Bump version to 4.4.7-SNAPSHOT for development 2024-06-14 12:05:47 -04:00
Jikoo f1bcb496cf Bump version to 4.4.6 for release 2024-06-14 12:05:28 -04:00
Adam 89beeca853 Update to 1.21 (#206)
* Update to 1.21

* Add new InventoryView methods
2024-06-14 12:02:11 -04:00
dependabot[bot] 282f8630ab Bump softprops/action-gh-release from 2.0.4 to 2.0.5 (#203)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.0.4 to 2.0.5.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v2.0.4...v2.0.5)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-01 17:07:48 -04:00
dependabot[bot] 8f374ef2a5 Bump com.github.jikoo:planarwrappers from 3.2.0 to 3.2.2 (#201)
Bumps [com.github.jikoo:planarwrappers](https://github.com/Jikoo/PlanarWrappers) from 3.2.0 to 3.2.2.
- [Commits](https://github.com/Jikoo/PlanarWrappers/compare/3.2.0...3.2.2)

---
updated-dependencies:
- dependency-name: com.github.jikoo:planarwrappers
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-01 17:06:46 -04:00
dependabot[bot] 3d81fdd0ca Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.3 to 3.6.0 (#202) 2024-06-01 17:58:26 +00:00
Jikoo 2dde9ba28f Bump version to 4.4.6-SNAPSHOT for development 2024-05-18 17:22:15 -04:00
Jikoo 96d2d2482a Bump version to 4.4.5 for release 2024-05-18 17:21:47 -04:00
Jikoo e543053eff Fix saving issue exacerbated by #183
Closes #199
2024-05-18 17:20:51 -04:00
Jikoo 8134887666 Bump version to 4.4.5-SNAPSHOT for development 2024-05-13 18:25:36 -04:00
Jikoo 7ee6206ffb Bump version to 4.4.4 for release 2024-05-13 18:25:21 -04:00
Adam b81eab8191 Update to 1.20.6 (#198)
* Fix version detection on Paper
* Fix offline player changes being overwritten
2024-05-13 18:10:13 -04:00
dependabot[bot] 8b71d3549f Bump dsaltares/fetch-gh-release-asset from 1.1.1 to 1.1.2 (#197)
Bumps [dsaltares/fetch-gh-release-asset](https://github.com/dsaltares/fetch-gh-release-asset) from 1.1.1 to 1.1.2.
- [Release notes](https://github.com/dsaltares/fetch-gh-release-asset/releases)
- [Commits](https://github.com/dsaltares/fetch-gh-release-asset/compare/1.1.1...1.1.2)

---
updated-dependencies:
- dependency-name: dsaltares/fetch-gh-release-asset
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-07 21:00:32 -04:00
dependabot[bot] dd82f59516 Bump pascalgn/automerge-action from 0.16.2 to 0.16.3 (#196)
Bumps [pascalgn/automerge-action](https://github.com/pascalgn/automerge-action) from 0.16.2 to 0.16.3.
- [Release notes](https://github.com/pascalgn/automerge-action/releases)
- [Commits](https://github.com/pascalgn/automerge-action/compare/v0.16.2...v0.16.3)

---
updated-dependencies:
- dependency-name: pascalgn/automerge-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-07 21:00:11 -04:00
Adam 80d3fb72e6 Update to 1.20.5/1.20.6 (#193)
* Move to API method for shulker expansion check
* Update to 1.20.6
  * Drop 1.20.2 support
* Fix containment check
* Drop 1.19.4 to compile without redoing Actions
2024-05-07 20:59:28 -04:00
dependabot[bot] 2ed23b24e5 Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.2 to 3.5.3 (#195) 2024-05-01 17:11:09 +00:00
dependabot[bot] b6d61d4cc9 Bump softprops/action-gh-release from 0.1.15 to 2.0.4 (#191)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 0.1.15 to 2.0.4.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v0.1.15...v2.0.4)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 22:03:00 -04:00
dependabot[bot] c3f7eaebf4 Bump org.apache.maven.plugins:maven-compiler-plugin (#190) 2024-04-01 17:21:51 +00:00
dependabot[bot] 52c90e1cea Bump org.apache.maven.plugins:maven-assembly-plugin from 3.6.0 to 3.7.1 (#189) 2024-04-01 17:20:43 +00:00
dependabot[bot] d887ff8eae Bump hmarr/auto-approve-action from 3.2.1 to 4.0.0 (#187)
Bumps [hmarr/auto-approve-action](https://github.com/hmarr/auto-approve-action) from 3.2.1 to 4.0.0.
- [Release notes](https://github.com/hmarr/auto-approve-action/releases)
- [Commits](https://github.com/hmarr/auto-approve-action/compare/v3.2.1...v4.0.0)

---
updated-dependencies:
- dependency-name: hmarr/auto-approve-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-02 01:14:49 -05:00
dependabot[bot] 0d5fb6a0ef Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.1 to 3.5.2 (#186) 2024-03-01 17:40:32 +00:00
Adam 6f8a6e868a Improve compatibility with custom NBT (#183) 2024-02-16 22:41:01 -05:00
Jikoo 70c1ecfc09 Bump version to 4.4.4-SNAPSHOT for development 2024-02-08 13:11:46 -05:00
Jikoo 176272a4b9 Bump version to 4.4.3 for release 2024-02-08 13:11:18 -05:00
Adam c102e6650e Fix game type not loading (#180) 2024-02-08 13:07:23 -05:00
dependabot[bot] c8fee82291 Bump pascalgn/automerge-action from 0.15.6 to 0.16.2 (#179)
Bumps [pascalgn/automerge-action](https://github.com/pascalgn/automerge-action) from 0.15.6 to 0.16.2.
- [Release notes](https://github.com/pascalgn/automerge-action/releases)
- [Commits](https://github.com/pascalgn/automerge-action/compare/v0.15.6...v0.16.2)

---
updated-dependencies:
- dependency-name: pascalgn/automerge-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-08 13:02:28 -05:00
dependabot[bot] 0ae9982ce5 Bump actions/download-artifact from 3 to 4 (#177)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-01 16:26:41 -05:00
dependabot[bot] 59d358d907 Bump actions/upload-artifact from 3 to 4 (#176)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-01 16:26:27 -05:00
dependabot[bot] 2621181c5d Bump org.apache.maven.plugins:maven-compiler-plugin (#178) 2024-01-01 18:07:35 +00:00
Jikoo 48d044af8c Bump version to 4.4.3-SNAPSHOT for development 2023-12-29 10:05:02 -05:00
Jikoo 9a7623c209 Bump version to 4.4.2 for release 2023-12-29 10:04:28 -05:00
Adam 78354fdf68 Add support for 1.20.4 (#173) 2023-12-29 10:01:54 -05:00
dependabot[bot] 859179d1fc Bump actions/setup-java from 3 to 4 (#168)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3 to 4.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-01 18:03:17 -05:00
dependabot[bot] 79f53e9086 Bump actions/github-script from 6 to 7 (#169)
Bumps [actions/github-script](https://github.com/actions/github-script) from 6 to 7.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-01 18:02:30 -05:00
dependabot[bot] c14d777519 Bump org.jetbrains:annotations from 24.0.1 to 24.1.0 (#170) 2023-12-01 17:51:58 +00:00
Jikoo 1829babe69 Bump version to 4.4.2-SNAPSHOT for development 2023-11-12 14:29:31 -05:00
34 changed files with 1114 additions and 801 deletions

No files matched your search

+3 -3
View File
@@ -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.3
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
MERGE_LABELS: "dependencies,java"
+4 -4
View File
@@ -15,10 +15,10 @@ jobs:
steps:
- 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 +31,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
- 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
+2 -2
View File
@@ -21,14 +21,14 @@ jobs:
run: bash ./scripts/set_release_env.sh
- name: Download Artifact
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: dist
path: dist
- name: Create Release
id: create-release
uses: softprops/action-gh-release@v0.1.15
uses: softprops/action-gh-release@v2.0.5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
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 }}
+1 -1
View File
@@ -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/
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>openinvparent</artifactId>
<groupId>com.lishid</groupId>
<version>4.4.1</version>
<version>4.4.7</version>
</parent>
<artifactId>openinvapi</artifactId>
@@ -23,6 +23,7 @@ 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.Directional;
import org.bukkit.block.data.type.Chest;
import org.bukkit.entity.Cat;
import org.bukkit.entity.Player;
@@ -80,11 +81,9 @@ public interface IAnySilentContainer {
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;
}
// 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)) {
@@ -96,7 +95,7 @@ public interface IAnySilentContainer {
}
BlockData blockData = block.getBlockData();
if (!(blockData instanceof Chest chest) || ((Chest) blockData).getType() == Chest.Type.SINGLE) {
if (!(blockData instanceof Chest chest) || chest.getType() == Chest.Type.SINGLE) {
return false;
}
@@ -124,10 +123,29 @@ public interface IAnySilentContainer {
/**
* Check if a {@link ShulkerBox} cannot be opened under ordinary circumstances.
*
* @deprecated Use {@link #isShulkerBlocked(Block)}.
* @param shulkerBox the shulker box container
* @return whether the container is blocked
*/
boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox);
@Deprecated(since = "4.4.4", forRemoval = true)
default boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox) {
return isShulkerBlocked(shulkerBox.getBlock());
}
/**
* Check if a shulker box block cannot be opened under ordinary circumstances.
*
* @param shulkerBox the shulker box block
* @return whether the container is blocked
*/
default boolean isShulkerBlocked(@NotNull Block shulkerBox) {
Directional directional = (Directional) shulkerBox.getBlockData();
BlockFace facing = directional.getFacing();
BoundingBox box = new BoundingBox(0, 0, 0, 1, 1, 1);
box.expand(facing, 0.5);
box.shift(facing.getOppositeFace().getDirection());
return shulkerBox.getRelative(facing).getCollisionShape().overlaps(box);
}
/**
* Check if a chest cannot be opened under ordinary circumstances.
@@ -138,7 +156,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();
}
/**
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId>
<version>4.4.1</version>
<version>4.4.7</version>
</parent>
<artifactId>openinvassembly</artifactId>
@@ -1,143 +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 net.minecraft.Util;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.NbtUtils;
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.apache.logging.log4j.LogManager;
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
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()) {
// Preserve certain data when offline.
CompoundTag oldData = worldNBTStorage.load(player);
revertSpecialValues(playerData, oldData);
}
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);
}
}
private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) {
if (oldData == null) {
return;
}
// Prevent vehicle deletion.
if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) {
// See net.minecraft.server.players.PlayerList#save(ServerPlayer)
// See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag)
try {
Tag attach = oldData.get("Attach");
if (attach != null) {
newData.putUUID("Attach", NbtUtils.loadUUID(attach));
}
} catch (IllegalArgumentException ignored) {
// Likely will not re-mount successfully, but at least the mount will not be deleted.
}
newData.put("Entity", oldData.getCompound("Entity"));
newData.put("RootVehicle", oldData.getCompound("RootVehicle"));
}
// 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,
@NotNull Class<T> tagType) {
CompoundTag oldContainer = getTag(source, container, CompoundTag.class);
CompoundTag newContainer = getTag(target, container, CompoundTag.class);
// Container being null means the server implementation doesn't store this data.
if (oldContainer == null || 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(
@NotNull CompoundTag container,
@NotNull String key,
@NotNull Class<T> dataType) {
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);
}
}
}
@@ -23,16 +23,16 @@
<artifactId>openinvparent</artifactId>
<groupId>com.lishid</groupId>
<relativePath>../../pom.xml</relativePath>
<version>4.4.1</version>
<version>4.4.7</version>
</parent>
<artifactId>openinvadapter1_19_R3</artifactId>
<name>OpenInvAdapter1_19_R3</name>
<artifactId>openinvadapter1_20_R3</artifactId>
<name>OpenInvAdapter1_20_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>
<spigot.version>1.20.4-R0.1-SNAPSHOT</spigot.version>
</properties>
<dependencies>
@@ -14,21 +14,17 @@
* 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_20_R3;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.util.ReflectionHelper;
import java.lang.reflect.Field;
import java.util.logging.Logger;
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,19 +37,17 @@ 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;
import java.lang.reflect.Field;
import java.util.logging.Logger;
public class AnySilentContainer implements IAnySilentContainer {
private @Nullable Field serverPlayerGameModeGameType;
@@ -78,37 +72,6 @@ public class AnySilentContainer implements IAnySilentContainer {
}
}
@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,
@@ -236,7 +199,8 @@ public class AnySilentContainer implements IAnySilentContainer {
this.serverPlayerGameModeGameType.setAccessible(true);
this.serverPlayerGameModeGameType.set(player.gameMode, gameMode);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e);
}
}
@@ -14,26 +14,62 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R2;
package com.lishid.openinv.internal.v1_20_R3;
import com.mojang.logging.LogUtils;
import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.NbtUtils;
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_20_R2.CraftServer;
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Set;
public class OpenPlayer extends CraftPlayer {
private static final Set<String> RESET_TAGS = Set.of(
// net.minecraft.world.Entity#saveWithoutId(CompoundTag)
"CustomName",
"CustomNameVisible",
"Silent",
"NoGravity",
"Glowing",
"TicksFrozen",
"HasVisualFire",
"Tags",
"Passengers",
// net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag)
// Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle
"warden_spawn_tracker",
"enteredNetherPosition",
"SpawnX",
"SpawnY",
"SpawnZ",
"SpawnForced",
"SpawnAngle",
"SpawnDimension",
// net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag)
"ShoulderEntityLeft",
"ShoulderEntityRight",
"LastDeathLocation",
// net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag)
"ActiveEffects", // Backwards compat: Renamed from 1.19
"active_effects",
"SleepingX",
"SleepingY",
"SleepingZ",
"Brain"
);
public OpenPlayer(CraftServer server, ServerPlayer entity) {
super(server, entity);
}
@@ -50,46 +86,44 @@ public class OpenPlayer extends CraftPlayer {
try {
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
CompoundTag playerData = player.saveWithoutId(new CompoundTag());
CompoundTag oldData = isOnline() ? null : worldNBTStorage.getPlayerData(player.getStringUUID());
CompoundTag playerData = getWritableTag(oldData);
playerData = player.saveWithoutId(playerData);
setExtraData(playerData);
if (!isOnline()) {
// Preserve certain data when offline.
CompoundTag oldData = worldNBTStorage.load(player);
if (oldData != null) {
// Revert certain special data values when offline.
revertSpecialValues(playerData, oldData);
}
File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir());
Path playerDataDir = worldNBTStorage.getPlayerDir().toPath();
Path file = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat");
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);
Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat");
Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old");
Util.safeReplaceFile(dataFile, file, backupFile);
} catch (Exception e) {
LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
}
}
private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) {
@Contract("null -> new")
private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) {
if (oldData == null) {
return;
return new CompoundTag();
}
// Prevent vehicle deletion.
if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) {
// See net.minecraft.server.players.PlayerList#save(ServerPlayer)
// See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag)
try {
Tag attach = oldData.get("Attach");
if (attach != null) {
newData.putUUID("Attach", NbtUtils.loadUUID(attach));
}
} catch (IllegalArgumentException ignored) {
// Likely will not re-mount successfully, but at least the mount will not be deleted.
}
newData.put("Entity", oldData.getCompound("Entity"));
newData.put("RootVehicle", oldData.getCompound("RootVehicle"));
}
// 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);
@@ -105,8 +139,8 @@ public class OpenPlayer extends CraftPlayer {
CompoundTag oldContainer = getTag(source, container, CompoundTag.class);
CompoundTag newContainer = getTag(target, container, CompoundTag.class);
// Container being null means the server implementation doesn't store this data.
if (oldContainer == null || newContainer == null) {
// New container being null means the server implementation doesn't store this data.
if (newContainer == null) {
return;
}
@@ -115,9 +149,12 @@ public class OpenPlayer extends CraftPlayer {
}
private <T extends Tag> @Nullable T getTag(
@NotNull CompoundTag container,
@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;
@@ -14,11 +14,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R2;
package com.lishid.openinv.internal.v1_20_R3;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.InventoryViewTitle;
import com.lishid.openinv.internal.OpenInventoryView;
import com.mojang.authlib.GameProfile;
import com.mojang.serialization.Dynamic;
@@ -40,11 +41,11 @@ import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R2.CraftServer;
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R2.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftContainer;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R3.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftContainer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.java.JavaPlugin;
@@ -177,6 +178,8 @@ public class PlayerDataManager implements IPlayerDataManager {
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).
@@ -191,10 +194,10 @@ public class PlayerDataManager implements IPlayerDataManager {
World bukkitWorld;
if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) {
// Modern Bukkit world.
bukkitWorld = org.bukkit.Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast")));
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 = org.bukkit.Bukkit.getServer().getWorld(loadedData.getString("world"));
bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world"));
} else {
// Vanilla player data.
DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension")))
@@ -248,12 +251,13 @@ public class PlayerDataManager implements IPlayerDataManager {
return null;
}
InventoryView view = getView(player, inventory);
InventoryViewTitle title = InventoryViewTitle.of(inventory);
if (view == null) {
if (title == null) {
return player.openInventory(inventory.getBukkitInventory());
}
InventoryView view = new OpenInventoryView(player, inventory, title.getTitle(player, inventory));
AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public MenuType<?> getType() {
@@ -261,7 +265,7 @@ public class PlayerDataManager implements IPlayerDataManager {
}
};
container.setTitle(Component.literal(view.getTitle()));
container.setTitle(Component.literal(view.getOriginalTitle()));
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
if (container == null) {
@@ -277,16 +281,6 @@ public class PlayerDataManager implements IPlayerDataManager {
}
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) {
@@ -14,12 +14,9 @@
* 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_20_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;
@@ -33,13 +30,17 @@ 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.craftbukkit.v1_20_R3.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
private final CraftInventory inventory;
@@ -14,16 +14,10 @@
* 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_20_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;
@@ -47,13 +41,20 @@ 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.craftbukkit.v1_20_R3.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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;
public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory {
private final CraftInventory inventory;
@@ -757,7 +758,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
@Override
public boolean contains(ItemStack itemstack) {
return contains(itemStack -> itemStack.isEmpty() && itemStack.is(itemstack.getItem()));
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(itemstack.getItem()));
}
@Override
@@ -23,16 +23,16 @@
<artifactId>openinvparent</artifactId>
<groupId>com.lishid</groupId>
<relativePath>../../pom.xml</relativePath>
<version>4.4.1</version>
<version>4.4.7</version>
</parent>
<artifactId>openinvadapter1_20_R1</artifactId>
<name>OpenInvAdapter1_20_R1</name>
<artifactId>openinvadapter1_20_R4</artifactId>
<name>OpenInvAdapter1_20_R4</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spigot.version>1.20.1-R0.1-SNAPSHOT</spigot.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<spigot.version>1.20.6-R0.1-SNAPSHOT</spigot.version>
</properties>
<dependencies>
@@ -14,21 +14,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R2;
package com.lishid.openinv.internal.v1_20_R4;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.util.ReflectionHelper;
import java.lang.reflect.Field;
import java.util.logging.Logger;
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,19 +37,17 @@ 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_R2.CraftWorld;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.logging.Logger;
public class AnySilentContainer implements IAnySilentContainer {
private @Nullable Field serverPlayerGameModeGameType;
@@ -78,37 +72,6 @@ public class AnySilentContainer implements IAnySilentContainer {
}
}
@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,
@@ -236,7 +199,8 @@ public class AnySilentContainer implements IAnySilentContainer {
this.serverPlayerGameModeGameType.setAccessible(true);
this.serverPlayerGameModeGameType.set(player.gameMode, gameMode);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e);
}
}
@@ -0,0 +1,186 @@
/*
* 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_R4;
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_20_R4.CraftServer;
import org.bukkit.craftbukkit.v1_20_R4.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",
// Player#addAdditionalSaveData(CompoundTag)
"ShoulderEntityLeft",
"ShoulderEntityRight",
"LastDeathLocation",
"current_explosion_impact_pos",
// LivingEntity#addAdditionalSaveData(CompoundTag)
"ActiveEffects", // Backwards compat: Renamed from 1.19
"active_effects",
"SleepingX",
"SleepingY",
"SleepingZ",
"Brain"
);
public OpenPlayer(CraftServer server, ServerPlayer entity) {
super(server, entity);
}
@Override
public void loadData() {
PlayerDataManager.loadData(getHandle());
}
@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 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,
@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);
}
}
}
@@ -14,47 +14,69 @@
* 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_20_R4;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.InventoryViewTitle;
import com.lishid.openinv.internal.OpenInventoryView;
import com.mojang.authlib.GameProfile;
import java.lang.reflect.Field;
import java.util.logging.Logger;
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.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.inventory.MenuType;
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.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.World;
import org.bukkit.craftbukkit.v1_20_R4.CraftServer;
import org.bukkit.craftbukkit.v1_20_R4.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R4.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftContainer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.java.JavaPlugin;
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 PlayerDataManager implements IPlayerDataManager {
private static boolean paper;
static {
try {
Class.forName("io.papermc.paper.configuration.Configuration");
paper = true;
} catch (ClassNotFoundException ignored) {
paper = false;
}
}
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 logger = JavaPlugin.getPlugin(OpenInv.class).getLogger();
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;
}
@@ -74,7 +96,7 @@ public class PlayerDataManager implements IPlayerDataManager {
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");
throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName());
}
return nmsPlayer;
@@ -87,11 +109,6 @@ public class PlayerDataManager implements IPlayerDataManager {
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);
@@ -99,39 +116,105 @@ public class PlayerDataManager implements IPlayerDataManager {
return null;
}
ServerPlayer entity = new ServerPlayer(server, worldServer, profile);
// 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).
);
ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo);
try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
e.printStackTrace();
JavaPlugin.getPlugin(OpenInv.class).getLogger().log(
java.util.logging.Level.WARNING,
e,
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
}
// Load data. This also reads basic data into the player.
return entity;
}
static boolean loadData(@NotNull ServerPlayer player) {
// See CraftPlayer#loadData
CompoundTag loadedData = server.getPlayerList().playerIo.load(entity);
CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null);
if (loadedData == null) {
// Exceptions with loading are logged by Mojang.
return null;
return false;
}
// Read basic data into the player.
player.load(loadedData);
// Also read "extra" data.
entity.readAdditionalSaveData(loadedData);
player.readAdditionalSaveData(loadedData);
// Game type settings are also loaded separately.
player.loadGameTypes(loadedData);
if (entity.level() == null) {
// Paper: Move player to spawn
entity.spawnIn(null);
if (paper) {
// Paper: world is not loaded by ServerPlayer#load(CompoundTag).
parseWorld(player, loadedData);
}
// Return the Bukkit entity.
return entity.getBukkitEntity();
return true;
}
void injectPlayer(ServerPlayer player) throws IllegalAccessException {
private static 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(JavaPlugin.getPlugin(OpenInv.class).getLogger()::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;
}
@@ -141,9 +224,8 @@ public class PlayerDataManager implements IPlayerDataManager {
bukkitEntity.set(player, new OpenPlayer(player.server.server, player));
}
@NotNull
@Override
public Player inject(@NotNull Player player) {
public @NotNull Player inject(@NotNull Player player) {
try {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) {
@@ -152,14 +234,16 @@ public class PlayerDataManager implements IPlayerDataManager {
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
e.printStackTrace();
JavaPlugin.getPlugin(OpenInv.class).getLogger().log(
java.util.logging.Level.WARNING,
e,
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
return player;
}
}
@Nullable
@Override
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
ServerPlayer nmsPlayer = getHandle(player);
@@ -167,12 +251,13 @@ public class PlayerDataManager implements IPlayerDataManager {
return null;
}
InventoryView view = getView(player, inventory);
InventoryViewTitle title = InventoryViewTitle.of(inventory);
if (view == null) {
if (title == null) {
return player.openInventory(inventory.getBukkitInventory());
}
InventoryView view = new OpenInventoryView(player, inventory, title.getTitle(player, inventory));
AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public MenuType<?> getType() {
@@ -180,7 +265,7 @@ public class PlayerDataManager implements IPlayerDataManager {
}
};
container.setTitle(Component.literal(view.getTitle()));
container.setTitle(Component.literal(view.getOriginalTitle()));
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
if (container == null) {
@@ -196,16 +281,6 @@ public class PlayerDataManager implements IPlayerDataManager {
}
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) {
@@ -14,12 +14,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R2;
package com.lishid.openinv.internal.v1_20_R4;
import com.lishid.openinv.internal.ISpecialEnderChest;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
@@ -33,13 +31,17 @@ 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_R2.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventory;
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
private final CraftInventory inventory;
@@ -197,7 +199,7 @@ public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpe
@Override
public boolean canAddItem(ItemStack itemstack) {
for (ItemStack itemstack1 : this.items) {
if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) {
if (itemstack1.isEmpty() || ItemStack.isSameItemSameComponents(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) {
return true;
}
}
@@ -219,7 +221,7 @@ public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpe
private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (ItemStack.isSameItemSameTags(localItem, itemstack)) {
if (ItemStack.isSameItemSameComponents(localItem, itemstack)) {
this.moveItemsBetweenStacks(itemstack, localItem);
if (itemstack.isEmpty()) {
return;
@@ -332,7 +334,7 @@ public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpe
}
@Override
public void fromTag(ListTag listTag) {
public void fromTag(ListTag listTag, HolderLookup.Provider holderLookup) {
for (int i = 0; i < this.getContainerSize(); ++i) {
this.setItem(i, ItemStack.EMPTY);
}
@@ -341,7 +343,7 @@ public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpe
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
if (j < this.getContainerSize()) {
this.setItem(j, ItemStack.of(compoundTag));
this.setItem(j, ItemStack.parseOptional(holderLookup, compoundTag));
}
}
@@ -14,45 +14,43 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R2;
package com.lishid.openinv.internal.v1_20_R4;
import com.google.common.collect.ImmutableList;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.core.NonNullList;
import net.minecraft.core.component.DataComponents;
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.TagKey;
import net.minecraft.world.Container;
import net.minecraft.world.ContainerHelper;
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.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_R4.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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_R2.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R2.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 {
@@ -203,7 +201,8 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
return i + 9;
}
private boolean contains(Predicate<ItemStack> predicate) {
@Override
public boolean contains(Predicate<ItemStack> predicate) {
return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate);
}
@@ -263,7 +262,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
}
private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) {
return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
return !itemstack.isEmpty() && ItemStack.isSameItemSameComponents(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
}
@Override
@@ -336,7 +335,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
@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))) {
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameComponents(itemstack, this.items.get(i))) {
return i;
}
}
@@ -348,7 +347,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
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()) {
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameComponents(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.has(DataComponents.CUSTOM_NAME)) {
return i;
}
}
@@ -379,15 +378,9 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
@Override
public void swapPaint(double d0) {
if (d0 > 0.0D) {
d0 = 1.0D;
}
int i = (int) Math.signum(d0);
if (d0 < 0.0D) {
d0 = -1.0D;
}
this.selected = (int) (this.selected - d0);
this.selected -= i;
while (this.selected < 0) {
this.selected += 9;
@@ -423,25 +416,14 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
}
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());
}
localItemStack = itemstack.copyWithCount(0);
this.setRawItem(i, localItemStack);
}
int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount());
if (k > this.getMaxStackSize() - localItemStack.getCount()) {
k = this.getMaxStackSize() - localItemStack.getCount();
}
int k = Math.min(j, this.getMaxStackSize(localItemStack) - localItemStack.getCount());
if (k != 0) {
j -= k;
@@ -502,7 +484,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
this.items.get(i).setPopTime(5);
itemStack.setCount(0);
return true;
} else if (this.player.getAbilities().instabuild) {
} else if (this.player.hasInfiniteMaterials()) {
itemStack.setCount(0);
return true;
} else {
@@ -519,7 +501,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
}
} while(!itemStack.isEmpty() && itemStack.getCount() < j);
if (itemStack.getCount() == j && this.player.getAbilities().instabuild) {
if (itemStack.getCount() == j && this.player.hasInfiniteMaterials()) {
itemStack.setCount(0);
return true;
} else {
@@ -630,7 +612,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
if (!this.items.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)i);
this.items.get(i).save(compoundTag);
this.items.get(i).save(this.player.registryAccess(), compoundTag);
listTag.add(compoundTag);
}
}
@@ -639,7 +621,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
if (!this.armor.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 100));
this.armor.get(i).save(compoundTag);
this.armor.get(i).save(this.player.registryAccess(), compoundTag);
listTag.add(compoundTag);
}
}
@@ -648,7 +630,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
if (!this.offhand.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 150));
this.offhand.get(i).save(compoundTag);
this.offhand.get(i).save(this.player.registryAccess(), compoundTag);
listTag.add(compoundTag);
}
}
@@ -665,15 +647,13 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
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);
}
ItemStack itemstack = ItemStack.parse(this.player.registryAccess(), compoundTag).orElse(ItemStack.EMPTY);
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);
}
}
@@ -710,23 +690,6 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
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) {
@@ -757,12 +720,11 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
@Override
public boolean contains(ItemStack itemstack) {
return contains(itemStack -> itemStack.isEmpty() && itemStack.is(itemstack.getItem()));
return contains(itemStack -> !itemStack.isEmpty() && ItemStack.isSameItemSameComponents(itemstack, itemStack));
}
@Override
public boolean contains(TagKey<Item> tagKey) {
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey));
}
@@ -23,16 +23,16 @@
<artifactId>openinvparent</artifactId>
<groupId>com.lishid</groupId>
<relativePath>../../pom.xml</relativePath>
<version>4.4.1</version>
<version>4.4.7</version>
</parent>
<artifactId>openinvadapter1_20_R2</artifactId>
<name>OpenInvAdapter1_20_R2</name>
<artifactId>openinvadapter1_21_R1</artifactId>
<name>OpenInvAdapter1_21_R1</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spigot.version>1.20.2-R0.1-SNAPSHOT</spigot.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<spigot.version>1.21-R0.1-SNAPSHOT</spigot.version>
</properties>
<dependencies>
@@ -14,22 +14,17 @@
* 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_R1;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.util.ReflectionHelper;
import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;
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,19 +37,17 @@ 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;
import java.lang.reflect.Field;
import java.util.logging.Logger;
public class AnySilentContainer implements IAnySilentContainer {
private @Nullable Field serverPlayerGameModeGameType;
@@ -75,41 +68,10 @@ public class AnySilentContainer implements IAnySilentContainer {
} 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,
@@ -125,7 +87,7 @@ public class AnySilentContainer implements IAnySilentContainer {
ServerPlayer player = PlayerDataManager.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);
@@ -237,7 +199,8 @@ public class AnySilentContainer implements IAnySilentContainer {
this.serverPlayerGameModeGameType.setAccessible(true);
this.serverPlayerGameModeGameType.set(player.gameMode, gameMode);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e);
}
}
@@ -0,0 +1,59 @@
package com.lishid.openinv.internal.v1_21_R1;
import com.lishid.openinv.internal.ISpecialInventory;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftAbstractInventoryView;
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;
class OpenCraftView extends CraftAbstractInventoryView {
private final @NotNull Player player;
private final @NotNull ISpecialInventory inventory;
private final @NotNull String originalTitle;
private String title = null;
OpenCraftView(@NotNull Player player, @NotNull ISpecialInventory inventory, @NotNull String originalTitle) {
this.player = player;
this.inventory = inventory;
this.originalTitle = originalTitle;
}
@Override
public @NotNull Inventory getTopInventory() {
return inventory.getBukkitInventory();
}
@Override
public @NotNull Inventory getBottomInventory() {
return player.getInventory();
}
@Override
public @NotNull HumanEntity getPlayer() {
return player;
}
@Override
public @NotNull InventoryType getType() {
return inventory.getBukkitInventory().getType();
}
@Override
public @NotNull String getTitle() {
return title == null ? originalTitle : title;
}
@Override
public @NotNull String getOriginalTitle() {
return originalTitle;
}
@Override
public void setTitle(@NotNull String title) {
this.title = title;
}
}
@@ -14,37 +14,78 @@
* 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_R1;
import com.mojang.logging.LogUtils;
import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.NbtUtils;
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_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_21_R1.CraftServer;
import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
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",
// Player#addAdditionalSaveData(CompoundTag)
"ShoulderEntityLeft",
"ShoulderEntityRight",
"LastDeathLocation",
"current_explosion_impact_pos",
// LivingEntity#addAdditionalSaveData(CompoundTag)
"active_effects",
"SleepingX",
"SleepingY",
"SleepingZ",
"Brain"
);
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);
}
PlayerDataManager.loadData(getHandle());
}
@Override
@@ -54,46 +95,44 @@ public class OpenPlayer extends CraftPlayer {
try {
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
CompoundTag playerData = player.saveWithoutId(new CompoundTag());
CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player.getName().getString(), player.getStringUUID()).orElse(null);
CompoundTag playerData = getWritableTag(oldData);
playerData = player.saveWithoutId(playerData);
setExtraData(playerData);
if (!isOnline()) {
// Preserve certain data when offline.
CompoundTag oldData = worldNBTStorage.load(player);
if (oldData != null) {
// Revert certain special data values when offline.
revertSpecialValues(playerData, oldData);
}
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);
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);
}
}
private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) {
@Contract("null -> new")
private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) {
if (oldData == null) {
return;
return new CompoundTag();
}
// Prevent vehicle deletion.
if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) {
// See net.minecraft.server.players.PlayerList#save(ServerPlayer)
// See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag)
try {
Tag attach = oldData.get("Attach");
if (attach != null) {
newData.putUUID("Attach", NbtUtils.loadUUID(attach));
}
} catch (IllegalArgumentException ignored) {
// Likely will not re-mount successfully, but at least the mount will not be deleted.
}
newData.put("Entity", oldData.getCompound("Entity"));
newData.put("RootVehicle", oldData.getCompound("RootVehicle"));
}
// 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);
@@ -109,8 +148,8 @@ public class OpenPlayer extends CraftPlayer {
CompoundTag oldContainer = getTag(source, container, CompoundTag.class);
CompoundTag newContainer = getTag(target, container, CompoundTag.class);
// Container being null means the server implementation doesn't store this data.
if (oldContainer == null || newContainer == null) {
// New container being null means the server implementation doesn't store this data.
if (newContainer == null) {
return;
}
@@ -119,9 +158,12 @@ public class OpenPlayer extends CraftPlayer {
}
private <T extends Tag> @Nullable T getTag(
@NotNull CompoundTag container,
@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;
@@ -14,49 +14,77 @@
* 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_R1;
import com.github.jikoo.planarwrappers.function.TriFunction;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.InventoryViewTitle;
import com.lishid.openinv.internal.OpenInventoryView;
import com.mojang.authlib.GameProfile;
import java.lang.reflect.Field;
import java.util.logging.Logger;
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.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.inventory.MenuType;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.level.dimension.DimensionType;
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.World;
import org.bukkit.craftbukkit.v1_21_R1.CraftServer;
import org.bukkit.craftbukkit.v1_21_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_21_R1.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftContainer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.java.JavaPlugin;
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 PlayerDataManager implements IPlayerDataManager {
private static boolean paper;
private static TriFunction<Player, ISpecialInventory, String, InventoryView> viewProvider;
static {
try {
Class.forName("io.papermc.paper.configuration.Configuration");
paper = true;
} catch (ClassNotFoundException ignored) {
paper = false;
}
try {
Class.forName(Bukkit.getServer().getClass().getPackageName() + ".inventory.CraftAbstractInventoryView");
viewProvider = OpenCraftView::new;
} catch (ClassNotFoundException ignored) {
viewProvider = OpenInventoryView::new;
}
}
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 logger = JavaPlugin.getPlugin(OpenInv.class).getLogger();
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;
}
@@ -76,7 +104,7 @@ public class PlayerDataManager implements IPlayerDataManager {
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");
throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName());
}
return nmsPlayer;
@@ -89,11 +117,6 @@ public class PlayerDataManager implements IPlayerDataManager {
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);
@@ -101,53 +124,105 @@ public class PlayerDataManager implements IPlayerDataManager {
return null;
}
ServerPlayer entity = new ServerPlayer(server, worldServer, profile);
// 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).
);
ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo);
try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
e.printStackTrace();
JavaPlugin.getPlugin(OpenInv.class).getLogger().log(
java.util.logging.Level.WARNING,
e,
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
}
// Load data. This also reads basic data into the player.
return entity;
}
static boolean loadData(@NotNull ServerPlayer player) {
// See CraftPlayer#loadData
CompoundTag loadedData = server.getPlayerList().playerIo.load(entity);
CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null);
if (loadedData == null) {
// Exceptions with loading are logged by Mojang.
return null;
return false;
}
// Read basic data into the player.
player.load(loadedData);
// Also read "extra" data.
entity.readAdditionalSaveData(loadedData);
player.readAdditionalSaveData(loadedData);
// Game type settings are also loaded separately.
player.loadGameTypes(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);
if (paper) {
// Paper: world is not loaded by ServerPlayer#load(CompoundTag).
parseWorld(player, loadedData);
}
// Return the Bukkit entity.
return entity.getBukkitEntity();
return true;
}
void injectPlayer(ServerPlayer player) throws IllegalAccessException {
private static 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(JavaPlugin.getPlugin(OpenInv.class).getLogger()::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;
}
@@ -157,9 +232,8 @@ public class PlayerDataManager implements IPlayerDataManager {
bukkitEntity.set(player, new OpenPlayer(player.server.server, player));
}
@NotNull
@Override
public Player inject(@NotNull Player player) {
public @NotNull Player inject(@NotNull Player player) {
try {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) {
@@ -168,14 +242,16 @@ public class PlayerDataManager implements IPlayerDataManager {
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
e.printStackTrace();
JavaPlugin.getPlugin(OpenInv.class).getLogger().log(
java.util.logging.Level.WARNING,
e,
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
return player;
}
}
@Nullable
@Override
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
ServerPlayer nmsPlayer = getHandle(player);
@@ -183,28 +259,33 @@ public class PlayerDataManager implements IPlayerDataManager {
return null;
}
InventoryView view = getView(player, inventory);
InventoryViewTitle viewTitle = InventoryViewTitle.of(inventory);
if (view == null) {
if (viewTitle == null) {
return player.openInventory(inventory.getBukkitInventory());
}
String originalTitle = viewTitle.getTitle(player, inventory);
InventoryView view = viewProvider.apply(player, inventory, originalTitle);
Component title = Component.literal(originalTitle);
AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public MenuType<?> getType() {
return getContainers(inventory.getBukkitInventory().getSize());
}
};
container.setTitle(title);
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())));
// Note: Reusing component prevents plugins from changing title during InventoryOpenEvent, but there's not much
// we can do about that - we can't call InventoryView#getTitle on older versions without causing an
// IncompatibleClassChangeError due to the fact that InventoryView is now an interface.
nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), title));
nmsPlayer.containerMenu = container;
nmsPlayer.initMenu(container);
@@ -212,23 +293,13 @@ public class PlayerDataManager implements IPlayerDataManager {
}
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 36 -> MenuType.GENERIC_9x4;
case 41, 45 -> MenuType.GENERIC_9x5; // PLAYER
case 54 -> MenuType.GENERIC_9x6;
default -> MenuType.GENERIC_9x3; // Default 27-slot inventory
};
@@ -14,12 +14,10 @@
* 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_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.HolderLookup;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
@@ -33,13 +31,17 @@ 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.craftbukkit.v1_21_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
private final CraftInventory inventory;
@@ -197,7 +199,7 @@ public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpe
@Override
public boolean canAddItem(ItemStack itemstack) {
for (ItemStack itemstack1 : this.items) {
if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) {
if (itemstack1.isEmpty() || ItemStack.isSameItemSameComponents(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) {
return true;
}
}
@@ -219,7 +221,7 @@ public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpe
private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (ItemStack.isSameItemSameTags(localItem, itemstack)) {
if (ItemStack.isSameItemSameComponents(localItem, itemstack)) {
this.moveItemsBetweenStacks(itemstack, localItem);
if (itemstack.isEmpty()) {
return;
@@ -332,7 +334,7 @@ public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpe
}
@Override
public void fromTag(ListTag listTag) {
public void fromTag(ListTag listTag, HolderLookup.Provider holderLookup) {
for (int i = 0; i < this.getContainerSize(); ++i) {
this.setItem(i, ItemStack.EMPTY);
}
@@ -341,7 +343,7 @@ public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpe
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
if (j < this.getContainerSize()) {
this.setItem(j, ItemStack.of(compoundTag));
this.setItem(j, ItemStack.parseOptional(holderLookup, compoundTag));
}
}
@@ -14,45 +14,43 @@
* 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_R1;
import com.google.common.collect.ImmutableList;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.core.NonNullList;
import net.minecraft.core.component.DataComponents;
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.TagKey;
import net.minecraft.world.Container;
import net.minecraft.world.ContainerHelper;
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.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_21_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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 {
@@ -203,7 +201,8 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
return i + 9;
}
private boolean contains(Predicate<ItemStack> predicate) {
@Override
public boolean contains(Predicate<ItemStack> predicate) {
return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate);
}
@@ -263,7 +262,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
}
private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) {
return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
return !itemstack.isEmpty() && ItemStack.isSameItemSameComponents(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
}
@Override
@@ -336,7 +335,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
@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))) {
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameComponents(itemstack, this.items.get(i))) {
return i;
}
}
@@ -348,7 +347,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
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()) {
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameComponents(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.has(DataComponents.CUSTOM_NAME)) {
return i;
}
}
@@ -379,15 +378,9 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
@Override
public void swapPaint(double d0) {
if (d0 > 0.0D) {
d0 = 1.0D;
}
int i = (int) Math.signum(d0);
if (d0 < 0.0D) {
d0 = -1.0D;
}
this.selected = (int) (this.selected - d0);
this.selected -= i;
while (this.selected < 0) {
this.selected += 9;
@@ -423,25 +416,14 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
}
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());
}
localItemStack = itemstack.copyWithCount(0);
this.setRawItem(i, localItemStack);
}
int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount());
if (k > this.getMaxStackSize() - localItemStack.getCount()) {
k = this.getMaxStackSize() - localItemStack.getCount();
}
int k = Math.min(j, this.getMaxStackSize(localItemStack) - localItemStack.getCount());
if (k != 0) {
j -= k;
@@ -474,7 +456,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
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);
compartment.get(i).inventoryTick(this.player.level(), this.player, i, this.selected == i);
}
}
}
@@ -502,7 +484,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
this.items.get(i).setPopTime(5);
itemStack.setCount(0);
return true;
} else if (this.player.getAbilities().instabuild) {
} else if (this.player.hasInfiniteMaterials()) {
itemStack.setCount(0);
return true;
} else {
@@ -519,7 +501,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
}
} while(!itemStack.isEmpty() && itemStack.getCount() < j);
if (itemStack.getCount() == j && this.player.getAbilities().instabuild) {
if (itemStack.getCount() == j && this.player.hasInfiniteMaterials()) {
itemStack.setCount(0);
return true;
} else {
@@ -630,7 +612,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
if (!this.items.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)i);
this.items.get(i).save(compoundTag);
this.items.get(i).save(this.player.registryAccess(), compoundTag);
listTag.add(compoundTag);
}
}
@@ -639,7 +621,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
if (!this.armor.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 100));
this.armor.get(i).save(compoundTag);
this.armor.get(i).save(this.player.registryAccess(), compoundTag);
listTag.add(compoundTag);
}
}
@@ -648,7 +630,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
if (!this.offhand.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 150));
this.offhand.get(i).save(compoundTag);
this.offhand.get(i).save(this.player.registryAccess(), compoundTag);
listTag.add(compoundTag);
}
}
@@ -665,15 +647,13 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
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);
}
ItemStack itemstack = ItemStack.parse(this.player.registryAccess(), compoundTag).orElse(ItemStack.EMPTY);
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);
}
}
@@ -710,23 +690,6 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
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) {
@@ -757,12 +720,11 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
@Override
public boolean contains(ItemStack itemstack) {
return contains(itemStack -> itemStack.isEmpty() && itemStack.sameItem(itemstack));
return contains(itemStack -> !itemStack.isEmpty() && ItemStack.isSameItemSameComponents(itemstack, itemStack));
}
@Override
public boolean contains(TagKey<Item> tagKey) {
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey));
}
+30 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>openinvparent</artifactId>
<groupId>com.lishid</groupId>
<version>4.4.1</version>
<version>4.4.7</version>
</parent>
<artifactId>openinvplugincore</artifactId>
@@ -40,6 +40,10 @@
<artifactId>spigot-api</artifactId>
<groupId>org.spigotmc</groupId>
</dependency>
<dependency>
<artifactId>planarwrappers</artifactId>
<groupId>com.github.jikoo</groupId>
</dependency>
</dependencies>
<build>
@@ -53,6 +57,31 @@
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<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>
@@ -16,58 +16,134 @@
package com.lishid.openinv;
import com.github.jikoo.planarwrappers.util.version.BukkitVersions;
import com.github.jikoo.planarwrappers.util.version.Version;
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;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Constructor;
class InternalAccessor {
private final @NotNull Plugin plugin;
private final String version;
private final @Nullable String versionPackage;
private boolean supported = false;
private IPlayerDataManager playerDataManager;
private IAnySilentContainer anySilentContainer;
InternalAccessor(@NotNull Plugin plugin) {
this.plugin = plugin;
this.versionPackage = getVersionPackage();
checkSupported();
}
String packageName = plugin.getServer().getClass().getPackage().getName();
this.version = packageName.substring(packageName.lastIndexOf('.') + 1);
private void checkSupported() {
if (versionPackage == null) {
return;
}
try {
Class.forName("com.lishid.openinv.internal." + this.version + ".SpecialPlayerInventory");
Class.forName("com.lishid.openinv.internal." + this.version + ".SpecialEnderChest");
Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".SpecialPlayerInventory");
Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".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() {
private @Nullable String getVersionPackage() {
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 20, 4)) // Min supported version: 1.20.4
|| BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 5))) { // 1.20.5 not supported at all.
return null;
}
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 4))) { // 1.20.4
return "v1_20_R3";
}
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 6))) { // 1.20.6
return "v1_20_R4";
}
if (BukkitVersions.MINECRAFT.greaterThan(Version.of(1, 21))) {
return null;
}
// 1.21
return "v1_21_R1";
}
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";
};
public String getReleasesLink() {
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 4, 4))) { // Good luck.
return "https://dev.bukkit.org/projects/openinv/files?&sort=datecreated";
}
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 8, 8))) { // 1.8.8
return "https://github.com/lishid/OpenInv/releases/tag/4.1.5";
}
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 13))) { // 1.4.4+ had versioned packages.
return "https://github.com/lishid/OpenInv/releases/tag/4.0.0 (OpenInv-legacy)";
}
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 13))) { // 1.13
return "https://github.com/lishid/OpenInv/releases/tag/4.0.0";
}
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 14))) { // 1.13.1, 1.13.2
return "https://github.com/lishid/OpenInv/releases/tag/4.0.7";
}
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 14))) { // 1.14 to 1.14.1 had no revision bump.
return "https://github.com/lishid/OpenInv/releases/tag/4.0.0";
}
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 14, 1))) { // 1.14.1 to 1.14.2 had no revision bump.
return "https://github.com/lishid/OpenInv/releases/tag/4.0.1";
}
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 15))) { // 1.14.2
return "https://github.com/lishid/OpenInv/releases/tag/4.1.1";
}
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 15, 1))) { // 1.15, 1.15.1
return "https://github.com/lishid/OpenInv/releases/tag/4.1.5";
}
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 16))) { // 1.15.2
return "https://github.com/Jikoo/OpenInv/commit/502f661be39ee85d300851dd571f3da226f12345 (never released)";
}
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 16, 1))) { // 1.16, 1.16.1
return "https://github.com/lishid/OpenInv/releases/tag/4.1.4";
}
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 16, 3))) { // 1.16.2, 1.16.3
return "https://github.com/lishid/OpenInv/releases/tag/4.1.5";
}
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 17))) { // 1.16.4, 1.16.5
return "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8";
}
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 18, 1))) { // 1.17, 1.18, 1.18.1
return "https://github.com/Jikoo/OpenInv/releases/tag/4.1.10";
}
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 19))) { // 1.18.2
return "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0";
}
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 19))) { // 1.19
return "https://github.com/Jikoo/OpenInv/releases/tag/4.2.0";
}
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 19, 1))) { // 1.19.1
return "https://github.com/Jikoo/OpenInv/releases/tag/4.2.2";
}
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 19, 3))) { // 1.19.2, 1.19.3
return "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0";
}
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 20))) { // 1.19.4
return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3";
}
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 1))) { // 1.20, 1.20.1
return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.1";
}
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 3))) { // 1.20.2, 1.20.3
return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3";
}
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 5))) { // 1.20.5 shouldn't be used.
return "https://github.com/Jikoo/OpenInv/releases";
}
return "https://github.com/Jikoo/OpenInv/releases";
}
private @NotNull <T> T createObject(
@@ -76,7 +152,7 @@ class InternalAccessor {
@NotNull Object @NotNull ... params)
throws ClassCastException, ReflectiveOperationException {
// Fetch internal class if it exists.
Class<?> internalClass = Class.forName("com.lishid.openinv.internal." + this.version + "." + className);
Class<?> internalClass = Class.forName("com.lishid.openinv.internal." + this.versionPackage + "." + className);
// Quick return: no parameters, no need to fiddle about finding the correct constructor.
if (params.length == 0) {
@@ -116,7 +192,7 @@ class InternalAccessor {
@NotNull Player player,
boolean online) throws InstantiationException {
if (!this.supported) {
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT));
}
try {
return this.createObject(assignableClass, className, player, online);
@@ -135,7 +211,7 @@ class InternalAccessor {
*/
public @NotNull IAnySilentContainer getAnySilentContainer() {
if (!this.supported) {
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT));
}
return this.anySilentContainer;
}
@@ -148,7 +224,7 @@ class InternalAccessor {
*/
public @NotNull IPlayerDataManager getPlayerDataManager() {
if (!this.supported) {
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT));
}
return this.playerDataManager;
}
@@ -159,7 +235,7 @@ class InternalAccessor {
* @return the version
*/
public @NotNull String getVersion() {
return this.version;
return BukkitVersions.MINECRAFT.toString();
}
/**
@@ -33,18 +33,6 @@ import com.lishid.openinv.util.Permissions;
import com.lishid.openinv.util.StringMetric;
import com.lishid.openinv.util.lang.LanguageManager;
import com.lishid.openinv.util.lang.Replacement;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.stream.Stream;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
@@ -63,6 +51,19 @@ import org.bukkit.profile.PlayerProfile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.stream.Stream;
/**
* Open other player's inventory
*
@@ -136,7 +137,6 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
Class.forName("org.bukkit.entity.Player$Spigot");
isSpigot = true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
isSpigot = false;
}
@@ -318,7 +318,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
try {
player = future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
getLogger().log(Level.WARNING, e.getMessage(), e);
return null;
}
@@ -510,7 +510,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
return;
}
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message));
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacy(message));
}
/**
@@ -0,0 +1,46 @@
package com.lishid.openinv.internal;
import com.lishid.openinv.OpenInv;
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 Player viewer, @NotNull ISpecialInventory inventory) {
HumanEntity owner = inventory.getPlayer();
String localTitle = OpenInv.getPlugin(OpenInv.class)
.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;
}
}
}
@@ -16,9 +16,6 @@
package com.lishid.openinv.internal;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.util.lang.Replacement;
import java.util.Objects;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
@@ -30,19 +27,16 @@ public class OpenInventoryView extends InventoryView {
private final @NotNull Player player;
private final @NotNull ISpecialInventory inventory;
private final @NotNull String titleKey;
private final @NotNull String titleDefaultSuffix;
private final @NotNull String originalTitle;
private String title;
public OpenInventoryView(
@NotNull Player player,
@NotNull ISpecialInventory inventory,
@NotNull String titleKey,
@NotNull String titleDefaultSuffix) {
@NotNull String originalTitle) {
this.player = player;
this.inventory = inventory;
this.titleKey = titleKey;
this.titleDefaultSuffix = titleDefaultSuffix;
this.originalTitle = originalTitle;
}
@Override
@@ -67,18 +61,17 @@ public class OpenInventoryView extends InventoryView {
@Override
public @NotNull String getTitle() {
if (title == null) {
HumanEntity owner = inventory.getPlayer();
return title == null ? originalTitle : title;
}
String localTitle = OpenInv.getPlugin(OpenInv.class)
.getLocalizedMessage(
player,
titleKey,
new Replacement("%player%", owner.getName()));
title = Objects.requireNonNullElseGet(localTitle, () -> owner.getName() + titleDefaultSuffix);
}
@Override
public @NotNull String getOriginalTitle() {
return originalTitle;
}
return title;
@Override
public void setTitle(@NotNull String title) {
this.title = title;
}
}
+21 -12
View File
@@ -21,7 +21,7 @@
<artifactId>openinvparent</artifactId>
<name>OpenInv</name>
<url>http://dev.bukkit.org/bukkit-plugins/openinv/</url>
<version>4.4.1</version>
<version>4.4.7</version>
<packaging>pom</packaging>
@@ -40,9 +40,9 @@
<modules>
<module>api</module>
<module>plugin</module>
<module>internal/v1_19_R3</module>
<module>internal/v1_20_R1</module>
<module>internal/v1_20_R2</module>
<module>internal/v1_20_R3</module>
<module>internal/v1_20_R4</module>
<module>internal/v1_21_R1</module>
<module>assembly</module>
</modules>
</profile>
@@ -65,6 +65,10 @@
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencyManagement>
@@ -73,25 +77,25 @@
<artifactId>annotations</artifactId>
<groupId>org.jetbrains</groupId>
<scope>provided</scope>
<version>24.0.1</version>
<version>24.1.0</version>
</dependency>
<dependency>
<artifactId>spigot-api</artifactId>
<groupId>org.spigotmc</groupId>
<scope>provided</scope>
<version>1.18.2-R0.1-SNAPSHOT</version>
<version>1.20.4-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<artifactId>openinvapi</artifactId>
<groupId>com.lishid</groupId>
<scope>compile</scope>
<version>4.4.1</version>
<version>4.4.7</version>
</dependency>
<dependency>
<artifactId>openinvplugincore</artifactId>
<groupId>com.lishid</groupId>
<scope>compile</scope>
<version>4.4.1</version>
<version>4.4.7</version>
<exclusions>
<exclusion>
<groupId>com.lishid</groupId>
@@ -99,6 +103,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<artifactId>planarwrappers</artifactId>
<groupId>com.github.jikoo</groupId>
<version>3.2.3</version>
</dependency>
</dependencies>
</dependencyManagement>
@@ -145,25 +154,25 @@
</execution>
</executions>
<groupId>org.apache.maven.plugins</groupId>
<version>3.5.1</version>
<version>3.6.0</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>3.11.0</version>
<version>3.13.0</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>3.6.0</version>
<version>3.7.1</version>
</plugin>
<plugin>
<groupId>net.md-5</groupId>
<artifactId>specialsource-maven-plugin</artifactId>
<version>1.2.4</version>
<version>2.0.3</version>
<executions>
<execution>
<phase>package</phase>