Compare commits

...

41 commits

Author SHA1 Message Date
Telesphoreo 1609476188 Merge https://github.com/lishid/OpenInv 2021-09-29 15:13:28 -05:00
Telesphoreo f329da29a4 Merge https://github.com/AtlasMediaGroup/OpenInv 2021-09-29 15:11:48 -05:00
Jikoo 1c579564bc
Add reading assistant 2021-07-12 21:07:45 -04:00
Adam eae4021138
Add relocation notice 2021-04-11 18:29:37 -04:00
Nathan Curran be534df766
Merge branch 'master' of https://github.com/lishid/OpenInv 2021-03-21 21:50:18 +11:00
Jikoo 6563b4f6ce
Migrate API-only functions out of PlayerDataManager
With the update to 1.16 there's no need to maintain multiple copies of the same code. Additionally, in 1.16 the action bar now supports JSON text.
2021-03-18 20:31:45 -04:00
Jikoo 1d5a836fd0
Partially automate release changelog 2021-03-18 16:38:41 -04:00
Jikoo 3ba82683ec
Fix double chest custom names not displaying
Closes #175
2021-03-17 00:09:10 -04:00
Jikoo 6e5c332272
Extract player implementation 2021-03-16 23:49:17 -04:00
Jikoo 1c9d133ed1
Fix item delete in own inventory
Dragging items across top and bottom inventories with own inventory open resulted in the overlapping content being deleted.
2021-03-16 23:49:10 -04:00
Jikoo dad1e16c18
Fix item dupe in own inventory
Reduce desync for other actions in own inventory

Closes #182
2021-03-16 12:14:47 -04:00
Jikoo 519dd7da33
Fix cache breaking build on Spigot version change 2021-03-16 09:45:29 -04:00
Adam 1bd7932cc5
Drop 1.8/1.15/1.16.3 support (#185)
With the recent uptick in requests for support by people running 1.12 who have recently updated past Java 9, I revisited the supported versions.

First, the "modern" versions: 1.15 is run by 2.9% of servers, 1.16.3 by 5%. Since all of these versions are supported to ease the transition of updating servers and servers have updated, there's no real reason to keep them around. It's a lot easier if you can update a plugin and just have it work on both versions rather than push all the plugin updates to master with the server update, but none of these versions have any reason for long-term support.

Regarding heavily outdated server software: As of the time of writing, 1.8 and 1.12 have market shares of 8.6% and 8.5% of servers respectively.
Regarding 1.8: 1.8 support is already a bit wonky - with the changes made to inventory names, it's not (easily) possible to bridge the gap, and future changes will make that more and more difficult. People use 1.8 because they disliked the 1.9 combat changes, but there are plugins that fully rework combat to how it used to be. I have yet to hear a compelling argument that cannot be resolved with plugins. In the interest of my own sanity (handling and backporting the inventory name change in particular was a real humdinger) I will no longer be backporting changes to 1.8.
Regarding 1.12: I am honestly not sure why people are not updating. I get it, 1.13 and the flattening was a rough transition, but pretty much every developer active at the time bridged the gap. If your server is heavily dependent on some software only available at that time, you can live with other software available at that time. At the time of 1.12's release, Java 9 was not released. Either update your server, downgrade Java, or fork OpenInv and backport Java 9 support yourself.
2021-03-16 09:34:41 -04:00
Telesphoreo c8d16a590f
Merge https://github.com/lishid/OpenInv 2020-12-19 14:15:48 -06:00
speed 352dd1bbc2
Merge lishid/master 2020-12-04 13:40:38 -05:00
Adam dad00d2d9c
build: Add action to build and automatically release files (#170)
Auto-release is gonna get a nice live test at a later date.
2020-11-26 11:05:45 -05:00
Jikoo 502f661be3
fix: Update 1.15 adapter to 1.15.2 2020-11-26 10:47:51 -05:00
Jikoo 804d30bcc7
tpyo 2020-11-15 14:33:28 -05:00
Jikoo db8b67082b
Fix up .gitignore a bit 2020-11-15 13:25:50 -05:00
Jikoo b2156edcc7
Fix mistake in InventoryAccess grabbing ender chest 2020-11-15 12:59:59 -05:00
Jikoo 45de495699
Simplify command executor assignment a little more 2020-11-15 12:26:36 -05:00
Jikoo f45d332727
Clarify download locations for older versions
Hopefully this will help people figure out where they've gone wrong. Maybe I should be stating supported versions in release notes too.
2020-11-15 12:22:25 -05:00
Telesphoreo 41c085c4d7 br 2020-11-08 19:14:08 -06:00
Telesphoreo 3f9f382af1 Merge https://github.com/lishid/OpenInv 2020-11-08 19:10:05 -06:00
Jikoo 9705cb156b
Bump version to 4.1.6-SNAPSHOT for development 2020-11-03 18:32:43 -05:00
Jikoo 1c45ef517f
Bump version to 4.1.5 for release 2020-11-03 18:32:11 -05:00
Jikoo 3fee84fc58
Add support for 1.16.4, drop 1.16.1 2020-11-03 18:19:34 -05:00
Telesphoreo 41693b5db0 thank you video, very cool! 2020-09-28 09:15:07 -05:00
Jikoo 00113cc4d4 These are my favorite version bumps 2020-09-15 13:47:12 -04:00
Jikoo 788f022e74 Mitigate some permissions confusion
For some reason, certain permissions plugins seem to not respect our declared default false nodes. To alleviate user confusion, these nodes will not be allowed to be used via wildcard internally.
2020-09-15 13:44:50 -04:00
Jikoo da55790fd2 Allow spectators to edit inventories
Requires permission OpenInv.spectate
Closes #155
2020-09-15 13:35:04 -04:00
Jikoo 1a6d513603 Reduce technical debt
* Reduce duplicate code
* Use more specific functional interfaces where available
* Fix some potential NPEs
* Remove some unnecessary/nonfunctional code
* Merge inventory listeners - no longer need to keep separate due to event availability
* Removed TODO items that probably won't ever be implemented. Good ideas, too drastic changes or too much work to maintain.
2020-09-15 12:51:49 -04:00
Jikoo a5b02ab26a Bump version to 4.1.5-SNAPSHOT for development 2020-08-22 12:22:00 -04:00
Jikoo 0c3ce2dfb3 Bump version to 4.1.4 for release 2020-08-22 12:21:22 -04:00
Jikoo a1f4649a09 Reword invalid version message, include releases link 2020-08-22 12:00:08 -04:00
Jikoo 100d0e12cd Update to 1.16.2 2020-08-12 00:06:20 -04:00
Jikoo 3e629798e5 Bump version to 4.1.4-SNAPSHOT for development 2020-07-15 23:23:31 -04:00
Jikoo 7c1f15974c Bump version to 4.1.3 for release 2020-07-15 23:23:03 -04:00
mfnalex d992237766
Add support for 3rd party plugins firing PlayerInteractEvent (#146)
* Add support for InvUnload

* generalize 3rd party plugins' event check
2020-07-15 22:38:38 -04:00
Jikoo c93464e643 Fix double chest loot generation with AnyContainer (#142) 2020-07-08 20:44:15 -04:00
Jikoo 56afefc82b Bump version to 4.1.3-SNAPSHOT for development 2020-06-25 19:40:07 -04:00
46 changed files with 1115 additions and 3353 deletions

13
.github/workflows/close-issue.yml vendored Normal file
View file

@ -0,0 +1,13 @@
name: Close Issue
on:
issues:
types: [opened]
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: peter-evans/close-issue@v1.0.3
with:
comment: "Per the notice in the readme, OpenInv is now being maintained at [Jikoo/OpenInv](https://github.com/Jikoo/OpenInv)."

13
.github/workflows/close-pr.yml vendored Normal file
View file

@ -0,0 +1,13 @@
name: Close Pull Request
on:
pull_request_target:
types: [opened]
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: superbrothers/close-pull-request@v3.1.0
with:
comment: "Per the notice in the readme, OpenInv is now being maintained at [Jikoo/OpenInv](https://github.com/Jikoo/OpenInv)."

13
.gitignore vendored
View file

@ -1,11 +1,10 @@
**/.settings
**/.gitignore
**/.project
**/.classpath
**/.idea
**/.idea/
**.iml
**/target
**/bin
**/out
**dependency-reduced-pom.xml
**pom.xml.versionsBackup
**/target/
**/bin/
**/out/
**/dependency-reduced-pom.xml
**/pom.xml.versionsBackup

View file

@ -1,3 +1,6 @@
## Notice
I am now maintaining OpenInv over at [Jikoo/OpenInv](https://github.com/Jikoo/OpenInv) because I needed higher levels of access to add secrets to the repository for Github Actions. It feels ruder to send an email saying what amounts to "pls op" than to redirect people to my fork, though neither option is polite. Existing issues will remain open here until resolved (you can't transfer issues across organizations/users), but any new issues in this repo will be closed without resolution.
## About
OpenInv is a [Bukkit plugin](https://dev.bukkit.org/bukkit-plugins/openinv/) which allows users to open and edit anyone's inventory or ender chest - online or not!
@ -136,6 +139,10 @@ OpenInv is a [Bukkit plugin](https://dev.bukkit.org/bukkit-plugins/openinv/) whi
<td>OpenInv.silent.default</td>
<td>Cause SilentContainer to be enabled by default.</td>
</tr>
<tr>
<td>OpenInv.spectate</td>
<td>Allows users in spectate gamemode to edit inventories.</td>
</tr>
</table>
## For Developers

View file

@ -21,7 +21,7 @@
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId>
<version>4.1.2</version>
<version>4.1.5-TF</version>
</parent>
<artifactId>openinvapi</artifactId>
@ -36,7 +36,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.8.8-R0.1-SNAPSHOT</version>
<version>1.16.5-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>

View file

@ -18,6 +18,7 @@ package com.lishid.openinv.util;
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;
@ -33,76 +34,63 @@ public class InventoryAccess implements IInventoryAccess {
static {
String packageName = Bukkit.getServer().getClass().getPackage().getName();
String version = packageName.substring(packageName.lastIndexOf('.') + 1);
try {
craftInventory = Class.forName("org.bukkit.craftbukkit." + version + ".inventory.CraftInventory");
craftInventory = Class.forName(packageName + ".inventory.CraftInventory");
} catch (ClassNotFoundException ignored) {}
try {
getInventory = craftInventory.getDeclaredMethod("getInventory");
} catch (NoSuchMethodException ignored) {}
}
/**
* @deprecated use {@link #isUsable()}
*/
@Deprecated
public static boolean isUseable() {
return isUsable();
}
public static boolean isUsable() {
return craftInventory != null && getInventory != null;
}
public static boolean isPlayerInventory(@NotNull Inventory inventory) {
if (craftInventory.isAssignableFrom(inventory.getClass())) {
try {
return getInventory.invoke(inventory) instanceof ISpecialPlayerInventory;
} catch (ReflectiveOperationException ignored) {}
}
return grabFieldOfTypeFromObject(ISpecialPlayerInventory.class, inventory) != null;
return getPlayerInventory(inventory) != null;
}
public static ISpecialPlayerInventory getPlayerInventory(@NotNull Inventory inventory) {
Object inv = null;
if (craftInventory.isAssignableFrom(inventory.getClass())) {
try {
inv = getInventory.invoke(inventory);
} catch (ReflectiveOperationException ignored) {}
}
if (inv == null) {
inv = grabFieldOfTypeFromObject(ISpecialPlayerInventory.class, inventory);
}
if (inv instanceof ISpecialPlayerInventory) {
return (ISpecialPlayerInventory) inv;
}
return null;
public static @Nullable ISpecialPlayerInventory getPlayerInventory(@NotNull Inventory inventory) {
return getSpecialInventory(ISpecialPlayerInventory.class, inventory);
}
public static boolean isEnderChest(@NotNull Inventory inventory) {
if (craftInventory.isAssignableFrom(inventory.getClass())) {
try {
return getInventory.invoke(inventory) instanceof ISpecialEnderChest;
} catch (ReflectiveOperationException ignored) {}
}
return grabFieldOfTypeFromObject(ISpecialEnderChest.class, inventory) != null;
return getEnderChest(inventory) != null;
}
public static ISpecialEnderChest getEnderChest(@NotNull Inventory inventory) {
Object inv = null;
if (craftInventory.isAssignableFrom(inventory.getClass())) {
public static @Nullable ISpecialEnderChest getEnderChest(@NotNull Inventory inventory) {
return getSpecialInventory(ISpecialEnderChest.class, inventory);
}
private static <T extends ISpecialInventory> @Nullable T getSpecialInventory(@NotNull Class<T> expected, @NotNull Inventory inventory) {
Object inv;
if (craftInventory != null && getInventory != null && craftInventory.isAssignableFrom(inventory.getClass())) {
try {
inv = getInventory.invoke(inventory);
if (expected.isInstance(inv)) {
return expected.cast(inv);
}
} catch (ReflectiveOperationException ignored) {}
}
if (inv == null) {
inv = grabFieldOfTypeFromObject(ISpecialEnderChest.class, inventory);
}
inv = grabFieldOfTypeFromObject(expected, inventory);
if (inv instanceof ISpecialEnderChest) {
return (ISpecialEnderChest) inv;
if (expected.isInstance(inv)) {
return expected.cast(inv);
}
return null;
}
private static <T> T grabFieldOfTypeFromObject(final Class<T> type, final Object object) {
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;
@ -142,4 +130,5 @@ public class InventoryAccess implements IInventoryAccess {
public boolean isSpecialPlayerInventory(@NotNull Inventory inventory) {
return isPlayerInventory(inventory);
}
}

View file

@ -21,7 +21,7 @@
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId>
<version>4.1.2</version>
<version>4.1.5-TF</version>
</parent>
<artifactId>openinvassembly</artifactId>

View file

@ -20,7 +20,7 @@
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId>
<version>4.1.2</version>
<version>4.1.5-TF</version>
</parent>
<artifactId>openinvinternal</artifactId>
@ -33,9 +33,7 @@
<profile>
<id>all</id>
<modules>
<module>v1_8_R3</module>
<module>v1_15_R1</module>
<module>v1_16_R1</module>
<module>v1_16_R3</module>
</modules>
</profile>

View file

@ -1,360 +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_15_R1;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IAnySilentContainer;
import java.lang.reflect.Field;
import net.minecraft.server.v1_15_R1.Block;
import net.minecraft.server.v1_15_R1.BlockBarrel;
import net.minecraft.server.v1_15_R1.BlockChest;
import net.minecraft.server.v1_15_R1.BlockChestTrapped;
import net.minecraft.server.v1_15_R1.BlockEnderChest;
import net.minecraft.server.v1_15_R1.BlockPosition;
import net.minecraft.server.v1_15_R1.BlockPropertyChestType;
import net.minecraft.server.v1_15_R1.BlockShulkerBox;
import net.minecraft.server.v1_15_R1.ChatMessage;
import net.minecraft.server.v1_15_R1.Container;
import net.minecraft.server.v1_15_R1.ContainerChest;
import net.minecraft.server.v1_15_R1.Containers;
import net.minecraft.server.v1_15_R1.EntityHuman;
import net.minecraft.server.v1_15_R1.EntityPlayer;
import net.minecraft.server.v1_15_R1.EnumChatFormat;
import net.minecraft.server.v1_15_R1.EnumGamemode;
import net.minecraft.server.v1_15_R1.IBlockData;
import net.minecraft.server.v1_15_R1.IChatBaseComponent;
import net.minecraft.server.v1_15_R1.ITileInventory;
import net.minecraft.server.v1_15_R1.InventoryEnderChest;
import net.minecraft.server.v1_15_R1.InventoryLargeChest;
import net.minecraft.server.v1_15_R1.PlayerInteractManager;
import net.minecraft.server.v1_15_R1.PlayerInventory;
import net.minecraft.server.v1_15_R1.TileEntity;
import net.minecraft.server.v1_15_R1.TileEntityChest;
import net.minecraft.server.v1_15_R1.TileEntityEnderChest;
import net.minecraft.server.v1_15_R1.TileEntityLootable;
import net.minecraft.server.v1_15_R1.TileInventory;
import net.minecraft.server.v1_15_R1.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;
int rows = enderChest.getSize() / 9;
switch (rows) {
case 1:
containers = Containers.GENERIC_9X1;
break;
case 2:
containers = Containers.GENERIC_9X2;
break;
case 3:
default:
containers = Containers.GENERIC_9X3;
break;
case 4:
containers = Containers.GENERIC_9X4;
break;
case 5:
containers = Containers.GENERIC_9X5;
break;
case 6:
containers = Containers.GENERIC_9X6;
break;
}
return new ContainerChest(containers, containerCounter, playerInventory, enderChest, rows);
}, BlockEnderChest.e));
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.i(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 (rightChest.lootTable != null || leftChest.lootTable != null) {
player.a(new ChatMessage("Loot not generated! Please disable /silentcontainer.").a(EnumChatFormat.RED), true);
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() {
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();
}
}
}

View file

@ -1,289 +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_15_R1;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.mojang.authlib.GameProfile;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import net.minecraft.server.v1_15_R1.ChatComponentText;
import net.minecraft.server.v1_15_R1.ChatMessageType;
import net.minecraft.server.v1_15_R1.Container;
import net.minecraft.server.v1_15_R1.Containers;
import net.minecraft.server.v1_15_R1.DimensionManager;
import net.minecraft.server.v1_15_R1.Entity;
import net.minecraft.server.v1_15_R1.EntityHuman;
import net.minecraft.server.v1_15_R1.EntityPlayer;
import net.minecraft.server.v1_15_R1.MinecraftServer;
import net.minecraft.server.v1_15_R1.NBTCompressedStreamTools;
import net.minecraft.server.v1_15_R1.NBTTagCompound;
import net.minecraft.server.v1_15_R1.PacketPlayOutChat;
import net.minecraft.server.v1_15_R1.PacketPlayOutOpenWindow;
import net.minecraft.server.v1_15_R1.PlayerInteractManager;
import net.minecraft.server.v1_15_R1.PlayerInventory;
import net.minecraft.server.v1_15_R1.WorldNBTStorage;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_15_R1.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftContainer;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PlayerDataManager implements IPlayerDataManager {
private Field bukkitEntity;
public PlayerDataManager() {
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();
bukkitEntity = null;
}
}
@NotNull
public static EntityPlayer getHandle(final Player player) {
if (player instanceof CraftPlayer) {
return ((CraftPlayer) player).getHandle();
}
Server server = player.getServer();
EntityPlayer nmsPlayer = null;
if (server instanceof CraftServer) {
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getName());
}
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;
}
@Nullable
@Override
public 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();
EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(DimensionManager.OVERWORLD), profile,
new PlayerInteractManager(server.getWorldServer(DimensionManager.OVERWORLD)));
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(EntityPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new CraftPlayer(player.server.server, player) {
@Override
public void saveData() {
super.saveData();
// See net.minecraft.server.WorldNBTStorage#save(EntityPlayer)
try {
WorldNBTStorage worldNBTStorage = (WorldNBTStorage) player.server.getPlayerList().playerFileData;
NBTTagCompound playerData = player.save(new NBTTagCompound());
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);
} catch (Exception e) {
LogManager.getLogger().warn("Failed to save player data for {}", player.getDisplayName().getString());
}
}
});
}
@NotNull
@Override
public Player inject(@NotNull Player player) {
try {
EntityPlayer 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) {
EntityPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.playerConnection == null) {
return null;
}
String title;
if (inventory instanceof SpecialEnderChest) {
HumanEntity owner = (HumanEntity) ((SpecialEnderChest) inventory).getBukkitOwner();
title = OpenInv.getPlugin(OpenInv.class).getLocalizedMessage(player, "container.enderchest", "%player%", owner.getName());
if (title == null) {
title = owner.getName() + "'s Ender Chest";
}
} else if (inventory instanceof SpecialPlayerInventory) {
EntityHuman owner = ((PlayerInventory) inventory).player;
title = OpenInv.getPlugin(OpenInv.class).getLocalizedMessage(player, "container.player", "%player%", owner.getName());
if (title == null) {
title = owner.getName() + "'s Inventory";
}
} else {
return player.openInventory(inventory.getBukkitInventory());
}
String finalTitle = title;
Container container = new CraftContainer(new InventoryView() {
@Override
public @NotNull Inventory getTopInventory() {
return inventory.getBukkitInventory();
}
@Override
public @NotNull Inventory getBottomInventory() {
return player.getInventory();
}
@Override
public @NotNull HumanEntity getPlayer() {
return player;
}
@Override
public @NotNull InventoryType getType() {
return inventory.getBukkitInventory().getType();
}
@Override
public @NotNull String getTitle() {
return finalTitle;
}
}, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public Containers<?> getType() {
switch (inventory.getBukkitInventory().getSize()) {
case 9:
return Containers.GENERIC_9X1;
case 18:
return Containers.GENERIC_9X2;
case 27:
default:
return Containers.GENERIC_9X3;
case 36:
return Containers.GENERIC_9X4;
case 41: // PLAYER
case 45:
return Containers.GENERIC_9X5;
case 54:
return Containers.GENERIC_9X6;
}
}
};
container.setTitle(new ChatComponentText(title));
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);
return container.getBukkitView();
}
@Override
public void sendSystemMessage(@NotNull Player player, @NotNull String message) {
int newline = message.indexOf('\n');
if (newline != -1) {
// No newlines in action bar chat.
message = message.substring(0, newline);
}
if (message.isEmpty()) {
return;
}
EntityPlayer nmsPlayer = getHandle(player);
// For action bar chat, color codes are still supported but JSON text color is not allowed. Do not convert text.
if (nmsPlayer.playerConnection != null) {
nmsPlayer.playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(message), ChatMessageType.GAME_INFO));
}
}
}

View file

@ -1,255 +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_15_R1;
import com.lishid.openinv.internal.ISpecialEnderChest;
import java.util.List;
import net.minecraft.server.v1_15_R1.AutoRecipeStackManager;
import net.minecraft.server.v1_15_R1.ContainerUtil;
import net.minecraft.server.v1_15_R1.EntityHuman;
import net.minecraft.server.v1_15_R1.EntityPlayer;
import net.minecraft.server.v1_15_R1.IInventoryListener;
import net.minecraft.server.v1_15_R1.InventoryEnderChest;
import net.minecraft.server.v1_15_R1.ItemStack;
import net.minecraft.server.v1_15_R1.NonNullList;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
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 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 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.a;
}
@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.a;
}
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.a;
}
}
}
}
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.a;
} else {
this.items.set(i, ItemStack.a);
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 isNotEmpty() {
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);
}
}
}

View file

@ -1,759 +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_15_R1;
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_15_R1.AutoRecipeStackManager;
import net.minecraft.server.v1_15_R1.ChatMessage;
import net.minecraft.server.v1_15_R1.ContainerUtil;
import net.minecraft.server.v1_15_R1.CrashReport;
import net.minecraft.server.v1_15_R1.CrashReportSystemDetails;
import net.minecraft.server.v1_15_R1.EntityHuman;
import net.minecraft.server.v1_15_R1.EntityPlayer;
import net.minecraft.server.v1_15_R1.EnumItemSlot;
import net.minecraft.server.v1_15_R1.IBlockData;
import net.minecraft.server.v1_15_R1.IChatBaseComponent;
import net.minecraft.server.v1_15_R1.Item;
import net.minecraft.server.v1_15_R1.ItemArmor;
import net.minecraft.server.v1_15_R1.ItemStack;
import net.minecraft.server.v1_15_R1.NBTTagCompound;
import net.minecraft.server.v1_15_R1.NBTTagList;
import net.minecraft.server.v1_15_R1.NonNullList;
import net.minecraft.server.v1_15_R1.PacketPlayOutSetSlot;
import net.minecraft.server.v1_15_R1.PlayerInventory;
import net.minecraft.server.v1_15_R1.ReportedException;
import net.minecraft.server.v1_15_R1.World;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_15_R1.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.a;
}
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.a : 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 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.a;
}
return list.get(i).isEmpty() ? ItemStack.a : 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.a;
}
if (!list.get(i).isEmpty()) {
ItemStack itemstack = list.get(i);
list.set(i, ItemStack.a);
return itemstack;
}
return ItemStack.a;
}
@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.a;
}
private boolean a(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.a(itemstack, itemstack1)) {
remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount();
}
if (remains <= 0) {
return itemstack.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) {
int j = 0;
int k;
for (k = 0; k < this.getSize(); ++k) {
ItemStack itemstack = this.getItem(k);
if (!itemstack.isEmpty() && predicate.test(itemstack)) {
int l = i <= 0 ? itemstack.getCount() : Math.min(i - j, itemstack.getCount());
j += l;
if (i != 0) {
itemstack.subtract(l);
if (itemstack.isEmpty()) {
this.setItem(k, ItemStack.a);
}
if (i > 0 && j >= i) {
return j;
}
}
}
}
if (!this.getCarried().isEmpty() && predicate.test(this.getCarried())) {
k = i <= 0 ? this.getCarried().getCount() : Math.min(i - j, this.getCarried().getCount());
j += k;
if (i != 0) {
this.getCarried().subtract(k);
if (this.getCarried().isEmpty()) {
this.setCarried(ItemStack.a);
}
if (i > 0 && j >= i) {
return j;
}
}
}
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);
if (itemstack.hasTag()) {
itemstack1.setTag(itemstack.getTag().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) {
return j;
} else {
j -= k;
itemstack1.add(k);
itemstack1.d(5);
return j;
}
}
@Override
public int firstPartial(ItemStack itemstack) {
if (this.a(this.getItem(this.itemInHandIndex), itemstack)) {
return this.itemInHandIndex;
} else if (this.a(this.getItem(40), itemstack)) {
return 40;
} else {
for (int i = 0; i < this.items.size(); ++i) {
if (this.a(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.a);
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 isNotEmpty() {
Iterator iterator = this.items.iterator();
ItemStack itemstack;
while (iterator.hasNext()) {
itemstack = (ItemStack)iterator.next();
if (!itemstack.isEmpty()) {
return false;
}
}
iterator = this.armor.iterator();
while (iterator.hasNext()) {
itemstack = (ItemStack)iterator.next();
if (!itemstack.isEmpty()) {
return false;
}
}
iterator = this.extraSlots.iterator();
while (iterator.hasNext()) {
itemstack = (ItemStack)iterator.next();
if (!itemstack.isEmpty()) {
return false;
}
}
return true;
}
@Nullable
@Override
public IChatBaseComponent getCustomName() {
return null;
}
@Override
public boolean b(IBlockData iblockdata) {
return this.getItem(this.itemInHandIndex).canDestroySpecialBlock(iblockdata);
}
@Override
public void a(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 (itemstack.getItem() instanceof ItemArmor) {
itemstack.damage((int) f, this.player, (entityhuman) -> entityhuman.c(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.a);
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);
}
}
}

View file

@ -1,75 +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>
<groupId>com.lishid</groupId>
<artifactId>openinvinternal</artifactId>
<version>4.1.2</version>
</parent>
<artifactId>openinvadapter1_16_R1</artifactId>
<name>OpenInvAdapter1_16_R1</name>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.16.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.lishid</groupId>
<artifactId>openinvplugincore</artifactId>
<version>4.1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<minimizeJar>true</minimizeJar>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,296 +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_R1;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.mojang.authlib.GameProfile;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import net.minecraft.server.v1_16_R1.ChatComponentText;
import net.minecraft.server.v1_16_R1.ChatMessageType;
import net.minecraft.server.v1_16_R1.Container;
import net.minecraft.server.v1_16_R1.Containers;
import net.minecraft.server.v1_16_R1.Entity;
import net.minecraft.server.v1_16_R1.EntityHuman;
import net.minecraft.server.v1_16_R1.EntityPlayer;
import net.minecraft.server.v1_16_R1.MinecraftServer;
import net.minecraft.server.v1_16_R1.NBTCompressedStreamTools;
import net.minecraft.server.v1_16_R1.NBTTagCompound;
import net.minecraft.server.v1_16_R1.PacketPlayOutChat;
import net.minecraft.server.v1_16_R1.PacketPlayOutOpenWindow;
import net.minecraft.server.v1_16_R1.PlayerInteractManager;
import net.minecraft.server.v1_16_R1.PlayerInventory;
import net.minecraft.server.v1_16_R1.SystemUtils;
import net.minecraft.server.v1_16_R1.World;
import net.minecraft.server.v1_16_R1.WorldNBTStorage;
import net.minecraft.server.v1_16_R1.WorldServer;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_16_R1.CraftServer;
import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_16_R1.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftContainer;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PlayerDataManager implements IPlayerDataManager {
private @Nullable Field bukkitEntity;
public PlayerDataManager() {
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();
bukkitEntity = null;
}
}
@NotNull
public static EntityPlayer getHandle(final Player player) {
if (player instanceof CraftPlayer) {
return ((CraftPlayer) player).getHandle();
}
Server server = player.getServer();
EntityPlayer nmsPlayer = null;
if (server instanceof CraftServer) {
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getName());
}
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;
}
@Nullable
@Override
public 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();
WorldServer worldServer = server.getWorldServer(World.OVERWORLD);
if (worldServer == null) {
return null;
}
EntityPlayer entity = new EntityPlayer(server, worldServer, profile, new PlayerInteractManager(worldServer));
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(EntityPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new CraftPlayer(player.server.server, player) {
@Override
public void saveData() {
super.saveData();
// See net.minecraft.server.WorldNBTStorage#save(EntityPlayer)
try {
WorldNBTStorage worldNBTStorage = player.server.getPlayerList().playerFileData;
NBTTagCompound playerData = player.save(new NBTTagCompound());
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);
} catch (Exception e) {
LogManager.getLogger().warn("Failed to save player data for {}", player.getDisplayName().getString());
}
}
});
}
@NotNull
@Override
public Player inject(@NotNull Player player) {
try {
EntityPlayer 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) {
EntityPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.playerConnection == null) {
return null;
}
String title;
if (inventory instanceof SpecialEnderChest) {
HumanEntity owner = (HumanEntity) ((SpecialEnderChest) inventory).getBukkitOwner();
title = OpenInv.getPlugin(OpenInv.class).getLocalizedMessage(player, "container.enderchest", "%player%", owner.getName());
if (title == null) {
title = owner.getName() + "'s Ender Chest";
}
} else if (inventory instanceof SpecialPlayerInventory) {
EntityHuman owner = ((PlayerInventory) inventory).player;
title = OpenInv.getPlugin(OpenInv.class).getLocalizedMessage(player, "container.player", "%player%", owner.getName());
if (title == null) {
title = owner.getName() + "'s Inventory";
}
} else {
return player.openInventory(inventory.getBukkitInventory());
}
String finalTitle = title;
Container container = new CraftContainer(new InventoryView() {
@Override
public @NotNull Inventory getTopInventory() {
return inventory.getBukkitInventory();
}
@Override
public @NotNull Inventory getBottomInventory() {
return player.getInventory();
}
@Override
public @NotNull HumanEntity getPlayer() {
return player;
}
@Override
public @NotNull InventoryType getType() {
return inventory.getBukkitInventory().getType();
}
@Override
public @NotNull String getTitle() {
return finalTitle;
}
}, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public Containers<?> getType() {
switch (inventory.getBukkitInventory().getSize()) {
case 9:
return Containers.GENERIC_9X1;
case 18:
return Containers.GENERIC_9X2;
case 27:
default:
return Containers.GENERIC_9X3;
case 36:
return Containers.GENERIC_9X4;
case 41: // PLAYER
case 45:
return Containers.GENERIC_9X5;
case 54:
return Containers.GENERIC_9X6;
}
}
};
container.setTitle(new ChatComponentText(title));
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);
return container.getBukkitView();
}
@Override
public void sendSystemMessage(@NotNull Player player, @NotNull String message) {
int newline = message.indexOf('\n');
if (newline != -1) {
// No newlines in action bar chat.
message = message.substring(0, newline);
}
if (message.isEmpty()) {
return;
}
EntityPlayer nmsPlayer = getHandle(player);
// For action bar chat, color codes are still supported but JSON text color is not allowed. Do not convert text.
if (nmsPlayer.playerConnection != null) {
nmsPlayer.playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(message), ChatMessageType.GAME_INFO, SystemUtils.b));
}
}
}

View file

@ -22,23 +22,23 @@
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvinternal</artifactId>
<version>4.1.2</version>
<version>4.1.5-TF</version>
</parent>
<artifactId>openinvadapter1_15_R1</artifactId>
<name>OpenInvAdapter1_15_R1</name>
<artifactId>openinvadapter1_16_R3</artifactId>
<name>OpenInvAdapter1_16_R3</name>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.15.1-R0.1-SNAPSHOT</version>
<version>1.16.5-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.lishid</groupId>
<artifactId>openinvplugincore</artifactId>
<version>4.1.2</version>
<version>4.1.5-TF</version>
</dependency>
</dependencies>
@ -72,4 +72,4 @@
</plugins>
</build>
</project>
</project>

View file

@ -14,40 +14,38 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_16_R1;
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_R1.Block;
import net.minecraft.server.v1_16_R1.BlockBarrel;
import net.minecraft.server.v1_16_R1.BlockChest;
import net.minecraft.server.v1_16_R1.BlockChestTrapped;
import net.minecraft.server.v1_16_R1.BlockEnderChest;
import net.minecraft.server.v1_16_R1.BlockPosition;
import net.minecraft.server.v1_16_R1.BlockPropertyChestType;
import net.minecraft.server.v1_16_R1.BlockShulkerBox;
import net.minecraft.server.v1_16_R1.ChatMessage;
import net.minecraft.server.v1_16_R1.Container;
import net.minecraft.server.v1_16_R1.ContainerChest;
import net.minecraft.server.v1_16_R1.Containers;
import net.minecraft.server.v1_16_R1.EntityHuman;
import net.minecraft.server.v1_16_R1.EntityPlayer;
import net.minecraft.server.v1_16_R1.EnumChatFormat;
import net.minecraft.server.v1_16_R1.EnumGamemode;
import net.minecraft.server.v1_16_R1.IBlockData;
import net.minecraft.server.v1_16_R1.IChatBaseComponent;
import net.minecraft.server.v1_16_R1.ITileInventory;
import net.minecraft.server.v1_16_R1.InventoryEnderChest;
import net.minecraft.server.v1_16_R1.InventoryLargeChest;
import net.minecraft.server.v1_16_R1.PlayerInteractManager;
import net.minecraft.server.v1_16_R1.PlayerInventory;
import net.minecraft.server.v1_16_R1.TileEntity;
import net.minecraft.server.v1_16_R1.TileEntityChest;
import net.minecraft.server.v1_16_R1.TileEntityEnderChest;
import net.minecraft.server.v1_16_R1.TileEntityLootable;
import net.minecraft.server.v1_16_R1.TileInventory;
import net.minecraft.server.v1_16_R1.World;
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;
@ -150,7 +148,7 @@ public class AnySilentContainer implements IAnySilentContainer {
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)) {
@ -192,31 +190,10 @@ public class AnySilentContainer implements IAnySilentContainer {
InventoryEnderChest enderChest = player.getEnderChest();
enderChest.a((TileEntityEnderChest) tile);
player.openContainer(new TileInventory((containerCounter, playerInventory, ignored) -> {
Containers<?> containers;
Containers<?> containers = PlayerDataManager.getContainers(enderChest.getSize());
int rows = enderChest.getSize() / 9;
switch (rows) {
case 1:
containers = Containers.GENERIC_9X1;
break;
case 2:
containers = Containers.GENERIC_9X2;
break;
case 3:
default:
containers = Containers.GENERIC_9X3;
break;
case 4:
containers = Containers.GENERIC_9X4;
break;
case 5:
containers = Containers.GENERIC_9X5;
break;
case 6:
containers = Containers.GENERIC_9X6;
break;
}
return new ContainerChest(containers, containerCounter, playerInventory, enderChest, rows);
}, BlockEnderChest.e));
}, new ChatMessage("container.enderchest")));
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
@ -251,8 +228,8 @@ public class AnySilentContainer implements IAnySilentContainer {
TileEntityChest rightChest = chestType == BlockPropertyChestType.RIGHT ? ((TileEntityChest) tileInventory) : (TileEntityChest) adjacentTile;
TileEntityChest leftChest = chestType == BlockPropertyChestType.RIGHT ? (TileEntityChest) adjacentTile : ((TileEntityChest) tileInventory);
if (rightChest.lootTable != null || leftChest.lootTable != null) {
player.a(new ChatMessage("Loot not generated! Please disable /silentcontainer.").a(EnumChatFormat.RED), true);
if (silentchest && (rightChest.lootTable != null || leftChest.lootTable != null)) {
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
return false;
}
@ -264,6 +241,12 @@ public class AnySilentContainer implements IAnySilentContainer {
}
public IChatBaseComponent getScoreboardDisplayName() {
if (leftChest.hasCustomName()) {
return leftChest.getScoreboardDisplayName();
}
if (rightChest.hasCustomName()) {
return rightChest.getScoreboardDisplayName();
}
return new ChatMessage("container.chestDouble");
}
};
@ -325,9 +308,9 @@ public class AnySilentContainer implements IAnySilentContainer {
case ENDER_CHEST:
case SHULKER_BOX:
case BARREL:
break;
default:
return;
break;
default:
return;
}
EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer);

View file

@ -0,0 +1,69 @@
/*
* 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 saveData() {
super.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());
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());
}
}
}

View file

@ -0,0 +1,237 @@
/*
* 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.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 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.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) {
System.out.println("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
e.printStackTrace();
bukkitEntity = null;
}
}
@NotNull
public static EntityPlayer getHandle(final Player player) {
if (player instanceof CraftPlayer) {
return ((CraftPlayer) player).getHandle();
}
Server server = player.getServer();
EntityPlayer nmsPlayer = null;
if (server instanceof CraftServer) {
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getName());
}
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;
}
@Nullable
@Override
public 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();
WorldServer worldServer = server.getWorldServer(World.OVERWORLD);
if (worldServer == null) {
return null;
}
EntityPlayer entity = new EntityPlayer(server, worldServer, profile, new PlayerInteractManager(worldServer));
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(EntityPlayer 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 {
EntityPlayer 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) {
EntityPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.playerConnection == null) {
return null;
}
InventoryView view = getView(player, inventory);
if (view == null) {
return player.openInventory(inventory.getBukkitInventory());
}
Container container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public Containers<?> getType() {
return getContainers(inventory.getBukkitInventory().getSize());
}
};
container.setTitle(new ChatComponentText(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);
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 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;
}
}
@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;
}
}

View file

@ -14,21 +14,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_16_R1;
package com.lishid.openinv.internal.v1_16_R3;
import com.lishid.openinv.internal.ISpecialEnderChest;
import java.util.List;
import net.minecraft.server.v1_16_R1.AutoRecipeStackManager;
import net.minecraft.server.v1_16_R1.ContainerUtil;
import net.minecraft.server.v1_16_R1.EntityHuman;
import net.minecraft.server.v1_16_R1.EntityPlayer;
import net.minecraft.server.v1_16_R1.IInventoryListener;
import net.minecraft.server.v1_16_R1.InventoryEnderChest;
import net.minecraft.server.v1_16_R1.ItemStack;
import net.minecraft.server.v1_16_R1.NonNullList;
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_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory;
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;

View file

@ -14,7 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_16_R1;
package com.lishid.openinv.internal.v1_16_R3;
import com.google.common.collect.ImmutableList;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
@ -22,31 +22,31 @@ import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.server.v1_16_R1.AutoRecipeStackManager;
import net.minecraft.server.v1_16_R1.ChatMessage;
import net.minecraft.server.v1_16_R1.ContainerUtil;
import net.minecraft.server.v1_16_R1.CrashReport;
import net.minecraft.server.v1_16_R1.CrashReportSystemDetails;
import net.minecraft.server.v1_16_R1.DamageSource;
import net.minecraft.server.v1_16_R1.EntityHuman;
import net.minecraft.server.v1_16_R1.EntityPlayer;
import net.minecraft.server.v1_16_R1.EnumItemSlot;
import net.minecraft.server.v1_16_R1.IBlockData;
import net.minecraft.server.v1_16_R1.IChatBaseComponent;
import net.minecraft.server.v1_16_R1.IInventory;
import net.minecraft.server.v1_16_R1.Item;
import net.minecraft.server.v1_16_R1.ItemArmor;
import net.minecraft.server.v1_16_R1.ItemStack;
import net.minecraft.server.v1_16_R1.NBTTagCompound;
import net.minecraft.server.v1_16_R1.NBTTagList;
import net.minecraft.server.v1_16_R1.NonNullList;
import net.minecraft.server.v1_16_R1.PacketPlayOutSetSlot;
import net.minecraft.server.v1_16_R1.PlayerInventory;
import net.minecraft.server.v1_16_R1.ReportedException;
import net.minecraft.server.v1_16_R1.World;
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_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory;
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;

View file

@ -1,74 +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/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvinternal</artifactId>
<version>4.1.2</version>
</parent>
<artifactId>openinvadapter1_8_R3</artifactId>
<name>OpenInvAdapter1_8_R3</name>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.8.8-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.lishid</groupId>
<artifactId>openinvplugincore</artifactId>
<version>4.1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<minimizeJar>true</minimizeJar>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,250 +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_8_R3;
import com.lishid.openinv.internal.IAnySilentContainer;
import java.lang.reflect.Field;
import net.minecraft.server.v1_8_R3.AxisAlignedBB;
import net.minecraft.server.v1_8_R3.Block;
import net.minecraft.server.v1_8_R3.BlockChest;
import net.minecraft.server.v1_8_R3.BlockEnderChest;
import net.minecraft.server.v1_8_R3.BlockPosition;
import net.minecraft.server.v1_8_R3.Entity;
import net.minecraft.server.v1_8_R3.EntityOcelot;
import net.minecraft.server.v1_8_R3.EntityPlayer;
import net.minecraft.server.v1_8_R3.EnumDirection;
import net.minecraft.server.v1_8_R3.ITileInventory;
import net.minecraft.server.v1_8_R3.InventoryEnderChest;
import net.minecraft.server.v1_8_R3.InventoryLargeChest;
import net.minecraft.server.v1_8_R3.PlayerInteractManager;
import net.minecraft.server.v1_8_R3.StatisticList;
import net.minecraft.server.v1_8_R3.TileEntity;
import net.minecraft.server.v1_8_R3.TileEntityChest;
import net.minecraft.server.v1_8_R3.TileEntityEnderChest;
import net.minecraft.server.v1_8_R3.World;
import net.minecraft.server.v1_8_R3.WorldSettings.EnumGamemode;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
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 (Exception e) {
System.err.println("[OpenInv] Unable to directly write player gamemode! SilentChest will fail.");
e.printStackTrace();
}
}
@Override
public boolean isAnySilentContainer(@NotNull org.bukkit.block.Block bukkitBlock) {
return bukkitBlock.getType() == Material.ENDER_CHEST || bukkitBlock.getState() instanceof org.bukkit.block.Chest;
}
@Override
public boolean isAnyContainerNeeded(@NotNull Player bukkitPlayer, @NotNull org.bukkit.block.Block bukkitBlock) {
World world = PlayerDataManager.getHandle(bukkitPlayer).world;
BlockPosition blockPosition = new BlockPosition(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
Block block = world.getType(blockPosition).getBlock();
if (block instanceof BlockEnderChest) {
// Ender chests are not blocked by ocelots.
return world.getType(blockPosition.up()).getBlock().c();
}
// Check if chest is blocked or has an ocelot on top
if (isBlockedChest(world, blockPosition)) {
return true;
}
// Check for matching adjacent chests that are blocked or have an ocelot on top
for (EnumDirection localEnumDirection : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
BlockPosition localBlockPosition = blockPosition.shift(localEnumDirection);
Block localBlock = world.getType(localBlockPosition).getBlock();
if (localBlock != block) {
continue;
}
TileEntity localTileEntity = world.getTileEntity(localBlockPosition);
if (!(localTileEntity instanceof TileEntityChest)) {
continue;
}
if (isBlockedChest(world, localBlockPosition)) {
return true;
}
}
return false;
}
private boolean isBlockedChest(World world, BlockPosition blockPosition) {
// For reference, loot at net.minecraft.server.BlockChest
return world.getType(blockPosition.up()).getBlock().c() || hasOcelotOnTop(world, blockPosition);
}
private boolean hasOcelotOnTop(World world, BlockPosition blockPosition) {
for (Entity localEntity : world.a(EntityOcelot.class,
new AxisAlignedBB(blockPosition.getX(), blockPosition.getY() + 1,
blockPosition.getZ(), blockPosition.getX() + 1, blockPosition.getY() + 2,
blockPosition.getZ() + 1))) {
EntityOcelot localEntityOcelot = (EntityOcelot) localEntity;
if (localEntityOcelot.isSitting()) {
return true;
}
}
return false;
}
@Override
public boolean activateContainer(@NotNull Player bukkitPlayer, boolean silent, @NotNull org.bukkit.block.Block bukkitBlock) {
EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
// Silent ender chest is pretty much API-only
if (silent && bukkitBlock.getType() == Material.ENDER_CHEST) {
bukkitPlayer.openInventory(bukkitPlayer.getEnderChest());
player.b(StatisticList.V);
return true;
}
World world = player.world;
BlockPosition blockPosition = new BlockPosition(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
Object 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(enderChest);
player.b(StatisticList.V);
return true;
}
if (!(tile instanceof ITileInventory)) {
return false;
}
ITileInventory tileInventory = (ITileInventory) tile;
Block block = world.getType(blockPosition).getBlock();
if (block instanceof BlockChest) {
for (EnumDirection localEnumDirection : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
BlockPosition localBlockPosition = blockPosition.shift(localEnumDirection);
Block localBlock = world.getType(localBlockPosition).getBlock();
if (localBlock != block) {
continue;
}
TileEntity localTileEntity = world.getTileEntity(localBlockPosition);
if (!(localTileEntity instanceof TileEntityChest)) {
continue;
}
if ((localEnumDirection == EnumDirection.WEST) || (localEnumDirection == EnumDirection.NORTH)) {
tileInventory = new InventoryLargeChest("container.chestDouble",
(TileEntityChest) localTileEntity, tileInventory);
} else {
tileInventory = new InventoryLargeChest("container.chestDouble",
tileInventory, (TileEntityChest) localTileEntity);
}
break;
}
BlockChest blockChest = (BlockChest) block;
if (blockChest.b == 0) {
player.b(StatisticList.aa);
} else if (blockChest.b == 1) {
player.b(StatisticList.U);
}
}
// AnyChest only - SilentChest not active, container unsupported, or unnecessary.
if (!silent || player.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR) {
player.openContainer(tileInventory);
return true;
}
// SilentChest requires access to setting players' gamemode directly.
if (this.playerInteractManagerGamemode == null) {
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:
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 (Exception e) {
e.printStackTrace();
}
}
}

View file

@ -1,201 +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_8_R3;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.mojang.authlib.GameProfile;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import net.minecraft.server.v1_8_R3.ChatComponentText;
import net.minecraft.server.v1_8_R3.Entity;
import net.minecraft.server.v1_8_R3.EntityPlayer;
import net.minecraft.server.v1_8_R3.MinecraftServer;
import net.minecraft.server.v1_8_R3.NBTCompressedStreamTools;
import net.minecraft.server.v1_8_R3.NBTTagCompound;
import net.minecraft.server.v1_8_R3.PacketPlayOutChat;
import net.minecraft.server.v1_8_R3.PlayerInteractManager;
import net.minecraft.server.v1_8_R3.WorldNBTStorage;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_8_R3.CraftServer;
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
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 Field bukkitEntity;
public PlayerDataManager() {
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();
bukkitEntity = null;
}
}
@NotNull
public static EntityPlayer getHandle(Player player) {
if (player instanceof CraftPlayer) {
return ((CraftPlayer) player).getHandle();
}
Server server = player.getServer();
EntityPlayer nmsPlayer = null;
if (server instanceof CraftServer) {
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getName());
}
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;
}
@Nullable
@Override
public Player loadPlayer(@NotNull OfflinePlayer offline) {
// Ensure the player has data
if (!offline.hasPlayedBefore()) {
return null;
}
// Create a profile and entity to load the player data
GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(0), profile,
new PlayerInteractManager(server.getWorldServer(0)));
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(EntityPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new CraftPlayer(player.server.server, player) {
@Override
public void saveData() {
super.saveData();
// See net.minecraft.server.WorldNBTStorage#save(EntityHuman)
try {
WorldNBTStorage worldNBTStorage = (WorldNBTStorage) player.server.getPlayerList().playerFileData;
NBTTagCompound playerData = new NBTTagCompound();
player.e(playerData);
if (!isOnline()) {
// Special case: save old vehicle data
NBTTagCompound oldData = worldNBTStorage.load(player);
if (oldData != null && oldData.hasKeyOfType("Riding", 10)) {
// See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound)
playerData.set("Riding", oldData.getCompound("Riding"));
}
}
File file = new File(worldNBTStorage.getPlayerDir(), player.getUniqueID().toString() + ".dat.tmp");
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getUniqueID().toString() + ".dat");
NBTCompressedStreamTools.a(playerData, new FileOutputStream(file));
if (file1.exists()) {
file1.delete();
}
file.renameTo(file1);
} catch (Exception e) {
LogManager.getLogger().warn("Failed to save player data for {}", player.getName());
}
}
});
}
@NotNull
@Override
public Player inject(@NotNull Player player) {
try {
EntityPlayer 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) {
return player.openInventory(inventory.getBukkitInventory());
}
@Override
public void sendSystemMessage(@NotNull Player player, @NotNull String message) {
int newline = message.indexOf('\n');
if (newline != -1) {
// No newlines in action bar chat.
message = message.substring(0, newline);
}
if (message.isEmpty()) {
return;
}
EntityPlayer nmsPlayer = getHandle(player);
// For action bar chat, color codes are still supported but JSON text color is not allowed. Do not convert text.
if (nmsPlayer.playerConnection != null) {
nmsPlayer.playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(message), (byte) 2));
}
}
@NotNull
@Override
public String getLocale(Player player) {
return getHandle(player).locale;
}
}

View file

@ -1,83 +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_8_R3;
import com.lishid.openinv.internal.ISpecialEnderChest;
import java.lang.reflect.Field;
import net.minecraft.server.v1_8_R3.EntityPlayer;
import net.minecraft.server.v1_8_R3.IInventory;
import net.minecraft.server.v1_8_R3.InventoryEnderChest;
import net.minecraft.server.v1_8_R3.InventorySubcontainer;
import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftInventory;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
public class SpecialEnderChest extends InventorySubcontainer implements IInventory, ISpecialEnderChest {
private final InventoryEnderChest enderChest;
private final CraftInventory inventory = new CraftInventory(this);
private boolean playerOnline;
public SpecialEnderChest(Player player, Boolean online) {
super(PlayerDataManager.getHandle(player).getEnderChest().getName(),
PlayerDataManager.getHandle(player).getEnderChest().hasCustomName(),
PlayerDataManager.getHandle(player).getEnderChest().getSize());
this.playerOnline = online;
EntityPlayer nmsPlayer = PlayerDataManager.getHandle(player);
this.enderChest = nmsPlayer.getEnderChest();
this.bukkitOwner = nmsPlayer.getBukkitEntity();
this.items = enderChest.getContents();
}
@Override
public @NotNull Inventory getBukkitInventory() {
return inventory;
}
@Override
public void setPlayerOnline(@NotNull Player player) {
if (!playerOnline) {
try {
EntityPlayer nmsPlayer = PlayerDataManager.getHandle(player);
this.bukkitOwner = nmsPlayer.getBukkitEntity();
InventoryEnderChest playerEnderChest = nmsPlayer.getEnderChest();
Field field = playerEnderChest.getClass().getField("items");
field.setAccessible(true);
field.set(playerEnderChest, this.items);
} catch (Exception ignored) {}
playerOnline = true;
}
}
@Override
public void setPlayerOffline() {
playerOnline = false;
}
@Override
public boolean isInUse() {
return !this.getViewers().isEmpty();
}
@Override
public void update() {
super.update();
enderChest.update();
}
}

View file

@ -1,230 +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_8_R3;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import net.minecraft.server.v1_8_R3.ItemStack;
import net.minecraft.server.v1_8_R3.PlayerInventory;
import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftInventory;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
public class SpecialPlayerInventory extends PlayerInventory implements ISpecialPlayerInventory {
private final ItemStack[] extra = new ItemStack[5];
private final CraftInventory inventory = new CraftInventory(this);
private boolean playerOnline;
public SpecialPlayerInventory(Player bukkitPlayer, Boolean online) {
super(PlayerDataManager.getHandle(bukkitPlayer));
this.playerOnline = online;
this.items = player.inventory.items;
this.armor = player.inventory.armor;
}
@Override
public @NotNull Inventory getBukkitInventory() {
return inventory;
}
@Override
public void setPlayerOnline(@NotNull Player player) {
if (!playerOnline) {
this.player = PlayerDataManager.getHandle(player);
this.player.inventory.items = this.items;
this.player.inventory.armor = this.armor;
playerOnline = true;
}
}
@Override
public void setPlayerOffline() {
playerOnline = false;
}
@Override
public boolean isInUse() {
return !this.getViewers().isEmpty();
}
@Override
public ItemStack[] getContents() {
ItemStack[] contents = new ItemStack[getSize()];
System.arraycopy(items, 0, contents, 0, items.length);
System.arraycopy(armor, 0, contents, items.length, armor.length);
return contents;
}
@Override
public int getSize() {
return super.getSize() + 5;
}
@Override
public ItemStack getItem(int i) {
ItemStack[] is = this.items;
if (i >= is.length) {
i -= is.length;
is = this.armor;
} else {
i = getReversedItemSlotNum(i);
}
if (i >= is.length) {
i -= is.length;
is = this.extra;
} else if (is == this.armor) {
i = getReversedArmorSlotNum(i);
}
return is[i];
}
@Override
public ItemStack splitStack(int i, int j) {
ItemStack[] is = this.items;
if (i >= is.length) {
i -= is.length;
is = this.armor;
} else {
i = getReversedItemSlotNum(i);
}
if (i >= is.length) {
i -= is.length;
is = this.extra;
} else if (is == this.armor) {
i = getReversedArmorSlotNum(i);
}
if (is[i] != null) {
ItemStack itemstack;
if (is[i].count <= j) {
itemstack = is[i];
is[i] = null;
return itemstack;
} else {
itemstack = is[i].cloneAndSubtract(j);
if (is[i].count == 0) {
is[i] = null;
}
return itemstack;
}
}
return null;
}
@Override
public ItemStack splitWithoutUpdate(int i) {
ItemStack[] is = this.items;
if (i >= is.length) {
i -= is.length;
is = this.armor;
} else {
i = getReversedItemSlotNum(i);
}
if (i >= is.length) {
i -= is.length;
is = this.extra;
} else if (is == this.armor) {
i = getReversedArmorSlotNum(i);
}
if (is[i] != null) {
ItemStack itemstack = is[i];
is[i] = null;
return itemstack;
}
return null;
}
@Override
public void setItem(int i, ItemStack itemstack) {
ItemStack[] is = this.items;
if (i >= is.length) {
i -= is.length;
is = this.armor;
} else {
i = getReversedItemSlotNum(i);
}
if (i >= is.length) {
i -= is.length;
is = this.extra;
} else if (is == this.armor) {
i = getReversedArmorSlotNum(i);
}
// Effects
if (is == this.extra) {
player.drop(itemstack, true);
itemstack = null;
}
is[i] = itemstack;
player.defaultContainer.b();
}
private int getReversedItemSlotNum(int i) {
if (i >= 27) {
return i - 27;
}
return i + 9;
}
private int getReversedArmorSlotNum(int i) {
if (i == 0) {
return 3;
}
if (i == 1) {
return 2;
}
if (i == 2) {
return 1;
}
if (i == 3) {
return 0;
}
return i;
}
@Override
public String getName() {
if (player.getName().length() > 16) {
return player.getName().substring(0, 16);
}
return player.getName();
}
@Override
public boolean hasCustomName() {
return true;
}
}

View file

@ -21,7 +21,7 @@
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId>
<version>4.1.2</version>
<version>4.1.5-TF</version>
</parent>
<artifactId>openinvplugincore</artifactId>
@ -31,7 +31,7 @@
<dependency>
<groupId>com.lishid</groupId>
<artifactId>openinvapi</artifactId>
<version>4.1.2</version>
<version>4.1.5-TF</version>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>

View file

@ -27,9 +27,7 @@ import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.listeners.InventoryClickListener;
import com.lishid.openinv.listeners.InventoryCloseListener;
import com.lishid.openinv.listeners.InventoryDragListener;
import com.lishid.openinv.listeners.InventoryListener;
import com.lishid.openinv.listeners.PlayerListener;
import com.lishid.openinv.listeners.PluginListener;
import com.lishid.openinv.util.Cache;
@ -43,6 +41,9 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
@ -105,7 +106,6 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
if (!OpenInv.this.disableSaving() && !value.isOnline()) {
value.saveData();
}
return true;
});
private InternalAccessor accessor;
@ -127,6 +127,7 @@ 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
@ -140,6 +141,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
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())) {
@ -150,6 +152,20 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
}
}
/**
* Convert a raw slot number into a player inventory slot number.
*
* <p>Note that this method is specifically for converting an ISpecialPlayerInventory slot number into a regular
* player inventory slot number.
*
* @param view the open inventory view
* @param rawSlot the raw slot in the view
* @return the converted slot number
*/
public int convertToPlayerSlot(InventoryView view, int rawSlot) {
return this.accessor.getPlayerDataManager().convertToPlayerSlot(view, rawSlot);
}
@Override
public boolean disableSaving() {
return this.getConfig().getBoolean("settings.disable-saving", false);
@ -222,22 +238,18 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
return this.accessor != null && this.accessor.isSupported();
}
@Nullable
@Override
public Player loadPlayer(@NotNull final OfflinePlayer offline) {
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
String key = this.getPlayerID(offline);
if (this.playerCache.containsKey(key)) {
return this.playerCache.get(key);
}
// TODO: wrap Player to ensure all methods can safely be called offline
Player loaded;
if (offline.isOnline()) {
loaded = offline.getPlayer();
this.playerCache.put(key, loaded);
return loaded;
Player player = offline.getPlayer();
if (player != null) {
this.playerCache.put(key, player);
return player;
}
if (!this.isSupportedVersion()) {
@ -251,33 +263,18 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
Future<Player> future = Bukkit.getScheduler().callSyncMethod(this,
() -> OpenInv.this.accessor.getPlayerDataManager().loadPlayer(offline));
int ticks = 0;
while (!future.isDone() && !future.isCancelled() && ticks < 10) {
++ticks;
try {
Thread.sleep(50L);
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
}
if (!future.isDone() || future.isCancelled()) {
return null;
}
try {
loaded = future.get();
player = future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
if (loaded != null) {
this.playerCache.put(key, loaded);
if (player != null) {
this.playerCache.put(key, player);
}
return loaded;
return player;
}
@Override
@ -304,9 +301,21 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
public void sendSystemMessage(@NotNull Player player, @NotNull String key) {
String message = this.languageManager.getValue(key, getLocale(player));
if (message != null) {
this.accessor.getPlayerDataManager().sendSystemMessage(player, message);
if (message == null) {
return;
}
int newline = message.indexOf('\n');
if (newline != -1) {
// No newlines in action bar chat.
message = message.substring(0, newline);
}
if (message.isEmpty()) {
return;
}
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message));
}
public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key) {
@ -317,10 +326,9 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
return this.languageManager.getValue(key, getLocale(sender), replacements);
}
@Nullable
private String getLocale(@NotNull CommandSender sender) {
private @Nullable String getLocale(@NotNull CommandSender sender) {
if (sender instanceof Player) {
return this.accessor.getPlayerDataManager().getLocale((Player) sender);
return ((Player) sender).getLocale();
} else {
return this.getConfig().getString("settings.locale", "en_us");
}
@ -369,44 +377,39 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
// Register listeners
pm.registerEvents(new PlayerListener(this), this);
pm.registerEvents(new PluginListener(this), this);
pm.registerEvents(new InventoryClickListener(), this);
pm.registerEvents(new InventoryCloseListener(this), this);
// Bukkit will handle missing events for us, attempt to register InventoryDragEvent without a version check
pm.registerEvents(new InventoryDragListener(), this);
pm.registerEvents(new InventoryListener(this), this);
// Register commands to their executors
OpenInvCommand openInv = new OpenInvCommand(this);
this.setCommandExecutor("openinv", openInv);
this.setCommandExecutor("openender", openInv);
this.setCommandExecutor("searchcontainer", new SearchContainerCommand(this));
SearchInvCommand searchInv = new SearchInvCommand(this);
this.setCommandExecutor("searchinv", searchInv);
this.setCommandExecutor("searchender", searchInv);
this.setCommandExecutor("searchenchant", new SearchEnchantCommand(this));
ContainerSettingCommand settingCommand = new ContainerSettingCommand(this);
this.setCommandExecutor("silentcontainer", settingCommand);
this.setCommandExecutor("anycontainer", settingCommand);
this.setCommandExecutor(new OpenInvCommand(this), "openinv", "openender");
this.setCommandExecutor(new SearchContainerCommand(this), "searchcontainer");
this.setCommandExecutor(new SearchInvCommand(this), "searchinv", "searchender");
this.setCommandExecutor(new SearchEnchantCommand(this), "searchenchant");
this.setCommandExecutor(new ContainerSettingCommand(this), "silentcontainer", "anycontainer");
} else {
this.getLogger().info("Your version of CraftBukkit (" + this.accessor.getVersion() + ") is not supported.");
this.getLogger().info("If this version is a recent release, check for an update.");
this.getLogger().info("If this is an older version, ensure that you've downloaded the legacy support version.");
this.sendVersionError(this.getLogger()::warning);
}
}
private void setCommandExecutor(String commandName, CommandExecutor executor) {
PluginCommand command = this.getCommand(commandName);
if (command != null) {
command.setExecutor(executor);
private void sendVersionError(Consumer<String> messageMethod) {
messageMethod.accept("Your server version (" + this.accessor.getVersion() + ") is not supported.");
messageMethod.accept("Please obtain an appropriate version here: " + accessor.getReleasesLink());
}
private void setCommandExecutor(CommandExecutor executor, String... commands) {
for (String commandName : commands) {
PluginCommand command = this.getCommand(commandName);
if (command != null) {
command.setExecutor(executor);
}
}
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!this.accessor.isSupported()) {
sender.sendMessage("Your version of CraftBukkit (" + this.accessor.getVersion() + ") is not supported.");
sender.sendMessage("If this version is a recent release, check for an update.");
this.sendVersionError(sender::sendMessage);
return true;
}
return false;

View file

@ -21,7 +21,7 @@ import com.lishid.openinv.util.TabCompleter;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@ -46,7 +46,7 @@ public class ContainerSettingCommand implements TabExecutor {
Player player = (Player) sender;
boolean any = command.getName().startsWith("any");
Function<Player, Boolean> getSetting = any ? plugin::getPlayerAnyChestStatus : plugin::getPlayerSilentChestStatus;
Predicate<Player> getSetting = any ? plugin::getPlayerAnyChestStatus : plugin::getPlayerSilentChestStatus;
BiConsumer<OfflinePlayer, Boolean> setSetting = any ? plugin::setPlayerAnyChestStatus : plugin::setPlayerSilentChestStatus;
if (args.length > 0) {
@ -62,12 +62,12 @@ public class ContainerSettingCommand implements TabExecutor {
}
} else {
setSetting.accept(player, !getSetting.apply(player));
setSetting.accept(player, !getSetting.test(player));
}
String onOff = plugin.getLocalizedMessage(player, getSetting.apply(player) ? "messages.info.on" : "messages.info.off");
String onOff = plugin.getLocalizedMessage(player, getSetting.test(player) ? "messages.info.on" : "messages.info.off");
if (onOff == null) {
onOff = String.valueOf(getSetting.apply(player));
onOff = String.valueOf(getSetting.test(player));
}
plugin.sendMessage(sender, "messages.info.settingState","%setting%", any ? "AnyContainer" : "SilentContainer", "%state%", onOff);

View file

@ -72,6 +72,11 @@ public class SearchContainerCommand implements TabExecutor {
}
}
if (radius > 20)
{
radius = 20;
}
Player senderPlayer = (Player) sender;
World world = senderPlayer.getWorld();
Chunk centerChunk = senderPlayer.getLocation().getChunk();

View file

@ -31,6 +31,7 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Command adding the ability to search online players' inventories for enchantments of a specific
@ -124,8 +125,9 @@ public class SearchEnchantCommand implements TabExecutor {
return true;
}
private boolean containsEnchantment(Inventory inventory, Enchantment enchant, int minLevel) {
private boolean containsEnchantment(Inventory inventory, @Nullable Enchantment enchant, int minLevel) {
for (ItemStack item : inventory.getContents()) {
//noinspection ConstantConditions // Spigot improperly annotated, should be ItemStack @NotNull []
if (item == null || item.getType() == Material.AIR) {
continue;
}

View file

@ -32,16 +32,14 @@ public interface IPlayerDataManager {
* @param offline the OfflinePlayer
* @return the Player loaded
*/
@Nullable
Player loadPlayer(@NotNull OfflinePlayer offline);
@Nullable Player loadPlayer(@NotNull OfflinePlayer offline);
/**
* Creates a new Player from an existing one that will function slightly better offline.
*
* @return the Player
*/
@NotNull
Player inject(@NotNull Player player);
@NotNull Player inject(@NotNull Player player);
/**
* Opens an ISpecialInventory for a Player.
@ -51,14 +49,18 @@ public interface IPlayerDataManager {
*`
* @return the InventoryView opened
*/
@Nullable
InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory);
@Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory);
void sendSystemMessage(@NotNull Player player, @NotNull String message);
@NotNull
default String getLocale(Player player) {
return player.getLocale();
}
/**
* Convert a raw slot number into a player inventory slot number.
*
* <p>Note that this method is specifically for converting an ISpecialPlayerInventory slot number into a regular
* player inventory slot number.
*
* @param view the open inventory view
* @param rawSlot the raw slot in the view
* @return the converted slot number
*/
int convertToPlayerSlot(InventoryView view, int rawSlot);
}

View file

@ -0,0 +1,83 @@
/*
* 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;
import com.lishid.openinv.OpenInv;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
public class OpenInventoryView extends InventoryView {
private final Player player;
private final ISpecialInventory inventory;
private final String titleKey;
private final String titleDefaultSuffix;
private String title;
public OpenInventoryView(Player player, ISpecialInventory inventory, String titleKey, String titleDefaultSuffix) {
this.player = player;
this.inventory = inventory;
this.titleKey = titleKey;
this.titleDefaultSuffix = titleDefaultSuffix;
}
@Override
public @NotNull Inventory getTopInventory() {
return inventory.getBukkitInventory();
}
@Override
public @NotNull Inventory getBottomInventory() {
return getPlayer().getInventory();
}
@Override
public @NotNull HumanEntity getPlayer() {
return player;
}
@Override
public @NotNull InventoryType getType() {
return inventory.getBukkitInventory().getType();
}
@Override
public @NotNull String getTitle() {
if (title == null) {
HumanEntity owner = getPlayer();
String localTitle = OpenInv.getPlugin(OpenInv.class)
.getLocalizedMessage(
owner,
titleKey,
"%player%",
owner.getName());
if (localTitle != null) {
title = localTitle;
} else {
title = owner.getName() + titleDefaultSuffix;
}
}
return title;
}
}

View file

@ -1,40 +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.listeners;
import com.lishid.openinv.util.InventoryAccess;
import com.lishid.openinv.util.Permissions;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
public class InventoryClickListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onInventoryClick(InventoryClickEvent event) {
HumanEntity entity = event.getWhoClicked();
Inventory inventory = event.getInventory();
if (InventoryAccess.isPlayerInventory(inventory) && !Permissions.EDITINV.hasPermission(entity)
|| InventoryAccess.isEnderChest(inventory) && !Permissions.EDITENDER.hasPermission(entity)) {
event.setCancelled(true);
}
}
}

View file

@ -1,46 +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.listeners;
import com.lishid.openinv.IOpenInv;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryCloseEvent;
public class InventoryCloseListener implements Listener {
private final IOpenInv plugin;
public InventoryCloseListener(final IOpenInv plugin) {
this.plugin = plugin;
}
@EventHandler
public void onInventoryClose(final InventoryCloseEvent event) {
if (!(event.getPlayer() instanceof Player)) {
return;
}
Player player = (Player) event.getPlayer();
if (this.plugin.getPlayerSilentChestStatus(player)) {
this.plugin.getAnySilentContainer().deactivateContainer(player);
}
}
}

View file

@ -1,45 +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.listeners;
import com.lishid.openinv.util.InventoryAccess;
import com.lishid.openinv.util.Permissions;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.inventory.Inventory;
/**
* Listener for InventoryDragEvents to prevent unpermitted modification of special inventories.
*
* @author Jikoo
*/
public class InventoryDragListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onInventoryDrag(InventoryDragEvent event) {
HumanEntity entity = event.getWhoClicked();
Inventory inventory = event.getInventory();
if (InventoryAccess.isPlayerInventory(inventory) && !Permissions.EDITINV.hasPermission(entity)
|| InventoryAccess.isEnderChest(inventory) && !Permissions.EDITENDER.hasPermission(entity)) {
event.setCancelled(true);
}
}
}

View file

@ -0,0 +1,205 @@
/*
* 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.listeners;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.util.InventoryAccess;
import com.lishid.openinv.util.Permissions;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.bukkit.GameMode;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.inventory.InventoryInteractEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Listener for inventory-related events to prevent modification of inventories where not allowed.
*
* @author Jikoo
*/
public class InventoryListener implements Listener {
private final OpenInv plugin;
public InventoryListener(final OpenInv plugin) {
this.plugin = plugin;
}
@EventHandler
public void onInventoryClose(@NotNull final InventoryCloseEvent event) {
if (!(event.getPlayer() instanceof Player)) {
return;
}
Player player = (Player) event.getPlayer();
if (this.plugin.getPlayerSilentChestStatus(player)) {
this.plugin.getAnySilentContainer().deactivateContainer(player);
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onInventoryClick(@NotNull final InventoryClickEvent event) {
if (handleInventoryInteract(event)) {
return;
}
// Safe cast - has to be a player to be the holder of a special player inventory.
Player player = (Player) event.getWhoClicked();
if (event.getAction() != InventoryAction.MOVE_TO_OTHER_INVENTORY) {
// All own-inventory interactions require updates to display properly.
// Update in same tick after event completion.
this.plugin.getServer().getScheduler().runTask(this.plugin, player::updateInventory);
return;
}
// Extra handling for MOVE_TO_OTHER_INVENTORY - apparently Mojang no longer removes the item from the target
// inventory prior to adding it to existing stacks.
ItemStack currentItem = event.getCurrentItem();
if (currentItem == null) {
// Other plugin doing some sort of handling (would be NOTHING for null item otherwise), ignore.
return;
}
ItemStack clone = currentItem.clone();
event.setCurrentItem(null);
// Complete add action in same tick after event completion.
this.plugin.getServer().getScheduler().runTask(this.plugin, () -> {
player.getInventory().addItem(clone);
player.updateInventory();
});
}
@EventHandler(priority = EventPriority.LOWEST)
public void onInventoryDrag(@NotNull final InventoryDragEvent event) {
if (handleInventoryInteract(event)) {
return;
}
InventoryView view = event.getView();
int topSize = view.getTopInventory().getSize();
// Get bottom inventory active slots as player inventory slots.
Set<Integer> slots = event.getRawSlots().stream()
.filter(slot -> slot >= topSize)
.map(slot -> plugin.convertToPlayerSlot(view, slot)).collect(Collectors.toSet());
int overlapLosses = 0;
// Count overlapping slots.
for (Map.Entry<Integer, ItemStack> newItem : event.getNewItems().entrySet()) {
int rawSlot = newItem.getKey();
// Skip bottom inventory slots.
if (rawSlot >= topSize) {
continue;
}
int convertedSlot = plugin.convertToPlayerSlot(view, rawSlot);
if (slots.contains(convertedSlot)) {
overlapLosses += getCountDiff(view.getItem(rawSlot), newItem.getValue());
}
}
// Allow no overlap to proceed as usual.
if (overlapLosses < 1) {
return;
}
ItemStack cursor = event.getCursor();
if (cursor != null) {
cursor.setAmount(cursor.getAmount() + overlapLosses);
} else {
cursor = event.getOldCursor().clone();
cursor.setAmount(overlapLosses);
}
event.setCursor(cursor);
}
private int getCountDiff(@Nullable ItemStack original, @NotNull ItemStack result) {
if (original == null || original.getType() != result.getType()) {
return result.getAmount();
}
return result.getAmount() - original.getAmount();
}
/**
* Handle common InventoryInteractEvent functions.
*
* @param event the InventoryInteractEvent
* @return true unless the top inventory is the holder's own inventory
*/
private boolean handleInventoryInteract(@NotNull final InventoryInteractEvent event) {
HumanEntity entity = event.getWhoClicked();
// Un-cancel spectator interactions.
if (Permissions.SPECTATE.hasPermission(entity) && entity.getGameMode() == GameMode.SPECTATOR) {
event.setCancelled(false);
}
if (event.isCancelled()) {
return true;
}
Inventory inventory = event.getView().getTopInventory();
// Is the inventory a special ender chest?
if (InventoryAccess.isEnderChest(inventory)) {
// Disallow ender chest interaction for users without edit permission.
if (!Permissions.EDITENDER.hasPermission(entity)) {
event.setCancelled(true);
}
return true;
}
ISpecialPlayerInventory playerInventory = InventoryAccess.getPlayerInventory(inventory);
// Ignore inventories other than special player inventories.
if (playerInventory == null) {
return true;
}
// Disallow player inventory interaction for users without edit permission.
if (!Permissions.EDITINV.hasPermission(entity)) {
event.setCancelled(true);
return true;
}
// Only specially handle actions in the player's own inventory.
return !event.getWhoClicked().equals(event.getView().getTopInventory().getHolder());
}
}

View file

@ -54,6 +54,12 @@ public class PlayerListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerInteract(PlayerInteractEvent event) {
// Do not cancel 3rd party plugins' custom events
if (!PlayerInteractEvent.class.equals(event.getClass())) {
return;
}
if (event.getAction() != Action.RIGHT_CLICK_BLOCK || event.getPlayer().isSneaking()
|| event.useInteractedBlock() == Result.DENY || event.getClickedBlock() == null
|| !plugin.getAnySilentContainer().isAnySilentContainer(event.getClickedBlock())) {

View file

@ -24,7 +24,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* A minimal thread-safe time-based cache implementation backed by a HashMap and TreeMultimap.
@ -36,16 +37,17 @@ public class Cache<K, V> {
private final Map<K, V> internal;
private final Multimap<Long, K> expiry;
private final long retention;
private final Function<V, Boolean> inUseCheck, postRemoval;
private final Predicate<V> inUseCheck;
private final Consumer<V> postRemoval;
/**
* Constructs a Cache with the specified retention duration, in use function, and post-removal function.
*
* @param retention duration after which keys are automatically invalidated if not in use
* @param inUseCheck Function used to check if a key is considered in use
* @param postRemoval Function used to perform any operations required when a key is invalidated
* @param inUseCheck Predicate used to check if a key is considered in use
* @param postRemoval Consumer used to perform any operations required when a key is invalidated
*/
public Cache(final long retention, final Function<V, Boolean> inUseCheck, final Function<V, Boolean> postRemoval) {
public Cache(final long retention, final Predicate<V> inUseCheck, final Consumer<V> postRemoval) {
this.internal = new HashMap<>();
this.expiry = TreeMultimap.create(Long::compareTo, (k1, k2) -> Objects.equals(k1, k2) ? 0 : 1);
@ -136,7 +138,7 @@ public class Cache<K, V> {
public void invalidateAll() {
synchronized (this.internal) {
for (V value : this.internal.values()) {
this.postRemoval.apply(value);
this.postRemoval.accept(value);
}
this.expiry.clear();
this.internal.clear();
@ -161,7 +163,7 @@ public class Cache<K, V> {
iterator.remove();
if (this.inUseCheck.apply(this.internal.get(entry.getValue()))) {
if (this.inUseCheck.test(this.internal.get(entry.getValue()))) {
inUse.add(entry.getValue());
continue;
}
@ -172,7 +174,7 @@ public class Cache<K, V> {
continue;
}
this.postRemoval.apply(value);
this.postRemoval.accept(value);
}
long nextExpiry = now + this.retention;

View file

@ -24,7 +24,6 @@ import java.util.Map;
import java.util.Set;
import org.bukkit.OfflinePlayer;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.scheduler.BukkitRunnable;
public class ConfigUpdater {
@ -36,7 +35,8 @@ public class ConfigUpdater {
public void checkForUpdates() {
final int version = plugin.getConfig().getInt("config-version", 1);
if (version >= plugin.getConfig().getDefaults().getInt("config-version")) {
ConfigurationSection defaults = plugin.getConfig().getDefaults();
if (defaults == null || version >= defaults.getInt("config-version")) {
return;
}
@ -50,87 +50,72 @@ public class ConfigUpdater {
plugin.getLogger().warning("Could not back up config.yml before updating!");
}
new BukkitRunnable() {
@Override
public void run() {
if (version < 2) {
updateConfig1To2();
}
if (version < 3) {
updateConfig2To3();
}
if (version < 4) {
updateConfig3To4();
}
new BukkitRunnable() {
@Override
public void run() {
plugin.saveConfig();
plugin.getLogger().info("Configuration update complete!");
}
}.runTaskLater(plugin, 1L); // Run on 1 tick delay; on older versions Bukkit's scheduler is not guaranteed FIFO
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
if (version < 2) {
updateConfig1To2();
}
}.runTaskAsynchronously(plugin);
if (version < 3) {
updateConfig2To3();
}
if (version < 4) {
updateConfig3To4();
}
plugin.getServer().getScheduler().runTask(plugin, () -> {
plugin.saveConfig();
plugin.getLogger().info("Configuration update complete!");
});
});
}
private void updateConfig3To4() {
new BukkitRunnable() {
@Override
public void run() {
plugin.getConfig().set("notify", null);
plugin.getConfig().set("settings.locale", "en_US");
plugin.getConfig().set("config-version", 4);
}
}.runTask(plugin);
plugin.getServer().getScheduler().runTask(plugin, () -> {
plugin.getConfig().set("notify", null);
plugin.getConfig().set("settings.locale", "en_US");
plugin.getConfig().set("config-version", 4);
});
}
private void updateConfig2To3() {
new BukkitRunnable() {
@Override
public void run() {
plugin.getConfig().set("config-version", 3);
plugin.getConfig().set("items.open-inv", null);
plugin.getConfig().set("ItemOpenInv", null);
plugin.getConfig().set("toggles.items.open-inv", null);
plugin.getConfig().set("settings.disable-saving",
plugin.getConfig().getBoolean("DisableSaving", false));
plugin.getConfig().set("DisableSaving", null);
}
}.runTask(plugin);
plugin.getServer().getScheduler().runTask(plugin, () -> {
plugin.getConfig().set("config-version", 3);
plugin.getConfig().set("items.open-inv", null);
plugin.getConfig().set("ItemOpenInv", null);
plugin.getConfig().set("toggles.items.open-inv", null);
plugin.getConfig().set("settings.disable-saving",
plugin.getConfig().getBoolean("DisableSaving", false));
plugin.getConfig().set("DisableSaving", null);
});
}
private void updateConfig1To2() {
new BukkitRunnable() {
@Override
public void run() {
// Get the old config settings
boolean notifySilentChest = plugin.getConfig().getBoolean("NotifySilentChest", true);
boolean notifyAnyChest = plugin.getConfig().getBoolean("NotifyAnyChest", true);
plugin.getConfig().set("ItemOpenInvItemID", null);
plugin.getConfig().set("NotifySilentChest", null);
plugin.getConfig().set("NotifyAnyChest", null);
plugin.getConfig().set("config-version", 2);
plugin.getConfig().set("notify.any-chest", notifyAnyChest);
plugin.getConfig().set("notify.silent-chest", notifySilentChest);
}
}.runTask(plugin);
plugin.getServer().getScheduler().runTask(plugin, () -> {
// Get the old config settings
boolean notifySilentChest = plugin.getConfig().getBoolean("NotifySilentChest", true);
boolean notifyAnyChest = plugin.getConfig().getBoolean("NotifyAnyChest", true);
plugin.getConfig().set("ItemOpenInvItemID", null);
plugin.getConfig().set("NotifySilentChest", null);
plugin.getConfig().set("NotifyAnyChest", null);
plugin.getConfig().set("config-version", 2);
plugin.getConfig().set("notify.any-chest", notifyAnyChest);
plugin.getConfig().set("notify.silent-chest", notifySilentChest);
});
updateToggles("AnyChest", "toggles.any-chest");
updateToggles("SilentChest", "toggles.silent-chest");
}
private void updateToggles(final String sectionName, final String newSectionName) {
ConfigurationSection section = plugin.getConfig().getConfigurationSection(sectionName);
// Ensure section exists
if (!plugin.getConfig().isConfigurationSection(sectionName)) {
if (section == null) {
return;
}
ConfigurationSection section = plugin.getConfig().getConfigurationSection(sectionName);
Set<String> keys = section.getKeys(false);
// Ensure section has content
if (keys == null || keys.isEmpty()) {
if (keys.isEmpty()) {
return;
}
@ -143,25 +128,20 @@ public class ConfigUpdater {
}
}
new BukkitRunnable() {
@Override
public void run() {
// Wipe old ConfigurationSection
plugin.getConfig().set(sectionName, null);
plugin.getServer().getScheduler().runTask(plugin, () -> {
// Wipe old ConfigurationSection
plugin.getConfig().set(sectionName, null);
// Prepare new ConfigurationSection
ConfigurationSection newSection;
if (plugin.getConfig().isConfigurationSection(newSectionName)) {
newSection = plugin.getConfig().getConfigurationSection(newSectionName);
} else {
newSection = plugin.getConfig().createSection(newSectionName);
}
// Set new values
for (Map.Entry<String, Boolean> entry : toggles.entrySet()) {
newSection.set(entry.getKey(), entry.getValue());
}
// Prepare new ConfigurationSection
ConfigurationSection newSection = plugin.getConfig().getConfigurationSection(newSectionName);
if (newSection == null) {
newSection = plugin.getConfig().createSection(newSectionName);
}
}.runTask(plugin);
// Set new values
for (Map.Entry<String, Boolean> entry : toggles.entrySet()) {
newSection.set(entry.getKey(), entry.getValue());
}
});
}
}

View file

@ -40,15 +40,54 @@ public class InternalAccessor {
this.version = packageName.substring(packageName.lastIndexOf('.') + 1);
try {
// TODO: implement support for CraftMagicNumbers#getMappingsVersion
Class.forName("com.lishid.openinv.internal." + this.version + ".SpecialPlayerInventory");
Class.forName("com.lishid.openinv.internal." + this.version + ".SpecialEnderChest");
this.playerDataManager = this.createObject(IPlayerDataManager.class, "PlayerDataManager");
this.anySilentContainer = this.createObject(IAnySilentContainer.class, "AnySilentContainer");
this.supported = InventoryAccess.isUseable();
this.supported = InventoryAccess.isUsable();
} catch (Exception ignored) {}
}
public String getReleasesLink() {
switch (version) {
case "1_4_5":
case "1_4_6":
case "v1_4_R1":
case "v1_5_R2":
case "v1_5_R3":
case "v1_6_R1":
case "v1_6_R2":
case "v1_6_R3":
case "v1_7_R1":
case "v1_7_R2":
case "v1_7_R3":
case "v1_7_R4":
case "v1_8_R1":
case "v1_8_R2":
case "v1_9_R1":
case "v1_9_R2":
case "v1_10_R1":
case "v1_11_R1":
case "v1_12_R1":
return "https://github.com/lishid/OpenInv/releases/tag/4.0.0 (OpenInv-legacy)";
case "v1_13_R1":
return "https://github.com/lishid/OpenInv/releases/tag/4.0.0";
case "v1_13_R2":
return "https://github.com/lishid/OpenInv/releases/tag/4.0.7";
case "v1_14_R1":
return "https://github.com/lishid/OpenInv/releases/tag/4.1.1";
case "v1_16_R1":
return "https://github.com/lishid/OpenInv/releases/tag/4.1.4";
case "v1_8_R3":
case "v1_15_R1":
case "v1_16_R2":
return "https://github.com/lishid/OpenInv/releases/tag/4.1.5";
case "v1_16_R3":
default:
return "https://github.com/lishid/OpenInv/releases";
}
}
private <T> T createObject(final Class<? extends T> assignableClass, final String className,
final Object... params) throws ClassCastException, ClassNotFoundException,
InstantiationException, IllegalAccessException, IllegalArgumentException,

View file

@ -41,7 +41,7 @@ public class LanguageManager {
private final OpenInv plugin;
private final String defaultLocale;
private Map<String, YamlConfiguration> locales;
private final Map<String, YamlConfiguration> locales;
public LanguageManager(@NotNull OpenInv plugin, @NotNull String defaultLocale) {
this.plugin = plugin;

View file

@ -25,9 +25,9 @@ public enum Permissions {
EXEMPT("exempt"),
CROSSWORLD("crossworld"),
SILENT("silent"),
SILENT_DEFAULT("silent.default"),
SILENT_DEFAULT("silent.default", true),
ANYCHEST("anychest"),
ANY_DEFAULT("any.default"),
ANY_DEFAULT("any.default", true),
ENDERCHEST("openender"),
ENDERCHEST_ALL("openenderall"),
SEARCH("search"),
@ -35,18 +35,25 @@ public enum Permissions {
EDITENDER("editender"),
OPENSELF("openself"),
OPENONLINE("openonline"),
OPENOFFLINE("openoffline");
OPENOFFLINE("openoffline"),
SPECTATE("spectate");
private final String permission;
private final boolean uninheritable;
Permissions(String permission) {
this(permission, false);
}
Permissions(String permission, boolean uninheritable) {
this.permission = "OpenInv." + permission;
this.uninheritable = uninheritable;
}
public boolean hasPermission(Permissible permissible) {
boolean hasPermission = permissible.hasPermission(permission);
if (hasPermission || permissible.isPermissionSet(permission)) {
if (uninheritable || hasPermission || permissible.isPermissionSet(permission)) {
return hasPermission;
}

View file

@ -5,7 +5,7 @@ author: lishid
authors: [Jikoo, ShadowRanger]
description: >
This plugin allows you to open a player's inventory as a chest and interact with it in real time.
api-version: "1.14"
api-version: "1.16"
permissions:
OpenInv.any.default:
@ -27,6 +27,7 @@ permissions:
OpenInv.searchcontainer: true
OpenInv.openonline: true
OpenInv.openoffline: true
OpenInv.spectate: true
OpenInv.openinv:
default: op
children:

22
pom.xml
View file

@ -21,7 +21,7 @@
<artifactId>openinvparent</artifactId>
<name>OpenInvParent</name>
<url>http://dev.bukkit.org/bukkit-plugins/openinv/</url>
<version>4.1.2</version>
<version>4.1.5-TF</version>
<packaging>pom</packaging>
@ -54,26 +54,6 @@
</activation>
</profile>
<profile>
<id>latest</id>
<activation>
<property>
<name>latest</name>
<value>true</value>
</property>
</activation>
</profile>
<profile>
<id>recent</id>
<activation>
<property>
<name>recent</name>
<value>true</value>
</property>
</activation>
</profile>
</profiles>
<repositories>

View file

@ -0,0 +1,67 @@
#!/bin/bash
#
# 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/>.
#
# A script for generating a changelog from Git.
#
# Note that this script is designed for use in GitHub Actions, and is not
# particularly robust nor configurable. Run from project parent directory.
# Query GitHub for the username of the given email address.
# Falls through to the given author name.
lookup_email_username() {
lookup=$(curl -G --data-urlencode "q=$1 in:email" https://api.github.com/search/users -H 'Accept: application/vnd.github.v3+json' | grep '"login":' | sed -e 's/^.*": "//g' -e 's/",.*$//g')
if [[ $lookup ]]; then
echo -n "@$lookup"
else
echo "$2"
fi
}
# Use formatted log to pull authors list
authors_raw=$(git log --pretty=format:"%ae|%an" "$(git describe --tags --abbrev=0 @^)"..@)
readarray -t authors <<<"$authors_raw"
declare -A author_data
for author in "${authors[@]}"; do
# Match author email
author_email=${author%|*}
# Convert to lower case
author_email=${author_email,,}
# Match author name
author_name=${author##*|}
if [[ -n ${author_data[$author_email]} ]]; then
# Skip emails we already have data for
continue
fi
# Fetch and store author GitHub username by email
author_data[$author_email]=$(lookup_email_username "$author_email" "$author_name")
done
# Fetch actual formatted changelog
changelog=$(git log --pretty=format:"%s (%h) - %ae" "$(git describe --tags --abbrev=0 @^)"..@)
for author_email in "${!author_data[@]}"; do
# Ignore case when matching
shopt -s nocasematch
# Match and replace email
changelog=${changelog//$author_email/${author_data[$author_email]}}
done
echo "GENERATED_CHANGELOG<<EOF${changelog}EOF" >> "$GITHUB_ENV"

View file

@ -0,0 +1,75 @@
#!/bin/bash
#
# 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/>.
#
# A script for installing required Spigot versions.
#
# Note that this script is designed for use in GitHub Actions, and is
# not particularly robust nor configurable.
# In its current state, the script must be run from OpenInv's parent
# project directory and will always install BuildTools to ~/buildtools.
buildtools_dir=~/buildtools
buildtools=$buildtools_dir/BuildTools.jar
get_spigot_versions () {
# Get all submodules of internal module
modules=$(mvn help:evaluate -Dexpression=project.modules -q -DforceStdout -P all -pl internal | grep -oP '(?<=<string>)(.*)(?=<\/string>)')
for module in "${modules[@]}"; do
# Get number of dependencies declared in pom of specified internal module
max_index=$(mvn help:evaluate -Dexpression=project.dependencies -q -DforceStdout -P all -pl internal/"$module" | grep -c "<dependency>")
for ((i=0; i < max_index; i++)); do
# Get artifactId of dependency
artifact_id=$(mvn help:evaluate -Dexpression=project.dependencies["$i"].artifactId -q -DforceStdout -P all -pl internal/"$module")
# Ensure dependency is spigot
if [[ "$artifact_id" == spigot ]]; then
# Get spigot version
spigot_version=$(mvn help:evaluate -Dexpression=project.dependencies["$i"].version -q -DforceStdout -P all -pl internal/"$module")
echo "$spigot_version"
break
fi
done
done
}
get_buildtools () {
if [[ -d $buildtools_dir && -f $buildtools ]]; then
return
fi
mkdir $buildtools_dir
wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O $buildtools
}
versions=$(get_spigot_versions)
echo Found Spigot dependencies: "$versions"
for version in "${versions[@]}"; do
set -e
exit_code=0
mvn dependency:get -Dartifact=org.spigotmc:spigot:"$version" -q -o || exit_code=$?
if [ $exit_code -ne 0 ]; then
echo Installing missing Spigot version "$version"
revision=$(echo "$version" | grep -oP '(\d+\.\d+(\.\d+)?)(?=-R[0-9\.]+-SNAPSHOT)')
get_buildtools
java -jar $buildtools -rev "$revision"
else
echo Spigot "$version" is already installed
fi
done