Compare commits

..
22 Commits
Author SHA1 Message Date
Jikoo 3e2b601997 Bump version to 4.1.9 for release 2021-12-02 16:15:22 -05:00
Jikoo 160ae47bf5 Correct wrong item list usage 2021-12-02 16:13:37 -05:00
Jikoo 98fbcebfd9 Fix non-translatable component, wrong flag value 2021-12-02 15:44:19 -05:00
Jikoo d236dd27b3 Update to 1.18-pre5 2021-11-23 13:00:42 -05:00
Jikoo dd90b5281f Use Java 17 for CI 2021-11-17 10:41:53 -05:00
Jikoo b4601bb6c1 Fix cached Spigot dependencies never being used
Since plugins have not been downloaded, get always fails when running in offline mode.
2021-10-16 19:16:08 -04:00
Jikoo e3d9cea027 Fix spigot dependency parsing
* Re-add cache
* Work around reflection issues with mvn help:evaluate on Java 9+
2021-10-16 18:03:38 -04:00
Jikoo 556a8bcfce Drop 1.16.5 support to use Mojang mappings
Because there is no option to create a `remapped-mojang` artifact for 1.16.5 and the whole point of the scripts is to save me time, 1.16.5 support is being removed earlier than usual.

Also fixes issues with NMS-based shulker collision check.
Closes #36
2021-10-16 16:53:59 -04:00
Jikoo 8599997e03 Work towards fixing shulkers 2021-10-16 16:11:18 -04:00
Jikoo ea99bd5fd4 Extract duplicate API-only code 2021-07-28 11:28:08 -04:00
蘭蘭露 Flandre_tw 2f36a4d4dc Add Chinese Simplified translation (#35) 2021-06-28 20:05:55 -04:00
蘭蘭露 Flandre_tw e5ebf11d32 Add Chinese Traditional translation (#34) 2021-06-28 20:05:15 -04:00
Jikoo 4336b454b7 Fix potential CME closing viewers without access
Closes #33
2021-06-24 08:21:26 -04:00
Jikoo b198931f30 Bump version to 4.1.9-SNAPSHOT for development 2021-06-11 18:40:48 -04:00
Jikoo 64af4dddb0 Bump version to 4.1.8 for release 2021-06-11 18:40:39 -04:00
Jikoo f613c0a522 Remove redundant load 2021-06-11 18:39:19 -04:00
Jikoo 1cae4c7a8e Correct window id 2021-06-11 18:39:07 -04:00
Jikoo ff7243db6a Fix script issues
Temporarily explicitly declare Spigot dependencies
I don't have the time to deal with whatever the difference between remote and local bash/maven versions is right now.
2021-06-11 12:05:38 -04:00
Jikoo 07a8e3b973 Add 1.17 support 2021-06-11 10:50:50 -04:00
Jikoo 9fccea60f7 Update copyright 2021-06-11 10:50:36 -04:00
Jikoo 9a2b379a64 Escape changelog for YAML
With 1.17 releasing soon, we'll get to see if this was the issue very shortly.
Worst case scenario the yaml is still messed up, but since the changelog is now the last element, unless it's so badly malformed that the entire action can't run the file should still upload even if the changelog does not.
2021-06-08 11:38:26 -04:00
Jikoo 4d800361d8 Bump version to 4.1.8-SNAPSHOT for development 2021-05-29 09:51:39 -04:00
55 changed files with 3812 additions and 1631 deletions

No files matched your search

+3 -2
View File
@@ -12,9 +12,10 @@ jobs:
uses: actions/checkout@v2
- name: Set Up Java
uses: actions/setup-java@v1
uses: actions/setup-java@v2
with:
java-version: 1.8
distribution: 'adopt'
java-version: '17'
# Use cache to speed up build
- name: Cache Maven Repo
+8 -7
View File
@@ -13,9 +13,6 @@ jobs:
with:
fetch-depth: 0
- name: Set CurseForge Variables
run: . scripts/set_curseforge_env.sh
- name: Fetch Github Release Asset
uses: dsaltares/fetch-gh-release-asset@0.0.5
with:
@@ -23,14 +20,18 @@ jobs:
version: ${{ github.event.release.id }}
file: OpenInv.jar
- name: Set CurseForge Variables
run: . scripts/set_curseforge_env.sh "${{ github.event.release.body }}"
- name: Create CurseForge Release
uses: itsmeow/curseforge-upload@v3
with:
token: ${{ secrets.CURSEFORGE_TOKEN }}
token: "${{ secrets.CURSEFORGE_TOKEN }}"
project_id: 31432
game_endpoint: minecraft
file_path: ./OpenInv.jar
changelog: ${{ github.event.release.body }}
display_name: ${{ github.event.release.name }}
game_versions: ${{ env.CURSEFORGE_MINECRAFT_VERSIONS }}
display_name: "${{ github.event.release.name }}"
game_versions: "${{ env.CURSEFORGE_MINECRAFT_VERSIONS }}"
release_type: release
changelog_type: markdown
changelog: "${{ env.CURSEFORGE_CHANGELOG }}"
+2 -2
View File
@@ -1,5 +1,5 @@
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~ Copyright (C) 2011-2021 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
@@ -21,7 +21,7 @@
<parent>
<artifactId>openinvparent</artifactId>
<groupId>com.lishid</groupId>
<version>4.1.7</version>
<version>4.1.9</version>
</parent>
<artifactId>openinvapi</artifactId>
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,8 +16,18 @@
package com.lishid.openinv.internal;
import org.bukkit.Material;
import org.bukkit.block.Barrel;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.EnderChest;
import org.bukkit.block.ShulkerBox;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Chest;
import org.bukkit.entity.Cat;
import org.bukkit.entity.Player;
import org.bukkit.util.BoundingBox;
import org.jetbrains.annotations.NotNull;
public interface IAnySilentContainer {
@@ -41,14 +51,92 @@ public interface IAnySilentContainer {
*/
void deactivateContainer(@NotNull Player player);
/**
* @deprecated use {@link #isAnyContainerNeeded(Block)}
* @param player the player opening the container
* @param block the block
* @return true if the container is blocked
*/
@Deprecated
default boolean isAnyContainerNeeded(@NotNull Player player, @NotNull Block block) {
return isAnyContainerNeeded(block);
}
/**
* Checks if the container at the given coordinates is blocked.
*
* @param player the Player opening the container
* @param block the Block
* @return true if the container is blocked
*/
boolean isAnyContainerNeeded(@NotNull Player player, @NotNull Block block);
default boolean isAnyContainerNeeded(@NotNull Block block) {
BlockState blockState = block.getState();
// Barrels do not require AnyContainer.
if (blockState instanceof Barrel) {
return false;
}
// Enderchests require a non-occluding block on top to open.
if (blockState instanceof EnderChest) {
return block.getRelative(0, 1, 0).getType().isOccluding();
}
// Shulker boxes require 1/2 a block clear in the direction they open.
if (blockState instanceof ShulkerBox) {
if (isShulkerBlocked((ShulkerBox) blockState)) {
return true;
}
}
if (!(blockState instanceof org.bukkit.block.Chest)) {
return false;
}
if (isChestBlocked(block)) {
return true;
}
BlockData blockData = block.getBlockData();
if (!(blockData instanceof Chest) || ((Chest) blockData).getType() == Chest.Type.SINGLE) {
return false;
}
Chest chest = (Chest) blockData;
int ordinal = (chest.getFacing().ordinal() + 4 + (chest.getType() == Chest.Type.RIGHT ? -1 : 1)) % 4;
BlockFace relativeFace = BlockFace.values()[ordinal];
org.bukkit.block.Block relative = block.getRelative(relativeFace);
if (relative.getType() != block.getType()) {
return false;
}
BlockData relativeData = relative.getBlockData();
if (!(relativeData instanceof Chest)) {
return false;
}
Chest relativeChest = (Chest) relativeData;
if (relativeChest.getFacing() != chest.getFacing()
|| relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) {
return false;
}
return isChestBlocked(relative);
}
boolean isShulkerBlocked(ShulkerBox block);
/**
* Determine whether or not a chest is blocked.
*
* @param chest the chest block
* @return true if the chest block cannot be opened under ordinary circumstances
*/
default boolean isChestBlocked(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;
}
/**
* Checks if the given block is a container which can be unblocked or silenced.
@@ -56,6 +144,14 @@ public interface IAnySilentContainer {
* @param block the BlockState
* @return true if the Block is a supported container
*/
boolean isAnySilentContainer(@NotNull Block block);
default boolean isAnySilentContainer(@NotNull Block block) {
if (block.getType() == Material.ENDER_CHEST) {
return true;
}
BlockState state = block.getState();
return state instanceof org.bukkit.block.Chest
|| state instanceof org.bukkit.block.ShulkerBox
|| state instanceof org.bukkit.block.Barrel;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,7 +20,6 @@ import com.lishid.openinv.internal.IInventoryAccess;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.bukkit.Bukkit;
import org.bukkit.inventory.Inventory;
@@ -81,7 +80,8 @@ public class InventoryAccess implements IInventoryAccess {
} catch (ReflectiveOperationException ignored) {}
}
inv = grabFieldOfTypeFromObject(expected, inventory);
// Use reflection to find the IInventory
inv = ReflectionHelper.grabObjectByType(inventory, expected);
if (expected.isInstance(inv)) {
return expected.cast(inv);
@@ -90,23 +90,6 @@ public class InventoryAccess implements IInventoryAccess {
return null;
}
private static <T> @Nullable T grabFieldOfTypeFromObject(final Class<T> type, final Object object) {
// Use reflection to find the IInventory
Class<?> clazz = object.getClass();
T result = null;
for (Field f : clazz.getDeclaredFields()) {
f.setAccessible(true);
if (type.isAssignableFrom(f.getDeclaringClass())) {
try {
result = type.cast(f.get(object));
} catch (Exception e) {
e.printStackTrace();
}
}
}
return result;
}
@Deprecated
@Override
public @Nullable ISpecialEnderChest getSpecialEnderChest(@NotNull Inventory inventory) {
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util;
import java.lang.reflect.Field;
import org.jetbrains.annotations.Nullable;
/**
* A utility for making reflection easier.
*/
public final class ReflectionHelper {
private ReflectionHelper() {}
/**
* Grab an {@link Object} stored in a {@link Field} of another {@code Object}.
*
* <p>This casts the field to the correct class. Any issues will result in a {@code null} return value.
*
* @param fieldType the {@link Class} of {@code Object} stored in the {@code Field}
* @param holder the containing {@code Object}
* @param <T> the type of stored {@code Object}
* @return the first matching {@code Object} or {@code null} if none match
*/
public static <T> @Nullable T grabObjectByType(final Object holder, final Class<T> fieldType) {
Field field = grabFieldByType(holder.getClass(), fieldType);
if (field != null) {
try {
return fieldType.cast(field.get(holder));
} catch (IllegalAccessException ignored) {
// Ignore issues obtaining field
}
}
return null;
}
/**
* Grab a {@link Field} of an {@link Object}
*
* @param fieldType the {@link Class} of the object
* @param holderType the containing {@code Class}
* @return the first matching object or {@code null} if none match
*/
public static @Nullable Field grabFieldByType(Class<?> holderType, Class<?> fieldType) {
for (Field field : holderType.getDeclaredFields()) {
field.setAccessible(true);
if (fieldType.isAssignableFrom(field.getType())) {
return field;
}
}
if (holderType.getSuperclass() != null) {
return grabFieldByType(fieldType, holderType.getSuperclass());
}
return null;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
+2 -2
View File
@@ -1,5 +1,5 @@
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~ Copyright (C) 2011-2021 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
@@ -21,7 +21,7 @@
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId>
<version>4.1.7</version>
<version>4.1.9</version>
</parent>
<artifactId>openinvassembly</artifactId>
+1 -1
View File
@@ -1,5 +1,5 @@
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~ Copyright (C) 2011-2021 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
-64
View File
@@ -1,64 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>openinvparent</artifactId>
<groupId>com.lishid</groupId>
<relativePath>../../pom.xml</relativePath>
<version>4.1.7</version>
</parent>
<artifactId>openinvadapter1_16_R3</artifactId>
<name>OpenInvAdapter1_16_R3</name>
<dependencies>
<dependency>
<artifactId>spigot</artifactId>
<groupId>org.spigotmc</groupId>
<scope>provided</scope>
<version>1.16.5-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<artifactId>openinvapi</artifactId>
<groupId>com.lishid</groupId>
</dependency>
<dependency>
<artifactId>openinvplugincore</artifactId>
<groupId>com.lishid</groupId>
</dependency>
<dependency>
<artifactId>annotations</artifactId>
<groupId>org.jetbrains</groupId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -1,343 +0,0 @@
/*
* Copyright (C) 2011-2020 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_16_R3;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IAnySilentContainer;
import java.lang.reflect.Field;
import net.minecraft.server.v1_16_R3.Block;
import net.minecraft.server.v1_16_R3.BlockBarrel;
import net.minecraft.server.v1_16_R3.BlockChest;
import net.minecraft.server.v1_16_R3.BlockChestTrapped;
import net.minecraft.server.v1_16_R3.BlockPosition;
import net.minecraft.server.v1_16_R3.BlockPropertyChestType;
import net.minecraft.server.v1_16_R3.BlockShulkerBox;
import net.minecraft.server.v1_16_R3.ChatMessage;
import net.minecraft.server.v1_16_R3.Container;
import net.minecraft.server.v1_16_R3.ContainerChest;
import net.minecraft.server.v1_16_R3.Containers;
import net.minecraft.server.v1_16_R3.EntityHuman;
import net.minecraft.server.v1_16_R3.EntityPlayer;
import net.minecraft.server.v1_16_R3.EnumGamemode;
import net.minecraft.server.v1_16_R3.IBlockData;
import net.minecraft.server.v1_16_R3.IChatBaseComponent;
import net.minecraft.server.v1_16_R3.ITileInventory;
import net.minecraft.server.v1_16_R3.InventoryEnderChest;
import net.minecraft.server.v1_16_R3.InventoryLargeChest;
import net.minecraft.server.v1_16_R3.PlayerInteractManager;
import net.minecraft.server.v1_16_R3.PlayerInventory;
import net.minecraft.server.v1_16_R3.TileEntity;
import net.minecraft.server.v1_16_R3.TileEntityChest;
import net.minecraft.server.v1_16_R3.TileEntityEnderChest;
import net.minecraft.server.v1_16_R3.TileEntityLootable;
import net.minecraft.server.v1_16_R3.TileInventory;
import net.minecraft.server.v1_16_R3.World;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.block.Barrel;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.EnderChest;
import org.bukkit.block.ShulkerBox;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.type.Chest;
import org.bukkit.entity.Cat;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.bukkit.util.BoundingBox;
import org.jetbrains.annotations.NotNull;
public class AnySilentContainer implements IAnySilentContainer {
private Field playerInteractManagerGamemode;
public AnySilentContainer() {
try {
this.playerInteractManagerGamemode = PlayerInteractManager.class.getDeclaredField("gamemode");
this.playerInteractManagerGamemode.setAccessible(true);
} catch (NoSuchFieldException | SecurityException e) {
System.err.println("[OpenInv] Unable to directly write player gamemode! SilentChest will fail.");
e.printStackTrace();
}
}
@Override
public boolean isAnySilentContainer(@NotNull final org.bukkit.block.Block bukkitBlock) {
if (bukkitBlock.getType() == Material.ENDER_CHEST) {
return true;
}
BlockState state = bukkitBlock.getState();
return state instanceof org.bukkit.block.Chest
|| state instanceof org.bukkit.block.ShulkerBox
|| state instanceof org.bukkit.block.Barrel;
}
@Override
public boolean isAnyContainerNeeded(@NotNull final Player p, @NotNull final org.bukkit.block.Block block) {
BlockState blockState = block.getState();
// Barrels do not require AnyContainer.
if (blockState instanceof Barrel) {
return false;
}
// Enderchests require a non-occluding block on top to open.
if (blockState instanceof EnderChest) {
return block.getRelative(0, 1, 0).getType().isOccluding();
}
// Shulker boxes require 1/2 a block clear in the direction they open.
if (blockState instanceof ShulkerBox) {
BoundingBox boundingBox = block.getBoundingBox();
if (boundingBox.getVolume() > 1) {
// Shulker box is already open.
return false;
}
BlockData blockData = block.getBlockData();
if (!(blockData instanceof Directional)) {
// Shouldn't be possible. Just in case, demand AnyChest.
return true;
}
Directional directional = (Directional) blockData;
BlockFace face = directional.getFacing();
boundingBox.shift(face.getDirection());
// Return whether or not bounding boxes overlap.
return block.getRelative(face, 1).getBoundingBox().overlaps(boundingBox);
}
if (!(blockState instanceof org.bukkit.block.Chest)) {
return false;
}
if (isBlockedChest(block)) {
return true;
}
BlockData blockData = block.getBlockData();
if (!(blockData instanceof Chest) || ((Chest) blockData).getType() == Chest.Type.SINGLE) {
return false;
}
Chest chest = (Chest) blockData;
int ordinal = (chest.getFacing().ordinal() + 4 + (chest.getType() == Chest.Type.RIGHT ? -1 : 1)) % 4;
BlockFace relativeFace = BlockFace.values()[ordinal];
org.bukkit.block.Block relative = block.getRelative(relativeFace);
if (relative.getType() != block.getType()) {
return false;
}
BlockData relativeData = relative.getBlockData();
if (!(relativeData instanceof Chest)) {
return false;
}
Chest relativeChest = (Chest) relativeData;
if (relativeChest.getFacing() != chest.getFacing()
|| relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) {
return false;
}
return isBlockedChest(relative);
}
private boolean isBlockedChest(org.bukkit.block.Block block) {
org.bukkit.block.Block relative = block.getRelative(0, 1, 0);
return relative.getType().isOccluding()
|| block.getWorld().getNearbyEntities(BoundingBox.of(relative), entity -> entity instanceof Cat).size() > 0;
}
@Override
public boolean activateContainer(@NotNull final Player bukkitPlayer, final boolean silentchest,
@NotNull final org.bukkit.block.Block bukkitBlock) {
// Silent ender chest is API-only
if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) {
bukkitPlayer.openInventory(bukkitPlayer.getEnderChest());
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
final World world = player.world;
final BlockPosition blockPosition = new BlockPosition(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
final TileEntity tile = world.getTileEntity(blockPosition);
if (tile == null) {
return false;
}
if (tile instanceof TileEntityEnderChest) {
// Anychest ender chest. See net.minecraft.server.BlockEnderChest
InventoryEnderChest enderChest = player.getEnderChest();
enderChest.a((TileEntityEnderChest) tile);
player.openContainer(new TileInventory((containerCounter, playerInventory, ignored) -> {
Containers<?> containers = PlayerDataManager.getContainers(enderChest.getSize());
int rows = enderChest.getSize() / 9;
return new ContainerChest(containers, containerCounter, playerInventory, enderChest, rows);
}, new ChatMessage("container.enderchest")));
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
if (!(tile instanceof ITileInventory)) {
return false;
}
ITileInventory tileInventory = (ITileInventory) tile;
IBlockData blockData = world.getType(blockPosition);
Block block = blockData.getBlock();
if (block instanceof BlockChest) {
BlockPropertyChestType chestType = blockData.get(BlockChest.c);
if (chestType != BlockPropertyChestType.SINGLE) {
BlockPosition adjacentBlockPosition = blockPosition.shift(BlockChest.h(blockData));
IBlockData adjacentBlockData = world.getType(adjacentBlockPosition);
if (adjacentBlockData.getBlock() == block) {
BlockPropertyChestType adjacentChestType = adjacentBlockData.get(BlockChest.c);
if (adjacentChestType != BlockPropertyChestType.SINGLE && chestType != adjacentChestType
&& adjacentBlockData.get(BlockChest.FACING) == blockData.get(BlockChest.FACING)) {
TileEntity adjacentTile = world.getTileEntity(adjacentBlockPosition);
if (adjacentTile instanceof TileEntityChest && tileInventory instanceof TileEntityChest) {
TileEntityChest rightChest = chestType == BlockPropertyChestType.RIGHT ? ((TileEntityChest) tileInventory) : (TileEntityChest) adjacentTile;
TileEntityChest leftChest = chestType == BlockPropertyChestType.RIGHT ? (TileEntityChest) adjacentTile : ((TileEntityChest) tileInventory);
if (silentchest && (rightChest.lootTable != null || leftChest.lootTable != null)) {
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
return false;
}
tileInventory = new ITileInventory() {
public Container createMenu(int containerCounter, PlayerInventory playerInventory, EntityHuman entityHuman) {
leftChest.d(playerInventory.player);
rightChest.d(playerInventory.player);
return ContainerChest.b(containerCounter, playerInventory, new InventoryLargeChest(rightChest, leftChest));
}
public IChatBaseComponent getScoreboardDisplayName() {
if (leftChest.hasCustomName()) {
return leftChest.getScoreboardDisplayName();
}
if (rightChest.hasCustomName()) {
return rightChest.getScoreboardDisplayName();
}
return new ChatMessage("container.chestDouble");
}
};
}
}
}
}
if (block instanceof BlockChestTrapped) {
bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED);
} else {
bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED);
}
}
if (block instanceof BlockShulkerBox) {
bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED);
}
if (block instanceof BlockBarrel) {
bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL);
}
// AnyChest only - SilentChest not active, container unsupported, or unnecessary.
if (!silentchest || player.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR) {
player.openContainer(tileInventory);
return true;
}
// SilentChest requires access to setting players' gamemode directly.
if (this.playerInteractManagerGamemode == null) {
return false;
}
if (tile instanceof TileEntityLootable) {
TileEntityLootable lootable = (TileEntityLootable) tile;
if (lootable.lootTable != null) {
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
return false;
}
}
EnumGamemode gamemode = player.playerInteractManager.getGameMode();
this.forceGameMode(player, EnumGamemode.SPECTATOR);
player.openContainer(tileInventory);
this.forceGameMode(player, gamemode);
return true;
}
@Override
public void deactivateContainer(@NotNull final Player bukkitPlayer) {
if (this.playerInteractManagerGamemode == null) {
return;
}
InventoryView view = bukkitPlayer.getOpenInventory();
switch (view.getType()) {
case CHEST:
case ENDER_CHEST:
case SHULKER_BOX:
case BARREL:
break;
default:
return;
}
EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
EnumGamemode gamemode = player.playerInteractManager.getGameMode();
this.forceGameMode(player, EnumGamemode.SPECTATOR);
player.activeContainer.b(player);
player.activeContainer.a(player, false);
player.activeContainer.transferTo(player.defaultContainer, player.getBukkitEntity());
player.activeContainer = player.defaultContainer;
this.forceGameMode(player, gamemode);
}
private void forceGameMode(final EntityPlayer player, final EnumGamemode gameMode) {
if (this.playerInteractManagerGamemode == null) {
// No need to warn repeatedly, error on startup and lack of function should be enough.
return;
}
try {
if (!this.playerInteractManagerGamemode.isAccessible()) {
// Just in case, ensure accessible.
this.playerInteractManagerGamemode.setAccessible(true);
}
this.playerInteractManagerGamemode.set(player.playerInteractManager, gameMode);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
@@ -1,78 +0,0 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_16_R3;
import java.io.File;
import java.io.FileOutputStream;
import net.minecraft.server.v1_16_R3.EntityPlayer;
import net.minecraft.server.v1_16_R3.NBTCompressedStreamTools;
import net.minecraft.server.v1_16_R3.NBTTagCompound;
import net.minecraft.server.v1_16_R3.WorldNBTStorage;
import org.apache.logging.log4j.LogManager;
import org.bukkit.craftbukkit.v1_16_R3.CraftServer;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
public class OpenPlayer extends CraftPlayer {
public OpenPlayer(CraftServer server, EntityPlayer entity) {
super(server, entity);
}
@Override
public void loadData() {
// See CraftPlayer#loadData
NBTTagCompound loaded = this.server.getHandle().playerFileData.load(this.getHandle());
if (loaded != null) {
readExtraData(loaded);
}
}
@Override
public void saveData() {
EntityPlayer player = this.getHandle();
// See net.minecraft.server.WorldNBTStorage#save(EntityPlayer)
try {
WorldNBTStorage worldNBTStorage = player.server.getPlayerList().playerFileData;
NBTTagCompound playerData = player.save(new NBTTagCompound());
setExtraData(playerData);
if (!isOnline()) {
// Special case: save old vehicle data
NBTTagCompound oldData = worldNBTStorage.load(player);
if (oldData != null && oldData.hasKeyOfType("RootVehicle", 10)) {
// See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound)
playerData.set("RootVehicle", oldData.getCompound("RootVehicle"));
}
}
File file = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat.tmp");
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat");
NBTCompressedStreamTools.a(playerData, new FileOutputStream(file));
if (file1.exists() && !file1.delete() || !file.renameTo(file1)) {
LogManager.getLogger().warn("Failed to save player data for {}", player.getDisplayName().getString());
}
} catch (Exception e) {
LogManager.getLogger().warn("Failed to save player data for {}", player.getDisplayName().getString());
}
}
}
@@ -1,261 +0,0 @@
/*
* Copyright (C) 2011-2020 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_16_R3;
import com.lishid.openinv.internal.ISpecialEnderChest;
import java.util.List;
import net.minecraft.server.v1_16_R3.AutoRecipeStackManager;
import net.minecraft.server.v1_16_R3.ContainerUtil;
import net.minecraft.server.v1_16_R3.EntityHuman;
import net.minecraft.server.v1_16_R3.EntityPlayer;
import net.minecraft.server.v1_16_R3.IInventoryListener;
import net.minecraft.server.v1_16_R3.InventoryEnderChest;
import net.minecraft.server.v1_16_R3.ItemStack;
import net.minecraft.server.v1_16_R3.NonNullList;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpecialEnderChest extends InventoryEnderChest implements ISpecialEnderChest {
private final CraftInventory inventory;
private EntityPlayer owner;
private NonNullList<ItemStack> items;
private boolean playerOnline;
public SpecialEnderChest(final Player player, final Boolean online) {
super(PlayerDataManager.getHandle(player));
this.inventory = new CraftInventory(this);
this.owner = PlayerDataManager.getHandle(player);
this.playerOnline = online;
this.items = this.owner.getEnderChest().items;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return inventory;
}
@Override
public boolean isInUse() {
return !this.getViewers().isEmpty();
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public void setPlayerOnline(@NotNull final Player player) {
if (!this.playerOnline) {
try {
this.owner = PlayerDataManager.getHandle(player);
InventoryEnderChest enderChest = owner.getEnderChest();
for (int i = 0; i < enderChest.getSize(); ++i) {
enderChest.setItem(i, this.items.get(i));
}
this.items = enderChest.items;
} catch (Exception ignored) {}
this.playerOnline = true;
}
}
@Override
public @NotNull Player getPlayer() {
return owner.getBukkitEntity();
}
@Override
public void update() {
this.owner.getEnderChest().update();
}
@Override
public List<ItemStack> getContents() {
return this.items;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.owner.getEnderChest().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.owner.getEnderChest().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.owner.getEnderChest().getViewers();
}
@Override
public void setMaxStackSize(int i) {
this.owner.getEnderChest().setMaxStackSize(i);
}
@Override
public InventoryHolder getOwner() {
return this.owner.getEnderChest().getOwner();
}
@Override
public @Nullable Location getLocation() {
return null;
}
@Override
public void a(IInventoryListener iinventorylistener) {
this.owner.getEnderChest().a(iinventorylistener);
}
@Override
public void b(IInventoryListener iinventorylistener) {
this.owner.getEnderChest().b(iinventorylistener);
}
@Override
public ItemStack getItem(int i) {
return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.b;
}
@Override
public ItemStack splitStack(int i, int j) {
ItemStack itemstack = ContainerUtil.a(this.items, i, j);
if (!itemstack.isEmpty()) {
this.update();
}
return itemstack;
}
@Override
public ItemStack a(ItemStack itemstack) {
ItemStack itemstack1 = itemstack.cloneItemStack();
for (int i = 0; i < this.getSize(); ++i) {
ItemStack itemstack2 = this.getItem(i);
if (itemstack2.isEmpty()) {
this.setItem(i, itemstack1);
this.update();
return ItemStack.b;
}
if (ItemStack.c(itemstack2, itemstack1)) {
int j = Math.min(this.getMaxStackSize(), itemstack2.getMaxStackSize());
int k = Math.min(itemstack1.getCount(), j - itemstack2.getCount());
if (k > 0) {
itemstack2.add(k);
itemstack1.subtract(k);
if (itemstack1.isEmpty()) {
this.update();
return ItemStack.b;
}
}
}
}
if (itemstack1.getCount() != itemstack.getCount()) {
this.update();
}
return itemstack1;
}
@Override
public ItemStack splitWithoutUpdate(int i) {
ItemStack itemstack = this.items.get(i);
if (itemstack.isEmpty()) {
return ItemStack.b;
} else {
this.items.set(i, ItemStack.b);
return itemstack;
}
}
@Override
public void setItem(int i, ItemStack itemstack) {
this.items.set(i, itemstack);
if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) {
itemstack.setCount(this.getMaxStackSize());
}
this.update();
}
@Override
public int getSize() {
return this.owner.getEnderChest().getSize();
}
@Override
public boolean isEmpty() {
for (ItemStack itemstack : this.items) {
if (!itemstack.isEmpty()) {
return false;
}
}
return true;
}
@Override
public int getMaxStackSize() {
return 64;
}
@Override
public boolean a(EntityHuman entityhuman) {
return true;
}
@Override
public void startOpen(EntityHuman entityhuman) {
}
@Override
public void closeContainer(EntityHuman entityhuman) {
}
@Override
public boolean b(int i, ItemStack itemstack) {
return true;
}
@Override
public void clear() {
this.items.clear();
}
@Override
public void a(AutoRecipeStackManager autorecipestackmanager) {
for (ItemStack itemstack : this.items) {
autorecipestackmanager.b(itemstack);
}
}
}
@@ -1,738 +0,0 @@
/*
* Copyright (C) 2011-2020 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_16_R3;
import com.google.common.collect.ImmutableList;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.server.v1_16_R3.AutoRecipeStackManager;
import net.minecraft.server.v1_16_R3.ChatMessage;
import net.minecraft.server.v1_16_R3.ContainerUtil;
import net.minecraft.server.v1_16_R3.CrashReport;
import net.minecraft.server.v1_16_R3.CrashReportSystemDetails;
import net.minecraft.server.v1_16_R3.DamageSource;
import net.minecraft.server.v1_16_R3.EntityHuman;
import net.minecraft.server.v1_16_R3.EntityPlayer;
import net.minecraft.server.v1_16_R3.EnumItemSlot;
import net.minecraft.server.v1_16_R3.IBlockData;
import net.minecraft.server.v1_16_R3.IChatBaseComponent;
import net.minecraft.server.v1_16_R3.IInventory;
import net.minecraft.server.v1_16_R3.Item;
import net.minecraft.server.v1_16_R3.ItemArmor;
import net.minecraft.server.v1_16_R3.ItemStack;
import net.minecraft.server.v1_16_R3.NBTTagCompound;
import net.minecraft.server.v1_16_R3.NBTTagList;
import net.minecraft.server.v1_16_R3.NonNullList;
import net.minecraft.server.v1_16_R3.PacketPlayOutSetSlot;
import net.minecraft.server.v1_16_R3.PlayerInventory;
import net.minecraft.server.v1_16_R3.ReportedException;
import net.minecraft.server.v1_16_R3.World;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpecialPlayerInventory extends PlayerInventory implements ISpecialPlayerInventory {
private final CraftInventory inventory;
private boolean playerOnline;
private EntityHuman player;
private NonNullList<ItemStack> items, armor, extraSlots;
private List<NonNullList<ItemStack>> f;
public SpecialPlayerInventory(final Player bukkitPlayer, final Boolean online) {
super(PlayerDataManager.getHandle(bukkitPlayer));
this.inventory = new CraftInventory(this);
this.playerOnline = online;
this.player = super.player;
this.items = this.player.inventory.items;
this.armor = this.player.inventory.armor;
this.extraSlots = this.player.inventory.extraSlots;
this.f = ImmutableList.of(this.items, this.armor, this.extraSlots);
}
@Override
public void setPlayerOnline(@NotNull final Player player) {
if (!this.playerOnline) {
EntityPlayer entityPlayer = PlayerDataManager.getHandle(player);
entityPlayer.inventory.transaction.addAll(this.transaction);
this.player = entityPlayer;
for (int i = 0; i < getSize(); ++i) {
this.player.inventory.setItem(i, getRawItem(i));
}
this.player.inventory.itemInHandIndex = this.itemInHandIndex;
this.items = this.player.inventory.items;
this.armor = this.player.inventory.armor;
this.extraSlots = this.player.inventory.extraSlots;
this.f = ImmutableList.of(this.items, this.armor, this.extraSlots);
this.playerOnline = true;
}
}
@Override
public boolean a(final EntityHuman entityhuman) {
return true;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return this.inventory;
}
@Override
public ItemStack getItem(int i) {
List<ItemStack> list = this.items;
if (i >= list.size()) {
i -= list.size();
list = this.armor;
} else {
i = this.getReversedItemSlotNum(i);
}
if (i >= list.size()) {
i -= list.size();
list = this.extraSlots;
} else if (list == this.armor) {
i = this.getReversedArmorSlotNum(i);
}
if (i >= list.size()) {
return ItemStack.b;
}
return list.get(i);
}
private ItemStack getRawItem(int i) {
NonNullList<ItemStack> list = null;
for (NonNullList<ItemStack> next : this.f) {
if (i < next.size()) {
list = next;
break;
}
i -= next.size();
}
return list == null ? ItemStack.b : list.get(i);
}
@Override
public IChatBaseComponent getDisplayName() {
return new ChatMessage(this.player.getName());
}
@Override
public boolean hasCustomName() {
return false;
}
private int getReversedArmorSlotNum(final int i) {
if (i == 0) {
return 3;
}
if (i == 1) {
return 2;
}
if (i == 2) {
return 1;
}
if (i == 3) {
return 0;
}
return i;
}
private int getReversedItemSlotNum(final int i) {
if (i >= 27) {
return i - 27;
}
return i + 9;
}
@Override
public int getSize() {
return 45;
}
@Override
public boolean isInUse() {
return !this.getViewers().isEmpty();
}
@Override
public void setItem(int i, final ItemStack itemstack) {
List<ItemStack> list = this.items;
if (i >= list.size()) {
i -= list.size();
list = this.armor;
} else {
i = this.getReversedItemSlotNum(i);
}
if (i >= list.size()) {
i -= list.size();
list = this.extraSlots;
} else if (list == this.armor) {
i = this.getReversedArmorSlotNum(i);
}
if (i >= list.size()) {
this.player.drop(itemstack, true);
return;
}
list.set(i, itemstack);
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public @NotNull HumanEntity getPlayer() {
return this.player.getBukkitEntity();
}
@Override
public ItemStack splitStack(int i, final int j) {
List<ItemStack> list = this.items;
if (i >= list.size()) {
i -= list.size();
list = this.armor;
} else {
i = this.getReversedItemSlotNum(i);
}
if (i >= list.size()) {
i -= list.size();
list = this.extraSlots;
} else if (list == this.armor) {
i = this.getReversedArmorSlotNum(i);
}
if (i >= list.size()) {
return ItemStack.b;
}
return list.get(i).isEmpty() ? ItemStack.b : ContainerUtil.a(list, i, j);
}
@Override
public ItemStack splitWithoutUpdate(int i) {
List<ItemStack> list = this.items;
if (i >= list.size()) {
i -= list.size();
list = this.armor;
} else {
i = this.getReversedItemSlotNum(i);
}
if (i >= list.size()) {
i -= list.size();
list = this.extraSlots;
} else if (list == this.armor) {
i = this.getReversedArmorSlotNum(i);
}
if (i >= list.size()) {
return ItemStack.b;
}
if (!list.get(i).isEmpty()) {
ItemStack itemstack = list.get(i);
list.set(i, ItemStack.b);
return itemstack;
}
return ItemStack.b;
}
@Override
public List<ItemStack> getContents() {
return this.f.stream().flatMap(List::stream).collect(Collectors.toList());
}
@Override
public List<ItemStack> getArmorContents() {
return this.armor;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.transaction.add(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.transaction.remove(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.transaction;
}
@Override
public InventoryHolder getOwner() {
return this.player.getBukkitEntity();
}
@Override
public Location getLocation() {
return this.player.getBukkitEntity().getLocation();
}
@Override
public ItemStack getItemInHand() {
return d(this.itemInHandIndex) ? this.items.get(this.itemInHandIndex) : ItemStack.b;
}
private boolean isSimilarAndNotFull(ItemStack itemstack, ItemStack itemstack1) {
return !itemstack.isEmpty() && this.b(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
}
private boolean b(ItemStack itemstack, ItemStack itemstack1) {
return itemstack.getItem() == itemstack1.getItem() && ItemStack.equals(itemstack, itemstack1);
}
@Override
public int canHold(ItemStack itemstack) {
int remains = itemstack.getCount();
for (int i = 0; i < this.items.size(); ++i) {
ItemStack itemstack1 = this.getItem(i);
if (itemstack1.isEmpty()) {
return itemstack.getCount();
}
if (!this.isSimilarAndNotFull(itemstack, itemstack1)) {
remains -= Math.min(itemstack1.getMaxStackSize(), this.getMaxStackSize()) - itemstack1.getCount();
}
if (remains <= 0) {
return itemstack.getCount();
}
}
ItemStack offhandItemStack = this.getItem(this.items.size() + this.armor.size());
if (this.isSimilarAndNotFull(offhandItemStack, itemstack)) {
remains -= Math.min(offhandItemStack.getMaxStackSize(), this.getMaxStackSize()) - offhandItemStack.getCount();
}
return itemstack.getCount() - remains;
}
@Override
public int getFirstEmptySlotIndex() {
for (int i = 0; i < this.items.size(); ++i) {
if (this.items.get(i).isEmpty()) {
return i;
}
}
return -1;
}
@Override
public void c(int i) {
this.itemInHandIndex = this.i();
ItemStack itemstack = this.items.get(this.itemInHandIndex);
this.items.set(this.itemInHandIndex, this.items.get(i));
this.items.set(i, itemstack);
}
@Override
public int c(ItemStack itemstack) {
for (int i = 0; i < this.items.size(); ++i) {
ItemStack itemstack1 = this.items.get(i);
if (!this.items.get(i).isEmpty() && this.b(itemstack, this.items.get(i)) && !this.items.get(i).f() && !itemstack1.hasEnchantments() && !itemstack1.hasName()) {
return i;
}
}
return -1;
}
@Override
public int i() {
int i;
int j;
for (j = 0; j < 9; ++j) {
i = (this.itemInHandIndex + j) % 9;
if (this.items.get(i).isEmpty()) {
return i;
}
}
for (j = 0; j < 9; ++j) {
i = (this.itemInHandIndex + j) % 9;
if (!this.items.get(i).hasEnchantments()) {
return i;
}
}
return this.itemInHandIndex;
}
@Override
public int a(Predicate<ItemStack> predicate, int i, IInventory iinventory) {
byte b0 = 0;
boolean flag = i == 0;
int j = b0 + ContainerUtil.a(this, predicate, i - b0, flag);
j += ContainerUtil.a(iinventory, predicate, i - j, flag);
j += ContainerUtil.a(this.getCarried(), predicate, i - j, flag);
if (this.getCarried().isEmpty()) {
this.setCarried(ItemStack.b);
}
return j;
}
private int i(ItemStack itemstack) {
int i = this.firstPartial(itemstack);
if (i == -1) {
i = this.getFirstEmptySlotIndex();
}
return i == -1 ? itemstack.getCount() : this.d(i, itemstack);
}
private int d(int i, ItemStack itemstack) {
Item item = itemstack.getItem();
int j = itemstack.getCount();
ItemStack itemstack1 = this.getItem(i);
if (itemstack1.isEmpty()) {
itemstack1 = new ItemStack(item, 0);
NBTTagCompound tag = itemstack.getTag();
if (tag != null) {
itemstack1.setTag(tag.clone());
}
this.setItem(i, itemstack1);
}
int k = j;
if (j > itemstack1.getMaxStackSize() - itemstack1.getCount()) {
k = itemstack1.getMaxStackSize() - itemstack1.getCount();
}
if (k > this.getMaxStackSize() - itemstack1.getCount()) {
k = this.getMaxStackSize() - itemstack1.getCount();
}
if (k != 0) {
j -= k;
itemstack1.add(k);
itemstack1.d(5);
}
return j;
}
@Override
public int firstPartial(ItemStack itemstack) {
if (this.isSimilarAndNotFull(this.getItem(this.itemInHandIndex), itemstack)) {
return this.itemInHandIndex;
} else if (this.isSimilarAndNotFull(this.getItem(40), itemstack)) {
return 40;
} else {
for (int i = 0; i < this.items.size(); ++i) {
if (this.isSimilarAndNotFull(this.items.get(i), itemstack)) {
return i;
}
}
return -1;
}
}
@Override
public void j() {
for (List<ItemStack> itemStacks : this.f) {
for (int i = 0; i < itemStacks.size(); ++i) {
if (!itemStacks.get(i).isEmpty()) {
itemStacks.get(i).a(this.player.world, this.player, i, this.itemInHandIndex == i);
}
}
}
}
@Override
public boolean pickup(ItemStack itemstack) {
return this.c(-1, itemstack);
}
@Override
public boolean c(int i, ItemStack itemstack) {
if (itemstack.isEmpty()) {
return false;
} else {
try {
if (itemstack.f()) {
if (i == -1) {
i = this.getFirstEmptySlotIndex();
}
if (i >= 0) {
this.items.set(i, itemstack.cloneItemStack());
this.items.get(i).d(5);
itemstack.setCount(0);
return true;
} else if (this.player.abilities.canInstantlyBuild) {
itemstack.setCount(0);
return true;
} else {
return false;
}
} else {
int j;
do {
j = itemstack.getCount();
if (i == -1) {
itemstack.setCount(this.i(itemstack));
} else {
itemstack.setCount(this.d(i, itemstack));
}
} while(!itemstack.isEmpty() && itemstack.getCount() < j);
if (itemstack.getCount() == j && this.player.abilities.canInstantlyBuild) {
itemstack.setCount(0);
return true;
} else {
return itemstack.getCount() < j;
}
}
} catch (Throwable var6) {
CrashReport crashreport = CrashReport.a(var6, "Adding item to inventory");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Item being added");
crashreportsystemdetails.a("Item ID", Item.getId(itemstack.getItem()));
crashreportsystemdetails.a("Item data", itemstack.getDamage());
crashreportsystemdetails.a("Item name", () -> itemstack.getName().getString());
throw new ReportedException(crashreport);
}
}
}
@Override
public void a(World world, ItemStack itemstack) {
if (!world.isClientSide) {
while(!itemstack.isEmpty()) {
int i = this.firstPartial(itemstack);
if (i == -1) {
i = this.getFirstEmptySlotIndex();
}
if (i == -1) {
this.player.drop(itemstack, false);
break;
}
int j = itemstack.getMaxStackSize() - this.getItem(i).getCount();
if (this.c(i, itemstack.cloneAndSubtract(j))) {
((EntityPlayer)this.player).playerConnection.sendPacket(new PacketPlayOutSetSlot(-2, i, this.getItem(i)));
}
}
}
}
@Override
public void f(ItemStack itemstack) {
for (List<ItemStack> list : this.f) {
for (int i = 0; i < list.size(); ++i) {
if (list.get(i) == itemstack) {
list.set(i, ItemStack.b);
break;
}
}
}
}
@Override
public float a(IBlockData iblockdata) {
return this.items.get(this.itemInHandIndex).a(iblockdata);
}
@Override
public NBTTagList a(NBTTagList nbttaglist) {
NBTTagCompound nbttagcompound;
int i;
for (i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty()) {
nbttagcompound = new NBTTagCompound();
nbttagcompound.setByte("Slot", (byte) i);
this.items.get(i).save(nbttagcompound);
nbttaglist.add(nbttagcompound);
}
}
for (i = 0; i < this.armor.size(); ++i) {
if (!this.armor.get(i).isEmpty()) {
nbttagcompound = new NBTTagCompound();
nbttagcompound.setByte("Slot", (byte) (i + 100));
this.armor.get(i).save(nbttagcompound);
nbttaglist.add(nbttagcompound);
}
}
for (i = 0; i < this.extraSlots.size(); ++i) {
if (!this.extraSlots.get(i).isEmpty()) {
nbttagcompound = new NBTTagCompound();
nbttagcompound.setByte("Slot", (byte) (i + 150));
this.extraSlots.get(i).save(nbttagcompound);
nbttaglist.add(nbttagcompound);
}
}
return nbttaglist;
}
@Override
public void b(NBTTagList nbttaglist) {
this.items.clear();
this.armor.clear();
this.extraSlots.clear();
for(int i = 0; i < nbttaglist.size(); ++i) {
NBTTagCompound nbttagcompound = nbttaglist.getCompound(i);
int j = nbttagcompound.getByte("Slot") & 255;
ItemStack itemstack = ItemStack.a(nbttagcompound);
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.extraSlots.size() + 150) {
this.extraSlots.set(j - 150, itemstack);
}
}
}
}
@Override
public boolean isEmpty() {
Iterator<ItemStack> iterator = this.items.iterator();
ItemStack itemstack;
while (iterator.hasNext()) {
itemstack = iterator.next();
if (!itemstack.isEmpty()) {
return false;
}
}
iterator = this.armor.iterator();
while (iterator.hasNext()) {
itemstack = iterator.next();
if (!itemstack.isEmpty()) {
return false;
}
}
iterator = this.extraSlots.iterator();
while (iterator.hasNext()) {
itemstack = iterator.next();
if (!itemstack.isEmpty()) {
return false;
}
}
return true;
}
@Nullable
@Override
public IChatBaseComponent getCustomName() {
return null;
}
@Override
public void a(DamageSource damagesource, float f) {
if (f > 0.0F) {
f /= 4.0F;
if (f < 1.0F) {
f = 1.0F;
}
for (int i = 0; i < this.armor.size(); ++i) {
ItemStack itemstack = this.armor.get(0);
int index = i;
if ((!damagesource.isFire() || !itemstack.getItem().u()) && itemstack.getItem() instanceof ItemArmor) {
itemstack.damage((int) f, this.player, (entityHuman) -> entityHuman.broadcastItemBreak(EnumItemSlot.a(EnumItemSlot.Function.ARMOR, index)));
}
}
}
}
@Override
public void dropContents() {
for (List<ItemStack> itemStacks : this.f) {
for (int i = 0; i < itemStacks.size(); ++i) {
ItemStack itemstack = itemStacks.get(i);
if (!itemstack.isEmpty()) {
itemStacks.set(i, ItemStack.b);
this.player.a(itemstack, true, false);
}
}
}
}
@Override
public boolean h(ItemStack itemstack) {
return this.f.stream().flatMap(List::stream).anyMatch(itemStack1 -> !itemStack1.isEmpty() && itemStack1.doMaterialsMatch(itemstack));
}
@Override
public void a(PlayerInventory playerinventory) {
for (int i = 0; i < playerinventory.getSize(); ++i) {
this.setItem(i, playerinventory.getItem(i));
}
this.itemInHandIndex = playerinventory.itemInHandIndex;
}
@Override
public void clear() {
this.f.forEach(List::clear);
}
@Override
public void a(AutoRecipeStackManager autorecipestackmanager) {
for (ItemStack itemstack : this.items) {
autorecipestackmanager.a(itemstack);
}
}
}
+106
View File
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (C) 2011-2021 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>openinvparent</artifactId>
<groupId>com.lishid</groupId>
<relativePath>../../pom.xml</relativePath>
<version>4.1.9</version>
</parent>
<artifactId>openinvadapter1_17_R1</artifactId>
<name>OpenInvAdapter1_17_R1</name>
<properties>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<artifactId>spigot</artifactId>
<groupId>org.spigotmc</groupId>
<scope>provided</scope>
<version>1.17.1-R0.1-SNAPSHOT</version>
<classifier>remapped-mojang</classifier>
</dependency>
<dependency>
<artifactId>openinvapi</artifactId>
<groupId>com.lishid</groupId>
</dependency>
<dependency>
<artifactId>openinvplugincore</artifactId>
<groupId>com.lishid</groupId>
</dependency>
<dependency>
<artifactId>annotations</artifactId>
<groupId>org.jetbrains</groupId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<!-- Cannot use minimizeJar until maven-shade-plugin 3.3.0 releases; ASM used does not support Java 16. -->
<configuration>
<minimizeJar>false</minimizeJar>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>net.md-5</groupId>
<artifactId>specialsource-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>remap</goal>
</goals>
<id>remap-obf</id>
<configuration>
<srgIn>org.spigotmc:minecraft-server:1.17.1-R0.1-SNAPSHOT:txt:maps-mojang</srgIn>
<reverse>true</reverse>
<remappedDependencies>org.spigotmc:spigot:1.17.1-R0.1-SNAPSHOT:jar:remapped-mojang</remappedDependencies>
<remappedArtifactAttached>true</remappedArtifactAttached>
<remappedClassifierName>remapped-obf</remappedClassifierName>
</configuration>
</execution>
<execution>
<phase>package</phase>
<goals>
<goal>remap</goal>
</goals>
<id>remap-spigot</id>
<configuration>
<inputFile>${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar</inputFile>
<srgIn>org.spigotmc:minecraft-server:1.17.1-R0.1-SNAPSHOT:csrg:maps-spigot</srgIn>
<remappedDependencies>org.spigotmc:spigot:1.17.1-R0.1-SNAPSHOT:jar:remapped-obf</remappedDependencies>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,277 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_17_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.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.world.CompoundContainer;
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;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.block.BarrelBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.ChestBlock;
import net.minecraft.world.level.block.DoubleBlockCombiner;
import net.minecraft.world.level.block.ShulkerBoxBlock;
import net.minecraft.world.level.block.TrappedChestBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
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.Material;
import org.bukkit.Statistic;
import org.bukkit.block.ShulkerBox;
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class AnySilentContainer implements IAnySilentContainer {
private @Nullable Field serverPlayerGameModeGameType;
public AnySilentContainer() {
try {
try {
// IDE warns about field not existing, but SpecialSource does not remap strings used in reflection.
// The warning is not suppressed as a reminder that it must manually be checked on updates.
this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b");
this.serverPlayerGameModeGameType.setAccessible(true);
} catch (NoSuchFieldException e) {
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!");
logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues");
logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail.");
// N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break.
this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class);
}
} catch (SecurityException e) {
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
logger.warning("Unable to directly write player game mode! SilentContainer will fail.");
logger.log(Level.WARNING, "Error obtaining GameType field", e);
}
}
@Override
public boolean isShulkerBlocked(ShulkerBox box) {
org.bukkit.World bukkitWorld = box.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(box.getX(), box.getY(), box.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,
final boolean silentchest,
@NotNull final org.bukkit.block.Block bukkitBlock) {
// Silent ender chest is API-only
if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) {
bukkitPlayer.openInventory(bukkitPlayer.getEnderChest());
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
final ServerLevel level = player.getLevel();
final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
final BlockEntity blockEntity = level.getBlockEntity(blockPos);
if (blockEntity == null) {
return false;
}
if (blockEntity instanceof EnderChestBlockEntity enderChestTile) {
// Anychest ender chest. See net.minecraft.world.level.block.BlockEnderChest
PlayerEnderChestContainer enderChest = player.getEnderChestInventory();
enderChest.setActiveChest(enderChestTile);
player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> {
MenuType<?> containers = PlayerDataManager.getContainers(enderChest.getContainerSize());
int rows = enderChest.getContainerSize() / 9;
return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows);
}, new TranslatableComponent("container.enderchest")));
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
if (!(blockEntity instanceof MenuProvider menuProvider)) {
return false;
}
BlockState blockState = level.getBlockState(blockPos);
Block block = blockState.getBlock();
if (block instanceof ChestBlock chestBlock) {
// boolean flag: do not check if chest is blocked
Optional<MenuProvider> menuOptional = chestBlock.combine(blockState, level, blockPos, true).apply(
// Combiner is a copy of private ChestBlock.MENU_PROVIDER_COMBINER
new DoubleBlockCombiner.Combiner<ChestBlockEntity, Optional<MenuProvider>>() {
@Override
public Optional<MenuProvider> acceptDouble(ChestBlockEntity localChest1, ChestBlockEntity localChest2) {
CompoundContainer doubleChest = new CompoundContainer(localChest1, localChest2);
return Optional.of(new ChestBlock.DoubleInventory(localChest1, localChest2, doubleChest));
}
@Override
public Optional<MenuProvider> acceptSingle(ChestBlockEntity localChest) {
return Optional.of(localChest);
}
@Override
public Optional<MenuProvider> acceptNone() {
return Optional.empty();
}
});
if (menuOptional.isEmpty()) {
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
return false;
}
menuProvider = menuOptional.get();
if (block instanceof TrappedChestBlock) {
bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED);
} else {
bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED);
}
}
if (block instanceof ShulkerBoxBlock) {
bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED);
}
if (block instanceof BarrelBlock) {
bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL);
}
// AnyChest only - SilentChest not active, container unsupported, or unnecessary.
if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) {
player.openMenu(menuProvider);
return true;
}
// SilentChest requires access to setting players' game mode directly.
if (this.serverPlayerGameModeGameType == null) {
return false;
}
if (blockEntity instanceof RandomizableContainerBlockEntity lootable) {
if (lootable.lootTable != null) {
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
return false;
}
}
GameType gameType = player.gameMode.getGameModeForPlayer();
this.forceGameType(player, GameType.SPECTATOR);
player.openMenu(menuProvider);
this.forceGameType(player, gameType);
return true;
}
@Override
public void deactivateContainer(@NotNull final Player bukkitPlayer) {
if (this.serverPlayerGameModeGameType == null) {
return;
}
InventoryView view = bukkitPlayer.getOpenInventory();
switch (view.getType()) {
case CHEST:
case ENDER_CHEST:
case SHULKER_BOX:
case BARREL:
break;
default:
return;
}
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
// Force game mode change without informing plugins or players.
// Regular game mode set calls GameModeChangeEvent and is cancellable.
GameType gameType = player.gameMode.getGameModeForPlayer();
this.forceGameType(player, GameType.SPECTATOR);
// Close container - note that this is very different from ServerPlayer#closeContainer!
// Triggering event must not be re-called or we'll enter an infinite loop.
player.doCloseContainer();
// Revert forced game mode.
this.forceGameType(player, gameType);
}
private void forceGameType(final ServerPlayer player, final GameType gameMode) {
if (this.serverPlayerGameModeGameType == null) {
// No need to warn repeatedly, error on startup and lack of function should be enough.
return;
}
try {
this.serverPlayerGameModeGameType.setAccessible(true);
this.serverPlayerGameModeGameType.set(player.gameMode, gameMode);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,74 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_17_R1;
import java.io.File;
import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.storage.PlayerDataStorage;
import org.apache.logging.log4j.LogManager;
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
public class OpenPlayer extends CraftPlayer {
public OpenPlayer(CraftServer server, ServerPlayer entity) {
super(server, entity);
}
@Override
public void loadData() {
// See CraftPlayer#loadData
CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle());
if (loaded != null) {
readExtraData(loaded);
}
}
@Override
public void saveData() {
ServerPlayer player = this.getHandle();
// See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman)
try {
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
CompoundTag playerData = player.saveWithoutId(new CompoundTag());
setExtraData(playerData);
if (!isOnline()) {
// Special case: save old vehicle data
CompoundTag oldData = worldNBTStorage.load(player);
if (oldData != null && oldData.contains("RootVehicle", 10)) {
// See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound)
playerData.put("RootVehicle", oldData.getCompound("RootVehicle"));
}
}
File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir());
NbtIo.writeCompressed(playerData, file);
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat");
File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old");
Util.safeReplaceFile(file1, file, file2);
} catch (Exception e) {
LogManager.getLogger().warn("Failed to save player data for {}", player.getName().getString());
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,30 +14,31 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_16_R3;
package com.lishid.openinv.internal.v1_17_R1;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.OpenInventoryView;
import com.mojang.authlib.GameProfile;
import java.lang.reflect.Field;
import net.minecraft.server.v1_16_R3.ChatComponentText;
import net.minecraft.server.v1_16_R3.Container;
import net.minecraft.server.v1_16_R3.Containers;
import net.minecraft.server.v1_16_R3.Entity;
import net.minecraft.server.v1_16_R3.EntityPlayer;
import net.minecraft.server.v1_16_R3.MinecraftServer;
import net.minecraft.server.v1_16_R3.PacketPlayOutOpenWindow;
import net.minecraft.server.v1_16_R3.PlayerInteractManager;
import net.minecraft.server.v1_16_R3.World;
import net.minecraft.server.v1_16_R3.WorldServer;
import java.util.logging.Logger;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.Level;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_16_R3.CraftServer;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_16_R3.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftContainer;
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_17_R1.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftContainer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
@@ -51,22 +52,23 @@ public class PlayerDataManager implements IPlayerDataManager {
try {
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
} catch (NoSuchFieldException e) {
System.out.println("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
e.printStackTrace();
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
logger.log(java.util.logging.Level.WARNING, e.getMessage(), e);
bukkitEntity = null;
}
}
public static @NotNull EntityPlayer getHandle(final Player player) {
public static @NotNull ServerPlayer getHandle(final Player player) {
if (player instanceof CraftPlayer) {
return ((CraftPlayer) player).getHandle();
}
Server server = player.getServer();
EntityPlayer nmsPlayer = null;
ServerPlayer nmsPlayer = null;
if (server instanceof CraftServer) {
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getName());
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId());
}
if (nmsPlayer == null) {
@@ -89,13 +91,13 @@ public class PlayerDataManager implements IPlayerDataManager {
GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
WorldServer worldServer = server.getWorldServer(World.OVERWORLD);
ServerLevel worldServer = server.getLevel(Level.OVERWORLD);
if (worldServer == null) {
return null;
}
EntityPlayer entity = new EntityPlayer(server, worldServer, profile, new PlayerInteractManager(worldServer));
ServerPlayer entity = new ServerPlayer(server, worldServer, profile);
try {
injectPlayer(entity);
@@ -113,7 +115,7 @@ public class PlayerDataManager implements IPlayerDataManager {
return target;
}
void injectPlayer(EntityPlayer player) throws IllegalAccessException {
void injectPlayer(ServerPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
@@ -127,7 +129,7 @@ public class PlayerDataManager implements IPlayerDataManager {
@Override
public Player inject(@NotNull Player player) {
try {
EntityPlayer nmsPlayer = getHandle(player);
ServerPlayer nmsPlayer = getHandle(player);
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
@@ -140,9 +142,9 @@ public class PlayerDataManager implements IPlayerDataManager {
@Override
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
EntityPlayer nmsPlayer = getHandle(player);
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.playerConnection == null) {
if (nmsPlayer.connection == null) {
return null;
}
@@ -152,24 +154,24 @@ public class PlayerDataManager implements IPlayerDataManager {
return player.openInventory(inventory.getBukkitInventory());
}
Container container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public Containers<?> getType() {
public MenuType<?> getType() {
return getContainers(inventory.getBukkitInventory().getSize());
}
};
container.setTitle(new ChatComponentText(view.getTitle()));
container.setTitle(new TextComponent(view.getTitle()));
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
if (container == null) {
return null;
}
nmsPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, container.getType(),
new ChatComponentText(container.getBukkitView().getTitle())));
nmsPlayer.activeContainer = container;
container.addSlotListener(nmsPlayer);
nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(),
new TextComponent(container.getBukkitView().getTitle())));
nmsPlayer.containerMenu = container;
nmsPlayer.initMenu(container);
return container.getBukkitView();
@@ -185,23 +187,16 @@ public class PlayerDataManager implements IPlayerDataManager {
}
}
static @NotNull Containers<?> getContainers(int inventorySize) {
switch (inventorySize) {
case 9:
return Containers.GENERIC_9X1;
case 18:
return Containers.GENERIC_9X2;
case 36:
return Containers.GENERIC_9X4;
case 41: // PLAYER
case 45:
return Containers.GENERIC_9X5;
case 54:
return Containers.GENERIC_9X6;
case 27:
default:
return Containers.GENERIC_9X3;
}
static @NotNull MenuType<?> getContainers(int inventorySize) {
return switch (inventorySize) {
case 9 -> MenuType.GENERIC_9x1;
case 18 -> MenuType.GENERIC_9x2;
case 36 -> MenuType.GENERIC_9x4; // PLAYER
case 41, 45 -> MenuType.GENERIC_9x5;
case 54 -> MenuType.GENERIC_9x6;
default -> MenuType.GENERIC_9x3; // Default 27-slot inventory
};
}
@Override
@@ -0,0 +1,344 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_17_R1;
import com.lishid.openinv.internal.ISpecialEnderChest;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.ContainerListener;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.inventory.PlayerEnderChestContainer;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
private final CraftInventory inventory;
private ServerPlayer owner;
private NonNullList<ItemStack> items;
private boolean playerOnline;
public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) {
super(PlayerDataManager.getHandle(player));
this.inventory = new CraftInventory(this);
this.owner = PlayerDataManager.getHandle(player);
this.playerOnline = online;
this.items = this.owner.getEnderChestInventory().items;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return inventory;
}
@Override
public boolean isInUse() {
return !this.getViewers().isEmpty();
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) {
if (!this.playerOnline) {
try {
this.owner = PlayerDataManager.getHandle(player);
PlayerEnderChestContainer enderChest = owner.getEnderChestInventory();
for (int i = 0; i < enderChest.getContainerSize(); ++i) {
enderChest.setItem(i, this.items.get(i));
}
this.items = enderChest.items;
enderChest.transaction.addAll(this.transaction);
} catch (Exception ignored) {}
this.playerOnline = true;
}
}
@Override
public @NotNull org.bukkit.entity.Player getPlayer() {
return owner.getBukkitEntity();
}
@Override
public void setChanged() {
this.owner.getEnderChestInventory().setChanged();
}
@Override
public List<ItemStack> getContents() {
return this.items;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.owner.getEnderChestInventory().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.owner.getEnderChestInventory().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.owner.getEnderChestInventory().getViewers();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public void setActiveChest(EnderChestBlockEntity enderChest) {
this.owner.getEnderChestInventory().setActiveChest(enderChest);
}
@Override
public boolean isActiveChest(EnderChestBlockEntity enderChest) {
return this.owner.getEnderChestInventory().isActiveChest(enderChest);
}
@Override
public int getMaxStackSize() {
return this.owner.getEnderChestInventory().getMaxStackSize();
}
@Override
public void setMaxStackSize(int i) {
this.owner.getEnderChestInventory().setMaxStackSize(i);
}
@Override
public InventoryHolder getOwner() {
return this.owner.getEnderChestInventory().getOwner();
}
@Override
public @Nullable Location getLocation() {
return null;
}
@Override
public void addListener(ContainerListener listener) {
this.owner.getEnderChestInventory().addListener(listener);
}
@Override
public void removeListener(ContainerListener listener) {
this.owner.getEnderChestInventory().removeListener(listener);
}
@Override
public ItemStack getItem(int i) {
return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY;
}
@Override
public ItemStack removeItem(int i, int j) {
ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j);
if (!itemstack.isEmpty()) {
this.setChanged();
}
return itemstack;
}
@Override
public ItemStack addItem(ItemStack itemstack) {
ItemStack localItem = itemstack.copy();
this.moveItemToOccupiedSlotsWithSameType(localItem);
if (localItem.isEmpty()) {
return ItemStack.EMPTY;
} else {
this.moveItemToEmptySlots(localItem);
return localItem.isEmpty() ? ItemStack.EMPTY : localItem;
}
}
@Override
public boolean canAddItem(ItemStack itemstack) {
for (ItemStack itemstack1 : this.items) {
if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) {
return true;
}
}
return false;
}
private void moveItemToEmptySlots(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (localItem.isEmpty()) {
this.setItem(i, itemstack.copy());
itemstack.setCount(0);
return;
}
}
}
private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (ItemStack.isSameItemSameTags(localItem, itemstack)) {
this.moveItemsBetweenStacks(itemstack, localItem);
if (itemstack.isEmpty()) {
return;
}
}
}
}
private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) {
int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize());
int j = Math.min(itemstack.getCount(), i - itemstack1.getCount());
if (j > 0) {
itemstack1.grow(j);
itemstack.shrink(j);
this.setChanged();
}
}
@Override
public ItemStack removeItemNoUpdate(int i) {
ItemStack itemstack = this.items.get(i);
if (itemstack.isEmpty()) {
return ItemStack.EMPTY;
} else {
this.items.set(i, ItemStack.EMPTY);
return itemstack;
}
}
@Override
public void setItem(int i, ItemStack itemstack) {
this.items.set(i, itemstack);
if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) {
itemstack.setCount(this.getMaxStackSize());
}
this.setChanged();
}
@Override
public int getContainerSize() {
return this.owner.getEnderChestInventory().getContainerSize();
}
@Override
public boolean isEmpty() {
return this.items.stream().allMatch(ItemStack::isEmpty);
}
@Override
public void startOpen(Player player) {
}
@Override
public void stopOpen(Player player) {
}
@Override
public boolean canPlaceItem(int i, ItemStack itemstack) {
return true;
}
@Override
public void clearContent() {
this.items.clear();
this.setChanged();
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
for (ItemStack itemstack : this.items) {
stackedContents.accountStack(itemstack);
}
}
@Override
public List<ItemStack> removeAllItems() {
List<ItemStack> list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList());
this.clearContent();
return list;
}
@Override
public ItemStack removeItemType(Item item, int i) {
ItemStack itemstack = new ItemStack(item, 0);
for(int j = this.getContainerSize() - 1; j >= 0; --j) {
ItemStack localItem = this.getItem(j);
if (localItem.getItem().equals(item)) {
int k = i - itemstack.getCount();
ItemStack splitItem = localItem.split(k);
itemstack.grow(splitItem.getCount());
if (itemstack.getCount() == i) {
break;
}
}
}
if (!itemstack.isEmpty()) {
this.setChanged();
}
return itemstack;
}
@Override
public String toString() {
return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).collect(Collectors.toList()).toString();
}
@Override
public void fromTag(ListTag listTag) {
for (int i = 0; i < this.getContainerSize(); ++i) {
this.setItem(i, ItemStack.EMPTY);
}
for (int i = 0; i < listTag.size(); ++i) {
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
if (j < this.getContainerSize()) {
this.setItem(j, ItemStack.of(compoundTag));
}
}
}
}
@@ -0,0 +1,792 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_17_R1;
import com.google.common.collect.ImmutableList;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.Tag;
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_17_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory {
private final CraftInventory inventory;
private boolean playerOnline;
private Player player;
private NonNullList<ItemStack> items;
private NonNullList<ItemStack> armor;
private NonNullList<ItemStack> offhand;
private List<NonNullList<ItemStack>> compartments;
public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) {
super(PlayerDataManager.getHandle(bukkitPlayer));
this.inventory = new CraftInventory(this);
this.playerOnline = online;
this.player = super.player;
this.selected = player.getInventory().selected;
this.items = this.player.getInventory().items;
this.armor = this.player.getInventory().armor;
this.offhand = this.player.getInventory().offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
}
@Override
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
if (!this.playerOnline) {
Player entityPlayer = PlayerDataManager.getHandle(player);
entityPlayer.getInventory().transaction.addAll(this.transaction);
this.player = entityPlayer;
for (int i = 0; i < getContainerSize(); ++i) {
this.player.getInventory().setItem(i, getRawItem(i));
}
this.player.getInventory().selected = this.selected;
this.items = this.player.getInventory().items;
this.armor = this.player.getInventory().armor;
this.offhand = this.player.getInventory().offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
this.playerOnline = true;
}
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return this.inventory;
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public boolean isInUse() {
return !this.getViewers().isEmpty();
}
@Override
public @NotNull HumanEntity getPlayer() {
return this.player.getBukkitEntity();
}
private @NotNull ItemStack getRawItem(int i) {
if (i < 0) {
return ItemStack.EMPTY;
}
NonNullList<ItemStack> list;
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
list = iterator.next();
if (i < list.size()) {
return list.get(i);
}
}
return ItemStack.EMPTY;
}
private void setRawItem(int i, @NotNull ItemStack itemStack) {
if (i < 0) {
return;
}
NonNullList<ItemStack> list;
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
list = iterator.next();
if (i < list.size()) {
list.set(i, itemStack);
}
}
}
private static record IndexedCompartment(@Nullable NonNullList<ItemStack> compartment, int index) {}
private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) {
if (index < items.size()) {
return new IndexedCompartment(items, getReversedItemSlotNum(index));
}
index -= items.size();
if (index < armor.size()) {
return new IndexedCompartment(armor, getReversedArmorSlotNum(index));
}
index -= armor.size();
if (index < offhand.size()) {
return new IndexedCompartment(offhand, index);
}
index -= offhand.size();
return new IndexedCompartment(null, index);
}
private int getReversedArmorSlotNum(final int i) {
if (i == 0) {
return 3;
}
if (i == 1) {
return 2;
}
if (i == 2) {
return 1;
}
if (i == 3) {
return 0;
}
return i;
}
private int getReversedItemSlotNum(final int i) {
if (i >= 27) {
return i - 27;
}
return i + 9;
}
private boolean contains(Predicate<ItemStack> predicate) {
return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate);
}
@Override
public List<ItemStack> getArmorContents() {
return this.armor;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.player.getInventory().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.player.getInventory().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.player.getInventory().getViewers();
}
@Override
public InventoryHolder getOwner() {
return this.player.getBukkitEntity();
}
@Override
public int getMaxStackSize() {
return this.player.getInventory().getMaxStackSize();
}
@Override
public void setMaxStackSize(int size) {
this.player.getInventory().setMaxStackSize(size);
}
@Override
public Location getLocation() {
return this.player.getBukkitEntity().getLocation();
}
@Override
public boolean hasCustomName() {
return false;
}
@Override
public List<ItemStack> getContents() {
return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList());
}
@Override
public ItemStack getSelected() {
return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY;
}
private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) {
return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
}
@Override
public int canHold(ItemStack itemstack) {
int remains = itemstack.getCount();
for (int i = 0; i < this.items.size(); ++i) {
ItemStack itemstack1 = this.getRawItem(i);
if (itemstack1.isEmpty()) {
return itemstack.getCount();
}
if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) {
remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount();
}
if (remains <= 0) {
return itemstack.getCount();
}
}
ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size());
if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) {
remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount();
}
return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains;
}
@Override
public int getFreeSlot() {
for(int i = 0; i < this.items.size(); ++i) {
if (this.items.get(i).isEmpty()) {
return i;
}
}
return -1;
}
@Override
public void setPickedItem(ItemStack itemstack) {
int i = this.findSlotMatchingItem(itemstack);
if (isHotbarSlot(i)) {
this.selected = i;
} else if (i == -1) {
this.selected = this.getSuitableHotbarSlot();
if (!this.items.get(this.selected).isEmpty()) {
int j = this.getFreeSlot();
if (j != -1) {
this.items.set(j, this.items.get(this.selected));
}
}
this.items.set(this.selected, itemstack);
} else {
this.pickSlot(i);
}
}
@Override
public void pickSlot(int i) {
this.selected = this.getSuitableHotbarSlot();
ItemStack itemstack = this.items.get(this.selected);
this.items.set(this.selected, this.items.get(i));
this.items.set(i, itemstack);
}
@Override
public int findSlotMatchingItem(ItemStack itemstack) {
for(int i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) {
return i;
}
}
return -1;
}
@Override
public int findSlotMatchingUnusedItem(ItemStack itemStack) {
for(int i = 0; i < this.items.size(); ++i) {
ItemStack localItem = this.items.get(i);
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) {
return i;
}
}
return -1;
}
@Override
public int getSuitableHotbarSlot() {
int i;
int j;
for(j = 0; j < 9; ++j) {
i = (this.selected + j) % 9;
if (this.items.get(i).isEmpty()) {
return i;
}
}
for(j = 0; j < 9; ++j) {
i = (this.selected + j) % 9;
if (!this.items.get(i).isEnchanted()) {
return i;
}
}
return this.selected;
}
@Override
public void swapPaint(double d0) {
if (d0 > 0.0D) {
d0 = 1.0D;
}
if (d0 < 0.0D) {
d0 = -1.0D;
}
this.selected = (int) (this.selected - d0);
while (this.selected < 0) {
this.selected += 9;
}
while(this.selected >= 9) {
this.selected -= 9;
}
}
@Override
public int clearOrCountMatchingItems(Predicate<ItemStack> predicate, int i, Container container) {
byte b0 = 0;
boolean flag = i == 0;
int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag);
j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag);
ItemStack itemstack = this.player.containerMenu.getCarried();
j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag);
if (itemstack.isEmpty()) {
this.player.containerMenu.setCarried(ItemStack.EMPTY);
}
return j;
}
private int addResource(ItemStack itemstack) {
int i = this.getSlotWithRemainingSpace(itemstack);
if (i == -1) {
i = this.getFreeSlot();
}
return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack);
}
private int addResource(int i, ItemStack itemstack) {
Item item = itemstack.getItem();
int j = itemstack.getCount();
ItemStack localItemStack = this.getRawItem(i);
if (localItemStack.isEmpty()) {
localItemStack = new ItemStack(item, 0);
if (itemstack.hasTag()) {
// hasTag ensures tag not null
//noinspection ConstantConditions
localItemStack.setTag(itemstack.getTag().copy());
}
this.setRawItem(i, localItemStack);
}
int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount());
if (k > this.getMaxStackSize() - localItemStack.getCount()) {
k = this.getMaxStackSize() - localItemStack.getCount();
}
if (k != 0) {
j -= k;
localItemStack.grow(k);
localItemStack.setPopTime(5);
}
return j;
}
@Override
public int getSlotWithRemainingSpace(ItemStack itemstack) {
if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) {
return this.selected;
} else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) {
return 40;
} else {
for(int i = 0; i < this.items.size(); ++i) {
if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) {
return i;
}
}
return -1;
}
}
@Override
public void tick() {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
if (!compartment.get(i).isEmpty()) {
compartment.get(i).inventoryTick(this.player.level, this.player, i, this.selected == i);
}
}
}
}
@Override
public boolean add(ItemStack itemStack) {
return this.add(-1, itemStack);
}
@Override
public boolean add(int i, ItemStack itemStack) {
if (itemStack.isEmpty()) {
return false;
} else {
try {
if (itemStack.isDamaged()) {
if (i == -1) {
i = this.getFreeSlot();
}
if (i >= 0) {
this.items.set(i, itemStack.copy());
this.items.get(i).setPopTime(5);
itemStack.setCount(0);
return true;
} else if (this.player.getAbilities().instabuild) {
itemStack.setCount(0);
return true;
} else {
return false;
}
} else {
int j;
do {
j = itemStack.getCount();
if (i == -1) {
itemStack.setCount(this.addResource(itemStack));
} else {
itemStack.setCount(this.addResource(i, itemStack));
}
} while(!itemStack.isEmpty() && itemStack.getCount() < j);
if (itemStack.getCount() == j && this.player.getAbilities().instabuild) {
itemStack.setCount(0);
return true;
} else {
return itemStack.getCount() < j;
}
}
} catch (Throwable var6) {
CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory");
CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added");
crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem()));
crashReportCategory.setDetail("Item data", itemStack.getDamageValue());
crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString());
throw new ReportedException(crashReport);
}
}
}
@Override
public void placeItemBackInInventory(ItemStack itemStack) {
this.placeItemBackInInventory(itemStack, true);
}
@Override
public void placeItemBackInInventory(ItemStack itemStack, boolean flag) {
while(true) {
if (!itemStack.isEmpty()) {
int i = this.getSlotWithRemainingSpace(itemStack);
if (i == -1) {
i = this.getFreeSlot();
}
if (i != -1) {
int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount();
if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) {
((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i)));
}
continue;
}
this.player.drop(itemStack, false);
}
return;
}
}
@Override
public ItemStack removeItem(int rawIndex, final int j) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null
|| indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) {
return ItemStack.EMPTY;
}
return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j);
}
@Override
public void removeItem(ItemStack itemStack) {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
if (compartment.get(i) == itemStack) {
compartment.set(i, ItemStack.EMPTY);
break;
}
}
}
}
@Override
public ItemStack removeItemNoUpdate(int rawIndex) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
return ItemStack.EMPTY;
}
ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY);
if (removed.isEmpty()) {
return ItemStack.EMPTY;
}
return removed;
}
@Override
public void setItem(int rawIndex, final ItemStack itemStack) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
this.player.drop(itemStack, true);
return;
}
indexedCompartment.compartment().set(indexedCompartment.index(), itemStack);
}
@Override
public float getDestroySpeed(BlockState blockState) {
return this.items.get(this.selected).getDestroySpeed(blockState);
}
@Override
public ListTag save(ListTag listTag) {
for (int i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)i);
this.items.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
for (int i = 0; i < this.armor.size(); ++i) {
if (!this.armor.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 100));
this.armor.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
for (int i = 0; i < this.offhand.size(); ++i) {
if (!this.offhand.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 150));
this.offhand.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
return listTag;
}
@Override
public void load(ListTag listTag) {
this.items.clear();
this.armor.clear();
this.offhand.clear();
for(int i = 0; i < listTag.size(); ++i) {
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
ItemStack itemstack = ItemStack.of(compoundTag);
if (!itemstack.isEmpty()) {
if (j < this.items.size()) {
this.items.set(j, itemstack);
} else if (j >= 100 && j < this.armor.size() + 100) {
this.armor.set(j - 100, itemstack);
} else if (j >= 150 && j < this.offhand.size() + 150) {
this.offhand.set(j - 150, itemstack);
}
}
}
}
@Override
public int getContainerSize() {
return 45;
}
@Override
public boolean isEmpty() {
return !contains(itemStack -> !itemStack.isEmpty());
}
@Override
public ItemStack getItem(int rawIndex) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
return ItemStack.EMPTY;
}
return indexedCompartment.compartment().get(indexedCompartment.index());
}
@Override
public Component getName() {
return this.player.getName();
}
@Override
public ItemStack getArmor(int index) {
return this.armor.get(index);
}
@Override
public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) {
if (damage > 0.0F) {
damage /= 4.0F;
if (damage < 1.0F) {
damage = 1.0F;
}
for (int index : armorIndices) {
ItemStack itemstack = this.armor.get(index);
if ((!damagesource.isFire() || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) {
itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index)));
}
}
}
}
@Override
public void dropAll() {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
ItemStack itemstack = compartment.get(i);
if (!itemstack.isEmpty()) {
this.player.drop(itemstack, true, false);
compartment.set(i, ItemStack.EMPTY);
}
}
}
}
@Override
public void setChanged() {
super.setChanged();
}
@Override
public int getTimesChanged() {
return super.getTimesChanged();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public boolean contains(ItemStack itemstack) {
return contains(itemStack -> itemStack.isEmpty() && itemStack.sameItem(itemstack));
}
@Override
public boolean contains(Tag<Item> tag) {
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tag));
}
@Override
public void replaceWith(Inventory inventory) {
Function<Integer, ItemStack> getter;
if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) {
getter = specialPlayerInventory::getRawItem;
} else {
getter = inventory::getItem;
}
for(int i = 0; i < this.getContainerSize(); ++i) {
this.setRawItem(i, getter.apply(i));
}
this.selected = inventory.selected;
}
@Override
public void clearContent() {
for (NonNullList<ItemStack> compartment : this.compartments) {
compartment.clear();
}
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
for (ItemStack itemstack : this.items) {
stackedContents.accountSimpleStack(itemstack);
}
}
@Override
public ItemStack removeFromSelected(boolean dropWholeStack) {
ItemStack itemstack = this.getSelected();
return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1);
}
}
+112
View File
@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (C) 2011-2021 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>openinvparent</artifactId>
<groupId>com.lishid</groupId>
<relativePath>../../pom.xml</relativePath>
<version>4.1.9</version>
</parent>
<artifactId>openinvadapter1_18_R1</artifactId>
<name>OpenInvAdapter1_18_R1</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spigot.version>1.18-R0.1-SNAPSHOT</spigot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${spigot.version}</version>
</dependency>
<dependency>
<artifactId>spigot</artifactId>
<groupId>org.spigotmc</groupId>
<scope>provided</scope>
<version>${spigot.version}</version>
<classifier>remapped-mojang</classifier>
</dependency>
<dependency>
<artifactId>openinvapi</artifactId>
<groupId>com.lishid</groupId>
</dependency>
<dependency>
<artifactId>openinvplugincore</artifactId>
<groupId>com.lishid</groupId>
</dependency>
<dependency>
<artifactId>annotations</artifactId>
<groupId>org.jetbrains</groupId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<!-- Cannot use minimizeJar until maven-shade-plugin 3.3.0 releases; ASM used does not support Java 17. -->
<configuration>
<minimizeJar>false</minimizeJar>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>net.md-5</groupId>
<artifactId>specialsource-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>remap</goal>
</goals>
<id>remap-obf</id>
<configuration>
<srgIn>org.spigotmc:minecraft-server:${spigot.version}:txt:maps-mojang</srgIn>
<reverse>true</reverse>
<remappedDependencies>org.spigotmc:spigot:${spigot.version}:jar:remapped-mojang</remappedDependencies>
<remappedArtifactAttached>true</remappedArtifactAttached>
<remappedClassifierName>remapped-obf</remappedClassifierName>
</configuration>
</execution>
<execution>
<phase>package</phase>
<goals>
<goal>remap</goal>
</goals>
<id>remap-spigot</id>
<configuration>
<inputFile>${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar</inputFile>
<srgIn>org.spigotmc:minecraft-server:${spigot.version}:csrg:maps-spigot</srgIn>
<remappedDependencies>org.spigotmc:spigot:${spigot.version}:jar:remapped-obf</remappedDependencies>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,277 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_18_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.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.world.CompoundContainer;
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;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.block.BarrelBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.ChestBlock;
import net.minecraft.world.level.block.DoubleBlockCombiner;
import net.minecraft.world.level.block.ShulkerBoxBlock;
import net.minecraft.world.level.block.TrappedChestBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
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.Material;
import org.bukkit.Statistic;
import org.bukkit.block.ShulkerBox;
import org.bukkit.craftbukkit.v1_18_R1.CraftWorld;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class AnySilentContainer implements IAnySilentContainer {
private @Nullable Field serverPlayerGameModeGameType;
public AnySilentContainer() {
try {
try {
// IDE warns about field not existing, but SpecialSource does not remap strings used in reflection.
// The warning is not suppressed as a reminder that it must manually be checked on updates.
this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b");
this.serverPlayerGameModeGameType.setAccessible(true);
} catch (NoSuchFieldException e) {
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!");
logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues");
logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail.");
// N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break.
this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class);
}
} catch (SecurityException e) {
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
logger.warning("Unable to directly write player game mode! SilentContainer will fail.");
logger.log(Level.WARNING, "Error obtaining GameType field", e);
}
}
@Override
public boolean isShulkerBlocked(ShulkerBox box) {
org.bukkit.World bukkitWorld = box.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(box.getX(), box.getY(), box.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,
final boolean silentchest,
@NotNull final org.bukkit.block.Block bukkitBlock) {
// Silent ender chest is API-only
if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) {
bukkitPlayer.openInventory(bukkitPlayer.getEnderChest());
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
final ServerLevel level = player.getLevel();
final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
final BlockEntity blockEntity = level.getBlockEntity(blockPos);
if (blockEntity == null) {
return false;
}
if (blockEntity instanceof EnderChestBlockEntity enderChestTile) {
// Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock
PlayerEnderChestContainer enderChest = player.getEnderChestInventory();
enderChest.setActiveChest(enderChestTile);
player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> {
MenuType<?> containers = PlayerDataManager.getContainers(enderChest.getContainerSize());
int rows = enderChest.getContainerSize() / 9;
return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows);
}, new TranslatableComponent("container.enderchest")));
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
if (!(blockEntity instanceof MenuProvider menuProvider)) {
return false;
}
BlockState blockState = level.getBlockState(blockPos);
Block block = blockState.getBlock();
if (block instanceof ChestBlock chestBlock) {
// boolean flag: do not check if chest is blocked
Optional<MenuProvider> menuOptional = chestBlock.combine(blockState, level, blockPos, true).apply(
// Combiner is a copy of private ChestBlock.MENU_PROVIDER_COMBINER
new DoubleBlockCombiner.Combiner<ChestBlockEntity, Optional<MenuProvider>>() {
@Override
public Optional<MenuProvider> acceptDouble(ChestBlockEntity localChest1, ChestBlockEntity localChest2) {
CompoundContainer doubleChest = new CompoundContainer(localChest1, localChest2);
return Optional.of(new ChestBlock.DoubleInventory(localChest1, localChest2, doubleChest));
}
@Override
public Optional<MenuProvider> acceptSingle(ChestBlockEntity localChest) {
return Optional.of(localChest);
}
@Override
public Optional<MenuProvider> acceptNone() {
return Optional.empty();
}
});
if (menuOptional.isEmpty()) {
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
return false;
}
menuProvider = menuOptional.get();
if (block instanceof TrappedChestBlock) {
bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED);
} else {
bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED);
}
}
if (block instanceof ShulkerBoxBlock) {
bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED);
}
if (block instanceof BarrelBlock) {
bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL);
}
// AnyChest only - SilentChest not active, container unsupported, or unnecessary.
if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) {
player.openMenu(menuProvider);
return true;
}
// SilentChest requires access to setting players' game mode directly.
if (this.serverPlayerGameModeGameType == null) {
return false;
}
if (blockEntity instanceof RandomizableContainerBlockEntity lootable) {
if (lootable.lootTable != null) {
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
return false;
}
}
GameType gameType = player.gameMode.getGameModeForPlayer();
this.forceGameType(player, GameType.SPECTATOR);
player.openMenu(menuProvider);
this.forceGameType(player, gameType);
return true;
}
@Override
public void deactivateContainer(@NotNull final Player bukkitPlayer) {
if (this.serverPlayerGameModeGameType == null) {
return;
}
InventoryView view = bukkitPlayer.getOpenInventory();
switch (view.getType()) {
case CHEST:
case ENDER_CHEST:
case SHULKER_BOX:
case BARREL:
break;
default:
return;
}
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
// Force game mode change without informing plugins or players.
// Regular game mode set calls GameModeChangeEvent and is cancellable.
GameType gameType = player.gameMode.getGameModeForPlayer();
this.forceGameType(player, GameType.SPECTATOR);
// Close container - note that this is very different from ServerPlayer#closeContainer!
// Triggering event must not be re-called or we'll enter an infinite loop.
player.doCloseContainer();
// Revert forced game mode.
this.forceGameType(player, gameType);
}
private void forceGameType(final ServerPlayer player, final GameType gameMode) {
if (this.serverPlayerGameModeGameType == null) {
// No need to warn repeatedly, error on startup and lack of function should be enough.
return;
}
try {
this.serverPlayerGameModeGameType.setAccessible(true);
this.serverPlayerGameModeGameType.set(player.gameMode, gameMode);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,74 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_18_R1;
import java.io.File;
import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.storage.PlayerDataStorage;
import org.apache.logging.log4j.LogManager;
import org.bukkit.craftbukkit.v1_18_R1.CraftServer;
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer;
public class OpenPlayer extends CraftPlayer {
public OpenPlayer(CraftServer server, ServerPlayer entity) {
super(server, entity);
}
@Override
public void loadData() {
// See CraftPlayer#loadData
CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle());
if (loaded != null) {
readExtraData(loaded);
}
}
@Override
public void saveData() {
ServerPlayer player = this.getHandle();
// See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman)
try {
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
CompoundTag playerData = player.saveWithoutId(new CompoundTag());
setExtraData(playerData);
if (!isOnline()) {
// Special case: save old vehicle data
CompoundTag oldData = worldNBTStorage.load(player);
if (oldData != null && oldData.contains("RootVehicle", 10)) {
// See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound)
playerData.put("RootVehicle", oldData.getCompound("RootVehicle"));
}
}
File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir());
NbtIo.writeCompressed(playerData, file);
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat");
File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old");
Util.safeReplaceFile(file1, file, file2);
} catch (Exception e) {
LogManager.getLogger().warn("Failed to save player data for {}", player.getName().getString());
}
}
}
@@ -0,0 +1,230 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_18_R1;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.OpenInventoryView;
import com.mojang.authlib.GameProfile;
import java.lang.reflect.Field;
import java.util.logging.Logger;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.Level;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_18_R1.CraftServer;
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_18_R1.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftContainer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PlayerDataManager implements IPlayerDataManager {
private @Nullable Field bukkitEntity;
public PlayerDataManager() {
try {
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
} catch (NoSuchFieldException e) {
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
logger.log(java.util.logging.Level.WARNING, e.getMessage(), e);
bukkitEntity = null;
}
}
public static @NotNull ServerPlayer getHandle(final Player player) {
if (player instanceof CraftPlayer) {
return ((CraftPlayer) player).getHandle();
}
Server server = player.getServer();
ServerPlayer nmsPlayer = null;
if (server instanceof CraftServer) {
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId());
}
if (nmsPlayer == null) {
// Could use reflection to examine fields, but it's honestly not worth the bother.
throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation");
}
return nmsPlayer;
}
@Override
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
// Ensure player has data
if (!offline.hasPlayedBefore()) {
return null;
}
// Create a profile and entity to load the player data
// See net.minecraft.server.PlayerList#attemptLogin
GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
ServerLevel worldServer = server.getLevel(Level.OVERWORLD);
if (worldServer == null) {
return null;
}
ServerPlayer entity = new ServerPlayer(server, worldServer, profile);
try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// Get the bukkit entity
Player target = entity.getBukkitEntity();
if (target != null) {
// Load data
target.loadData();
}
// Return the entity
return target;
}
void injectPlayer(ServerPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new OpenPlayer(player.server.server, player));
}
@NotNull
@Override
public Player inject(@NotNull Player player) {
try {
ServerPlayer nmsPlayer = getHandle(player);
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
e.printStackTrace();
return player;
}
}
@Nullable
@Override
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.connection == null) {
return null;
}
InventoryView view = getView(player, inventory);
if (view == null) {
return player.openInventory(inventory.getBukkitInventory());
}
AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public MenuType<?> getType() {
return getContainers(inventory.getBukkitInventory().getSize());
}
};
container.setTitle(new TextComponent(view.getTitle()));
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
if (container == null) {
return null;
}
nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(),
new TextComponent(container.getBukkitView().getTitle())));
nmsPlayer.containerMenu = container;
nmsPlayer.initMenu(container);
return container.getBukkitView();
}
private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) {
if (inventory instanceof SpecialEnderChest) {
return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest");
} else if (inventory instanceof SpecialPlayerInventory) {
return new OpenInventoryView(player, inventory, "container.player", "'s Inventory");
} else {
return null;
}
}
static @NotNull MenuType<?> getContainers(int inventorySize) {
return switch (inventorySize) {
case 9 -> MenuType.GENERIC_9x1;
case 18 -> MenuType.GENERIC_9x2;
case 36 -> MenuType.GENERIC_9x4; // PLAYER
case 41, 45 -> MenuType.GENERIC_9x5;
case 54 -> MenuType.GENERIC_9x6;
default -> MenuType.GENERIC_9x3; // Default 27-slot inventory
};
}
@Override
public int convertToPlayerSlot(InventoryView view, int rawSlot) {
int topSize = view.getTopInventory().getSize();
if (topSize <= rawSlot) {
// Slot is not inside special inventory, use Bukkit logic.
return view.convertSlot(rawSlot);
}
// Main inventory, slots 0-26 -> 9-35
if (rawSlot < 27) {
return rawSlot + 9;
}
// Hotbar, slots 27-35 -> 0-8
if (rawSlot < 36) {
return rawSlot - 27;
}
// Armor, slots 36-39 -> 39-36
if (rawSlot < 40) {
return 36 + (39 - rawSlot);
}
// Off hand
if (rawSlot == 40) {
return 40;
}
// Drop slots, "out of inventory"
return -1;
}
}
@@ -0,0 +1,344 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_18_R1;
import com.lishid.openinv.internal.ISpecialEnderChest;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.ContainerListener;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.inventory.PlayerEnderChestContainer;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
private final CraftInventory inventory;
private ServerPlayer owner;
private NonNullList<ItemStack> items;
private boolean playerOnline;
public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) {
super(PlayerDataManager.getHandle(player));
this.inventory = new CraftInventory(this);
this.owner = PlayerDataManager.getHandle(player);
this.playerOnline = online;
this.items = this.owner.getEnderChestInventory().items;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return inventory;
}
@Override
public boolean isInUse() {
return !this.getViewers().isEmpty();
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) {
if (!this.playerOnline) {
try {
this.owner = PlayerDataManager.getHandle(player);
PlayerEnderChestContainer enderChest = owner.getEnderChestInventory();
for (int i = 0; i < enderChest.getContainerSize(); ++i) {
enderChest.setItem(i, this.items.get(i));
}
this.items = enderChest.items;
enderChest.transaction.addAll(this.transaction);
} catch (Exception ignored) {}
this.playerOnline = true;
}
}
@Override
public @NotNull org.bukkit.entity.Player getPlayer() {
return owner.getBukkitEntity();
}
@Override
public void setChanged() {
this.owner.getEnderChestInventory().setChanged();
}
@Override
public List<ItemStack> getContents() {
return this.items;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.owner.getEnderChestInventory().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.owner.getEnderChestInventory().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.owner.getEnderChestInventory().getViewers();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public void setActiveChest(EnderChestBlockEntity enderChest) {
this.owner.getEnderChestInventory().setActiveChest(enderChest);
}
@Override
public boolean isActiveChest(EnderChestBlockEntity enderChest) {
return this.owner.getEnderChestInventory().isActiveChest(enderChest);
}
@Override
public int getMaxStackSize() {
return this.owner.getEnderChestInventory().getMaxStackSize();
}
@Override
public void setMaxStackSize(int i) {
this.owner.getEnderChestInventory().setMaxStackSize(i);
}
@Override
public InventoryHolder getOwner() {
return this.owner.getEnderChestInventory().getOwner();
}
@Override
public @Nullable Location getLocation() {
return null;
}
@Override
public void addListener(ContainerListener listener) {
this.owner.getEnderChestInventory().addListener(listener);
}
@Override
public void removeListener(ContainerListener listener) {
this.owner.getEnderChestInventory().removeListener(listener);
}
@Override
public ItemStack getItem(int i) {
return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY;
}
@Override
public ItemStack removeItem(int i, int j) {
ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j);
if (!itemstack.isEmpty()) {
this.setChanged();
}
return itemstack;
}
@Override
public ItemStack addItem(ItemStack itemstack) {
ItemStack localItem = itemstack.copy();
this.moveItemToOccupiedSlotsWithSameType(localItem);
if (localItem.isEmpty()) {
return ItemStack.EMPTY;
} else {
this.moveItemToEmptySlots(localItem);
return localItem.isEmpty() ? ItemStack.EMPTY : localItem;
}
}
@Override
public boolean canAddItem(ItemStack itemstack) {
for (ItemStack itemstack1 : this.items) {
if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) {
return true;
}
}
return false;
}
private void moveItemToEmptySlots(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (localItem.isEmpty()) {
this.setItem(i, itemstack.copy());
itemstack.setCount(0);
return;
}
}
}
private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (ItemStack.isSameItemSameTags(localItem, itemstack)) {
this.moveItemsBetweenStacks(itemstack, localItem);
if (itemstack.isEmpty()) {
return;
}
}
}
}
private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) {
int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize());
int j = Math.min(itemstack.getCount(), i - itemstack1.getCount());
if (j > 0) {
itemstack1.grow(j);
itemstack.shrink(j);
this.setChanged();
}
}
@Override
public ItemStack removeItemNoUpdate(int i) {
ItemStack itemstack = this.items.get(i);
if (itemstack.isEmpty()) {
return ItemStack.EMPTY;
} else {
this.items.set(i, ItemStack.EMPTY);
return itemstack;
}
}
@Override
public void setItem(int i, ItemStack itemstack) {
this.items.set(i, itemstack);
if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) {
itemstack.setCount(this.getMaxStackSize());
}
this.setChanged();
}
@Override
public int getContainerSize() {
return this.owner.getEnderChestInventory().getContainerSize();
}
@Override
public boolean isEmpty() {
return this.items.stream().allMatch(ItemStack::isEmpty);
}
@Override
public void startOpen(Player player) {
}
@Override
public void stopOpen(Player player) {
}
@Override
public boolean canPlaceItem(int i, ItemStack itemstack) {
return true;
}
@Override
public void clearContent() {
this.items.clear();
this.setChanged();
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
for (ItemStack itemstack : this.items) {
stackedContents.accountStack(itemstack);
}
}
@Override
public List<ItemStack> removeAllItems() {
List<ItemStack> list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList());
this.clearContent();
return list;
}
@Override
public ItemStack removeItemType(Item item, int i) {
ItemStack itemstack = new ItemStack(item, 0);
for(int j = this.getContainerSize() - 1; j >= 0; --j) {
ItemStack localItem = this.getItem(j);
if (localItem.getItem().equals(item)) {
int k = i - itemstack.getCount();
ItemStack splitItem = localItem.split(k);
itemstack.grow(splitItem.getCount());
if (itemstack.getCount() == i) {
break;
}
}
}
if (!itemstack.isEmpty()) {
this.setChanged();
}
return itemstack;
}
@Override
public String toString() {
return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).collect(Collectors.toList()).toString();
}
@Override
public void fromTag(ListTag listTag) {
for (int i = 0; i < this.getContainerSize(); ++i) {
this.setItem(i, ItemStack.EMPTY);
}
for (int i = 0; i < listTag.size(); ++i) {
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
if (j < this.getContainerSize()) {
this.setItem(j, ItemStack.of(compoundTag));
}
}
}
}
@@ -0,0 +1,792 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_18_R1;
import com.google.common.collect.ImmutableList;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.Tag;
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_18_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory {
private final CraftInventory inventory;
private boolean playerOnline;
private Player player;
private NonNullList<ItemStack> items;
private NonNullList<ItemStack> armor;
private NonNullList<ItemStack> offhand;
private List<NonNullList<ItemStack>> compartments;
public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) {
super(PlayerDataManager.getHandle(bukkitPlayer));
this.inventory = new CraftInventory(this);
this.playerOnline = online;
this.player = super.player;
this.selected = player.getInventory().selected;
this.items = this.player.getInventory().items;
this.armor = this.player.getInventory().armor;
this.offhand = this.player.getInventory().offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
}
@Override
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
if (!this.playerOnline) {
Player entityPlayer = PlayerDataManager.getHandle(player);
entityPlayer.getInventory().transaction.addAll(this.transaction);
this.player = entityPlayer;
for (int i = 0; i < getContainerSize(); ++i) {
this.player.getInventory().setItem(i, getRawItem(i));
}
this.player.getInventory().selected = this.selected;
this.items = this.player.getInventory().items;
this.armor = this.player.getInventory().armor;
this.offhand = this.player.getInventory().offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
this.playerOnline = true;
}
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return this.inventory;
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public boolean isInUse() {
return !this.getViewers().isEmpty();
}
@Override
public @NotNull HumanEntity getPlayer() {
return this.player.getBukkitEntity();
}
private @NotNull ItemStack getRawItem(int i) {
if (i < 0) {
return ItemStack.EMPTY;
}
NonNullList<ItemStack> list;
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
list = iterator.next();
if (i < list.size()) {
return list.get(i);
}
}
return ItemStack.EMPTY;
}
private void setRawItem(int i, @NotNull ItemStack itemStack) {
if (i < 0) {
return;
}
NonNullList<ItemStack> list;
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
list = iterator.next();
if (i < list.size()) {
list.set(i, itemStack);
}
}
}
private static record IndexedCompartment(@Nullable NonNullList<ItemStack> compartment, int index) {}
private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) {
if (index < items.size()) {
return new IndexedCompartment(items, getReversedItemSlotNum(index));
}
index -= items.size();
if (index < armor.size()) {
return new IndexedCompartment(armor, getReversedArmorSlotNum(index));
}
index -= armor.size();
if (index < offhand.size()) {
return new IndexedCompartment(offhand, index);
}
index -= offhand.size();
return new IndexedCompartment(null, index);
}
private int getReversedArmorSlotNum(final int i) {
if (i == 0) {
return 3;
}
if (i == 1) {
return 2;
}
if (i == 2) {
return 1;
}
if (i == 3) {
return 0;
}
return i;
}
private int getReversedItemSlotNum(final int i) {
if (i >= 27) {
return i - 27;
}
return i + 9;
}
private boolean contains(Predicate<ItemStack> predicate) {
return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate);
}
@Override
public List<ItemStack> getArmorContents() {
return this.armor;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.player.getInventory().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.player.getInventory().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.player.getInventory().getViewers();
}
@Override
public InventoryHolder getOwner() {
return this.player.getBukkitEntity();
}
@Override
public int getMaxStackSize() {
return this.player.getInventory().getMaxStackSize();
}
@Override
public void setMaxStackSize(int size) {
this.player.getInventory().setMaxStackSize(size);
}
@Override
public Location getLocation() {
return this.player.getBukkitEntity().getLocation();
}
@Override
public boolean hasCustomName() {
return false;
}
@Override
public List<ItemStack> getContents() {
return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList());
}
@Override
public ItemStack getSelected() {
return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY;
}
private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) {
return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
}
@Override
public int canHold(ItemStack itemstack) {
int remains = itemstack.getCount();
for (int i = 0; i < this.items.size(); ++i) {
ItemStack itemstack1 = this.getRawItem(i);
if (itemstack1.isEmpty()) {
return itemstack.getCount();
}
if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) {
remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount();
}
if (remains <= 0) {
return itemstack.getCount();
}
}
ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size());
if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) {
remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount();
}
return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains;
}
@Override
public int getFreeSlot() {
for(int i = 0; i < this.items.size(); ++i) {
if (this.items.get(i).isEmpty()) {
return i;
}
}
return -1;
}
@Override
public void setPickedItem(ItemStack itemstack) {
int i = this.findSlotMatchingItem(itemstack);
if (isHotbarSlot(i)) {
this.selected = i;
} else if (i == -1) {
this.selected = this.getSuitableHotbarSlot();
if (!this.items.get(this.selected).isEmpty()) {
int j = this.getFreeSlot();
if (j != -1) {
this.items.set(j, this.items.get(this.selected));
}
}
this.items.set(this.selected, itemstack);
} else {
this.pickSlot(i);
}
}
@Override
public void pickSlot(int i) {
this.selected = this.getSuitableHotbarSlot();
ItemStack itemstack = this.items.get(this.selected);
this.items.set(this.selected, this.items.get(i));
this.items.set(i, itemstack);
}
@Override
public int findSlotMatchingItem(ItemStack itemstack) {
for(int i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) {
return i;
}
}
return -1;
}
@Override
public int findSlotMatchingUnusedItem(ItemStack itemStack) {
for(int i = 0; i < this.items.size(); ++i) {
ItemStack localItem = this.items.get(i);
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) {
return i;
}
}
return -1;
}
@Override
public int getSuitableHotbarSlot() {
int i;
int j;
for(j = 0; j < 9; ++j) {
i = (this.selected + j) % 9;
if (this.items.get(i).isEmpty()) {
return i;
}
}
for(j = 0; j < 9; ++j) {
i = (this.selected + j) % 9;
if (!this.items.get(i).isEnchanted()) {
return i;
}
}
return this.selected;
}
@Override
public void swapPaint(double d0) {
if (d0 > 0.0D) {
d0 = 1.0D;
}
if (d0 < 0.0D) {
d0 = -1.0D;
}
this.selected = (int) (this.selected - d0);
while (this.selected < 0) {
this.selected += 9;
}
while(this.selected >= 9) {
this.selected -= 9;
}
}
@Override
public int clearOrCountMatchingItems(Predicate<ItemStack> predicate, int i, Container container) {
byte b0 = 0;
boolean flag = i == 0;
int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag);
j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag);
ItemStack itemstack = this.player.containerMenu.getCarried();
j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag);
if (itemstack.isEmpty()) {
this.player.containerMenu.setCarried(ItemStack.EMPTY);
}
return j;
}
private int addResource(ItemStack itemstack) {
int i = this.getSlotWithRemainingSpace(itemstack);
if (i == -1) {
i = this.getFreeSlot();
}
return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack);
}
private int addResource(int i, ItemStack itemstack) {
Item item = itemstack.getItem();
int j = itemstack.getCount();
ItemStack localItemStack = this.getRawItem(i);
if (localItemStack.isEmpty()) {
localItemStack = new ItemStack(item, 0);
if (itemstack.hasTag()) {
// hasTag ensures tag not null
//noinspection ConstantConditions
localItemStack.setTag(itemstack.getTag().copy());
}
this.setRawItem(i, localItemStack);
}
int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount());
if (k > this.getMaxStackSize() - localItemStack.getCount()) {
k = this.getMaxStackSize() - localItemStack.getCount();
}
if (k != 0) {
j -= k;
localItemStack.grow(k);
localItemStack.setPopTime(5);
}
return j;
}
@Override
public int getSlotWithRemainingSpace(ItemStack itemstack) {
if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) {
return this.selected;
} else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) {
return 40;
} else {
for(int i = 0; i < this.items.size(); ++i) {
if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) {
return i;
}
}
return -1;
}
}
@Override
public void tick() {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
if (!compartment.get(i).isEmpty()) {
compartment.get(i).inventoryTick(this.player.level, this.player, i, this.selected == i);
}
}
}
}
@Override
public boolean add(ItemStack itemStack) {
return this.add(-1, itemStack);
}
@Override
public boolean add(int i, ItemStack itemStack) {
if (itemStack.isEmpty()) {
return false;
} else {
try {
if (itemStack.isDamaged()) {
if (i == -1) {
i = this.getFreeSlot();
}
if (i >= 0) {
this.items.set(i, itemStack.copy());
this.items.get(i).setPopTime(5);
itemStack.setCount(0);
return true;
} else if (this.player.getAbilities().instabuild) {
itemStack.setCount(0);
return true;
} else {
return false;
}
} else {
int j;
do {
j = itemStack.getCount();
if (i == -1) {
itemStack.setCount(this.addResource(itemStack));
} else {
itemStack.setCount(this.addResource(i, itemStack));
}
} while(!itemStack.isEmpty() && itemStack.getCount() < j);
if (itemStack.getCount() == j && this.player.getAbilities().instabuild) {
itemStack.setCount(0);
return true;
} else {
return itemStack.getCount() < j;
}
}
} catch (Throwable var6) {
CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory");
CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added");
crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem()));
crashReportCategory.setDetail("Item data", itemStack.getDamageValue());
crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString());
throw new ReportedException(crashReport);
}
}
}
@Override
public void placeItemBackInInventory(ItemStack itemStack) {
this.placeItemBackInInventory(itemStack, true);
}
@Override
public void placeItemBackInInventory(ItemStack itemStack, boolean flag) {
while(true) {
if (!itemStack.isEmpty()) {
int i = this.getSlotWithRemainingSpace(itemStack);
if (i == -1) {
i = this.getFreeSlot();
}
if (i != -1) {
int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount();
if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) {
((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i)));
}
continue;
}
this.player.drop(itemStack, false);
}
return;
}
}
@Override
public ItemStack removeItem(int rawIndex, final int j) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null
|| indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) {
return ItemStack.EMPTY;
}
return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j);
}
@Override
public void removeItem(ItemStack itemStack) {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
if (compartment.get(i) == itemStack) {
compartment.set(i, ItemStack.EMPTY);
break;
}
}
}
}
@Override
public ItemStack removeItemNoUpdate(int rawIndex) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
return ItemStack.EMPTY;
}
ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY);
if (removed.isEmpty()) {
return ItemStack.EMPTY;
}
return removed;
}
@Override
public void setItem(int rawIndex, final ItemStack itemStack) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
this.player.drop(itemStack, true);
return;
}
indexedCompartment.compartment().set(indexedCompartment.index(), itemStack);
}
@Override
public float getDestroySpeed(BlockState blockState) {
return this.items.get(this.selected).getDestroySpeed(blockState);
}
@Override
public ListTag save(ListTag listTag) {
for (int i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)i);
this.items.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
for (int i = 0; i < this.armor.size(); ++i) {
if (!this.armor.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 100));
this.armor.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
for (int i = 0; i < this.offhand.size(); ++i) {
if (!this.offhand.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 150));
this.offhand.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
return listTag;
}
@Override
public void load(ListTag listTag) {
this.items.clear();
this.armor.clear();
this.offhand.clear();
for(int i = 0; i < listTag.size(); ++i) {
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
ItemStack itemstack = ItemStack.of(compoundTag);
if (!itemstack.isEmpty()) {
if (j < this.items.size()) {
this.items.set(j, itemstack);
} else if (j >= 100 && j < this.armor.size() + 100) {
this.armor.set(j - 100, itemstack);
} else if (j >= 150 && j < this.offhand.size() + 150) {
this.offhand.set(j - 150, itemstack);
}
}
}
}
@Override
public int getContainerSize() {
return 45;
}
@Override
public boolean isEmpty() {
return !contains(itemStack -> !itemStack.isEmpty());
}
@Override
public ItemStack getItem(int rawIndex) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
return ItemStack.EMPTY;
}
return indexedCompartment.compartment().get(indexedCompartment.index());
}
@Override
public Component getName() {
return this.player.getName();
}
@Override
public ItemStack getArmor(int index) {
return this.armor.get(index);
}
@Override
public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) {
if (damage > 0.0F) {
damage /= 4.0F;
if (damage < 1.0F) {
damage = 1.0F;
}
for (int index : armorIndices) {
ItemStack itemstack = this.armor.get(index);
if ((!damagesource.isFire() || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) {
itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index)));
}
}
}
}
@Override
public void dropAll() {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
ItemStack itemstack = compartment.get(i);
if (!itemstack.isEmpty()) {
this.player.drop(itemstack, true, false);
compartment.set(i, ItemStack.EMPTY);
}
}
}
}
@Override
public void setChanged() {
super.setChanged();
}
@Override
public int getTimesChanged() {
return super.getTimesChanged();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public boolean contains(ItemStack itemstack) {
return contains(itemStack -> itemStack.isEmpty() && itemStack.sameItem(itemstack));
}
@Override
public boolean contains(Tag<Item> tag) {
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tag));
}
@Override
public void replaceWith(Inventory inventory) {
Function<Integer, ItemStack> getter;
if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) {
getter = specialPlayerInventory::getRawItem;
} else {
getter = inventory::getItem;
}
for(int i = 0; i < this.getContainerSize(); ++i) {
this.setRawItem(i, getter.apply(i));
}
this.selected = inventory.selected;
}
@Override
public void clearContent() {
for (NonNullList<ItemStack> compartment : this.compartments) {
compartment.clear();
}
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
for (ItemStack itemstack : this.items) {
stackedContents.accountSimpleStack(itemstack);
}
}
@Override
public ItemStack removeFromSelected(boolean dropWholeStack) {
ItemStack itemstack = this.getSelected();
return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1);
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~ Copyright (C) 2011-2021 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
@@ -21,7 +21,7 @@
<parent>
<artifactId>openinvparent</artifactId>
<groupId>com.lishid</groupId>
<version>4.1.7</version>
<version>4.1.9</version>
</parent>
<artifactId>openinvplugincore</artifactId>
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,10 +35,12 @@ import com.lishid.openinv.util.ConfigUpdater;
import com.lishid.openinv.util.InternalAccessor;
import com.lishid.openinv.util.LanguageManager;
import com.lishid.openinv.util.Permissions;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Consumer;
@@ -88,7 +90,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
// Check if inventory is stored, and if it is, remove it and eject all viewers
if (OpenInv.this.inventories.containsKey(key)) {
Inventory inv = OpenInv.this.inventories.remove(key).getBukkitInventory();
List<HumanEntity> viewers = inv.getViewers();
List<HumanEntity> viewers = new ArrayList<>(inv.getViewers());
for (HumanEntity entity : viewers.toArray(new HumanEntity[0])) {
entity.closeInventory();
}
@@ -97,7 +99,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
// Check if ender chest is stored, and if it is, remove it and eject all viewers
if (OpenInv.this.enderChests.containsKey(key)) {
Inventory inv = OpenInv.this.enderChests.remove(key).getBukkitInventory();
List<HumanEntity> viewers = inv.getViewers();
List<HumanEntity> viewers = new ArrayList<>(inv.getViewers());
for (HumanEntity entity : viewers.toArray(new HumanEntity[0])) {
entity.closeInventory();
}
@@ -126,29 +128,23 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
}
if (this.inventories.containsKey(key)) {
Iterator<HumanEntity> iterator = this.inventories.get(key).getBukkitInventory().getViewers().iterator();
//noinspection WhileLoopReplaceableByForEach
while (iterator.hasNext()) {
HumanEntity human = iterator.next();
// If player has permission or is in the same world, allow continued access
// Just in case, also allow null worlds.
if (Permissions.CROSSWORLD.hasPermission(human) || human.getWorld().equals(player.getWorld())) {
continue;
}
human.closeInventory();
}
kickCrossWorldViewers(player, this.inventories.get(key));
}
if (this.enderChests.containsKey(key)) {
Iterator<HumanEntity> iterator = this.enderChests.get(key).getBukkitInventory().getViewers().iterator();
//noinspection WhileLoopReplaceableByForEach
while (iterator.hasNext()) {
HumanEntity human = iterator.next();
if (Permissions.CROSSWORLD.hasPermission(human) || human.getWorld().equals(player.getWorld())) {
continue;
}
human.closeInventory();
kickCrossWorldViewers(player, this.enderChests.get(key));
}
}
private void kickCrossWorldViewers(Player player, ISpecialInventory inventory) {
List<HumanEntity> viewers = new ArrayList<>(inventory.getBukkitInventory().getViewers());
for (HumanEntity human : viewers) {
// If player has permission or is in the same world, allow continued access
// Just in case, also allow null worlds.
if (Permissions.CROSSWORLD.hasPermission(human) || Objects.equals(human.getWorld(), player.getWorld())) {
continue;
}
human.closeInventory();
}
}
@@ -326,7 +322,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
return this.languageManager.getValue(key, getLocale(sender), replacements);
}
private @Nullable String getLocale(@NotNull CommandSender sender) {
private @NotNull String getLocale(@NotNull CommandSender sender) {
if (sender instanceof Player) {
return ((Player) sender).getLocale();
} else {
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -83,6 +83,8 @@ public class InternalAccessor {
case "v1_16_R2":
return "https://github.com/lishid/OpenInv/releases/tag/4.1.5";
case "v1_16_R3":
return "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8";
case "v1_17_R1":
default:
return "https://github.com/Jikoo/OpenInv/releases";
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 Jikoo. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
+31
View File
@@ -0,0 +1,31 @@
# Translated into Chinese Simplified by Flandre_tw
messages:
error:
consoleUnsupported: 该命令无法在后台执行。
lootNotGenerated: '&c奖励箱尚未生成 ! 请关闭 &b/silentcontainer&c。'
invalidMaterial: '&c无效的物品 : "%target%"'
invalidNumber: '&c无效的数字 : "%target%"'
invalidPlayer: '&c玩家不存在 !'
permissionOpenSelf: '&c你无法开启自己的物品栏。'
permissionEnderAll: '&c你无法开启其他玩家的末影箱。'
permissionExempt: '&c%target% 的物品栏受到保护。'
permissionCrossWorld: '&c%target% 不在你所在的世界。'
permissionPlayerOnline: '&c你无法开启线上玩家的物品栏。'
permissionPlayerOffline: '&c你无法开启离线玩家的物品栏。'
commandException: '&c发生错误,请查看后台。'
info:
containerBlocked: 你正在开启受阻挡的储物箱。
containerBlockedSilent: 你正在悄悄开启受阻挡的储物箱。
containerSilent: 你正在悄悄开启储物箱。
settingState: '%setting% : %state%'
player:
noMatches: 找不到持有 %target% 的玩家。
matches: '找到持有 %target% 的玩家 : %detail%'
container:
noMatches: 找不到放有 %target% 的储物箱。
matches: '找到放有 %target% 的储物箱 : %detail%'
'true': '开启'
'false': '关闭'
container:
player: '%player% 的物品栏'
enderchest: '%player% 的末影箱'
+31
View File
@@ -0,0 +1,31 @@
# Translated into Chinese Traditional by Flandre_tw
messages:
error:
consoleUnsupported: 該指令無法在控制台執行。
lootNotGenerated: '&c獎勵箱尚未生成 ! 請關閉 &b/silentcontainer&c。'
invalidMaterial: '&c無效的物品 : "%target%"'
invalidNumber: '&c無效的數字 : "%target%"'
invalidPlayer: '&c玩家不存在 !'
permissionOpenSelf: '&c你無法開啟自己的物品欄。'
permissionEnderAll: '&c你無法開啟其他玩家的終界箱。'
permissionExempt: '&c%target% 的物品欄受到保護。'
permissionCrossWorld: '&c%target% 不在你所在的世界。'
permissionPlayerOnline: '&c你無法開啟線上玩家的物品欄。'
permissionPlayerOffline: '&c你無法開啟離線玩家的物品欄。'
commandException: '&c發生錯誤,請查看控制台。'
info:
containerBlocked: 你正在開啟受阻擋的儲物箱。
containerBlockedSilent: 你正在悄悄開啟受阻擋的儲物箱。
containerSilent: 你正在悄悄開啟儲物箱。
settingState: '%setting% : %state%'
player:
noMatches: 找不到持有 %target% 的玩家。
matches: '找到持有 %target% 的玩家 : %detail%'
container:
noMatches: 找不到放有 %target% 的儲物箱。
matches: '找到放有 %target% 的儲物箱 : %detail%'
'true': '開啟'
'false': '關閉'
container:
player: '%player% 的物品欄'
enderchest: '%player% 的終界箱'
+12 -5
View File
@@ -1,5 +1,5 @@
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~ Copyright (C) 2011-2021 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
@@ -21,7 +21,7 @@
<artifactId>openinvparent</artifactId>
<name>OpenInv</name>
<url>http://dev.bukkit.org/bukkit-plugins/openinv/</url>
<version>4.1.7</version>
<version>4.1.9</version>
<packaging>pom</packaging>
@@ -37,7 +37,8 @@
<modules>
<module>api</module>
<module>plugin</module>
<module>internal/v1_16_R3</module>
<module>internal/v1_17_R1</module>
<module>internal/v1_18_R1</module>
<module>assembly</module>
</modules>
</profile>
@@ -80,13 +81,13 @@
<artifactId>openinvapi</artifactId>
<groupId>com.lishid</groupId>
<scope>compile</scope>
<version>4.1.7</version>
<version>4.1.9</version>
</dependency>
<dependency>
<artifactId>openinvplugincore</artifactId>
<groupId>com.lishid</groupId>
<scope>compile</scope>
<version>4.1.7</version>
<version>4.1.9</version>
</dependency>
</dependencies>
</dependencyManagement>
@@ -138,6 +139,12 @@
<groupId>org.apache.maven.plugins</groupId>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>net.md-5</groupId>
<artifactId>specialsource-maven-plugin</artifactId>
<version>1.2.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
+1 -1
View File
@@ -32,7 +32,7 @@ function lookup_email_username() {
# Get a pretty list of supported Minecraft versions
function get_minecraft_versions() {
versions=$(. ./scripts/get_spigot_versions.sh)
readarray -t versions <<< "$(. ./scripts/get_spigot_versions.sh)"
for version in "${versions[@]}"; do
# Append comma if variable is set, then append version
+8 -1
View File
@@ -22,7 +22,7 @@
# Re-calling the script and relying on it to handle caching is way easier than passing around info.
declare -a spigot_versions
# We don't care about concatenation - either it's not null and we return or it's null and we instantiate.
# We don't care about concatenation - either it's not null and we handle entries or it's null and we instantiate.
# shellcheck disable=SC2199
if [[ ${spigot_versions[@]} ]]; then
for spigot_version in "${spigot_versions[@]}"; do
@@ -31,6 +31,10 @@ if [[ ${spigot_versions[@]} ]]; then
return
fi
old_maven_opts=$MAVEN_OPTS
# Add JVM parameters to allow help plugin access to packages it needs.
export MAVEN_OPTS="$old_maven_opts --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED"
# Pull Spigot dependency information from Maven.
# Since we only care about Spigot versions, only check modules in the folder internal.
readarray -t modules <<< "$(mvn help:evaluate -Dexpression=project.modules -q -DforceStdout -P all | grep -oP '(?<=<string>)(internal/.*)(?=</string>)')"
@@ -55,3 +59,6 @@ for module in "${modules[@]}"; do
fi
done
done
# Reset JVM parameters
export MAVEN_OPTS=$old_maven_opts
+9 -4
View File
@@ -30,18 +30,23 @@ get_buildtools () {
wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O $buildtools
}
versions=$(. ./scripts/get_spigot_versions.sh)
echo Found Spigot dependencies: "$versions"
readarray -t versions <<< "$(. ./scripts/get_spigot_versions.sh)"
echo Found Spigot dependencies: "${versions[@]}"
# Install dependencies aside from Spigot prior to running in offline mode.
# Note that the default SuperPOM declares maven-dependency-plugin 2.8.0.
# Unfortunately, we run into MDEP-204 and require a version >= 3.1.2.
mvn org.apache.maven.plugins:maven-dependency-plugin:3.2.0:go-offline -DexcludeArtifactIds=spigot
for version in "${versions[@]}"; do
set -e
exit_code=0
mvn dependency:get -Dartifact=org.spigotmc:spigot:"$version" -q -o || exit_code=$?
mvn dependency:get -Dartifact=org.spigotmc:spigot:"$version":remapped-mojang -q -o || exit_code=$?
if [ $exit_code -ne 0 ]; then
echo Installing missing Spigot version "$version"
revision=${version%%-R*}
get_buildtools
java -jar $buildtools -rev "$revision"
java -jar $buildtools -rev "$revision" --remapped
else
echo Spigot "$version" is already installed
fi
+14 -1
View File
@@ -20,7 +20,7 @@
# Parse Spigot dependency information into major Minecraft versions
function get_curseforge_minecraft_versions() {
versions=$(. ./scripts/get_spigot_versions.sh)
readarray -t versions <<< "$(. ./scripts/get_spigot_versions.sh)"
for version in "${versions[@]}"; do
# Parse Minecraft major version
@@ -38,5 +38,18 @@ function get_curseforge_minecraft_versions() {
echo "${minecraft_versions}"
}
# Modify provided changelog to not break when inserted into yaml file.
function get_yaml_safe_changelog() {
changelog=$1
# Since we're using a flow scalar, newlines need to be doubled.
echo "${changelog//
/
}"
}
minecraft_versions=$(get_curseforge_minecraft_versions)
echo "CURSEFORGE_MINECRAFT_VERSIONS=$minecraft_versions" >> "$GITHUB_ENV"
changelog=$(get_yaml_safe_changelog "$1")
printf "CURSEFORGE_CHANGELOG<<EOF\n%s\nEOF\n" "$changelog" >> "$GITHUB_ENV"