Compare commits
236
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9e17e4a12 | ||
|
|
78908932c5 | ||
|
|
5f1a613b48 | ||
|
|
18c3dbaee0 | ||
|
|
d236dd27b3 | ||
|
|
6775988347 | ||
|
|
2580797633 | ||
|
|
dd90b5281f | ||
|
|
b4601bb6c1 | ||
|
|
e3d9cea027 | ||
|
|
556a8bcfce | ||
|
|
8599997e03 | ||
|
|
ea99bd5fd4 | ||
|
|
2f36a4d4dc | ||
|
|
e5ebf11d32 | ||
|
|
4336b454b7 | ||
|
|
b198931f30 | ||
|
|
64af4dddb0 | ||
|
|
f613c0a522 | ||
|
|
1cae4c7a8e | ||
|
|
ff7243db6a | ||
|
|
07a8e3b973 | ||
|
|
9fccea60f7 | ||
|
|
9a2b379a64 | ||
|
|
4d800361d8 | ||
|
|
2cfc55813b | ||
|
|
28929fd61e | ||
|
|
0f41701429 | ||
|
|
9e0ca479a5 | ||
|
|
2445e7f8b6 | ||
|
|
0af6403570 | ||
|
|
55018ca228 | ||
|
|
64bd918f32 | ||
|
|
bd207e948a | ||
|
|
30425d2baa | ||
|
|
fb6a3a7bcf | ||
|
|
4d4aa86399 | ||
|
|
1dce65cecb | ||
|
|
c1145ae111 | ||
|
|
19f029f17f | ||
|
|
916a8c0c55 | ||
|
|
e3acb5384a | ||
|
|
6c4818dfd9 | ||
|
|
5b18656485 | ||
|
|
7ab86f2af9 | ||
|
|
2b563e0e1b | ||
|
|
2a0e0d33d8 | ||
|
|
889c2ffdab | ||
|
|
c440f618c9 | ||
|
|
ccc6f4b4a6 | ||
|
|
ae6c3bd292 | ||
|
|
0e3bdb8741 | ||
|
|
e09e7c59c7 | ||
|
|
18c7916d79 | ||
|
|
15ee6ef9a5 | ||
|
|
24224e4f9d | ||
|
|
6563b4f6ce | ||
|
|
1d5a836fd0 | ||
|
|
3ba82683ec | ||
|
|
6e5c332272 | ||
|
|
1c9d133ed1 | ||
|
|
dad1e16c18 | ||
|
|
519dd7da33 | ||
|
|
1bd7932cc5 | ||
|
|
dad00d2d9c | ||
|
|
502f661be3 | ||
|
|
804d30bcc7 | ||
|
|
db8b67082b | ||
|
|
b2156edcc7 | ||
|
|
45de495699 | ||
|
|
f45d332727 | ||
|
|
9705cb156b | ||
|
|
1c45ef517f | ||
|
|
3fee84fc58 | ||
|
|
00113cc4d4 | ||
|
|
788f022e74 | ||
|
|
da55790fd2 | ||
|
|
1a6d513603 | ||
|
|
a5b02ab26a | ||
|
|
0c3ce2dfb3 | ||
|
|
a1f4649a09 | ||
|
|
100d0e12cd | ||
|
|
3e629798e5 | ||
|
|
7c1f15974c | ||
|
|
d992237766 | ||
|
|
c93464e643 | ||
|
|
56afefc82b | ||
|
|
f0a66570d2 | ||
|
|
201a3578bf | ||
|
|
1228fc2de2 | ||
|
|
2561e75ae5 | ||
|
|
80661b9465 | ||
|
|
53701da34f | ||
|
|
9cf4712813 | ||
|
|
abe8fccdb5 | ||
|
|
4499bb5453 | ||
|
|
5405b7d7b9 | ||
|
|
34199d12a4 | ||
|
|
67131ab9f9 | ||
|
|
723381cb2b | ||
|
|
34e7252d77 | ||
|
|
a1b2df74cc | ||
|
|
7210f9153e | ||
|
|
dcdfac0d3b | ||
|
|
c7b4554a6c | ||
|
|
c51acb4e72 | ||
|
|
23d41cd6c8 | ||
|
|
8bc389496b | ||
|
|
1cc36d08bd | ||
|
|
c8938f451d | ||
|
|
4f2665fedc | ||
|
|
7d93bb06ad | ||
|
|
19c491411f | ||
|
|
65c6eb2693 | ||
|
|
ab1f4ace8a | ||
|
|
e7dc52bee4 | ||
|
|
a9c5799677 | ||
|
|
6d6257720a | ||
|
|
3e235fa688 | ||
|
|
dd02355ac2 | ||
|
|
4d3f5d67a8 | ||
|
|
7ad5faa2ce | ||
|
|
c28383ef36 | ||
|
|
5fbab1ab8f | ||
|
|
d97b092978 | ||
|
|
154bf56c2a | ||
|
|
d65a9caeb2 | ||
|
|
b3c4253606 | ||
|
|
b64000e89b | ||
|
|
3faf41ef18 | ||
|
|
db27d25a93 | ||
|
|
0f02e6fe66 | ||
|
|
ec07637e9f | ||
|
|
b269f3509c | ||
|
|
f2e4c8a3ad | ||
|
|
35839f1548 | ||
|
|
a4a462a1fa | ||
|
|
b9f901af66 | ||
|
|
1333056613 | ||
|
|
fb23df313a | ||
|
|
96dc400c83 | ||
|
|
b801d36f12 | ||
|
|
4c27599dd9 | ||
|
|
64ad0a5267 | ||
|
|
cba7f44192 | ||
|
|
e8ace6bd06 | ||
|
|
fd70f260ba | ||
|
|
be9874ba1d | ||
|
|
096faef161 | ||
|
|
df96d5106a | ||
|
|
fe77a1d2e7 | ||
|
|
3096e43540 | ||
|
|
2939551d65 | ||
|
|
fb74fd3ced | ||
|
|
393fe1d837 | ||
|
|
f80df9d83b | ||
|
|
5c006f8ebd | ||
|
|
57097e626e | ||
|
|
0f266c8a64 | ||
|
|
185f037254 | ||
|
|
e4d0020296 | ||
|
|
9e37cbbca8 | ||
|
|
adc35e9ad5 | ||
|
|
a469c732cc | ||
|
|
c5c1f47167 | ||
|
|
dc59356f76 | ||
|
|
d497772ad7 | ||
|
|
4f4d46974a | ||
|
|
96c38895e2 | ||
|
|
5753648d77 | ||
|
|
3725711b94 | ||
|
|
c22e18c096 | ||
|
|
6bff759d72 | ||
|
|
582c7201dd | ||
|
|
59a8ed70bb | ||
|
|
27812c133d | ||
|
|
165f3fb00e | ||
|
|
60dcb64558 | ||
|
|
077b34d304 | ||
|
|
8ce6e2f8d3 | ||
|
|
f5dce23f18 | ||
|
|
669cfd72aa | ||
|
|
9e66885690 | ||
|
|
a802769265 | ||
|
|
50c45b6616 | ||
|
|
eaf01fa32c | ||
|
|
4abaa6ea51 | ||
|
|
7c621bf899 | ||
|
|
f5c682ff2b | ||
|
|
e652b43670 | ||
|
|
f07cef5621 | ||
|
|
ded6421e33 | ||
|
|
cbb2b4d232 | ||
|
|
48c61eeb82 | ||
|
|
e3d1af010b | ||
|
|
2195677651 | ||
|
|
c72af5dbac | ||
|
|
931cdb6fb1 | ||
|
|
9db18613a1 | ||
|
|
784935a975 | ||
|
|
d9abe76531 | ||
|
|
ca85a392aa | ||
|
|
307f47e4d3 | ||
|
|
96c59f163d | ||
|
|
01f147b13c | ||
|
|
1c104b775f | ||
|
|
9a53e46dd3 | ||
|
|
6744e71127 | ||
|
|
12c3ebb798 | ||
|
|
32ec8d8147 | ||
|
|
443e0c489e | ||
|
|
7ab0003b62 | ||
|
|
12c0cac311 | ||
|
|
f54481e872 | ||
|
|
f27dd8a233 | ||
|
|
d6df1a0871 | ||
|
|
e34e51d127 | ||
|
|
5ca2ae461a | ||
|
|
4095e8039d | ||
|
|
73f390ab20 | ||
|
|
2f7d3bc235 | ||
|
|
ef486032f1 | ||
|
|
72ef873772 | ||
|
|
9b6e32a775 | ||
|
|
f05110c9b8 | ||
|
|
a41f89b011 | ||
|
|
d24827ffcb | ||
|
|
802ce28103 | ||
|
|
f31356b227 | ||
|
|
7942466863 | ||
|
|
db2cade4e2 | ||
|
|
718b4bb5dd | ||
|
|
6aa25dd2dc | ||
|
|
0bbcf6cdb2 | ||
|
|
f11d60f78c | ||
|
|
8a6b98614f |
No files matched your search
@@ -0,0 +1,77 @@
|
||||
name: OpenInv CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request_target:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set Up Java
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '17'
|
||||
|
||||
# Use cache to speed up build
|
||||
- name: Cache Maven Repo
|
||||
uses: actions/cache@v2
|
||||
id: cache
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
|
||||
# Install Spigot dependencies.
|
||||
# This script uses Maven to check all required installations and ensure that they are present.
|
||||
- name: Install Spigot Dependencies
|
||||
run: . scripts/install_spigot_dependencies.sh
|
||||
|
||||
- name: Build With Maven
|
||||
run: mvn -e clean package -am -P all
|
||||
|
||||
# Upload artifacts
|
||||
- name: Upload Distributable Jar
|
||||
id: upload-final
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: dist
|
||||
path: ./target/OpenInv.jar
|
||||
- name: Upload API Jar
|
||||
id: upload-api
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: api
|
||||
path: ./api/target/openinvapi*.jar
|
||||
|
||||
release:
|
||||
name: Create Github Release
|
||||
needs: [ build ]
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set Release Variables
|
||||
run: bash ./scripts/set_release_env.sh
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
|
||||
- name: Create Release
|
||||
id: create-release
|
||||
uses: softprops/action-gh-release@v0.1.5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
name: ${{ env.VERSIONED_NAME }}
|
||||
body: ${{ env.GENERATED_CHANGELOG }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
files: ./dist/OpenInv.jar
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Release to CurseForge
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [ released ]
|
||||
|
||||
jobs:
|
||||
curseforge_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Fetch Github Release Asset
|
||||
uses: dsaltares/fetch-gh-release-asset@0.0.5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: ${{ github.event.release.id }}
|
||||
file: OpenInv.jar
|
||||
|
||||
- name: Set CurseForge Variables
|
||||
run: . scripts/set_curseforge_env.sh "${{ github.event.release.body }}"
|
||||
|
||||
- name: Create CurseForge Release
|
||||
uses: itsmeow/curseforge-upload@v3
|
||||
with:
|
||||
token: ${{ secrets.CURSEFORGE_TOKEN }}
|
||||
project_id: 31432
|
||||
game_endpoint: minecraft
|
||||
file_path: ./OpenInv.jar
|
||||
display_name: "${{ github.event.release.name }}"
|
||||
game_versions: "${{ env.CURSEFORGE_MINECRAFT_VERSIONS }}"
|
||||
release_type: release
|
||||
changelog_type: markdown
|
||||
changelog: "${{ env.CURSEFORGE_CHANGELOG }}"
|
||||
+7
-7
@@ -1,10 +1,10 @@
|
||||
**/.settings
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.classpath
|
||||
**/.idea
|
||||
**/target
|
||||
**/bin
|
||||
**/lib
|
||||
**/out
|
||||
**dependency-reduced-pom.xml
|
||||
**/.idea/
|
||||
**.iml
|
||||
**/target/
|
||||
**/bin/
|
||||
**/out/
|
||||
**/dependency-reduced-pom.xml
|
||||
**/pom.xml.versionsBackup
|
||||
@@ -1,28 +0,0 @@
|
||||
<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>openinv</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvadapter1_4_5</artifactId>
|
||||
<name>OpenInvAdapter1_4_5</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvplugin</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>craftbukkit</artifactId>
|
||||
<version>1.4.5-R1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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_4_5;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import com.lishid.openinv.internal.IAnySilentContainer;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
// Volatile
|
||||
import net.minecraft.server.v1_4_5.AxisAlignedBB;
|
||||
import net.minecraft.server.v1_4_5.BlockEnderChest;
|
||||
import net.minecraft.server.v1_4_5.EntityOcelot;
|
||||
import net.minecraft.server.v1_4_5.EntityPlayer;
|
||||
import net.minecraft.server.v1_4_5.IInventory;
|
||||
import net.minecraft.server.v1_4_5.InventoryEnderChest;
|
||||
import net.minecraft.server.v1_4_5.InventoryLargeChest;
|
||||
import net.minecraft.server.v1_4_5.Packet100OpenWindow;
|
||||
import net.minecraft.server.v1_4_5.TileEntityChest;
|
||||
import net.minecraft.server.v1_4_5.TileEntityEnderChest;
|
||||
import net.minecraft.server.v1_4_5.World;
|
||||
|
||||
import org.bukkit.craftbukkit.v1_4_5.entity.CraftPlayer;
|
||||
|
||||
public class AnySilentContainer implements IAnySilentContainer {
|
||||
|
||||
@Override
|
||||
public boolean isAnySilentContainer(org.bukkit.block.Block block) {
|
||||
return block.getType() == Material.ENDER_CHEST || block.getState() instanceof org.bukkit.block.Chest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnyContainerNeeded(Player p, org.bukkit.block.Block block) {
|
||||
// FOR REFERENCE, LOOK AT net.minecraft.server.BlockChest
|
||||
EntityPlayer player = ((CraftPlayer) p).getHandle();
|
||||
World world = player.world;
|
||||
|
||||
if (block instanceof BlockEnderChest) {
|
||||
// Ender chests are not blocked by ocelots.
|
||||
return world.t(block.getX(), block.getY() + 1, block.getZ());
|
||||
}
|
||||
|
||||
// If block or ocelot on top
|
||||
if (isBlockedChest(world, block.getX(), block.getY() + 1, block.getZ())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int id = world.getTypeId(block.getX(), block.getY(), block.getZ());
|
||||
|
||||
// If block next to chest is chest and has a block or ocelot on top
|
||||
if (world.getTypeId(block.getX(), block.getY(), block.getZ() + 1) == id) {
|
||||
return isBlockedChest(world, block.getX(), block.getY() + 1, block.getZ() + 1);
|
||||
} else if(world.getTypeId(block.getX(), block.getY(), block.getZ() - 1) == id) {
|
||||
return isBlockedChest(world, block.getX(), block.getY() + 1, block.getZ() - 1);
|
||||
} else if (world.getTypeId(block.getX() + 1, block.getY(), block.getZ()) == id) {
|
||||
return isBlockedChest(world, block.getX() + 1, block.getY() + 1, block.getZ());
|
||||
} else if (world.getTypeId(block.getX() - 1, block.getY(), block.getZ()) == id) {
|
||||
return isBlockedChest(world, block.getX() - 1, block.getY() + 1, block.getZ());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isBlockedChest(World world, int x, int y, int z) {
|
||||
return world.t(x, y + 1, z) || hasOcelotOnTop(world, x, y, z);
|
||||
}
|
||||
|
||||
private boolean hasOcelotOnTop(World world, int x, int y, int z) {
|
||||
for (Object localEntity : world.a(EntityOcelot.class,
|
||||
AxisAlignedBB.a(x, y + 1, z, x + 1, y + 2, z + 1))) {
|
||||
EntityOcelot localEntityOcelot = (EntityOcelot) localEntity;
|
||||
if (localEntityOcelot.isSitting()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activateContainer(Player p, boolean silentchest, org.bukkit.block.Block block) {
|
||||
|
||||
EntityPlayer player = ((CraftPlayer) p).getHandle();
|
||||
|
||||
// Silent ender chest is API-only
|
||||
if (silentchest && block.getType() == Material.ENDER_CHEST) {
|
||||
p.openInventory(p.getEnderChest());
|
||||
return true;
|
||||
}
|
||||
|
||||
World world = player.world;
|
||||
Object tile = world.getTileEntity(block.getX(), block.getY(), block.getZ());
|
||||
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(tile instanceof IInventory)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int id = world.getTypeId(block.getX(), block.getY(), block.getZ());
|
||||
|
||||
if (world.getTypeId(block.getX(), block.getY(), block.getZ() + 1) == id) {
|
||||
tile = new InventoryLargeChest("Large chest", (IInventory) tile, (TileEntityChest) world.getTileEntity(block.getX(), block.getY(), block.getZ() + 1));
|
||||
} else if (world.getTypeId(block.getX(), block.getY(), block.getZ() - 1) == id) {
|
||||
tile = new InventoryLargeChest("Large chest", (TileEntityChest) world.getTileEntity(block.getX(), block.getY(), block.getZ() - 1), (IInventory) tile);
|
||||
} else if (world.getTypeId(block.getX() + 1, block.getY(), block.getZ()) == id) {
|
||||
tile = new InventoryLargeChest("Large chest", (IInventory) tile, (TileEntityChest) world.getTileEntity(block.getX() + 1, block.getY(), block.getZ()));
|
||||
} else if (world.getTypeId(block.getX() - 1, block.getY(), block.getZ()) == id) {
|
||||
tile = new InventoryLargeChest("Large chest", (TileEntityChest) world.getTileEntity(block.getX() - 1, block.getY(), block.getZ()), (IInventory) tile);
|
||||
}
|
||||
|
||||
boolean returnValue = false;
|
||||
if (!silentchest) {
|
||||
player.openContainer((IInventory) tile);
|
||||
returnValue = true;
|
||||
} else {
|
||||
try {
|
||||
int windowId = 0;
|
||||
try {
|
||||
Field windowID = player.getClass().getDeclaredField("containerCounter");
|
||||
windowID.setAccessible(true);
|
||||
windowId = windowID.getInt(player);
|
||||
windowId = windowId % 100 + 1;
|
||||
windowID.setInt(player, windowId);
|
||||
} catch (NoSuchFieldException e) {}
|
||||
|
||||
player.netServerHandler.sendPacket(new Packet100OpenWindow(windowId, 0, ((IInventory) tile).getName(), ((IInventory) tile).getSize()));
|
||||
player.activeContainer = new SilentContainerChest(player.inventory, ((IInventory) tile));
|
||||
player.activeContainer.windowId = windowId;
|
||||
player.activeContainer.addSlotListener(player);
|
||||
returnValue = true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
p.sendMessage(ChatColor.RED + "Error while sending silent chest.");
|
||||
}
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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_4_5;
|
||||
|
||||
import com.lishid.openinv.internal.IInventoryAccess;
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.internal.InternalAccessor;
|
||||
|
||||
import org.bukkit.inventory.Inventory;
|
||||
|
||||
// Volatile
|
||||
import net.minecraft.server.v1_4_5.IInventory;
|
||||
|
||||
import org.bukkit.craftbukkit.v1_4_5.inventory.CraftInventory;
|
||||
|
||||
public class InventoryAccess implements IInventoryAccess {
|
||||
|
||||
@Override
|
||||
public boolean isSpecialPlayerInventory(Inventory inventory) {
|
||||
if (inventory instanceof CraftInventory) {
|
||||
return ((CraftInventory) inventory).getInventory() instanceof ISpecialPlayerInventory;
|
||||
}
|
||||
return InternalAccessor.grabFieldOfTypeFromObject(IInventory.class, inventory) instanceof ISpecialPlayerInventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISpecialPlayerInventory getSpecialPlayerInventory(Inventory inventory) {
|
||||
IInventory inv;
|
||||
if (inventory instanceof CraftInventory) {
|
||||
inv = ((CraftInventory) inventory).getInventory();
|
||||
} else {
|
||||
inv = InternalAccessor.grabFieldOfTypeFromObject(IInventory.class, inventory);
|
||||
}
|
||||
|
||||
if (inv instanceof SpecialPlayerInventory) {
|
||||
return (SpecialPlayerInventory) inv;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSpecialEnderChest(Inventory inventory) {
|
||||
if (inventory instanceof CraftInventory) {
|
||||
return ((CraftInventory) inventory).getInventory() instanceof ISpecialEnderChest;
|
||||
}
|
||||
return InternalAccessor.grabFieldOfTypeFromObject(IInventory.class, inventory) instanceof ISpecialEnderChest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISpecialEnderChest getSpecialEnderChest(Inventory inventory) {
|
||||
IInventory inv;
|
||||
if (inventory instanceof CraftInventory) {
|
||||
inv = ((CraftInventory) inventory).getInventory();
|
||||
} else {
|
||||
inv = InternalAccessor.grabFieldOfTypeFromObject(IInventory.class, inventory);
|
||||
}
|
||||
|
||||
if (inv instanceof SpecialEnderChest) {
|
||||
return (SpecialEnderChest) inv;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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_4_5;
|
||||
|
||||
import com.lishid.openinv.internal.IPlayerDataManager;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
// Volatile
|
||||
import net.minecraft.server.v1_4_5.EntityPlayer;
|
||||
import net.minecraft.server.v1_4_5.ItemInWorldManager;
|
||||
import net.minecraft.server.v1_4_5.MinecraftServer;
|
||||
|
||||
import org.bukkit.craftbukkit.v1_4_5.CraftServer;
|
||||
|
||||
public class PlayerDataManager implements IPlayerDataManager {
|
||||
|
||||
@Override
|
||||
public Player loadPlayer(OfflinePlayer offline) {
|
||||
// Ensure the player has data
|
||||
if (offline == null || !offline.hasPlayedBefore()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
|
||||
// Create an entity to load the player data
|
||||
EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(0), offline.getName(),
|
||||
new ItemInWorldManager(server.getWorldServer(0)));
|
||||
|
||||
// Get the bukkit entity
|
||||
Player target = (entity == null) ? null : entity.getBukkitEntity();
|
||||
if (target != null) {
|
||||
// Load data
|
||||
target.loadData();
|
||||
}
|
||||
// Return the entity
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayerDataID(OfflinePlayer player) {
|
||||
return player.getName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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_4_5;
|
||||
|
||||
// Volatile
|
||||
import net.minecraft.server.v1_4_5.ContainerChest;
|
||||
import net.minecraft.server.v1_4_5.EntityHuman;
|
||||
import net.minecraft.server.v1_4_5.IInventory;
|
||||
import net.minecraft.server.v1_4_5.ItemStack;
|
||||
import net.minecraft.server.v1_4_5.PlayerInventory;
|
||||
|
||||
public class SilentContainerChest extends ContainerChest {
|
||||
|
||||
public SilentContainerChest(IInventory i1, IInventory i2) {
|
||||
super(i1, i2);
|
||||
// Send close signal
|
||||
i2.f();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void b(EntityHuman entityHuman) {
|
||||
// Don't send close signal twice, might screw up
|
||||
PlayerInventory playerinventory = entityHuman.inventory;
|
||||
|
||||
if (playerinventory.getCarried() != null) {
|
||||
ItemStack carried = playerinventory.getCarried();
|
||||
playerinventory.setCarried(null);
|
||||
entityHuman.drop(carried);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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_4_5;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
|
||||
// Volatile
|
||||
import net.minecraft.server.v1_4_5.IInventory;
|
||||
import net.minecraft.server.v1_4_5.InventoryEnderChest;
|
||||
import net.minecraft.server.v1_4_5.InventorySubcontainer;
|
||||
import net.minecraft.server.v1_4_5.ItemStack;
|
||||
|
||||
import org.bukkit.craftbukkit.v1_4_5.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_4_5.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_4_5.inventory.CraftInventory;
|
||||
|
||||
public class SpecialEnderChest extends InventorySubcontainer implements IInventory, ISpecialEnderChest {
|
||||
|
||||
private final InventoryEnderChest enderChest;
|
||||
private final CraftInventory inventory = new CraftInventory(this);
|
||||
private final List<HumanEntity> transaction = new ArrayList<HumanEntity>();
|
||||
private boolean playerOnline = false;
|
||||
private CraftPlayer owner;
|
||||
private int maxStack = MAX_STACK;
|
||||
|
||||
public SpecialEnderChest(Player p, Boolean online) {
|
||||
super(((CraftPlayer) p).getHandle().getEnderChest().getName(),
|
||||
((CraftPlayer) p).getHandle().getEnderChest().getSize());
|
||||
CraftPlayer player = (CraftPlayer) p;
|
||||
this.enderChest = player.getHandle().getEnderChest();
|
||||
this.owner = player;
|
||||
this.items = enderChest.getContents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory getBukkitInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(Player player) {
|
||||
if (!playerOnline) {
|
||||
try {
|
||||
owner = (CraftPlayer) player;
|
||||
InventoryEnderChest playerEnderChest = owner.getHandle().getEnderChest();
|
||||
Field field = playerEnderChest.getClass().getField("items");
|
||||
field.setAccessible(true);
|
||||
field.set(playerEnderChest, this.items);
|
||||
} catch (Exception e) {}
|
||||
playerOnline = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {
|
||||
playerOnline = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUse() {
|
||||
return !this.getViewers().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getContents() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity who) {
|
||||
transaction.add(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity who) {
|
||||
transaction.remove(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryHolder getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int size) {
|
||||
maxStack = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return maxStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
super.update();
|
||||
enderChest.update();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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_4_5;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
|
||||
// Volatile
|
||||
import net.minecraft.server.v1_4_5.ItemStack;
|
||||
import net.minecraft.server.v1_4_5.PlayerInventory;
|
||||
|
||||
import org.bukkit.craftbukkit.v1_4_5.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_4_5.inventory.CraftInventory;
|
||||
|
||||
public class SpecialPlayerInventory extends PlayerInventory implements ISpecialPlayerInventory {
|
||||
|
||||
private final ItemStack[] extra = new ItemStack[5];
|
||||
private final CraftInventory inventory = new CraftInventory(this);
|
||||
private boolean playerOnline = false;
|
||||
|
||||
public SpecialPlayerInventory(Player bukkitPlayer, Boolean online) {
|
||||
super(((CraftPlayer) bukkitPlayer).getHandle());
|
||||
this.playerOnline = online;
|
||||
this.items = player.inventory.items;
|
||||
this.armor = player.inventory.armor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory getBukkitInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(Player player) {
|
||||
if (!playerOnline) {
|
||||
this.player = ((CraftPlayer) player).getHandle();
|
||||
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].a(j);
|
||||
if (is[i].count == 0) {
|
||||
is[i] = null;
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
else {
|
||||
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;
|
||||
}
|
||||
else {
|
||||
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);
|
||||
itemstack = null;
|
||||
}
|
||||
|
||||
is[i] = itemstack;
|
||||
|
||||
player.defaultContainer.b();
|
||||
}
|
||||
|
||||
private int getReversedItemSlotNum(int i) {
|
||||
if (i >= 27)
|
||||
return i - 27;
|
||||
else
|
||||
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;
|
||||
else
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
if (player.getName().length() > 16) {
|
||||
return player.getName().substring(0, 16);
|
||||
}
|
||||
return player.getName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<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>openinv</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvadapter1_4_6</artifactId>
|
||||
<name>OpenInvAdapter1_4_6</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvplugin</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>craftbukkit</artifactId>
|
||||
<version>1.4.6-R0.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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_4_6;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import com.lishid.openinv.internal.IAnySilentContainer;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
// Volatile
|
||||
import net.minecraft.server.v1_4_6.AxisAlignedBB;
|
||||
import net.minecraft.server.v1_4_6.BlockEnderChest;
|
||||
import net.minecraft.server.v1_4_6.EntityOcelot;
|
||||
import net.minecraft.server.v1_4_6.EntityPlayer;
|
||||
import net.minecraft.server.v1_4_6.IInventory;
|
||||
import net.minecraft.server.v1_4_6.InventoryEnderChest;
|
||||
import net.minecraft.server.v1_4_6.InventoryLargeChest;
|
||||
import net.minecraft.server.v1_4_6.Packet100OpenWindow;
|
||||
import net.minecraft.server.v1_4_6.TileEntityChest;
|
||||
import net.minecraft.server.v1_4_6.TileEntityEnderChest;
|
||||
import net.minecraft.server.v1_4_6.World;
|
||||
|
||||
import org.bukkit.craftbukkit.v1_4_6.entity.CraftPlayer;
|
||||
|
||||
public class AnySilentContainer implements IAnySilentContainer {
|
||||
|
||||
@Override
|
||||
public boolean isAnySilentContainer(org.bukkit.block.Block block) {
|
||||
return block.getType() == Material.ENDER_CHEST || block.getState() instanceof org.bukkit.block.Chest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnyContainerNeeded(Player p, org.bukkit.block.Block block) {
|
||||
// FOR REFERENCE, LOOK AT net.minecraft.server.BlockChest
|
||||
EntityPlayer player = ((CraftPlayer) p).getHandle();
|
||||
World world = player.world;
|
||||
|
||||
if (block instanceof BlockEnderChest) {
|
||||
// Ender chests are not blocked by ocelots.
|
||||
return world.t(block.getX(), block.getY() + 1, block.getZ());
|
||||
}
|
||||
|
||||
// If block or ocelot on top
|
||||
if (isBlockedChest(world, block.getX(), block.getY() + 1, block.getZ())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int id = world.getTypeId(block.getX(), block.getY(), block.getZ());
|
||||
|
||||
// If block next to chest is chest and has a block or ocelot on top
|
||||
if (world.getTypeId(block.getX(), block.getY(), block.getZ() + 1) == id) {
|
||||
return isBlockedChest(world, block.getX(), block.getY() + 1, block.getZ() + 1);
|
||||
} else if(world.getTypeId(block.getX(), block.getY(), block.getZ() - 1) == id) {
|
||||
return isBlockedChest(world, block.getX(), block.getY() + 1, block.getZ() - 1);
|
||||
} else if (world.getTypeId(block.getX() + 1, block.getY(), block.getZ()) == id) {
|
||||
return isBlockedChest(world, block.getX() + 1, block.getY() + 1, block.getZ());
|
||||
} else if (world.getTypeId(block.getX() - 1, block.getY(), block.getZ()) == id) {
|
||||
return isBlockedChest(world, block.getX() - 1, block.getY() + 1, block.getZ());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isBlockedChest(World world, int x, int y, int z) {
|
||||
return world.t(x, y + 1, z) || hasOcelotOnTop(world, x, y, z);
|
||||
}
|
||||
|
||||
private boolean hasOcelotOnTop(World world, int x, int y, int z) {
|
||||
for (Object localEntity : world.a(EntityOcelot.class,
|
||||
AxisAlignedBB.a(x, y + 1, z, x + 1, y + 2, z + 1))) {
|
||||
EntityOcelot localEntityOcelot = (EntityOcelot) localEntity;
|
||||
if (localEntityOcelot.isSitting()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activateContainer(Player p, boolean silentchest, org.bukkit.block.Block block) {
|
||||
|
||||
EntityPlayer player = ((CraftPlayer) p).getHandle();
|
||||
|
||||
// Silent ender chest is API-only
|
||||
if (silentchest && block.getType() == Material.ENDER_CHEST) {
|
||||
p.openInventory(p.getEnderChest());
|
||||
return true;
|
||||
}
|
||||
|
||||
World world = player.world;
|
||||
Object tile = world.getTileEntity(block.getX(), block.getY(), block.getZ());
|
||||
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(tile instanceof IInventory)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int id = world.getTypeId(block.getX(), block.getY(), block.getZ());
|
||||
|
||||
if (world.getTypeId(block.getX(), block.getY(), block.getZ() + 1) == id) {
|
||||
tile = new InventoryLargeChest("Large chest", (IInventory) tile, (TileEntityChest) world.getTileEntity(block.getX(), block.getY(), block.getZ() + 1));
|
||||
} else if (world.getTypeId(block.getX(), block.getY(), block.getZ() - 1) == id) {
|
||||
tile = new InventoryLargeChest("Large chest", (TileEntityChest) world.getTileEntity(block.getX(), block.getY(), block.getZ() - 1), (IInventory) tile);
|
||||
} else if (world.getTypeId(block.getX() + 1, block.getY(), block.getZ()) == id) {
|
||||
tile = new InventoryLargeChest("Large chest", (IInventory) tile, (TileEntityChest) world.getTileEntity(block.getX() + 1, block.getY(), block.getZ()));
|
||||
} else if (world.getTypeId(block.getX() - 1, block.getY(), block.getZ()) == id) {
|
||||
tile = new InventoryLargeChest("Large chest", (TileEntityChest) world.getTileEntity(block.getX() - 1, block.getY(), block.getZ()), (IInventory) tile);
|
||||
}
|
||||
|
||||
boolean returnValue = false;
|
||||
if (!silentchest) {
|
||||
player.openContainer((IInventory) tile);
|
||||
returnValue = true;
|
||||
} else {
|
||||
try {
|
||||
int windowId = 0;
|
||||
try {
|
||||
Field windowID = player.getClass().getDeclaredField("containerCounter");
|
||||
windowID.setAccessible(true);
|
||||
windowId = windowID.getInt(player);
|
||||
windowId = windowId % 100 + 1;
|
||||
windowID.setInt(player, windowId);
|
||||
} catch (NoSuchFieldException e) {}
|
||||
|
||||
player.playerConnection.sendPacket(new Packet100OpenWindow(windowId, 0, ((IInventory) tile).getName(), ((IInventory) tile).getSize()));
|
||||
player.activeContainer = new SilentContainerChest(player.inventory, ((IInventory) tile));
|
||||
player.activeContainer.windowId = windowId;
|
||||
player.activeContainer.addSlotListener(player);
|
||||
returnValue = true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
p.sendMessage(ChatColor.RED + "Error while sending silent chest.");
|
||||
}
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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_4_6;
|
||||
|
||||
import com.lishid.openinv.internal.IInventoryAccess;
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.internal.InternalAccessor;
|
||||
|
||||
import org.bukkit.inventory.Inventory;
|
||||
|
||||
// Volatile
|
||||
import net.minecraft.server.v1_4_6.IInventory;
|
||||
|
||||
import org.bukkit.craftbukkit.v1_4_6.inventory.CraftInventory;
|
||||
|
||||
public class InventoryAccess implements IInventoryAccess {
|
||||
|
||||
@Override
|
||||
public boolean isSpecialPlayerInventory(Inventory inventory) {
|
||||
if (inventory instanceof CraftInventory) {
|
||||
return ((CraftInventory) inventory).getInventory() instanceof ISpecialPlayerInventory;
|
||||
}
|
||||
return InternalAccessor.grabFieldOfTypeFromObject(IInventory.class, inventory) instanceof ISpecialPlayerInventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISpecialPlayerInventory getSpecialPlayerInventory(Inventory inventory) {
|
||||
IInventory inv;
|
||||
if (inventory instanceof CraftInventory) {
|
||||
inv = ((CraftInventory) inventory).getInventory();
|
||||
} else {
|
||||
inv = InternalAccessor.grabFieldOfTypeFromObject(IInventory.class, inventory);
|
||||
}
|
||||
|
||||
if (inv instanceof SpecialPlayerInventory) {
|
||||
return (SpecialPlayerInventory) inv;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSpecialEnderChest(Inventory inventory) {
|
||||
if (inventory instanceof CraftInventory) {
|
||||
return ((CraftInventory) inventory).getInventory() instanceof ISpecialEnderChest;
|
||||
}
|
||||
return InternalAccessor.grabFieldOfTypeFromObject(IInventory.class, inventory) instanceof ISpecialEnderChest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISpecialEnderChest getSpecialEnderChest(Inventory inventory) {
|
||||
IInventory inv;
|
||||
if (inventory instanceof CraftInventory) {
|
||||
inv = ((CraftInventory) inventory).getInventory();
|
||||
} else {
|
||||
inv = InternalAccessor.grabFieldOfTypeFromObject(IInventory.class, inventory);
|
||||
}
|
||||
|
||||
if (inv instanceof SpecialEnderChest) {
|
||||
return (SpecialEnderChest) inv;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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_4_6;
|
||||
|
||||
import com.lishid.openinv.internal.IPlayerDataManager;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
// Volatile
|
||||
import net.minecraft.server.v1_4_6.EntityPlayer;
|
||||
import net.minecraft.server.v1_4_6.MinecraftServer;
|
||||
import net.minecraft.server.v1_4_6.PlayerInteractManager;
|
||||
|
||||
import org.bukkit.craftbukkit.v1_4_6.CraftServer;
|
||||
|
||||
public class PlayerDataManager implements IPlayerDataManager {
|
||||
|
||||
@Override
|
||||
public Player loadPlayer(OfflinePlayer offline) {
|
||||
// Ensure the player has data
|
||||
if (offline == null || !offline.hasPlayedBefore()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
|
||||
// Create an entity to load the player data
|
||||
EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(0), offline.getName(),
|
||||
new PlayerInteractManager(server.getWorldServer(0)));
|
||||
|
||||
// Get the bukkit entity
|
||||
Player target = (entity == null) ? null : entity.getBukkitEntity();
|
||||
if (target != null) {
|
||||
// Load data
|
||||
target.loadData();
|
||||
}
|
||||
// Return the entity
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayerDataID(OfflinePlayer player) {
|
||||
return player.getName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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_4_6;
|
||||
|
||||
// Volatile
|
||||
import net.minecraft.server.v1_4_6.ContainerChest;
|
||||
import net.minecraft.server.v1_4_6.EntityHuman;
|
||||
import net.minecraft.server.v1_4_6.IInventory;
|
||||
import net.minecraft.server.v1_4_6.ItemStack;
|
||||
import net.minecraft.server.v1_4_6.PlayerInventory;
|
||||
|
||||
public class SilentContainerChest extends ContainerChest {
|
||||
|
||||
public SilentContainerChest(IInventory i1, IInventory i2) {
|
||||
super(i1, i2);
|
||||
// Send close signal
|
||||
i2.f();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void b(EntityHuman entityHuman) {
|
||||
// Don't send close signal twice, might screw up
|
||||
PlayerInventory playerinventory = entityHuman.inventory;
|
||||
|
||||
if (playerinventory.getCarried() != null) {
|
||||
ItemStack carried = playerinventory.getCarried();
|
||||
playerinventory.setCarried(null);
|
||||
entityHuman.drop(carried);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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_4_6;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
|
||||
// Volatile
|
||||
import net.minecraft.server.v1_4_6.IInventory;
|
||||
import net.minecraft.server.v1_4_6.InventoryEnderChest;
|
||||
import net.minecraft.server.v1_4_6.InventorySubcontainer;
|
||||
import net.minecraft.server.v1_4_6.ItemStack;
|
||||
|
||||
import org.bukkit.craftbukkit.v1_4_6.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_4_6.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_4_6.inventory.CraftInventory;
|
||||
|
||||
public class SpecialEnderChest extends InventorySubcontainer implements IInventory, ISpecialEnderChest {
|
||||
|
||||
private final InventoryEnderChest enderChest;
|
||||
private final CraftInventory inventory = new CraftInventory(this);
|
||||
private final List<HumanEntity> transaction = new ArrayList<HumanEntity>();
|
||||
private boolean playerOnline = false;
|
||||
private CraftPlayer owner;
|
||||
private int maxStack = MAX_STACK;
|
||||
|
||||
public SpecialEnderChest(Player p, Boolean online) {
|
||||
super(((CraftPlayer) p).getHandle().getEnderChest().getName(),
|
||||
((CraftPlayer) p).getHandle().getEnderChest().getSize());
|
||||
CraftPlayer player = (CraftPlayer) p;
|
||||
this.enderChest = player.getHandle().getEnderChest();
|
||||
this.owner = player;
|
||||
this.items = enderChest.getContents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory getBukkitInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(Player player) {
|
||||
if (!playerOnline) {
|
||||
try {
|
||||
owner = (CraftPlayer) player;
|
||||
InventoryEnderChest playerEnderChest = owner.getHandle().getEnderChest();
|
||||
Field field = playerEnderChest.getClass().getField("items");
|
||||
field.setAccessible(true);
|
||||
field.set(playerEnderChest, this.items);
|
||||
} catch (Exception e) {}
|
||||
playerOnline = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {
|
||||
playerOnline = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUse() {
|
||||
return !this.getViewers().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getContents() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity who) {
|
||||
transaction.add(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity who) {
|
||||
transaction.remove(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryHolder getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int size) {
|
||||
maxStack = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return maxStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
super.update();
|
||||
enderChest.update();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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_4_6;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
|
||||
// Volatile
|
||||
import net.minecraft.server.v1_4_6.ItemStack;
|
||||
import net.minecraft.server.v1_4_6.PlayerInventory;
|
||||
|
||||
import org.bukkit.craftbukkit.v1_4_6.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_4_6.inventory.CraftInventory;
|
||||
|
||||
public class SpecialPlayerInventory extends PlayerInventory implements ISpecialPlayerInventory {
|
||||
|
||||
private final ItemStack[] extra = new ItemStack[5];
|
||||
private final CraftInventory inventory = new CraftInventory(this);
|
||||
private boolean playerOnline = false;
|
||||
|
||||
public SpecialPlayerInventory(Player bukkitPlayer, Boolean online) {
|
||||
super(((CraftPlayer) bukkitPlayer).getHandle());
|
||||
this.playerOnline = online;
|
||||
this.items = player.inventory.items;
|
||||
this.armor = player.inventory.armor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory getBukkitInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(Player player) {
|
||||
if (!playerOnline) {
|
||||
this.player = ((CraftPlayer) player).getHandle();
|
||||
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].a(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);
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,51 +7,15 @@ OpenInv is a [Bukkit plugin](https://dev.bukkit.org/bukkit-plugins/openinv/) whi
|
||||
- Cross-world support! Don't grant `OpenInv.crossworld`
|
||||
- No self-opening! Don't grant `OpenInv.openself`
|
||||
- Drop items as the player! Place items in the unused slots to the right of the armor to drop them
|
||||
- **OpenEnder**: Open anyone's inventory, even if they're offline.
|
||||
- **OpenEnder**: Open anyone's ender chest, even if they're offline.
|
||||
- Read-only mode! No edits allowed! Don't grant `OpenInv.editender`
|
||||
- Cross-world support! Don't grant `OpenInv.crossworld`
|
||||
- No self-opening! Don't grant `OpenInv.openself`
|
||||
- **SilentChest**: Open containers without displaying an animation or making sound.
|
||||
- **AnyChest**: Open containers, even if blocked by ocelots or blocks.
|
||||
- No opening others! Don't grant `OpenInv.openenderall`
|
||||
- **SilentContainer**: Open containers without displaying an animation or making sound.
|
||||
- **AnyContainer**: Open containers, even if blocked by ocelots or blocks.
|
||||
|
||||
## Commands
|
||||
<table width=100%>
|
||||
<tr>
|
||||
<th width=175px>Command</th>
|
||||
<th>Aliases</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>/openinv [player]</td>
|
||||
<td>oi, inv, open</td>
|
||||
<td>Open a player's inventory. If unspecified, will select last player opened or own if none opened previously.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>/openender [player]</td>
|
||||
<td>oe</td>
|
||||
<td>Open a player's ender chest. If unspecified, will select last player opened or own if none opened previously.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>/searchinv <item> [minAmount]</td>
|
||||
<td>si</td>
|
||||
<td>Lists all online players that have a certain item in their inventory.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>/searchender <item> [minAmount]</td>
|
||||
<td>se</td>
|
||||
<td>Lists all online players that have a certain item in their ender chest.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>/anychest [check]</td>
|
||||
<td>ac</td>
|
||||
<td>Check or toggle the AnyChest function, allowing opening blocked containers.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>/silentchest [check]</td>
|
||||
<td>sc</td>
|
||||
<td>Check or toggle the SilentChest function, allowing opening containers silently.</td>
|
||||
</tr>
|
||||
</table>
|
||||
See [the wiki](https://github.com/Jikoo/OpenInv/wiki/Commands).
|
||||
|
||||
## Permissions
|
||||
<table>
|
||||
@@ -75,12 +39,20 @@ OpenInv is a [Bukkit plugin](https://dev.bukkit.org/bukkit-plugins/openinv/) whi
|
||||
<td>OpenInv.editinv</td>
|
||||
<td>Required to make changes to open inventories.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.openonline</td>
|
||||
<td>Allows users to open online players' inventories. For compatibility reasons this is granted by the nodes OpenInv.openinv and OpenInv.openender.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.openoffline</td>
|
||||
<td>Allows users to open offline players' inventories. For compatibility reasons this is granted by the nodes OpenInv.openinv and OpenInv.openender.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.openender</td>
|
||||
<td>Required to use /openender.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.editinv</td>
|
||||
<td>OpenInv.editender</td>
|
||||
<td>Required to make changes to open ender chests.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -103,37 +75,71 @@ OpenInv is a [Bukkit plugin](https://dev.bukkit.org/bukkit-plugins/openinv/) whi
|
||||
<td>OpenInv.search</td>
|
||||
<td>Required to use /searchinv and /searchender.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.searchenchant</td>
|
||||
<td>Required to use /searchenchant.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.anychest</td>
|
||||
<td>Required to use /anychest.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.any.default</td>
|
||||
<td>Cause AnyContainer to be enabled by default.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.silent</td>
|
||||
<td>Required to use /silentchest.</td>
|
||||
<td>Required to use /silentcontainer.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.silent.default</td>
|
||||
<td>Cause SilentContainer to be enabled by default.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OpenInv.spectate</td>
|
||||
<td>Allows users in spectate gamemode to edit inventories.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## For Developers
|
||||
To compile, the relevant Craftbukkit/Spigot jars must be installed in your local repository using the install plugin.
|
||||
Ex: `mvn install:install-file -Dpackaging=jar -Dfile=spigot-1.11-R0.1-SNAPSHOT.jar -DgroupId=org.spigotmc -DartifactId=spigot -Dversion=1.11-R0.1-SNAPSHOT`
|
||||
|
||||
Compiling OpenInv for a specific version is very easy - just compile the correct module.
|
||||
|
||||
Compiling for a set of versions is slightly more complex. You'll need to use a profile for the versions you want to compile. Provided profiles are latest, modern (versions 1.8+), and all. For more information, check out the [official guide](http://maven.apache.org/guides/introduction/introduction-to-profiles.html).
|
||||
|
||||
## License
|
||||
### As a Dependency
|
||||
The OpenInv API is available via [JitPack](https://jitpack.io/).
|
||||
```xml
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
```
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.jikoo.OpenInv</groupId>
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<version>${openinv.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
Copyright (C) 2011-2014 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.
|
||||
### Compilation
|
||||
To compile, the relevant Spigot jars must be installed in your local repository using the `install` plugin:
|
||||
```shell
|
||||
mvn install:install-file -Dpackaging=jar -Dfile=spigot-1.8-R0.1-SNAPSHOT.jar \
|
||||
-DgroupId=org.spigotmc -DartifactId=spigot -Dversion=1.8-R0.1-SNAPSHOT
|
||||
```
|
||||
Note that BuildTools automatically installs produced files. If you use BuildTools to compile Spigot locally, you don't need to install it manually.
|
||||
If you want to use Paper as a dependency, you can install it by executing PaperClip with the property `paperclip.install` set to true:
|
||||
```shell
|
||||
wget -O paperclip.jar https://papermc.io/api/v1/paper/1.16.5/latest/download
|
||||
java -jar -Dpaperclip.install=true paperclip.jar
|
||||
```
|
||||
|
||||
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.
|
||||
To compile for a single version, specify the NMS revision you are targeting: `mvn -pl <NMS module> -am clean install`
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
```
|
||||
To compile for a set of versions, you'll need to use a profile. The only provided profile is `all`. Select a profile using the `-P` argument: `mvn clean package -am -P all`
|
||||
|
||||
For more information, check out the [official Maven guide](http://maven.apache.org/guides/introduction/introduction-to-profiles.html).
|
||||
|
||||
The final file is `target/OpenInv.jar`
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<!--
|
||||
~ Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<version>4.1.9-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<name>OpenInvAPI</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<artifactId>annotations</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.lishid.openinv.internal.IAnySilentContainer;
|
||||
import com.lishid.openinv.internal.IInventoryAccess;
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.util.InventoryAccess;
|
||||
import com.lishid.openinv.util.StringMetric;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Interface defining behavior for the OpenInv plugin.
|
||||
*
|
||||
* @author Jikoo
|
||||
*/
|
||||
public interface IOpenInv {
|
||||
|
||||
/**
|
||||
* Check the configuration value for whether or not OpenInv saves player data when unloading
|
||||
* players. This is exclusively for users who do not allow editing of inventories, only viewing,
|
||||
* and wish to prevent any possibility of bugs such as lishid#40. If true, OpenInv will not ever
|
||||
* save any edits made to players.
|
||||
*
|
||||
* @return false unless configured otherwise
|
||||
*/
|
||||
boolean disableSaving();
|
||||
|
||||
/**
|
||||
* Gets the active ISilentContainer implementation.
|
||||
*
|
||||
* @return the ISilentContainer
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
*/
|
||||
@NotNull IAnySilentContainer getAnySilentContainer();
|
||||
|
||||
/**
|
||||
* Gets the active IInventoryAccess implementation.
|
||||
*
|
||||
* @return the IInventoryAccess
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
*/
|
||||
@Deprecated
|
||||
default @NotNull IInventoryAccess getInventoryAccess() {
|
||||
return new InventoryAccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the provided player's AnyChest setting.
|
||||
*
|
||||
* @param player the OfflinePlayer
|
||||
* @return true if AnyChest is enabled
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
*/
|
||||
boolean getPlayerAnyChestStatus(@NotNull OfflinePlayer player);
|
||||
|
||||
/**
|
||||
* Gets a unique identifier by which the OfflinePlayer can be referenced. Using the value
|
||||
* returned to look up a Player will generally be much faster for later implementations.
|
||||
*
|
||||
* @param offline the OfflinePlayer
|
||||
* @return the identifier
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
*/
|
||||
default @NotNull String getPlayerID(@NotNull OfflinePlayer offline) {
|
||||
return offline.getUniqueId().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a player's SilentChest setting.
|
||||
*
|
||||
* @param offline the OfflinePlayer
|
||||
* @return true if SilentChest is enabled
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
*/
|
||||
boolean getPlayerSilentChestStatus(@NotNull OfflinePlayer offline);
|
||||
|
||||
/**
|
||||
* Gets an ISpecialEnderChest for the given Player.
|
||||
*
|
||||
* @param player the Player
|
||||
* @param online true if the Player is currently online
|
||||
* @return the ISpecialEnderChest
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
* @throws InstantiationException if the ISpecialEnderChest could not be instantiated
|
||||
*/
|
||||
@NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull Player player, boolean online) throws InstantiationException;
|
||||
|
||||
/**
|
||||
* Gets an ISpecialPlayerInventory for the given Player.
|
||||
*
|
||||
* @param player the Player
|
||||
* @param online true if the Player is currently online
|
||||
* @return the ISpecialPlayerInventory
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
* @throws InstantiationException if the ISpecialPlayerInventory could not be instantiated
|
||||
*/
|
||||
@NotNull ISpecialPlayerInventory getSpecialInventory(@NotNull Player player, boolean online) throws InstantiationException;
|
||||
|
||||
/**
|
||||
* Checks if the server version is supported by OpenInv.
|
||||
*
|
||||
* @return true if the server version is supported
|
||||
*/
|
||||
boolean isSupportedVersion();
|
||||
|
||||
/**
|
||||
* Load a Player from an OfflinePlayer. May return null under some circumstances.
|
||||
*
|
||||
* @param offline the OfflinePlayer to load a Player for
|
||||
* @return the Player, or null
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
*/
|
||||
@Nullable Player loadPlayer(@NotNull final OfflinePlayer offline);
|
||||
|
||||
/**
|
||||
* Get an OfflinePlayer by name.
|
||||
* <p>
|
||||
* Note: This method is potentially very heavily blocking. It should not ever be called on the
|
||||
* main thread, and if it is, a stack trace will be displayed alerting server owners to the
|
||||
* call.
|
||||
*
|
||||
* @param name the name of the Player
|
||||
* @return the OfflinePlayer with the closest matching name or null if no players have ever logged in
|
||||
*/
|
||||
default @Nullable OfflinePlayer matchPlayer(@NotNull String name) {
|
||||
|
||||
// Warn if called on the main thread - if we resort to searching offline players, this may take several seconds.
|
||||
if (Bukkit.getServer().isPrimaryThread()) {
|
||||
this.getLogger().warning("Call to OpenInv#matchPlayer made on the main thread!");
|
||||
this.getLogger().warning("This can cause the server to hang, potentially severely.");
|
||||
this.getLogger().log(Level.WARNING, new Throwable("Current stack trace"), () -> "Current stack trace");
|
||||
}
|
||||
|
||||
OfflinePlayer player;
|
||||
|
||||
try {
|
||||
UUID uuid = UUID.fromString(name);
|
||||
player = Bukkit.getOfflinePlayer(uuid);
|
||||
// Ensure player is a real player, otherwise return null
|
||||
if (player.hasPlayedBefore() || player.isOnline()) {
|
||||
return player;
|
||||
}
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
// Not a UUID
|
||||
}
|
||||
|
||||
player = Bukkit.getServer().getPlayerExact(name);
|
||||
|
||||
if (player != null) {
|
||||
return player;
|
||||
}
|
||||
|
||||
player = Bukkit.getServer().getOfflinePlayer(name);
|
||||
|
||||
if (player.hasPlayedBefore()) {
|
||||
return player;
|
||||
}
|
||||
|
||||
player = Bukkit.getServer().getPlayer(name);
|
||||
|
||||
if (player != null) {
|
||||
return player;
|
||||
}
|
||||
|
||||
float bestMatch = 0;
|
||||
for (OfflinePlayer offline : Bukkit.getServer().getOfflinePlayers()) {
|
||||
if (offline.getName() == null) {
|
||||
// Loaded by UUID only, name has never been looked up.
|
||||
continue;
|
||||
}
|
||||
|
||||
float currentMatch = StringMetric.compareJaroWinkler(name, offline.getName());
|
||||
|
||||
if (currentMatch == 1.0F) {
|
||||
return offline;
|
||||
}
|
||||
|
||||
if (currentMatch > bestMatch) {
|
||||
bestMatch = currentMatch;
|
||||
player = offline;
|
||||
}
|
||||
}
|
||||
|
||||
// Only null if no players have played ever, otherwise even the worst match will do.
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an ISpecialInventory for a Player.
|
||||
*
|
||||
* @param player the Player
|
||||
* @param inventory the ISpecialInventory
|
||||
* @return the InventoryView for the opened ISpecialInventory
|
||||
*/
|
||||
@Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory);
|
||||
|
||||
/**
|
||||
* Check the configuration value for whether or not OpenInv displays a notification to the user
|
||||
* when a container is activated with AnyChest.
|
||||
*
|
||||
* @return true unless configured otherwise
|
||||
* @deprecated OpenInv uses action bar chat for notifications. Whether or not they show is based on language settings.
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean notifyAnyChest() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the configuration value for whether or not OpenInv displays a notification to the user
|
||||
* when a container is activated with SilentChest.
|
||||
*
|
||||
* @return true unless configured otherwise
|
||||
* @deprecated OpenInv uses action bar chat for notifications. Whether or not they show is based on language settings.
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean notifySilentChest() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a Player as no longer in use by a Plugin to allow OpenInv to remove it from the cache
|
||||
* when eligible.
|
||||
*
|
||||
* @param player the Player
|
||||
* @param plugin the Plugin no longer holding a reference to the Player
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
*/
|
||||
void releasePlayer(@NotNull Player player, @NotNull Plugin plugin);
|
||||
|
||||
/**
|
||||
* Mark a Player as in use by a Plugin to prevent it from being removed from the cache. Used to
|
||||
* prevent issues with multiple copies of the same Player being loaded such as lishid#49.
|
||||
* Changes made to loaded copies overwrite changes to the others when saved, leading to
|
||||
* duplication bugs and more.
|
||||
* <p>
|
||||
* When finished with the Player object, be sure to call {@link #releasePlayer(Player, Plugin)}
|
||||
* to prevent the cache from keeping it stored until the plugin is disabled.
|
||||
* <p>
|
||||
* When using a Player object from OpenInv, you must handle the Player coming online, replacing
|
||||
* your Player reference with the Player from the PlayerJoinEvent. In addition, you must change
|
||||
* any values in the Player to reflect any unsaved alterations to the existing Player which do
|
||||
* not affect the inventory or ender chest contents.
|
||||
* <p>
|
||||
* OpenInv only saves player data when unloading a Player from the cache, and then only if
|
||||
* {@link #disableSaving()} returns false. If you are making changes that OpenInv does not cause
|
||||
* to persist when a Player logs in as noted above, it is suggested that you manually call
|
||||
* {@link Player#saveData()} when releasing your reference to ensure your changes persist.
|
||||
*
|
||||
* @param player the Player
|
||||
* @param plugin the Plugin holding the reference to the Player
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
*/
|
||||
void retainPlayer(@NotNull Player player, @NotNull Plugin plugin);
|
||||
|
||||
/**
|
||||
* Sets a player's AnyChest setting.
|
||||
*
|
||||
* @param offline the OfflinePlayer
|
||||
* @param status the status
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
*/
|
||||
void setPlayerAnyChestStatus(@NotNull OfflinePlayer offline, boolean status);
|
||||
|
||||
/**
|
||||
* Sets a player's SilentChest setting.
|
||||
*
|
||||
* @param offline the OfflinePlayer
|
||||
* @param status the status
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
*/
|
||||
void setPlayerSilentChestStatus(@NotNull OfflinePlayer offline, boolean status);
|
||||
|
||||
/**
|
||||
* Forcibly unload a cached Player's data.
|
||||
*
|
||||
* @param offline the OfflinePlayer to unload
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
*/
|
||||
void unload(@NotNull OfflinePlayer offline);
|
||||
|
||||
Logger getLogger();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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 org.bukkit.Material;
|
||||
import org.bukkit.block.Barrel;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.EnderChest;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.Chest;
|
||||
import org.bukkit.entity.Cat;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface IAnySilentContainer {
|
||||
|
||||
/**
|
||||
* Opens the container at the given coordinates for the Player. If you do not want blocked
|
||||
* containers to open, be sure to check {@link #isAnyContainerNeeded(Player, Block)}
|
||||
* first.
|
||||
*
|
||||
* @param player the Player opening the container
|
||||
* @param silent whether the container's noise is to be silenced
|
||||
* @param block the Block
|
||||
* @return true if the container can be opened
|
||||
*/
|
||||
boolean activateContainer(@NotNull Player player, boolean silent, @NotNull Block block);
|
||||
|
||||
/**
|
||||
* Closes the Player's currently open container silently, if necessary.
|
||||
*
|
||||
* @param player the Player closing a container
|
||||
*/
|
||||
void deactivateContainer(@NotNull Player player);
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #isAnyContainerNeeded(Block)}
|
||||
* @param player the player opening the container
|
||||
* @param block the block
|
||||
* @return true if the container is blocked
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean isAnyContainerNeeded(@NotNull Player player, @NotNull Block block) {
|
||||
return isAnyContainerNeeded(block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the container at the given coordinates is blocked.
|
||||
*
|
||||
* @param block the Block
|
||||
* @return true if the container is blocked
|
||||
*/
|
||||
default boolean isAnyContainerNeeded(@NotNull Block block) {
|
||||
BlockState blockState = block.getState();
|
||||
|
||||
// Barrels do not require AnyContainer.
|
||||
if (blockState instanceof Barrel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enderchests require a non-occluding block on top to open.
|
||||
if (blockState instanceof EnderChest) {
|
||||
return block.getRelative(0, 1, 0).getType().isOccluding();
|
||||
}
|
||||
|
||||
// Shulker boxes require 1/2 a block clear in the direction they open.
|
||||
if (blockState instanceof ShulkerBox) {
|
||||
if (isShulkerBlocked((ShulkerBox) blockState)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(blockState instanceof org.bukkit.block.Chest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isChestBlocked(block)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BlockData blockData = block.getBlockData();
|
||||
if (!(blockData instanceof Chest) || ((Chest) blockData).getType() == Chest.Type.SINGLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Chest chest = (Chest) blockData;
|
||||
int ordinal = (chest.getFacing().ordinal() + 4 + (chest.getType() == Chest.Type.RIGHT ? -1 : 1)) % 4;
|
||||
BlockFace relativeFace = BlockFace.values()[ordinal];
|
||||
org.bukkit.block.Block relative = block.getRelative(relativeFace);
|
||||
|
||||
if (relative.getType() != block.getType()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockData relativeData = relative.getBlockData();
|
||||
if (!(relativeData instanceof Chest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Chest relativeChest = (Chest) relativeData;
|
||||
if (relativeChest.getFacing() != chest.getFacing()
|
||||
|| relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isChestBlocked(relative);
|
||||
}
|
||||
|
||||
boolean isShulkerBlocked(ShulkerBox block);
|
||||
|
||||
/**
|
||||
* Determine whether or not a chest is blocked.
|
||||
*
|
||||
* @param chest the chest block
|
||||
* @return true if the chest block cannot be opened under ordinary circumstances
|
||||
*/
|
||||
default boolean isChestBlocked(Block chest) {
|
||||
org.bukkit.block.Block relative = chest.getRelative(0, 1, 0);
|
||||
return relative.getType().isOccluding()
|
||||
|| chest.getWorld().getNearbyEntities(BoundingBox.of(relative), entity -> entity instanceof Cat).size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given block is a container which can be unblocked or silenced.
|
||||
*
|
||||
* @param block the BlockState
|
||||
* @return true if the Block is a supported container
|
||||
*/
|
||||
default boolean isAnySilentContainer(@NotNull Block block) {
|
||||
if (block.getType() == Material.ENDER_CHEST) {
|
||||
return true;
|
||||
}
|
||||
BlockState state = block.getState();
|
||||
return state instanceof org.bukkit.block.Chest
|
||||
|| state instanceof org.bukkit.block.ShulkerBox
|
||||
|| state instanceof org.bukkit.block.Barrel;
|
||||
}
|
||||
|
||||
}
|
||||
+40
-33
@@ -1,15 +1,15 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 lishid. All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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
|
||||
* 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/>.
|
||||
*/
|
||||
@@ -17,41 +17,48 @@
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@Deprecated
|
||||
public interface IInventoryAccess {
|
||||
|
||||
/**
|
||||
* Check if an Inventory is an ISpecialPlayerInventory implementation.
|
||||
*
|
||||
* @param inventory the Inventory
|
||||
* @return true if the Inventory is backed by an ISpecialPlayerInventory
|
||||
*/
|
||||
public boolean isSpecialPlayerInventory(Inventory inventory);
|
||||
|
||||
/**
|
||||
* Gets an ISpecialPlayerInventory from an Inventory or null if the Inventory is not backed by
|
||||
* an ISpecialPlayerInventory.
|
||||
*
|
||||
* @param inventory the Inventory
|
||||
* @return the ISpecialPlayerInventory or null
|
||||
*/
|
||||
public ISpecialPlayerInventory getSpecialPlayerInventory(Inventory inventory);
|
||||
|
||||
/**
|
||||
* Check if an Inventory is an ISpecialEnderChest implementation.
|
||||
*
|
||||
* @param inventory the Inventory
|
||||
* @return true if the Inventory is backed by an ISpecialEnderChest
|
||||
*/
|
||||
public boolean isSpecialEnderChest(Inventory inventory);
|
||||
|
||||
/**
|
||||
* Gets an ISpecialEnderChest from an Inventory or null if the Inventory is not backed by an
|
||||
* ISpecialEnderChest.
|
||||
*
|
||||
*
|
||||
* @param inventory the Inventory
|
||||
* @return the ISpecialEnderChest or null
|
||||
*/
|
||||
public ISpecialEnderChest getSpecialEnderChest(Inventory inventory);
|
||||
@Deprecated
|
||||
@Nullable ISpecialEnderChest getSpecialEnderChest(@NotNull Inventory inventory);
|
||||
|
||||
/**
|
||||
* Gets an ISpecialPlayerInventory from an Inventory or null if the Inventory is not backed by
|
||||
* an ISpecialPlayerInventory.
|
||||
*
|
||||
* @param inventory the Inventory
|
||||
* @return the ISpecialPlayerInventory or null
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable ISpecialPlayerInventory getSpecialPlayerInventory(@NotNull Inventory inventory);
|
||||
|
||||
/**
|
||||
* Check if an Inventory is an ISpecialEnderChest implementation.
|
||||
*
|
||||
* @param inventory the Inventory
|
||||
* @return true if the Inventory is backed by an ISpecialEnderChest
|
||||
*/
|
||||
@Deprecated
|
||||
boolean isSpecialEnderChest(@NotNull Inventory inventory);
|
||||
|
||||
/**
|
||||
* Check if an Inventory is an ISpecialPlayerInventory implementation.
|
||||
*
|
||||
* @param inventory the Inventory
|
||||
* @return true if the Inventory is backed by an ISpecialPlayerInventory
|
||||
*/
|
||||
@Deprecated
|
||||
boolean isSpecialPlayerInventory(@NotNull Inventory inventory);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public interface ISpecialEnderChest extends ISpecialInventory {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface ISpecialInventory {
|
||||
|
||||
/**
|
||||
* Gets the Inventory associated with this ISpecialInventory.
|
||||
*
|
||||
* @return the Inventory
|
||||
*/
|
||||
@NotNull Inventory getBukkitInventory();
|
||||
|
||||
/**
|
||||
* Sets the Player associated with this ISpecialInventory online.
|
||||
*
|
||||
* @param player the Player coming online
|
||||
*/
|
||||
void setPlayerOnline(@NotNull Player player);
|
||||
|
||||
/**
|
||||
* Sets the Player associated with this ISpecialInventory offline.
|
||||
*/
|
||||
void setPlayerOffline();
|
||||
|
||||
/**
|
||||
* Gets whether or not this ISpecialInventory is in use.
|
||||
*
|
||||
* @return true if the ISpecialInventory is in use
|
||||
*/
|
||||
boolean isInUse();
|
||||
|
||||
/**
|
||||
* Gets the Player associated with this ISpecialInventory.
|
||||
*
|
||||
* @return the HumanEntity
|
||||
*/
|
||||
@NotNull HumanEntity getPlayer();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public interface ISpecialPlayerInventory extends ISpecialInventory {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import 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.Method;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class InventoryAccess implements IInventoryAccess {
|
||||
|
||||
private static Class<?> craftInventory = null;
|
||||
private static Method getInventory = null;
|
||||
|
||||
static {
|
||||
String packageName = Bukkit.getServer().getClass().getPackage().getName();
|
||||
try {
|
||||
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) {
|
||||
return getPlayerInventory(inventory) != null;
|
||||
}
|
||||
|
||||
public static @Nullable ISpecialPlayerInventory getPlayerInventory(@NotNull Inventory inventory) {
|
||||
return getSpecialInventory(ISpecialPlayerInventory.class, inventory);
|
||||
}
|
||||
|
||||
public static boolean isEnderChest(@NotNull Inventory inventory) {
|
||||
return getEnderChest(inventory) != null;
|
||||
}
|
||||
|
||||
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) {}
|
||||
}
|
||||
|
||||
// Use reflection to find the IInventory
|
||||
inv = ReflectionHelper.grabObjectByType(inventory, expected);
|
||||
|
||||
if (expected.isInstance(inv)) {
|
||||
return expected.cast(inv);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public @Nullable ISpecialEnderChest getSpecialEnderChest(@NotNull Inventory inventory) {
|
||||
return getEnderChest(inventory);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public @Nullable ISpecialPlayerInventory getSpecialPlayerInventory(@NotNull Inventory inventory) {
|
||||
return getPlayerInventory(inventory);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean isSpecialEnderChest(@NotNull Inventory inventory) {
|
||||
return isEnderChest(inventory);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean isSpecialPlayerInventory(@NotNull Inventory inventory) {
|
||||
return isPlayerInventory(inventory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A utility for making reflection easier.
|
||||
*/
|
||||
public final class ReflectionHelper {
|
||||
|
||||
private ReflectionHelper() {}
|
||||
|
||||
/**
|
||||
* Grab an {@link Object} stored in a {@link Field} of another {@code Object}.
|
||||
*
|
||||
* <p>This casts the field to the correct class. Any issues will result in a {@code null} return value.
|
||||
*
|
||||
* @param fieldType the {@link Class} of {@code Object} stored in the {@code Field}
|
||||
* @param holder the containing {@code Object}
|
||||
* @param <T> the type of stored {@code Object}
|
||||
* @return the first matching {@code Object} or {@code null} if none match
|
||||
*/
|
||||
public static <T> @Nullable T grabObjectByType(final Object holder, final Class<T> fieldType) {
|
||||
Field field = grabFieldByType(holder.getClass(), fieldType);
|
||||
|
||||
if (field != null) {
|
||||
try {
|
||||
return fieldType.cast(field.get(holder));
|
||||
} catch (IllegalAccessException ignored) {
|
||||
// Ignore issues obtaining field
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab a {@link Field} of an {@link Object}
|
||||
*
|
||||
* @param fieldType the {@link Class} of the object
|
||||
* @param holderType the containing {@code Class}
|
||||
* @return the first matching object or {@code null} if none match
|
||||
*/
|
||||
public static @Nullable Field grabFieldByType(Class<?> holderType, Class<?> fieldType) {
|
||||
for (Field field : holderType.getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
if (fieldType.isAssignableFrom(field.getType())) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
if (holderType.getSuperclass() != null) {
|
||||
return grabFieldByType(fieldType, holderType.getSuperclass());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
public class StringMetric {
|
||||
|
||||
public static float compareJaroWinkler(String a, String b) {
|
||||
final float jaroScore = compareJaro(a, b);
|
||||
|
||||
if (jaroScore < (float) 0.7) {
|
||||
return jaroScore;
|
||||
}
|
||||
|
||||
String prefix = commonPrefix(a, b);
|
||||
int prefixLength = Math.min(prefix.codePointCount(0, prefix.length()), 4);
|
||||
|
||||
return jaroScore + (prefixLength * (float) 0.1 * (1.0f - jaroScore));
|
||||
|
||||
}
|
||||
|
||||
private static float compareJaro(String a, String b) {
|
||||
if (a.isEmpty() && b.isEmpty()) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
if (a.isEmpty() || b.isEmpty()) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
final int[] charsA = a.codePoints().toArray();
|
||||
final int[] charsB = b.codePoints().toArray();
|
||||
|
||||
// Intentional integer division to round down.
|
||||
final int halfLength = Math.max(0, Math.max(charsA.length, charsB.length) / 2 - 1);
|
||||
|
||||
final int[] commonA = getCommonCodePoints(charsA, charsB, halfLength);
|
||||
final int[] commonB = getCommonCodePoints(charsB, charsA, halfLength);
|
||||
|
||||
// commonA and commonB will always contain the same multi-set of
|
||||
// characters. Because getCommonCharacters has been optimized, commonA
|
||||
// and commonB are -1-padded. So in this loop we count transposition
|
||||
// and use commonCharacters to determine the length of the multi-set.
|
||||
float transpositions = 0;
|
||||
int commonCharacters = 0;
|
||||
for (int length = commonA.length; commonCharacters < length
|
||||
&& commonA[commonCharacters] > -1; commonCharacters++) {
|
||||
if (commonA[commonCharacters] != commonB[commonCharacters]) {
|
||||
transpositions++;
|
||||
}
|
||||
}
|
||||
|
||||
if (commonCharacters == 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float aCommonRatio = commonCharacters / (float) charsA.length;
|
||||
float bCommonRatio = commonCharacters / (float) charsB.length;
|
||||
float transpositionRatio = (commonCharacters - transpositions / 2.0f) / commonCharacters;
|
||||
|
||||
return (aCommonRatio + bCommonRatio + transpositionRatio) / 3.0f;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns an array of code points from a within b. A character in b is
|
||||
* counted as common when it is within separation distance from the position
|
||||
* in a.
|
||||
*/
|
||||
private static int[] getCommonCodePoints(final int[] charsA, final int[] charsB, final int separation) {
|
||||
final int[] common = new int[Math.min(charsA.length, charsB.length)];
|
||||
final boolean[] matched = new boolean[charsB.length];
|
||||
|
||||
// Iterate of string a and find all characters that occur in b within
|
||||
// the separation distance. Mark any matches found to avoid
|
||||
// duplicate matchings.
|
||||
int commonIndex = 0;
|
||||
for (int i = 0, length = charsA.length; i < length; i++) {
|
||||
final int character = charsA[i];
|
||||
final int index = indexOf(character, charsB, i - separation, i
|
||||
+ separation + 1, matched);
|
||||
if (index > -1) {
|
||||
common[commonIndex++] = character;
|
||||
matched[index] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (commonIndex < common.length) {
|
||||
common[commonIndex] = -1;
|
||||
}
|
||||
|
||||
// Both invocations will yield the same multi-set terminated by -1, so
|
||||
// they can be compared for transposition without making a copy.
|
||||
return common;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for code point in buffer starting at fromIndex to toIndex - 1.
|
||||
*
|
||||
* Returns -1 when not found.
|
||||
*/
|
||||
private static int indexOf(int character, int[] buffer, int fromIndex, int toIndex, boolean[] matched) {
|
||||
|
||||
// compare char with range of characters to either side
|
||||
for (int j = Math.max(0, fromIndex), length = Math.min(toIndex, buffer.length); j < length; j++) {
|
||||
// check if found
|
||||
if (buffer[j] == character && !matched[j]) {
|
||||
return j;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static String commonPrefix(CharSequence a, CharSequence b) {
|
||||
int maxPrefixLength = Math.min(a.length(), b.length());
|
||||
|
||||
int p;
|
||||
|
||||
p = 0;
|
||||
while (p < maxPrefixLength && a.charAt(p) == b.charAt(p)) {
|
||||
++p;
|
||||
}
|
||||
|
||||
if (validSurrogatePairAt(a, p - 1) || validSurrogatePairAt(b, p - 1)) {
|
||||
--p;
|
||||
}
|
||||
|
||||
return a.subSequence(0, p).toString();
|
||||
}
|
||||
|
||||
private static boolean validSurrogatePairAt(CharSequence string, int index) {
|
||||
return index >= 0 && index <= string.length() - 2 && Character.isHighSurrogate(string.charAt(index)) && Character.isLowSurrogate(string.charAt(index + 1));
|
||||
}
|
||||
|
||||
private StringMetric(){}
|
||||
|
||||
}
|
||||
+30
-180
@@ -1,172 +1,32 @@
|
||||
<!--
|
||||
~ Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinv</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<version>4.1.9-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvassembly</artifactId>
|
||||
<name>OpenInvAssembly</name>
|
||||
|
||||
<profiles>
|
||||
|
||||
<profile>
|
||||
<id>latest</id>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_11_R1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>modern</id>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_8_R1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_8_R2</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_8_R3</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_9_R1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_9_R2</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_10_R1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_11_R1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>all</id>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_4_5</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_4_6</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_4_R1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_5_R2</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_5_R3</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_6_R1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_6_R2</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_6_R3</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_7_R1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_7_R2</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_7_R3</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_7_R4</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_8_R1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_8_R2</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_8_R3</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_9_R1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_9_R2</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_10_R1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_11_R1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<build>
|
||||
<directory>../target</directory>
|
||||
@@ -174,35 +34,25 @@
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.3</version>
|
||||
<configuration>
|
||||
<!--
|
||||
~ This keeps the final file clean, but may cause issues for people not familiar with the setup.
|
||||
~ If you're having trouble with the final product missing files, remove or tweak this configuration.
|
||||
-->
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>com.lishid:*</artifact>
|
||||
<includes>
|
||||
<include>com/lishid/openinv/**/*</include>
|
||||
<include>plugin.yml</include>
|
||||
</includes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>reactor-uberjar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<descriptors>
|
||||
<descriptor>src/assembly/reactor-uberjar.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
@@ -0,0 +1,54 @@
|
||||
<!--
|
||||
~ Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
|
||||
|
||||
<id>reactor-uberjar</id>
|
||||
|
||||
<formats>
|
||||
<format>jar</format>
|
||||
</formats>
|
||||
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
|
||||
<moduleSets>
|
||||
<moduleSet>
|
||||
|
||||
<useAllReactorProjects>true</useAllReactorProjects>
|
||||
|
||||
<binaries>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<unpack>true</unpack>
|
||||
<unpackOptions>
|
||||
<excludes>
|
||||
<!--
|
||||
- Exclude existing meta - assembly will write its own manifest, and the rest is maven
|
||||
- details that end users don't need. Ignoring it saves around 7KB after compression
|
||||
- with a single internal module present.
|
||||
-->
|
||||
<exclude>META-INF/**</exclude>
|
||||
</excludes>
|
||||
</unpackOptions>
|
||||
<!-- Exclude dependencies - shade plugin will handle their inclusion as necessary in individual jars. -->
|
||||
<includeDependencies>false</includeDependencies>
|
||||
</binaries>
|
||||
|
||||
</moduleSet>
|
||||
</moduleSets>
|
||||
|
||||
</assembly>
|
||||
@@ -1,23 +0,0 @@
|
||||
<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>openinv</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvinternal</artifactId>
|
||||
<name>OpenInvInternal</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
<version>1.4.5-R1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface IAnySilentContainer {
|
||||
|
||||
/**
|
||||
* Checks if the given block is a container which can be unblocked or silenced.
|
||||
*
|
||||
* @param block the BlockState
|
||||
* @return true if the Block is a supported container
|
||||
*/
|
||||
public boolean isAnySilentContainer(Block block);
|
||||
|
||||
/**
|
||||
* Checks if the container at the given coordinates is blocked.
|
||||
*
|
||||
* @param player the Player opening the container
|
||||
* @param block the Block
|
||||
* @return true if the container is blocked
|
||||
*/
|
||||
public boolean isAnyContainerNeeded(Player player, Block block);
|
||||
|
||||
/**
|
||||
* Opens the container at the given coordinates for the Player. If you do not want blocked
|
||||
* containers to open, be sure to check {@link #isAnyContainerNeeded(Player, Block)}
|
||||
* first.
|
||||
*
|
||||
* @param player
|
||||
* @param silentchest whether the container's noise is to be silenced
|
||||
* @param block the Block
|
||||
* @return true if the container can be opened
|
||||
*/
|
||||
public boolean activateContainer(Player player, boolean silentchest, Block block);
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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 org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface IPlayerDataManager {
|
||||
|
||||
/**
|
||||
* Loads a Player for an OfflinePlayer.
|
||||
* </p>
|
||||
* This method is potentially blocking, and should not be called on the main thread.
|
||||
*
|
||||
* @param offline
|
||||
* @return
|
||||
*/
|
||||
public Player loadPlayer(OfflinePlayer offline);
|
||||
|
||||
/**
|
||||
* Gets a unique identifying string for an OfflinePlayer.
|
||||
*
|
||||
* @param player
|
||||
* @return
|
||||
*/
|
||||
public String getPlayerDataID(OfflinePlayer player);
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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 org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
|
||||
public interface ISpecialEnderChest {
|
||||
|
||||
/**
|
||||
* Gets the Inventory associated with this ISpecialEnderChest.
|
||||
*
|
||||
* @return the Inventory
|
||||
*/
|
||||
public Inventory getBukkitInventory();
|
||||
|
||||
/**
|
||||
* Sets the Player associated with this ISpecialEnderChest online.
|
||||
*
|
||||
* @param player the Player coming online
|
||||
*/
|
||||
public void setPlayerOnline(Player player);
|
||||
|
||||
/**
|
||||
* Sets the Player associated with this ISpecialEnderChest offline.
|
||||
*/
|
||||
public void setPlayerOffline();
|
||||
|
||||
/**
|
||||
* Gets whether or not this ISpecialEnderChest is in use.
|
||||
*
|
||||
* @return true if the ISpecialEnderChest is in use
|
||||
*/
|
||||
public boolean isInUse();
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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 org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
|
||||
public interface ISpecialPlayerInventory {
|
||||
|
||||
/**
|
||||
* Gets the Inventory associated with this ISpecialPlayerInventory.
|
||||
*
|
||||
* @return the Inventory
|
||||
*/
|
||||
public Inventory getBukkitInventory();
|
||||
|
||||
/**
|
||||
* Sets the Player associated with this ISpecialPlayerInventory online.
|
||||
*
|
||||
* @param player the Player coming online
|
||||
*/
|
||||
public void setPlayerOnline(Player player);
|
||||
|
||||
/**
|
||||
* Sets the Player associated with this ISpecialPlayerInventory offline.
|
||||
*/
|
||||
public void setPlayerOffline();
|
||||
|
||||
/**
|
||||
* Gets whether or not this ISpecialPlayerInventory is in use.
|
||||
*
|
||||
* @return true if the ISpecialPlayerInventory is in use
|
||||
*/
|
||||
public boolean isInUse();
|
||||
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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 java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class InternalAccessor {
|
||||
|
||||
private final Plugin plugin;
|
||||
|
||||
private final String version;
|
||||
private boolean supported = false;
|
||||
|
||||
public InternalAccessor(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
String packageName = plugin.getServer().getClass().getPackage().getName();
|
||||
version = packageName.substring(packageName.lastIndexOf('.') + 1);
|
||||
|
||||
try {
|
||||
Class.forName("com.lishid.openinv.internal." + version + ".PlayerDataManager");
|
||||
supported = true;
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server implementation version. If not initialized, returns the string "null"
|
||||
* instead.
|
||||
*
|
||||
* @return the version, or "null"
|
||||
*/
|
||||
public String getVersion() {
|
||||
return this.version != null ? this.version : "null";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the server implementation is supported.
|
||||
*
|
||||
* @return true if initialized for a supported server version
|
||||
*/
|
||||
public boolean isSupported() {
|
||||
return this.supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the IPlayerDataManager implementation for the current server version,
|
||||
* or null if unsupported.
|
||||
*
|
||||
* @return the IPlayerDataManager
|
||||
*/
|
||||
public IPlayerDataManager newPlayerDataManager() {
|
||||
return createObject(IPlayerDataManager.class, "PlayerDataManager");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the IInventoryAccess implementation for the current server version, or
|
||||
* null if unsupported.
|
||||
*
|
||||
* @return the IInventoryAccess
|
||||
*/
|
||||
public IInventoryAccess newInventoryAccess() {
|
||||
return createObject(IInventoryAccess.class, "InventoryAccess");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the IAnySilentContainer implementation for the current server version,
|
||||
* or null if unsupported.
|
||||
*
|
||||
* @return the IAnySilentContainer
|
||||
*/
|
||||
public IAnySilentContainer newAnySilentContainer() {
|
||||
return createObject(IAnySilentContainer.class, "AnySilentContainer");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the ISpecialPlayerInventory implementation for the given Player, or
|
||||
* null if the current version is unsupported.
|
||||
*
|
||||
* @param player the Player
|
||||
* @param online true if the Player is online
|
||||
* @return the ISpecialPlayerInventory created
|
||||
*/
|
||||
public ISpecialPlayerInventory newSpecialPlayerInventory(Player player, boolean online) {
|
||||
return createObject(ISpecialPlayerInventory.class, "SpecialPlayerInventory", player, online);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the ISpecialEnderChest implementation for the given Player, or
|
||||
* null if the current version is unsupported.
|
||||
*
|
||||
* @param player the Player
|
||||
* @param online true if the Player is online
|
||||
* @return the ISpecialEnderChest created
|
||||
*/
|
||||
public ISpecialEnderChest newSpecialEnderChest(Player player, boolean online) {
|
||||
return createObject(ISpecialEnderChest.class, "SpecialEnderChest", player, online);
|
||||
}
|
||||
|
||||
private <T> T createObject(Class<? extends T> assignableClass, String className, Object... params) {
|
||||
try {
|
||||
// Check if internal versioned class exists
|
||||
Class<?> internalClass = Class.forName("com.lishid.openinv.internal." + version + "." + className);
|
||||
if (!assignableClass.isAssignableFrom(internalClass)) {
|
||||
plugin.getLogger().warning("Found class " + internalClass.getName() + " but cannot cast to " + assignableClass.getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
// Quick return: no parameters, no need to fiddle about finding the correct constructor.
|
||||
if (params.length == 0) {
|
||||
return assignableClass.cast(internalClass.getConstructor().newInstance());
|
||||
}
|
||||
|
||||
// Search constructors for one matching the given parameters
|
||||
nextConstructor: for (Constructor<?> constructor : internalClass.getConstructors()) {
|
||||
Class<?>[] requiredClasses = constructor.getParameterTypes();
|
||||
if (requiredClasses.length != params.length) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < params.length; ++i) {
|
||||
if (!requiredClasses[i].isAssignableFrom(params[i].getClass())) {
|
||||
continue nextConstructor;
|
||||
}
|
||||
}
|
||||
return assignableClass.cast(constructor.newInstance(params));
|
||||
}
|
||||
|
||||
StringBuilder message = new StringBuilder("Found class ").append(internalClass.getName())
|
||||
.append(" but cannot find any matching constructors for [");
|
||||
for (Object object : params) {
|
||||
message.append(object.getClass().getName()).append(", ");
|
||||
}
|
||||
if (params.length > 0) {
|
||||
message.delete(message.length() - 2, message.length());
|
||||
}
|
||||
|
||||
plugin.getLogger().warning(message.append(']').toString());
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("OpenInv encountered an error with the CraftBukkit version \"" + version + "\". Please look for an updated version of OpenInv.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> T grabFieldOfTypeFromObject(Class<T> type, Object object) {
|
||||
// Use reflection to find the iinventory
|
||||
Class<?> clazz = object.getClass();
|
||||
T result = null;
|
||||
for (Field f : clazz.getDeclaredFields()) {
|
||||
f.setAccessible(true);
|
||||
if (type.isAssignableFrom(f.getDeclaringClass())) {
|
||||
try {
|
||||
result = type.cast(f.get(object));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
<version>4.1.9-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvadapter1_17_R1</artifactId>
|
||||
<name>OpenInvAdapter1_17_R1</name>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>16</maven.compiler.source>
|
||||
<maven.compiler.target>16</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<artifactId>spigot</artifactId>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<scope>provided</scope>
|
||||
<version>1.17.1-R0.1-SNAPSHOT</version>
|
||||
<classifier>remapped-mojang</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvplugincore</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>annotations</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<configuration>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>specialsource-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>remap</goal>
|
||||
</goals>
|
||||
<id>remap-obf</id>
|
||||
<configuration>
|
||||
<srgIn>org.spigotmc:minecraft-server:1.17.1-R0.1-SNAPSHOT:txt:maps-mojang</srgIn>
|
||||
<reverse>true</reverse>
|
||||
<remappedDependencies>org.spigotmc:spigot:1.17.1-R0.1-SNAPSHOT:jar:remapped-mojang</remappedDependencies>
|
||||
<remappedArtifactAttached>true</remappedArtifactAttached>
|
||||
<remappedClassifierName>remapped-obf</remappedClassifierName>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>remap</goal>
|
||||
</goals>
|
||||
<id>remap-spigot</id>
|
||||
<configuration>
|
||||
<inputFile>${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar</inputFile>
|
||||
<srgIn>org.spigotmc:minecraft-server:1.17.1-R0.1-SNAPSHOT:csrg:maps-spigot</srgIn>
|
||||
<remappedDependencies>org.spigotmc:spigot:1.17.1-R0.1-SNAPSHOT:jar:remapped-obf</remappedDependencies>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_17_R1;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.internal.IAnySilentContainer;
|
||||
import com.lishid.openinv.util.ReflectionHelper;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.ServerPlayerGameMode;
|
||||
import net.minecraft.world.CompoundContainer;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.SimpleMenuProvider;
|
||||
import net.minecraft.world.entity.monster.Shulker;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.PlayerEnderChestContainer;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraft.world.level.block.BarrelBlock;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.ChestBlock;
|
||||
import net.minecraft.world.level.block.DoubleBlockCombiner;
|
||||
import net.minecraft.world.level.block.ShulkerBoxBlock;
|
||||
import net.minecraft.world.level.block.TrappedChestBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.ChestBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class AnySilentContainer implements IAnySilentContainer {
|
||||
|
||||
private @Nullable Field serverPlayerGameModeGameType;
|
||||
|
||||
public AnySilentContainer() {
|
||||
try {
|
||||
try {
|
||||
// IDE warns about field not existing, but SpecialSource does not remap strings used in reflection.
|
||||
// The warning is not suppressed as a reminder that it must manually be checked on updates.
|
||||
this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b");
|
||||
this.serverPlayerGameModeGameType.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
|
||||
logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!");
|
||||
logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues");
|
||||
logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail.");
|
||||
// N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break.
|
||||
this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class);
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
|
||||
logger.warning("Unable to directly write player game mode! SilentContainer will fail.");
|
||||
logger.log(Level.WARNING, "Error obtaining GameType field", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShulkerBlocked(ShulkerBox box) {
|
||||
org.bukkit.World bukkitWorld = box.getWorld();
|
||||
if (!(bukkitWorld instanceof CraftWorld)) {
|
||||
bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID());
|
||||
}
|
||||
|
||||
if (!(bukkitWorld instanceof CraftWorld craftWorld)) {
|
||||
Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!");
|
||||
OpenInv.getPlugin(OpenInv.class).getLogger().log(Level.WARNING, exception.getMessage(), exception);
|
||||
return false;
|
||||
}
|
||||
|
||||
final ServerLevel world = craftWorld.getHandle();
|
||||
final BlockPos blockPosition = new BlockPos(box.getX(), box.getY(), box.getZ());
|
||||
final BlockEntity tile = world.getBlockEntity(blockPosition);
|
||||
|
||||
if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity)
|
||||
|| shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockState blockState = world.getBlockState(blockPosition);
|
||||
|
||||
// See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen
|
||||
AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F)
|
||||
.move(blockPosition)
|
||||
.deflate(1.0E-6D);
|
||||
return !world.noCollision(boundingBox);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activateContainer(
|
||||
@NotNull final Player bukkitPlayer,
|
||||
final boolean silentchest,
|
||||
@NotNull final org.bukkit.block.Block bukkitBlock) {
|
||||
|
||||
// Silent ender chest is API-only
|
||||
if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) {
|
||||
bukkitPlayer.openInventory(bukkitPlayer.getEnderChest());
|
||||
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
|
||||
return true;
|
||||
}
|
||||
|
||||
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
|
||||
|
||||
final ServerLevel level = player.getLevel();
|
||||
final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
|
||||
final BlockEntity blockEntity = level.getBlockEntity(blockPos);
|
||||
|
||||
if (blockEntity == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (blockEntity instanceof EnderChestBlockEntity enderChestTile) {
|
||||
// Anychest ender chest. See net.minecraft.world.level.block.BlockEnderChest
|
||||
PlayerEnderChestContainer enderChest = player.getEnderChestInventory();
|
||||
enderChest.setActiveChest(enderChestTile);
|
||||
player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> {
|
||||
MenuType<?> containers = PlayerDataManager.getContainers(enderChest.getContainerSize());
|
||||
int rows = enderChest.getContainerSize() / 9;
|
||||
return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows);
|
||||
}, new TextComponent("container.enderchest")));
|
||||
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(blockEntity instanceof MenuProvider menuProvider)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockState blockState = level.getBlockState(blockPos);
|
||||
Block block = blockState.getBlock();
|
||||
|
||||
if (block instanceof ChestBlock chestBlock) {
|
||||
|
||||
// boolean flag: check if chest is blocked
|
||||
Optional<MenuProvider> menuOptional = chestBlock.combine(blockState, level, blockPos, false).apply(
|
||||
// Combiner is a copy of private ChestBlock.MENU_PROVIDER_COMBINER
|
||||
new DoubleBlockCombiner.Combiner<ChestBlockEntity, Optional<MenuProvider>>() {
|
||||
@Override
|
||||
public Optional<MenuProvider> acceptDouble(ChestBlockEntity localChest1, ChestBlockEntity localChest2) {
|
||||
CompoundContainer doubleChest = new CompoundContainer(localChest1, localChest2);
|
||||
return Optional.of(new ChestBlock.DoubleInventory(localChest1, localChest2, doubleChest));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MenuProvider> acceptSingle(ChestBlockEntity localChest) {
|
||||
return Optional.of(localChest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MenuProvider> acceptNone() {
|
||||
return Optional.empty();
|
||||
}
|
||||
});
|
||||
|
||||
if (menuOptional.isEmpty()) {
|
||||
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
|
||||
return false;
|
||||
}
|
||||
|
||||
menuProvider = menuOptional.get();
|
||||
|
||||
if (block instanceof TrappedChestBlock) {
|
||||
bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED);
|
||||
} else {
|
||||
bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED);
|
||||
}
|
||||
}
|
||||
|
||||
if (block instanceof ShulkerBoxBlock) {
|
||||
bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED);
|
||||
}
|
||||
|
||||
if (block instanceof BarrelBlock) {
|
||||
bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL);
|
||||
}
|
||||
|
||||
// AnyChest only - SilentChest not active, container unsupported, or unnecessary.
|
||||
if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) {
|
||||
player.openMenu(menuProvider);
|
||||
return true;
|
||||
}
|
||||
|
||||
// SilentChest requires access to setting players' game mode directly.
|
||||
if (this.serverPlayerGameModeGameType == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (blockEntity instanceof RandomizableContainerBlockEntity lootable) {
|
||||
if (lootable.lootTable != null) {
|
||||
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
GameType gameType = player.gameMode.getGameModeForPlayer();
|
||||
this.forceGameType(player, GameType.SPECTATOR);
|
||||
player.openMenu(menuProvider);
|
||||
this.forceGameType(player, gameType);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivateContainer(@NotNull final Player bukkitPlayer) {
|
||||
if (this.serverPlayerGameModeGameType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
InventoryView view = bukkitPlayer.getOpenInventory();
|
||||
switch (view.getType()) {
|
||||
case CHEST:
|
||||
case ENDER_CHEST:
|
||||
case SHULKER_BOX:
|
||||
case BARREL:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
|
||||
|
||||
// Force game mode change without informing plugins or players.
|
||||
// Regular game mode set calls GameModeChangeEvent and is cancellable.
|
||||
GameType gameType = player.gameMode.getGameModeForPlayer();
|
||||
this.forceGameType(player, GameType.SPECTATOR);
|
||||
|
||||
// Close container - note that this is very different from ServerPlayer#closeContainer!
|
||||
// Triggering event must not be re-called or we'll enter an infinite loop.
|
||||
player.doCloseContainer();
|
||||
|
||||
// Revert forced game mode.
|
||||
this.forceGameType(player, gameType);
|
||||
}
|
||||
|
||||
private void forceGameType(final ServerPlayer player, final GameType gameMode) {
|
||||
if (this.serverPlayerGameModeGameType == null) {
|
||||
// No need to warn repeatedly, error on startup and lack of function should be enough.
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.serverPlayerGameModeGameType.setAccessible(true);
|
||||
this.serverPlayerGameModeGameType.set(player.gameMode, gameMode);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_17_R1;
|
||||
|
||||
import java.io.File;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.storage.PlayerDataStorage;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
|
||||
|
||||
public class OpenPlayer extends CraftPlayer {
|
||||
|
||||
public OpenPlayer(CraftServer server, ServerPlayer entity) {
|
||||
super(server, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadData() {
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle());
|
||||
if (loaded != null) {
|
||||
readExtraData(loaded);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveData() {
|
||||
ServerPlayer player = this.getHandle();
|
||||
// See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman)
|
||||
try {
|
||||
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
|
||||
|
||||
CompoundTag playerData = player.saveWithoutId(new CompoundTag());
|
||||
setExtraData(playerData);
|
||||
|
||||
if (!isOnline()) {
|
||||
// Special case: save old vehicle data
|
||||
CompoundTag oldData = worldNBTStorage.load(player);
|
||||
|
||||
if (oldData != null && oldData.contains("RootVehicle", 10)) {
|
||||
// See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound)
|
||||
playerData.put("RootVehicle", oldData.getCompound("RootVehicle"));
|
||||
}
|
||||
}
|
||||
|
||||
File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir());
|
||||
NbtIo.writeCompressed(playerData, file);
|
||||
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat");
|
||||
File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old");
|
||||
Util.safeReplaceFile(file1, file, file2);
|
||||
} catch (Exception e) {
|
||||
LogManager.getLogger().warn("Failed to save player data for {}", player.getName().getString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_17_R1;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.internal.IPlayerDataManager;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.OpenInventoryView;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.logging.Logger;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.event.CraftEventFactory;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftContainer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class PlayerDataManager implements IPlayerDataManager {
|
||||
|
||||
private @Nullable Field bukkitEntity;
|
||||
|
||||
public PlayerDataManager() {
|
||||
try {
|
||||
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
|
||||
} catch (NoSuchFieldException e) {
|
||||
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
|
||||
logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
|
||||
logger.log(java.util.logging.Level.WARNING, e.getMessage(), e);
|
||||
bukkitEntity = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull ServerPlayer getHandle(final Player player) {
|
||||
if (player instanceof CraftPlayer) {
|
||||
return ((CraftPlayer) player).getHandle();
|
||||
}
|
||||
|
||||
Server server = player.getServer();
|
||||
ServerPlayer nmsPlayer = null;
|
||||
|
||||
if (server instanceof CraftServer) {
|
||||
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId());
|
||||
}
|
||||
|
||||
if (nmsPlayer == null) {
|
||||
// Could use reflection to examine fields, but it's honestly not worth the bother.
|
||||
throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation");
|
||||
}
|
||||
|
||||
return nmsPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
|
||||
// Ensure player has data
|
||||
if (!offline.hasPlayedBefore()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a profile and entity to load the player data
|
||||
// See net.minecraft.server.PlayerList#attemptLogin
|
||||
GameProfile profile = new GameProfile(offline.getUniqueId(),
|
||||
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
|
||||
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
ServerLevel worldServer = server.getLevel(Level.OVERWORLD);
|
||||
|
||||
if (worldServer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ServerPlayer entity = new ServerPlayer(server, worldServer, profile);
|
||||
|
||||
try {
|
||||
injectPlayer(entity);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Get the bukkit entity
|
||||
Player target = entity.getBukkitEntity();
|
||||
if (target != null) {
|
||||
// Load data
|
||||
target.loadData();
|
||||
}
|
||||
// Return the entity
|
||||
return target;
|
||||
}
|
||||
|
||||
void injectPlayer(ServerPlayer player) throws IllegalAccessException {
|
||||
if (bukkitEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
bukkitEntity.setAccessible(true);
|
||||
|
||||
bukkitEntity.set(player, new OpenPlayer(player.server.server, player));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Player inject(@NotNull Player player) {
|
||||
try {
|
||||
ServerPlayer nmsPlayer = getHandle(player);
|
||||
injectPlayer(nmsPlayer);
|
||||
return nmsPlayer.getBukkitEntity();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
|
||||
|
||||
ServerPlayer nmsPlayer = getHandle(player);
|
||||
|
||||
if (nmsPlayer.connection == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InventoryView view = getView(player, inventory);
|
||||
|
||||
if (view == null) {
|
||||
return player.openInventory(inventory.getBukkitInventory());
|
||||
}
|
||||
|
||||
AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
|
||||
@Override
|
||||
public MenuType<?> getType() {
|
||||
return getContainers(inventory.getBukkitInventory().getSize());
|
||||
}
|
||||
};
|
||||
|
||||
container.setTitle(new TextComponent(view.getTitle()));
|
||||
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
|
||||
|
||||
if (container == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(),
|
||||
new TextComponent(container.getBukkitView().getTitle())));
|
||||
nmsPlayer.containerMenu = container;
|
||||
nmsPlayer.initMenu(container);
|
||||
|
||||
return container.getBukkitView();
|
||||
|
||||
}
|
||||
|
||||
private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) {
|
||||
if (inventory instanceof SpecialEnderChest) {
|
||||
return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest");
|
||||
} else if (inventory instanceof SpecialPlayerInventory) {
|
||||
return new OpenInventoryView(player, inventory, "container.player", "'s Inventory");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static @NotNull MenuType<?> getContainers(int inventorySize) {
|
||||
|
||||
return switch (inventorySize) {
|
||||
case 9 -> MenuType.GENERIC_9x1;
|
||||
case 18 -> MenuType.GENERIC_9x2;
|
||||
case 36 -> MenuType.GENERIC_9x4; // PLAYER
|
||||
case 41, 45 -> MenuType.GENERIC_9x5;
|
||||
case 54 -> MenuType.GENERIC_9x6;
|
||||
default -> MenuType.GENERIC_9x3; // Default 27-slot inventory
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertToPlayerSlot(InventoryView view, int rawSlot) {
|
||||
int topSize = view.getTopInventory().getSize();
|
||||
if (topSize <= rawSlot) {
|
||||
// Slot is not inside special inventory, use Bukkit logic.
|
||||
return view.convertSlot(rawSlot);
|
||||
}
|
||||
|
||||
// Main inventory, slots 0-26 -> 9-35
|
||||
if (rawSlot < 27) {
|
||||
return rawSlot + 9;
|
||||
}
|
||||
// Hotbar, slots 27-35 -> 0-8
|
||||
if (rawSlot < 36) {
|
||||
return rawSlot - 27;
|
||||
}
|
||||
// Armor, slots 36-39 -> 39-36
|
||||
if (rawSlot < 40) {
|
||||
return 36 + (39 - rawSlot);
|
||||
}
|
||||
// Off hand
|
||||
if (rawSlot == 40) {
|
||||
return 40;
|
||||
}
|
||||
// Drop slots, "out of inventory"
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
+344
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_17_R1;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.ContainerListener;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.player.StackedContents;
|
||||
import net.minecraft.world.inventory.PlayerEnderChestContainer;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
|
||||
|
||||
private final CraftInventory inventory;
|
||||
private ServerPlayer owner;
|
||||
private NonNullList<ItemStack> items;
|
||||
private boolean playerOnline;
|
||||
|
||||
public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) {
|
||||
super(PlayerDataManager.getHandle(player));
|
||||
this.inventory = new CraftInventory(this);
|
||||
this.owner = PlayerDataManager.getHandle(player);
|
||||
this.playerOnline = online;
|
||||
this.items = this.owner.getEnderChestInventory().items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CraftInventory getBukkitInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUse() {
|
||||
return !this.getViewers().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {
|
||||
this.playerOnline = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) {
|
||||
if (!this.playerOnline) {
|
||||
try {
|
||||
this.owner = PlayerDataManager.getHandle(player);
|
||||
PlayerEnderChestContainer enderChest = owner.getEnderChestInventory();
|
||||
for (int i = 0; i < enderChest.getContainerSize(); ++i) {
|
||||
enderChest.setItem(i, this.items.get(i));
|
||||
}
|
||||
this.items = enderChest.items;
|
||||
enderChest.transaction.addAll(this.transaction);
|
||||
} catch (Exception ignored) {}
|
||||
this.playerOnline = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.entity.Player getPlayer() {
|
||||
return owner.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
this.owner.getEnderChestInventory().setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity who) {
|
||||
this.owner.getEnderChestInventory().onOpen(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity who) {
|
||||
this.owner.getEnderChestInventory().onClose(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return this.owner.getEnderChestInventory().getViewers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActiveChest(EnderChestBlockEntity enderChest) {
|
||||
this.owner.getEnderChestInventory().setActiveChest(enderChest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActiveChest(EnderChestBlockEntity enderChest) {
|
||||
return this.owner.getEnderChestInventory().isActiveChest(enderChest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return this.owner.getEnderChestInventory().getMaxStackSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int i) {
|
||||
this.owner.getEnderChestInventory().setMaxStackSize(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryHolder getOwner() {
|
||||
return this.owner.getEnderChestInventory().getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Location getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(ContainerListener listener) {
|
||||
this.owner.getEnderChestInventory().addListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(ContainerListener listener) {
|
||||
this.owner.getEnderChestInventory().removeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int i) {
|
||||
return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int i, int j) {
|
||||
ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j);
|
||||
if (!itemstack.isEmpty()) {
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack addItem(ItemStack itemstack) {
|
||||
ItemStack localItem = itemstack.copy();
|
||||
this.moveItemToOccupiedSlotsWithSameType(localItem);
|
||||
if (localItem.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
} else {
|
||||
this.moveItemToEmptySlots(localItem);
|
||||
return localItem.isEmpty() ? ItemStack.EMPTY : localItem;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAddItem(ItemStack itemstack) {
|
||||
for (ItemStack itemstack1 : this.items) {
|
||||
if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void moveItemToEmptySlots(ItemStack itemstack) {
|
||||
for(int i = 0; i < this.getContainerSize(); ++i) {
|
||||
ItemStack localItem = this.getItem(i);
|
||||
if (localItem.isEmpty()) {
|
||||
this.setItem(i, itemstack.copy());
|
||||
itemstack.setCount(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) {
|
||||
for(int i = 0; i < this.getContainerSize(); ++i) {
|
||||
ItemStack localItem = this.getItem(i);
|
||||
if (ItemStack.isSameItemSameTags(localItem, itemstack)) {
|
||||
this.moveItemsBetweenStacks(itemstack, localItem);
|
||||
if (itemstack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) {
|
||||
int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize());
|
||||
int j = Math.min(itemstack.getCount(), i - itemstack1.getCount());
|
||||
if (j > 0) {
|
||||
itemstack1.grow(j);
|
||||
itemstack.shrink(j);
|
||||
this.setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int i) {
|
||||
ItemStack itemstack = this.items.get(i);
|
||||
if (itemstack.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
} else {
|
||||
this.items.set(i, ItemStack.EMPTY);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int i, ItemStack itemstack) {
|
||||
this.items.set(i, itemstack);
|
||||
if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) {
|
||||
itemstack.setCount(this.getMaxStackSize());
|
||||
}
|
||||
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return this.owner.getEnderChestInventory().getContainerSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.items.stream().allMatch(ItemStack::isEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startOpen(Player player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopOpen(Player player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceItem(int i, ItemStack itemstack) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
this.items.clear();
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStackedContents(StackedContents stackedContents) {
|
||||
for (ItemStack itemstack : this.items) {
|
||||
stackedContents.accountStack(itemstack);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> removeAllItems() {
|
||||
List<ItemStack> list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList());
|
||||
this.clearContent();
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemType(Item item, int i) {
|
||||
ItemStack itemstack = new ItemStack(item, 0);
|
||||
|
||||
for(int j = this.getContainerSize() - 1; j >= 0; --j) {
|
||||
ItemStack localItem = this.getItem(j);
|
||||
if (localItem.getItem().equals(item)) {
|
||||
int k = i - itemstack.getCount();
|
||||
ItemStack splitItem = localItem.split(k);
|
||||
itemstack.grow(splitItem.getCount());
|
||||
if (itemstack.getCount() == i) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).collect(Collectors.toList()).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromTag(ListTag listTag) {
|
||||
for (int i = 0; i < this.getContainerSize(); ++i) {
|
||||
this.setItem(i, ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
for (int i = 0; i < listTag.size(); ++i) {
|
||||
CompoundTag compoundTag = listTag.getCompound(i);
|
||||
int j = compoundTag.getByte("Slot") & 255;
|
||||
if (j < this.getContainerSize()) {
|
||||
this.setItem(j, ItemStack.of(compoundTag));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+769
@@ -0,0 +1,769 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_17_R1;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.CrashReportCategory;
|
||||
import net.minecraft.ReportedException;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.tags.Tag;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.player.StackedContents;
|
||||
import net.minecraft.world.item.ArmorItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory {
|
||||
|
||||
private final CraftInventory inventory;
|
||||
private boolean playerOnline;
|
||||
private Player player;
|
||||
private NonNullList<ItemStack> items;
|
||||
private NonNullList<ItemStack> armor;
|
||||
private NonNullList<ItemStack> offhand;
|
||||
private List<NonNullList<ItemStack>> compartments;
|
||||
|
||||
public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) {
|
||||
super(PlayerDataManager.getHandle(bukkitPlayer));
|
||||
this.inventory = new CraftInventory(this);
|
||||
this.playerOnline = online;
|
||||
this.player = super.player;
|
||||
this.selected = player.getInventory().selected;
|
||||
this.items = this.player.getInventory().items;
|
||||
this.armor = this.player.getInventory().armor;
|
||||
this.offhand = this.player.getInventory().offhand;
|
||||
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
|
||||
if (!this.playerOnline) {
|
||||
Player entityPlayer = PlayerDataManager.getHandle(player);
|
||||
entityPlayer.getInventory().transaction.addAll(this.transaction);
|
||||
this.player = entityPlayer;
|
||||
for (int i = 0; i < getContainerSize(); ++i) {
|
||||
this.player.getInventory().setItem(i, getRawItem(i));
|
||||
}
|
||||
this.player.getInventory().selected = this.selected;
|
||||
this.items = this.player.getInventory().items;
|
||||
this.armor = this.player.getInventory().armor;
|
||||
this.offhand = this.player.getInventory().offhand;
|
||||
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
|
||||
this.playerOnline = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CraftInventory getBukkitInventory() {
|
||||
return this.inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {
|
||||
this.playerOnline = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUse() {
|
||||
return !this.getViewers().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HumanEntity getPlayer() {
|
||||
return this.player.getBukkitEntity();
|
||||
}
|
||||
|
||||
private @NotNull ItemStack getRawItem(int i) {
|
||||
return super.getItem(i);
|
||||
}
|
||||
|
||||
private void setRawItem(int i, @NotNull ItemStack itemStack) {
|
||||
super.setItem(i, itemStack);
|
||||
}
|
||||
|
||||
private static record IndexedCompartment(@Nullable NonNullList<ItemStack> compartment, int index) {}
|
||||
|
||||
private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) {
|
||||
if (index < items.size()) {
|
||||
return new IndexedCompartment(items, getReversedItemSlotNum(index));
|
||||
}
|
||||
|
||||
index -= items.size();
|
||||
|
||||
if (index < armor.size()) {
|
||||
return new IndexedCompartment(armor, getReversedArmorSlotNum(index));
|
||||
}
|
||||
|
||||
index -= armor.size();
|
||||
|
||||
if (index < offhand.size()) {
|
||||
return new IndexedCompartment(offhand, index);
|
||||
}
|
||||
|
||||
index -= offhand.size();
|
||||
|
||||
return new IndexedCompartment(null, index);
|
||||
}
|
||||
|
||||
private int getReversedArmorSlotNum(final int i) {
|
||||
if (i == 0) {
|
||||
return 3;
|
||||
}
|
||||
if (i == 1) {
|
||||
return 2;
|
||||
}
|
||||
if (i == 2) {
|
||||
return 1;
|
||||
}
|
||||
if (i == 3) {
|
||||
return 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
private int getReversedItemSlotNum(final int i) {
|
||||
if (i >= 27) {
|
||||
return i - 27;
|
||||
}
|
||||
return i + 9;
|
||||
}
|
||||
|
||||
private boolean contains(Predicate<ItemStack> predicate) {
|
||||
return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getArmorContents() {
|
||||
return this.armor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity who) {
|
||||
this.player.getInventory().onOpen(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity who) {
|
||||
this.player.getInventory().onClose(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return this.player.getInventory().getViewers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryHolder getOwner() {
|
||||
return this.player.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return this.player.getInventory().getMaxStackSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int size) {
|
||||
this.player.getInventory().setMaxStackSize(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return this.player.getBukkitEntity().getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCustomName() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getSelected() {
|
||||
return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) {
|
||||
return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int canHold(ItemStack itemstack) {
|
||||
int remains = itemstack.getCount();
|
||||
|
||||
for (int i = 0; i < this.items.size(); ++i) {
|
||||
ItemStack itemstack1 = this.getRawItem(i);
|
||||
if (itemstack1.isEmpty()) {
|
||||
return itemstack.getCount();
|
||||
}
|
||||
|
||||
if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) {
|
||||
remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount();
|
||||
}
|
||||
|
||||
if (remains <= 0) {
|
||||
return itemstack.getCount();
|
||||
}
|
||||
}
|
||||
|
||||
ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size());
|
||||
if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) {
|
||||
remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount();
|
||||
}
|
||||
|
||||
return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFreeSlot() {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
if (this.items.get(i).isEmpty()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPickedItem(ItemStack itemstack) {
|
||||
int i = this.findSlotMatchingItem(itemstack);
|
||||
if (isHotbarSlot(i)) {
|
||||
this.selected = i;
|
||||
} else if (i == -1) {
|
||||
this.selected = this.getSuitableHotbarSlot();
|
||||
if (!this.items.get(this.selected).isEmpty()) {
|
||||
int j = this.getFreeSlot();
|
||||
if (j != -1) {
|
||||
this.items.set(j, this.items.get(this.selected));
|
||||
}
|
||||
}
|
||||
|
||||
this.items.set(this.selected, itemstack);
|
||||
} else {
|
||||
this.pickSlot(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pickSlot(int i) {
|
||||
this.selected = this.getSuitableHotbarSlot();
|
||||
ItemStack itemstack = this.items.get(this.selected);
|
||||
this.items.set(this.selected, this.items.get(i));
|
||||
this.items.set(i, itemstack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findSlotMatchingItem(ItemStack itemstack) {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findSlotMatchingUnusedItem(ItemStack itemStack) {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
ItemStack localItem = this.items.get(i);
|
||||
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSuitableHotbarSlot() {
|
||||
int i;
|
||||
int j;
|
||||
for(j = 0; j < 9; ++j) {
|
||||
i = (this.selected + j) % 9;
|
||||
if (this.items.get(i).isEmpty()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
for(j = 0; j < 9; ++j) {
|
||||
i = (this.selected + j) % 9;
|
||||
if (!this.items.get(i).isEnchanted()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return this.selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swapPaint(double d0) {
|
||||
if (d0 > 0.0D) {
|
||||
d0 = 1.0D;
|
||||
}
|
||||
|
||||
if (d0 < 0.0D) {
|
||||
d0 = -1.0D;
|
||||
}
|
||||
|
||||
this.selected = (int) (this.selected - d0);
|
||||
|
||||
while (this.selected < 0) {
|
||||
this.selected += 9;
|
||||
}
|
||||
|
||||
while(this.selected >= 9) {
|
||||
this.selected -= 9;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int clearOrCountMatchingItems(Predicate<ItemStack> predicate, int i, Container container) {
|
||||
byte b0 = 0;
|
||||
boolean flag = i == 0;
|
||||
int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag);
|
||||
j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag);
|
||||
ItemStack itemstack = this.player.containerMenu.getCarried();
|
||||
j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag);
|
||||
if (itemstack.isEmpty()) {
|
||||
this.player.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
private int addResource(ItemStack itemstack) {
|
||||
int i = this.getSlotWithRemainingSpace(itemstack);
|
||||
if (i == -1) {
|
||||
i = this.getFreeSlot();
|
||||
}
|
||||
|
||||
return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack);
|
||||
}
|
||||
|
||||
private int addResource(int i, ItemStack itemstack) {
|
||||
Item item = itemstack.getItem();
|
||||
int j = itemstack.getCount();
|
||||
ItemStack localItemStack = this.getRawItem(i);
|
||||
if (localItemStack.isEmpty()) {
|
||||
localItemStack = new ItemStack(item, 0);
|
||||
if (itemstack.hasTag()) {
|
||||
// hasTag ensures tag not null
|
||||
//noinspection ConstantConditions
|
||||
localItemStack.setTag(itemstack.getTag().copy());
|
||||
}
|
||||
|
||||
this.setRawItem(i, localItemStack);
|
||||
}
|
||||
|
||||
int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount());
|
||||
|
||||
if (k > this.getMaxStackSize() - localItemStack.getCount()) {
|
||||
k = this.getMaxStackSize() - localItemStack.getCount();
|
||||
}
|
||||
|
||||
if (k != 0) {
|
||||
j -= k;
|
||||
localItemStack.grow(k);
|
||||
localItemStack.setPopTime(5);
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSlotWithRemainingSpace(ItemStack itemstack) {
|
||||
if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) {
|
||||
return this.selected;
|
||||
} else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) {
|
||||
return 40;
|
||||
} else {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
for (int i = 0; i < compartment.size(); ++i) {
|
||||
if (!compartment.get(i).isEmpty()) {
|
||||
compartment.get(i).inventoryTick(this.player.level, this.player, i, this.selected == i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(ItemStack itemStack) {
|
||||
return this.add(-1, itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(int i, ItemStack itemStack) {
|
||||
if (itemStack.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
try {
|
||||
if (itemStack.isDamaged()) {
|
||||
if (i == -1) {
|
||||
i = this.getFreeSlot();
|
||||
}
|
||||
|
||||
if (i >= 0) {
|
||||
this.items.set(i, itemStack.copy());
|
||||
this.items.get(i).setPopTime(5);
|
||||
itemStack.setCount(0);
|
||||
return true;
|
||||
} else if (this.player.getAbilities().instabuild) {
|
||||
itemStack.setCount(0);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
int j;
|
||||
do {
|
||||
j = itemStack.getCount();
|
||||
if (i == -1) {
|
||||
itemStack.setCount(this.addResource(itemStack));
|
||||
} else {
|
||||
itemStack.setCount(this.addResource(i, itemStack));
|
||||
}
|
||||
} while(!itemStack.isEmpty() && itemStack.getCount() < j);
|
||||
|
||||
if (itemStack.getCount() == j && this.player.getAbilities().instabuild) {
|
||||
itemStack.setCount(0);
|
||||
return true;
|
||||
} else {
|
||||
return itemStack.getCount() < j;
|
||||
}
|
||||
}
|
||||
} catch (Throwable var6) {
|
||||
CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory");
|
||||
CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added");
|
||||
crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem()));
|
||||
crashReportCategory.setDetail("Item data", itemStack.getDamageValue());
|
||||
crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString());
|
||||
throw new ReportedException(crashReport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeItemBackInInventory(ItemStack itemStack) {
|
||||
this.placeItemBackInInventory(itemStack, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeItemBackInInventory(ItemStack itemStack, boolean flag) {
|
||||
while(true) {
|
||||
if (!itemStack.isEmpty()) {
|
||||
int i = this.getSlotWithRemainingSpace(itemStack);
|
||||
if (i == -1) {
|
||||
i = this.getFreeSlot();
|
||||
}
|
||||
|
||||
if (i != -1) {
|
||||
int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount();
|
||||
if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) {
|
||||
((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
this.player.drop(itemStack, false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int rawIndex, final int j) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null
|
||||
|| indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeItem(ItemStack itemStack) {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
for (int i = 0; i < compartment.size(); ++i) {
|
||||
if (compartment.get(i) == itemStack) {
|
||||
compartment.set(i, ItemStack.EMPTY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int rawIndex) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY);
|
||||
|
||||
if (removed.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int rawIndex, final ItemStack itemStack) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null) {
|
||||
this.player.drop(itemStack, true);
|
||||
return;
|
||||
}
|
||||
|
||||
indexedCompartment.compartment().set(indexedCompartment.index(), itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDestroySpeed(BlockState blockState) {
|
||||
return this.items.get(this.selected).getDestroySpeed(blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListTag save(ListTag listTag) {
|
||||
for (int i = 0; i < this.items.size(); ++i) {
|
||||
if (!this.items.get(i).isEmpty()) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putByte("Slot", (byte)i);
|
||||
this.items.get(i).save(compoundTag);
|
||||
listTag.add(compoundTag);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < this.armor.size(); ++i) {
|
||||
if (!this.armor.get(i).isEmpty()) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putByte("Slot", (byte)(i + 100));
|
||||
this.armor.get(i).save(compoundTag);
|
||||
listTag.add(compoundTag);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < this.offhand.size(); ++i) {
|
||||
if (!this.offhand.get(i).isEmpty()) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putByte("Slot", (byte)(i + 150));
|
||||
this.offhand.get(i).save(compoundTag);
|
||||
listTag.add(compoundTag);
|
||||
}
|
||||
}
|
||||
|
||||
return listTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(ListTag listTag) {
|
||||
this.items.clear();
|
||||
this.armor.clear();
|
||||
this.offhand.clear();
|
||||
|
||||
for(int i = 0; i < listTag.size(); ++i) {
|
||||
CompoundTag compoundTag = listTag.getCompound(i);
|
||||
int j = compoundTag.getByte("Slot") & 255;
|
||||
ItemStack itemstack = ItemStack.of(compoundTag);
|
||||
if (!itemstack.isEmpty()) {
|
||||
if (j < this.items.size()) {
|
||||
this.items.set(j, itemstack);
|
||||
} else if (j >= 100 && j < this.armor.size() + 100) {
|
||||
this.armor.set(j - 100, itemstack);
|
||||
} else if (j >= 150 && j < this.offhand.size() + 150) {
|
||||
this.offhand.set(j - 150, itemstack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return 45;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return !contains(itemStack -> !itemStack.isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int rawIndex) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
return indexedCompartment.compartment().get(indexedCompartment.index());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getName() {
|
||||
return this.player.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getArmor(int index) {
|
||||
return this.armor.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) {
|
||||
if (damage > 0.0F) {
|
||||
damage /= 4.0F;
|
||||
if (damage < 1.0F) {
|
||||
damage = 1.0F;
|
||||
}
|
||||
|
||||
for (int index : armorIndices) {
|
||||
ItemStack itemstack = this.armor.get(index);
|
||||
if ((!damagesource.isFire() || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) {
|
||||
itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropAll() {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
for (int i = 0; i < compartment.size(); ++i) {
|
||||
ItemStack itemstack = compartment.get(i);
|
||||
if (!itemstack.isEmpty()) {
|
||||
this.player.drop(itemstack, true, false);
|
||||
compartment.set(i, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
super.setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimesChanged() {
|
||||
return super.getTimesChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(ItemStack itemstack) {
|
||||
return contains(itemStack -> itemStack.isEmpty() && itemStack.sameItem(itemstack));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Tag<Item> tag) {
|
||||
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tag));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceWith(Inventory inventory) {
|
||||
Function<Integer, ItemStack> getter;
|
||||
|
||||
if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) {
|
||||
getter = specialPlayerInventory::getRawItem;
|
||||
} else {
|
||||
getter = inventory::getItem;
|
||||
}
|
||||
|
||||
for(int i = 0; i < this.getContainerSize(); ++i) {
|
||||
this.setRawItem(i, getter.apply(i));
|
||||
}
|
||||
|
||||
this.selected = inventory.selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
compartment.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStackedContents(StackedContents stackedContents) {
|
||||
for (ItemStack itemstack : this.items) {
|
||||
stackedContents.accountSimpleStack(itemstack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeFromSelected(boolean dropWholeStack) {
|
||||
ItemStack itemstack = this.getSelected();
|
||||
return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
<version>4.1.9-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvadapter1_18_R1</artifactId>
|
||||
<name>OpenInvAdapter1_18_R1</name>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<spigot.version>1.18-pre5-R0.1-SNAPSHOT</spigot.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>${spigot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>spigot</artifactId>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<scope>provided</scope>
|
||||
<version>${spigot.version}</version>
|
||||
<classifier>remapped-mojang</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvplugincore</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>annotations</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<configuration>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>specialsource-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>remap</goal>
|
||||
</goals>
|
||||
<id>remap-obf</id>
|
||||
<configuration>
|
||||
<srgIn>org.spigotmc:minecraft-server:${spigot.version}:txt:maps-mojang</srgIn>
|
||||
<reverse>true</reverse>
|
||||
<remappedDependencies>org.spigotmc:spigot:${spigot.version}:jar:remapped-mojang</remappedDependencies>
|
||||
<remappedArtifactAttached>true</remappedArtifactAttached>
|
||||
<remappedClassifierName>remapped-obf</remappedClassifierName>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>remap</goal>
|
||||
</goals>
|
||||
<id>remap-spigot</id>
|
||||
<configuration>
|
||||
<inputFile>${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar</inputFile>
|
||||
<srgIn>org.spigotmc:minecraft-server:${spigot.version}:csrg:maps-spigot</srgIn>
|
||||
<remappedDependencies>org.spigotmc:spigot:${spigot.version}:jar:remapped-obf</remappedDependencies>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_18_R1;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.internal.IAnySilentContainer;
|
||||
import com.lishid.openinv.util.ReflectionHelper;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.ServerPlayerGameMode;
|
||||
import net.minecraft.world.CompoundContainer;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.SimpleMenuProvider;
|
||||
import net.minecraft.world.entity.monster.Shulker;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.PlayerEnderChestContainer;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraft.world.level.block.BarrelBlock;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.ChestBlock;
|
||||
import net.minecraft.world.level.block.DoubleBlockCombiner;
|
||||
import net.minecraft.world.level.block.ShulkerBoxBlock;
|
||||
import net.minecraft.world.level.block.TrappedChestBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.ChestBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class AnySilentContainer implements IAnySilentContainer {
|
||||
|
||||
private @Nullable Field serverPlayerGameModeGameType;
|
||||
|
||||
public AnySilentContainer() {
|
||||
try {
|
||||
try {
|
||||
// IDE warns about field not existing, but SpecialSource does not remap strings used in reflection.
|
||||
// The warning is not suppressed as a reminder that it must manually be checked on updates.
|
||||
this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b");
|
||||
this.serverPlayerGameModeGameType.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
|
||||
logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!");
|
||||
logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues");
|
||||
logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail.");
|
||||
// N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break.
|
||||
this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class);
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
|
||||
logger.warning("Unable to directly write player game mode! SilentContainer will fail.");
|
||||
logger.log(Level.WARNING, "Error obtaining GameType field", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShulkerBlocked(ShulkerBox box) {
|
||||
org.bukkit.World bukkitWorld = box.getWorld();
|
||||
if (!(bukkitWorld instanceof CraftWorld)) {
|
||||
bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID());
|
||||
}
|
||||
|
||||
if (!(bukkitWorld instanceof CraftWorld craftWorld)) {
|
||||
Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!");
|
||||
OpenInv.getPlugin(OpenInv.class).getLogger().log(Level.WARNING, exception.getMessage(), exception);
|
||||
return false;
|
||||
}
|
||||
|
||||
final ServerLevel world = craftWorld.getHandle();
|
||||
final BlockPos blockPosition = new BlockPos(box.getX(), box.getY(), box.getZ());
|
||||
final BlockEntity tile = world.getBlockEntity(blockPosition);
|
||||
|
||||
if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity)
|
||||
|| shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockState blockState = world.getBlockState(blockPosition);
|
||||
|
||||
// See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen
|
||||
AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F)
|
||||
.move(blockPosition)
|
||||
.deflate(1.0E-6D);
|
||||
return !world.noCollision(boundingBox);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activateContainer(
|
||||
@NotNull final Player bukkitPlayer,
|
||||
final boolean silentchest,
|
||||
@NotNull final org.bukkit.block.Block bukkitBlock) {
|
||||
|
||||
// Silent ender chest is API-only
|
||||
if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) {
|
||||
bukkitPlayer.openInventory(bukkitPlayer.getEnderChest());
|
||||
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
|
||||
return true;
|
||||
}
|
||||
|
||||
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
|
||||
|
||||
final ServerLevel level = player.getLevel();
|
||||
final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
|
||||
final BlockEntity blockEntity = level.getBlockEntity(blockPos);
|
||||
|
||||
if (blockEntity == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (blockEntity instanceof EnderChestBlockEntity enderChestTile) {
|
||||
// Anychest ender chest. See net.minecraft.world.level.block.BlockEnderChest
|
||||
PlayerEnderChestContainer enderChest = player.getEnderChestInventory();
|
||||
enderChest.setActiveChest(enderChestTile);
|
||||
player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> {
|
||||
MenuType<?> containers = PlayerDataManager.getContainers(enderChest.getContainerSize());
|
||||
int rows = enderChest.getContainerSize() / 9;
|
||||
return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows);
|
||||
}, new TextComponent("container.enderchest")));
|
||||
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(blockEntity instanceof MenuProvider menuProvider)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockState blockState = level.getBlockState(blockPos);
|
||||
Block block = blockState.getBlock();
|
||||
|
||||
if (block instanceof ChestBlock chestBlock) {
|
||||
|
||||
// boolean flag: check if chest is blocked
|
||||
Optional<MenuProvider> menuOptional = chestBlock.combine(blockState, level, blockPos, false).apply(
|
||||
// Combiner is a copy of private ChestBlock.MENU_PROVIDER_COMBINER
|
||||
new DoubleBlockCombiner.Combiner<ChestBlockEntity, Optional<MenuProvider>>() {
|
||||
@Override
|
||||
public Optional<MenuProvider> acceptDouble(ChestBlockEntity localChest1, ChestBlockEntity localChest2) {
|
||||
CompoundContainer doubleChest = new CompoundContainer(localChest1, localChest2);
|
||||
return Optional.of(new ChestBlock.DoubleInventory(localChest1, localChest2, doubleChest));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MenuProvider> acceptSingle(ChestBlockEntity localChest) {
|
||||
return Optional.of(localChest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MenuProvider> acceptNone() {
|
||||
return Optional.empty();
|
||||
}
|
||||
});
|
||||
|
||||
if (menuOptional.isEmpty()) {
|
||||
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
|
||||
return false;
|
||||
}
|
||||
|
||||
menuProvider = menuOptional.get();
|
||||
|
||||
if (block instanceof TrappedChestBlock) {
|
||||
bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED);
|
||||
} else {
|
||||
bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED);
|
||||
}
|
||||
}
|
||||
|
||||
if (block instanceof ShulkerBoxBlock) {
|
||||
bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED);
|
||||
}
|
||||
|
||||
if (block instanceof BarrelBlock) {
|
||||
bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL);
|
||||
}
|
||||
|
||||
// AnyChest only - SilentChest not active, container unsupported, or unnecessary.
|
||||
if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) {
|
||||
player.openMenu(menuProvider);
|
||||
return true;
|
||||
}
|
||||
|
||||
// SilentChest requires access to setting players' game mode directly.
|
||||
if (this.serverPlayerGameModeGameType == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (blockEntity instanceof RandomizableContainerBlockEntity lootable) {
|
||||
if (lootable.lootTable != null) {
|
||||
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
GameType gameType = player.gameMode.getGameModeForPlayer();
|
||||
this.forceGameType(player, GameType.SPECTATOR);
|
||||
player.openMenu(menuProvider);
|
||||
this.forceGameType(player, gameType);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivateContainer(@NotNull final Player bukkitPlayer) {
|
||||
if (this.serverPlayerGameModeGameType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
InventoryView view = bukkitPlayer.getOpenInventory();
|
||||
switch (view.getType()) {
|
||||
case CHEST:
|
||||
case ENDER_CHEST:
|
||||
case SHULKER_BOX:
|
||||
case BARREL:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
|
||||
|
||||
// Force game mode change without informing plugins or players.
|
||||
// Regular game mode set calls GameModeChangeEvent and is cancellable.
|
||||
GameType gameType = player.gameMode.getGameModeForPlayer();
|
||||
this.forceGameType(player, GameType.SPECTATOR);
|
||||
|
||||
// Close container - note that this is very different from ServerPlayer#closeContainer!
|
||||
// Triggering event must not be re-called or we'll enter an infinite loop.
|
||||
player.doCloseContainer();
|
||||
|
||||
// Revert forced game mode.
|
||||
this.forceGameType(player, gameType);
|
||||
}
|
||||
|
||||
private void forceGameType(final ServerPlayer player, final GameType gameMode) {
|
||||
if (this.serverPlayerGameModeGameType == null) {
|
||||
// No need to warn repeatedly, error on startup and lack of function should be enough.
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.serverPlayerGameModeGameType.setAccessible(true);
|
||||
this.serverPlayerGameModeGameType.set(player.gameMode, gameMode);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_18_R1;
|
||||
|
||||
import java.io.File;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.storage.PlayerDataStorage;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer;
|
||||
|
||||
public class OpenPlayer extends CraftPlayer {
|
||||
|
||||
public OpenPlayer(CraftServer server, ServerPlayer entity) {
|
||||
super(server, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadData() {
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle());
|
||||
if (loaded != null) {
|
||||
readExtraData(loaded);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveData() {
|
||||
ServerPlayer player = this.getHandle();
|
||||
// See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman)
|
||||
try {
|
||||
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
|
||||
|
||||
CompoundTag playerData = player.saveWithoutId(new CompoundTag());
|
||||
setExtraData(playerData);
|
||||
|
||||
if (!isOnline()) {
|
||||
// Special case: save old vehicle data
|
||||
CompoundTag oldData = worldNBTStorage.load(player);
|
||||
|
||||
if (oldData != null && oldData.contains("RootVehicle", 10)) {
|
||||
// See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound)
|
||||
playerData.put("RootVehicle", oldData.getCompound("RootVehicle"));
|
||||
}
|
||||
}
|
||||
|
||||
File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir());
|
||||
NbtIo.writeCompressed(playerData, file);
|
||||
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat");
|
||||
File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old");
|
||||
Util.safeReplaceFile(file1, file, file2);
|
||||
} catch (Exception e) {
|
||||
LogManager.getLogger().warn("Failed to save player data for {}", player.getName().getString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_18_R1;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.internal.IPlayerDataManager;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.OpenInventoryView;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.logging.Logger;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.event.CraftEventFactory;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftContainer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class PlayerDataManager implements IPlayerDataManager {
|
||||
|
||||
private @Nullable Field bukkitEntity;
|
||||
|
||||
public PlayerDataManager() {
|
||||
try {
|
||||
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
|
||||
} catch (NoSuchFieldException e) {
|
||||
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
|
||||
logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
|
||||
logger.log(java.util.logging.Level.WARNING, e.getMessage(), e);
|
||||
bukkitEntity = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull ServerPlayer getHandle(final Player player) {
|
||||
if (player instanceof CraftPlayer) {
|
||||
return ((CraftPlayer) player).getHandle();
|
||||
}
|
||||
|
||||
Server server = player.getServer();
|
||||
ServerPlayer nmsPlayer = null;
|
||||
|
||||
if (server instanceof CraftServer) {
|
||||
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId());
|
||||
}
|
||||
|
||||
if (nmsPlayer == null) {
|
||||
// Could use reflection to examine fields, but it's honestly not worth the bother.
|
||||
throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation");
|
||||
}
|
||||
|
||||
return nmsPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
|
||||
// Ensure player has data
|
||||
if (!offline.hasPlayedBefore()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a profile and entity to load the player data
|
||||
// See net.minecraft.server.PlayerList#attemptLogin
|
||||
GameProfile profile = new GameProfile(offline.getUniqueId(),
|
||||
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
|
||||
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
ServerLevel worldServer = server.getLevel(Level.OVERWORLD);
|
||||
|
||||
if (worldServer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ServerPlayer entity = new ServerPlayer(server, worldServer, profile);
|
||||
|
||||
try {
|
||||
injectPlayer(entity);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Get the bukkit entity
|
||||
Player target = entity.getBukkitEntity();
|
||||
if (target != null) {
|
||||
// Load data
|
||||
target.loadData();
|
||||
}
|
||||
// Return the entity
|
||||
return target;
|
||||
}
|
||||
|
||||
void injectPlayer(ServerPlayer player) throws IllegalAccessException {
|
||||
if (bukkitEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
bukkitEntity.setAccessible(true);
|
||||
|
||||
bukkitEntity.set(player, new OpenPlayer(player.server.server, player));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Player inject(@NotNull Player player) {
|
||||
try {
|
||||
ServerPlayer nmsPlayer = getHandle(player);
|
||||
injectPlayer(nmsPlayer);
|
||||
return nmsPlayer.getBukkitEntity();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
|
||||
|
||||
ServerPlayer nmsPlayer = getHandle(player);
|
||||
|
||||
if (nmsPlayer.connection == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InventoryView view = getView(player, inventory);
|
||||
|
||||
if (view == null) {
|
||||
return player.openInventory(inventory.getBukkitInventory());
|
||||
}
|
||||
|
||||
AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
|
||||
@Override
|
||||
public MenuType<?> getType() {
|
||||
return getContainers(inventory.getBukkitInventory().getSize());
|
||||
}
|
||||
};
|
||||
|
||||
container.setTitle(new TextComponent(view.getTitle()));
|
||||
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
|
||||
|
||||
if (container == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(),
|
||||
new TextComponent(container.getBukkitView().getTitle())));
|
||||
nmsPlayer.containerMenu = container;
|
||||
nmsPlayer.initMenu(container);
|
||||
|
||||
return container.getBukkitView();
|
||||
|
||||
}
|
||||
|
||||
private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) {
|
||||
if (inventory instanceof SpecialEnderChest) {
|
||||
return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest");
|
||||
} else if (inventory instanceof SpecialPlayerInventory) {
|
||||
return new OpenInventoryView(player, inventory, "container.player", "'s Inventory");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static @NotNull MenuType<?> getContainers(int inventorySize) {
|
||||
|
||||
return switch (inventorySize) {
|
||||
case 9 -> MenuType.GENERIC_9x1;
|
||||
case 18 -> MenuType.GENERIC_9x2;
|
||||
case 36 -> MenuType.GENERIC_9x4; // PLAYER
|
||||
case 41, 45 -> MenuType.GENERIC_9x5;
|
||||
case 54 -> MenuType.GENERIC_9x6;
|
||||
default -> MenuType.GENERIC_9x3; // Default 27-slot inventory
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertToPlayerSlot(InventoryView view, int rawSlot) {
|
||||
int topSize = view.getTopInventory().getSize();
|
||||
if (topSize <= rawSlot) {
|
||||
// Slot is not inside special inventory, use Bukkit logic.
|
||||
return view.convertSlot(rawSlot);
|
||||
}
|
||||
|
||||
// Main inventory, slots 0-26 -> 9-35
|
||||
if (rawSlot < 27) {
|
||||
return rawSlot + 9;
|
||||
}
|
||||
// Hotbar, slots 27-35 -> 0-8
|
||||
if (rawSlot < 36) {
|
||||
return rawSlot - 27;
|
||||
}
|
||||
// Armor, slots 36-39 -> 39-36
|
||||
if (rawSlot < 40) {
|
||||
return 36 + (39 - rawSlot);
|
||||
}
|
||||
// Off hand
|
||||
if (rawSlot == 40) {
|
||||
return 40;
|
||||
}
|
||||
// Drop slots, "out of inventory"
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
+344
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_18_R1;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.ContainerListener;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.player.StackedContents;
|
||||
import net.minecraft.world.inventory.PlayerEnderChestContainer;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
|
||||
|
||||
private final CraftInventory inventory;
|
||||
private ServerPlayer owner;
|
||||
private NonNullList<ItemStack> items;
|
||||
private boolean playerOnline;
|
||||
|
||||
public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) {
|
||||
super(PlayerDataManager.getHandle(player));
|
||||
this.inventory = new CraftInventory(this);
|
||||
this.owner = PlayerDataManager.getHandle(player);
|
||||
this.playerOnline = online;
|
||||
this.items = this.owner.getEnderChestInventory().items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CraftInventory getBukkitInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUse() {
|
||||
return !this.getViewers().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {
|
||||
this.playerOnline = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) {
|
||||
if (!this.playerOnline) {
|
||||
try {
|
||||
this.owner = PlayerDataManager.getHandle(player);
|
||||
PlayerEnderChestContainer enderChest = owner.getEnderChestInventory();
|
||||
for (int i = 0; i < enderChest.getContainerSize(); ++i) {
|
||||
enderChest.setItem(i, this.items.get(i));
|
||||
}
|
||||
this.items = enderChest.items;
|
||||
enderChest.transaction.addAll(this.transaction);
|
||||
} catch (Exception ignored) {}
|
||||
this.playerOnline = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.entity.Player getPlayer() {
|
||||
return owner.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
this.owner.getEnderChestInventory().setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity who) {
|
||||
this.owner.getEnderChestInventory().onOpen(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity who) {
|
||||
this.owner.getEnderChestInventory().onClose(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return this.owner.getEnderChestInventory().getViewers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActiveChest(EnderChestBlockEntity enderChest) {
|
||||
this.owner.getEnderChestInventory().setActiveChest(enderChest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActiveChest(EnderChestBlockEntity enderChest) {
|
||||
return this.owner.getEnderChestInventory().isActiveChest(enderChest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return this.owner.getEnderChestInventory().getMaxStackSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int i) {
|
||||
this.owner.getEnderChestInventory().setMaxStackSize(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryHolder getOwner() {
|
||||
return this.owner.getEnderChestInventory().getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Location getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(ContainerListener listener) {
|
||||
this.owner.getEnderChestInventory().addListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(ContainerListener listener) {
|
||||
this.owner.getEnderChestInventory().removeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int i) {
|
||||
return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int i, int j) {
|
||||
ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j);
|
||||
if (!itemstack.isEmpty()) {
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack addItem(ItemStack itemstack) {
|
||||
ItemStack localItem = itemstack.copy();
|
||||
this.moveItemToOccupiedSlotsWithSameType(localItem);
|
||||
if (localItem.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
} else {
|
||||
this.moveItemToEmptySlots(localItem);
|
||||
return localItem.isEmpty() ? ItemStack.EMPTY : localItem;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAddItem(ItemStack itemstack) {
|
||||
for (ItemStack itemstack1 : this.items) {
|
||||
if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void moveItemToEmptySlots(ItemStack itemstack) {
|
||||
for(int i = 0; i < this.getContainerSize(); ++i) {
|
||||
ItemStack localItem = this.getItem(i);
|
||||
if (localItem.isEmpty()) {
|
||||
this.setItem(i, itemstack.copy());
|
||||
itemstack.setCount(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) {
|
||||
for(int i = 0; i < this.getContainerSize(); ++i) {
|
||||
ItemStack localItem = this.getItem(i);
|
||||
if (ItemStack.isSameItemSameTags(localItem, itemstack)) {
|
||||
this.moveItemsBetweenStacks(itemstack, localItem);
|
||||
if (itemstack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) {
|
||||
int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize());
|
||||
int j = Math.min(itemstack.getCount(), i - itemstack1.getCount());
|
||||
if (j > 0) {
|
||||
itemstack1.grow(j);
|
||||
itemstack.shrink(j);
|
||||
this.setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int i) {
|
||||
ItemStack itemstack = this.items.get(i);
|
||||
if (itemstack.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
} else {
|
||||
this.items.set(i, ItemStack.EMPTY);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int i, ItemStack itemstack) {
|
||||
this.items.set(i, itemstack);
|
||||
if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) {
|
||||
itemstack.setCount(this.getMaxStackSize());
|
||||
}
|
||||
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return this.owner.getEnderChestInventory().getContainerSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.items.stream().allMatch(ItemStack::isEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startOpen(Player player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopOpen(Player player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceItem(int i, ItemStack itemstack) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
this.items.clear();
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStackedContents(StackedContents stackedContents) {
|
||||
for (ItemStack itemstack : this.items) {
|
||||
stackedContents.accountStack(itemstack);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> removeAllItems() {
|
||||
List<ItemStack> list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList());
|
||||
this.clearContent();
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemType(Item item, int i) {
|
||||
ItemStack itemstack = new ItemStack(item, 0);
|
||||
|
||||
for(int j = this.getContainerSize() - 1; j >= 0; --j) {
|
||||
ItemStack localItem = this.getItem(j);
|
||||
if (localItem.getItem().equals(item)) {
|
||||
int k = i - itemstack.getCount();
|
||||
ItemStack splitItem = localItem.split(k);
|
||||
itemstack.grow(splitItem.getCount());
|
||||
if (itemstack.getCount() == i) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).collect(Collectors.toList()).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromTag(ListTag listTag) {
|
||||
for (int i = 0; i < this.getContainerSize(); ++i) {
|
||||
this.setItem(i, ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
for (int i = 0; i < listTag.size(); ++i) {
|
||||
CompoundTag compoundTag = listTag.getCompound(i);
|
||||
int j = compoundTag.getByte("Slot") & 255;
|
||||
if (j < this.getContainerSize()) {
|
||||
this.setItem(j, ItemStack.of(compoundTag));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+769
@@ -0,0 +1,769 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_18_R1;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.CrashReportCategory;
|
||||
import net.minecraft.ReportedException;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.tags.Tag;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.player.StackedContents;
|
||||
import net.minecraft.world.item.ArmorItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory {
|
||||
|
||||
private final CraftInventory inventory;
|
||||
private boolean playerOnline;
|
||||
private Player player;
|
||||
private NonNullList<ItemStack> items;
|
||||
private NonNullList<ItemStack> armor;
|
||||
private NonNullList<ItemStack> offhand;
|
||||
private List<NonNullList<ItemStack>> compartments;
|
||||
|
||||
public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) {
|
||||
super(PlayerDataManager.getHandle(bukkitPlayer));
|
||||
this.inventory = new CraftInventory(this);
|
||||
this.playerOnline = online;
|
||||
this.player = super.player;
|
||||
this.selected = player.getInventory().selected;
|
||||
this.items = this.player.getInventory().items;
|
||||
this.armor = this.player.getInventory().armor;
|
||||
this.offhand = this.player.getInventory().offhand;
|
||||
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
|
||||
if (!this.playerOnline) {
|
||||
Player entityPlayer = PlayerDataManager.getHandle(player);
|
||||
entityPlayer.getInventory().transaction.addAll(this.transaction);
|
||||
this.player = entityPlayer;
|
||||
for (int i = 0; i < getContainerSize(); ++i) {
|
||||
this.player.getInventory().setItem(i, getRawItem(i));
|
||||
}
|
||||
this.player.getInventory().selected = this.selected;
|
||||
this.items = this.player.getInventory().items;
|
||||
this.armor = this.player.getInventory().armor;
|
||||
this.offhand = this.player.getInventory().offhand;
|
||||
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
|
||||
this.playerOnline = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CraftInventory getBukkitInventory() {
|
||||
return this.inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {
|
||||
this.playerOnline = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUse() {
|
||||
return !this.getViewers().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HumanEntity getPlayer() {
|
||||
return this.player.getBukkitEntity();
|
||||
}
|
||||
|
||||
private @NotNull ItemStack getRawItem(int i) {
|
||||
return super.getItem(i);
|
||||
}
|
||||
|
||||
private void setRawItem(int i, @NotNull ItemStack itemStack) {
|
||||
super.setItem(i, itemStack);
|
||||
}
|
||||
|
||||
private static record IndexedCompartment(@Nullable NonNullList<ItemStack> compartment, int index) {}
|
||||
|
||||
private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) {
|
||||
if (index < items.size()) {
|
||||
return new IndexedCompartment(items, getReversedItemSlotNum(index));
|
||||
}
|
||||
|
||||
index -= items.size();
|
||||
|
||||
if (index < armor.size()) {
|
||||
return new IndexedCompartment(armor, getReversedArmorSlotNum(index));
|
||||
}
|
||||
|
||||
index -= armor.size();
|
||||
|
||||
if (index < offhand.size()) {
|
||||
return new IndexedCompartment(offhand, index);
|
||||
}
|
||||
|
||||
index -= offhand.size();
|
||||
|
||||
return new IndexedCompartment(null, index);
|
||||
}
|
||||
|
||||
private int getReversedArmorSlotNum(final int i) {
|
||||
if (i == 0) {
|
||||
return 3;
|
||||
}
|
||||
if (i == 1) {
|
||||
return 2;
|
||||
}
|
||||
if (i == 2) {
|
||||
return 1;
|
||||
}
|
||||
if (i == 3) {
|
||||
return 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
private int getReversedItemSlotNum(final int i) {
|
||||
if (i >= 27) {
|
||||
return i - 27;
|
||||
}
|
||||
return i + 9;
|
||||
}
|
||||
|
||||
private boolean contains(Predicate<ItemStack> predicate) {
|
||||
return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getArmorContents() {
|
||||
return this.armor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity who) {
|
||||
this.player.getInventory().onOpen(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity who) {
|
||||
this.player.getInventory().onClose(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return this.player.getInventory().getViewers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryHolder getOwner() {
|
||||
return this.player.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return this.player.getInventory().getMaxStackSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int size) {
|
||||
this.player.getInventory().setMaxStackSize(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return this.player.getBukkitEntity().getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCustomName() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getSelected() {
|
||||
return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) {
|
||||
return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int canHold(ItemStack itemstack) {
|
||||
int remains = itemstack.getCount();
|
||||
|
||||
for (int i = 0; i < this.items.size(); ++i) {
|
||||
ItemStack itemstack1 = this.getRawItem(i);
|
||||
if (itemstack1.isEmpty()) {
|
||||
return itemstack.getCount();
|
||||
}
|
||||
|
||||
if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) {
|
||||
remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount();
|
||||
}
|
||||
|
||||
if (remains <= 0) {
|
||||
return itemstack.getCount();
|
||||
}
|
||||
}
|
||||
|
||||
ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size());
|
||||
if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) {
|
||||
remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount();
|
||||
}
|
||||
|
||||
return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFreeSlot() {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
if (this.items.get(i).isEmpty()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPickedItem(ItemStack itemstack) {
|
||||
int i = this.findSlotMatchingItem(itemstack);
|
||||
if (isHotbarSlot(i)) {
|
||||
this.selected = i;
|
||||
} else if (i == -1) {
|
||||
this.selected = this.getSuitableHotbarSlot();
|
||||
if (!this.items.get(this.selected).isEmpty()) {
|
||||
int j = this.getFreeSlot();
|
||||
if (j != -1) {
|
||||
this.items.set(j, this.items.get(this.selected));
|
||||
}
|
||||
}
|
||||
|
||||
this.items.set(this.selected, itemstack);
|
||||
} else {
|
||||
this.pickSlot(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pickSlot(int i) {
|
||||
this.selected = this.getSuitableHotbarSlot();
|
||||
ItemStack itemstack = this.items.get(this.selected);
|
||||
this.items.set(this.selected, this.items.get(i));
|
||||
this.items.set(i, itemstack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findSlotMatchingItem(ItemStack itemstack) {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findSlotMatchingUnusedItem(ItemStack itemStack) {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
ItemStack localItem = this.items.get(i);
|
||||
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSuitableHotbarSlot() {
|
||||
int i;
|
||||
int j;
|
||||
for(j = 0; j < 9; ++j) {
|
||||
i = (this.selected + j) % 9;
|
||||
if (this.items.get(i).isEmpty()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
for(j = 0; j < 9; ++j) {
|
||||
i = (this.selected + j) % 9;
|
||||
if (!this.items.get(i).isEnchanted()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return this.selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swapPaint(double d0) {
|
||||
if (d0 > 0.0D) {
|
||||
d0 = 1.0D;
|
||||
}
|
||||
|
||||
if (d0 < 0.0D) {
|
||||
d0 = -1.0D;
|
||||
}
|
||||
|
||||
this.selected = (int) (this.selected - d0);
|
||||
|
||||
while (this.selected < 0) {
|
||||
this.selected += 9;
|
||||
}
|
||||
|
||||
while(this.selected >= 9) {
|
||||
this.selected -= 9;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int clearOrCountMatchingItems(Predicate<ItemStack> predicate, int i, Container container) {
|
||||
byte b0 = 0;
|
||||
boolean flag = i == 0;
|
||||
int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag);
|
||||
j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag);
|
||||
ItemStack itemstack = this.player.containerMenu.getCarried();
|
||||
j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag);
|
||||
if (itemstack.isEmpty()) {
|
||||
this.player.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
private int addResource(ItemStack itemstack) {
|
||||
int i = this.getSlotWithRemainingSpace(itemstack);
|
||||
if (i == -1) {
|
||||
i = this.getFreeSlot();
|
||||
}
|
||||
|
||||
return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack);
|
||||
}
|
||||
|
||||
private int addResource(int i, ItemStack itemstack) {
|
||||
Item item = itemstack.getItem();
|
||||
int j = itemstack.getCount();
|
||||
ItemStack localItemStack = this.getRawItem(i);
|
||||
if (localItemStack.isEmpty()) {
|
||||
localItemStack = new ItemStack(item, 0);
|
||||
if (itemstack.hasTag()) {
|
||||
// hasTag ensures tag not null
|
||||
//noinspection ConstantConditions
|
||||
localItemStack.setTag(itemstack.getTag().copy());
|
||||
}
|
||||
|
||||
this.setRawItem(i, localItemStack);
|
||||
}
|
||||
|
||||
int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount());
|
||||
|
||||
if (k > this.getMaxStackSize() - localItemStack.getCount()) {
|
||||
k = this.getMaxStackSize() - localItemStack.getCount();
|
||||
}
|
||||
|
||||
if (k != 0) {
|
||||
j -= k;
|
||||
localItemStack.grow(k);
|
||||
localItemStack.setPopTime(5);
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSlotWithRemainingSpace(ItemStack itemstack) {
|
||||
if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) {
|
||||
return this.selected;
|
||||
} else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) {
|
||||
return 40;
|
||||
} else {
|
||||
for(int i = 0; i < this.items.size(); ++i) {
|
||||
if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
for (int i = 0; i < compartment.size(); ++i) {
|
||||
if (!compartment.get(i).isEmpty()) {
|
||||
compartment.get(i).inventoryTick(this.player.level, this.player, i, this.selected == i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(ItemStack itemStack) {
|
||||
return this.add(-1, itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(int i, ItemStack itemStack) {
|
||||
if (itemStack.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
try {
|
||||
if (itemStack.isDamaged()) {
|
||||
if (i == -1) {
|
||||
i = this.getFreeSlot();
|
||||
}
|
||||
|
||||
if (i >= 0) {
|
||||
this.items.set(i, itemStack.copy());
|
||||
this.items.get(i).setPopTime(5);
|
||||
itemStack.setCount(0);
|
||||
return true;
|
||||
} else if (this.player.getAbilities().instabuild) {
|
||||
itemStack.setCount(0);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
int j;
|
||||
do {
|
||||
j = itemStack.getCount();
|
||||
if (i == -1) {
|
||||
itemStack.setCount(this.addResource(itemStack));
|
||||
} else {
|
||||
itemStack.setCount(this.addResource(i, itemStack));
|
||||
}
|
||||
} while(!itemStack.isEmpty() && itemStack.getCount() < j);
|
||||
|
||||
if (itemStack.getCount() == j && this.player.getAbilities().instabuild) {
|
||||
itemStack.setCount(0);
|
||||
return true;
|
||||
} else {
|
||||
return itemStack.getCount() < j;
|
||||
}
|
||||
}
|
||||
} catch (Throwable var6) {
|
||||
CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory");
|
||||
CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added");
|
||||
crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem()));
|
||||
crashReportCategory.setDetail("Item data", itemStack.getDamageValue());
|
||||
crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString());
|
||||
throw new ReportedException(crashReport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeItemBackInInventory(ItemStack itemStack) {
|
||||
this.placeItemBackInInventory(itemStack, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeItemBackInInventory(ItemStack itemStack, boolean flag) {
|
||||
while(true) {
|
||||
if (!itemStack.isEmpty()) {
|
||||
int i = this.getSlotWithRemainingSpace(itemStack);
|
||||
if (i == -1) {
|
||||
i = this.getFreeSlot();
|
||||
}
|
||||
|
||||
if (i != -1) {
|
||||
int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount();
|
||||
if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) {
|
||||
((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
this.player.drop(itemStack, false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int rawIndex, final int j) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null
|
||||
|| indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeItem(ItemStack itemStack) {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
for (int i = 0; i < compartment.size(); ++i) {
|
||||
if (compartment.get(i) == itemStack) {
|
||||
compartment.set(i, ItemStack.EMPTY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int rawIndex) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY);
|
||||
|
||||
if (removed.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int rawIndex, final ItemStack itemStack) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null) {
|
||||
this.player.drop(itemStack, true);
|
||||
return;
|
||||
}
|
||||
|
||||
indexedCompartment.compartment().set(indexedCompartment.index(), itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDestroySpeed(BlockState blockState) {
|
||||
return this.items.get(this.selected).getDestroySpeed(blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListTag save(ListTag listTag) {
|
||||
for (int i = 0; i < this.items.size(); ++i) {
|
||||
if (!this.items.get(i).isEmpty()) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putByte("Slot", (byte)i);
|
||||
this.items.get(i).save(compoundTag);
|
||||
listTag.add(compoundTag);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < this.armor.size(); ++i) {
|
||||
if (!this.armor.get(i).isEmpty()) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putByte("Slot", (byte)(i + 100));
|
||||
this.armor.get(i).save(compoundTag);
|
||||
listTag.add(compoundTag);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < this.offhand.size(); ++i) {
|
||||
if (!this.offhand.get(i).isEmpty()) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putByte("Slot", (byte)(i + 150));
|
||||
this.offhand.get(i).save(compoundTag);
|
||||
listTag.add(compoundTag);
|
||||
}
|
||||
}
|
||||
|
||||
return listTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(ListTag listTag) {
|
||||
this.items.clear();
|
||||
this.armor.clear();
|
||||
this.offhand.clear();
|
||||
|
||||
for(int i = 0; i < listTag.size(); ++i) {
|
||||
CompoundTag compoundTag = listTag.getCompound(i);
|
||||
int j = compoundTag.getByte("Slot") & 255;
|
||||
ItemStack itemstack = ItemStack.of(compoundTag);
|
||||
if (!itemstack.isEmpty()) {
|
||||
if (j < this.items.size()) {
|
||||
this.items.set(j, itemstack);
|
||||
} else if (j >= 100 && j < this.armor.size() + 100) {
|
||||
this.armor.set(j - 100, itemstack);
|
||||
} else if (j >= 150 && j < this.offhand.size() + 150) {
|
||||
this.offhand.set(j - 150, itemstack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return 45;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return !contains(itemStack -> !itemStack.isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int rawIndex) {
|
||||
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
|
||||
|
||||
if (indexedCompartment.compartment() == null) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
return indexedCompartment.compartment().get(indexedCompartment.index());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getName() {
|
||||
return this.player.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getArmor(int index) {
|
||||
return this.armor.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) {
|
||||
if (damage > 0.0F) {
|
||||
damage /= 4.0F;
|
||||
if (damage < 1.0F) {
|
||||
damage = 1.0F;
|
||||
}
|
||||
|
||||
for (int index : armorIndices) {
|
||||
ItemStack itemstack = this.armor.get(index);
|
||||
if ((!damagesource.isFire() || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) {
|
||||
itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropAll() {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
for (int i = 0; i < compartment.size(); ++i) {
|
||||
ItemStack itemstack = compartment.get(i);
|
||||
if (!itemstack.isEmpty()) {
|
||||
this.player.drop(itemstack, true, false);
|
||||
compartment.set(i, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
super.setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimesChanged() {
|
||||
return super.getTimesChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(ItemStack itemstack) {
|
||||
return contains(itemStack -> itemStack.isEmpty() && itemStack.sameItem(itemstack));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Tag<Item> tag) {
|
||||
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tag));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceWith(Inventory inventory) {
|
||||
Function<Integer, ItemStack> getter;
|
||||
|
||||
if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) {
|
||||
getter = specialPlayerInventory::getRawItem;
|
||||
} else {
|
||||
getter = inventory::getItem;
|
||||
}
|
||||
|
||||
for(int i = 0; i < this.getContainerSize(); ++i) {
|
||||
this.setRawItem(i, getter.apply(i));
|
||||
}
|
||||
|
||||
this.selected = inventory.selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
for (NonNullList<ItemStack> compartment : this.compartments) {
|
||||
compartment.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStackedContents(StackedContents stackedContents) {
|
||||
for (ItemStack itemstack : this.items) {
|
||||
stackedContents.accountSimpleStack(itemstack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeFromSelected(boolean dropWholeStack) {
|
||||
ItemStack itemstack = this.getSelected();
|
||||
return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1);
|
||||
}
|
||||
|
||||
}
|
||||
+41
-12
@@ -1,28 +1,48 @@
|
||||
<!--
|
||||
~ Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinv</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<version>4.1.9-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvplugin</artifactId>
|
||||
<artifactId>openinvplugincore</artifactId>
|
||||
<name>OpenInvPlugin</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
<!-- Really should use 1.4.5-R1.0, but the InventoryDragEvent doesn't exist. TODO separate module? -->
|
||||
<version>1.11-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvinternal</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<artifactId>annotations</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>planarwrappers</artifactId>
|
||||
<groupId>com.github.jikoo</groupId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -33,6 +53,15 @@
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
@@ -1,156 +1,343 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 lishid. All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.lishid.openinv.commands.ContainerSettingCommand;
|
||||
import com.lishid.openinv.commands.OpenInvCommand;
|
||||
import com.lishid.openinv.commands.SearchCommand;
|
||||
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.InventoryListener;
|
||||
import com.lishid.openinv.listeners.PlayerListener;
|
||||
import com.lishid.openinv.listeners.PluginListener;
|
||||
import com.lishid.openinv.util.Cache;
|
||||
import com.lishid.openinv.util.ConfigUpdater;
|
||||
import com.lishid.openinv.util.InternalAccessor;
|
||||
import com.lishid.openinv.util.LanguageManager;
|
||||
import com.lishid.openinv.util.Permissions;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import com.lishid.openinv.commands.AnyChestPluginCommand;
|
||||
import com.lishid.openinv.commands.OpenEnderPluginCommand;
|
||||
import com.lishid.openinv.commands.OpenInvPluginCommand;
|
||||
import com.lishid.openinv.commands.SearchInvPluginCommand;
|
||||
import com.lishid.openinv.commands.SilentChestPluginCommand;
|
||||
import com.lishid.openinv.internal.IAnySilentContainer;
|
||||
import com.lishid.openinv.internal.IInventoryAccess;
|
||||
import com.lishid.openinv.internal.IPlayerDataManager;
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.internal.InternalAccessor;
|
||||
import com.lishid.openinv.util.Cache;
|
||||
import com.lishid.openinv.util.Function;
|
||||
|
||||
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;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Open other player's inventory
|
||||
*
|
||||
*
|
||||
* @author lishid
|
||||
*/
|
||||
public class OpenInv extends JavaPlugin {
|
||||
public class OpenInv extends JavaPlugin implements IOpenInv {
|
||||
|
||||
private final Map<String, ISpecialPlayerInventory> inventories = new HashMap<String, ISpecialPlayerInventory>();
|
||||
private final Map<String, ISpecialEnderChest> enderChests = new HashMap<String, ISpecialEnderChest>();
|
||||
private final Cache<String, Player> playerCache = new Cache<String, Player>(300000L,
|
||||
new Function<Player>() {
|
||||
@Override
|
||||
public boolean run(Player value) {
|
||||
String key = playerLoader.getPlayerDataID(value);
|
||||
return inventories.containsKey(key) && inventories.get(key).isInUse()
|
||||
|| enderChests.containsKey(key) && enderChests.get(key).isInUse();
|
||||
}
|
||||
private final Map<String, ISpecialPlayerInventory> inventories = new HashMap<>();
|
||||
private final Map<String, ISpecialEnderChest> enderChests = new HashMap<>();
|
||||
private final Multimap<String, Class<? extends Plugin>> pluginUsage = HashMultimap.create();
|
||||
|
||||
private final Cache<String, Player> playerCache = new Cache<>(300000L,
|
||||
value -> {
|
||||
String key = OpenInv.this.getPlayerID(value);
|
||||
|
||||
return OpenInv.this.inventories.containsKey(key)
|
||||
&& OpenInv.this.inventories.get(key).isInUse()
|
||||
|| OpenInv.this.enderChests.containsKey(key)
|
||||
&& OpenInv.this.enderChests.get(key).isInUse()
|
||||
|| OpenInv.this.pluginUsage.containsKey(key);
|
||||
},
|
||||
new Function<Player>() {
|
||||
@Override
|
||||
public boolean run(Player value) {
|
||||
String key = playerLoader.getPlayerDataID(value);
|
||||
value -> {
|
||||
String key = OpenInv.this.getPlayerID(value);
|
||||
|
||||
// Check if inventory is stored, and if it is, remove it and eject all viewers
|
||||
if (inventories.containsKey(key)) {
|
||||
Inventory inv = inventories.remove(key).getBukkitInventory();
|
||||
for (HumanEntity entity : inv.getViewers()) {
|
||||
entity.closeInventory();
|
||||
}
|
||||
// Check if inventory is stored, and if it is, remove it and eject all viewers
|
||||
if (OpenInv.this.inventories.containsKey(key)) {
|
||||
Inventory inv = OpenInv.this.inventories.remove(key).getBukkitInventory();
|
||||
List<HumanEntity> viewers = new ArrayList<>(inv.getViewers());
|
||||
for (HumanEntity entity : viewers.toArray(new HumanEntity[0])) {
|
||||
entity.closeInventory();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if ender chest is stored, and if it is, remove it and eject all viewers
|
||||
if (enderChests.containsKey(key)) {
|
||||
Inventory inv = enderChests.remove(key).getBukkitInventory();
|
||||
for (HumanEntity entity : inv.getViewers()) {
|
||||
entity.closeInventory();
|
||||
}
|
||||
// Check if ender chest is stored, and if it is, remove it and eject all viewers
|
||||
if (OpenInv.this.enderChests.containsKey(key)) {
|
||||
Inventory inv = OpenInv.this.enderChests.remove(key).getBukkitInventory();
|
||||
List<HumanEntity> viewers = new ArrayList<>(inv.getViewers());
|
||||
for (HumanEntity entity : viewers.toArray(new HumanEntity[0])) {
|
||||
entity.closeInventory();
|
||||
}
|
||||
}
|
||||
|
||||
if (!OpenInv.this.disableSaving() && !value.isOnline()) {
|
||||
value.saveData();
|
||||
}
|
||||
return true;
|
||||
if (!OpenInv.this.disableSaving() && !value.isOnline()) {
|
||||
value.saveData();
|
||||
}
|
||||
});
|
||||
|
||||
private InternalAccessor accessor;
|
||||
private IPlayerDataManager playerLoader;
|
||||
private IInventoryAccess inventoryAccess;
|
||||
private IAnySilentContainer anySilentContainer;
|
||||
private LanguageManager languageManager;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
// Get plugin manager
|
||||
PluginManager pm = getServer().getPluginManager();
|
||||
/**
|
||||
* Evicts all viewers lacking cross-world permissions from a Player's inventory.
|
||||
*
|
||||
* @param player the Player
|
||||
*/
|
||||
public void changeWorld(final Player player) {
|
||||
|
||||
accessor = new InternalAccessor(this);
|
||||
// Version check
|
||||
if (!accessor.isSupported()) {
|
||||
getLogger().info("Your version of CraftBukkit (" + accessor.getVersion() + ") is not supported.");
|
||||
getLogger().info("Please look for an updated version of OpenInv.");
|
||||
pm.disablePlugin(this);
|
||||
String key = this.getPlayerID(player);
|
||||
|
||||
// Check if the player is cached. If not, neither of their inventories is open.
|
||||
if (!this.playerCache.containsKey(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
playerLoader = accessor.newPlayerDataManager();
|
||||
inventoryAccess = accessor.newInventoryAccess();
|
||||
anySilentContainer = accessor.newAnySilentContainer();
|
||||
|
||||
FileConfiguration config = getConfig();
|
||||
boolean dirtyConfig = false;
|
||||
if (!config.isBoolean("NotifySilentChest")) {
|
||||
config.set("NotifySilentChest", true);
|
||||
dirtyConfig = true;
|
||||
}
|
||||
if (!config.isBoolean("NotifyAnyChest")) {
|
||||
config.set("NotifyAnyChest", true);
|
||||
dirtyConfig = true;
|
||||
}
|
||||
if (!config.isBoolean("DisableSaving")) {
|
||||
config.set("DisableSaving", false);
|
||||
dirtyConfig = true;
|
||||
}
|
||||
config.addDefault("NotifySilentChest", true);
|
||||
config.addDefault("NotifyAnyChest", true);
|
||||
config.addDefault("DisableSaving", false);
|
||||
config.options().copyDefaults(true);
|
||||
if (dirtyConfig) {
|
||||
saveConfig();
|
||||
if (this.inventories.containsKey(key)) {
|
||||
kickCrossWorldViewers(player, this.inventories.get(key));
|
||||
}
|
||||
|
||||
pm.registerEvents(new OpenInvPlayerListener(this), this);
|
||||
pm.registerEvents(new OpenInvInventoryListener(this), this);
|
||||
if (this.enderChests.containsKey(key)) {
|
||||
kickCrossWorldViewers(player, this.enderChests.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
getCommand("openinv").setExecutor(new OpenInvPluginCommand(this));
|
||||
SearchInvPluginCommand searchInv = new SearchInvPluginCommand();
|
||||
getCommand("searchinv").setExecutor(searchInv);
|
||||
getCommand("searchender").setExecutor(searchInv);
|
||||
getCommand("silentchest").setExecutor(new SilentChestPluginCommand(this));
|
||||
getCommand("anychest").setExecutor(new AnyChestPluginCommand(this));
|
||||
getCommand("openender").setExecutor(new OpenEnderPluginCommand(this));
|
||||
private void kickCrossWorldViewers(Player player, ISpecialInventory inventory) {
|
||||
List<HumanEntity> viewers = new ArrayList<>(inventory.getBukkitInventory().getViewers());
|
||||
for (HumanEntity human : viewers) {
|
||||
// If player has permission or is in the same world, allow continued access
|
||||
// Just in case, also allow null worlds.
|
||||
if (Permissions.CROSSWORLD.hasPermission(human) || Objects.equals(human.getWorld(), player.getWorld())) {
|
||||
continue;
|
||||
}
|
||||
human.closeInventory();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public IAnySilentContainer getAnySilentContainer() {
|
||||
return this.accessor.getAnySilentContainer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPlayerAnyChestStatus(@NotNull final OfflinePlayer player) {
|
||||
boolean defaultState = false;
|
||||
|
||||
if (player.isOnline()) {
|
||||
Player onlinePlayer = player.getPlayer();
|
||||
if (onlinePlayer != null) {
|
||||
defaultState = Permissions.ANY_DEFAULT.hasPermission(onlinePlayer);
|
||||
}
|
||||
}
|
||||
|
||||
return this.getConfig().getBoolean("toggles.any-chest." + this.getPlayerID(player), defaultState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPlayerSilentChestStatus(@NotNull final OfflinePlayer offline) {
|
||||
boolean defaultState = false;
|
||||
|
||||
if (offline.isOnline()) {
|
||||
Player onlinePlayer = offline.getPlayer();
|
||||
if (onlinePlayer != null) {
|
||||
defaultState = Permissions.SILENT_DEFAULT.hasPermission(onlinePlayer);
|
||||
}
|
||||
}
|
||||
|
||||
return this.getConfig().getBoolean("toggles.silent-chest." + this.getPlayerID(offline), defaultState);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online)
|
||||
throws InstantiationException {
|
||||
String id = this.getPlayerID(player);
|
||||
if (this.enderChests.containsKey(id)) {
|
||||
return this.enderChests.get(id);
|
||||
}
|
||||
ISpecialEnderChest inv = this.accessor.newSpecialEnderChest(player, online);
|
||||
this.enderChests.put(id, inv);
|
||||
this.playerCache.put(id, player);
|
||||
return inv;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ISpecialPlayerInventory getSpecialInventory(@NotNull final Player player, final boolean online)
|
||||
throws InstantiationException {
|
||||
String id = this.getPlayerID(player);
|
||||
if (this.inventories.containsKey(id)) {
|
||||
return this.inventories.get(id);
|
||||
}
|
||||
ISpecialPlayerInventory inv = this.accessor.newSpecialPlayerInventory(player, online);
|
||||
this.inventories.put(id, inv);
|
||||
this.playerCache.put(id, player);
|
||||
return inv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupportedVersion() {
|
||||
return this.accessor != null && this.accessor.isSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
|
||||
|
||||
String key = this.getPlayerID(offline);
|
||||
if (this.playerCache.containsKey(key)) {
|
||||
return this.playerCache.get(key);
|
||||
}
|
||||
|
||||
Player player = offline.getPlayer();
|
||||
if (player != null) {
|
||||
this.playerCache.put(key, player);
|
||||
return player;
|
||||
}
|
||||
|
||||
if (!this.isSupportedVersion()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
return this.accessor.getPlayerDataManager().loadPlayer(offline);
|
||||
}
|
||||
|
||||
Future<Player> future = Bukkit.getScheduler().callSyncMethod(this,
|
||||
() -> OpenInv.this.accessor.getPlayerDataManager().loadPlayer(offline));
|
||||
|
||||
try {
|
||||
player = future.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (player != null) {
|
||||
this.playerCache.put(key, player);
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
|
||||
return this.accessor.getPlayerDataManager().openInventory(player, inventory);
|
||||
}
|
||||
|
||||
public void sendMessage(@NotNull CommandSender sender, @NotNull String key) {
|
||||
String message = this.languageManager.getValue(key, getLocale(sender));
|
||||
|
||||
if (message != null && !message.isEmpty()) {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessage(@NotNull CommandSender sender, @NotNull String key, String... replacements) {
|
||||
String message = this.languageManager.getValue(key, getLocale(sender), replacements);
|
||||
|
||||
if (message != null && !message.isEmpty()) {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendSystemMessage(@NotNull Player player, @NotNull String key) {
|
||||
this.sendSystemMessage(player, key, new String[0]);
|
||||
}
|
||||
|
||||
public void sendSystemMessage(@NotNull Player player, @NotNull String key, String... replacements) {
|
||||
String message = this.languageManager.getValue(key, getLocale(player));
|
||||
|
||||
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) {
|
||||
return this.languageManager.getValue(key, getLocale(sender));
|
||||
}
|
||||
|
||||
public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key, String... replacements) {
|
||||
return this.languageManager.getValue(key, getLocale(sender), replacements);
|
||||
}
|
||||
|
||||
private @NotNull String getLocale(@NotNull CommandSender sender) {
|
||||
if (sender instanceof Player) {
|
||||
return ((Player) sender).getLocale();
|
||||
} else {
|
||||
return this.getConfig().getString("settings.locale", "en_us");
|
||||
}
|
||||
}
|
||||
|
||||
public int getSearchPollsPerTick() {
|
||||
return this.getConfig().getInt("settings.search.polls-per-tick", 5);
|
||||
}
|
||||
|
||||
public int getSearchResultsMax() {
|
||||
return this.getConfig().getInt("settings.search.max-results", 40);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -160,309 +347,146 @@ public class OpenInv extends JavaPlugin {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playerCache.invalidateAll();
|
||||
if (this.isSupportedVersion()) {
|
||||
this.playerCache.invalidateAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
|
||||
// Save default configuration if not present.
|
||||
this.saveDefaultConfig();
|
||||
|
||||
// Get plugin manager
|
||||
PluginManager pm = this.getServer().getPluginManager();
|
||||
|
||||
this.accessor = new InternalAccessor(this);
|
||||
|
||||
this.languageManager = new LanguageManager(this, "en_us");
|
||||
|
||||
// Version check
|
||||
if (this.accessor.isSupported()) {
|
||||
// Update existing configuration. May require internal access.
|
||||
new ConfigUpdater(this).checkForUpdates();
|
||||
|
||||
// Register listeners
|
||||
pm.registerEvents(new PlayerListener(this), this);
|
||||
pm.registerEvents(new PluginListener(this), this);
|
||||
pm.registerEvents(new InventoryListener(this), this);
|
||||
|
||||
// Register commands to their executors
|
||||
this.setCommandExecutor(new OpenInvCommand(this), "openinv", "openender");
|
||||
this.setCommandExecutor(new SearchCommand(this), "search");
|
||||
this.setCommandExecutor(new ContainerSettingCommand(this), "silentcontainer", "anycontainer");
|
||||
|
||||
} else {
|
||||
this.sendVersionError(this.getLogger()::warning);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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()) {
|
||||
this.sendVersionError(sender::sendMessage);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void releaseAllPlayers(final Plugin plugin) {
|
||||
Iterator<Map.Entry<String, Class<? extends Plugin>>> iterator = this.pluginUsage.entries().iterator();
|
||||
|
||||
if (!iterator.hasNext()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Class<? extends Plugin>> entry = iterator.next(); iterator.hasNext(); entry = iterator.next()) {
|
||||
if (entry.getValue().equals(plugin.getClass())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releasePlayer(@NotNull final Player player, @NotNull final Plugin plugin) {
|
||||
String key = this.getPlayerID(player);
|
||||
|
||||
if (!this.pluginUsage.containsEntry(key, plugin.getClass())) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.pluginUsage.remove(key, plugin.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retainPlayer(@NotNull final Player player, @NotNull final Plugin plugin) {
|
||||
String key = this.getPlayerID(player);
|
||||
|
||||
if (this.pluginUsage.containsEntry(key, plugin.getClass())) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.pluginUsage.put(key, plugin.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerAnyChestStatus(@NotNull final OfflinePlayer offline, final boolean status) {
|
||||
this.getConfig().set("toggles.any-chest." + this.getPlayerID(offline), status);
|
||||
this.saveConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the server version is supported by OpenInv.
|
||||
*
|
||||
* @return true if the server version is supported
|
||||
*/
|
||||
public boolean isSupportedVersion() {
|
||||
return accessor.isSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the active IInventoryAccess implementation. May return null if the server version is
|
||||
* unsupported.
|
||||
*
|
||||
* @return the IInventoryAccess
|
||||
*/
|
||||
public IInventoryAccess getInventoryAccess() {
|
||||
return this.inventoryAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the active ISilentContainer implementation. May return null if the server version is
|
||||
* unsupported.
|
||||
*
|
||||
* @return the ISilentContainer
|
||||
*/
|
||||
public IAnySilentContainer getAnySilentContainer() {
|
||||
return this.anySilentContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an ISpecialPlayerInventory for the given Player.
|
||||
*
|
||||
* Method for handling a Player going offline.
|
||||
*
|
||||
* @param player the Player
|
||||
* @param online true if the Player is currently online
|
||||
* @return the ISpecialPlayerInventory
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
*/
|
||||
public ISpecialPlayerInventory getInventory(Player player, boolean online) {
|
||||
String id = playerLoader.getPlayerDataID(player);
|
||||
if (inventories.containsKey(id)) {
|
||||
return inventories.get(id);
|
||||
}
|
||||
ISpecialPlayerInventory inv = accessor.newSpecialPlayerInventory(player, online);
|
||||
inventories.put(id, inv);
|
||||
playerCache.put(id, player);
|
||||
return inv;
|
||||
}
|
||||
public void setPlayerOffline(final Player player) {
|
||||
|
||||
/**
|
||||
* Gets an ISpecialEnderChest for the given Player.
|
||||
*
|
||||
* @param player the Player
|
||||
* @param online true if the Player is currently online
|
||||
* @return the ISpecialEnderChest
|
||||
*/
|
||||
public ISpecialEnderChest getEnderChest(Player player, boolean online) {
|
||||
String id = playerLoader.getPlayerDataID(player);
|
||||
if (enderChests.containsKey(id)) {
|
||||
return enderChests.get(id);
|
||||
}
|
||||
ISpecialEnderChest inv = accessor.newSpecialEnderChest(player, online);
|
||||
enderChests.put(id, inv);
|
||||
playerCache.put(id, player);
|
||||
return inv;
|
||||
}
|
||||
String key = this.getPlayerID(player);
|
||||
|
||||
/**
|
||||
* Forcibly unload a cached Player's data.
|
||||
*
|
||||
* @param player the OfflinePlayer to unload
|
||||
*/
|
||||
public void unload(OfflinePlayer player) {
|
||||
this.playerCache.invalidate(this.playerLoader.getPlayerDataID(player));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the configuration value for whether or not OpenInv saves player data when unloading
|
||||
* players. This is exclusively for users who do not allow editing of inventories, only viewing,
|
||||
* and wish to prevent any possibility of bugs such as lishid#40. If true, OpenInv will not ever
|
||||
* save any edits made to players.
|
||||
*
|
||||
* @return false unless configured otherwise
|
||||
*/
|
||||
public boolean disableSaving() {
|
||||
return getConfig().getBoolean("DisableSaving", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the configuration value for whether or not OpenInv displays a notification to the user
|
||||
* when a container is activated with SilentChest.
|
||||
*
|
||||
* @return true unless configured otherwise
|
||||
*/
|
||||
public boolean notifySilentChest() {
|
||||
return getConfig().getBoolean("NotifySilentChest", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the configuration value for whether or not OpenInv displays a notification to the user
|
||||
* when a container is activated with AnyChest.
|
||||
*
|
||||
* @return true unless configured otherwise
|
||||
*/
|
||||
public boolean notifyAnyChest() {
|
||||
return getConfig().getBoolean("NotifyAnyChest", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a player's SilentChest setting.
|
||||
*
|
||||
* @param player the OfflinePlayer
|
||||
* @return true if SilentChest is enabled
|
||||
*/
|
||||
public boolean getPlayerSilentChestStatus(OfflinePlayer player) {
|
||||
return getConfig().getBoolean("SilentChest." + playerLoader.getPlayerDataID(player) + ".toggle", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a player's SilentChest setting.
|
||||
*
|
||||
* @param player the OfflinePlayer
|
||||
* @param status the status
|
||||
*/
|
||||
public void setPlayerSilentChestStatus(OfflinePlayer player, boolean status) {
|
||||
getConfig().set("SilentChest." + playerLoader.getPlayerDataID(player) + ".toggle", status);
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the provided player's AnyChest setting.
|
||||
*
|
||||
* @param player the OfflinePlayer
|
||||
* @return true if AnyChest is enabled
|
||||
*/
|
||||
public boolean getPlayerAnyChestStatus(OfflinePlayer player) {
|
||||
return getConfig().getBoolean("AnyChest." + playerLoader.getPlayerDataID(player) + ".toggle", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a player's AnyChest setting.
|
||||
*
|
||||
* @param player the OfflinePlayer
|
||||
* @param status the status
|
||||
*/
|
||||
public void setPlayerAnyChestStatus(OfflinePlayer player, boolean status) {
|
||||
getConfig().set("AnyChest." + playerLoader.getPlayerDataID(player) + ".toggle", status);
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an OfflinePlayer by name.
|
||||
* <p>
|
||||
* Note: This method is potentially very heavily blocking. It should not ever be called on the
|
||||
* main thread, and if it is, a stack trace will be displayed alerting server owners to the
|
||||
* call.
|
||||
*
|
||||
* @param name the name of the Player
|
||||
* @return the OfflinePlayer with the closest matching name or null if no players have ever logged in
|
||||
*/
|
||||
public OfflinePlayer matchPlayer(String name) {
|
||||
|
||||
// Warn if called on the main thread - if we resort to searching offline players, this may take several seconds.
|
||||
if (getServer().isPrimaryThread()) {
|
||||
getLogger().warning("Call to OpenInv#matchPlayer made on the main thread!");
|
||||
getLogger().warning("This can cause the server to hang, potentially severely.");
|
||||
getLogger().warning("Trace:");
|
||||
for (StackTraceElement element : new Throwable().fillInStackTrace().getStackTrace()) {
|
||||
getLogger().warning(element.toString());
|
||||
}
|
||||
// Check if the player is cached. If not, neither of their inventories is open.
|
||||
if (!this.playerCache.containsKey(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure name is valid if server is in online mode to avoid unnecessary searching
|
||||
if (getServer().getOnlineMode() && !name.matches("[a-zA-Z0-9_]{3,16}")) {
|
||||
return null;
|
||||
// Replace stored player with our own version
|
||||
this.playerCache.put(key, this.accessor.getPlayerDataManager().inject(player));
|
||||
|
||||
if (this.inventories.containsKey(key)) {
|
||||
this.inventories.get(key).setPlayerOffline();
|
||||
}
|
||||
|
||||
OfflinePlayer player = getServer().getPlayerExact(name);
|
||||
|
||||
if (player != null) {
|
||||
return player;
|
||||
if (this.enderChests.containsKey(key)) {
|
||||
this.enderChests.get(key).setPlayerOffline();
|
||||
}
|
||||
|
||||
player = getServer().getOfflinePlayer(name);
|
||||
|
||||
/*
|
||||
* Compatibility: Pre-UUID, getOfflinePlayer always returns an OfflinePlayer. Post-UUID,
|
||||
* getOfflinePlayer will return null if no matching player is found. To preserve
|
||||
* compatibility, only return the player if they have played before. Ignoring current online
|
||||
* status is fine, they'd have been found by getPlayerExact otherwise.
|
||||
*/
|
||||
if (player != null && player.hasPlayedBefore()) {
|
||||
return player;
|
||||
}
|
||||
|
||||
player = getServer().getPlayer(name);
|
||||
|
||||
if (player != null) {
|
||||
return player;
|
||||
}
|
||||
|
||||
int bestMatch = Integer.MAX_VALUE;
|
||||
for (OfflinePlayer offline : getServer().getOfflinePlayers()) {
|
||||
if (offline.getName() == null) {
|
||||
// Loaded by UUID only, name has never been looked up.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compatibility: Lang3 is only bundled with 1.8+
|
||||
int currentMatch = org.apache.commons.lang.StringUtils.getLevenshteinDistance(name, offline.getName());
|
||||
|
||||
if (currentMatch == 0) {
|
||||
return offline;
|
||||
}
|
||||
|
||||
if (currentMatch < bestMatch) {
|
||||
bestMatch = currentMatch;
|
||||
player = offline;
|
||||
}
|
||||
}
|
||||
|
||||
// Only null if no players have played ever, otherwise even the worst match will do.
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a Player from an OfflinePlayer. May return null under some circumstances.
|
||||
*
|
||||
* @param offline the OfflinePlayer to load a Player for
|
||||
* @return the Player
|
||||
*/
|
||||
public Player loadPlayer(final OfflinePlayer offline) {
|
||||
|
||||
if (offline == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String key = this.playerLoader.getPlayerDataID(offline);
|
||||
if (this.playerCache.containsKey(key)) {
|
||||
return this.playerCache.get(key);
|
||||
}
|
||||
|
||||
Player loaded;
|
||||
|
||||
if (offline.isOnline()) {
|
||||
loaded = offline.getPlayer();
|
||||
this.playerCache.put(key, loaded);
|
||||
return loaded;
|
||||
}
|
||||
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
return this.playerLoader.loadPlayer(offline);
|
||||
}
|
||||
|
||||
Future<Player> future = Bukkit.getScheduler().callSyncMethod(this,
|
||||
new Callable<Player>() {
|
||||
@Override
|
||||
public Player call() throws Exception {
|
||||
return playerLoader.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();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch (ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (loaded != null) {
|
||||
this.playerCache.put(key, loaded);
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for handling a Player coming online.
|
||||
*
|
||||
*
|
||||
* @param player the Player
|
||||
* @throws IllegalStateException if the server version is unsupported
|
||||
*/
|
||||
public void setPlayerOnline(final Player player) {
|
||||
|
||||
String key = this.playerLoader.getPlayerDataID(player);
|
||||
String key = this.getPlayerID(player);
|
||||
|
||||
// Check if the player is cached. If not, neither of their inventories is open.
|
||||
if (!this.playerCache.containsKey(key)) {
|
||||
@@ -488,76 +512,18 @@ public class OpenInv extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for handling a Player going offline.
|
||||
*
|
||||
* @param player the Player
|
||||
*/
|
||||
public void setPlayerOffline(final Player player) {
|
||||
|
||||
String key = this.playerLoader.getPlayerDataID(player);
|
||||
|
||||
// Check if the player is cached. If not, neither of their inventories is open.
|
||||
if (!this.playerCache.containsKey(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.inventories.containsKey(key)) {
|
||||
this.inventories.get(key).setPlayerOffline();
|
||||
}
|
||||
|
||||
if (this.enderChests.containsKey(key)) {
|
||||
this.enderChests.get(key).setPlayerOffline();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evicts all viewers lacking cross-world permissions from a Player's inventory.
|
||||
*
|
||||
* @param player the Player
|
||||
*/
|
||||
public void changeWorld(final Player player) {
|
||||
|
||||
String key = this.playerLoader.getPlayerDataID(player);
|
||||
|
||||
// Check if the player is cached. If not, neither of their inventories is open.
|
||||
if (!this.playerCache.containsKey(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.inventories.containsKey(key)) {
|
||||
Iterator<HumanEntity> iterator = this.inventories.get(key).getBukkitInventory().getViewers().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
HumanEntity human = iterator.next();
|
||||
// If player has permission or is in the same world, allow continued access
|
||||
// Just in case, also allow null worlds.
|
||||
if (Permissions.CROSSWORLD.hasPermission(human) || human.getWorld() == null
|
||||
|| human.getWorld().equals(player.getWorld())) {
|
||||
continue;
|
||||
}
|
||||
human.closeInventory();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.enderChests.containsKey(key)) {
|
||||
Iterator<HumanEntity> iterator = this.enderChests.get(key).getBukkitInventory().getViewers().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
HumanEntity human = iterator.next();
|
||||
if (Permissions.CROSSWORLD.hasPermission(human) || human.getWorld() == null
|
||||
|| human.getWorld().equals(player.getWorld())) {
|
||||
continue;
|
||||
}
|
||||
human.closeInventory();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void setPlayerSilentChestStatus(@NotNull final OfflinePlayer offline, final boolean status) {
|
||||
this.getConfig().set("toggles.silent-chest." + this.getPlayerID(offline), status);
|
||||
this.saveConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays all applicable help for OpenInv commands.
|
||||
*
|
||||
*
|
||||
* @param player the Player to help
|
||||
*/
|
||||
public void showHelp(Player player) {
|
||||
public void showHelp(final Player player) {
|
||||
// Get registered commands
|
||||
for (String commandName : this.getDescription().getCommands().keySet()) {
|
||||
PluginCommand command = this.getCommand(commandName);
|
||||
@@ -587,4 +553,9 @@ public class OpenInv extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload(@NotNull final OfflinePlayer offline) {
|
||||
this.playerCache.invalidate(this.getPlayerID(offline));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv;
|
||||
|
||||
import 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.event.inventory.InventoryDragEvent;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
|
||||
public class OpenInvInventoryListener implements Listener {
|
||||
|
||||
private final OpenInv plugin;
|
||||
|
||||
public OpenInvInventoryListener(OpenInv plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
if (cancelInteract(event.getWhoClicked(), event.getInventory())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onInventoryDrag(InventoryDragEvent event) {
|
||||
if (cancelInteract(event.getWhoClicked(), event.getInventory())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean cancelInteract(HumanEntity entity, Inventory inventory) {
|
||||
return plugin.getInventoryAccess().isSpecialPlayerInventory(inventory)
|
||||
&& !Permissions.EDITINV.hasPermission(entity)
|
||||
|| plugin.getInventoryAccess().isSpecialEnderChest(inventory)
|
||||
&& !Permissions.EDITENDER.hasPermission(entity);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldChange(PlayerChangedWorldEvent event) {
|
||||
plugin.changeWorld(event.getPlayer());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Event.Result;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
public class OpenInvPlayerListener implements Listener {
|
||||
|
||||
private final OpenInv plugin;
|
||||
|
||||
public OpenInvPlayerListener(OpenInv plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerJoin(final PlayerJoinEvent event) {
|
||||
plugin.setPlayerOnline(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
plugin.setPlayerOffline(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||
if (event.getAction() != Action.RIGHT_CLICK_BLOCK || event.getPlayer().isSneaking()
|
||||
|| event.useInteractedBlock() == Result.DENY
|
||||
|| !plugin.getAnySilentContainer().isAnySilentContainer(event.getClickedBlock())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = event.getPlayer();
|
||||
boolean anychest = Permissions.ANYCHEST.hasPermission(player) && plugin.getPlayerAnyChestStatus(player);
|
||||
boolean needsAnyChest = plugin.getAnySilentContainer().isAnyContainerNeeded(player, event.getClickedBlock());
|
||||
|
||||
if (!anychest && needsAnyChest) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean silentchest = Permissions.SILENT.hasPermission(player) && plugin.getPlayerSilentChestStatus(player);
|
||||
|
||||
// If anychest or silentchest is active
|
||||
if ((anychest || silentchest) && plugin.getAnySilentContainer().activateContainer(player, silentchest, event.getClickedBlock())) {
|
||||
if (silentchest && plugin.notifySilentChest() && needsAnyChest && plugin.notifyAnyChest()) {
|
||||
player.sendMessage("You are opening a blocked container silently.");
|
||||
} else if (silentchest && plugin.notifySilentChest()) {
|
||||
player.sendMessage("You are opening a container silently.");
|
||||
} else if (needsAnyChest && plugin.notifyAnyChest()) {
|
||||
player.sendMessage("You are opening a blocked container.");
|
||||
}
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.lishid.openinv;
|
||||
|
||||
import org.bukkit.permissions.Permissible;
|
||||
|
||||
public enum Permissions {
|
||||
|
||||
OPENINV("OpenInv.openinv"),
|
||||
OVERRIDE("OpenInv.override"),
|
||||
EXEMPT("OpenInv.exempt"),
|
||||
CROSSWORLD("OpenInv.crossworld"),
|
||||
SILENT("OpenInv.silent"),
|
||||
ANYCHEST("OpenInv.anychest"),
|
||||
ENDERCHEST("OpenInv.openender"),
|
||||
ENDERCHEST_ALL("OpenInv.openenderall"),
|
||||
SEARCH("OpenInv.search"),
|
||||
EDITINV("OpenInv.editinv"),
|
||||
EDITENDER("OpenInv.editender"),
|
||||
OPENSELF("OpenInv.openself");
|
||||
|
||||
private final String permission;
|
||||
|
||||
private Permissions(String permission) {
|
||||
this.permission = permission;
|
||||
}
|
||||
|
||||
public boolean hasPermission(Permissible permissible) {
|
||||
String[] parts = permission.split("\\.");
|
||||
String perm = "";
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
if (permissible.hasPermission(perm + "*")) {
|
||||
return true;
|
||||
}
|
||||
perm += parts[i] + ".";
|
||||
}
|
||||
return permissible.hasPermission(permission);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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.commands;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
|
||||
public class AnyChestPluginCommand implements CommandExecutor {
|
||||
|
||||
private final OpenInv plugin;
|
||||
|
||||
public AnyChestPluginCommand(OpenInv plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
sender.sendMessage(ChatColor.RED + "You can't use this from the console.");
|
||||
return true;
|
||||
}
|
||||
|
||||
Player player = (Player) sender;
|
||||
|
||||
if (args.length > 0 && args[0].equalsIgnoreCase("check")) {
|
||||
sender.sendMessage("AnyChest is " + (plugin.getPlayerAnyChestStatus(player) ? "ON" : "OFF") + ".");
|
||||
return true;
|
||||
}
|
||||
|
||||
plugin.setPlayerAnyChestStatus(player, !plugin.getPlayerAnyChestStatus(player));
|
||||
sender.sendMessage("AnyChest is now " + (plugin.getPlayerAnyChestStatus(player) ? "ON" : "OFF") + ".");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.commands;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ContainerSettingCommand implements TabExecutor {
|
||||
|
||||
private final OpenInv plugin;
|
||||
|
||||
public ContainerSettingCommand(final OpenInv plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
plugin.sendMessage(sender, "messages.error.consoleUnsupported");
|
||||
return true;
|
||||
}
|
||||
|
||||
Player player = (Player) sender;
|
||||
boolean any = command.getName().startsWith("any");
|
||||
Predicate<Player> getSetting = any ? plugin::getPlayerAnyChestStatus : plugin::getPlayerSilentChestStatus;
|
||||
BiConsumer<OfflinePlayer, Boolean> setSetting = any ? plugin::setPlayerAnyChestStatus : plugin::setPlayerSilentChestStatus;
|
||||
|
||||
if (args.length > 0) {
|
||||
args[0] = args[0].toLowerCase();
|
||||
|
||||
if (args[0].equals("on")) {
|
||||
setSetting.accept(player, true);
|
||||
} else if (args[0].equals("off")) {
|
||||
setSetting.accept(player, false);
|
||||
} else if (!args[0].equals("check")) {
|
||||
// Invalid argument, show usage.
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
setSetting.accept(player, !getSetting.test(player));
|
||||
}
|
||||
|
||||
String onOff = plugin.getLocalizedMessage(player, getSetting.test(player) ? "messages.info.on" : "messages.info.off");
|
||||
if (onOff == null) {
|
||||
onOff = String.valueOf(getSetting.test(player));
|
||||
}
|
||||
|
||||
plugin.sendMessage(sender, "messages.info.settingState","%setting%", any ? "AnyContainer" : "SilentContainer", "%state%", onOff);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!command.testPermissionSilent(sender) || args.length != 1) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return TabCompleter.completeString(args[0], new String[] {"check", "on", "off"});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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.commands;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.Permissions;
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
public class OpenEnderPluginCommand implements CommandExecutor {
|
||||
|
||||
private final OpenInv plugin;
|
||||
private final HashMap<Player, String> openEnderHistory = new HashMap<Player, String>();
|
||||
|
||||
public OpenEnderPluginCommand(OpenInv plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
sender.sendMessage(ChatColor.RED + "You can't use this from the console.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length > 0 && args[0].equalsIgnoreCase("?")) {
|
||||
plugin.showHelp((Player) sender);
|
||||
return true;
|
||||
}
|
||||
|
||||
final Player player = (Player) sender;
|
||||
|
||||
// History management
|
||||
String history = openEnderHistory.get(player);
|
||||
|
||||
if (history == null || history == "") {
|
||||
history = player.getName();
|
||||
openEnderHistory.put(player, history);
|
||||
}
|
||||
|
||||
final String name;
|
||||
|
||||
// Read from history if target is not named
|
||||
if (args.length < 1) {
|
||||
name = history;
|
||||
} else {
|
||||
name = args[0];
|
||||
}
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final OfflinePlayer offlinePlayer = plugin.matchPlayer(name);
|
||||
|
||||
if (offlinePlayer == null || !offlinePlayer.hasPlayedBefore() && !offlinePlayer.isOnline()) {
|
||||
player.sendMessage(ChatColor.RED + "Player not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!player.isOnline()) {
|
||||
return;
|
||||
}
|
||||
openInventory(player, offlinePlayer);
|
||||
}
|
||||
}.runTask(plugin);
|
||||
|
||||
}
|
||||
}.runTaskAsynchronously(plugin);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void openInventory(Player player, OfflinePlayer target) {
|
||||
|
||||
Player onlineTarget;
|
||||
boolean online = target.isOnline();
|
||||
|
||||
if (!online) {
|
||||
// Try loading the player's data
|
||||
onlineTarget = plugin.loadPlayer(target);
|
||||
|
||||
if (onlineTarget == null) {
|
||||
player.sendMessage(ChatColor.RED + "Player not found!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
onlineTarget = target.getPlayer();
|
||||
}
|
||||
|
||||
if (!onlineTarget.equals(player)) {
|
||||
if (!Permissions.ENDERCHEST_ALL.hasPermission(player)) {
|
||||
player.sendMessage(ChatColor.RED + "You do not have permission to access other players' enderchests.");
|
||||
return;
|
||||
}
|
||||
if (!Permissions.CROSSWORLD.hasPermission(player) && !player.getWorld().equals(onlineTarget.getWorld())) {
|
||||
player.sendMessage(ChatColor.RED + onlineTarget.getDisplayName() + " is not in your world!");
|
||||
return;
|
||||
}
|
||||
if (!Permissions.OVERRIDE.hasPermission(player) && Permissions.EXEMPT.hasPermission(onlineTarget)) {
|
||||
player.sendMessage(ChatColor.RED + onlineTarget.getDisplayName() + "'s inventory is protected!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Record the target
|
||||
openEnderHistory.put(player, onlineTarget.getName());
|
||||
|
||||
// Create the inventory
|
||||
ISpecialEnderChest chest = plugin.getEnderChest(onlineTarget, online);
|
||||
|
||||
// Open the inventory
|
||||
player.openInventory(chest.getBukkitInventory());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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.commands;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.util.Permissions;
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class OpenInvCommand implements TabExecutor {
|
||||
|
||||
private final OpenInv plugin;
|
||||
private final HashMap<Player, String> openInvHistory = new HashMap<>();
|
||||
private final HashMap<Player, String> openEnderHistory = new HashMap<>();
|
||||
|
||||
public OpenInvCommand(final OpenInv plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Command command, @NotNull final String label, @NotNull final String[] args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
plugin.sendMessage(sender, "messages.error.consoleUnsupported");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length > 0 && (args[0].equalsIgnoreCase("help") || args[0].equals("?"))) {
|
||||
this.plugin.showHelp((Player) sender);
|
||||
return true;
|
||||
}
|
||||
|
||||
final Player player = (Player) sender;
|
||||
final boolean openinv = command.getName().equals("openinv");
|
||||
|
||||
// History management
|
||||
String history = (openinv ? this.openInvHistory : this.openEnderHistory).get(player);
|
||||
|
||||
if (history == null || history.isEmpty()) {
|
||||
history = player.getName();
|
||||
(openinv ? this.openInvHistory : this.openEnderHistory).put(player, history);
|
||||
}
|
||||
|
||||
final String name;
|
||||
|
||||
// Read from history if target is not named
|
||||
if (args.length < 1) {
|
||||
name = history;
|
||||
} else {
|
||||
name = args[0];
|
||||
}
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final OfflinePlayer offlinePlayer = OpenInvCommand.this.plugin.matchPlayer(name);
|
||||
|
||||
if (offlinePlayer == null || !offlinePlayer.hasPlayedBefore() && !offlinePlayer.isOnline()) {
|
||||
plugin.sendMessage(player, "messages.error.invalidPlayer");
|
||||
return;
|
||||
}
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!player.isOnline()) {
|
||||
return;
|
||||
}
|
||||
OpenInvCommand.this.openInventory(player, offlinePlayer, openinv);
|
||||
}
|
||||
}.runTask(OpenInvCommand.this.plugin);
|
||||
|
||||
}
|
||||
}.runTaskAsynchronously(this.plugin);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void openInventory(final Player player, final OfflinePlayer target, boolean openinv) {
|
||||
Player onlineTarget;
|
||||
boolean online = target.isOnline();
|
||||
|
||||
if (!online) {
|
||||
if (Permissions.OPENOFFLINE.hasPermission(player)) {
|
||||
// Try loading the player's data
|
||||
onlineTarget = this.plugin.loadPlayer(target);
|
||||
} else {
|
||||
plugin.sendMessage(player, "messages.error.permissionPlayerOffline");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (Permissions.OPENONLINE.hasPermission(player)) {
|
||||
onlineTarget = target.getPlayer();
|
||||
} else {
|
||||
plugin.sendMessage(player, "messages.error.permissionPlayerOnline");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (onlineTarget == null) {
|
||||
plugin.sendMessage(player, "messages.error.invalidPlayer");
|
||||
return;
|
||||
}
|
||||
|
||||
// Permissions checks
|
||||
if (onlineTarget.equals(player)) {
|
||||
// Inventory: Additional permission required to open own inventory
|
||||
if (openinv && !Permissions.OPENSELF.hasPermission(player)) {
|
||||
plugin.sendMessage(player, "messages.error.permissionOpenSelf");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Enderchest: Additional permission required to open others' ender chests
|
||||
if (!openinv && !Permissions.ENDERCHEST_ALL.hasPermission(player)) {
|
||||
plugin.sendMessage(player, "messages.error.permissionEnderAll");
|
||||
return;
|
||||
}
|
||||
|
||||
// Protected check
|
||||
if (!Permissions.OVERRIDE.hasPermission(player)
|
||||
&& Permissions.EXEMPT.hasPermission(onlineTarget)) {
|
||||
plugin.sendMessage(player, "messages.error.permissionExempt",
|
||||
"%target%", onlineTarget.getDisplayName());
|
||||
return;
|
||||
}
|
||||
|
||||
// Crossworld check
|
||||
if (!Permissions.CROSSWORLD.hasPermission(player)
|
||||
&& !onlineTarget.getWorld().equals(player.getWorld())) {
|
||||
plugin.sendMessage(player, "messages.error.permissionCrossWorld",
|
||||
"%target%", onlineTarget.getDisplayName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Record the target
|
||||
(openinv ? this.openInvHistory : this.openEnderHistory).put(player, this.plugin.getPlayerID(target));
|
||||
|
||||
// Create the inventory
|
||||
final ISpecialInventory inv;
|
||||
try {
|
||||
inv = openinv ? this.plugin.getSpecialInventory(onlineTarget, online) : this.plugin.getSpecialEnderChest(onlineTarget, online);
|
||||
} catch (Exception e) {
|
||||
plugin.sendMessage(player, "messages.error.commandException");
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the inventory
|
||||
plugin.openInventory(player, inv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!command.testPermissionSilent(sender) || args.length != 1) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return TabCompleter.completeOnlinePlayer(sender, args[0]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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.commands;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.Permissions;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
public class OpenInvPluginCommand implements CommandExecutor {
|
||||
|
||||
private final OpenInv plugin;
|
||||
private final HashMap<Player, String> openInvHistory = new HashMap<Player, String>();
|
||||
|
||||
public OpenInvPluginCommand(OpenInv plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
sender.sendMessage(ChatColor.RED + "You can't use this from the console.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length > 0 && args[0].equalsIgnoreCase("?")) {
|
||||
plugin.showHelp((Player) sender);
|
||||
return true;
|
||||
}
|
||||
|
||||
final Player player = (Player) sender;
|
||||
|
||||
// History management
|
||||
String history = openInvHistory.get(player);
|
||||
|
||||
if (history == null || history == "") {
|
||||
history = player.getName();
|
||||
openInvHistory.put(player, history);
|
||||
}
|
||||
|
||||
final String name;
|
||||
|
||||
// Read from history if target is not named
|
||||
if (args.length < 1) {
|
||||
name = history;
|
||||
} else {
|
||||
name = args[0];
|
||||
}
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final OfflinePlayer offlinePlayer = plugin.matchPlayer(name);
|
||||
|
||||
if (offlinePlayer == null || !offlinePlayer.hasPlayedBefore() && !offlinePlayer.isOnline()) {
|
||||
player.sendMessage(ChatColor.RED + "Player not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!player.isOnline()) {
|
||||
return;
|
||||
}
|
||||
openInventory(player, offlinePlayer);
|
||||
}
|
||||
}.runTask(plugin);
|
||||
|
||||
}
|
||||
}.runTaskAsynchronously(plugin);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void openInventory(Player player, OfflinePlayer target) {
|
||||
|
||||
|
||||
Player onlineTarget;
|
||||
boolean online = target.isOnline();
|
||||
|
||||
if (!online) {
|
||||
// Try loading the player's data
|
||||
onlineTarget = plugin.loadPlayer(target);
|
||||
|
||||
if (onlineTarget == null) {
|
||||
player.sendMessage(ChatColor.RED + "Player not found!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
onlineTarget = target.getPlayer();
|
||||
}
|
||||
|
||||
// Permissions checks
|
||||
if (onlineTarget.equals(player)) {
|
||||
// Self-open check
|
||||
if (!Permissions.OPENSELF.hasPermission(player)) {
|
||||
player.sendMessage(ChatColor.RED + "You're not allowed to openinv yourself.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Protected check
|
||||
if (!Permissions.OVERRIDE.hasPermission(player) && Permissions.EXEMPT.hasPermission(onlineTarget)) {
|
||||
player.sendMessage(ChatColor.RED + onlineTarget.getDisplayName() + "'s inventory is protected!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Crossworld check
|
||||
if ((!Permissions.CROSSWORLD.hasPermission(player) && !Permissions.OVERRIDE.hasPermission(player)) && onlineTarget.getWorld() != player.getWorld()) {
|
||||
player.sendMessage(ChatColor.RED + onlineTarget.getDisplayName() + " is not in your world!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Record the target
|
||||
openInvHistory.put(player, onlineTarget.getName());
|
||||
|
||||
// Create the inventory
|
||||
ISpecialPlayerInventory inv = plugin.getInventory(onlineTarget, online);
|
||||
|
||||
// Open the inventory
|
||||
player.openInventory(inv.getBukkitInventory());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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.commands;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.commands.search.ChunkSearchOption;
|
||||
import com.lishid.openinv.commands.search.CompletableOption;
|
||||
import com.lishid.openinv.commands.search.ItemAmountOption;
|
||||
import com.lishid.openinv.commands.search.ItemEnchantOption;
|
||||
import com.lishid.openinv.commands.search.ItemTypeOption;
|
||||
import com.lishid.openinv.commands.search.PlayerSearchOption;
|
||||
import com.lishid.openinv.commands.search.PseudoOption;
|
||||
import com.lishid.openinv.search.Search;
|
||||
import com.lishid.openinv.search.bucket.SearchBucket;
|
||||
import com.lishid.openinv.search.match.ItemMatcher;
|
||||
import com.lishid.openinv.search.match.MatchableItem;
|
||||
import com.lishid.openinv.search.match.MatchableMeta;
|
||||
import com.lishid.openinv.util.PseudoJson;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SearchCommand implements TabExecutor {
|
||||
|
||||
private final OpenInv plugin;
|
||||
private final Collection<String> activeSearches = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
|
||||
private final Collection<CompletableOption<SearchBucket>> completableSearchBuckets = new HashSet<>();
|
||||
private final Collection<CompletableOption<MatchableItem>> completableMatchOptions = new HashSet<>();
|
||||
private final Collection<CompletableOption<MatchableMeta>> completableMatchMetaOptions = new HashSet<>();
|
||||
|
||||
public SearchCommand(OpenInv plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
this.completableSearchBuckets.add(new ChunkSearchOption());
|
||||
this.completableSearchBuckets.add(new PlayerSearchOption(plugin));
|
||||
this.completableMatchOptions.add(new ItemAmountOption());
|
||||
this.completableMatchOptions.add(new ItemTypeOption());
|
||||
this.completableMatchMetaOptions.add(new ItemEnchantOption());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(
|
||||
@NotNull CommandSender sender,
|
||||
@NotNull Command command,
|
||||
@NotNull String label,
|
||||
@NotNull String[] args) {
|
||||
|
||||
if (args.length < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String id = sender instanceof Player ? plugin.getPlayerID((Player) sender) : "CONSOLE";
|
||||
|
||||
if (activeSearches.contains(id)) {
|
||||
plugin.sendMessage(sender, "messages.error.search.activeSearch");
|
||||
return true;
|
||||
}
|
||||
|
||||
Collection<PseudoJson> arguments = new HashSet<>();
|
||||
for (String arg : args) {
|
||||
arg = arg.toLowerCase(Locale.ROOT);
|
||||
try {
|
||||
arguments.add(PseudoJson.fromString(arg));
|
||||
} catch (IllegalArgumentException e) {
|
||||
plugin.sendMessage(sender, "messages.error.search.invalidSchema", "%value%", arg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Collection<SearchBucket> searchBuckets = getSearchBuckets(sender, arguments);
|
||||
|
||||
if (searchBuckets.isEmpty()) {
|
||||
plugin.sendMessage(sender, "messages.error.search.invalidInventory");
|
||||
return true;
|
||||
}
|
||||
|
||||
ItemMatcher matcher = getMatcher(sender, arguments);
|
||||
|
||||
if (matcher == null) {
|
||||
plugin.sendMessage(sender, "messages.error.search.invalidMatcher");
|
||||
return true;
|
||||
}
|
||||
|
||||
activeSearches.add(id);
|
||||
|
||||
new Search(matcher, searchBuckets, () -> activeSearches.remove(id)).schedule(sender, plugin);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private @Nullable ItemMatcher getMatcher(@NotNull CommandSender sender, @NotNull Collection<PseudoJson> args) {
|
||||
List<PseudoOption<MatchableItem>> matchItemOptions = new ArrayList<>();
|
||||
List<PseudoOption<MatchableMeta>> matchMetaOptions = new ArrayList<>();
|
||||
|
||||
|
||||
for (PseudoJson arg : args) {
|
||||
addMatchOptions(completableMatchOptions, matchItemOptions, sender, arg);
|
||||
addMatchOptions(completableMatchMetaOptions, matchMetaOptions, sender, arg);
|
||||
}
|
||||
|
||||
// Must have a match option. Null entries indicate a parsing error.
|
||||
if (matchItemOptions.isEmpty()
|
||||
|| matchItemOptions.stream().anyMatch(Objects::isNull)
|
||||
|| matchMetaOptions.stream().anyMatch(Objects::isNull)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ItemMatcher(
|
||||
matchItemOptions.stream().map(PseudoOption::option).toList(),
|
||||
matchMetaOptions.stream().map(PseudoOption::option).toList());
|
||||
}
|
||||
|
||||
private <T> void addMatchOptions(
|
||||
@NotNull Collection<CompletableOption<T>> completableOptions,
|
||||
@NotNull Collection<PseudoOption<T>> options,
|
||||
@NotNull CommandSender sender,
|
||||
@NotNull PseudoJson arg) {
|
||||
for (CompletableOption<T> option : completableOptions) {
|
||||
if (option.matches(sender, arg)) {
|
||||
PseudoOption<T> parsed = option.parse(sender, arg);
|
||||
if (parsed == null) {
|
||||
options.add(null);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (option.isUnique()) {
|
||||
String uniqueId = option.getName();
|
||||
|
||||
// Match existing options.
|
||||
List<PseudoOption<T>> matches = options.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(bucket -> bucket.pseudoJson().getIdentifier().equals(uniqueId))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Remove all existing options.
|
||||
options.removeAll(matches);
|
||||
|
||||
// Merge with existing options.
|
||||
for (PseudoOption<T> match : matches) {
|
||||
PseudoOption<T> newParsed = option.merge(sender, match, parsed);
|
||||
if (newParsed != null) {
|
||||
parsed = newParsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
options.add(parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<SearchBucket> getSearchBuckets(
|
||||
@NotNull CommandSender sender,
|
||||
@NotNull Collection<PseudoJson> args) {
|
||||
List<PseudoOption<SearchBucket>> buckets = new ArrayList<>();
|
||||
|
||||
for (PseudoJson arg : args) {
|
||||
addMatchOptions(completableSearchBuckets, buckets, sender, arg);
|
||||
}
|
||||
|
||||
return buckets.stream().map(PseudoOption::option).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(
|
||||
@NotNull CommandSender sender,
|
||||
@NotNull Command command,
|
||||
@NotNull String label,
|
||||
@NotNull String[] args) {
|
||||
|
||||
List<PseudoJson> arguments = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String arg = args[i].toLowerCase(Locale.ROOT);
|
||||
try {
|
||||
arguments.add(PseudoJson.fromString(arg));
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (i == args.length - 1) {
|
||||
// If last argument cannot be parsed, do not complete.
|
||||
return List.of();
|
||||
}
|
||||
// Otherwise, ignore formatting issues during tab completion.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Ensure an argument exists.
|
||||
if (arguments.isEmpty()) {
|
||||
arguments.add(new PseudoJson(""));
|
||||
}
|
||||
|
||||
// Remove last indexed argument, it is being completed.
|
||||
PseudoJson last = arguments.remove(arguments.size() - 1);
|
||||
|
||||
// Due to generics, can't just use generic list of options to start with.
|
||||
Collection<PseudoOption<SearchBucket>> buckets = new HashSet<>();
|
||||
Collection<PseudoOption<MatchableItem>> items = new HashSet<>();
|
||||
Collection<PseudoOption<MatchableMeta>> metas = new HashSet<>();
|
||||
for (PseudoJson arg : arguments) {
|
||||
addMatchOptions(completableSearchBuckets, buckets, sender, arg);
|
||||
addMatchOptions(completableMatchOptions, items, sender, arg);
|
||||
addMatchOptions(completableMatchMetaOptions, metas, sender, arg);
|
||||
}
|
||||
Collection<PseudoOption<?>> options = new HashSet<>();
|
||||
options.addAll(buckets);
|
||||
options.addAll(items);
|
||||
options.addAll(metas);
|
||||
|
||||
// Suggest all completable options' completions.
|
||||
return Stream.concat(completableSearchBuckets.stream(),
|
||||
Stream.concat(completableMatchOptions.stream(),
|
||||
completableMatchMetaOptions.stream()))
|
||||
// Don't suggest unique options that are already present.
|
||||
.filter(option ->
|
||||
option.isUnique() && options.stream().anyMatch(pseudoOption ->
|
||||
pseudoOption.pseudoJson().getIdentifier().equals(option.getName())))
|
||||
.flatMap(option -> option.suggestOptions(sender, last).stream())
|
||||
.distinct()
|
||||
.toList();
|
||||
}
|
||||
|
||||
}
|
||||
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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.commands;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
|
||||
public class SearchInvPluginCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
|
||||
Material material = null;
|
||||
int count = 1;
|
||||
|
||||
if (args.length >= 1) {
|
||||
String[] gData = null;
|
||||
gData = args[0].split(":");
|
||||
material = Material.matchMaterial(gData[0]);
|
||||
}
|
||||
|
||||
if (args.length >= 2) {
|
||||
try {
|
||||
count = Integer.parseInt(args[1]);
|
||||
} catch (NumberFormatException ex) {
|
||||
sender.sendMessage(ChatColor.RED + "'" + args[1] + "' is not a number!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (material == null) {
|
||||
sender.sendMessage(ChatColor.RED + "Unknown item");
|
||||
return false;
|
||||
}
|
||||
|
||||
StringBuilder players = new StringBuilder();
|
||||
for (Player player : Bukkit.getServer().getOnlinePlayers()) {
|
||||
Inventory inventory = command.getName().equals("searchinv") ? player.getInventory() : player.getEnderChest();
|
||||
if (inventory.contains(material, count)) {
|
||||
players.append(player.getName()).append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
// Matches found, delete trailing comma and space
|
||||
if (players.length() > 0) {
|
||||
players.delete(players.length() - 2, players.length());
|
||||
} else {
|
||||
sender.sendMessage("No players found with " + material.toString());
|
||||
}
|
||||
|
||||
sender.sendMessage("Players with the item " + material.toString() + ": " + players.toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 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.commands;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
|
||||
public class SilentChestPluginCommand implements CommandExecutor {
|
||||
|
||||
private final OpenInv plugin;
|
||||
|
||||
public SilentChestPluginCommand(OpenInv plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
sender.sendMessage(ChatColor.RED + "You can't use this from the console.");
|
||||
return true;
|
||||
}
|
||||
|
||||
Player player = (Player) sender;
|
||||
|
||||
if (args.length > 0 && args[0].equalsIgnoreCase("check")) {
|
||||
sender.sendMessage("SilentChest is " + (plugin.getPlayerAnyChestStatus(player) ? "ON" : "OFF") + ".");
|
||||
return true;
|
||||
}
|
||||
|
||||
plugin.setPlayerSilentChestStatus(player, !plugin.getPlayerSilentChestStatus(player));
|
||||
sender.sendMessage("SilentChest is now " + (plugin.getPlayerSilentChestStatus(player) ? "ON" : "OFF") + ".");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.commands.search;
|
||||
|
||||
import com.lishid.openinv.search.bucket.ChunkBucket;
|
||||
import com.lishid.openinv.search.bucket.SearchBucket;
|
||||
import com.lishid.openinv.util.Permissions;
|
||||
import com.lishid.openinv.util.PseudoJson;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ChunkSearchOption implements CompletableOption<SearchBucket> {
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "chunk";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(@NotNull CommandSender sender, @NotNull PseudoJson pseudoJson) {
|
||||
return CompletableOption.super.matches(sender, pseudoJson)
|
||||
&& Permissions.SEARCH_CHUNKS_LOADED.hasPermission(sender);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PseudoOption<SearchBucket> parse(@NotNull CommandSender sender, @NotNull PseudoJson pseudoJson) {
|
||||
World world = null;
|
||||
Integer chunkX = null;
|
||||
Integer chunkZ = null;
|
||||
int chunkRadius = 5;
|
||||
boolean chunkLoad = false;
|
||||
Map<String, String> mappings = pseudoJson.getMappings();
|
||||
|
||||
if (!(sender instanceof Player senderPlayer)) {
|
||||
if (mappings.size() < 3) {
|
||||
// Console must specify world, x center, z center.
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
world = senderPlayer.getWorld();
|
||||
Chunk senderChunk = senderPlayer.getLocation().getChunk();
|
||||
chunkX = senderChunk.getX();
|
||||
chunkZ = senderChunk.getZ();
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> entry : mappings.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
|
||||
if (key.indexOf('w') >= 0) {
|
||||
world = null;
|
||||
// Name should always be lower case by this point. Ignore case when selecting world.
|
||||
for (World loaded : Bukkit.getWorlds()) {
|
||||
if (loaded.getName().equalsIgnoreCase(value)) {
|
||||
world = loaded;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (key.indexOf('x') >= 0) {
|
||||
try {
|
||||
chunkX = Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
chunkX = null;
|
||||
}
|
||||
} else if (key.indexOf('z') >= 0) {
|
||||
try {
|
||||
chunkZ = Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
chunkZ = null;
|
||||
}
|
||||
} else if (key.indexOf('r') >= 0) {
|
||||
// R is for radius. Needs to be checked later so that "world" doesn't result in a false positive.
|
||||
try {
|
||||
chunkRadius = Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
// Fall through to default radius.
|
||||
}
|
||||
} else if (key.indexOf('l') >= 0) {
|
||||
// L is for loaded. Needs to be checked later so that "world" doesn't result in a false positive.
|
||||
chunkLoad = Permissions.SEARCH_CHUNKS_UNLOADED.hasPermission(sender) && Boolean.parseBoolean(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (world == null || chunkX == null || chunkZ == null || chunkRadius < 0) {
|
||||
// Invalid data provided.
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PseudoOption<>(pseudoJson, new ChunkBucket(world, chunkX, chunkZ, chunkRadius, chunkLoad));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<String> suggestOptions(@NotNull CommandSender sender, @NotNull PseudoJson pseudoJson) {
|
||||
// TODO
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnique() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.commands.search;
|
||||
|
||||
import com.lishid.openinv.util.PseudoJson;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Interface defining behavior for an option.
|
||||
*
|
||||
* @param <T> the type of option represented
|
||||
*/
|
||||
public interface CompletableOption<T> {
|
||||
|
||||
/**
|
||||
* Get unique identifier representing the option.
|
||||
*
|
||||
* @return the unique identifier
|
||||
*/
|
||||
@NotNull String getName();
|
||||
|
||||
/**
|
||||
* Check if the <code>CompletableOption</code> matches the given <code>PseudoJson</code>.
|
||||
*
|
||||
* @param sender the individual using the option
|
||||
* @param pseudoJson the pseudo-JSON element
|
||||
* @return true if the CompletableOption matches
|
||||
*/
|
||||
default boolean matches(@NotNull CommandSender sender, @NotNull PseudoJson pseudoJson) {
|
||||
return pseudoJson.getIdentifier().toLowerCase(Locale.ROOT).startsWith(getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the option represented from the given <code>PseudoJson</code>.
|
||||
*
|
||||
* @param sender the individual using the option
|
||||
* @param pseudoJson the pseudo-JSON element
|
||||
* @return the parsed options
|
||||
*/
|
||||
@Nullable PseudoOption<T> parse(@NotNull CommandSender sender, @NotNull PseudoJson pseudoJson);
|
||||
|
||||
/**
|
||||
* Form a collection of viable permutations to suggest for the given <code>PseudoJson</code>.
|
||||
*
|
||||
* @param sender the individual to suggest options for
|
||||
* @param pseudoJson the parsed pseudo-JSON
|
||||
* @return the suggested permutations
|
||||
*/
|
||||
@NotNull Collection<String> suggestOptions(
|
||||
@NotNull CommandSender sender,
|
||||
@NotNull PseudoJson pseudoJson);
|
||||
|
||||
/**
|
||||
* Get whether or not multiple different copies of this <code>CompletableOption</code> are allowed.
|
||||
*
|
||||
* @return true if only a single instance is allowed
|
||||
*/
|
||||
boolean isUnique();
|
||||
|
||||
/**
|
||||
* Merge similar elements. Used to enforce {@link #isUnique()} despite multiples being provided.
|
||||
*
|
||||
* @param sender the individual using the options
|
||||
* @param first the first parsed option
|
||||
* @param second the second parsed option
|
||||
* @return a single element containing a best-effort merge of both
|
||||
*/
|
||||
default @Nullable PseudoOption<T> merge(
|
||||
@NotNull CommandSender sender,
|
||||
@NotNull PseudoOption<?> first,
|
||||
@NotNull PseudoOption<?> second) {
|
||||
PseudoJson merge = new PseudoJson(first.pseudoJson().getIdentifier());
|
||||
|
||||
// Use mappings of first for our base.
|
||||
first.pseudoJson().getMappings().forEach(merge::put);
|
||||
// Overwrite with second.
|
||||
second.pseudoJson().getMappings().forEach(merge::put);
|
||||
|
||||
// Parse merged options.
|
||||
PseudoOption<T> parsed = parse(sender, merge);
|
||||
|
||||
if (parsed != null) {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
// On parsing failure, try second instead.
|
||||
merge = new PseudoJson(second.pseudoJson().getIdentifier(), merge.getMappings());
|
||||
|
||||
return parse(sender, merge);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.commands.search;
|
||||
|
||||
import com.lishid.openinv.search.match.ItemMatchables;
|
||||
import com.lishid.openinv.search.match.MatchableItem;
|
||||
import com.lishid.openinv.util.PseudoJson;
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.util.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ItemAmountOption implements CompletableOption<MatchableItem> {
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "amount";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PseudoOption<MatchableItem> parse(@NotNull CommandSender sender, @NotNull PseudoJson pseudoJson) {
|
||||
String minAmountString = pseudoJson.getMappings().entrySet().stream().findFirst().map(Map.Entry::getValue).orElse(null);
|
||||
if (minAmountString == null) {
|
||||
// Amount not correctly specified. Warn about spec.
|
||||
return null;
|
||||
}
|
||||
int minAmount;
|
||||
try {
|
||||
minAmount = Integer.parseInt(minAmountString);
|
||||
} catch (NumberFormatException e) {
|
||||
// Amount can't be parsed. Warn about spec.
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PseudoOption<>(pseudoJson, ItemMatchables.hasAmount(minAmount));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<String> suggestOptions(@NotNull CommandSender sender, @NotNull PseudoJson pseudoJson) {
|
||||
// TODO combine with type
|
||||
if (!pseudoJson.getIdentifier().equals(getName())) {
|
||||
// Suggest name while unformed.
|
||||
if (StringUtil.startsWithIgnoreCase(getName(), pseudoJson.getIdentifier())) {
|
||||
return Set.of(getName() + ":");
|
||||
}
|
||||
// If name is not a valid suggestion, suggest nothing.
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
String minAmountString = pseudoJson.getMappings().entrySet().stream().findFirst().map(Map.Entry::getValue).orElse("");
|
||||
|
||||
// Find and suggest name in full pseudojson.
|
||||
return TabCompleter.completeInteger(minAmountString).stream().map(match -> {
|
||||
PseudoJson amount = new PseudoJson(getName());
|
||||
amount.put(getName(), match);
|
||||
return amount;
|
||||
}).map(PseudoJson::asString).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnique() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.commands.search;
|
||||
|
||||
import com.github.jikoo.planarwrappers.util.StringConverters;
|
||||
import com.lishid.openinv.search.match.ItemMatchables;
|
||||
import com.lishid.openinv.search.match.MatchableMeta;
|
||||
import com.lishid.openinv.util.PseudoJson;
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.util.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ItemEnchantOption implements CompletableOption<MatchableMeta> {
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "enchant";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PseudoOption<MatchableMeta> parse(@NotNull CommandSender sender, @NotNull PseudoJson pseudoJson) {
|
||||
Enchantment enchantment = null;
|
||||
Integer enchantLevel = null;
|
||||
for (Map.Entry<String, String> entry : pseudoJson.getMappings().entrySet()) {
|
||||
if (entry.getValue().isBlank()) {
|
||||
continue;
|
||||
}
|
||||
if (entry.getKey().indexOf('l') >= 0) {
|
||||
try {
|
||||
enchantLevel = Integer.parseInt(entry.getValue());
|
||||
} catch (NumberFormatException e) {
|
||||
// Level can't be parsed. Warn about spec.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (entry.getKey().indexOf('t') >= 0) {
|
||||
enchantment = StringConverters.toEnchant(entry.getValue());
|
||||
if (enchantment == null) {
|
||||
// Enchantment is specified but can't be parsed. Warn about spec.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new PseudoOption<>(pseudoJson, ItemMatchables.hasEnchant(enchantment, enchantLevel));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<String> suggestOptions(@NotNull CommandSender sender, @NotNull PseudoJson pseudoJson) {
|
||||
if (!pseudoJson.getIdentifier().equals(getName())) {
|
||||
if (!StringUtil.startsWithIgnoreCase(getName(), pseudoJson.getIdentifier())) {
|
||||
// If enchant is not a valid suggestion, suggest nothing.
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
// Suggest all options while unformed.
|
||||
Set<String> set = new HashSet<>();
|
||||
PseudoJson enchant = new PseudoJson(getName());
|
||||
set.add(enchant.asString());
|
||||
enchant.put("type", "");
|
||||
set.add(enchant.asString());
|
||||
enchant.put("level", "");
|
||||
set.add(enchant.asString());
|
||||
enchant.remove("type");
|
||||
set.add(enchant.asString());
|
||||
return set;
|
||||
}
|
||||
|
||||
Collection<String> enchantments = null;
|
||||
Integer enchantLevel = null;
|
||||
for (Map.Entry<String, String> entry : pseudoJson.getMappings().entrySet()) {
|
||||
if (entry.getValue().isBlank()) {
|
||||
continue;
|
||||
}
|
||||
if (entry.getKey().indexOf('l') >= 0) {
|
||||
try {
|
||||
enchantLevel = Integer.parseInt(entry.getValue());
|
||||
} catch (NumberFormatException e) {
|
||||
// Level is present but cannot be parsed. Default 0.
|
||||
enchantLevel = 0;
|
||||
}
|
||||
}
|
||||
if (entry.getKey().indexOf('t') >= 0) {
|
||||
Enchantment enchant = StringConverters.toEnchant(entry.getValue());
|
||||
if (enchant == null) {
|
||||
// Enchantment is specified but can't be parsed, tab complete.
|
||||
enchantments = TabCompleter.completeKeyed(entry.getValue(), Enchantment.values());
|
||||
} else {
|
||||
enchantments = Set.of(entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PseudoJson enchant = new PseudoJson(getName());
|
||||
if (enchantLevel != null) {
|
||||
enchant.put("level", String.valueOf(enchantLevel));
|
||||
}
|
||||
|
||||
if (enchantments == null) {
|
||||
return Set.of(enchant.asString());
|
||||
}
|
||||
|
||||
return enchantments.stream().map(enchantment -> {
|
||||
// Reuse is fine, single-threaded.
|
||||
enchant.put("type", enchantment);
|
||||
return enchant;
|
||||
}).map(PseudoJson::asString).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnique() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.commands.search;
|
||||
|
||||
import com.github.jikoo.planarwrappers.util.StringConverters;
|
||||
import com.lishid.openinv.search.match.ItemMatchables;
|
||||
import com.lishid.openinv.search.match.MatchableItem;
|
||||
import com.lishid.openinv.util.PseudoJson;
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.util.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ItemTypeOption implements CompletableOption<MatchableItem> {
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "type";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PseudoOption<MatchableItem> parse(@NotNull CommandSender sender, @NotNull PseudoJson pseudoJson) {
|
||||
String typeName = pseudoJson.getMappings().entrySet().stream().findFirst().map(Map.Entry::getValue).orElse(null);
|
||||
Material material = StringConverters.toMaterial(typeName);
|
||||
if (material == null) {
|
||||
// Invalid material, warn about matcher spec.
|
||||
return null;
|
||||
}
|
||||
return new PseudoOption<>(pseudoJson, ItemMatchables.isType(material));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<String> suggestOptions(@NotNull CommandSender sender, @NotNull PseudoJson pseudoJson) {
|
||||
if (!pseudoJson.getIdentifier().equals(getName())) {
|
||||
// Suggest name while unformed.
|
||||
if (StringUtil.startsWithIgnoreCase(getName(), pseudoJson.getIdentifier())) {
|
||||
return Set.of(getName() + ":");
|
||||
}
|
||||
// If name is not a valid suggestion, suggest nothing.
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
String typeName = pseudoJson.getMappings().entrySet().stream().findFirst().map(Map.Entry::getValue).orElse("");
|
||||
|
||||
// Find and suggest matching types in full pseudojson.
|
||||
return TabCompleter.completeMaterial(typeName).stream().map(match -> {
|
||||
PseudoJson type = new PseudoJson(getName());
|
||||
type.put(getName(), match);
|
||||
return type;
|
||||
}).map(PseudoJson::asString).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnique() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.commands.search;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.search.bucket.PlayerBucket;
|
||||
import com.lishid.openinv.search.bucket.SearchBucket;
|
||||
import com.lishid.openinv.util.Permissions;
|
||||
import com.lishid.openinv.util.PseudoJson;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class PlayerSearchOption implements CompletableOption<SearchBucket> {
|
||||
|
||||
private final OpenInv plugin;
|
||||
|
||||
public PlayerSearchOption(OpenInv plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "player";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(@NotNull CommandSender sender, @NotNull PseudoJson pseudoJson) {
|
||||
return CompletableOption.super.matches(sender, pseudoJson)
|
||||
&& Permissions.SEARCH_PLAYERS_ONLINE.hasPermission(sender);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PseudoOption<SearchBucket> parse(@NotNull CommandSender sender, @NotNull PseudoJson pseudoJson) {
|
||||
Optional<Boolean> optional = pseudoJson.getMappings().entrySet().stream()
|
||||
.filter(entry -> entry.getKey().contains("offline"))
|
||||
.findFirst()
|
||||
.map(Map.Entry::getValue)
|
||||
.map(Boolean::parseBoolean);
|
||||
|
||||
return new PseudoOption<>(
|
||||
pseudoJson, new PlayerBucket(plugin,
|
||||
!(optional.orElse(false) && Permissions.SEARCH_PLAYERS_OFFLINE.hasPermission(sender))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<String> suggestOptions(@NotNull CommandSender sender, @NotNull PseudoJson pseudoJson) {
|
||||
// TODO
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnique() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.commands.search;
|
||||
|
||||
import com.lishid.openinv.util.PseudoJson;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Data holder for completable options.
|
||||
*
|
||||
* @param <T> the type of data
|
||||
*/
|
||||
public record PseudoOption<T>(@NotNull PseudoJson pseudoJson, @NotNull T option) {}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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 org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface IPlayerDataManager {
|
||||
|
||||
/**
|
||||
* Loads a Player for an OfflinePlayer.
|
||||
* </p>
|
||||
* This method is potentially blocking, and should not be called on the main thread.
|
||||
*
|
||||
* @param offline the OfflinePlayer
|
||||
* @return the Player loaded
|
||||
*/
|
||||
@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);
|
||||
|
||||
/**
|
||||
* Opens an ISpecialInventory for a Player.
|
||||
*
|
||||
* @param player the Player opening the ISpecialInventory
|
||||
* @param inventory the Inventory
|
||||
*`
|
||||
* @return the InventoryView opened
|
||||
*/
|
||||
@Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory);
|
||||
|
||||
/**
|
||||
* Convert a raw slot number into a player inventory slot number.
|
||||
*
|
||||
* <p>Note that this method is specifically for converting an ISpecialPlayerInventory slot number into a regular
|
||||
* player inventory slot number.
|
||||
*
|
||||
* @param view the open inventory view
|
||||
* @param rawSlot the raw slot in the view
|
||||
* @return the converted slot number
|
||||
*/
|
||||
int convertToPlayerSlot(InventoryView view, int rawSlot);
|
||||
|
||||
}
|
||||
@@ -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 = inventory.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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.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(playerInventory.getPlayer());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.listeners;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.util.Permissions;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Event.Result;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
public class PlayerListener implements Listener {
|
||||
|
||||
private final OpenInv plugin;
|
||||
|
||||
public PlayerListener(OpenInv plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onPlayerJoin(final PlayerJoinEvent event) {
|
||||
plugin.setPlayerOnline(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
plugin.setPlayerOffline(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldChange(PlayerChangedWorldEvent event) {
|
||||
plugin.changeWorld(event.getPlayer());
|
||||
}
|
||||
|
||||
@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())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = event.getPlayer();
|
||||
boolean any = Permissions.ANYCHEST.hasPermission(player) && plugin.getPlayerAnyChestStatus(player);
|
||||
boolean needsAny = plugin.getAnySilentContainer().isAnyContainerNeeded(player, event.getClickedBlock());
|
||||
|
||||
if (!any && needsAny) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean silent = Permissions.SILENT.hasPermission(player) && plugin.getPlayerSilentChestStatus(player);
|
||||
|
||||
// If anycontainer or silentcontainer is active
|
||||
if (any || silent) {
|
||||
if (plugin.getAnySilentContainer().activateContainer(player, silent, event.getClickedBlock())) {
|
||||
if (silent && plugin.notifySilentChest() && needsAny && plugin.notifyAnyChest()) {
|
||||
plugin.sendSystemMessage(player, "messages.info.containerBlockedSilent");
|
||||
} else if (needsAny && plugin.notifyAnyChest()) {
|
||||
plugin.sendSystemMessage(player, "messages.info.containerBlocked");
|
||||
} else if (silent && plugin.notifySilentChest()) {
|
||||
plugin.sendSystemMessage(player, "messages.info.containerSilent");
|
||||
}
|
||||
}
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.listeners;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.server.PluginDisableEvent;
|
||||
|
||||
/**
|
||||
* Listener for plugin-related events.
|
||||
*
|
||||
* @author Jikoo
|
||||
*/
|
||||
public class PluginListener implements Listener {
|
||||
|
||||
private final OpenInv plugin;
|
||||
|
||||
public PluginListener(OpenInv plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPluginDisable(PluginDisableEvent event) {
|
||||
plugin.releaseAllPlayers(event.getPlugin());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* 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.search;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.search.bucket.SearchBucket;
|
||||
import com.lishid.openinv.search.match.ItemMatcher;
|
||||
import com.lishid.openinv.search.match.MatchResult;
|
||||
import com.lishid.openinv.search.match.Matchable;
|
||||
import com.lishid.openinv.util.MessagePart;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.stream.Collectors;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A search function.
|
||||
*/
|
||||
public class Search {
|
||||
|
||||
public static final BaseComponent SEPARATOR = new TextComponent(", ");
|
||||
|
||||
private final Set<MessagePart> matches = new HashSet<>();
|
||||
private final ItemMatcher matcher;
|
||||
private final double totalMatchables;
|
||||
private final Iterator<SearchBucket> bucketIterator;
|
||||
private final Runnable callback;
|
||||
private SearchBucket current = null;
|
||||
private int polls = 0;
|
||||
|
||||
public Search(
|
||||
@NotNull ItemMatcher matcher,
|
||||
@NotNull Collection<@NotNull SearchBucket> buckets,
|
||||
@Nullable Runnable callback) {
|
||||
// Just in case, copy contents to new set.
|
||||
Set<SearchBucket> bucketSet = new HashSet<>(buckets);
|
||||
this.matcher = matcher;
|
||||
this.totalMatchables = bucketSet.stream().mapToInt(SearchBucket::size).sum();
|
||||
this.bucketIterator = bucketSet.iterator();
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
private boolean isComplete(OpenInv plugin) {
|
||||
if (matches.size() >= plugin.getSearchResultsMax()) {
|
||||
return true;
|
||||
}
|
||||
if (current != null && current.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
while (bucketIterator.hasNext()) {
|
||||
current = bucketIterator.next();
|
||||
if (current.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the configured amount of Matchables.
|
||||
*
|
||||
* <p>Matchables are assembled asynchronously, then
|
||||
*
|
||||
* @param plugin the OpenInv instance
|
||||
*/
|
||||
private void poll(OpenInv plugin) {
|
||||
Collection<Matchable> syncMatchables = new ArrayList<>();
|
||||
// Assemble matchables async, then search synchronously.
|
||||
for (int i = 0; !isComplete(plugin) && i < plugin.getSearchPollsPerTick(); ++i) {
|
||||
++polls;
|
||||
// Calling #isComplete will advance to next InventoryProvider as necessary.
|
||||
// Bucket matchables for searching synchronously.
|
||||
syncMatchables.add(current.next());
|
||||
}
|
||||
|
||||
Future<Boolean> future = plugin.getServer().getScheduler().callSyncMethod(plugin, () -> {
|
||||
for (Matchable matchable : syncMatchables) {
|
||||
MatchResult match = matchable.match(matcher);
|
||||
if (match.isMatch()) {
|
||||
matches.addAll(Arrays.asList(match.getMatch()));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
try {
|
||||
future.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
// Eat exception to prevent spam.
|
||||
}
|
||||
}
|
||||
|
||||
private void sendProgress(CommandSender sender, OpenInv plugin) {
|
||||
if (!(sender instanceof Player) || polls % (10 * plugin.getSearchPollsPerTick()) != 1) {
|
||||
return;
|
||||
}
|
||||
double progress = 100 * Math.min(polls, totalMatchables) / totalMatchables;
|
||||
DecimalFormat percent = new DecimalFormat("00.0");
|
||||
plugin.sendSystemMessage((Player) sender, "messages.info.search.progress",
|
||||
"%progress%", percent.format(progress),
|
||||
"%matches%", String.valueOf(matches.size()));
|
||||
}
|
||||
|
||||
private void sendResults(CommandSender sender, OpenInv plugin) {
|
||||
if (matches.isEmpty()) {
|
||||
String localizedMessage = plugin.getLocalizedMessage(sender, "messages.info.search.noMatches");
|
||||
if (localizedMessage == null) {
|
||||
localizedMessage = "No items found with search parameters.";
|
||||
}
|
||||
sender.sendMessage(localizedMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
String prefix = plugin.getLocalizedMessage(sender, "messages.info.search.matches");
|
||||
if (prefix == null) {
|
||||
prefix = "Locations of items matching parameters:";
|
||||
}
|
||||
|
||||
if (sender instanceof Player) {
|
||||
TextComponent textComponent = new TextComponent(prefix);
|
||||
Iterator<MessagePart> iterator = matches.iterator();
|
||||
|
||||
int total = 0;
|
||||
int searchResultsMax = plugin.getSearchResultsMax();
|
||||
while (iterator.hasNext()) {
|
||||
textComponent.addExtra(iterator.next().forPlayer());
|
||||
|
||||
++total;
|
||||
if (total >= searchResultsMax) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (iterator.hasNext()) {
|
||||
textComponent.addExtra(SEPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
// Paper deprecated Spigot's message API.
|
||||
// This is a risky deprecation ignore - Spigot may eventually do the sane thing
|
||||
// and just add a method accepting components to the player directly.
|
||||
//noinspection deprecation
|
||||
sender.spigot().sendMessage(textComponent);
|
||||
return;
|
||||
}
|
||||
|
||||
String message = matches.stream()
|
||||
.limit(plugin.getSearchResultsMax())
|
||||
.map(MessagePart::forConsole)
|
||||
.collect(Collectors.joining(", ", prefix, ""));
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
|
||||
public void schedule(CommandSender sender, OpenInv plugin) {
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Scheduler struggles to cancel async tasks with high frequency.
|
||||
if (isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
poll(plugin);
|
||||
|
||||
if (isComplete(plugin)) {
|
||||
if (isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendResults(sender, plugin);
|
||||
cancel();
|
||||
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sendProgress(sender, plugin);
|
||||
}
|
||||
}.runTaskTimerAsynchronously(plugin, 0L, 1L);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.search.bucket;
|
||||
|
||||
import com.lishid.openinv.search.match.MatchResult;
|
||||
import com.lishid.openinv.search.match.Matchable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.BiConsumer;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ChunkBucket implements SearchBucket {
|
||||
|
||||
private final World world;
|
||||
private final boolean load;
|
||||
private final List<ChunkCoord> chunks;
|
||||
protected int index = -1;
|
||||
private Boolean paper = null;
|
||||
|
||||
public ChunkBucket(@NotNull World world, int chunkX, int chunkZ, int radius, boolean load) {
|
||||
this.world = world;
|
||||
this.load = load;
|
||||
this.chunks = new ArrayList<>();
|
||||
// Order chunks based (loosely) on proximity.
|
||||
spiralRectangle(radius, (deltaX, deltaZ) -> chunks.add(new ChunkCoord(chunkX + deltaX, chunkZ + deltaZ)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Matchable next() throws IndexOutOfBoundsException {
|
||||
ChunkCoord chunkCoord = chunks.get(++index);
|
||||
|
||||
if (!world.isChunkGenerated(chunkCoord.x(), chunkCoord.z())) {
|
||||
return Matchable.EMPTY;
|
||||
}
|
||||
|
||||
if (!load && !world.isChunkLoaded(chunkCoord.x(), chunkCoord.z())) {
|
||||
return Matchable.EMPTY;
|
||||
}
|
||||
|
||||
// If async chunk loading is not available, load chunk when checking.
|
||||
if (!isPaper()) {
|
||||
return new MatchableChunk(world, chunkCoord.x(), chunkCoord.z());
|
||||
}
|
||||
|
||||
CompletableFuture<Chunk> chunkAt = world.getChunkAtAsync(chunkCoord.x(), chunkCoord.z());
|
||||
|
||||
Chunk chunk;
|
||||
try {
|
||||
chunk = chunkAt.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
return matcher -> MatchResult.NO_MATCH;
|
||||
}
|
||||
|
||||
return new MatchableChunk(chunk);
|
||||
}
|
||||
|
||||
private boolean isPaper() {
|
||||
if (paper == null) {
|
||||
try {
|
||||
// Check for Paper.
|
||||
Class.forName("com.destroystokyo.paper.PaperConfig");
|
||||
paper = true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
paper = false;
|
||||
}
|
||||
}
|
||||
|
||||
return paper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return index < chunks.size() - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return chunks.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a series of integer coordinates starting at 0, 0 that extend outward to form a centered square,
|
||||
* producing closer proximity results sooner than a traditional double for loop from min to max.
|
||||
*
|
||||
* @param radius the radius of the square
|
||||
* @param coordConsumer the method consuming the values produced
|
||||
*/
|
||||
private static void spiralRectangle(int radius, BiConsumer<Integer, Integer> coordConsumer) {
|
||||
for (int x = 0; x <= radius; ++x) {
|
||||
for (int z = 0; z <= radius; ++z) {
|
||||
coordConsumer.accept(x, z);
|
||||
if (x != 0) {
|
||||
coordConsumer.accept(-x, z);
|
||||
if (z != 0) {
|
||||
coordConsumer.accept(-x, -z);
|
||||
}
|
||||
}
|
||||
if (z != 0) {
|
||||
coordConsumer.accept(x, -z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.search.bucket;
|
||||
|
||||
/**
|
||||
* A data holder for chunk coordinates.
|
||||
*/
|
||||
public record ChunkCoord(int x, int z) {}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.search.bucket;
|
||||
|
||||
import com.lishid.openinv.search.match.ItemMatcher;
|
||||
import com.lishid.openinv.search.match.MatchResult;
|
||||
import com.lishid.openinv.search.match.Matchable;
|
||||
import com.lishid.openinv.util.MessagePart;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class MatchableChunk implements Matchable {
|
||||
|
||||
private Chunk chunk;
|
||||
private World world;
|
||||
private int chunkX;
|
||||
private int chunkZ;
|
||||
|
||||
MatchableChunk(@NotNull Chunk chunk) {
|
||||
this.chunk = chunk;
|
||||
}
|
||||
|
||||
MatchableChunk(@NotNull World world, int chunkX, int chunkZ) {
|
||||
this.world = world;
|
||||
this.chunkX = chunkX;
|
||||
this.chunkZ = chunkZ;
|
||||
}
|
||||
|
||||
private Chunk getChunk() {
|
||||
if (chunk == null) {
|
||||
chunk = world.getChunkAt(chunkX, chunkZ);
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull MatchResult match(@NotNull ItemMatcher matcher) {
|
||||
List<BlockState> blocks = new ArrayList<>();
|
||||
|
||||
for (BlockState tileEntity : getChunk().getTileEntities()) {
|
||||
if (!(tileEntity instanceof InventoryHolder)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Inventory inventory = ((InventoryHolder) tileEntity).getInventory();
|
||||
|
||||
for (ItemStack content : inventory.getContents()) {
|
||||
if (matcher.matches(content)) {
|
||||
blocks.add(tileEntity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (blocks.isEmpty()) {
|
||||
return MatchResult.NO_MATCH;
|
||||
}
|
||||
|
||||
MessagePart[] matches = new MessagePart[blocks.size()];
|
||||
for (int i = 0; i < matches.length; ++i) {
|
||||
BlockState state = blocks.get(i);
|
||||
matches[i] = new MessagePart(() -> getComponent(state), () -> getString(state));
|
||||
}
|
||||
|
||||
return new MatchResult(matches);
|
||||
}
|
||||
|
||||
private static BaseComponent getComponent(BlockState state) {
|
||||
TextComponent component = new TextComponent(state.getType().name());
|
||||
|
||||
String command = String.format(
|
||||
"/execute in %s minecraft:tp %s %s %s",
|
||||
state.getWorld().getName(),
|
||||
state.getX(),
|
||||
state.getY() + 1,
|
||||
state.getZ());
|
||||
component.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, command));
|
||||
|
||||
String hover = String.format(
|
||||
"%s{world:%s,x:%s,y:%s,z:%s}\n\nClick to teleport.",
|
||||
state.getType().getKey(),
|
||||
state.getWorld().getName(),
|
||||
state.getX(),
|
||||
state.getY(),
|
||||
state.getZ());
|
||||
Text hoverText = new Text(new BaseComponent[] { new TextComponent(hover)});
|
||||
component.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverText));
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
private static String getString(BlockState state) {
|
||||
return String.format(
|
||||
"%s{world:%s,x:%s,y:%s,z:%s}",
|
||||
state.getType().getKey(),
|
||||
state.getWorld().getName(),
|
||||
state.getX(),
|
||||
state.getY(),
|
||||
state.getZ());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.search.bucket;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.search.match.ItemMatcher;
|
||||
import com.lishid.openinv.search.match.MatchResult;
|
||||
import com.lishid.openinv.search.match.Matchable;
|
||||
import com.lishid.openinv.util.MessagePart;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class MatchablePlayer implements Matchable {
|
||||
|
||||
private final OpenInv plugin;
|
||||
private final OfflinePlayer offlinePlayer;
|
||||
|
||||
public MatchablePlayer(OpenInv plugin, OfflinePlayer offlinePlayer) {
|
||||
this.plugin = plugin;
|
||||
this.offlinePlayer = offlinePlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull MatchResult match(@NotNull ItemMatcher matcher) {
|
||||
|
||||
Player player;
|
||||
if (offlinePlayer instanceof Player) {
|
||||
player = (Player) offlinePlayer;
|
||||
} else {
|
||||
player = plugin.loadPlayer(offlinePlayer);
|
||||
}
|
||||
|
||||
if (player == null) {
|
||||
return MatchResult.NO_MATCH;
|
||||
}
|
||||
|
||||
boolean matchInv = false;
|
||||
boolean matchEnder = false;
|
||||
|
||||
PlayerInventory playerInventory = player.getInventory();
|
||||
|
||||
// Check inventory. Modern implementations include armor and off hand in contents.
|
||||
for (ItemStack content : playerInventory.getContents()) {
|
||||
if (matcher.matches(content)) {
|
||||
matchInv = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
InventoryView openInventory = player.getOpenInventory();
|
||||
|
||||
// If no matches are found yet, check cursor.
|
||||
if (!matchInv && matcher.matches(openInventory.getCursor())) {
|
||||
matchInv = true;
|
||||
}
|
||||
|
||||
// Check player crafting inventory.
|
||||
if (!matchInv && openInventory.getType() == InventoryType.CRAFTING) {
|
||||
for (ItemStack content : openInventory.getTopInventory().getContents()) {
|
||||
if (matcher.matches(content)) {
|
||||
matchInv = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check ender chest.
|
||||
for (ItemStack content : player.getEnderChest().getContents()) {
|
||||
if (matcher.matches(content)) {
|
||||
matchEnder = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matchInv && !matchEnder) {
|
||||
return MatchResult.NO_MATCH;
|
||||
}
|
||||
|
||||
String name = player.getName();
|
||||
UUID uuid = player.getUniqueId();
|
||||
|
||||
if (matchInv && matchEnder) {
|
||||
return new MatchResult(new MessagePart[] {
|
||||
new MessagePart(() -> getBoth(name, uuid), () -> name + " (inv, ender)")
|
||||
});
|
||||
}
|
||||
|
||||
Supplier<BaseComponent> componentSupplier;
|
||||
Supplier<String> stringSupplier;
|
||||
|
||||
if (matchInv) {
|
||||
componentSupplier = () -> getSingle(name, () -> getInvClick(uuid), () -> getInvHover(name));
|
||||
stringSupplier = () -> name + " (inv)";
|
||||
} else {
|
||||
componentSupplier = () -> getSingle(name, () -> getEnderClick(uuid), () -> getEnderHover(name));
|
||||
stringSupplier = () -> name + " (ender)";
|
||||
}
|
||||
|
||||
return new MatchResult(new MessagePart[] { new MessagePart(componentSupplier, stringSupplier) });
|
||||
}
|
||||
|
||||
private static BaseComponent getBoth(String name, UUID uuid) {
|
||||
BaseComponent component = new TextComponent(name + " (inv,");
|
||||
component.setClickEvent(getInvClick(uuid));
|
||||
component.setHoverEvent(getInvHover(name));
|
||||
|
||||
BaseComponent enderComponent = new TextComponent(" ender)");
|
||||
enderComponent.setClickEvent(getEnderClick(uuid));
|
||||
enderComponent.setHoverEvent(getEnderHover(name));
|
||||
|
||||
component.addExtra(enderComponent);
|
||||
return component;
|
||||
}
|
||||
|
||||
private static BaseComponent getSingle(String name, Supplier<ClickEvent> click, Supplier<HoverEvent> hover) {
|
||||
BaseComponent component = new TextComponent(name);
|
||||
component.setClickEvent(click.get());
|
||||
component.setHoverEvent(hover.get());
|
||||
return component;
|
||||
}
|
||||
|
||||
private static ClickEvent getInvClick(UUID uuid) {
|
||||
return getClick("/openinv:openinv ", uuid);
|
||||
}
|
||||
|
||||
private static HoverEvent getInvHover(String name) {
|
||||
return getHover("/openinv ", name);
|
||||
}
|
||||
|
||||
private static ClickEvent getEnderClick(UUID uuid) {
|
||||
return getClick("/openinv:openender ", uuid);
|
||||
}
|
||||
|
||||
private static HoverEvent getEnderHover(String name) {
|
||||
return getHover("/openender ", name);
|
||||
}
|
||||
|
||||
private static ClickEvent getClick(String command, UUID uuid) {
|
||||
// Actual command run is not exposed to user, so there's no reason not to use UUID and be precise.
|
||||
return new ClickEvent(ClickEvent.Action.RUN_COMMAND, command + uuid);
|
||||
}
|
||||
|
||||
private static HoverEvent getHover(String command, String name) {
|
||||
BaseComponent[] text = new BaseComponent[] { new TextComponent(command + name) };
|
||||
return new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(text));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.search.bucket;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.search.match.Matchable;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class PlayerBucket implements SearchBucket {
|
||||
|
||||
private final OpenInv plugin;
|
||||
private final OfflinePlayer[] players;
|
||||
private int index = -1;
|
||||
|
||||
public PlayerBucket(OpenInv plugin, boolean online) {
|
||||
this.plugin = plugin;
|
||||
if (online) {
|
||||
this.players = Bukkit.getOnlinePlayers().toArray(new Player[0]);
|
||||
} else {
|
||||
this.players = Bukkit.getOfflinePlayers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return index < players.length - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Matchable next() {
|
||||
return new MatchablePlayer(plugin, players[++index]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.search.bucket;
|
||||
|
||||
import com.lishid.openinv.search.match.Matchable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface SearchBucket {
|
||||
|
||||
boolean hasNext();
|
||||
|
||||
@NotNull Matchable next() throws IndexOutOfBoundsException;
|
||||
|
||||
int size();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.search.match;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ItemMatchables {
|
||||
|
||||
public static MatchableItem isType(@NotNull Material material) {
|
||||
return itemStack -> itemStack.getType() == material;
|
||||
}
|
||||
|
||||
public static MatchableItem hasAmount(int minAmount) {
|
||||
return itemStack -> itemStack.getAmount() >= minAmount;
|
||||
}
|
||||
|
||||
public static MatchableMeta hasEnchant(@Nullable Enchantment enchantment, @Nullable Integer minLevel) {
|
||||
return itemMeta -> {
|
||||
if (minLevel == null) {
|
||||
if (enchantment == null) {
|
||||
// Level and enchantment not specified, any enchantment of any level matches.
|
||||
return !itemMeta.getEnchants().isEmpty();
|
||||
}
|
||||
|
||||
// Level not specified, any level of enchantment matches.
|
||||
return itemMeta.hasEnchant(enchantment);
|
||||
}
|
||||
|
||||
if (enchantment != null) {
|
||||
// Level and enchantment are specified, enchantment must meet or exceed level.
|
||||
return itemMeta.getEnchantLevel(enchantment) >= minLevel;
|
||||
}
|
||||
|
||||
// Enchantment not specified, any enchantment that meets or exceeds level matches.
|
||||
for (int enchantLevel : itemMeta.getEnchants().values()) {
|
||||
if (enchantLevel >= minLevel) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
private ItemMatchables() {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.search.match;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A collection of multiple filters used to match items.
|
||||
*/
|
||||
public class ItemMatcher {
|
||||
|
||||
private final Collection<MatchableItem> baseOptions;
|
||||
private final Collection<MatchableMeta> metaOptions;
|
||||
|
||||
public ItemMatcher(
|
||||
Collection<MatchableItem> baseOptions,
|
||||
Collection<MatchableMeta> metaOptions) {
|
||||
this.baseOptions = new HashSet<>(baseOptions);
|
||||
this.metaOptions = new HashSet<>(metaOptions);
|
||||
}
|
||||
|
||||
public boolean matches(@Nullable ItemStack other) {
|
||||
// Ensure item exists.
|
||||
if (other == null || other.getType() == Material.AIR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Match options against base item.
|
||||
for (MatchableItem baseOption : baseOptions) {
|
||||
if (!baseOption.matches(other)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If no meta options exist, item matches.
|
||||
if (metaOptions.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Require item meta for meta options.
|
||||
ItemMeta meta;
|
||||
if (!other.hasItemMeta() || (meta = other.getItemMeta()) == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Match meta-based options.
|
||||
for (MatchableMeta metaOption : metaOptions) {
|
||||
if (!metaOption.matches(meta)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return baseOptions.isEmpty() && metaOptions.isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.search.match;
|
||||
|
||||
import com.lishid.openinv.util.MessagePart;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class MatchResult {
|
||||
|
||||
public static final MatchResult NO_MATCH = new MatchResult();
|
||||
|
||||
private final MessagePart[] result;
|
||||
|
||||
private MatchResult() {
|
||||
result = null;
|
||||
}
|
||||
|
||||
public MatchResult(MessagePart @NotNull [] result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public boolean isMatch() {
|
||||
return result != null;
|
||||
}
|
||||
|
||||
public MessagePart @Nullable [] getMatch() {
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.search.match;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface Matchable {
|
||||
|
||||
Matchable EMPTY = matcher -> MatchResult.NO_MATCH;
|
||||
|
||||
@NotNull MatchResult match(@NotNull ItemMatcher matcher);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.search.match;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface MatchableItem {
|
||||
|
||||
boolean matches(@NotNull ItemStack itemStack);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.search.match;
|
||||
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface MatchableMeta {
|
||||
|
||||
boolean matches(@NotNull ItemMeta itemMeta);
|
||||
|
||||
}
|
||||
@@ -1,16 +1,35 @@
|
||||
package com.lishid.openinv.util;
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.TreeMultimap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* A minimal time-based cache implementation backed by a HashMap and TreeMultimap.
|
||||
*
|
||||
* A minimal thread-safe time-based cache implementation backed by a HashMap and TreeMultimap.
|
||||
*
|
||||
* @author Jikoo
|
||||
*/
|
||||
public class Cache<K, V> {
|
||||
@@ -18,30 +37,20 @@ 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> 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(long retention, Function<V> inUseCheck, Function<V> postRemoval) {
|
||||
this.internal = new HashMap<K, V>();
|
||||
public Cache(final long retention, final Predicate<V> inUseCheck, final Consumer<V> postRemoval) {
|
||||
this.internal = new HashMap<>();
|
||||
|
||||
this.expiry = TreeMultimap.create(new Comparator<Long>() {
|
||||
@Override
|
||||
public int compare(Long long1, Long long2) {
|
||||
return long1.compareTo(long2);
|
||||
}
|
||||
},
|
||||
new Comparator<K>() {
|
||||
@Override
|
||||
public int compare(K k1, K k2) {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
this.expiry = TreeMultimap.create(Long::compareTo, (k1, k2) -> Objects.equals(k1, k2) ? 0 : 1);
|
||||
|
||||
this.retention = retention;
|
||||
this.inUseCheck = inUseCheck;
|
||||
@@ -51,66 +60,74 @@ public class Cache<K, V> {
|
||||
/**
|
||||
* Set a key and value pair. Keys are unique. Using an existing key will cause the old value to
|
||||
* be overwritten and the expiration timer to be reset.
|
||||
*
|
||||
*
|
||||
* @param key key with which the specified value is to be associated
|
||||
* @param value value to be associated with the specified key
|
||||
*/
|
||||
public void put(K key, V value) {
|
||||
public void put(final K key, final V value) {
|
||||
// Invalidate key - runs lazy check and ensures value won't be cleaned up early
|
||||
invalidate(key);
|
||||
this.invalidate(key);
|
||||
|
||||
internal.put(key, value);
|
||||
expiry.put(System.currentTimeMillis() + retention, key);
|
||||
synchronized (this.internal) {
|
||||
this.internal.put(key, value);
|
||||
this.expiry.put(System.currentTimeMillis() + this.retention, key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified key is mapped, or null if no value is mapped for the key.
|
||||
*
|
||||
*
|
||||
* @param key the key whose associated value is to be returned
|
||||
* @return the value to which the specified key is mapped, or null if no value is mapped for the key
|
||||
*/
|
||||
public V get(K key) {
|
||||
public V get(final K key) {
|
||||
// Run lazy check to clean cache
|
||||
lazyCheck();
|
||||
this.lazyCheck();
|
||||
|
||||
return internal.get(key);
|
||||
synchronized (this.internal) {
|
||||
return this.internal.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified key is mapped to a value.
|
||||
*
|
||||
*
|
||||
* @param key key to check if a mapping exists for
|
||||
* @return true if a mapping exists for the specified key
|
||||
*/
|
||||
public boolean containsKey(K key) {
|
||||
public boolean containsKey(final K key) {
|
||||
// Run lazy check to clean cache
|
||||
lazyCheck();
|
||||
this.lazyCheck();
|
||||
|
||||
return internal.containsKey(key);
|
||||
synchronized (this.internal) {
|
||||
return this.internal.containsKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcibly invalidates a key, even if it is considered to be in use.
|
||||
*
|
||||
*
|
||||
* @param key key to invalidate
|
||||
*/
|
||||
public void invalidate(K key) {
|
||||
public void invalidate(final K key) {
|
||||
// Run lazy check to clean cache
|
||||
lazyCheck();
|
||||
this.lazyCheck();
|
||||
|
||||
if (!internal.containsKey(key)) {
|
||||
// Value either not present or cleaned by lazy check. Either way, we're good
|
||||
return;
|
||||
}
|
||||
synchronized (this.internal) {
|
||||
if (!this.internal.containsKey(key)) {
|
||||
// Value either not present or cleaned by lazy check. Either way, we're good
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove stored object
|
||||
internal.remove(key);
|
||||
// Remove stored object
|
||||
this.internal.remove(key);
|
||||
|
||||
// Remove expiration entry - prevents more work later, plus prevents issues with values invalidating early
|
||||
for (Iterator<Map.Entry<Long, K>> iterator = expiry.entries().iterator(); iterator.hasNext();) {
|
||||
if (key.equals(iterator.next().getValue())) {
|
||||
iterator.remove();
|
||||
break;
|
||||
// Remove expiration entry - prevents more work later, plus prevents issues with values invalidating early
|
||||
for (Iterator<Map.Entry<Long, K>> iterator = this.expiry.entries().iterator(); iterator.hasNext();) {
|
||||
if (key.equals(iterator.next().getValue())) {
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,11 +136,13 @@ public class Cache<K, V> {
|
||||
* Forcibly invalidates all keys, even if they are considered to be in use.
|
||||
*/
|
||||
public void invalidateAll() {
|
||||
for (V value : internal.values()) {
|
||||
postRemoval.run(value);
|
||||
synchronized (this.internal) {
|
||||
for (V value : this.internal.values()) {
|
||||
this.postRemoval.accept(value);
|
||||
}
|
||||
this.expiry.clear();
|
||||
this.internal.clear();
|
||||
}
|
||||
expiry.clear();
|
||||
internal.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,28 +151,36 @@ public class Cache<K, V> {
|
||||
*/
|
||||
private void lazyCheck() {
|
||||
long now = System.currentTimeMillis();
|
||||
long nextExpiry = now + retention;
|
||||
for (Iterator<Map.Entry<Long, K>> iterator = expiry.entries().iterator(); iterator.hasNext();) {
|
||||
Map.Entry<Long, K> entry = iterator.next();
|
||||
synchronized (this.internal) {
|
||||
List<K> inUse = new ArrayList<>();
|
||||
for (Iterator<Map.Entry<Long, K>> iterator = this.expiry.entries().iterator(); iterator
|
||||
.hasNext();) {
|
||||
Map.Entry<Long, K> entry = iterator.next();
|
||||
|
||||
if (entry.getKey() > now) {
|
||||
break;
|
||||
if (entry.getKey() > now) {
|
||||
break;
|
||||
}
|
||||
|
||||
iterator.remove();
|
||||
|
||||
if (this.inUseCheck.test(this.internal.get(entry.getValue()))) {
|
||||
inUse.add(entry.getValue());
|
||||
continue;
|
||||
}
|
||||
|
||||
V value = this.internal.remove(entry.getValue());
|
||||
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.postRemoval.accept(value);
|
||||
}
|
||||
|
||||
iterator.remove();
|
||||
|
||||
if (inUseCheck.run(internal.get(entry.getValue()))) {
|
||||
expiry.put(nextExpiry, entry.getValue());
|
||||
continue;
|
||||
long nextExpiry = now + this.retention;
|
||||
for (K value : inUse) {
|
||||
this.expiry.put(nextExpiry, value);
|
||||
}
|
||||
|
||||
V value = internal.remove(entry.getValue());
|
||||
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
postRemoval.run(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
public class ConfigUpdater {
|
||||
|
||||
private final OpenInv plugin;
|
||||
|
||||
public ConfigUpdater(OpenInv plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void checkForUpdates() {
|
||||
final int version = plugin.getConfig().getInt("config-version", 1);
|
||||
ConfigurationSection defaults = plugin.getConfig().getDefaults();
|
||||
if (defaults == null || version >= defaults.getInt("config-version")) {
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.getLogger().info("Configuration update found! Performing update...");
|
||||
|
||||
// Backup the old config file
|
||||
try {
|
||||
plugin.getConfig().save(new File(plugin.getDataFolder(), "config_old.yml"));
|
||||
plugin.getLogger().info("Backed up config.yml to config_old.yml before updating.");
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Could not back up config.yml before updating!");
|
||||
}
|
||||
|
||||
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
if (version < 2) {
|
||||
updateConfig1To2();
|
||||
}
|
||||
if (version < 3) {
|
||||
updateConfig2To3();
|
||||
}
|
||||
if (version < 4) {
|
||||
updateConfig3To4();
|
||||
}
|
||||
if (version < 5) {
|
||||
updateConfig4To5();
|
||||
}
|
||||
|
||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||
plugin.saveConfig();
|
||||
plugin.getLogger().info("Configuration update complete!");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void updateConfig4To5() {
|
||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||
plugin.getConfig().set("settings.search.polls-per-tick", 5);
|
||||
plugin.getConfig().set("settings.search.max-results", 40);
|
||||
plugin.getConfig().set("config-version", 5);
|
||||
});
|
||||
}
|
||||
|
||||
private void updateConfig3To4() {
|
||||
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() {
|
||||
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() {
|
||||
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 (section == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<String> keys = section.getKeys(false);
|
||||
|
||||
// Ensure section has content
|
||||
if (keys.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<String, Boolean> toggles = new HashMap<>();
|
||||
|
||||
for (String playerName : keys) {
|
||||
OfflinePlayer player = plugin.matchPlayer(playerName);
|
||||
if (player != null) {
|
||||
toggles.put(plugin.getPlayerID(player), section.getBoolean(playerName + ".toggle", false));
|
||||
}
|
||||
}
|
||||
|
||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||
// Wipe old ConfigurationSection
|
||||
plugin.getConfig().set(sectionName, null);
|
||||
|
||||
// Prepare new ConfigurationSection
|
||||
ConfigurationSection newSection = plugin.getConfig().getConfigurationSection(newSectionName);
|
||||
if (newSection == null) {
|
||||
newSection = plugin.getConfig().createSection(newSectionName);
|
||||
}
|
||||
// Set new values
|
||||
for (Map.Entry<String, Boolean> entry : toggles.entrySet()) {
|
||||
newSection.set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
/**
|
||||
* Abstraction for some simple cache calls.
|
||||
*
|
||||
* @author Jikoo
|
||||
*/
|
||||
public abstract class Function<V> {
|
||||
|
||||
public abstract boolean run(V value);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import com.lishid.openinv.internal.IAnySilentContainer;
|
||||
import com.lishid.openinv.internal.IPlayerDataManager;
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class InternalAccessor {
|
||||
|
||||
private final Plugin plugin;
|
||||
private final String version;
|
||||
private boolean supported = false;
|
||||
private IPlayerDataManager playerDataManager;
|
||||
private IAnySilentContainer anySilentContainer;
|
||||
|
||||
public InternalAccessor(final Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
String packageName = plugin.getServer().getClass().getPackage().getName();
|
||||
this.version = packageName.substring(packageName.lastIndexOf('.') + 1);
|
||||
|
||||
try {
|
||||
Class.forName("com.lishid.openinv.internal." + this.version + ".SpecialPlayerInventory");
|
||||
Class.forName("com.lishid.openinv.internal." + this.version + ".SpecialEnderChest");
|
||||
this.playerDataManager = this.createObject(IPlayerDataManager.class, "PlayerDataManager");
|
||||
this.anySilentContainer = this.createObject(IAnySilentContainer.class, "AnySilentContainer");
|
||||
this.supported = InventoryAccess.isUsable();
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
public String getReleasesLink() {
|
||||
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":
|
||||
return "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8";
|
||||
case "v1_17_R1":
|
||||
default:
|
||||
return "https://github.com/Jikoo/OpenInv/releases";
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T createObject(final Class<? extends T> assignableClass, final String className,
|
||||
final Object... params) throws ClassCastException, ClassNotFoundException,
|
||||
InstantiationException, IllegalAccessException, IllegalArgumentException,
|
||||
InvocationTargetException, NoSuchMethodException, SecurityException {
|
||||
// Fetch internal class if it exists.
|
||||
Class<?> internalClass = Class.forName("com.lishid.openinv.internal." + this.version + "." + className);
|
||||
if (!assignableClass.isAssignableFrom(internalClass)) {
|
||||
String message = String.format("Found class %s but cannot cast to %s!", internalClass.getName(), assignableClass.getName());
|
||||
this.plugin.getLogger().warning(message);
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
|
||||
// Quick return: no parameters, no need to fiddle about finding the correct constructor.
|
||||
if (params.length == 0) {
|
||||
return assignableClass.cast(internalClass.getConstructor().newInstance());
|
||||
}
|
||||
|
||||
// Search constructors for one matching the given parameters
|
||||
nextConstructor: for (Constructor<?> constructor : internalClass.getConstructors()) {
|
||||
Class<?>[] requiredClasses = constructor.getParameterTypes();
|
||||
if (requiredClasses.length != params.length) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < params.length; ++i) {
|
||||
if (!requiredClasses[i].isAssignableFrom(params[i].getClass())) {
|
||||
continue nextConstructor;
|
||||
}
|
||||
}
|
||||
return assignableClass.cast(constructor.newInstance(params));
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder("Found class ").append(internalClass.getName())
|
||||
.append(" but cannot find any matching constructors for [");
|
||||
for (Object object : params) {
|
||||
builder.append(object.getClass().getName()).append(", ");
|
||||
}
|
||||
builder.delete(builder.length() - 2, builder.length());
|
||||
|
||||
String message = builder.append(']').toString();
|
||||
this.plugin.getLogger().warning(message);
|
||||
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the IAnySilentContainer implementation for the current server version.
|
||||
*
|
||||
* @return the IAnySilentContainer
|
||||
* @throws IllegalStateException if server version is unsupported
|
||||
*/
|
||||
public IAnySilentContainer getAnySilentContainer() {
|
||||
if (!this.supported) {
|
||||
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
|
||||
}
|
||||
return this.anySilentContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the IPlayerDataManager implementation for the current server version.
|
||||
*
|
||||
* @return the IPlayerDataManager
|
||||
* @throws IllegalStateException if server version is unsupported
|
||||
*/
|
||||
public IPlayerDataManager getPlayerDataManager() {
|
||||
if (!this.supported) {
|
||||
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
|
||||
}
|
||||
return this.playerDataManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server implementation version. If not initialized, returns the string "null"
|
||||
* instead.
|
||||
*
|
||||
* @return the version, or "null"
|
||||
*/
|
||||
public String getVersion() {
|
||||
return this.version != null ? this.version : "null";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the server implementation is supported.
|
||||
*
|
||||
* @return true if initialized for a supported server version
|
||||
*/
|
||||
public boolean isSupported() {
|
||||
return this.supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the ISpecialEnderChest implementation for the given Player, or
|
||||
* null if the current version is unsupported.
|
||||
*
|
||||
* @param player the Player
|
||||
* @param online true if the Player is online
|
||||
* @return the ISpecialEnderChest created
|
||||
* @throws InstantiationException if the ISpecialEnderChest could not be instantiated
|
||||
*/
|
||||
public ISpecialEnderChest newSpecialEnderChest(final Player player, final boolean online) throws InstantiationException {
|
||||
if (!this.supported) {
|
||||
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
|
||||
}
|
||||
try {
|
||||
return this.createObject(ISpecialEnderChest.class, "SpecialEnderChest", player, online);
|
||||
} catch (Exception e) {
|
||||
throw new InstantiationException(String.format("Unable to create a new ISpecialEnderChest: %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the ISpecialPlayerInventory implementation for the given Player..
|
||||
*
|
||||
* @param player the Player
|
||||
* @param online true if the Player is online
|
||||
* @return the ISpecialPlayerInventory created
|
||||
* @throws InstantiationException if the ISpecialPlayerInventory could not be instantiated
|
||||
*/
|
||||
public ISpecialPlayerInventory newSpecialPlayerInventory(final Player player, final boolean online) throws InstantiationException {
|
||||
if (!this.supported) {
|
||||
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
|
||||
}
|
||||
try {
|
||||
return this.createObject(ISpecialPlayerInventory.class, "SpecialPlayerInventory", player, online);
|
||||
} catch (Exception e) {
|
||||
throw new InstantiationException(String.format("Unable to create a new ISpecialPlayerInventory: %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A simple language manager supporting both custom and bundled languages.
|
||||
*
|
||||
* @author Jikoo
|
||||
*/
|
||||
public class LanguageManager {
|
||||
|
||||
private final OpenInv plugin;
|
||||
private final String defaultLocale;
|
||||
private final Map<String, YamlConfiguration> locales;
|
||||
|
||||
public LanguageManager(@NotNull OpenInv plugin, @NotNull String defaultLocale) {
|
||||
this.plugin = plugin;
|
||||
this.defaultLocale = defaultLocale;
|
||||
this.locales = new HashMap<>();
|
||||
getOrLoadLocale(defaultLocale);
|
||||
}
|
||||
|
||||
private YamlConfiguration getOrLoadLocale(@NotNull String locale) {
|
||||
YamlConfiguration loaded = locales.get(locale);
|
||||
if (loaded != null) {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
InputStream resourceStream = plugin.getResource(locale + ".yml");
|
||||
YamlConfiguration localeConfigDefaults;
|
||||
if (resourceStream == null) {
|
||||
localeConfigDefaults = new YamlConfiguration();
|
||||
} else {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream))) {
|
||||
localeConfigDefaults = YamlConfiguration.loadConfiguration(reader);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "[LanguageManager] Unable to load resource " + locale + ".yml", e);
|
||||
localeConfigDefaults = new YamlConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
File file = new File(plugin.getDataFolder(), locale + ".yml");
|
||||
YamlConfiguration localeConfig;
|
||||
|
||||
if (!file.exists()) {
|
||||
localeConfig = localeConfigDefaults;
|
||||
try {
|
||||
localeConfigDefaults.save(file);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "[LanguageManager] Unable to save resource " + locale + ".yml", e);
|
||||
}
|
||||
} else {
|
||||
localeConfig = YamlConfiguration.loadConfiguration(file);
|
||||
|
||||
// Add new language keys
|
||||
List<String> newKeys = new ArrayList<>();
|
||||
for (String key : localeConfigDefaults.getKeys(true)) {
|
||||
if (localeConfigDefaults.isConfigurationSection(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (localeConfig.isSet(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
localeConfig.set(key, localeConfigDefaults.get(key));
|
||||
newKeys.add(key);
|
||||
}
|
||||
|
||||
if (!newKeys.isEmpty()) {
|
||||
plugin.getLogger().info("[LanguageManager] Added new language keys: " + String.join(", ", newKeys));
|
||||
try {
|
||||
localeConfig.save(file);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "[LanguageManager] Unable to save resource " + locale + ".yml", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!locale.equals(defaultLocale)) {
|
||||
localeConfigDefaults = locales.get(defaultLocale);
|
||||
|
||||
// Check for missing keys
|
||||
List<String> newKeys = new ArrayList<>();
|
||||
for (String key : localeConfigDefaults.getKeys(true)) {
|
||||
if (localeConfigDefaults.isConfigurationSection(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (localeConfig.isSet(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newKeys.add(key);
|
||||
}
|
||||
|
||||
if (!newKeys.isEmpty()) {
|
||||
plugin.getLogger().info("[LanguageManager] Missing translations from " + locale + ".yml: " + String.join(", ", newKeys));
|
||||
}
|
||||
|
||||
// Fall through to default locale
|
||||
localeConfig.setDefaults(localeConfigDefaults);
|
||||
}
|
||||
|
||||
locales.put(locale, localeConfig);
|
||||
return localeConfig;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getValue(@NotNull String key, @Nullable String locale) {
|
||||
String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase()).getString(key);
|
||||
if (value == null || value.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
value = ChatColor.translateAlternateColorCodes('&', value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getValue(@NotNull String key, @Nullable String locale, @NotNull String... replacements) {
|
||||
if (replacements.length % 2 != 0) {
|
||||
plugin.getLogger().log(Level.WARNING, "[LanguageManager] Replacement data is uneven", new Exception());
|
||||
}
|
||||
|
||||
String value = getValue(key, locale);
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < replacements.length; i += 2) {
|
||||
value = value.replace(replacements[i], replacements[i + 1]);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class MessagePart {
|
||||
|
||||
private final Supplier<BaseComponent> playerProvider;
|
||||
private final Supplier<String> consoleProvider;
|
||||
private @Nullable BaseComponent playerComponent;
|
||||
private @Nullable String consoleComponent;
|
||||
|
||||
public MessagePart(@NotNull Supplier<@NotNull BaseComponent> playerProvider) {
|
||||
this(playerProvider, () -> unwrapComponent(playerProvider.get()));
|
||||
}
|
||||
|
||||
public MessagePart(
|
||||
@NotNull Supplier<@NotNull BaseComponent> playerProvider,
|
||||
@NotNull Supplier<@NotNull String> consoleProvider) {
|
||||
this.playerProvider = playerProvider;
|
||||
this.consoleProvider = consoleProvider;
|
||||
}
|
||||
|
||||
public BaseComponent forPlayer() {
|
||||
if (this.playerComponent == null) {
|
||||
this.playerComponent = playerProvider.get();
|
||||
}
|
||||
return this.playerComponent;
|
||||
}
|
||||
|
||||
public String forConsole() {
|
||||
if (consoleComponent == null) {
|
||||
consoleComponent = consoleProvider.get();
|
||||
}
|
||||
return consoleComponent;
|
||||
}
|
||||
|
||||
private static String unwrapComponent(BaseComponent component) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
ClickEvent clickEvent = component.getClickEvent();
|
||||
|
||||
if (clickEvent != null) {
|
||||
// Prefer click events, i.e. "Check out our GitHub" with a link -> raw link
|
||||
builder.append(clickEvent.getValue());
|
||||
} else if (component instanceof TextComponent) {
|
||||
builder.append(((TextComponent) component).getText());
|
||||
}
|
||||
|
||||
List<BaseComponent> extra = component.getExtra();
|
||||
if (extra != null) {
|
||||
for (BaseComponent extraComponent : extra) {
|
||||
builder.append(unwrapComponent(extraComponent));
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import org.bukkit.permissions.Permissible;
|
||||
|
||||
public enum Permissions {
|
||||
|
||||
OPENINV("openinv"),
|
||||
OVERRIDE("override"),
|
||||
EXEMPT("exempt"),
|
||||
CROSSWORLD("crossworld"),
|
||||
SILENT("silent"),
|
||||
SILENT_DEFAULT("silent.default", true),
|
||||
ANYCHEST("anychest"),
|
||||
ANY_DEFAULT("any.default", true),
|
||||
ENDERCHEST("openender"),
|
||||
ENDERCHEST_ALL("openenderall"),
|
||||
SEARCH("search"),
|
||||
SEARCH_PLAYERS_ONLINE("search.players.online"),
|
||||
SEARCH_PLAYERS_OFFLINE("search.players.offline"),
|
||||
SEARCH_CHUNKS_LOADED("search.chunks.loaded"),
|
||||
SEARCH_CHUNKS_UNLOADED("search.chunks.unloaded"),
|
||||
EDITINV("editinv"),
|
||||
EDITENDER("editender"),
|
||||
OPENSELF("openself"),
|
||||
OPENONLINE("openonline"),
|
||||
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 (uninheritable || hasPermission || permissible.isPermissionSet(permission)) {
|
||||
return hasPermission;
|
||||
}
|
||||
|
||||
StringBuilder permissionDestroyer = new StringBuilder(permission);
|
||||
for (int lastPeriod = permissionDestroyer.lastIndexOf("."); lastPeriod > 0;
|
||||
lastPeriod = permissionDestroyer.lastIndexOf(".")) {
|
||||
permissionDestroyer.delete(lastPeriod + 1, permissionDestroyer.length()).append('*');
|
||||
|
||||
hasPermission = permissible.hasPermission(permissionDestroyer.toString());
|
||||
if (hasPermission || permissible.isPermissionSet(permissionDestroyer.toString())) {
|
||||
return hasPermission;
|
||||
}
|
||||
|
||||
permissionDestroyer.delete(lastPeriod, permissionDestroyer.length());
|
||||
|
||||
}
|
||||
|
||||
return permissible.hasPermission("*");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A data container for JSON-esque expressions.
|
||||
*
|
||||
* <p>Ex: A ChunkBucket can be expressed <code>chunks{world:world_name,x:0,z:0,radius:10,load:true}</code>. The
|
||||
* starting "chunks" identifies it as a ChunkBucket and the content in braces contains the construction detail.
|
||||
* However, a simpler detail, such as a MatchableItem, may be specified <code>type:IRON_SWORD</code> where "type"
|
||||
* is both the identifier of the MatchableItem and the key of the sole value.
|
||||
*
|
||||
* <p>A best-effort will be made to match other start and finish demarcation techniques.
|
||||
*/
|
||||
public class PseudoJson {
|
||||
|
||||
private static final char[] BRACE_OPEN = new char[] { '{', '[', '(' };
|
||||
private static final char[] BRACE_CLOSE = new char[] { '}', ']', ')' };
|
||||
private static final Set<Character> RESERVED_CHARACTERS = new HashSet<>();
|
||||
|
||||
static {
|
||||
for (char brace : BRACE_OPEN) {
|
||||
RESERVED_CHARACTERS.add(brace);
|
||||
}
|
||||
for (char brace : BRACE_CLOSE) {
|
||||
RESERVED_CHARACTERS.add(brace);
|
||||
}
|
||||
RESERVED_CHARACTERS.add(',');
|
||||
RESERVED_CHARACTERS.add(':');
|
||||
}
|
||||
|
||||
private final String identifier;
|
||||
private final Map<String, String> mappings;
|
||||
|
||||
public PseudoJson(@NotNull String identifier) {
|
||||
this(identifier, new HashMap<>());
|
||||
}
|
||||
|
||||
public PseudoJson(@NotNull String identifier, @NotNull Map<String, String> mappings) {
|
||||
this.identifier = identifier;
|
||||
mappings.forEach(PseudoJson::validate);
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
public @NotNull String getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public @NotNull Optional<String> put(@NotNull String key, @NotNull String value) {
|
||||
validate(key, value);
|
||||
|
||||
return Optional.ofNullable(this.mappings.put(key, value));
|
||||
}
|
||||
|
||||
public @NotNull Optional<String> get(@NotNull String key) {
|
||||
return Optional.ofNullable(this.mappings.get(key));
|
||||
}
|
||||
|
||||
public @NotNull Map<String, String> getMappings() {
|
||||
return Collections.unmodifiableMap(mappings);
|
||||
}
|
||||
|
||||
public Optional<String> remove(@NotNull String key) {
|
||||
return Optional.ofNullable(this.mappings.get(key));
|
||||
}
|
||||
|
||||
public String asString() {
|
||||
StringBuilder builder = new StringBuilder(identifier);
|
||||
|
||||
builder.append('{');
|
||||
|
||||
for (Iterator<Map.Entry<String, String>> iterator = mappings.entrySet().iterator(); iterator.hasNext();) {
|
||||
Map.Entry<String, String> entry = iterator.next();
|
||||
builder.append(entry.getKey()).append(':').append(entry.getValue());
|
||||
if (iterator.hasNext()) {
|
||||
builder.append(',');
|
||||
}
|
||||
}
|
||||
|
||||
builder.append('}');
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return asString();
|
||||
}
|
||||
|
||||
private static void validate(String key, String value) {
|
||||
if (RESERVED_CHARACTERS.stream().anyMatch(character -> key.indexOf(character) >= 0 || value.indexOf(character) >= 0)) {
|
||||
throw new IllegalArgumentException(String.format("Primitive pseudo-JSON reserves characters %s", RESERVED_CHARACTERS));
|
||||
}
|
||||
}
|
||||
|
||||
public static PseudoJson fromString(String pseudoJson) {
|
||||
int open = -1;
|
||||
for (char brace : BRACE_OPEN) {
|
||||
open = pseudoJson.indexOf(brace);
|
||||
if (open >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String identifier = getIdentifier(pseudoJson, open);
|
||||
|
||||
int close = -1;
|
||||
for (char brace : BRACE_CLOSE) {
|
||||
close = pseudoJson.lastIndexOf(brace);
|
||||
if (close >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (open >= 0) {
|
||||
if (close <= open) {
|
||||
// Ignore bad ending brace.
|
||||
pseudoJson = pseudoJson.substring(open + 1);
|
||||
} else {
|
||||
// Use only bracketed content
|
||||
pseudoJson = pseudoJson.substring(open + 1, close);
|
||||
}
|
||||
}
|
||||
|
||||
return new PseudoJson(identifier, getMappings(pseudoJson));
|
||||
}
|
||||
|
||||
private static String getIdentifier(String pseudoJson, int braceOpen) {
|
||||
int identifierEnd;
|
||||
if (braceOpen >= 0) {
|
||||
identifierEnd = braceOpen;
|
||||
} else {
|
||||
// No quote, use first mapping.
|
||||
identifierEnd = pseudoJson.indexOf(':');
|
||||
if (identifierEnd == -1) {
|
||||
// No mappings, use whole string.
|
||||
return pseudoJson;
|
||||
}
|
||||
}
|
||||
|
||||
return pseudoJson.substring(0, identifierEnd);
|
||||
}
|
||||
|
||||
private static Map<String, String> getMappings(String pseudoJson) {
|
||||
String[] mappings = pseudoJson.split(",");
|
||||
|
||||
Map<String, String> data = new HashMap<>();
|
||||
for (String mapping : mappings) {
|
||||
String[] datum = mapping.split(":");
|
||||
if (datum.length >= 2) {
|
||||
data.put(datum[0], datum[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Utility class for common tab completions.
|
||||
*/
|
||||
public class TabCompleter {
|
||||
|
||||
/**
|
||||
* Offer tab completions for whole numbers.
|
||||
*
|
||||
* @param argument the argument to complete
|
||||
* @return integer options
|
||||
*/
|
||||
public static List<String> completeInteger(@NotNull String argument) {
|
||||
// Ensure existing argument is actually a number
|
||||
if (!argument.isEmpty()) {
|
||||
try {
|
||||
Integer.parseInt(argument);
|
||||
} catch (NumberFormatException e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
List<String> completions = new ArrayList<>(10);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
completions.add(argument + i);
|
||||
}
|
||||
|
||||
return completions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offer tab completions for a given Enum.
|
||||
*
|
||||
* @param argument the argument to complete
|
||||
* @param enumClazz the Enum to complete for
|
||||
* @return the matching Enum values
|
||||
*/
|
||||
public static List<String> completeEnum(@NotNull String argument, @NotNull Class<? extends Enum<?>> enumClazz) {
|
||||
return completeObject(argument, Enum::name, enumClazz.getEnumConstants());
|
||||
}
|
||||
|
||||
/**
|
||||
* Offer tab completions for {@link Material Materials}.
|
||||
*
|
||||
* @param argument the argument to complete
|
||||
* @return the matching {@code Material} keys
|
||||
*/
|
||||
public static List<String> completeMaterial(@NotNull String argument) {
|
||||
argument = argument.toLowerCase(Locale.ENGLISH);
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
for (Material material : Material.values()) {
|
||||
if (material.name().toLowerCase(Locale.ENGLISH).startsWith(argument)
|
||||
|| material.getKey().toString().startsWith(argument)
|
||||
|| material.getKey().getKey().startsWith(argument)) {
|
||||
completions.add(material.getKey().toString());
|
||||
}
|
||||
}
|
||||
|
||||
return completions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offer tab completions for {@link Keyed} values.
|
||||
*
|
||||
* @param argument the argument to complete
|
||||
* @param options the {@code Keyed} values
|
||||
* @return the matching keys
|
||||
*/
|
||||
public static List<String> completeKeyed(@NotNull String argument, @NotNull Keyed @NotNull [] options) {
|
||||
argument = argument.toLowerCase(Locale.ENGLISH);
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
for (Keyed keyed : options) {
|
||||
if (keyed.getKey().toString().startsWith(argument)
|
||||
|| keyed.getKey().getKey().startsWith(argument)) {
|
||||
completions.add(keyed.getKey().toString());
|
||||
}
|
||||
}
|
||||
|
||||
return completions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offer tab completions for a given array of Strings.
|
||||
*
|
||||
* @param argument the argument to complete
|
||||
* @param options the Strings which may be completed
|
||||
* @return the matching Strings
|
||||
*/
|
||||
public static List<String> completeString(@NotNull String argument, @NotNull String @NotNull [] options) {
|
||||
argument = argument.toLowerCase(Locale.ENGLISH);
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
for (String option : options) {
|
||||
if (option.startsWith(argument)) {
|
||||
completions.add(option);
|
||||
}
|
||||
}
|
||||
|
||||
return completions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offer tab completions for visible online Players' names.
|
||||
*
|
||||
* @param sender the command's sender
|
||||
* @param argument the argument to complete
|
||||
* @return the matching Players' names
|
||||
*/
|
||||
public static List<String> completeOnlinePlayer(@NotNull CommandSender sender, String argument) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
Player senderPlayer = sender instanceof Player ? (Player) sender : null;
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (senderPlayer != null && !senderPlayer.canSee(player)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StringUtil.startsWithIgnoreCase(player.getName(), argument)) {
|
||||
completions.add(player.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return completions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offer tab completions for a given array of Objects.
|
||||
*
|
||||
* @param argument the argument to complete
|
||||
* @param converter the Function for converting the Object into a comparable String
|
||||
* @param options the Objects which may be completed
|
||||
* @return the matching Strings
|
||||
*/
|
||||
public static <T> List<String> completeObject(@NotNull String argument, @NotNull Function<T, String> converter, @NotNull T @NotNull [] options) {
|
||||
argument = argument.toLowerCase(Locale.ENGLISH);
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
for (T option : options) {
|
||||
String optionString = converter.apply(option).toLowerCase(Locale.ENGLISH);
|
||||
if (optionString.startsWith(argument)) {
|
||||
completions.add(optionString);
|
||||
}
|
||||
}
|
||||
|
||||
return completions;
|
||||
}
|
||||
|
||||
private TabCompleter() {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
config-version: 5
|
||||
settings:
|
||||
search:
|
||||
polls-per-tick: 5
|
||||
max-results: 40
|
||||
disable-saving: false
|
||||
locale: 'en_us'
|
||||
@@ -0,0 +1,30 @@
|
||||
messages:
|
||||
error:
|
||||
consoleUnsupported: 'Du kannst diesen Befehl nicht von der Konsole ausführen.'
|
||||
lootNotGenerated: '&cBeute noch nicht erstellt! Bitte schalte &b/silentcontainer&c aus.'
|
||||
invalidMaterial: '&cUngültiges Material: "%target%"'
|
||||
invalidNumber: '&cUngültige Nummer: "%target%"'
|
||||
invalidPlayer: '&cSpieler nicht gefunden.'
|
||||
permissionOpenSelf: '&cDu darfst dein eigenes Inventar nicht öffnen.'
|
||||
permissionEnderAll: '&cDu darfst die Endertruhen von anderen Spielern nicht öffnen.'
|
||||
permissionExempt: '&c%target%''s Inventar ist geschützt.'
|
||||
permissionCrossWorld: '&c%target% ist nicht in der selben Welt wie du.'
|
||||
permissionPlayerOnline: '&cDu darfst keine Inventare von Spielern öffnen, die online sind.'
|
||||
permissionPlayerOffline: '&cDu darfst keine Inventare von Spielern öffnen, die offline sind.'
|
||||
commandException: '&cAn error occurred. Please check console for details.'
|
||||
info:
|
||||
containerBlocked: 'Du öffnest einen gesperrten Container.'
|
||||
containerBlockedSilent: 'Du öffnest einen gesperrten Container lautlos.'
|
||||
containerSilent: 'Du öffnest einen Container lautlos.'
|
||||
settingState: '%setting%: %state%'
|
||||
player:
|
||||
noMatches: 'Keine Spieler mit %target% gefunden.'
|
||||
matches: 'Spieler hält %target%: %detail%'
|
||||
container:
|
||||
noMatches: 'Keine Container mit %target% gefunden.'
|
||||
matches: 'Container hat %target%: %detail%'
|
||||
on: 'an'
|
||||
off: 'aus'
|
||||
container:
|
||||
player: '%player%''s Inventar'
|
||||
enderchest: '%player%''s Endertruhe'
|
||||
Loaded 100 of 233 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user